{-# LANGUAGE BangPatterns #-} {-# LANGUAGE TypeFamilies #-} module Data.Indexation.NormalIx ( SimpleIx(..) , Indexed(..) , createIndexed , withUnique1 , withUnique2 , withIndex1 , withIndex2 , withIndex3 , withIndex4 , withFilters , insertGetIx , insert , deleteIndex , deleteU1 , deleteU2 , getU1 , getU2 , getI1 , getI2 , getI3 , getI4 , getFilter , adjust , adjustWithU1 , adjustWithU2 , update , updateWithU1 , updateWithU2 ) where import Data.HBlock (HBlock) import qualified Data.HBlock as HB import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM import Data.IntSet (IntSet) import qualified Data.IntSet as IS import Data.Vector (Vector, (!?)) import qualified Data.Vector as V import Data.SafeCopy import Data.Hashable import qualified Data.Foldable as Fold -- import Data.Indexation.IxData (IxData(ixDataPos, ixData)) import Data.Maybe (fromMaybe) data Indexed a u1 u2 i1 i2 i3 i4 = Indexed !(HBlock a) -- ^ Where all the data is !(Maybe (IndexU a u1 u2)) -- ^ Unique indexation structures !(Maybe (IndexI a i1 i2 i3 i4)) -- ^ Indexation structures !(Vector (a -> Bool)) -- ^ Filters (i.e. >= 30) !(Vector IntSet) -- ^ Filters results -- | IndexCore is the index structures for a given data data IndexU a u1 u2 = IxHasU1 !(HashMap u1 Int) !(a -> u1) | IxHasU2 !(HashMap u1 Int) !(a -> u1) !(HashMap u2 Int) !(a -> u2) data IndexI a i1 i2 i3 i4 = IxHasI1 !(HashMap i1 IntSet) !(a -> i1) | IxHasI2 !(HashMap i1 IntSet) !(a -> i1) !(HashMap i2 IntSet) !(a -> i2) | IxHasI3 !(HashMap i1 IntSet) !(a -> i1) !(HashMap i2 IntSet) !(a -> i2) !(HashMap i3 IntSet) !(a -> i3) | IxHasI4 !(HashMap i1 IntSet) !(a -> i1) !(HashMap i2 IntSet) !(a -> i2) !(HashMap i3 IntSet) !(a -> i3) !(HashMap i4 IntSet) !(a -> i4) -- * -- * Creation functions -- * class SimpleIx a where -- | Creates your indexed dataype type U1 a type U2 a type I1 a type I2 a type I3 a type I4 a create :: Indexed a (U1 a) (U2 a) (I1 a) (I2 a) (I3 a) (I4 a) defSlots :: Int defSlots = 4096 defBlockLen :: Int defBlockLen = 4096 -- | createIndexed is a helper function to create the base Indexed datatype -- it is used in the "createdUxIx" functions to avoid repetition createIndexed :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4) => a -> Indexed a u1 u2 i1 i2 i3 i4 createIndexed e = Indexed (HB.empty defSlots defBlockLen e) Nothing Nothing V.empty V.empty withUnique1 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> (a -> u1) -> Indexed a u1 u2 i1 i2 i3 i4 withUnique1 i@(Indexed v uis iis vf vs) f = case uis of Nothing -> Indexed v (Just $ IxHasU1 HM.empty f) iis vf vs _ -> i withUnique2 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> (a -> u2) -> Indexed a u1 u2 i1 i2 i3 i4 withUnique2 i@(Indexed v uis iis vf vs) f = case uis of Just (IxHasU1 m1 f1) -> Indexed v (Just $ IxHasU2 m1 f1 HM.empty f) iis vf vs _ -> i withIndex1 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> (a -> i1) -> Indexed a u1 u2 i1 i2 i3 i4 withIndex1 i@(Indexed v uis iis vf vs) f = case iis of Nothing -> Indexed v uis (Just $ IxHasI1 HM.empty f) vf vs _ -> i withIndex2 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> (a -> i2) -> Indexed a u1 u2 i1 i2 i3 i4 withIndex2 i@(Indexed v uis iis vf vs) f = case iis of Just (IxHasI1 m1 f1) -> let niis = Just $ IxHasI2 m1 f1 HM.empty f in Indexed v uis niis vf vs _ -> i withIndex3 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> (a -> i3) -> Indexed a u1 u2 i1 i2 i3 i4 withIndex3 i@(Indexed v uis iis vf vs) f = case iis of Just (IxHasI2 m1 f1 m2 f2) -> let niis = Just $ IxHasI3 m1 f1 m2 f2 HM.empty f in Indexed v uis niis vf vs _ -> i withIndex4 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> (a -> i4) -> Indexed a u1 u2 i1 i2 i3 i4 withIndex4 i@(Indexed v uis iis vf vs) f = case iis of Just (IxHasI3 m1 f1 m2 f2 m3 f3) -> let niis = Just $ IxHasI4 m1 f1 m2 f2 m3 f3 HM.empty f in Indexed v uis niis vf vs _ -> i withFilters :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> [(a -> Bool)] -> Indexed a u1 u2 i1 i2 i3 i4 withFilters (Indexed v uis iis _ _) lst = Indexed v uis iis flst vlst where flst = V.fromList lst vlst = V.replicate (V.length flst) IS.empty -- * -- * Insertion functions -- * insertGetIx :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> a -> (Int, Indexed a u1 u2 i1 i2 i3 i4) insertGetIx (Indexed hb uis iis vf vi) item = (pos, Indexed newhb newuis newiis vf newvi) where (pos, newhb) = HB.insertGetIx item hb newuis = case uis of Nothing -> Nothing Just (IxHasU1 m1 f1) -> Just $ IxHasU1 (HM.insert (f1 item) pos m1) f1 Just (IxHasU2 m1 f1 m2 f2) -> Just $ IxHasU2 (HM.insert (f1 item) pos m1) f1 (HM.insert (f2 item) pos m2) f2 newiis = case iis of Nothing -> Nothing Just (IxHasI1 m1 f1) -> Just $ IxHasI1 (HM.adjust (IS.insert pos) (f1 item) m1) f1 Just (IxHasI2 m1 f1 m2 f2) -> Just $ IxHasI2 (HM.adjust (IS.insert pos) (f1 item) m1) f1 (HM.adjust (IS.insert pos) (f2 item) m2) f2 Just (IxHasI3 m1 f1 m2 f2 m3 f3) -> Just $ IxHasI3 (HM.adjust (IS.insert pos) (f1 item) m1) f1 (HM.adjust (IS.insert pos) (f2 item) m2) f2 (HM.adjust (IS.insert pos) (f3 item) m3) f3 Just (IxHasI4 m1 f1 m2 f2 m3 f3 m4 f4) -> Just $ IxHasI4 (HM.adjust (IS.insert pos) (f1 item) m1) f1 (HM.adjust (IS.insert pos) (f2 item) m2) f2 (HM.adjust (IS.insert pos) (f3 item) m3) f3 (HM.adjust (IS.insert pos) (f4 item) m4) f4 newvi = V.imap fvi vi -- check if the filter applies to this element, if it applies insert -- the element index into the intset fvi i e = if V.unsafeIndex vf i $ item then IS.insert pos e else e {-# INLINABLE insertGetIx #-} insert :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> a -> Indexed a u1 u2 i1 i2 i3 i4 insert i item = snd $ insertGetIx i item {-# INLINE insert #-} -- * -- * Deletion functions -- * deleteIndex :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> Int -> Indexed a u1 u2 i1 i2 i3 i4 deleteIndex (Indexed hb uis iis vf vi) ix = Indexed (HB.delete ix hb) uis iis vf vi {-# INLINE deleteIndex #-} -- ^ TODO: delete from the indices and vi ? deleteU1 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u1 -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) deleteU1 i@(Indexed hb uis _ _ _) u1 = do u <- uis pos <- f u return $ deleteIndex i pos where f (IxHasU1 m _) = HM.lookup u1 m f (IxHasU2 m _ _ _) = HM.lookup u1 m deleteU2 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u2 -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) deleteU2 i@(Indexed hb uis _ _ _) u2 = do u <- uis pos <- f u return $ deleteIndex i pos where f (IxHasU1 _ _) = Nothing f (IxHasU2 _ _ m _) = HM.lookup u2 m -- deleteIxData {- delete :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> a -> Indexed a u1 u2 i1 i2 i3 i4 delete (Indexed hb uis iis vf vi) a = -} -- * -- * Accessor (getters) functions -- * getU1 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u1 -> Maybe a getU1 (Indexed hb uis _ _ _) u1 = do u <- uis pos <- f u HB.getPos hb pos where f (IxHasU1 m _) = HM.lookup u1 m f (IxHasU2 m _ _ _) = HM.lookup u1 m {-# INLINABLE getU1 #-} getU2 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u2 -> Maybe a getU2 (Indexed hb uis _ _ _) u2 = do u <- uis pos <- f u HB.getPos hb pos where f (IxHasU1 _ _) = Nothing f (IxHasU2 _ _ m _) = HM.lookup u2 m {-# INLINABLE getU2 #-} getI1 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> i1 -> Maybe (HBlock a) getI1 (Indexed hb _ iis _ _) i = do iis' <- iis set <- f iis' return $ HB.getSet hb set where f (IxHasI1 m _) = HM.lookup i m f (IxHasI2 m _ _ _) = HM.lookup i m f (IxHasI3 m _ _ _ _ _) = HM.lookup i m f (IxHasI4 m _ _ _ _ _ _ _) = HM.lookup i m {-# INLINABLE getI1 #-} getI2 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> i2 -> Maybe (HBlock a) getI2 (Indexed hb _ iis _ _) i = do iis' <- iis set <- f iis' return $ HB.getSet hb set where f (IxHasI2 _ _ m _) = HM.lookup i m f (IxHasI3 _ _ m _ _ _) = HM.lookup i m f (IxHasI4 _ _ m _ _ _ _ _) = HM.lookup i m f _ = Nothing {-# INLINABLE getI2 #-} getI3 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> i3 -> Maybe (HBlock a) getI3 (Indexed hb _ iis _ _) i = do iis' <- iis set <- f iis' return $ HB.getSet hb set where f (IxHasI3 _ _ _ _ m _) = HM.lookup i m f (IxHasI4 _ _ _ _ m _ _ _) = HM.lookup i m f _ = Nothing {-# INLINABLE getI3 #-} getI4 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> i4 -> Maybe (HBlock a) getI4 (Indexed hb _ iis _ _) i = do iis' <- iis set <- f iis' return $ HB.getSet hb set where f (IxHasI4 _ _ _ _ _ _ m _) = HM.lookup i m f _ = Nothing {-# INLINABLE getI4 #-} getFilter :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> Int -> Maybe (HBlock a) getFilter (Indexed hb _ _ _ vi) filterPos = do set <- vi !? filterPos return $ HB.getSet hb set {-# INLINABLE getFilter #-} -- * -- * Setter functions -- * -- | helper function, updates the hash of a unique index _updateUHash :: (Eq u, Hashable u) => HashMap u Int -> Int -> u -> u -> HashMap u Int _updateUHash m val old new = HM.insert new val $ HM.delete old m _updateIHash :: (Eq u, Hashable u) => HashMap u IntSet -> Int -> u -> u -> HashMap u IntSet _updateIHash m val old new = HM.insertWith insSet new valSet $ HM.adjust (IS.delete val) old m where insSet _ o = IS.insert val o valSet = IS.singleton val -- | helper function, updates the unique indices _updateU :: (Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2) => Maybe (IndexU a u1 u2) -> Int -> a -> a -> Maybe (IndexU a u1 u2) _updateU (Just (IxHasU1 m f)) val old new = Just $ IxHasU1 (_updateUHash m val (f old) (f new)) f _updateU (Just (IxHasU2 m1 f1 m2 f2)) val old new = Just $ IxHasU2 (_updateUHash m1 val (f1 old) (f1 new)) f1 (_updateUHash m2 val (f2 old) (f2 new)) f2 _updateU Nothing _ _ _ = Nothing -- | helper function, updates the normal indices _updateI :: ( Eq a , Eq i1, Hashable i1, Eq i2, Hashable i2 , Eq i3, Hashable i3, Eq i4, Hashable i4) => Maybe (IndexI a i1 i2 i3 i4) -> Int -> a -> a -> Maybe (IndexI a i1 i2 i3 i4) _updateI (Just (IxHasI1 m f)) val old new = Just $ IxHasI1 (_updateIHash m val (f old) (f new)) f _updateI (Just (IxHasI2 m1 f1 m2 f2)) val old new = Just $ IxHasI2 (_updateIHash m1 val (f1 old) (f1 new)) f1 (_updateIHash m2 val (f2 old) (f2 new)) f2 _updateI (Just (IxHasI3 m1 f1 m2 f2 m3 f3)) val old new = Just $ IxHasI3 (_updateIHash m1 val (f1 old) (f1 new)) f1 (_updateIHash m2 val (f2 old) (f2 new)) f2 (_updateIHash m3 val (f3 old) (f3 new)) f3 _updateI (Just (IxHasI4 m1 f1 m2 f2 m3 f3 m4 f4)) val old new = Just $ IxHasI4 (_updateIHash m1 val (f1 old) (f1 new)) f1 (_updateIHash m2 val (f2 old) (f2 new)) f2 (_updateIHash m3 val (f3 old) (f3 new)) f3 (_updateIHash m4 val (f4 old) (f4 new)) f4 _updateI Nothing _ _ _ = Nothing -- | helper function, updates the normal indices _updateF :: Vector (a -> Bool) -> Vector IntSet -> Int -> a -> a -> Vector IntSet _updateF vf v value old new = V.imap applyF vf -- map the vf, apply the function to the old and the new, if diff, change the v -- because it is faster to check for indexation in a vector than to check -- membership in a intset where applyF pos f = let f1 = f old f2 = f new iset = V.unsafeIndex v pos in if f1 == f2 then iset else if f2 then IS.insert value iset else IS.delete value iset adjust :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> Int -> (a -> a) -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) adjust i@(Indexed hb ius iis vf vi) pos f = do e <- HB.getPos hb pos let newe = f e return $ Indexed (HB.update newe pos hb) (_updateU ius pos e newe) (_updateI iis pos e newe) vf (_updateF vf vi pos e newe) {-# INLINABLE adjust #-} adjustWithU1 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u1 -> (a -> a) -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) adjustWithU1 i@(Indexed hb uis iis vf vi) u1 af = do u <- uis pos <- f u e <- HB.getPos hb pos let newe = af e return $ Indexed (HB.update newe pos hb) (_updateU uis pos e newe) (_updateI iis pos e newe) vf (_updateF vf vi pos e newe) where f (IxHasU1 m _) = HM.lookup u1 m f (IxHasU2 m _ _ _) = HM.lookup u1 m {-# INLINABLE adjustWithU1 #-} adjustWithU2 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u2 -> (a -> a) -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) adjustWithU2 i@(Indexed hb uis iis vf vi) u2 af = do u <- uis pos <- f u e <- HB.getPos hb pos let newe = af e return $ Indexed (HB.update newe pos hb) (_updateU uis pos e newe) (_updateI iis pos e newe) vf (_updateF vf vi pos e newe) where f (IxHasU1 _ _) = Nothing f (IxHasU2 _ _ m _) = HM.lookup u2 m {-# INLINABLE adjustWithU2 #-} update :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> Int -> a -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) update i p a = adjust i p (const a) {-# INLINABLE update #-} updateWithU1 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u1 -> a -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) updateWithU1 i u1 a = adjustWithU1 i u1 (const a) {-# INLINABLE updateWithU1 #-} updateWithU2 :: ( Eq a, Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 ) => Indexed a u1 u2 i1 i2 i3 i4 -> u2 -> a -> Maybe (Indexed a u1 u2 i1 i2 i3 i4) updateWithU2 i u2 a = adjustWithU2 i u2 (const a) {-# INLINABLE updateWithU2 #-} instance ( SafeCopy a, Eq a, SimpleIx a , Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3, Hashable i3 , Eq i4, Hashable i4 , u1 ~ U1 a, u2 ~ U2 a, i1 ~ I1 a, i2 ~ I2 a, i3 ~ I3 a, i4 ~ I4 a ) => SafeCopy (Indexed a u1 u2 i1 i2 i3 i4) where version = 0 kind = base putCopy (Indexed hb _ _ _ _) = contain $ safePut hb getCopy = contain $ fmap withData safeGet where withData :: (SafeCopy a, Eq a, SimpleIx a , Eq u1, Hashable u1, Eq u2, Hashable u2 , Eq i1, Hashable i1, Eq i2, Hashable i2, Eq i3 , Hashable i3, Eq i4, Hashable i4 , u1 ~ U1 a, u2 ~ U2 a, i1 ~ I1 a, i2 ~ I2 a, i3 ~ I3 a , i4 ~ I4 a) => HBlock a -> Indexed a u1 u2 i1 i2 i3 i4 withData h = Fold.foldl insert create h