packages feed

Fungi-1.0.6: src/Data/History.hs

module Data.History (
    History (getSize)
  , empty
  , fromHistory
  , fromList
  , toList
  , push
  , pop
  , popN
  , lookup
  ) where

import Prelude hiding (lookup)

import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.Maybe (fromMaybe, mapMaybe)

import Text.PrettyShow

-----------------------------------------------------------

data History a = History {
    getSize :: Int
  , getReplacePos :: Int
  , getMap :: IntMap a
  }
  deriving
    (Show)

instance (PrettyShow a) => PrettyShow (History a) where
  pshow = ("History " ++) . pshow . toList

empty :: Int -> History a
empty n = fromList (Just n) []

fromHistory :: Maybe Int -> History a -> History a
fromHistory Nothing h = h
fromHistory (Just newSize) h
  | newSize >= getSize h = h { getSize = newSize }
  | otherwise = fromList (Just newSize) $ take newSize $ toList h -- TODO : Make this more efficient if needed

fromList :: Maybe Int -> [a] -> History a
fromList mSize xs = foldr push History { 
    getSize = size
  , getReplacePos = 0
  , getMap = IntMap.empty
  } xs
  where
    size = fromMaybe (length xs) mSize

getHeadPos :: History a -> Int
getHeadPos h = if rpos - 1 < 0
  then getSize h - 1
  else rpos - 1
  where
    rpos = getReplacePos h

fromLogicalPos :: History a -> Int -> Int
fromLogicalPos h pos = if pos <= hpos
  then hpos - pos
  else size + hpos - pos
  where
    size = getSize h
    hpos = getHeadPos h

push :: a -> History a -> History a
push x h = h {
    getMap = IntMap.insert rpos x $ getMap h
  , getReplacePos = if rpos + 1 < getSize h
      then rpos + 1
      else 0
  }
  where
    rpos = getReplacePos h

pop :: History a -> History a
pop h = h {
    getMap = IntMap.delete (getHeadPos h) $ getMap h
  , getReplacePos = if rpos - 1 < 0
      then getSize h - 1
      else rpos - 1
  }
  where
    rpos = getReplacePos h

popN :: Int -> History a -> History a
popN n = head . drop n . iterate pop

lookup :: Int -> History a -> Maybe a
lookup pos h
  | 0 <= pos && pos < size = IntMap.lookup (fromLogicalPos h pos) $ getMap h
  | otherwise = Nothing
  where
    size = getSize h

toList :: History a -> [a]
toList h = mapMaybe (`lookup` h) [0 .. getSize h - 1]