content: -- ----------------------------------------------------------------------------
  
  {- |
    Module     : Hunt.Common.Occurrences
    Copyright  : Copyright (C) 2011 Sebastian M. Schlatt, Timo B. Huebel, Uwe Schmidt
    License    : MIT
  
    Maintainer : Timo B. Huebel (tbh@holumbus.org)
    Stability  : experimental
    Portability: none portable
  
    Occurrences of words within the index.
    A word occurs in document at specific locations.
  -}
  
  -- ----------------------------------------------------------------------------
  
  module Hunt.Common.Occurrences
  where
  
  import           Prelude                hiding (subtract)
  
  import           Hunt.Common.BasicTypes
  import           Hunt.Common.DocId
  import           Hunt.Common.DocIdMap   (DocIdMap)
  import           Hunt.Common.DocIdSet   (DocIdSet)
  import qualified Hunt.Common.DocIdMap   as DM
  import           Hunt.Common.Positions  (Positions)
  import qualified Hunt.Common.Positions  as Pos
  
  -- ------------------------------------------------------------
  
  -- | The occurrences of words in documents.
  --   A mapping from document ids to the positions in the document.
  
  type Occurrences        = DocIdMap Positions
  
  -- ------------------------------------------------------------
  
  -- | Create an empty set of positions.
  empty                   :: Occurrences
  empty                   = DM.empty
  
  -- | Create Occurrences from a 'DocIdSet'
  --   Since the 'DocIdSet' contains no position information, we
  --   assume position one for each 'DocId'
  fromDocIdSet            :: DocIdSet -> Occurrences
  fromDocIdSet s          = DM.fromDocIdSet (\_ -> Pos.singleton 1) s
  
  -- | Create a single dcid set with a single position.
  singleton               :: DocId -> Position -> Occurrences
  singleton d p           = singleton' d [p]
  
  -- | Create a single dcid set with a set of.
  singleton'              :: DocId -> [Position] -> Occurrences
  singleton' d ps         = DM.insert d (Pos.fromList ps) DM.empty
  
  -- | Test on empty set of positions.
  null                    :: Occurrences -> Bool
  null                    = DM.null
  
  -- | Determine the number of positions in a set of occurrences.
  size                    :: Occurrences -> Int
  size                    = DM.foldr ((+) . Pos.size) 0
  
  -- | Add a position to occurrences.
  insert                  :: DocId -> Position -> Occurrences -> Occurrences
  insert d p              = DM.insertWith Pos.union d (Pos.singleton p)
  
  -- | Add multiple positions to occurrences
  insert'                 :: DocId -> Positions -> Occurrences -> Occurrences
  insert' d ps occs       = Pos.foldr (insert d) occs ps
  
  -- | Remove a position from occurrences.
  deleteOccurrence        :: DocId -> Position -> Occurrences -> Occurrences
  deleteOccurrence d p    = subtract (DM.singleton d (Pos.singleton p))
  
  -- | Delete a document (by 'DocId') from occurrences.
  delete                  :: DocId -> Occurrences -> Occurrences
  delete                  = DM.delete
  
  -- | Changes the DocIDs of the occurrences.
  update                  :: (DocId -> DocId) -> Occurrences -> Occurrences
  update f                = DM.foldrWithKey
                            (\ d ps res -> DM.insertWith Pos.union (f d) ps res) empty
  
  -- | Merge two occurrences.
  merge                   :: Occurrences -> Occurrences -> Occurrences
  merge                   = DM.unionWith Pos.union
  
  -- | Merge occurrences
  merges                  :: [Occurrences] -> Occurrences
  merges                  = DM.unionsWith Pos.union
  
  -- | Difference of occurrences.
  difference              :: Occurrences -> Occurrences -> Occurrences
  difference              = DM.difference
  
  -- | Remove Set of DocIds from Occurrences
  diffWithSet             :: Occurrences -> DocIdSet -> Occurrences
  diffWithSet             = DM.diffWithSet
  
  -- | Subtract occurrences from some other occurrences.
  subtract                :: Occurrences -> Occurrences -> Occurrences
  subtract                = DM.differenceWith subtractPositions
    where
    subtractPositions p1 p2
      = if Pos.null diffPos
          then Nothing
          else Just diffPos
      where
      diffPos = Pos.difference p1 p2
  
  intersectOccurrences    :: (Positions -> Positions -> Positions) ->
                             Occurrences -> Occurrences -> Occurrences
  intersectOccurrences pf os1 os2
      = DM.filter (not . Pos.null) $ DM.intersectionWith pf os1 os2
  
  -- ------------------------------------------------------------
  