content: {-# LANGUAGE PatternGuards   #-}
  {-# LANGUAGE RecordWildCards #-}
  {-# LANGUAGE TypeFamilies    #-}
  
  module Page (
  
    PageId, Rank, Link, MLinks(..),
    parsePage, parsePageId
  
  ) where
  
  import Prelude                                   as P
  import qualified Data.ByteString.Lazy.Char8      as BL
  import qualified Data.Vector.Storable.Mutable    as UM
  import Data.Word
  
  
  -- | Unique identifier for a page.
  type PageId
          = Word32
  
  
  -- | Represents a link in the page raph.
  type Link
          = (PageId, PageId)
  
  
  -- | A single PageRank value.
  type Rank
          = Float
  
  -- | A mutable set of links
  data MLinks
          = MLinks
              { ml_ix    :: Int
              , ml_size  :: Int
              , ml_from  :: UM.IOVector PageId
              , ml_to    :: UM.IOVector PageId
              , ml_sizes :: UM.IOVector Int
              }
  
  
  -- | Parse just the PageId from a line in the links file.
  parsePageId :: BL.ByteString -> Maybe PageId
  parsePageId bs
          | Just (pid, _)         <- BL.readInt bs
          = Just $ fromIntegral pid
  
          | otherwise
          = Nothing
  
  
  -- | Parse a whole line of the links file.
  parsePage :: BL.ByteString -> MLinks -> IO (Maybe MLinks)
  parsePage bs links
          | Just (pid, bs2)       <- BL.readInt bs
          , Just bs3              <- char ':' bs2
          = do
               links'             <- pageIds (fromIntegral pid) bs3 links
               return (Just links')
  
          | otherwise
          = return Nothing
  
  
  -- | Parse a single character.
  char   :: Char -> BL.ByteString -> Maybe BL.ByteString
  char c bs
   | BL.null bs           = Nothing
   | BL.head bs == c      = Just (BL.tail bs)
   | otherwise            = Nothing
  {-# INLINE char #-}
  
  
  -- | Parse a vector of PageIds.
  pageIds    :: PageId -> BL.ByteString -> MLinks -> IO (MLinks)
  pageIds pid bs0 links0
   = go links0 0 bs0
  
   where  go links@(MLinks{..}) count bs
           | ml_ix >= ml_size
           = do   from'  <- UM.grow ml_from ml_size
                  to'    <- UM.grow ml_to   ml_size
                  go (MLinks ml_ix (2*ml_size) from' to' ml_sizes) count bs
  
           | BL.null bs
           = final
  
           | Just bs2     <- char ' ' bs
           = go links count bs2
  
           | Just (i, bs2) <- BL.readInt bs
           = do   UM.write ml_from ml_ix pid
                  UM.write ml_to   ml_ix (fromIntegral i)
                  go (MLinks (ml_ix+1) ml_size ml_from ml_to ml_sizes) (count+1) bs2
  
           | otherwise
           = final
           where
              final =
                  do
                      UM.write ml_sizes (fromIntegral pid) count
                      return links
  
  