chimera-0.4.1.0: src/Data/Chimera.hs
-- |
-- Module: Data.Chimera
-- Copyright: (c) 2018-2019 Andrew Lelechenko
-- Licence: BSD3
-- Maintainer: Andrew Lelechenko <andrew.lelechenko@gmail.com>
--
-- Lazy infinite streams with O(1) indexing.
module Data.Chimera (
-- * Memoization
memoize,
memoizeFix,
-- * Chimera
Chimera,
VChimera,
UChimera,
-- * Construction
tabulate,
tabulateFix,
tabulateFix',
iterate,
iterateWithIndex,
unfoldr,
cycle,
fromListWithDef,
fromVectorWithDef,
fromInfinite,
-- * Manipulation
interleave,
prependVector,
-- * Elimination
index,
foldr,
toList,
toInfinite,
-- * Monadic construction
-- $monadic
tabulateM,
tabulateFixM,
tabulateFixM',
iterateM,
iterateWithIndexM,
unfoldrM,
-- * Subvectors
-- $subvectors
mapSubvectors,
imapSubvectors,
traverseSubvectors,
zipWithSubvectors,
zipWithMSubvectors,
sliceSubvectors,
) where
import Prelude hiding (Applicative (..), and, cycle, div, drop, foldr, fromIntegral, iterate, not, or, (*), (^))
import Data.Chimera.Internal
import Data.Chimera.Memoize
-- $monadic
-- Be careful: the stream is infinite, so
-- monadic effects must be lazy
-- in order to be executed in a finite time.
--
-- For instance, lazy state monad works fine:
--
-- >>> import Control.Monad.State.Lazy
-- >>> ch = evalState (tabulateM (\i -> do modify (+ i); get)) 0 :: UChimera Word
-- >>> take 10 (toList ch)
-- [0,1,3,6,10,15,21,28,36,45]
--
-- But the same computation in the strict state
-- monad "Control.Monad.State.Strict" diverges.
-- $subvectors
-- Internally 'Chimera' consists of a number of subvectors.
-- Following functions provide a low-level access to them.
-- This ability is especially important for streams of booleans.
--
-- Let us use 'Chimera' to memoize predicates @f1@, @f2@ @::@ 'Word' @->@ 'Bool'.
-- Imagine them both already
-- caught in amber as @ch1@, @ch2@ @::@ 'UChimera' 'Bool',
-- and now we want to memoize @f3 x = f1 x && f2 x@ as @ch3@.
-- One can do it in as follows:
--
-- > ch3 = tabulate (\i -> index ch1 i && index ch2 i)
--
-- There are two unsatisfactory things here. Firstly,
-- even unboxed vectors store only one boolean per byte.
-- We would rather reach out for 'Data.Bit.Bit' wrapper,
-- which provides an instance of unboxed vector
-- with one boolean per bit. Secondly, combining
-- existing predicates by indexing them and tabulating again
-- becomes relatively expensive, given how small and simple
-- our data is. Fortunately, there is an ultra-fast 'Data.Bit.zipBits'
-- to zip bit vectors. We can combine it altogether like this:
--
-- > import Data.Bit
-- > import Data.Bits
-- > ch1 = tabulate (Bit . f1)
-- > ch2 = tabulate (Bit . f2)
-- > ch3 = zipWithSubvectors (zipBits (.&.)) ch1 ch2