packages feed

chimera 0.3.2.0 → 0.3.3.0

raw patch · 9 files changed

+446/−151 lines, 9 filesdep +primitivedep +transformersdep ~tastydep ~tasty-benchsetup-changedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: primitive, transformers

Dependency ranges changed: tasty, tasty-bench

API changes (from Hackage documentation)

+ Data.Chimera: fromListWithDef :: forall (v :: Type -> Type) a. Vector v a => a -> [a] -> Chimera v a
+ Data.Chimera: fromVectorWithDef :: Vector v a => a -> v a -> Chimera v a
+ Data.Chimera: interleave :: forall (v :: Type -> Type) a. Vector v a => Chimera v a -> Chimera v a -> Chimera v a
+ Data.Chimera: sliceSubvectors :: Vector v a => Int -> Int -> Chimera v a -> [v a]
+ Data.Chimera: tabulateFixM' :: forall m (v :: Type -> Type) a. (Monad m, Vector v a) => ((Word -> m a) -> Word -> m a) -> m (Chimera v a)
+ Data.Chimera: traverseSubvectors :: (Vector u a, Vector v b, Applicative m) => (u a -> m (v b)) -> Chimera u a -> m (Chimera v b)
+ Data.Chimera: unfoldr :: forall (v :: Type -> Type) b a. Vector v b => (a -> (b, a)) -> a -> Chimera v b
+ Data.Chimera: unfoldrM :: forall m (v :: Type -> Type) b a. (Monad m, Vector v b) => (a -> m (b, a)) -> a -> m (Chimera v b)
+ Data.Chimera: zipWithMSubvectors :: (Vector u a, Vector v b, Vector w c, Applicative m) => (u a -> v b -> m (w c)) -> Chimera u a -> Chimera v b -> m (Chimera w c)
+ Data.Chimera: zipWithSubvectors :: (Vector u a, Vector v b, Vector w c) => (u a -> v b -> w c) -> Chimera u a -> Chimera v b -> Chimera w c
- Data.Chimera: data Chimera v a
+ Data.Chimera: data Chimera (v :: Type -> Type) a
- Data.Chimera: index :: Vector v a => Chimera v a -> Word -> a
+ Data.Chimera: index :: forall (v :: Type -> Type) a. Vector v a => Chimera v a -> Word -> a
- Data.Chimera: iterate :: Vector v a => (a -> a) -> a -> Chimera v a
+ Data.Chimera: iterate :: forall (v :: Type -> Type) a. Vector v a => (a -> a) -> a -> Chimera v a
- Data.Chimera: iterateM :: forall m v a. (Monad m, Vector v a) => (a -> m a) -> a -> m (Chimera v a)
+ Data.Chimera: iterateM :: forall m (v :: Type -> Type) a. (Monad m, Vector v a) => (a -> m a) -> a -> m (Chimera v a)
- Data.Chimera: tabulate :: Vector v a => (Word -> a) -> Chimera v a
+ Data.Chimera: tabulate :: forall (v :: Type -> Type) a. Vector v a => (Word -> a) -> Chimera v a
- Data.Chimera: tabulateFix :: Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a
+ Data.Chimera: tabulateFix :: forall (v :: Type -> Type) a. Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a
- Data.Chimera: tabulateFix' :: Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a
+ Data.Chimera: tabulateFix' :: forall (v :: Type -> Type) a. Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a
- Data.Chimera: tabulateFixM :: forall m v a. (Monad m, Vector v a) => ((Word -> m a) -> Word -> m a) -> m (Chimera v a)
+ Data.Chimera: tabulateFixM :: forall m (v :: Type -> Type) a. (Monad m, Vector v a) => ((Word -> m a) -> Word -> m a) -> m (Chimera v a)
- Data.Chimera: tabulateM :: forall m v a. (Monad m, Vector v a) => (Word -> m a) -> m (Chimera v a)
+ Data.Chimera: tabulateM :: forall m (v :: Type -> Type) a. (Monad m, Vector v a) => (Word -> m a) -> m (Chimera v a)
- Data.Chimera: toList :: Vector v a => Chimera v a -> [a]
+ Data.Chimera: toList :: forall (v :: Type -> Type) a. Vector v a => Chimera v a -> [a]

Files

