module Data.Urn.Internal (
Weight, Index(..), Size(..),
BTree(..), WTree(..), pattern WLeaf, pattern WNode, Urn(..),
sample, bsample,
insert, uninsert,
update, replace, construct,
foldWTree,
randomIndexWith,
showUrnTreeStructureWith,
showUrnTreeStructure
) where
import Data.Bits
import Data.Urn.Internal.AlmostPerfect
import Data.List.NonEmpty (NonEmpty(..))
import Control.DeepSeq
import qualified Data.Ord as Ord
import qualified Data.List as List
type Weight = Word
newtype Index = Index { getIndex :: Word } deriving (Eq, Ord, NFData)
newtype Size = Size { getSize :: Word }
deriving ( Eq, Ord, Show, Bounded, Enum
, Num, Real, Integral
, Bits, FiniteBits
, NFData )
data BTree a = BLeaf a
| BNode !(WTree a) !(WTree a)
deriving (Eq, Ord, Show)
data WTree a = WTree { weight :: !Weight
, btree :: !(BTree a) }
deriving (Eq, Ord, Show)
pattern WLeaf :: Weight -> a -> WTree a
pattern WNode :: Weight -> WTree a -> WTree a -> WTree a
pattern WLeaf w a = WTree { weight = w, btree = BLeaf a }
pattern WNode w l r = WTree { weight = w, btree = BNode l r }
data Urn a = Urn { size :: !Size
, wtree :: !(WTree a) }
instance NFData a => NFData (BTree a) where
rnf (BLeaf a) = rnf a
rnf (BNode l r) = rnf l `seq` rnf r
instance NFData a => NFData (WTree a) where
rnf (WTree w t) = rnf w `seq` rnf t
instance NFData a => NFData (Urn a) where
rnf (Urn size wt) = rnf size `seq` rnf wt
instance Show a => Show (Urn a) where
showsPrec p u = showParen (p > 10) $
showString "fromList " . shows (toList [] $ wtree u) where
toList acc (WLeaf w a) = List.insertBy (flip $ Ord.comparing fst) (w,a) acc
toList acc (WNode _ l r) = toList (toList acc l) r
showUrnTreeStructureWith :: (a -> String) -> Urn a -> String
showUrnTreeStructureWith disp (Urn (Size size) wtree) =
unlines $ ("Urn, size " ++ show size ++ ":") : strings wtree
where
strings (WLeaf w a) = ["(" ++ show w ++ ": " ++ disp a ++ ")"]
strings (WNode w l r) = ("[" ++ show w ++ "]") :
" |" :
nest '+' '|' (strings l) ++
" |" :
nest '`' ' ' (strings r)
nest cc gc (child:grandchildren) =
([' ',cc,'-'] ++ child) : map ([' ', gc, ' '] ++) grandchildren
nest _ _ [] = []
showUrnTreeStructure :: Show a => Urn a -> String
showUrnTreeStructure = showUrnTreeStructureWith show
randomIndexWith :: Functor f => ((Word,Word) -> f Word) -> Urn a -> f Index
randomIndexWith rand u = Index <$> rand (0, weight (wtree u) 1)
bsample :: BTree a -> Index -> a
bsample (BLeaf a) _ =
a
bsample (BNode (WTree wl l) (WTree _ r)) (Index i)
| i < wl = bsample l (Index i)
| otherwise = bsample r (Index $ i wl)
sample :: WTree a -> Index -> a
sample = bsample . btree
foldWTree :: (Weight -> a -> b)
-> (Weight -> b -> WTree a -> b)
-> (Weight -> WTree a -> b -> b)
-> Size -> WTree a
-> b
foldWTree fLeaf fLeft fRight = go where
go _ (WLeaf w a) = fLeaf w a
go path (WNode w l r) | path `testBit` 0 = fRight w l (go path' r)
| otherwise = fLeft w (go path' l) r
where path' = path `shiftR` 1
insert :: Weight -> a -> Urn a -> Urn a
insert w' a' (Urn size wt) =
Urn (size+1) $ foldWTree (\w a -> WNode (w+w') (WLeaf w a) (WLeaf w' a'))
(\w -> WNode (w+w'))
(\w -> WNode (w+w'))
size wt
uninsert :: Urn a -> (Weight, a, Weight, Maybe (Urn a))
uninsert (Urn size wt) =
case foldWTree (\w a -> (w, a, 0, Nothing))
(\w ul' r -> case ul' of
(w', a', lb, Just l') -> (w', a', lb, Just $ WNode (ww') l' r)
(w', a', lb, Nothing) -> (w', a', lb, Just r))
(\w l ur' -> case ur' of
(w', a', lb, Just r') -> (w', a', lb + weight l, Just $ WNode (ww') l r')
(w', a', lb, Nothing) -> (w', a', lb + weight l, Just l))
(size1) wt of
(w', a', lb, mt) -> (w', a', lb, Urn (size1) <$> mt)
update :: (Weight -> a -> (Weight, a)) -> WTree a -> Index -> (Weight, a, Weight, a, WTree a)
update upd = go where
go (WLeaf w a) _ =
let (wNew, aNew) = upd w a
in (w, a, wNew, aNew, WLeaf wNew aNew)
go (WNode w l@(WTree wl _) r) (Index i)
| i < wl = case go l (Index i) of
(wOld, aOld, wNew, aNew, l') -> (wOld, aOld, wNew, aNew, WNode (wwOld+wNew) l' r)
| otherwise = case go r (Index $ iwl) of
(wOld, aOld, wNew, aNew, r') -> (wOld, aOld, wNew, aNew, WNode (wwOld+wNew) l r')
replace :: Weight -> a -> WTree a -> Index -> (Weight, a, WTree a)
replace wNew aNew = go where
go (WLeaf w a) _ =
(w, a, WLeaf wNew aNew)
go (WNode w l@(WTree wl _) r) (Index i)
| i < wl = case go l (Index i) of
(w', a', l') -> (w', a', WNode (ww'+wNew) l' r)
| otherwise = case go r (Index $ iwl) of
(w', a', r') -> (w', a', WNode (ww'+wNew) l r')
construct :: NonEmpty (Weight, a) -> Urn a
construct list = Urn (Size size) tree
where
size = fromIntegral $ length list
tree = almostPerfect (\l r -> WNode (weight l + weight r) l r)
(uncurry WLeaf)
size
list