Data/Chimera.hs view
@@ -11,8 +11,10 @@ {-# LANGUAGE DeriveTraversable     #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE LambdaCase            #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TupleSections         #-} {-# LANGUAGE TypeApplications      #-} {-# LANGUAGE TypeFamilies          #-} @@ -31,8 +33,14 @@   , tabulateFix   , tabulateFix'   , iterate+  , unfoldr   , cycle+  , fromListWithDef+  , fromVectorWithDef +  -- * Manipulation+  , interleave+   -- * Elimination   , index   , toList@@ -41,20 +49,30 @@   -- $monadic   , tabulateM   , tabulateFixM+  , tabulateFixM'   , iterateM+  , unfoldrM    -- * Subvectors   -- $subvectors   , mapSubvectors+  , traverseSubvectors   , zipSubvectors+  , zipWithSubvectors+  , zipWithMSubvectors+  , sliceSubvectors   ) where  import Prelude hiding ((^), (*), div, fromIntegral, not, and, or, cycle, iterate, drop) import Control.Applicative import Control.Monad.Fix+import Control.Monad.Trans.Class+import qualified Control.Monad.Trans.State.Lazy as LazyState import Control.Monad.Zip import Data.Bits+import qualified Data.Foldable as F import Data.Functor.Identity+import qualified Data.Primitive.Array as A import qualified Data.Vector as V import qualified Data.Vector.Generic as G import qualified Data.Vector.Unboxed as U@@ -69,7 +87,6 @@ #endif #endif -import Data.Chimera.Compat import Data.Chimera.FromIntegral  -- $monadic@@ -114,52 +131,71 @@ -- > import Data.Bits -- > ch1 = tabulate (Bit . f1) -- > ch2 = tabulate (Bit . f2)--- > ch3 = zipSubvectors (zipBits (.&.)) ch1 ch2+-- > ch3 = zipWithSubvectors (zipBits (.&.)) ch1 ch2  -- | Lazy infinite streams with elements from @a@, -- backed by a 'G.Vector' @v@ (boxed, unboxed, storable, etc.). -- Use 'tabulate', 'tabulateFix', etc. to create a stream -- and 'index' to access its arbitrary elements -- in constant time.-newtype Chimera v a = Chimera { _unChimera :: V.Vector (v a) }-  deriving (Functor, Foldable, Traversable)+--+-- @since 0.2.0.0+newtype Chimera v a = Chimera { unChimera :: A.Array (v a) }+  deriving+  ( Functor     -- ^ @since 0.2.0.0+  , Foldable    -- ^ @since 0.2.0.0+  , Traversable -- ^ @since 0.2.0.0+  )  -- | Streams backed by boxed vectors.+--+-- @since 0.3.0.0 type VChimera = Chimera V.Vector  -- | Streams backed by unboxed vectors.+--+-- @since 0.3.0.0 type UChimera = Chimera U.Vector  -- | 'pure' creates a constant stream.+--+-- @since 0.2.0.0 instance Applicative (Chimera V.Vector) where-  pure   = tabulate   . const-  (<*>)  = zipSubvectors (<*>)+  pure a = Chimera $ A.arrayFromListN (bits + 1) $+    G.singleton a : map (\k -> G.replicate (1 `shiftL` k) a) [0 .. bits - 1]+  (<*>)  = zipWithSubvectors (<*>) #if __GLASGOW_HASKELL__ > 801-  liftA2 f = zipSubvectors (liftA2 f)+  liftA2 f = zipWithSubvectors (liftA2 f) #endif +-- | @since 0.3.1.0 instance Monad (Chimera V.Vector) where   m >>= f = tabulate $ \w -> index (f (index m w)) w +-- | @since 0.3.1.0 instance MonadFix (Chimera V.Vector) where   mfix = tabulate . mfix . fmap index +-- | @since 0.3.1.0 instance MonadZip (Chimera V.Vector) where-  mzip as bs = tabulate (\w -> (index as w, index bs w))-  mzipWith f as bs = tabulate $ \w -> f (index as w) (index bs w)+  mzip = zipWithSubvectors mzip+  mzipWith = zipWithSubvectors . mzipWith  #ifdef MIN_VERSION_mtl+-- | @since 0.3.1.0 instance MonadReader Word (Chimera V.Vector) where   ask = tabulate id   local = flip $ (tabulate .) . (.) . index #endif  #ifdef MIN_VERSION_distributive+-- | @since 0.3.1.0 instance Distributive (Chimera V.Vector) where   distribute = tabulate . flip (fmap . flip index)   collect f = tabulate . flip ((<$>) . (. f) . flip index)  #ifdef MIN_VERSION_adjunctions+-- | @since 0.3.1.0 instance Rep.Representable (Chimera V.Vector) where   type Rep (Chimera V.Vector) = Word   tabulate = tabulate@@ -168,7 +204,7 @@ #endif  bits :: Int-bits = fbs (0 :: Word)+bits = finiteBitSize (0 :: Word)  -- | Create a stream of values of a given function. -- Once created it can be accessed via 'index' or 'toList'.@@ -178,18 +214,24 @@ -- 81 -- >>> take 10 (toList ch) -- [0,1,4,9,16,25,36,49,64,81]+--+-- @since 0.2.0.0 tabulate :: G.Vector v a => (Word -> a) -> Chimera v a tabulate f = runIdentity $ tabulateM (pure . f) +-- | Similar to 'V.generateM', but for raw arrays.+generateArrayM :: Monad m => Int -> (Int -> m a) -> m (A.Array a)+generateArrayM n f = A.arrayFromListN n <$> traverse f [0..n - 1]+ -- | Monadic version of 'tabulate'.+--+-- @since 0.2.0.0 tabulateM-  :: forall m v a.-     (Monad m, G.Vector v a)+  :: (Monad m, G.Vector v a)   => (Word -> m a)   -> m (Chimera v a)-tabulateM f = Chimera <$> V.generateM (bits + 1) tabulateSubVector+tabulateM f = Chimera <$> generateArrayM (bits + 1) tabulateSubVector   where-    tabulateSubVector :: Int -> m (v a)     tabulateSubVector 0 = G.singleton <$> f 0     tabulateSubVector i = G.generateM ii (\j -> f (int2word (ii + j)))       where@@ -220,6 +262,8 @@ -- -- __Note__: Only recursive function calls with decreasing arguments are memoized. -- If full memoization is desired, use 'tabulateFix'' instead.+--+-- @since 0.2.0.0 tabulateFix :: G.Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a tabulateFix uf = runIdentity $ tabulateFixM ((pure .) . uf . (runIdentity .)) @@ -243,6 +287,8 @@ -- -- >>> maximumBy (comparing $ memoizeFix collatzF) [0..1000000] -- 56991483520+--+-- @since 0.3.2.0 tabulateFix' :: G.Vector v a => ((Word -> a) -> Word -> a) -> Chimera v a tabulateFix' uf = runIdentity $ tabulateFixM' ((pure .) . uf . (runIdentity .)) @@ -250,9 +296,10 @@ -- There are no particular guarantees about the order of recursive calls: -- they may be executed more than once or executed in different order. -- That said, monadic effects must be idempotent and commutative.+--+-- @since 0.2.0.0 tabulateFixM-  :: forall m v a.-     (Monad m, G.Vector v a)+  :: (Monad m, G.Vector v a)   => ((Word -> m a) -> Word -> m a)   -> m (Chimera v a) tabulateFixM = tabulateFixM_ Downwards@@ -260,6 +307,8 @@ {-# SPECIALIZE tabulateFixM :: G.Vector v a => ((Word -> Identity a) -> Word -> Identity a) -> Identity (Chimera v a) #-}  -- | Monadic version of 'tabulateFix''.+--+-- @since 0.3.3.0 tabulateFixM'   :: forall m v a.      (Monad m, G.Vector v a)@@ -267,7 +316,9 @@   -> m (Chimera v a) tabulateFixM' = tabulateFixM_ Full --- | Memoization strategy, only used by 'tabulateFixM_'.+{-# SPECIALIZE tabulateFixM' :: G.Vector v a => ((Word -> Identity a) -> Word -> Identity a) -> Identity (Chimera v a) #-}++-- | Memoization strategy, only used by @tabulateFixM_@. data Strategy = Full | Downwards  -- | Internal implementation for 'tabulateFixM' and 'tabulateFixM''.@@ -280,7 +331,7 @@ tabulateFixM_ strat f = result   where     result :: m (Chimera v a)-    result = Chimera <$> V.generateM (bits + 1) tabulateSubVector+    result = Chimera <$> generateArrayM (bits + 1) tabulateSubVector      tabulateSubVector :: Int -> m (v a)     tabulateSubVector 0 = G.singleton <$> case strat of@@ -303,43 +354,114 @@               Downwards -> f fixF k               Full      -> flip index k <$> result --- | 'iterate' @f@ @x@ returns an infinite stream--- of repeated applications of @f@ to @x@.+-- | 'iterate' @f@ @x@ returns an infinite stream, generated by+-- repeated applications of @f@ to @x@. -- -- >>> ch = iterate (+ 1) 0 :: UChimera Int -- >>> take 10 (toList ch) -- [0,1,2,3,4,5,6,7,8,9]+--+-- @since 0.3.0.0 iterate :: G.Vector v a => (a -> a) -> a -> Chimera v a iterate f = runIdentity . iterateM (pure . f) +-- | Similar to 'G.iterateNM'.+iterateListNM :: forall a m. Monad m => Int -> (a -> m a) -> a -> m [a]+iterateListNM n f = if n <= 0 then const (pure []) else go (n - 1)+  where+    go :: Int -> a -> m [a]+    go 0 s = pure [s]+    go k s = do+      fs <- f s+      (s :) <$> go (k - 1) fs+ -- | Monadic version of 'iterate'.-iterateM :: forall m v a. (Monad m, G.Vector v a) => (a -> m a) -> a -> m (Chimera v a)+--+-- @since 0.3.0.0+iterateM :: (Monad m, G.Vector v a) => (a -> m a) -> a -> m (Chimera v a) iterateM f seed = do   nextSeed <- f seed   let z = G.singleton seed-  zs <- V.iterateNM bits go (G.singleton nextSeed)-  pure $ Chimera $ z `V.cons` zs+  zs <- iterateListNM bits go (G.singleton nextSeed)+  pure $ Chimera $ A.fromListN (bits + 1) (z : zs)   where-    go :: v a -> m (v a)     go vec = do       nextSeed <- f (G.unsafeLast vec)       G.iterateNM (G.length vec `shiftL` 1) f nextSeed  {-# SPECIALIZE iterateM :: G.Vector v a => (a -> Identity a) -> a -> Identity (Chimera v a) #-} +-- | 'unfoldr' @f@ @x@ returns an infinite stream, generated by+-- repeated applications of @f@ to @x@, similar to `Data.List.unfoldr`.+--+-- >>> ch = unfoldr (\acc -> (acc * acc, acc + 1)) 0 :: UChimera Int+-- >>> take 10 (toList ch)+-- [0,1,4,9,16,25,36,49,64,81]+--+-- @since 0.3.3.0+unfoldr :: G.Vector v b => (a -> (b, a)) -> a -> Chimera v b+unfoldr f = runIdentity . unfoldrM (pure . f)++-- | This is not quite satisfactory, see https://github.com/haskell/vector/issues/447+unfoldrExactVecNM :: forall m a b v. (Monad m, G.Vector v b) => Int -> (a -> m (b, a)) -> a -> m (v b, a)+unfoldrExactVecNM n f s = flip LazyState.evalStateT s $ do+  vec <- G.replicateM n f'+  seed <- LazyState.get+  pure (vec, seed)+  where+    f' :: LazyState.StateT a m b+    f' = do+      seed <- LazyState.get+      (value, newSeed) <- lift (f seed)+      LazyState.put newSeed+      pure value++-- | Monadic version of 'unfoldr'.+--+-- @since 0.3.3.0+unfoldrM :: (Monad m, G.Vector v b) => (a -> m (b, a)) -> a -> m (Chimera v b)+unfoldrM f seed = do+  let go n s = if n >= bits then pure [] else do+        (vec, s') <- unfoldrExactVecNM (1 `shiftL` n) f s+        rest <- go (n + 1) s'+        pure $ vec : rest+  (z, seed') <- unfoldrExactVecNM 1 f seed+  zs <- go 0 seed'+  pure $ Chimera $ A.fromListN (bits + 1) (z : zs)++interleaveVec :: G.Vector v a => v a -> v a -> v a+interleaveVec as bs = G.generate (G.length as `shiftL` 1)+  (\n -> (if even n then as else bs) G.! (n `shiftR` 1))++-- | Intertleave two streams, sourcing even elements from the first one+-- and odd elements from the second one.+--+-- >>> ch = interleave (tabulate id) (tabulate (+ 100)) :: UChimera Word+-- >>> take 10 (toList ch)+-- [0,100,1,101,2,102,3,103,4,104]+--+-- @since 0.3.3.0+interleave :: G.Vector v a => Chimera v a -> Chimera v a -> Chimera v a+interleave (Chimera as) (Chimera bs) = Chimera $ A.arrayFromListN (bits + 1) vecs+  where+    vecs = A.indexArray as 0 : A.indexArray bs 0 :+      map (\i -> interleaveVec (A.indexArray as i) (A.indexArray bs i)) [1 .. bits - 1]+ -- | Index a stream in a constant time. -- -- >>> ch = tabulate (^ 2) :: UChimera Word -- >>> index ch 9 -- 81+--+-- @since 0.2.0.0 index :: G.Vector v a => Chimera v a -> Word -> a index (Chimera vs) i =-  (vs `V.unsafeIndex` (fbs i - lz))+  (vs `A.indexArray` (bits - lz))   `G.unsafeIndex`-  word2int (i .&. complement ((1 `shiftL` (fbs i - 1)) `unsafeShiftR` lz))+  word2int (i .&. complement ((1 `shiftL` (bits - 1)) `unsafeShiftR` lz))   where     lz :: Int-    !lz = word2int (clz i)+    !lz = countLeadingZeros i {-# INLINE index #-}  -- | Convert a stream to an infinite list.@@ -347,15 +469,81 @@ -- >>> ch = tabulate (^ 2) :: UChimera Word -- >>> take 10 (toList ch) -- [0,1,4,9,16,25,36,49,64,81]+--+-- @since 0.3.0.0 toList :: G.Vector v a => Chimera v a -> [a] toList (Chimera vs) = foldMap G.toList vs --- | Return an infinite repetion of a given vector.+measureOff :: Int -> [a] -> Either Int ([a], [a])+measureOff n+  | n <= 0 = Right . ([], )+  | otherwise = go n+  where+    go m [] = Left m+    go 1 (x : xs) = Right ([x], xs)+    go m (x : xs) = case go (m - 1) xs of+      l@Left{} -> l+      Right (xs', xs'') -> Right (x : xs', xs'')++measureOffVector :: G.Vector v a => Int -> v a -> Either Int (v a, v a)+measureOffVector n xs+  | n <= l = Right (G.splitAt n xs)+  | otherwise = Left (n - l)+  where+    l = G.length xs++-- | Create a stream of values from a given prefix, followed by default value+-- afterwards.+--+-- @since 0.3.3.0+fromListWithDef+  :: G.Vector v a+  => a   -- ^ Default value+  -> [a] -- ^ Prefix+  -> Chimera v a+fromListWithDef a = Chimera . A.fromListN (bits + 1) . go0+  where+    go0 = \case+      [] -> G.singleton a : map (\k -> G.replicate (1 `shiftL` k) a) [0 .. bits - 1]+      x : xs -> G.singleton x : go 0 xs++    go k xs = case measureOff kk xs of+      Left l -> G.fromListN kk (xs ++ replicate l a) :+        map (\n -> G.replicate (1 `shiftL` n) a) [k + 1 .. bits - 1]+      Right (ys, zs) -> G.fromListN kk ys : go (k + 1) zs+      where+        kk = 1 `shiftL` k++-- | Create a stream of values from a given prefix, followed by default value+-- afterwards.+--+-- @since 0.3.3.0+fromVectorWithDef+  :: G.Vector v a+  => a   -- ^ Default value+  -> v a -- ^ Prefix+  -> Chimera v a+fromVectorWithDef a = Chimera . A.fromListN (bits + 1) . go0+  where+    go0 xs = case G.uncons xs of+      Nothing -> G.singleton a : map (\k -> G.replicate (1 `shiftL` k) a) [0 .. bits - 1]+      Just (y, ys) -> G.singleton y : go 0 ys++    go k xs = case measureOffVector kk xs of+      Left l -> (xs G.++ G.replicate l a) :+        map (\n -> G.replicate (1 `shiftL` n) a) [k + 1 .. bits - 1]+      Right (ys, zs) -> ys : go (k + 1) zs+      where+        kk = 1 `shiftL` k++-- | Return an infinite repetition of a given vector. -- Throw an error on an empty vector. -- -- >>> ch = cycle (Data.Vector.fromList [4, 2]) :: VChimera Int -- >>> take 10 (toList ch) -- [4,2,4,2,4,2,4,2,4,2]+--+-- @since 0.3.0.0 cycle :: G.Vector v a => v a -> Chimera v a cycle vec = case l of   0 -> error "Data.Chimera.cycle: empty list"@@ -372,6 +560,8 @@ -- 'index' ('tabulate' @f@ :: 'UChimera' @a@). -- -- prop> memoize f n = f n+--+-- @since 0.3.0.0 memoize :: (Word -> a) -> (Word -> a) memoize = index @V.Vector . tabulate @@ -410,22 +600,98 @@ -- >>> collatzF f n = if n <= 1 then 0 else 1 + f (if even n then n `quot` 2 else 3 * n + 1) -- >>> memoizeFix collatzF 27 -- 111+--+-- @since 0.3.0.0 memoizeFix :: ((Word -> a) -> Word -> a) -> (Word -> a) memoizeFix = index @V.Vector . tabulateFix  -- | Map subvectors of a stream, using a given length-preserving function.+--+-- @since 0.3.0.0 mapSubvectors   :: (G.Vector u a, G.Vector v b)   => (u a -> v b)   -> Chimera u a   -> Chimera v b-mapSubvectors f (Chimera bs) = Chimera (fmap f bs)+mapSubvectors f = runIdentity . traverseSubvectors (pure . f) +-- | Traverse subvectors of a stream, using a given length-preserving function.+--+-- Be careful, because similar to 'tabulateM', only lazy monadic effects can+-- be executed in a finite time: lazy state monad is fine, but strict one is+-- not.+--+-- @since 0.3.3.0+traverseSubvectors+  :: (G.Vector u a, G.Vector v b, Applicative m)+  => (u a -> m (v b))+  -> Chimera u a+  -> m (Chimera v b)+traverseSubvectors f (Chimera bs) = Chimera <$> traverse safeF bs+  where+    -- Computing vector length is cheap, so let's check that @f@ preserves length.+    safeF x = (\fx -> if G.length x == G.length fx then fx else+        error "traverseSubvectors: the function is not length-preserving") <$> f x++{-# SPECIALIZE traverseSubvectors :: (G.Vector u a, G.Vector v b) => (u a -> Identity (v b)) -> Chimera u a -> Identity (Chimera v b)  #-}++-- | @since 0.3.0.0+zipSubvectors :: (G.Vector u a, G.Vector v b, G.Vector w c) => (u a -> v b -> w c) -> Chimera u a -> Chimera v b -> Chimera w c+zipSubvectors = zipWithSubvectors+{-# DEPRECATED zipSubvectors "Use zipWithSubvectors instead" #-}+ -- | Zip subvectors from two streams, using a given length-preserving function.-zipSubvectors+--+-- @since 0.3.3.0+zipWithSubvectors   :: (G.Vector u a, G.Vector v b, G.Vector w c)   => (u a -> v b -> w c)   -> Chimera u a   -> Chimera v b   -> Chimera w c-zipSubvectors f (Chimera bs1) (Chimera bs2) = Chimera (mzipWith f bs1 bs2)+zipWithSubvectors f = (runIdentity .) . zipWithMSubvectors ((pure .) . f)++-- | Zip subvectors from two streams, using a given monadic length-preserving function.+-- Caveats for 'tabulateM' and 'traverseSubvectors' apply.+--+-- @since 0.3.3.0+zipWithMSubvectors+  :: (G.Vector u a, G.Vector v b, G.Vector w c, Applicative m)+  => (u a -> v b -> m (w c))+  -> Chimera u a+  -> Chimera v b+  -> m (Chimera w c)+zipWithMSubvectors f (Chimera bs1) (Chimera bs2) = Chimera <$> sequenceA (mzipWith safeF bs1 bs2)+  where+    -- Computing vector length is cheap, so let's check that @f@ preserves length.+    safeF x y = (\fx -> if G.length x == G.length fx then fx else+        error "traverseSubvectors: the function is not length-preserving") <$> f x y++{-# SPECIALIZE zipWithMSubvectors :: (G.Vector u a, G.Vector v b, G.Vector w c) => (u a -> v b -> Identity (w c)) -> Chimera u a -> Chimera v b -> Identity (Chimera w c) #-}++-- | Take a slice of 'Chimera', represented as a list on consecutive subvectors.+--+-- @since 0.3.3.0+sliceSubvectors+  :: G.Vector v a+  => Int -- ^ How many initial elements to drop?+  -> Int -- ^ How many subsequent elements to take?+  -> Chimera v a+  -> [v a]+sliceSubvectors off len = doTake len . doDrop off . F.toList . unChimera+  where+    doTake !_ [] = []+    doTake n (x : xs)+      | n <= 0 = []+      | n >= l = x : doTake (n - l) xs+      | otherwise = [G.take n x]+      where+        l = G.length x++    doDrop !_ [] = []+    doDrop n (x : xs)+      | n <= 0 = x : xs+      | l <= n = doDrop (n - l) xs+      | otherwise = G.drop n x : xs+      where+        l = G.length x
− Data/Chimera/Compat.hs
@@ -1,56 +0,0 @@--- |--- Module:      Data.Chimera.Compat--- Copyright:   (c) 2017 Andrew Lelechenko--- Licence:     MIT--- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>--{-# LANGUAGE CPP       #-}-{-# LANGUAGE MagicHash #-}--{-# OPTIONS_GHC -fno-warn-unused-imports #-}-{-# OPTIONS_HADDOCK hide #-}--module Data.Chimera.Compat-  ( clz-  , fbs-  ) where--import Data.Bits-import GHC.Exts-import Unsafe.Coerce--#if __GLASGOW_HASKELL__ > 709--clz :: Word -> Word-clz (W# w#) = W# (clz# w#)-{-# INLINE clz #-}--#else--int2word :: Int -> Word-int2word = unsafeCoerce--clz :: Word -> Word-clz w = int2word $ case setBits of-  []      -> sz-  (s : _) -> sz - s - 1-  where-    sz = fbs w-    setBits = map fst $ filter snd $ map (\i -> (i, testBit w i)) [sz - 1, sz - 2 .. 0]-{-# INLINE clz #-}--#endif--#if __GLASGOW_HASKELL__ > 707--fbs :: Word -> Int-fbs = finiteBitSize-{-# INLINE fbs #-}--#else--fbs :: Word -> Int-fbs = bitSize-{-# INLINE fbs #-}--#endif
Data/Chimera/ContinuousMapping.hs view
@@ -64,37 +64,26 @@ -- -- > cast :: (Int -> Int -> Bool) -> (Word -> Bool) -- > cast f = \n -> let (x, y) = fromZCurve n in--- >  f (word2int x) (word2int y)+-- >  f (wordToInt x) (wordToInt y) -- -- and then back: -- -- > uncast :: (Word -> Bool) -> (Int -> Int -> Bool)--- > uncast g = \x y -> g (toZCurve (int2word x) (int2word y))+-- > uncast g = \x y -> g (toZCurve (intToWord x) (intToWord y)) -- -{-# LANGUAGE CPP #-}--#include "MachDeps.h"- module Data.Chimera.ContinuousMapping   ( intToWord   , wordToInt-#if WORD_SIZE_IN_BITS == 64   , toZCurve   , fromZCurve   , toZCurve3   , fromZCurve3-#endif   ) where  import Data.Bits-import Unsafe.Coerce--word2int :: Word -> Int-word2int = unsafeCoerce--int2word :: Int -> Word-int2word = unsafeCoerce+import Data.Chimera.FromIntegral+import Data.Word  -- | Total map, which satisfies --@@ -106,19 +95,23 @@ -- -- >>> map intToWord [-5..5] -- [9,7,5,3,1,0,2,4,6,8,10]+--+-- @since 0.2.0.0 intToWord :: Int -> Word-intToWord i-  | i >= 0    = int2word        i `shiftL` 1-  | otherwise = int2word (-1 - i) `shiftL` 1 + 1+intToWord i = (if sign == 0 then id else complement) (int2word i) `shiftL` 1 + sign+  where+    sign = int2word i `shiftR` (finiteBitSize i - 1)+{-# INLINE intToWord #-}  -- | Inverse for 'intToWord'. -- -- >>> map wordToInt [0..10] -- [0,-1,1,-2,2,-3,3,-4,4,-5,5]+--+-- @since 0.2.0.0 wordToInt :: Word -> Int-wordToInt w-  | even w    =         word2int (w `shiftR` 1)-  | otherwise = negate (word2int (w `shiftR` 1)) - 1+wordToInt w = word2int $ (if w .&. 1 == 0 then id else complement) (w `shiftR` 1)+{-# INLINE wordToInt #-}  -- | Total map from plain to line, continuous almost everywhere. -- See <https://en.wikipedia.org/wiki/Z-order_curve Z-order curve>.@@ -127,6 +120,8 @@ -- -- >>> [ toZCurve x y | x <- [0..3], y <- [0..3] ] -- [0,2,8,10,1,3,9,11,4,6,12,14,5,7,13,15]+--+-- @since 0.2.0.0 toZCurve :: Word -> Word -> Word toZCurve x y = part1by1 y `shiftL` 1 .|. part1by1 x @@ -135,6 +130,8 @@ -- -- >>> map fromZCurve [0..15] -- [(0,0),(1,0),(0,1),(1,1),(2,0),(3,0),(2,1),(3,1),(0,2),(1,2),(0,3),(1,3),(2,2),(3,2),(2,3),(3,3)]+--+-- @since 0.2.0.0 fromZCurve :: Word -> (Word, Word) fromZCurve z = (compact1by1 z, compact1by1 (z `shiftR` 1)) @@ -146,6 +143,8 @@ -- >>> [ toZCurve3 x y z | x <- [0..3], y <- [0..3], z <- [0..3] ] -- [0,4,32,36,2,6,34,38,16,20,48,52,18,22,50,54,1,5,33,37,3,7,35,39,17,21,49,53,19,23,51,55, -- 8,12,40,44,10,14,42,46,24,28,56,60,26,30,58,62,9,13,41,45,11,15,43,47,25,29,57,61,27,31,59,63]+--+-- @since 0.2.0.0 toZCurve3 :: Word -> Word -> Word -> Word toZCurve3 x y z = part1by2 z `shiftL` 2 .|. part1by2 y `shiftL` 1 .|. part1by2 x @@ -157,14 +156,16 @@ --  (0,2,0),(1,2,0),(0,3,0),(1,3,0),(0,2,1),(1,2,1),(0,3,1),(1,3,1),(2,2,0),(3,2,0),(2,3,0),(3,3,0),(2,2,1),(3,2,1),(2,3,1),(3,3,1), --  (0,0,2),(1,0,2),(0,1,2),(1,1,2),(0,0,3),(1,0,3),(0,1,3),(1,1,3),(2,0,2),(3,0,2),(2,1,2),(3,1,2),(2,0,3),(3,0,3),(2,1,3),(3,1,3), --  (0,2,2),(1,2,2),(0,3,2),(1,3,2),(0,2,3),(1,2,3),(0,3,3),(1,3,3),(2,2,2),(3,2,2),(2,3,2),(3,3,2),(2,2,3),(3,2,3),(2,3,3),(3,3,3)]+--+-- @since 0.2.0.0 fromZCurve3 :: Word -> (Word, Word, Word) fromZCurve3 z = (compact1by2 z, compact1by2 (z `shiftR` 1), compact1by2 (z `shiftR` 2))  -- Inspired by https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ part1by1 :: Word -> Word-part1by1 x = x5+part1by1 x = fromIntegral (x5 :: Word64)   where-    x0 = x                           .&. 0x00000000ffffffff+    x0 = fromIntegral x              .&. 0x00000000ffffffff     x1 = (x0 `xor` (x0 `shiftL` 16)) .&. 0x0000ffff0000ffff     x2 = (x1 `xor` (x1 `shiftL`  8)) .&. 0x00ff00ff00ff00ff     x3 = (x2 `xor` (x2 `shiftL`  4)) .&. 0x0f0f0f0f0f0f0f0f@@ -173,9 +174,9 @@  -- Inspired by https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ part1by2 :: Word -> Word-part1by2 x = x5+part1by2 x = fromIntegral (x5 :: Word64)   where-    x0 = x                           .&. 0x00000000ffffffff+    x0 = fromIntegral x              .&. 0x00000000ffffffff     x1 = (x0 `xor` (x0 `shiftL` 32)) .&. 0xffff00000000ffff     x2 = (x1 `xor` (x1 `shiftL` 16)) .&. 0x00ff0000ff0000ff     x3 = (x2 `xor` (x2 `shiftL`  8)) .&. 0xf00f00f00f00f00f@@ -184,9 +185,9 @@  -- Inspired by https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ compact1by1 :: Word -> Word-compact1by1 x = x5+compact1by1 x = fromIntegral (x5 :: Word64)   where-    x0 = x                           .&. 0x5555555555555555+    x0 = fromIntegral x              .&. 0x5555555555555555     x1 = (x0 `xor` (x0 `shiftR`  1)) .&. 0x3333333333333333     x2 = (x1 `xor` (x1 `shiftR`  2)) .&. 0x0f0f0f0f0f0f0f0f     x3 = (x2 `xor` (x2 `shiftR`  4)) .&. 0x00ff00ff00ff00ff@@ -195,9 +196,9 @@  -- Inspired by https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/ compact1by2 :: Word -> Word-compact1by2 x = x5+compact1by2 x = fromIntegral (x5 :: Word64)   where-    x0 = x                           .&. 0x1249249249249249+    x0 = fromIntegral x              .&. 0x1249249249249249     x1 = (x0 `xor` (x0 `shiftR`  2)) .&. 0x30c30c30c30c30c3     x2 = (x1 `xor` (x1 `shiftR`  4)) .&. 0xf00f00f00f00f00f     x3 = (x2 `xor` (x2 `shiftR`  8)) .&. 0x00ff0000ff0000ff
Data/Chimera/WheelMapping.hs view
@@ -82,15 +82,16 @@   ) where  import Data.Bits-import Data.Chimera.Compat import GHC.Exts  bits :: Int-bits = fbs (0 :: Word)+bits = finiteBitSize (0 :: Word)  -- | Left inverse for 'fromWheel2'. Monotonically non-decreasing function. -- -- prop> toWheel2 . fromWheel2 == id+--+-- @since 0.2.0.0 toWheel2 :: Word -> Word toWheel2 i = i `shiftR` 1 {-# INLINE toWheel2 #-}@@ -102,6 +103,8 @@ -- -- >>> map fromWheel2 [0..9] -- [1,3,5,7,9,11,13,15,17,19]+--+-- @since 0.2.0.0 fromWheel2 :: Word -> Word fromWheel2 i = i `shiftL` 1 + 1 {-# INLINE fromWheel2 #-}@@ -109,6 +112,8 @@ -- | Left inverse for 'fromWheel6'. Monotonically non-decreasing function. -- -- prop> toWheel6 . fromWheel6 == id+--+-- @since 0.2.0.0 toWheel6 :: Word -> Word toWheel6 i@(W# i#) = case bits of   64 -> W# z1# `shiftR` 1@@ -126,6 +131,8 @@ -- -- >>> map fromWheel6 [0..9] -- [1,5,7,11,13,17,19,23,25,29]+--+-- @since 0.2.0.0 fromWheel6 :: Word -> Word fromWheel6 i = i `shiftL` 1 + i + (i .&. 1) + 1 {-# INLINE fromWheel6 #-}@@ -133,6 +140,8 @@ -- | Left inverse for 'fromWheel30'. Monotonically non-decreasing function. -- -- prop> toWheel30 . fromWheel30 == id+--+-- @since 0.2.0.0 toWheel30 :: Word -> Word toWheel30 i@(W# i#) = q `shiftL` 3 + (r + r `shiftR` 4) `shiftR` 2   where@@ -154,6 +163,8 @@ -- -- >>> map fromWheel30 [0..9] -- [1,7,11,13,17,19,23,29,31,37]+--+-- @since 0.2.0.0 fromWheel30 :: Word -> Word fromWheel30 i = ((i `shiftL` 2 - i `shiftR` 2) .|. 1)               + ((i `shiftL` 1 - i `shiftR` 1) .&. 2)@@ -162,6 +173,8 @@ -- | Left inverse for 'fromWheel210'. Monotonically non-decreasing function. -- -- prop> toWheel210 . fromWheel210 == id+--+-- @since 0.2.0.0 toWheel210 :: Word -> Word toWheel210 i@(W# i#) = q `shiftL` 5 + q `shiftL` 4 + W# tableEl#   where@@ -193,6 +206,8 @@ -- -- >>> map fromWheel210 [0..9] -- [1,11,13,17,19,23,29,31,37,41]+--+-- @since 0.2.0.0 fromWheel210 :: Word -> Word fromWheel210 i@(W# i#) = q * 210 + W# tableEl#   where
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
bench/Bench.hs view
@@ -3,8 +3,10 @@ module Main where  import Control.Monad.State (evalState, put, get)+import Data.Bits import Data.Chimera import Test.Tasty.Bench+import Test.Tasty.Patterns.Printer import System.Random  #ifdef MIN_VERSION_ral@@ -12,17 +14,25 @@ #endif  sizes :: Num a => [a]-sizes = [100, 200, 500, 1000]+sizes = [7, 8, 9, 10]  main :: IO ()-main = defaultMain $ (: []) $ bgroup "read"-  [ bgroup "Chimera" (map benchReadChimera sizes)-  , bgroup "List"    (map benchReadList    sizes)+main = defaultMain $ (: []) $ mapLeafBenchmarks addCompare $ bgroup "read"+  [ bgroup chimeraBenchName (map benchReadChimera sizes)+  , bgroup "List"           (map benchReadList    sizes) #ifdef MIN_VERSION_ral-  , bgroup "RAL"     (map benchReadRAL     sizes)+  , bgroup "RAL"            (map benchReadRAL     sizes) #endif   ] +chimeraBenchName :: String+chimeraBenchName = "Chimera"++addCompare :: ([String] -> Benchmark -> Benchmark)+addCompare (size : name : path)+  | name /= "Chimera" = bcompare (printAwkExpr (locateBenchmark (size : chimeraBenchName : path)))+addCompare _ = id+ randomChimera :: UChimera Int randomChimera = flip evalState (mkStdGen 42) $ tabulateM $ const $ do   g <- get@@ -35,7 +45,7 @@  #ifdef MIN_VERSION_ral randomRAL :: RAL.RAList Int-randomRAL = RAL.fromList $ take (maximum sizes) $ randoms (mkStdGen 42)+randomRAL = RAL.fromList $ take (1 `shiftL` (maximum sizes)) $ randoms (mkStdGen 42) #endif  randomIndicesWord :: [Word]@@ -44,27 +54,31 @@ randomIndicesInt :: [Int] randomIndicesInt = randoms (mkStdGen 42) -benchReadChimera :: Word -> Benchmark-benchReadChimera n+benchReadChimera :: Int -> Benchmark+benchReadChimera k   = bench (show n)   $ nf (sum . map (index randomChimera))-  $ map (`rem` n)+  $ map (.&. (n - 1))   $ take (fromIntegral n) randomIndicesWord+  where+    n = 1 `shiftL` k  benchReadList :: Int -> Benchmark-benchReadList n-  = bcompare ("$NF == \"" ++ show n ++ "\" && $(NF-1) == \"Chimera\"")-  $ bench (show n)+benchReadList k+  = bench (show n)   $ nf (sum . map (randomList !!))-  $ map (`mod` n)+  $ map (.&. (n - 1))   $ take n randomIndicesInt+  where+    n = 1 `shiftL` k  #ifdef MIN_VERSION_ral benchReadRAL :: Int -> Benchmark-benchReadRAL n-  = bcompare ("$NF == \"" ++ show n ++ "\" && $(NF-1) == \"Chimera\"")-  $ bench (show n)+benchReadRAL k+  = bench (show n)   $ nf (sum . map (randomRAL RAL.!))-  $ map (`mod` n)+  $ map (.&. (n - 1))   $ take n randomIndicesInt+  where+    n = 1 `shiftL` k #endif
changelog.md view
@@ -1,3 +1,11 @@+# 0.3.3.0++* Add `fromListWithDef`, `fromVectorWithDef`, `interleave`.+* Add `unfoldr` and `unfoldrM`.+* Export `tabulateFixM'`.+* Add `sliceSubvectors`, `traverseSubvectors`, `zipWithSubvectors` and `zipWithMSubvectors`.+* Deprecate `zipSubvectors` in favor of `zipWithSubvectors`.+ # 0.3.2.0  * Implement `tabulateFix'`.@@ -17,6 +25,7 @@ * Implement `mapSubvectors` and `zipSubvectors` * Make boxed `tabulateFix` even lazier. * Speed up `Data.Chimera.WheelMapping`.+* Remove `mapWithKey`, `traverseWithKey`, `zipWithKey`, `zipWithKeyM`.  # 0.2.0.0 
chimera.cabal view
@@ -1,5 +1,5 @@ name: chimera-version: 0.3.2.0+version: 0.3.3.0 cabal-version: 2.0 build-type: Simple license: BSD3@@ -13,7 +13,7 @@ extra-source-files:   README.md   changelog.md-tested-with: GHC==9.0.1, GHC==8.10.5, GHC==8.8.4, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2+tested-with: GHC==9.4.4, GHC==9.2.5, GHC==9.0.2, GHC==8.10.7, GHC==8.8.4, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2 description:   There are plenty of memoizing libraries on Hackage, but they   usually fall into two categories:@@ -49,7 +49,7 @@   default: True  library-  build-depends: base >=4.9 && <5, vector+  build-depends: base >=4.9 && <5, primitive, transformers, vector   if flag(representable)     build-depends: adjunctions, distributive, mtl   exposed-modules:@@ -57,7 +57,6 @@     Data.Chimera.ContinuousMapping     Data.Chimera.WheelMapping   other-modules:-    Data.Chimera.Compat     Data.Chimera.FromIntegral   default-language: Haskell2010   ghc-options: -Wall -Wcompat@@ -84,7 +83,8 @@     chimera,     mtl,     random,-    tasty-bench >= 0.2.4+    tasty >= 1.4.2,+    tasty-bench >= 0.3.2   type: exitcode-stdio-1.0   main-is: Bench.hs   default-language: Haskell2010
test/Test.hs view
@@ -12,11 +12,12 @@ import Data.Bits import Data.Foldable import Data.Function (fix)+import qualified Data.List as L import qualified Data.Vector.Generic as G-import qualified Data.Vector.Unboxed as U  import Data.Chimera.ContinuousMapping import Data.Chimera.WheelMapping+import Data.Chimera (UChimera, VChimera) import qualified Data.Chimera as Ch  instance (G.Vector v a, Arbitrary a) => Arbitrary (Ch.Chimera v a) where@@ -72,40 +73,87 @@   [ QC.testProperty "index . tabulate = id" $     \(Fun _ (f :: Word -> Bool)) ix ->       let jx = ix `mod` 65536 in-        f jx === Ch.index (Ch.tabulate f :: Ch.Chimera U.Vector Bool) jx+        f jx === Ch.index (Ch.tabulate f :: UChimera Bool) jx +  , QC.testProperty "memoize = id" $+    \(Fun _ (f :: Word -> Bool)) ix ->+      let jx = ix `mod` 65536 in+        f jx === Ch.memoize f jx+   , QC.testProperty "index . tabulateFix = fix" $     \(Fun _ g) ix ->       let jx = ix `mod` 65536 in         let f = mkUnfix g in-          fix f jx === Ch.index (Ch.tabulateFix f :: Ch.Chimera U.Vector Bool) jx+          fix f jx === Ch.index (Ch.tabulateFix f :: UChimera Bool) jx    , QC.testProperty "index . tabulateFix' = fix" $     \(Fun _ g) ix ->       let jx = ix `mod` 65536 in         let f = mkUnfix g in-          fix f jx === Ch.index (Ch.tabulateFix' f :: Ch.Chimera U.Vector Bool) jx+          fix f jx === Ch.index (Ch.tabulateFix' f :: UChimera Bool) jx +  , QC.testProperty "memoizeFix = fix" $+    \(Fun _ g) ix ->+      let jx = ix `mod` 65536 in+        let f = mkUnfix g in+          fix f jx === Ch.memoizeFix f jx+   , QC.testProperty "iterate" $     \(Fun _ (f :: Word -> Word)) seed ix ->       let jx = ix `mod` 65536 in-        iterate f seed !! fromIntegral jx === Ch.index (Ch.iterate f seed :: Ch.Chimera U.Vector Word) jx+        iterate f seed !! fromIntegral jx === Ch.index (Ch.iterate f seed :: UChimera Word) jx +  , QC.testProperty "unfoldr" $+    \(Fun _ (f :: Word -> (Int, Word))) seed ix ->+      let jx = ix `mod` 65536 in+        L.unfoldr (Just . f) seed !! fromIntegral jx === Ch.index (Ch.unfoldr f seed :: UChimera Int) jx++  , QC.testProperty "interleave" $+    \(Fun _ (f :: Word -> Bool)) (Fun _ (g :: Word -> Bool)) ix ->+      let jx = ix `mod` 65536 in+        (if even jx then f else g) (jx `quot` 2) === Ch.index (Ch.interleave (Ch.tabulate f) (Ch.tabulate g) :: UChimera Bool) jx++  , QC.testProperty "pure" $+    \x ix ->+      let jx = ix `mod` 65536 in+        x === Ch.index (pure x :: VChimera Word) jx+   , QC.testProperty "cycle" $     \xs ix -> not (null xs) ==>       let jx = ix `mod` 65536 in-        let vs = U.fromList xs :: U.Vector Bool in-          vs U.! (fromIntegral jx `mod` U.length vs) === Ch.index (Ch.cycle vs) jx+        let vs = G.fromList xs in+          vs G.! (fromIntegral jx `mod` G.length vs) === Ch.index (Ch.cycle vs :: UChimera Bool) jx +  , QC.testProperty "toList" $+    \x xs -> xs === take (length xs) (Ch.toList (Ch.fromListWithDef x xs :: UChimera Bool))++  , QC.testProperty "fromListWithDef" $+    \x xs ix ->+      let jx = ix `mod` 65536 in+        (if fromIntegral jx < length xs then xs !! fromIntegral jx else x) ===+          Ch.index (Ch.fromListWithDef x xs :: UChimera Bool) jx++  , QC.testProperty "fromVectorWithDef" $+    \x xs ix ->+      let jx = ix `mod` 65536 in+        let vs = G.fromList xs in+          (if fromIntegral jx < length xs then vs G.! fromIntegral jx else x) ===+            Ch.index (Ch.fromVectorWithDef x vs :: UChimera Bool) jx+   , QC.testProperty "mapWithKey" $     \(Blind bs) (Fun _ (g :: Word -> Word)) ix ->       let jx = ix `mod` 65536 in-        g (Ch.index bs jx) === Ch.index (Ch.mapSubvectors (G.map g) bs :: Ch.Chimera U.Vector Word) jx+        g (Ch.index bs jx) === Ch.index (Ch.mapSubvectors (G.map g) bs :: UChimera Word) jx    , QC.testProperty "zipWithKey" $     \(Blind bs1) (Blind bs2) (Fun _ (g :: (Word, Word) -> Word)) ix ->       let jx = ix `mod` 65536 in-        g (Ch.index bs1 jx, Ch.index bs2 jx) === Ch.index (Ch.zipSubvectors (G.zipWith (curry g)) bs1 bs2 :: Ch.Chimera U.Vector Word) jx+        g (Ch.index bs1 jx, Ch.index bs2 jx) === Ch.index (Ch.zipWithSubvectors (G.zipWith (curry g)) bs1 bs2 :: UChimera Word) jx++  , QC.testProperty "sliceSubvectors" $+    \x xs ix ->+      let vs = G.fromList xs in+        fold (Ch.sliceSubvectors ix (G.length vs - max 0 ix) (Ch.fromVectorWithDef x vs :: UChimera Bool)) === G.drop ix vs   ]  -------------------------------------------------------------------------------