storablevector 0.2.8.3 → 0.2.9
raw patch · 34 files changed
+5243/−5269 lines, 34 filesdep +storablevectordep ~basedep ~transformersdep ~utility-ht
Dependencies added: storablevector
Dependency ranges changed: base, transformers, utility-ht
Files
- Data/StorableVector.hs +0/−1565
- Data/StorableVector/Base.hs +0/−225
- Data/StorableVector/Cursor.hs +0/−353
- Data/StorableVector/Lazy.hs +0/−1359
- Data/StorableVector/Lazy/Builder.hs +0/−159
- Data/StorableVector/Lazy/Pattern.hs +0/−371
- Data/StorableVector/Lazy/Pointer.hs +0/−20
- Data/StorableVector/Lazy/PointerPrivate.hs +0/−42
- Data/StorableVector/Lazy/PointerPrivateIndex.hs +0/−38
- Data/StorableVector/Pointer.hs +0/−52
- Data/StorableVector/Private.hs +0/−151
- Data/StorableVector/ST/Lazy.hs +0/−152
- Data/StorableVector/ST/Private.hs +0/−54
- Data/StorableVector/ST/Strict.hs +0/−287
- speedtest/Data/StorableVector/Private.hs +151/−0
- src/Data/StorableVector.hs +1571/−0
- src/Data/StorableVector/Base.hs +225/−0
- src/Data/StorableVector/Cursor.hs +353/−0
- src/Data/StorableVector/Lazy.hs +1371/−0
- src/Data/StorableVector/Lazy/Builder.hs +159/−0
- src/Data/StorableVector/Lazy/Pattern.hs +371/−0
- src/Data/StorableVector/Lazy/Pointer.hs +20/−0
- src/Data/StorableVector/Lazy/PointerPrivate.hs +42/−0
- src/Data/StorableVector/Lazy/PointerPrivateIndex.hs +38/−0
- src/Data/StorableVector/Pointer.hs +52/−0
- src/Data/StorableVector/ST/Lazy.hs +152/−0
- src/Data/StorableVector/ST/Private.hs +54/−0
- src/Data/StorableVector/ST/Strict.hs +287/−0
- storablevector.cabal +45/−55
- tests-2/Instances.hs +0/−1
- tests/QuickCheckUtils.hs +0/−225
- tests/Test.hs +212/−0
- tests/Test/Utility.hs +140/−0
- tests/tests.hs +0/−160
− Data/StorableVector.hs
@@ -1,1565 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}------ Module : StorableVector--- Copyright : (c) The University of Glasgow 2001,--- (c) David Roundy 2003-2005,--- (c) Simon Marlow 2005--- (c) Don Stewart 2005-2006--- (c) Bjorn Bringert 2006--- (c) Spencer Janssen 2006--- (c) Henning Thielemann 2008-2013--------- License : BSD-style------ Maintainer : Henning Thielemann--- Stability : experimental--- Portability : portable, requires ffi and cpp--- Tested with : GHC 6.4.1 and Hugs March 2005---------- | A time and space-efficient implementation of vectors using--- packed arrays, suitable for high performance use, both in terms--- of large data quantities, or high speed requirements. Vectors--- are encoded as strict arrays, held in a 'ForeignPtr',--- and can be passed between C and Haskell with little effort.------ This module is intended to be imported @qualified@, to avoid name--- clashes with "Prelude" functions. eg.------ > import qualified Data.StorableVector as V------ Original GHC implementation by Bryan O\'Sullivan. Rewritten to use--- UArray by Simon Marlow. Rewritten to support slices and use--- ForeignPtr by David Roundy. Polished and extended by Don Stewart.--- Generalized to any Storable value by Spencer Janssen.--- Chunky lazy stream, also with chunk pattern control,--- mutable access in ST monad, Builder monoid by Henning Thieleman.--module Data.StorableVector (-- -- * The @Vector@ type- Vector,-- -- * Introducing and eliminating 'Vector's- empty,- singleton,- pack,- unpack,- packN,- packWith,- unpackWith,-- -- * Basic interface- cons,- snoc,- append,- head,- last,- tail,- init,- null,- length,- viewL,- viewR,- switchL,- switchR,-- -- * Transforming 'Vector's- map,- reverse,- intersperse,- transpose,-- -- * Reducing 'Vector's (folds)- foldl,- foldl',- foldl1,- foldl1',- foldr,- foldr1,-- -- ** Special folds- concat,- concatMap,- monoidConcatMap,- any,- all,- maximum,- minimum,-- -- * Building 'Vector's- -- ** Scans- scanl,- scanl1,- scanr,- scanr1,-- -- ** Accumulating maps- mapAccumL,- mapAccumR,- mapIndexed,-- -- ** Unfolding 'Vector's- replicate,- iterateN,- unfoldr,- unfoldrN,- unfoldrResultN,- sample,-- -- * Substrings-- -- ** Breaking strings- take,- drop,- splitAt,- takeWhile,- dropWhile,- span,- spanEnd,- break,- breakEnd,- group,- groupBy,- inits,- tails,-- -- ** Breaking into many substrings- split,- splitWith,- tokens,-- -- ** Joining strings- join,-- -- * Predicates- isPrefixOf,- isSuffixOf,-- -- * Searching 'Vector's-- -- ** Searching by equality- elem,- notElem,-- -- ** Searching with a predicate- find,- filter,-- -- * Indexing 'Vector's- index,- elemIndex,- elemIndices,- elemIndexEnd,- findIndex,- findIndices,- count,- findIndexOrEnd,-- -- * Zipping and unzipping 'Vector's- zip,- zipWith,- zipWith3,- zipWith4,- unzip,- copy,-- -- * Interleaved 'Vector's- sieve,- deinterleave,- interleave,-- -- * IO- hGet,- hPut,- readFile,- writeFile,- appendFile,-- ) where--import qualified Prelude as P-import Prelude hiding (reverse,head,tail,last,init,null- ,length,map,lines,foldl,foldr,unlines- ,concat,any,take,drop,splitAt,takeWhile- ,dropWhile,span,break,elem,filter,maximum- ,minimum,all,concatMap,foldl1,foldr1- ,scanl,scanl1,scanr,scanr1- ,readFile,writeFile,appendFile,replicate- ,getContents,getLine,putStr,putStrLn- ,zip,zipWith,zipWith3,unzip,notElem- ,pred,succ)--import Data.StorableVector.Base--import qualified Control.Monad.Trans.Cont as MC--import qualified Data.List as List-import qualified Data.List.HT as ListHT-import qualified Data.Strictness.HT as Strict-import Data.Tuple.HT (mapSnd, )-import Data.Maybe.HT (toMaybe, )-import Data.Maybe (fromMaybe, isJust, )-import Data.Bool.HT (if', )--import Control.Exception (assert, bracket, )--import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )-import Foreign.Marshal.Array (advancePtr, copyArray, withArray, )-import Foreign.Ptr (Ptr, minusPtr, )-import Foreign.Storable (Storable(..))--import Data.Monoid (Monoid, mempty, mappend, mconcat, )-import Control.Monad (mplus, guard, when, liftM2, liftM3, liftM4, )--import System.IO (openBinaryFile, hClose, hFileSize,- hGetBuf, hPutBuf,- Handle, IOMode(..), )--import qualified System.Unsafe as Unsafe--- import GHC.IOBase---- -------------------------------------------------------------------------------instance (Storable a, Eq a) => Eq (Vector a) where- (==) = equal--instance (Storable a) => Monoid (Vector a) where- mempty = empty- mappend = append- mconcat = concat---- | /O(n)/ Equality on the 'Vector' type.-equal :: (Storable a, Eq a) => Vector a -> Vector a -> Bool-equal a b =- Unsafe.performIO $- withStartPtr a $ \paf la ->- withStartPtr b $ \pbf lb ->- if la /= lb- then- return False- else- if paf == pbf- then return True- else- let go = Strict.arguments3 $ \p q l ->- if l==0- then return True- else- do x <- peek p- y <- peek q- if x==y- then go (incPtr p) (incPtr q) (l-1)- else return False- in go paf pbf la-{-# INLINE equal #-}---- -------------------------------------------------------------------------------- Introducing and eliminating 'Vector's---- | /O(1)/ The empty 'Vector'-empty :: (Storable a) => Vector a-empty = unsafeCreate 0 $ const $ return ()-{-# NOINLINE empty #-}---- | /O(1)/ Construct a 'Vector' containing a single element-singleton :: (Storable a) => a -> Vector a-singleton c = unsafeCreate 1 $ \p -> poke p c-{-# INLINE singleton #-}---- | /O(n)/ Convert a '[a]' into a 'Vector a'.----pack :: (Storable a) => [a] -> Vector a-pack str = unsafeCreate (P.length str) $ \p -> go p str- where- go = Strict.arguments2 $ \p ->- ListHT.switchL- (return ())- (\x xs -> poke p x >> go (incPtr p) xs)---- | /O(n)/ Convert first @n@ elements of a '[a]' into a 'Vector a'.----packN :: (Storable a) => Int -> [a] -> (Vector a, [a])-packN n =- mapSnd (fromMaybe []) . unfoldrN n ListHT.viewL---- | /O(n)/ Converts a 'Vector a' to a '[a]'.-unpack :: (Storable a) => Vector a -> [a]-unpack = foldr (:) []-{-# INLINE unpack #-}------------------------------------------------------------------------------ | /O(n)/ Convert a list into a 'Vector' using a conversion function-packWith :: (Storable b) => (a -> b) -> [a] -> Vector b-packWith k str = unsafeCreate (P.length str) $ \p -> go p str- where- go = Strict.arguments2 $ \p ->- ListHT.switchL- (return ())- (\x xs -> poke p (k x) >> go (incPtr p) xs)- -- less space than pokeElemOff-{-# INLINE packWith #-}--{--*Data.StorableVector> List.take 10 $ unpackWith id $ pack [0..10000000::Int]-[0,1,2,3,4,5,6,7,8,9]-(19.18 secs, 2327851592 bytes)--}--- | /O(n)/ Convert a 'Vector' into a list using a conversion function-unpackWith :: (Storable a) => (a -> b) -> Vector a -> [b]-unpackWith f = foldr ((:) . f) []-{-# INLINE unpackWith #-}--{--That's too inefficient, since it builds the list from back to front,-that is, in a too strict manner.---- | /O(n)/ Convert a 'Vector' into a list using a conversion function-unpackWith :: (Storable a) => (a -> b) -> Vector a -> [b]-unpackWith _ (SV _ _ 0) = []-unpackWith k v@(SV ps s l) = inlinePerformIO $ withStartPtr v $ \p ->- go p (l - 1) []- where- STRICT3(go)- go p 0 acc = peek p >>= \e -> return (k e : acc)- go p n acc = peekElemOff p n >>= \e -> go p (n-1) (k e : acc)-{-# INLINE unpackWith #-}---*Data.StorableVector> List.take 10 $ unpack $ pack [0..10000000::Int]-[0,1,2,3,4,5,6,7,8,9]-(18.57 secs, 2323959948 bytes)-*Data.StorableVector> unpack $ take 10 $ pack [0..10000000::Int]-[0,1,2,3,4,5,6,7,8,9]-(18.40 secs, 2324002120 bytes)-*Data.StorableVector> List.take 10 $ unpackWith id $ pack [0..10000000::Int]-Interrupted.--}---- ------------------------------------------------------------------------ Basic interface---- | /O(1)/ Test whether a 'Vector' is empty.-null :: Vector a -> Bool-null (SV _ _ l) = assert (l >= 0) $ l <= 0-{-# INLINE null #-}---- ------------------------------------------------------------------------ | /O(1)/ 'length' returns the length of a 'Vector' as an 'Int'.-length :: Vector a -> Int-length (SV _ _ l) = assert (l >= 0) $ l------- length/loop fusion. When taking the length of any fuseable loop,--- rewrite it as a foldl', and thus avoid allocating the result buffer--- worth around 10% in speed testing.-----{-# INLINE [1] length #-}------------------------------------------------------------------------------ | /O(n)/ 'cons' is analogous to (:) for lists, but of different--- complexity, as it requires a memcpy.-cons :: (Storable a) => a -> Vector a -> Vector a-cons c v =- unsafeWithStartPtr v $ \f l ->- create (l + 1) $ \p -> do- poke p c- copyArray (incPtr p) f (fromIntegral l)-{-# INLINE cons #-}---- | /O(n)/ Append an element to the end of a 'Vector'-snoc :: (Storable a) => Vector a -> a -> Vector a-snoc v c =- unsafeWithStartPtr v $ \f l ->- create (l + 1) $ \p -> do- copyArray p f l- pokeElemOff p l c-{-# INLINE snoc #-}---- | /O(1)/ Extract the first element of a 'Vector', which must be non-empty.--- An exception will be thrown in the case of an empty 'Vector'.-head :: (Storable a) => Vector a -> a-head =- withNonEmptyVector "head" $ \ p s _l -> foreignPeek p s-{-# INLINE head #-}---- | /O(1)/ Extract the elements after the head of a 'Vector', which must be non-empty.--- An exception will be thrown in the case of an empty 'Vector'.-tail :: (Storable a) => Vector a -> Vector a-tail =- withNonEmptyVector "tail" $ \ p s l -> SV p (s+1) (l-1)-{-# INLINE tail #-}--laxTail :: (Storable a) => Vector a -> Vector a-laxTail v@(SV fp s l) =- if l<=0- then v- else SV fp (s+1) (l-1)-{-# INLINE laxTail #-}---- | /O(1)/ Extract the last element of a 'Vector', which must be finite and non-empty.--- An exception will be thrown in the case of an empty 'Vector'.-last :: (Storable a) => Vector a -> a-last =- withNonEmptyVector "last" $ \ p s l -> foreignPeek p (s+l-1)-{-# INLINE last #-}---- | /O(1)/ Return all the elements of a 'Vector' except the last one.--- An exception will be thrown in the case of an empty 'Vector'.-init :: Vector a -> Vector a-init =- withNonEmptyVector "init" $ \ p s l -> SV p s (l-1)-{-# INLINE init #-}---- | /O(n)/ Append two Vectors-append :: (Storable a) => Vector a -> Vector a -> Vector a-append xs ys =- if' (null xs) ys $- if' (null ys) xs $- concat [xs,ys]-{-# INLINE append #-}---- ------------------------------------------------------------------------ Transformations---- | /O(n)/ 'map' @f xs@ is the 'Vector' obtained by applying @f@ to each--- element of @xs@.-map :: (Storable a, Storable b) => (a -> b) -> Vector a -> Vector b-map f v =- unsafeWithStartPtr v $ \a len ->- create len $ \p ->- let go = Strict.arguments3 $- \ n p1 p2 ->- when (n>0) $- do poke p2 . f =<< peek p1- go (n-1) (incPtr p1) (incPtr p2)- in go len a p-{-# INLINE map #-}--{--mapByIndex :: (Storable a, Storable b) => (a -> b) -> Vector a -> Vector b-mapByIndex f v = inlinePerformIO $ withStartPtr v $ \a len ->- create len $ \p2 ->- let go = Strict.arguments1 $ \ n ->- when (n<len) $- do pokeElemOff p2 n . f =<< peekElemOff a n- go (n+1)- in go 0--}---- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order.-reverse :: (Storable a) => Vector a -> Vector a-reverse v =- unsafeWithStartPtr v $ \f l ->- create l $ \p ->- sequence_ [peekElemOff f i >>= pokeElemOff p (l - i - 1)- | i <- [0 .. l - 1]]---- | /O(n)/ The 'intersperse' function takes a element and a--- 'Vector' and \`intersperses\' that element between the elements of--- the 'Vector'. It is analogous to the intersperse function on--- Lists.-intersperse :: (Storable a) => a -> Vector a -> Vector a-intersperse c = pack . List.intersperse c . unpack---- | The 'transpose' function transposes the rows and columns of its--- 'Vector' argument.-transpose :: (Storable a) => [Vector a] -> [Vector a]-transpose ps = P.map pack (List.transpose (P.map unpack ps))---- ------------------------------------------------------------------------ Reducing 'Vector's---- | 'foldl', applied to a binary operator, a starting value (typically--- the left-identity of the operator), and a Vector, reduces the--- 'Vector' using the binary operator, from left to right.--- This function is subject to array fusion.-foldl :: (Storable a) => (b -> a -> b) -> b -> Vector a -> b-foldl f v xs =- foldr (\x k acc -> k (f acc x)) id xs v-{-# INLINE foldl #-}---- | 'foldl\'' is like 'foldl', but strict in the accumulator.-foldl' :: (Storable a) => (b -> a -> b) -> b -> Vector a -> b-foldl' f b v =- Unsafe.performIO $ withStartPtr v $ \ptr l ->- let q = ptr `advancePtr` l- go = Strict.arguments2 $ \p z ->- if p == q- then return z- else go (incPtr p) . f z =<< peek p- in go ptr b-{-# INLINE foldl' #-}---- | 'foldr', applied to a binary operator, a starting value--- (typically the right-identity of the operator), and a 'Vector',--- reduces the 'Vector' using the binary operator, from right to left.--- However, it is not the same as 'foldl' applied to the reversed vector.--- Actually 'foldr' starts processing with the first element,--- and thus can be used for efficiently building a singly linked list--- by @foldr (:) [] vec@.--- Unfortunately 'foldr' is quite slow for low-level loops,--- since GHC (up to 6.12.1) cannot detect the loop.-foldr :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b-foldr = foldrByLoop-{-# INLINE foldr #-}--{--*Data.StorableVector> List.length $ foldrBySwitch (:) [] $ replicate 1000000 'a'-1000000-(11.29 secs, 1183476300 bytes)-*Data.StorableVector> List.length $ foldrByIO (:) [] $ replicate 1000000 'a'-1000000-(7.86 secs, 1033901140 bytes)-*Data.StorableVector> List.length $ foldrByIndex (:) [] $ replicate 1000000 'a'-1000000-(7.86 secs, 914340420 bytes)-*Data.StorableVector> List.length $ foldrByLoop (:) [] $ replicate 1000000 'a'-1000000-(6.38 secs, 815355460 bytes)--}-{--We cannot simply increment the pointer,-since ForeignPtr cannot be incremented.-We also cannot convert from ForeignPtr to Ptr-and increment that instead,-because we need to keep the reference to ForeignPtr,-otherwise memory might be freed.-We can also not perform loop entirely in strict IO,-since this eat up the stack quickly-and 'foldr' might be used to build a list lazily.--}-foldrByLoop :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b-foldrByLoop f z (SV fp s l) =- let end = s+l- go = Strict.arguments1 $ \k ->- if k<end- then f (foreignPeek fp k) (go (succ k))- else z- in go s-{-# INLINE foldrByLoop #-}--{--foldrByIO :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b-foldrByIO f z v@(SV fp _ _) =- unsafeWithStartPtr v $- let go = Strict.arguments2 $ \p l ->- Unsafe.interleaveIO $- if l>0- then liftM2 f (peek p) (go (incPtr p) (pred l))- else touchForeignPtr fp >> return z- in go-{-# INLINE foldrByIO #-}--foldrByIndex :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b-foldrByIndex k z xs =- let recourse n =- if n < length xs- then k (unsafeIndex xs n) (recourse (succ n))- else z- in recourse 0-{-# INLINE foldrByIndex #-}--{--This implementation is a bit inefficient,-since switchL creates a new Vector structure-instead of just incrementing an index.--}-foldrBySwitch :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b-foldrBySwitch k z =- let recourse = switchL z (\h t -> k h (recourse t))- in recourse-{-# INLINE foldrBySwitch #-}--}----- | 'foldl1' is a variant of 'foldl' that has no starting value--- argument, and thus must be applied to non-empty 'Vector's.--- This function is subject to array fusion.--- An exception will be thrown in the case of an empty 'Vector'.-foldl1 :: (Storable a) => (a -> a -> a) -> Vector a -> a-foldl1 f =- switchL- (errorEmpty "foldl1")- (foldl f)-{-# INLINE foldl1 #-}---- | 'foldl1\'' is like 'foldl1', but strict in the accumulator.--- An exception will be thrown in the case of an empty 'Vector'.-foldl1' :: (Storable a) => (a -> a -> a) -> Vector a -> a-foldl1' f =- switchL- (errorEmpty "foldl1'")- (foldl' f)-{-# INLINE foldl1' #-}---- | 'foldr1' is a variant of 'foldr' that has no starting value argument,--- and thus must be applied to non-empty 'Vector's--- An exception will be thrown in the case of an empty 'Vector'.-foldr1 :: (Storable a) => (a -> a -> a) -> Vector a -> a-foldr1 f =- switchR- (errorEmpty "foldr1")- (flip (foldr f))-{-# INLINE foldr1 #-}---- ------------------------------------------------------------------------ Special folds---- | /O(n)/ Concatenate a list of 'Vector's.-concat :: (Storable a) => [Vector a] -> Vector a-concat [] = empty-concat [ps] = ps-concat xs = unsafeCreate len $ \ptr -> go ptr xs- where len = P.sum . P.map length $ xs- go =- Strict.arguments2 $ \ptr ->- ListHT.switchL- (return ())- (\v ps -> do- withStartPtr v $ copyArray ptr- go (ptr `advancePtr` length v) ps)---- | Map a function over a 'Vector' and concatenate the results-concatMap :: (Storable a, Storable b) => (a -> Vector b) -> Vector a -> Vector b-concatMap f = concat . unpackWith f-{-# INLINE concatMap #-}---- | This is like @mconcat . map f@,--- but in many cases the result of @f@ will not be storable.-monoidConcatMap :: (Storable a, Monoid m) => (a -> m) -> Vector a -> m-monoidConcatMap f =- foldr (mappend . f) mempty-{-# INLINE monoidConcatMap #-}---- | /O(n)/ Applied to a predicate and a 'Vector', 'any' determines if--- any element of the 'Vector' satisfies the predicate.-any :: (Storable a) => (a -> Bool) -> Vector a -> Bool-any f = foldr ((||) . f) False-{-# INLINE any #-}---- | /O(n)/ Applied to a predicate and a 'Vector', 'all' determines--- if all elements of the 'Vector' satisfy the predicate.-all :: (Storable a) => (a -> Bool) -> Vector a -> Bool-all f = foldr ((&&) . f) True-{-# INLINE all #-}------------------------------------------------------------------------------ | /O(n)/ 'maximum' returns the maximum value from a 'Vector'--- This function will fuse.--- An exception will be thrown in the case of an empty 'Vector'.-maximum :: (Storable a, Ord a) => Vector a -> a-maximum = foldl1' max---- | /O(n)/ 'minimum' returns the minimum value from a 'Vector'--- This function will fuse.--- An exception will be thrown in the case of an empty 'Vector'.-minimum :: (Storable a, Ord a) => Vector a -> a-minimum = foldl1' min----------------------------------------------------------------------------switchL :: Storable a => b -> (a -> Vector a -> b) -> Vector a -> b-switchL n j x =- if null x- then n- else j (unsafeHead x) (unsafeTail x)-{-# INLINE switchL #-}--switchR :: Storable a => b -> (Vector a -> a -> b) -> Vector a -> b-switchR n j x =- if null x- then n- else j (unsafeInit x) (unsafeLast x)-{-# INLINE switchR #-}--viewL :: Storable a => Vector a -> Maybe (a, Vector a)-viewL = switchL Nothing (curry Just)-{-# INLINE viewL #-}--viewR :: Storable a => Vector a -> Maybe (Vector a, a)-viewR = switchR Nothing (curry Just)-{-# INLINE viewR #-}---- | The 'mapAccumL' function behaves like a combination of 'map' and--- 'foldl'; it applies a function to each element of a 'Vector',--- passing an accumulating parameter from left to right, and returning a--- final value of this accumulator together with the new list.-mapAccumL :: (Storable a, Storable b) => (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)-mapAccumL f acc0 as0 =- let (bs, Just (acc2, _)) =- unfoldrN (length as0)- (\(acc,as) ->- fmap- (\(asHead,asTail) ->- let (acc1,b) = f acc asHead- in (b, (acc1, asTail)))- (viewL as))- (acc0,as0)- in (acc2, bs)-{-# INLINE mapAccumL #-}---- | The 'mapAccumR' function behaves like a combination of 'map' and--- 'foldr'; it applies a function to each element of a 'Vector',--- passing an accumulating parameter from right to left, and returning a--- final value of this accumulator together with the new 'Vector'.-mapAccumR :: (Storable a, Storable b) => (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)-mapAccumR f acc0 as0 =- let (bs, Just (acc2, _)) =- unfoldlN (length as0)- (\(acc,as) ->- fmap- (\(asInit,asLast) ->- let (acc1,b) = f acc asLast- in (b, (acc1, asInit)))- (viewR as))- (acc0,as0)- in (acc2, bs)-{-# INLINE mapAccumR #-}---- | /O(n)/ map functions, provided with the index at each position-mapIndexed :: (Storable a, Storable b) => (Int -> a -> b) -> Vector a -> Vector b-mapIndexed f = snd . mapAccumL (\i e -> (i + 1, f i e)) 0-{-# INLINE mapIndexed #-}---- ------------------------------------------------------------------------ Building 'Vector's---- | 'scanl' is similar to 'foldl', but returns a list of successive--- reduced values from the left. This function will fuse.------ > scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...]------ Note that------ > last (scanl f z xs) == foldl f z xs.-scanl :: (Storable a, Storable b) => (a -> b -> a) -> a -> Vector b -> Vector a-scanl f acc0 as0 =- fst $- unfoldrN (succ (length as0))- (fmap $ \(acc,as) ->- (acc,- fmap- (\(asHead,asTail) ->- (f acc asHead, asTail))- (viewL as)))- (Just (acc0, as0))---- less efficient but much more comprehensible--- scanl f z ps =--- cons z (snd (mapAccumL (\acc a -> let b = f acc a in (b,b)) z ps))-- -- n.b. haskell's List scan returns a list one bigger than the- -- input, so we need to snoc here to get some extra space, however,- -- it breaks map/up fusion (i.e. scanl . map no longer fuses)-{-# INLINE scanl #-}---- | 'scanl1' is a variant of 'scanl' that has no starting value argument.--- This function will fuse.------ > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]-scanl1 :: (Storable a) => (a -> a -> a) -> Vector a -> Vector a-scanl1 f = switchL empty (scanl f)-{-# INLINE scanl1 #-}---- | scanr is the right-to-left dual of scanl.-scanr :: (Storable a, Storable b) => (a -> b -> b) -> b -> Vector a -> Vector b-scanr f acc0 as0 =- fst $- unfoldlN (succ (length as0))- (fmap $ \(acc,as) ->- (acc,- fmap- (\(asInit,asLast) ->- (f asLast acc, asInit))- (viewR as)))- (Just (acc0, as0))-{-# INLINE scanr #-}---- | 'scanr1' is a variant of 'scanr' that has no starting value argument.-scanr1 :: (Storable a) => (a -> a -> a) -> Vector a -> Vector a-scanr1 f = switchR empty (flip (scanl f))-{-# INLINE scanr1 #-}---- ------------------------------------------------------------------------ Unfolds and replicates---- | /O(n)/ 'replicate' @n x@ is a 'Vector' of length @n@ with @x@--- the value of every element.----{- nice implementation-replicate :: (Storable a) => Int -> a -> Vector a-replicate n c =- fst $ unfoldrN n (const $ Just (c, ())) ()--}--{--fast implementation--Maybe it could be made even faster by plainly copying the bit pattern of the first element.-Since there is no function like 'memset',-we could not warrant that the implementation is really efficient-for the actual machine we run on.--}-replicate :: (Storable a) => Int -> a -> Vector a-replicate n c =- if n <= 0- then empty- else unsafeCreate n $- let go = Strict.arguments2 $ \i p ->- if i == 0- then return ()- else poke p c >> go (pred i) (incPtr p)- in go n-{-# INLINE replicate #-}-{--For 'replicate 10000000 (42::Int)' generates:--Main_zdwa_info:- movl (%ebp),%eax- testl %eax,%eax- jne .LcfIQ- movl $ghczmprim_GHCziUnit_Z0T_closure+1,%esi- addl $8,%ebp- jmp *(%ebp)-.LcfIQ:- movl 4(%ebp),%ecx- movl $42,(%ecx)- decl %eax- addl $4,4(%ebp)- movl %eax,(%ebp)- jmp Main_zdwa_info--that is, the inner loop consists of 9 instructions,-where I would write something like:- # counter in %ecx- testl %ecx- jz skip_loop- movl $42,%ebx-start_loop:- movl %ebx,(%edx)- addl $4,%edx- loop start_loop-skip_loop:--and need only 3 instructions in the loop.--}----- | /O(n)/ 'iterateN' @n f x@ is a 'Vector' of length @n@--- where the elements of @x@ are generated by repeated application of @f@.----iterateN :: (Storable a) => Int -> (a -> a) -> a -> Vector a-iterateN n f =- fst . unfoldrN n (\a -> Just (a, f a))-{-# INLINE iterateN #-}---- | /O(n)/, where /n/ is the length of the result. The 'unfoldr'--- function is analogous to the List \'unfoldr\'. 'unfoldr' builds a--- 'Vector' from a seed value. The function takes the element and--- returns 'Nothing' if it is done producing the 'Vector or returns--- 'Just' @(a,b)@, in which case, @a@ is the next element in the 'Vector',--- and @b@ is the seed value for further production.------ Examples:------ > unfoldr (\x -> if x <= 5 then Just (x, x + 1) else Nothing) 0--- > == pack [0, 1, 2, 3, 4, 5]----unfoldr :: (Storable b) => (a -> Maybe (b, a)) -> a -> Vector b-unfoldr f = concat . unfoldChunk 32 64- where unfoldChunk n n' x =- case unfoldrN n f x of- (s, mx) -> s : maybe [] (unfoldChunk n' (n+n')) mx-{-# INLINE unfoldr #-}---- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a 'Vector' from a seed--- value. However, the length of the result is limited by the first--- argument to 'unfoldrN'. This function is more efficient than 'unfoldr'--- when the maximum length of the result is known.------ The following equation relates 'unfoldrN' and 'unfoldr':------ > fst (unfoldrN n f s) == take n (unfoldr f s)----unfoldrN :: (Storable b) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)-unfoldrN n f x0 =- if n <= 0- then (empty, Just x0)- else Unsafe.performIO $ createAndTrim' n $ \p -> go p n x0- {-- go must not be strict in the accumulator- since otherwise packN would be too strict.- -}- where- go = Strict.arguments2 $ \p i -> \x ->- if i == 0- then return (0, n-i, Just x)- else- case f x of- Nothing -> return (0, n-i, Nothing)- Just (w,x') -> do poke p w- go (incPtr p) (i-1) x'-{-# INLINE unfoldrN #-}--{--Examples:--f i = Just (i::Char, succ i)--f i = toMaybe (i<='p') (i::Char, succ i)---}--- | /O(n)/ Like 'unfoldrN' this function builds a 'Vector'--- from a seed value with limited size.--- Additionally it returns a value, that depends on the state,--- but is not necessarily the state itself.--- If end of vector and end of the generator coincide,--- then the result is as if only the end of vector is reached.------ Example:------ > unfoldrResultN 30 Char.ord (\c -> if c>'z' then Left 1000 else Right (c, succ c)) 'a'------ The following equation relates 'unfoldrN' and 'unfoldrResultN':------ > unfoldrN n f s ==--- > unfoldrResultN n Just--- > (maybe (Left Nothing) Right . f) s------ It is not possible to express 'unfoldrResultN' in terms of 'unfoldrN'.----unfoldrResultN :: (Storable b) => Int -> (a -> c) -> (a -> Either c (b, a)) -> a -> (Vector b, c)-unfoldrResultN i g f x0 =- if i <= 0- then (empty, g x0)- else Unsafe.performIO $ createAndTrim' i $ \p -> go p 0 x0- {-- go must not be strict in the accumulator- since otherwise packN would be too strict.- -}- where- go = Strict.arguments2 $ \p n -> \a0 ->- if n == i- then return (0, n, g a0)- else- case f a0 of- Left c -> return (0, n, c)- Right (b,a1) -> do poke p b- go (incPtr p) (n+1) a1-{-# INLINE unfoldrResultN #-}--unfoldlN :: (Storable b) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)-unfoldlN i f x0- | i < 0 = (empty, Just x0)- | otherwise = Unsafe.performIO $ createAndTrim' i $ \p -> go (p `advancePtr` i) i x0- where go = Strict.arguments2 $ \p n -> \x ->- if n == 0- then return (n, i, Just x)- else- case f x of- Nothing -> return (n, i, Nothing)- Just (w,x') ->- let p' = p `advancePtr` (-1)- in do poke p' w- go p' (n-1) x'-{-# INLINE unfoldlN #-}----- | /O(n)/, where /n/ is the length of the result.--- This function constructs a vector by evaluating a function--- that depends on the element index.--- It is a special case of 'unfoldrN' and can in principle be parallelized.------ Examples:------ > sample 26 (\x -> chr(ord 'a'+x))--- > == pack "abcdefghijklmnopqrstuvwxyz"----sample :: (Storable a) => Int -> (Int -> a) -> Vector a-sample n f =- fst $ unfoldrN n (\i -> Just (f i, succ i)) 0-{-# INLINE sample #-}----- ------------------------------------------------------------------------ Substrings---- | /O(1)/ 'take' @n@, applied to a 'Vector' @xs@, returns the prefix--- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@.-take :: (Storable a) => Int -> Vector a -> Vector a-take n ps@(SV x s l)- | n <= 0 = empty- | n >= l = ps- | otherwise = SV x s n-{-# INLINE take #-}---- | /O(1)/ 'drop' @n xs@ returns the suffix of @xs@ after the first @n@--- elements, or 'empty' if @n > 'length' xs@.-drop :: (Storable a) => Int -> Vector a -> Vector a-drop n ps@(SV x s l)- | n <= 0 = ps- | n >= l = empty- | otherwise = SV x (s+n) (l-n)-{-# INLINE drop #-}---- | /O(1)/ 'splitAt' @n xs@ is equivalent to @('take' n xs, 'drop' n xs)@.-splitAt :: (Storable a) => Int -> Vector a -> (Vector a, Vector a)-splitAt n ps@(SV x s l)- | n <= 0 = (empty, ps)- | n >= l = (ps, empty)- | otherwise = (SV x s n, SV x (s+n) (l-n))-{-# INLINE splitAt #-}---- | 'takeWhile', applied to a predicate @p@ and a 'Vector' @xs@,--- returns the longest prefix (possibly empty) of @xs@ of elements that--- satisfy @p@.-takeWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a-takeWhile f ps = unsafeTake (findIndexOrEnd (not . f) ps) ps-{-# INLINE takeWhile #-}---- | 'dropWhile' @p xs@ returns the suffix remaining after 'takeWhile' @p xs@.-dropWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a-dropWhile f ps = unsafeDrop (findIndexOrEnd (not . f) ps) ps-{-# INLINE dropWhile #-}---- | 'break' @p@ is equivalent to @'span' ('not' . p)@.-break :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)-break p ps = case findIndexOrEnd p ps of n -> (unsafeTake n ps, unsafeDrop n ps)-{-# INLINE break #-}---- | 'breakEnd' behaves like 'break' but from the end of the 'Vector'------ breakEnd p == spanEnd (not.p)-breakEnd :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)-breakEnd p ps = splitAt (findFromEndUntil p ps) ps---- | 'span' @p xs@ breaks the 'Vector' into two segments. It is--- equivalent to @('takeWhile' p xs, 'dropWhile' p xs)@-span :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)-span p ps = break (not . p) ps-{-# INLINE span #-}---- | 'spanEnd' behaves like 'span' but from the end of the 'Vector'.--- We have------ > spanEnd (not.isSpace) "x y z" == ("x y ","z")------ and------ > spanEnd (not . isSpace) ps--- > ==--- > let (x,y) = span (not.isSpace) (reverse ps) in (reverse y, reverse x)----spanEnd :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)-spanEnd p ps = splitAt (findFromEndUntil (not.p) ps) ps---- | /O(n)/ Splits a 'Vector' into components delimited by--- separators, where the predicate returns True for a separator element.--- The resulting components do not contain the separators. Two adjacent--- separators result in an empty component in the output. eg.------ > splitWith (=='a') "aabbaca" == ["","","bb","c",""]--- > splitWith (=='a') [] == []----splitWith :: (Storable a) => (a -> Bool) -> Vector a -> [Vector a]-splitWith _ (SV _ _ 0) = []-splitWith p ps = loop ps- where- loop =- uncurry (:) .- mapSnd (switchL [] (\ _ t -> loop t)) .- break p-{-# INLINE splitWith #-}---- | /O(n)/ Break a 'Vector' into pieces separated by the--- argument, consuming the delimiter. I.e.------ > split '\n' "a\nb\nd\ne" == ["a","b","d","e"]--- > split 'a' "aXaXaXa" == ["","X","X","X"]--- > split 'x' "x" == ["",""]------ and------ > join [c] . split c == id--- > split == splitWith . (==)------ As for all splitting functions in this library, this function does--- not copy the substrings, it just constructs new 'Vector's that--- are slices of the original.----split :: (Storable a, Eq a) => a -> Vector a -> [Vector a]-split w v = splitWith (w==) v-{-# INLINE split #-}---- | Like 'splitWith', except that sequences of adjacent separators are--- treated as a single separator. eg.------ > tokens (=='a') "aabbaca" == ["bb","c"]----tokens :: (Storable a) => (a -> Bool) -> Vector a -> [Vector a]-tokens f = P.filter (not.null) . splitWith f-{-# INLINE tokens #-}---- | The 'group' function takes a 'Vector' and returns a list of--- 'Vector's such that the concatenation of the result is equal to the--- argument. Moreover, each sublist in the result contains only equal--- elements. For example,------ > group "Mississippi" = ["M","i","ss","i","ss","i","pp","i"]------ It is a special case of 'groupBy', which allows the programmer to--- supply their own equality test. It is about 40% faster than--- /groupBy (==)/-group :: (Storable a, Eq a) => Vector a -> [Vector a]-group xs =- switchL []- (\ h _ ->- let (ys, zs) = span (== h) xs- in ys : group zs)- xs---- | The 'groupBy' function is the non-overloaded version of 'group'.-groupBy :: (Storable a) => (a -> a -> Bool) -> Vector a -> [Vector a]-groupBy k xs =- switchL []- (\ h t ->- let n = 1 + findIndexOrEnd (not . k h) t- in unsafeTake n xs : groupBy k (unsafeDrop n xs))- xs-{-# INLINE groupBy #-}----- | /O(n)/ The 'join' function takes a 'Vector' and a list of--- 'Vector's and concatenates the list after interspersing the first--- argument between each element of the list.-join :: (Storable a) => Vector a -> [Vector a] -> Vector a-join s = concat . List.intersperse s-{-# INLINE join #-}---- ------------------------------------------------------------------------ Indexing 'Vector's---- | /O(1)/ 'Vector' index (subscript) operator, starting from 0.-index :: (Storable a) => Vector a -> Int -> a-index ps n- | n < 0 = moduleError "index" ("negative index: " ++ show n)- | n >= length ps = moduleError "index" ("index too large: " ++ show n- ++ ", length = " ++ show (length ps))- | otherwise = ps `unsafeIndex` n-{-# INLINE index #-}---- | /O(n)/ The 'elemIndex' function returns the index of the first--- element in the given 'Vector' which is equal to the query--- element, or 'Nothing' if there is no such element.-elemIndex :: (Storable a, Eq a) => a -> Vector a -> Maybe Int-elemIndex c = findIndex (c==)-{-# INLINE elemIndex #-}---- | /O(n)/ The 'elemIndexEnd' function returns the last index of the--- element in the given 'Vector' which is equal to the query--- element, or 'Nothing' if there is no such element. The following--- holds:------ > elemIndexEnd c xs ==--- > (-) (length xs - 1) `fmap` elemIndex c (reverse xs)----elemIndexEnd :: (Storable a, Eq a) => a -> Vector a -> Maybe Int-elemIndexEnd c =- fst .- foldl- (\(ri,i) x -> (if c==x then Just i else ri, succ i))- (Nothing,0)-{-# INLINE elemIndexEnd #-}---- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning--- the indices of all elements equal to the query element, in ascending order.-elemIndices :: (Storable a, Eq a) => a -> Vector a -> [Int]-elemIndices c = findIndices (c==)-{-# INLINE elemIndices #-}---- | count returns the number of times its argument appears in the 'Vector'------ > count = length . elemIndices------ But more efficiently than using length on the intermediate list.-count :: (Storable a, Eq a) => a -> Vector a -> Int-count w =- foldl (flip $ \c -> if c==w then succ else id) 0-{--count w sv =- List.length $ elemIndices w sv--}-{-# INLINE count #-}---- | The 'findIndex' function takes a predicate and a 'Vector' and--- returns the index of the first element in the 'Vector'--- satisfying the predicate.-findIndex :: (Storable a) => (a -> Bool) -> Vector a -> Maybe Int-findIndex p xs =- {- The implementation is in principle the same as for findIndices,- but we use the First monoid, instead of the List/append monoid.- We could also implement findIndex in terms of monoidConcatMap. -}- foldr- (\x k n ->- toMaybe (p x) n `mplus` k (succ n))- (const Nothing) xs 0-{-# INLINE findIndex #-}---- | The 'findIndices' function extends 'findIndex', by returning the--- indices of all elements satisfying the predicate, in ascending order.-findIndices :: (Storable a) => (a -> Bool) -> Vector a -> [Int]-findIndices p xs =- foldr- (\x k n ->- (if p x then (n:) else id)- (k (succ n)))- (const []) xs 0-{-# INLINE findIndices #-}---- | 'findIndexOrEnd' is a variant of findIndex, that returns the length--- of the string if no element is found, rather than Nothing.-findIndexOrEnd :: (Storable a) => (a -> Bool) -> Vector a -> Int-findIndexOrEnd p xs =- foldr- (\x k n ->- if p x then n else k (succ n))- id xs 0-{-# INLINE findIndexOrEnd #-}---- ------------------------------------------------------------------------ Searching Vectors---- | /O(n)/ 'elem' is the 'Vector' membership predicate.-elem :: (Storable a, Eq a) => a -> Vector a -> Bool-elem c ps = isJust $ elemIndex c ps-{-# INLINE elem #-}---- | /O(n)/ 'notElem' is the inverse of 'elem'-notElem :: (Storable a, Eq a) => a -> Vector a -> Bool-notElem c ps = not (elem c ps)-{-# INLINE notElem #-}---- | /O(n)/ 'filter', applied to a predicate and a 'Vector',--- returns a 'Vector' containing those elements that satisfy the--- predicate. This function is subject to array fusion.-filter :: (Storable a) => (a -> Bool) -> Vector a -> Vector a-filter p (SV fp s l) =- let end = s+l- in fst $- unfoldrN l- (let go = Strict.arguments1 $ \k0 ->- do guard (k0<end)- let x = foreignPeek fp k0- k1 = succ k0- if p x- then Just (x,k1)- else go k1- in go)- s-{-# INLINE filter #-}---- | /O(n)/ The 'find' function takes a predicate and a 'Vector',--- and returns the first element in matching the predicate, or 'Nothing'--- if there is no such element.------ > find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> Nothing----find :: (Storable a) => (a -> Bool) -> Vector a -> Maybe a-find f p = fmap (unsafeIndex p) (findIndex f p)-{-# INLINE find #-}---- ------------------------------------------------------------------------ Searching for substrings---- | /O(n)/ The 'isPrefixOf' function takes two 'Vector' and returns 'True'--- iff the first is a prefix of the second.-isPrefixOf :: (Storable a, Eq a) => Vector a -> Vector a -> Bool-isPrefixOf x@(SV _ _ l1) y@(SV _ _ l2) =- l1 <= l2 && x == unsafeTake l1 y---- | /O(n)/ The 'isSuffixOf' function takes two 'Vector's and returns 'True'--- iff the first is a suffix of the second.------ The following holds:------ > isSuffixOf x y == reverse x `isPrefixOf` reverse y----isSuffixOf :: (Storable a, Eq a) => Vector a -> Vector a -> Bool-isSuffixOf x@(SV _ _ l1) y@(SV _ _ l2) =- l1 <= l2 && x == unsafeDrop (l2 - l1) y---- ------------------------------------------------------------------------ Zipping---- | /O(n)/ 'zip' takes two 'Vector's and returns a list of--- corresponding pairs of elements. If one input 'Vector' is short,--- excess elements of the longer 'Vector' are discarded. This is--- equivalent to a pair of 'unpack' operations.-zip :: (Storable a, Storable b) => Vector a -> Vector b -> [(a, b)]-zip ps qs =- maybe [] id $- do (ph,pt) <- viewL ps- (qh,qt) <- viewL qs- return ((ph,qh) : zip pt qt)---- | 'zipWith' generalises 'zip' by zipping with the function given as--- the first argument, instead of a tupling function. For example,--- @'zipWith' (+)@ is applied to two 'Vector's to produce the list of--- corresponding sums.-zipWith :: (Storable a, Storable b, Storable c) =>- (a -> b -> c) -> Vector a -> Vector b -> Vector c-zipWith f as bs =- unsafeWithStartPtr as $ \pa0 la ->- withStartPtr bs $ \pb0 lb ->- let len = min la lb- in create len $ \p0 ->- let go = Strict.arguments4 $ \n p pa pb ->- when (n>0) $- liftM2 f (peek pa) (peek pb) >>= poke p >>- go (pred n) (incPtr p) (incPtr pa) (incPtr pb)- in go len p0 pa0 pb0----- zipWith f ps qs = pack $ List.zipWith f (unpack ps) (unpack qs)-{-# INLINE zipWith #-}---- | Like 'zipWith' but for three input vectors-zipWith3 :: (Storable a, Storable b, Storable c, Storable d) =>- (a -> b -> c -> d) -> Vector a -> Vector b -> Vector c -> Vector d-zipWith3 f as bs cs =- unsafeWithStartPtr as $ \pa0 la ->- withStartPtr bs $ \pb0 lb ->- withStartPtr cs $ \pc0 lc ->- let len = la `min` lb `min` lc- in create len $ \p0 ->- let go = Strict.arguments5 $ \n p pa pb pc ->- when (n>0) $- liftM3 f (peek pa) (peek pb) (peek pc) >>= poke p >>- go (pred n) (incPtr p) (incPtr pa) (incPtr pb) (incPtr pc)- in go len p0 pa0 pb0 pc0-{-# INLINE zipWith3 #-}---- | Like 'zipWith' but for four input vectors--- If you need even more input vectors,--- you might write a function yourselve using unfoldrN and viewL.-zipWith4 :: (Storable a, Storable b, Storable c, Storable d, Storable e) =>- (a -> b -> c -> d -> e) -> Vector a -> Vector b -> Vector c -> Vector d -> Vector e-zipWith4 f as bs cs ds =- unsafeWithStartPtr as $ \pa0 la ->- withStartPtr bs $ \pb0 lb ->- withStartPtr cs $ \pc0 lc ->- withStartPtr ds $ \pd0 ld ->- let len = la `min` lb `min` lc `min` ld- in create len $ \p0 ->- let go =- Strict.arguments2 $ \n p ->- Strict.arguments4 $ \pa pb pc pd ->- when (n>0) $- liftM4 f (peek pa) (peek pb) (peek pc) (peek pd) >>= poke p >>- go (pred n) (incPtr p) (incPtr pa) (incPtr pb) (incPtr pc) (incPtr pd)- in go len p0 pa0 pb0 pc0 pd0-{-# INLINE zipWith4 #-}---- | /O(n)/ 'unzip' transforms a list of pairs of elements into a pair of--- 'Vector's. Note that this performs two 'pack' operations.-unzip :: (Storable a, Storable b) => [(a, b)] -> (Vector a, Vector b)-unzip ls = (pack (P.map fst ls), pack (P.map snd ls))-{-# INLINE unzip #-}---- ------------------------------------------------------------------------ Interleaved 'Vector's---- | /O(l/n)/ 'sieve' selects every 'n'th element.-sieve :: (Storable a) => Int -> Vector a -> Vector a-sieve n (SV fp s l) =- let end = s+l- in fst $- unfoldrN (- div (-l) n)- (Strict.arguments1 $ \k0 ->- do guard (k0<end)- Just (foreignPeek fp k0, k0 + n))- s-{-# INLINE sieve #-}---- | /O(n)/--- Returns n sieved vectors with successive starting elements.--- @deinterleave 3 (pack ['a'..'k']) = [pack "adgj", pack "behk", pack "cfi"]@--- This is the same as 'Data.List.HT.sliceHorizontal'.-deinterleave :: (Storable a) => Int -> Vector a -> [Vector a]-deinterleave n =- P.map (sieve n) . P.take n . P.iterate laxTail---- | /O(n)/--- Almost the inverse of deinterleave.--- Restriction is that all input vector must have equal length.--- @interleave [pack "adgj", pack "behk", pack "cfil"] = pack ['a'..'l']@-interleave :: (Storable a) => [Vector a] -> Vector a-interleave vs =- Unsafe.performIO $- MC.runContT- (do- pls <- mapM (\v -> MC.ContT (withStartPtr v . curry)) vs- let (ps,ls) = P.unzip pls- ptrs <- MC.ContT (withArray ps)- if and (ListHT.mapAdjacent (==) ls)- then return (ptrs, P.sum ls)- else moduleError "interleave" "all input vectors must have the same length")- (\(ptrs, totalLength) -> create totalLength $ \p ->- let n = P.length vs- pEnd = advancePtr p totalLength- go = Strict.arguments3 $ \k0 j p0 -> do- poke p0 =<< flip peekElemOff j =<< peekElemOff ptrs k0- let p1 = advancePtr p0 1- k1 = succ k0- when (p1 < pEnd) $- if k1 < n- then go k1 j p1- else go 0 (succ j) p1- in go 0 0 p)-{-# INLINE interleave #-}----- ------------------------------------------------------------------------ Special lists---- | /O(n)/ Return all initial segments of the given 'Vector', shortest first.-inits :: (Storable a) => Vector a -> [Vector a]-inits (SV x s l) = List.map (SV x s) [0..l]---- | /O(n)/ Return all final segments of the given 'Vector', longest first.-tails :: (Storable a) => Vector a -> [Vector a]-tails p =- switchL [empty] (\ _ t -> p : tails t) p---- ------------------------------------------------------------------------ ** Ordered 'Vector's---- ------------------------------------------------------------------------ Low level constructors---- | /O(n)/ Make a copy of the 'Vector' with its own storage.--- This is mainly useful to allow the rest of the data pointed--- to by the 'Vector' to be garbage collected, for example--- if a large string has been read in, and only a small part of it--- is needed in the rest of the program.-copy :: (Storable a) => Vector a -> Vector a-copy v =- unsafeWithStartPtr v $ \f l ->- create l $ \p ->- copyArray p f (fromIntegral l)------ ------------------------------------------------------------------------ IO---- | Outputs a 'Vector' to the specified 'Handle'.-hPut :: (Storable a) => Handle -> Vector a -> IO ()-hPut h v =- when (not (null v)) $- withStartPtr v $ \ ptrS l ->- let ptrE = advancePtr ptrS l- -- use advancePtr and minusPtr in order to respect alignment- in hPutBuf h ptrS (minusPtr ptrE ptrS)---- | Read a 'Vector' directly from the specified 'Handle'. This--- is far more efficient than reading the characters into a list--- and then using 'pack'.----hGet :: (Storable a) => Handle -> Int -> IO (Vector a)-hGet _ 0 = return empty-hGet h l =- createAndTrim l $ \p ->- let elemType :: Ptr a -> a- elemType _ = undefined- roundUp m n = n + mod (-n) m- sizeOfElem =- roundUp- (alignment (elemType p))- (sizeOf (elemType p))- in fmap (flip div sizeOfElem) $- hGetBuf h p (l * sizeOfElem)-{-- createAndTrim l $ \p ->- fmap (flip div (incPtr p `minusPtr` p)) $- hGetBuf h p (advancePtr p l `minusPtr` p)--}---- | Read an entire file strictly into a 'Vector'. This is far more--- efficient than reading the characters into a 'String' and then using--- 'pack'. It also may be more efficient than opening the file and--- reading it using hGet. Files are read using 'binary mode' on Windows.----readFile :: (Storable a) => FilePath -> IO (Vector a)-readFile f =- bracket (openBinaryFile f ReadMode) hClose- (\h -> hGet h . fromIntegral =<< hFileSize h)---- | Write a 'Vector' to a file.-writeFile :: (Storable a) => FilePath -> Vector a -> IO ()-writeFile f txt =- bracket (openBinaryFile f WriteMode) hClose- (\h -> hPut h txt)---- | Append a 'Vector' to a file.-appendFile :: (Storable a) => FilePath -> Vector a -> IO ()-appendFile f txt =- bracket (openBinaryFile f AppendMode) hClose- (\h -> hPut h txt)----- ------------------------------------------------------------------------ Internal utilities----- These definitions of succ and pred do not check for overflow--- and are faster than their counterparts from Enum class.-succ :: Int -> Int-succ n = n+1-{-# INLINE succ #-}--pred :: Int -> Int-pred n = n-1-{-# INLINE pred #-}--unsafeWithStartPtr :: Storable a => Vector a -> (Ptr a -> Int -> IO b) -> b-unsafeWithStartPtr v f =- Unsafe.performIO (withStartPtr v f)-{-# INLINE unsafeWithStartPtr #-}--foreignPeek :: Storable a => ForeignPtr a -> Int -> a-foreignPeek fp k =- inlinePerformIO $ withForeignPtr fp $ flip peekElemOff k-{-# INLINE foreignPeek #-}--withNonEmptyVector ::- String -> (ForeignPtr a -> Int -> Int -> b) -> Vector a -> b-withNonEmptyVector fun f (SV x s l) =- if l <= 0- then errorEmpty fun- else f x s l-{-# INLINE withNonEmptyVector #-}---- Common up near identical calls to `error' to reduce the number--- constant strings created when compiled:-errorEmpty :: String -> a-errorEmpty fun = moduleError fun "empty Vector"-{-# NOINLINE errorEmpty #-}--moduleError :: String -> String -> a-moduleError fun msg = error ("Data.StorableVector." ++ fun ++ ':':' ':msg)-{-# NOINLINE moduleError #-}---- Find from the end of the string using predicate-findFromEndUntil :: (Storable a) => (a -> Bool) -> Vector a -> Int-findFromEndUntil = Strict.arguments2 $ \f ps@(SV x s l) ->- if null ps then 0- else if f (last ps) then l- else findFromEndUntil f (SV x s (l-1))
− Data/StorableVector/Base.hs
@@ -1,225 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE DeriveDataTypeable #-}------ Module : Data.StorableVector.Base--- License : BSD-style--- Maintainer : dons@cse.unsw.edu.au--- Stability : experimental--- Portability : portable, requires ffi and cpp--- Tested with : GHC 6.4.1 and Hugs March 2005--- ---- | A module containing semi-public StorableVector internals. This exposes--- the StorableVector representation and low level construction functions.--- Modules which extend the StorableVector system will need to use this module--- while ideally most users will be able to make do with the public interface--- modules.----module Data.StorableVector.Base (-- -- * The @Vector@ type and representation- Vector(..), -- instances: Eq, Ord, Show, Read, Data, Typeable-- -- * Unchecked access- unsafeHead, -- :: Vector a -> a- unsafeTail, -- :: Vector a -> Vector a- unsafeLast, -- :: Vector a -> a- unsafeInit, -- :: Vector a -> Vector a- unsafeIndex, -- :: Vector a -> Int -> a- unsafeTake, -- :: Int -> Vector a -> Vector a- unsafeDrop, -- :: Int -> Vector a -> Vector a-- -- * Low level introduction and elimination- create, -- :: Int -> (Ptr a -> IO ()) -> IO (Vector a)- createAndTrim, -- :: Int -> (Ptr a -> IO Int) -> IO (Vector a)- createAndTrim', -- :: Int -> (Ptr a -> IO (Int, Int, b)) -> IO (Vector a, b)-- unsafeCreate, -- :: Int -> (Ptr a -> IO ()) -> Vector a-- fromForeignPtr, -- :: ForeignPtr a -> Int -> Vector a- toForeignPtr, -- :: Vector a -> (ForeignPtr a, Int, Int)- withStartPtr, -- :: Vector a -> (Ptr a -> Int -> IO b) -> IO b- incPtr, -- :: Ptr a -> Ptr a-- inlinePerformIO-- ) where--import Foreign.Ptr (Ptr)-import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )-import Foreign.Marshal.Array (advancePtr, copyArray)-import Foreign.Storable (Storable(peekElemOff))--import Data.StorableVector.Memory (mallocForeignPtrArray, )--import Control.Exception (assert)--#if defined(__GLASGOW_HASKELL__)-import Data.Generics (Data(..), Typeable(..))-import GHC.Base (realWorld#)-import GHC.IO (IO(IO), )-#endif--import qualified System.Unsafe as Unsafe---- CFILES stuff is Hugs only-{-# CFILES cbits/fpstring.c #-}---- --------------------------------------------------------------------------------- | A space-efficient representation of a vector, supporting many efficient--- operations.------ Instances of Eq, Ord, Read, Show, Data, Typeable----data Vector a = SV {-# UNPACK #-} !(ForeignPtr a)- {-# UNPACK #-} !Int -- offset- {-# UNPACK #-} !Int -- length-#if defined(__GLASGOW_HASKELL__)- deriving (Data, Typeable)-#endif---- --------------------------------------------------------------------------- Extensions to the basic interface------- | A variety of 'head' for non-empty Vectors. 'unsafeHead' omits the--- check for the empty case, so there is an obligation on the programmer--- to provide a proof that the Vector is non-empty.-unsafeHead :: (Storable a) => Vector a -> a-unsafeHead (SV x s l) = assert (l > 0) $- inlinePerformIO $ withForeignPtr x $ \p -> peekElemOff p s-{-# INLINE unsafeHead #-}---- | A variety of 'tail' for non-empty Vectors. 'unsafeTail' omits the--- check for the empty case. As with 'unsafeHead', the programmer must--- provide a separate proof that the Vector is non-empty.-unsafeTail :: (Storable a) => Vector a -> Vector a-unsafeTail (SV ps s l) = assert (l > 0) $ SV ps (s+1) (l-1)-{-# INLINE unsafeTail #-}---- | A variety of 'last' for non-empty Vectors. 'unsafeLast' omits the--- check for the empty case, so there is an obligation on the programmer--- to provide a proof that the Vector is non-empty.-unsafeLast :: (Storable a) => Vector a -> a-unsafeLast (SV x s l) = assert (l > 0) $- inlinePerformIO $ withForeignPtr x $ \p -> peekElemOff p (s+l-1)-{-# INLINE unsafeLast #-}---- | A variety of 'init' for non-empty Vectors. 'unsafeInit' omits the--- check for the empty case. As with 'unsafeLast', the programmer must--- provide a separate proof that the Vector is non-empty.-unsafeInit :: (Storable a) => Vector a -> Vector a-unsafeInit (SV ps s l) = assert (l > 0) $ SV ps s (l-1)-{-# INLINE unsafeInit #-}---- | Unsafe 'Vector' index (subscript) operator, starting from 0, returning a--- single element. This omits the bounds check, which means there is an--- accompanying obligation on the programmer to ensure the bounds are checked in--- some other way.-unsafeIndex :: (Storable a) => Vector a -> Int -> a-unsafeIndex (SV x s l) i = assert (i >= 0 && i < l) $- inlinePerformIO $ withForeignPtr x $ \p -> peekElemOff p (s+i)-{-# INLINE unsafeIndex #-}---- | A variety of 'take' which omits the checks on @n@ so there is an--- obligation on the programmer to provide a proof that @0 <= n <= 'length' xs@.-unsafeTake :: (Storable a) => Int -> Vector a -> Vector a-unsafeTake n (SV x s l) = assert (0 <= n && n <= l) $ SV x s n-{-# INLINE unsafeTake #-}---- | A variety of 'drop' which omits the checks on @n@ so there is an--- obligation on the programmer to provide a proof that @0 <= n <= 'length' xs@.-unsafeDrop :: (Storable a) => Int -> Vector a -> Vector a-unsafeDrop n (SV x s l) = assert (0 <= n && n <= l) $ SV x (s+n) (l-n)-{-# INLINE unsafeDrop #-}---instance (Storable a, Show a) => Show (Vector a) where- showsPrec p xs@(SV _ _ l) =- showParen (p>=10)- (showString "Vector.pack " .- showsPrec 10 (map (unsafeIndex xs) [0..(l-1)]))----- ------------------------------------------------------------------------ Low level constructors---- | /O(1)/ Build a Vector from a ForeignPtr-fromForeignPtr :: ForeignPtr a -> Int -> Vector a-fromForeignPtr fp l = SV fp 0 l---- | /O(1)/ Deconstruct a ForeignPtr from a Vector-toForeignPtr :: Vector a -> (ForeignPtr a, Int, Int)-toForeignPtr (SV ps s l) = (ps, s, l)---- | Run an action that is initialized--- with a pointer to the first element to be used.-withStartPtr :: Storable a => Vector a -> (Ptr a -> Int -> IO b) -> IO b-withStartPtr (SV x s l) f =- withForeignPtr x $ \p -> f (p `advancePtr` s) l-{-# INLINE withStartPtr #-}--incPtr :: (Storable a) => Ptr a -> Ptr a-incPtr v = advancePtr v 1-{-# INLINE incPtr #-}---- | A way of creating Vectors outside the IO monad. The @Int@--- argument gives the final size of the Vector. Unlike--- 'createAndTrim' the Vector is not reallocated if the final size--- is less than the estimated size.-unsafeCreate :: (Storable a) => Int -> (Ptr a -> IO ()) -> Vector a-unsafeCreate l f = Unsafe.performIO (create l f)-{-# INLINE unsafeCreate #-}---- | Wrapper of mallocForeignPtrArray.-create :: (Storable a) => Int -> (Ptr a -> IO ()) -> IO (Vector a)-create l f = do- fp <- mallocForeignPtrArray l- withForeignPtr fp $ \p -> f p- return $! SV fp 0 l---- | Given the maximum size needed and a function to make the contents--- of a Vector, createAndTrim makes the 'Vector'. The generating--- function is required to return the actual final size (<= the maximum--- size), and the resulting byte array is realloced to this size.------ createAndTrim is the main mechanism for creating custom, efficient--- Vector functions, using Haskell or C functions to fill the space.----createAndTrim :: (Storable a) => Int -> (Ptr a -> IO Int) -> IO (Vector a)-createAndTrim l f = do- fp <- mallocForeignPtrArray l- withForeignPtr fp $ \p -> do- l' <- f p- if assert (l' <= l) $ l' >= l- then return $! SV fp 0 l- else create l' $ \p' -> copyArray p' p l'--createAndTrim' :: (Storable a) => Int - -> (Ptr a -> IO (Int, Int, b))- -> IO (Vector a, b)-createAndTrim' l f = do- fp <- mallocForeignPtrArray l- withForeignPtr fp $ \p -> do- (off, l', res) <- f p- if assert (l' <= l) $ l' >= l- then return $! (SV fp 0 l, res)- else do ps <- create l' $ \p' -> copyArray p' (p `advancePtr` off) l'- return $! (ps, res)---- | Just like Unsafe.performIO, but we inline it. Big performance gains as--- it exposes lots of things to further inlining. /Very unsafe/. In--- particular, you should do no memory allocation inside an--- 'inlinePerformIO' block. On Hugs this is just @Unsafe.performIO@.----{-# INLINE inlinePerformIO #-}-inlinePerformIO :: IO a -> a-#if defined(__GLASGOW_HASKELL__)-inlinePerformIO (IO m) = case m realWorld# of (# _, r #) -> r-#else-inlinePerformIO = Unsafe.performIO-#endif
− Data/StorableVector/Cursor.hs
@@ -1,353 +0,0 @@-{-# LANGUAGE ExistentialQuantification #-}-{- |-Simulate a list with strict elements by a more efficient array structure.--}-module Data.StorableVector.Cursor where--import Control.Exception (assert, )-import Control.Monad.Trans.State (StateT(StateT), runStateT, )-import Data.IORef (IORef, newIORef, readIORef, writeIORef, )--import Foreign.Storable (Storable(peekElemOff, pokeElemOff))-import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )--- import Foreign.Ptr (Ptr)-import Data.StorableVector.Memory (mallocForeignPtrArray, )--import Control.Monad (when)-import Data.Maybe (isNothing)--import qualified System.Unsafe as Unsafe--import qualified Data.List.HT as ListHT-import Data.Tuple.HT (mapSnd, )--import Prelude hiding (length, foldr, zipWith, take, drop, )---{--ToDo:-I think that the state should be Storable as well-and that the IORef should be replaced by a ForeignPtr.-I hope that this is more efficient.-With this restriction @s@ cannot be e.g. a function type-but this would kill performance anyway.-Functions that need this flexibility may fall back to other data structures-(lists or chunky StorableVectors) and convert to the Cursor structure later.--}--- | Cf. StreamFusion Data.Stream-data Generator a =- forall s. -- Seq s =>- Generator- !(StateT s Maybe a) -- compute next value- {-# UNPACK #-}- !(IORef (Maybe s)) -- current state--{- |-This simulates a-@ data StrictList a = Elem !a (StrictList a) | End @-by an array and some unsafe hacks.--}-data Buffer a =- Buffer {- memory :: {-# UNPACK #-} !(ForeignPtr a),- size :: {-# UNPACK #-} !Int, -- size of allocated memory, I think I only need it for debugging- gen :: !(Generator a), -- we need this indirection for the existential type in Generator- cursor :: {-# UNPACK #-} !(IORef Int)- }--{- |-Vector is a part of a buffer.--}-data Vector a =- Vector {- buffer :: {-# UNPACK #-} !(Buffer a),- start :: {-# UNPACK #-} !Int, -- invariant: start <= cursor- maxLen :: {-# UNPACK #-} !Int -- invariant: start+maxLen <= size buffer- }----- * construction--{-# INLINE create #-}-create :: (Storable a) => Int -> Generator a -> Buffer a-create l g = Unsafe.performIO (createIO l g)---- | Wrapper of mallocForeignPtrArray.-createIO :: (Storable a) => Int -> Generator a -> IO (Buffer a)-createIO l g = do- fp <- mallocForeignPtrArray l- cur <- newIORef 0- return $! Buffer fp l g cur---{- |-@ unfoldrNTerm 20 (\n -> Just (n, succ n)) 'a' @--}-unfoldrNTerm :: (Storable b) =>- Int -> (a -> Maybe (b, a)) -> a -> Vector b-unfoldrNTerm l f x0 =- Unsafe.performIO (unfoldrNTermIO l f x0)--unfoldrNTermIO :: (Storable b) =>- Int -> (a -> Maybe (b, a)) -> a -> IO (Vector b)-unfoldrNTermIO l f x0 =- do ref <- newIORef (Just x0)- buf <- createIO l (Generator (StateT f) ref)- return (Vector buf 0 l)--unfoldrN :: (Storable b) =>- Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)-unfoldrN l f x0 =- Unsafe.performIO (unfoldrNIO l f x0)--unfoldrNIO :: (Storable b) =>- Int -> (a -> Maybe (b, a)) -> a -> IO (Vector b, Maybe a)-unfoldrNIO l f x0 =- do ref <- newIORef (Just x0)- buf <- createIO l (Generator (StateT f) ref)- s <- Unsafe.interleaveIO $- do evaluateToIO l buf- readIORef ref- return (Vector buf 0 l, s)-{--unfoldrNIO :: (Storable b) =>- Int -> (a -> Maybe (b, a)) -> a -> IO (Vector b, Maybe a)-unfoldrNIO l f x0 =- do y <- unfoldrNTermIO l f x0--- evaluateTo l y- let (Generator _ ref) = gen (buffer y)- s <- readIORef ref- return (y, s)--Data/StorableVector/Cursor.hs:98:10:- My brain just exploded.- I can't handle pattern bindings for existentially-quantified constructors.- In the binding group- (Generator _ ref) = gen (buffer y)- In the definition of `unfoldrNIO':- unfoldrNIO l f x0- = do- y <- unfoldrNTermIO l f x0- let (Generator _ ref) = gen (buffer y)- s <- readIORef ref- return (y, s)--}---{--unfoldrN :: (Storable b) =>- Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)-unfoldrN i f x0 =- let y = unfoldrNTerm i f x0- in (y, getFinalState y)--getFinalState :: (Storable b) =>- Vector b -> Maybe a-getFinalState y =- Unsafe.performIO $- ...--}---{-# INLINE pack #-}-pack :: (Storable a) => Int -> [a] -> Vector a-pack n = unfoldrNTerm n ListHT.viewL---{-# INLINE cons #-}-{- |-This is expensive and should not be used to construct lists iteratively!-A recursion-enabling 'cons' would be 'consN'-that allocates a buffer of given size,-initializes the leading cell and sets the buffer pointer to the next cell.--}-cons :: Storable a =>- a -> Vector a -> Vector a-cons x xs =- unfoldrNTerm (succ (maxLen xs))- (\(mx0,xs0) ->- fmap (mapSnd ((,) Nothing)) $- maybe- (viewL xs0)- (\x0 -> Just (x0, xs0))- mx0) $- (Just x, xs)---{-# INLINE zipWith #-}-zipWith :: (Storable a, Storable b, Storable c) =>- (a -> b -> c) -> Vector a -> Vector b -> Vector c-zipWith f ps0 qs0 =- zipNWith (min (maxLen ps0) (maxLen qs0)) f ps0 qs0---- zipWith f ps qs = pack $ List.zipWith f (unpack ps) (unpack qs)--{-# INLINE zipNWith #-}-zipNWith :: (Storable a, Storable b, Storable c) =>- Int -> (a -> b -> c) -> Vector a -> Vector b -> Vector c-zipNWith n f ps0 qs0 =- unfoldrNTerm n- (\(ps,qs) ->- do (ph,pt) <- viewL ps- (qh,qt) <- viewL qs- return (f ph qh, (pt,qt)))- (ps0,qs0)-{--let f2 = zipNWith 15 (+) f0 f1; f1 = cons 1 f2; f0 = cons (0::Int) f1 in f0--*Data.StorableVector.Cursor> let xs = unfoldrNTerm 20 (\n -> Just (n, succ n)) (0::Int)-*Data.StorableVector.Cursor> let ys = unfoldrNTerm 20 (\n -> Just (n, 2*n)) (1::Int)-*Data.StorableVector.Cursor> zipWith (+) xs ys--}------- * inspection---- | evaluate next value in a buffer-advanceIO :: Storable a =>- Buffer a -> IO (Maybe a)-advanceIO (Buffer p sz (Generator n s) cr) =- do c <- readIORef cr- assert (c < sz) $- do writeIORef cr (succ c)- ms <- readIORef s- case ms of- Nothing -> return Nothing- Just s0 ->- case runStateT n s0 of- Nothing ->- writeIORef s Nothing >>- return Nothing- Just (a,s1) ->- writeIORef s (Just s1) >>- withForeignPtr p (\q -> pokeElemOff q c a) >>- return (Just a)--{--It is tempting to turn this into a simple loop without the IORefs.-This could be compiled to an efficient strict loop,-but it would fail if the vector content depends on its own,-like in @fix (consN 1000 'a')@.--}--- | evaluate all values up to a given position-evaluateToIO :: Storable a =>- Int -> Buffer a -> IO ()-evaluateToIO l buf@(Buffer _p _sz _g cr) =- whileM- (fmap (<l) (readIORef cr))- (advanceIO buf)--whileM :: Monad m => m Bool -> m a -> m ()-whileM p f =- let recourse =- do b <- p- when b (f >> recourse)- in recourse--{-# INLINE switchL #-}-switchL :: Storable a => b -> (a -> Vector a -> b) -> Vector a -> b-switchL n j v = maybe n (uncurry j) (viewL v)---{--If it returns False the list can be empty anyway.--}-obviousNullIO :: Vector a -> IO Bool-obviousNullIO (Vector (Buffer _ _ (Generator _ s) _) _ ml) =- assert (ml >= 0) $- do b <- readIORef s- return (ml == 0 || isNothing b)--{--obviousNullIO :: Vector a -> IO Bool-obviousNullIO (Vector (Buffer _ sz (Generator _ s) _) st _) =- do b <- readIORef s- return (st >= sz || isNothing b)--}--- assert (l >= 0) $ l <= 0--{-# INLINE viewL #-}-viewL :: Storable a => Vector a -> Maybe (a, Vector a)-viewL v = Unsafe.performIO (viewLIO v)--{-# INLINE viewLIO #-}-viewLIO :: Storable a => Vector a -> IO (Maybe (a, Vector a))-viewLIO (Vector buf st ml) =- do c <- readIORef (cursor buf)- fmap (fmap (\a -> (a, Vector buf (succ st) (pred ml)))) $- assert (st <= c) $- if st == c- then advanceIO buf- else fmap Just $ withForeignPtr (memory buf) (\p -> peekElemOff p st)---{-# INLINE foldr #-}-foldr :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b-foldr k z =- let recourse = switchL z (\h t -> k h (recourse t))- in recourse---- | /O(n)/ Converts a 'Vector a' to a '[a]'.-{-# INLINE unpack #-}-unpack :: (Storable a) => Vector a -> [a]-unpack = foldr (:) []---instance (Show a, Storable a) => Show (Vector a) where- showsPrec p x = showsPrec p (unpack x)---{-# INLINE null #-}-{--This can hardly be simplified.-In order to check the list for emptiness,-we have to try to calculate the next element.-It is not enough to check whether the state is Nothing,-because when we try to compute the next value, this can be Nothing.--}-null :: Storable a => Vector a -> Bool-null = switchL True (const (const False))---{--toVector :: Storable a => Vector a -> VS.Vector a-toVector v =- VS.Cons (memory (buffer v)) ()--}---- length--drop :: (Storable a) => Int -> Vector a -> Vector a-drop n v = Unsafe.performIO $ dropIO n v--dropIO :: (Storable a) => Int -> Vector a -> IO (Vector a)-dropIO n v =- assert (n>=0) $- let pos = min (maxLen v) (start v + n)- in do evaluateToIO pos (buffer v)- return (Vector (buffer v) pos (max 0 (maxLen v - n)))--take :: (Storable a) => Int -> Vector a -> Vector a-take n v =- assert (n>=0) $- v{maxLen = min n (maxLen v)}--{--let x = unfoldrNTerm 10 (\c -> Just (c,succ c)) 'a'-let x = unfoldrNTerm 10 (\c -> Just (sum [c..100000],succ c)) (0::Int)--}---{- |-For the sake of laziness it may allocate considerably more memory than needed,-if it filters out very much.--}-{-# INLINE filter #-}-filter :: (Storable a) => (a -> Bool) -> Vector a -> Vector a-filter p xs0 =- unfoldrNTerm (maxLen xs0)- (let recourse = switchL Nothing (\x xs -> if p x then Just (x,xs) else recourse xs)- in recourse)- xs0
− Data/StorableVector/Lazy.hs
@@ -1,1359 +0,0 @@-{- |-Chunky signal stream build on StorableVector.--Hints for fusion:- - Higher order functions should always be inlined in the end- in order to turn them into machine loops- instead of calling a function in an inner loop.--}-module Data.StorableVector.Lazy where--import qualified Data.List as List-import qualified Data.StorableVector as V-import qualified Data.StorableVector.Base as VB-import qualified Data.StorableVector.Lazy.PointerPrivate as Ptr--import qualified Numeric.NonNegative.Class as NonNeg--import qualified Data.List.HT as ListHT-import Data.Tuple.HT (mapPair, mapFst, mapSnd, swap, )-import Data.Maybe.HT (toMaybe, )-import Data.Maybe (fromMaybe, )--import Foreign.Storable (Storable)--import Data.Monoid (Monoid, mempty, mappend, mconcat, )--- import Control.Arrow ((***))-import Control.Monad (liftM, liftM2, liftM3, liftM4, {- guard, -} )---import System.IO (openBinaryFile, IOMode(WriteMode, ReadMode, AppendMode),- hClose, Handle)-import Control.Exception (bracket, catch, )--import qualified System.IO.Error as Exc-import qualified System.Unsafe as Unsafe--import Test.QuickCheck (Arbitrary(..))---{--import Prelude hiding- (length, (++), concat, iterate, foldl, map, repeat, replicate, null,- zip, zipWith, zipWith3, drop, take, splitAt, takeWhile, dropWhile, reverse)--}--import qualified Prelude as P--import Data.Either (Either(Left, Right), either, )-import Data.Maybe (Maybe(Just, Nothing), maybe, )-import Data.Function (const, flip, ($), (.), )-import Data.Tuple (fst, snd, uncurry, )-import Data.Bool (Bool(True,False), not, (&&), )-import Data.Ord (Ord, (<), (>), (<=), (>=), min, max, )-import Data.Eq (Eq, (==), )-import Control.Monad (mapM_, fmap, (=<<), (>>=), (>>), return, )-import Text.Show (Show, showsPrec, showParen, showString, show, )-import Prelude- (IO, error, IOError,- FilePath, String, Char,- Enum, succ, pred,- Num, Int, sum, (+), (-), divMod, mod, fromInteger, )----newtype Vector a = SV {chunks :: [V.Vector a]}---instance (Storable a) => Monoid (Vector a) where- mempty = empty- mappend = append- mconcat = concat--instance (Storable a, Eq a) => Eq (Vector a) where- (==) = equal--instance (Storable a, Show a) => Show (Vector a) where- showsPrec p xs =- showParen (p>=10)- (showString "VectorLazy.fromChunks " .- showsPrec 10 (chunks xs))--instance (Storable a, Arbitrary a) => Arbitrary (Vector a) where- arbitrary = liftM2 pack arbitrary arbitrary----- for a list of chunk sizes see "Data.StorableVector.LazySize".-newtype ChunkSize = ChunkSize Int- deriving (Eq, Ord, Show)--instance Arbitrary ChunkSize where- arbitrary = fmap (ChunkSize . max 1 . min 2048) arbitrary--{--ToDo:-Since non-negative-0.1 we have the Monoid superclass for NonNeg.-Maybe we do not need the Num instance anymore.--}-instance Num ChunkSize where- (ChunkSize x) + (ChunkSize y) =- ChunkSize (x+y)- (-) = moduleError "ChunkSize.-" "intentionally unimplemented"- (*) = moduleError "ChunkSize.*" "intentionally unimplemented"- abs = moduleError "ChunkSize.abs" "intentionally unimplemented"- signum = moduleError "ChunkSize.signum" "intentionally unimplemented"- fromInteger = ChunkSize . fromInteger--instance Monoid ChunkSize where- mempty = ChunkSize 0- mappend (ChunkSize x) (ChunkSize y) = ChunkSize (x+y)- mconcat = ChunkSize . sum . List.map (\(ChunkSize c) -> c)--instance NonNeg.C ChunkSize where- split = NonNeg.splitDefault (\(ChunkSize c) -> c) ChunkSize--chunkSize :: Int -> ChunkSize-chunkSize x =- ChunkSize $- if x>0- then x- else moduleError "chunkSize" ("no positive number: " List.++ show x)--defaultChunkSize :: ChunkSize-defaultChunkSize =- ChunkSize 1024------ * Introducing and eliminating 'Vector's--{-# INLINE empty #-}-empty :: (Storable a) => Vector a-empty = SV []--{-# INLINE singleton #-}-singleton :: (Storable a) => a -> Vector a-singleton x = SV [V.singleton x]--fromChunks :: (Storable a) => [V.Vector a] -> Vector a-fromChunks = SV--pack :: (Storable a) => ChunkSize -> [a] -> Vector a-pack size = unfoldr size ListHT.viewL--unpack :: (Storable a) => Vector a -> [a]-unpack = List.concatMap V.unpack . chunks---{-# INLINE packWith #-}-packWith :: (Storable b) => ChunkSize -> (a -> b) -> [a] -> Vector b-packWith size f =- unfoldr size (fmap (mapFst f) . ListHT.viewL)--{-# INLINE unpackWith #-}-unpackWith :: (Storable a) => (a -> b) -> Vector a -> [b]-unpackWith f = List.concatMap (V.unpackWith f) . chunks---{-# INLINE unfoldr #-}-unfoldr :: (Storable b) =>- ChunkSize ->- (a -> Maybe (b,a)) ->- a ->- Vector b-unfoldr (ChunkSize size) f =- SV .- List.unfoldr (cancelNullVector . V.unfoldrN size f =<<) .- Just--{- |-Example:--> *Data.StorableVector.Lazy> unfoldrResult (ChunkSize 5) (\c -> if c>'z' then Left (Char.ord c) else Right (c, succ c)) 'a'-> (VectorLazy.fromChunks [Vector.pack "abcde",Vector.pack "fghij",Vector.pack "klmno",Vector.pack "pqrst",Vector.pack "uvwxy",Vector.pack "z"],123)--}-{-# INLINE unfoldrResult #-}-unfoldrResult :: (Storable b) =>- ChunkSize ->- (a -> Either c (b, a)) ->- a ->- (Vector b, c)-unfoldrResult (ChunkSize size) f =- let recourse a0 =- let (chunk, a1) =- V.unfoldrResultN size Right (either (Left . Left) Right . f) a0- in either- ((,) (if V.null chunk then [] else [chunk]))- (mapFst (chunk :) . recourse) a1- in mapFst SV . recourse---{-# INLINE sample #-}-sample :: (Storable a) => ChunkSize -> (Int -> a) -> Vector a-sample size f =- unfoldr size (\i -> Just (f i, succ i)) 0--{-# INLINE sampleN #-}-sampleN :: (Storable a) => ChunkSize -> Int -> (Int -> a) -> Vector a-sampleN size n f =- unfoldr size (\i -> toMaybe (i<n) (f i, succ i)) 0---{-# INLINE iterate #-}-iterate :: Storable a => ChunkSize -> (a -> a) -> a -> Vector a-iterate size f = unfoldr size (\x -> Just (x, f x))--repeat :: Storable a => ChunkSize -> a -> Vector a-repeat (ChunkSize size) =- SV . List.repeat . V.replicate size--cycle :: Storable a => Vector a -> Vector a-cycle =- SV . List.cycle . chunks--replicate :: Storable a => ChunkSize -> Int -> a -> Vector a-replicate (ChunkSize size) n x =- let (numChunks, rest) = divMod n size- in append- (SV (List.replicate numChunks (V.replicate size x)))- (fromChunk (V.replicate rest x))------- * Basic interface--{-# INLINE null #-}-null :: (Storable a) => Vector a -> Bool-null = List.null . chunks--length :: Vector a -> Int-length = sum . List.map V.length . chunks--equal :: (Storable a, Eq a) => Vector a -> Vector a -> Bool-equal (SV xs0) (SV ys0) =- let recourse (x:xs) (y:ys) =- let l = min (V.length x) (V.length y)- (xPrefix, xSuffix) = V.splitAt l x- (yPrefix, ySuffix) = V.splitAt l y- build z zs =- if V.null z then zs else z:zs- in xPrefix == yPrefix &&- recourse (build xSuffix xs) (build ySuffix ys)- recourse [] [] = True- -- this requires that chunks will always be non-empty- recourse _ _ = False- in recourse xs0 ys0--index :: (Storable a) => Vector a -> Int -> a-index (SV xs) n =- if n < 0- then- moduleError "index"- ("negative index: " List.++ show n)- else- List.foldr- (\x k m0 ->- let m1 = m0 - V.length x- in if m1 < 0- then VB.unsafeIndex x m0- else k m1)- (\m -> moduleError "index"- ("index too large: " List.++ show n- List.++ ", length = " List.++ show (n-m)))- xs n---{-# NOINLINE [0] cons #-}-cons :: Storable a => a -> Vector a -> Vector a-cons x = SV . (V.singleton x :) . chunks--infixr 5 `append`--{-# NOINLINE [0] append #-}-append :: Storable a => Vector a -> Vector a -> Vector a-append (SV xs) (SV ys) = SV (xs List.++ ys)---{- |-@extendL size x y@-prepends the chunk @x@ and merges it with the first chunk of @y@-if the total size is at most @size@.-This way you can prepend small chunks-while asserting a reasonable average size for chunks.--}-extendL :: Storable a => ChunkSize -> V.Vector a -> Vector a -> Vector a-extendL (ChunkSize size) x (SV yt) =- SV $- maybe- [x]- (\(y,ys) ->- if V.length x + V.length y <= size- then V.append x y : ys- else x:yt)- (ListHT.viewL yt)---concat :: (Storable a) => [Vector a] -> Vector a-concat = SV . List.concat . List.map chunks----- * Transformations--{-# INLINE map #-}-map :: (Storable x, Storable y) =>- (x -> y)- -> Vector x- -> Vector y-map f = SV . List.map (V.map f) . chunks---reverse :: Storable a => Vector a -> Vector a-reverse =- SV . List.reverse . List.map V.reverse . chunks----- * Reducing 'Vector's--{-# INLINE foldl #-}-foldl :: Storable b => (a -> b -> a) -> a -> Vector b -> a-foldl f x0 = List.foldl (V.foldl f) x0 . chunks--{-# INLINE foldl' #-}-foldl' :: Storable b => (a -> b -> a) -> a -> Vector b -> a-foldl' f x0 = List.foldl' (V.foldl f) x0 . chunks--{-# INLINE foldr #-}-foldr :: Storable b => (b -> a -> a) -> a -> Vector b -> a-foldr f x0 = List.foldr (flip (V.foldr f)) x0 . chunks---{-# INLINE monoidConcatMap #-}-monoidConcatMap :: (Storable a, Monoid m) => (a -> m) -> Vector a -> m-monoidConcatMap f =- List.foldr (mappend . V.monoidConcatMap f) mempty . chunks--{-# INLINE any #-}-any :: (Storable a) => (a -> Bool) -> Vector a -> Bool-any p = List.any (V.any p) . chunks--{-# INLINE all #-}-all :: (Storable a) => (a -> Bool) -> Vector a -> Bool-all p = List.all (V.all p) . chunks--maximum :: (Storable a, Ord a) => Vector a -> a-maximum =- List.maximum . List.map V.maximum . chunks--- List.foldl1' max . List.map V.maximum . chunks--minimum :: (Storable a, Ord a) => Vector a -> a-minimum =- List.minimum . List.map V.minimum . chunks--- List.foldl1' min . List.map V.minimum . chunks--{--sum :: (Storable a, Num a) => Vector a -> a-sum =- List.sum . List.map V.sum . chunks--product :: (Storable a, Num a) => Vector a -> a-product =- List.product . List.map V.product . chunks--}----- * inspecting a vector--{-# INLINE pointer #-}-pointer :: Storable a => Vector a -> Ptr.Pointer a-pointer = Ptr.cons . chunks--{-# INLINE viewL #-}-viewL :: Storable a => Vector a -> Maybe (a, Vector a)-viewL (SV xs0) =- do (x,xs) <- ListHT.viewL xs0- (y,ys) <- V.viewL x- return (y, append (fromChunk ys) (SV xs))--{-# INLINE viewR #-}-viewR :: Storable a => Vector a -> Maybe (Vector a, a)-viewR (SV xs0) =- do xsp <- ListHT.viewR xs0- let (xs,x) = xsp-{-- do ~(xs,x) <- ListHT.viewR xs0--}- let (ys,y) = fromMaybe (moduleError "viewR" "last chunk empty") (V.viewR x)- return (append (SV xs) (fromChunk ys), y)--{-# INLINE switchL #-}-switchL :: Storable a => b -> (a -> Vector a -> b) -> Vector a -> b-switchL n j =- maybe n (uncurry j) . viewL--{-# INLINE switchR #-}-switchR :: Storable a => b -> (Vector a -> a -> b) -> Vector a -> b-switchR n j =- maybe n (uncurry j) . viewR---{--viewLSafe :: Storable a => Vector a -> Maybe (a, Vector a)-viewLSafe (SV xs0) =- -- dropWhile would be unnecessary if we require that all chunks are non-empty- do (x,xs) <- ListHT.viewL (List.dropWhile V.null xs0)- (y,ys) <- viewLVector x- return (y, append (fromChunk ys) (SV xs))--viewRSafe :: Storable a => Vector a -> Maybe (Vector a, a)-viewRSafe (SV xs0) =- -- dropWhile would be unnecessary if we require that all chunks are non-empty- do (xs,x) <- ListHT.viewR (dropWhileRev V.null xs0)- (ys,y) <- V.viewR x- return (append (SV xs) (fromChunk ys), y)--}---{-# INLINE scanl #-}-scanl :: (Storable a, Storable b) =>- (a -> b -> a) -> a -> Vector b -> Vector a-scanl f start =- cons start . snd .- mapAccumL (\acc -> (\b -> (b,b)) . f acc) start--{-# INLINE mapAccumL #-}-mapAccumL :: (Storable a, Storable b) =>- (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)-mapAccumL f start =- mapSnd SV .- List.mapAccumL (V.mapAccumL f) start .- chunks--{-# INLINE mapAccumR #-}-mapAccumR :: (Storable a, Storable b) =>- (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)-mapAccumR f start =- mapSnd SV .- List.mapAccumR (V.mapAccumR f) start .- chunks--{-# INLINE crochetLChunk #-}-crochetLChunk :: (Storable x, Storable y) =>- (x -> acc -> Maybe (y, acc))- -> acc- -> V.Vector x- -> (V.Vector y, Maybe acc)-crochetLChunk f acc0 x0 =- mapSnd (fmap fst) $- V.unfoldrN- (V.length x0)- (\(acc,xt) ->- do (x,xs) <- V.viewL xt- (y,acc') <- f x acc- return (y, (acc',xs)))- (acc0, x0)--{-# INLINE crochetL #-}-crochetL :: (Storable x, Storable y) =>- (x -> acc -> Maybe (y, acc))- -> acc- -> Vector x- -> Vector y-crochetL f acc0 =- SV . List.unfoldr (\(xt,acc) ->- do (x,xs) <- ListHT.viewL xt- acc' <- acc- return $ mapSnd ((,) xs) $ crochetLChunk f acc' x) .- flip (,) (Just acc0) .- chunks------ * sub-vectors--{-# INLINE take #-}-take :: (Storable a) => Int -> Vector a -> Vector a-{- this order of pattern matches is certainly the most lazy one-> take 4 (pack (chunkSize 2) $ "abcd" List.++ undefined)-VectorLazy.fromChunks [Vector.pack "ab",Vector.pack "cd"]--}-take 0 _ = empty-take _ (SV []) = empty-take n (SV (x:xs)) =- let m = V.length x- in if m<=n- then SV $ (x:) $ chunks $ take (n-m) $ SV xs- else fromChunk (V.take n x)--{-# INLINE drop #-}-drop :: (Storable a) => Int -> Vector a -> Vector a-drop _ (SV []) = empty-drop n (SV (x:xs)) =- let m = V.length x- in if m<=n- then drop (n-m) (SV xs)- else SV (V.drop n x : xs)--{-# INLINE splitAt #-}-splitAt :: (Storable a) => Int -> Vector a -> (Vector a, Vector a)-splitAt n0 =- {- this order of pattern matches is certainly the most lazy one- > splitAt 4 (pack (chunkSize 2) $ "abcd" List.++ undefined)- (VectorLazy.fromChunks [Vector.pack "ab",Vector.pack "cd"],VectorLazy.fromChunks *** Exception: Prelude.undefined- -}- let recourse 0 xs = ([], xs)- recourse _ [] = ([], [])- recourse n (x:xs) =- let m = V.length x- in if m<=n- then mapFst (x:) $ recourse (n-m) xs- else mapPair ((:[]), (:xs)) $ V.splitAt n x- in mapPair (SV, SV) . recourse n0 . chunks----{-# INLINE dropMarginRem #-}--- I have used this in an inner loop thus I prefer inlining-{- |-@dropMarginRem n m xs@-drops at most the first @m@ elements of @xs@-and ensures that @xs@ still contains @n@ elements.-Additionally returns the number of elements that could not be dropped-due to the margin constraint.-That is @dropMarginRem n m xs == (k,ys)@ implies @length xs - m == length ys - k@.-Requires @length xs >= n@.--}-dropMarginRem :: (Storable a) => Int -> Int -> Vector a -> (Int, Vector a)-dropMarginRem n m xs =- List.foldl'- (\(mi,xsi) k -> (mi-k, drop k xsi))- (m,xs)- (List.map V.length $ chunks $ take m $ drop n xs)--{--This implementation does only walk once through the dropped prefix.-It is maximally lazy and minimally space consuming.--}-{-# INLINE dropMargin #-}-dropMargin :: (Storable a) => Int -> Int -> Vector a -> Vector a-dropMargin n m xs =- List.foldl' (flip drop) xs- (List.map V.length $ chunks $ take m $ drop n xs)----{-# INLINE dropWhile #-}-dropWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a-dropWhile _ (SV []) = empty-dropWhile p (SV (x:xs)) =- let y = V.dropWhile p x- in if V.null y- then dropWhile p (SV xs)- else SV (y:xs)--{-# INLINE takeWhile #-}-takeWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a-takeWhile _ (SV []) = empty-takeWhile p (SV (x:xs)) =- let y = V.takeWhile p x- in if V.length y < V.length x- then fromChunk y- else SV (x : chunks (takeWhile p (SV xs)))---{-# INLINE span #-}-span :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)-span p =- let recourse [] = ([],[])- recourse (x:xs) =- let (y,z) = V.span p x- in if V.null z- then mapFst (x:) (recourse xs)- else (chunks $ fromChunk y, (z:xs))- in mapPair (SV, SV) . recourse . chunks-{--span _ (SV []) = (empty, empty)-span p (SV (x:xs)) =- let (y,z) = V.span p x- in if V.length y == 0- then mapFst (SV . (x:) . chunks) (span p (SV xs))- else (SV [y], SV (z:xs))--}----- * other functions---{-# INLINE filter #-}-filter :: (Storable a) => (a -> Bool) -> Vector a -> Vector a-filter p =- SV . List.filter (not . V.null) . List.map (V.filter p) . chunks---{- |-Generates laziness breaks-wherever one of the input signals has a chunk boundary.--}-{-# INLINE zipWith #-}-zipWith :: (Storable a, Storable b, Storable c) =>- (a -> b -> c)- -> Vector a- -> Vector b- -> Vector c-zipWith f as0 bs0 =- let recourse at@(a:_) bt@(b:_) =- let z = V.zipWith f a b- n = V.length z- in z : recourse- (chunks $ drop n $ fromChunks at)- (chunks $ drop n $ fromChunks bt)- recourse _ _ = []- in fromChunks $ recourse (chunks as0) (chunks bs0)--{-# INLINE zipWith3 #-}-zipWith3 :: (Storable a, Storable b, Storable c, Storable d) =>- (a -> b -> c -> d)- -> Vector a- -> Vector b- -> Vector c- -> Vector d-zipWith3 f as0 bs0 cs0 =- let recourse at@(a:_) bt@(b:_) ct@(c:_) =- let z = V.zipWith3 f a b c- n = V.length z- in z : recourse- (chunks $ drop n $ fromChunks at)- (chunks $ drop n $ fromChunks bt)- (chunks $ drop n $ fromChunks ct)- recourse _ _ _ = []- in fromChunks $ recourse (chunks as0) (chunks bs0) (chunks cs0)--{-# INLINE zipWith4 #-}-zipWith4 :: (Storable a, Storable b, Storable c, Storable d, Storable e) =>- (a -> b -> c -> d -> e)- -> Vector a- -> Vector b- -> Vector c- -> Vector d- -> Vector e-zipWith4 f as0 bs0 cs0 ds0 =- let recourse at@(a:_) bt@(b:_) ct@(c:_) dt@(d:_) =- let z = V.zipWith4 f a b c d- n = V.length z- in z : recourse- (chunks $ drop n $ fromChunks at)- (chunks $ drop n $ fromChunks bt)- (chunks $ drop n $ fromChunks ct)- (chunks $ drop n $ fromChunks dt)- recourse _ _ _ _ = []- in fromChunks $- recourse (chunks as0) (chunks bs0) (chunks cs0) (chunks ds0)---{- |-Preserves chunk pattern of the last argument.--}-{-# INLINE zipWithLastPattern #-}-zipWithLastPattern :: (Storable a, Storable b, Storable c) =>- (a -> b -> c)- -> Vector a- -> Vector b- -> Vector c-zipWithLastPattern f =- crochetL (\y -> liftM (mapFst (flip f y)) . Ptr.viewL)- . pointer--{- |-Preserves chunk pattern of the last argument.--}-{-# INLINE zipWithLastPattern3 #-}-zipWithLastPattern3 ::- (Storable a, Storable b, Storable c, Storable d) =>- (a -> b -> c -> d) ->- (Vector a -> Vector b -> Vector c -> Vector d)-zipWithLastPattern3 f s0 s1 =- crochetL (\z (xt,yt) ->- liftM2- (\(x,xs) (y,ys) -> (f x y z, (xs,ys)))- (Ptr.viewL xt)- (Ptr.viewL yt))- (pointer s0, pointer s1)--{- |-Preserves chunk pattern of the last argument.--}-{-# INLINE zipWithLastPattern4 #-}-zipWithLastPattern4 ::- (Storable a, Storable b, Storable c, Storable d, Storable e) =>- (a -> b -> c -> d -> e) ->- (Vector a -> Vector b -> Vector c -> Vector d -> Vector e)-zipWithLastPattern4 f s0 s1 s2 =- crochetL (\w (xt,yt,zt) ->- liftM3- (\(x,xs) (y,ys) (z,zs) -> (f x y z w, (xs,ys,zs)))- (Ptr.viewL xt)- (Ptr.viewL yt)- (Ptr.viewL zt))- (pointer s0, pointer s1, pointer s2)---{-# INLINE zipWithSize #-}-zipWithSize :: (Storable a, Storable b, Storable c) =>- ChunkSize- -> (a -> b -> c)- -> Vector a- -> Vector b- -> Vector c-zipWithSize size f s0 s1 =- unfoldr size (\(xt,yt) ->- liftM2- (\(x,xs) (y,ys) -> (f x y, (xs,ys)))- (Ptr.viewL xt)- (Ptr.viewL yt))- (pointer s0, pointer s1)--{-# INLINE zipWithSize3 #-}-zipWithSize3 ::- (Storable a, Storable b, Storable c, Storable d) =>- ChunkSize -> (a -> b -> c -> d) ->- (Vector a -> Vector b -> Vector c -> Vector d)-zipWithSize3 size f s0 s1 s2 =- unfoldr size (\(xt,yt,zt) ->- liftM3- (\(x,xs) (y,ys) (z,zs) ->- (f x y z, (xs,ys,zs)))- (Ptr.viewL xt)- (Ptr.viewL yt)- (Ptr.viewL zt))- (pointer s0, pointer s1, pointer s2)--{-# INLINE zipWithSize4 #-}-zipWithSize4 ::- (Storable a, Storable b, Storable c, Storable d, Storable e) =>- ChunkSize -> (a -> b -> c -> d -> e) ->- (Vector a -> Vector b -> Vector c -> Vector d -> Vector e)-zipWithSize4 size f s0 s1 s2 s3 =- unfoldr size (\(xt,yt,zt,wt) ->- liftM4- (\(x,xs) (y,ys) (z,zs) (w,ws) ->- (f x y z w, (xs,ys,zs,ws)))- (Ptr.viewL xt)- (Ptr.viewL yt)- (Ptr.viewL zt)- (Ptr.viewL wt))- (pointer s0, pointer s1, pointer s2, pointer s3)----- * interleaved vectors--{-# INLINE sieve #-}-sieve :: (Storable a) => Int -> Vector a -> Vector a-sieve n =- fromChunks . List.filter (not . V.null) . snd .- List.mapAccumL- (\offset chunk ->- (mod (offset - V.length chunk) n,- V.sieve n $ V.drop offset chunk)) 0 .- chunks--{-# INLINE deinterleave #-}-deinterleave :: (Storable a) => Int -> Vector a -> [Vector a]-deinterleave n =- P.map (sieve n) . P.take n . P.iterate (switchL empty (flip const))--{- |-Interleave lazy vectors-while maintaining the chunk pattern of the first vector.-All input vectors must have the same length.--}-{-# INLINE interleaveFirstPattern #-}-interleaveFirstPattern :: (Storable a) => [Vector a] -> Vector a-interleaveFirstPattern [] = empty-interleaveFirstPattern vss@(vs:_) =- let pattern = List.map V.length $ chunks vs- split xs =- snd $- List.mapAccumL- (\x n -> swap $ mapFst (V.concat . chunks) $ splitAt n x)- xs pattern- in fromChunks $ List.map V.interleave $- List.transpose $ List.map split vss--{--interleaveFirstPattern vss@(vs:_) =- fromChunks . snd .- List.mapAccumL- (\xss n ->- swap $- mapFst (V.interleave . List.map (V.concat . chunks)) $- List.unzip $ List.map (splitAt n) xss)- vss .- List.map V.length . chunks $ vs--}----{- |-Ensure a minimal length of the list by appending pad values.--}-{- disabled INLINE pad -}-pad :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a-pad size y n0 =- let recourse n xt =- if n<=0- then xt- else- case xt of- [] -> chunks $ replicate size n y- x:xs -> x : recourse (n - V.length x) xs- in SV . recourse n0 . chunks--padAlt :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a-padAlt size x n xs =- append xs- (let m = length xs- in if n>m- then replicate size (n-m) x- else empty)-------- * Helper functions for StorableVector---{-# INLINE cancelNullVector #-}-cancelNullVector :: (V.Vector a, b) -> Maybe (V.Vector a, b)-cancelNullVector y =- toMaybe (not (V.null (fst y))) y---- if the chunk has length zero, an empty sequence is generated-{-# INLINE fromChunk #-}-fromChunk :: (Storable a) => V.Vector a -> Vector a-fromChunk x =- if V.null x- then empty- else SV [x]----{--reduceLVector :: Storable x =>- (x -> acc -> Maybe acc) -> acc -> Vector x -> (acc, Bool)-reduceLVector f acc0 x =- let recourse i acc =- if i < V.length x- then (acc, True)- else- maybe- (acc, False)- (recourse (succ i))- (f (V.index x i) acc)- in recourse 0 acc0-----{- * Fundamental functions -}--{--Usage of 'unfoldr' seems to be clumsy but that covers all cases,-like different block sizes in source and destination list.--}-crochetLSize :: (Storable x, Storable y) =>- ChunkSize- -> (x -> acc -> Maybe (y, acc))- -> acc- -> T x- -> T y-crochetLSize size f =- curry (unfoldr size (\(acc,xt) ->- do (x,xs) <- viewL xt- (y,acc') <- f x acc- return (y, (acc',xs))))--crochetListL :: (Storable y) =>- ChunkSize- -> (x -> acc -> Maybe (y, acc))- -> acc- -> [x]- -> T y-crochetListL size f =- curry (unfoldr size (\(acc,xt) ->- do (x,xs) <- ListHT.viewL xt- (y,acc') <- f x acc- return (y, (acc',xs))))----{-# NOINLINE [0] crochetFusionListL #-}-crochetFusionListL :: (Storable y) =>- ChunkSize- -> (x -> acc -> Maybe (y, acc))- -> acc- -> FList.T x- -> T y-crochetFusionListL size f =- curry (unfoldr size (\(acc,xt) ->- do (x,xs) <- FList.viewL xt- (y,acc') <- f x acc- return (y, (acc',xs))))---{-# INLINE [0] reduceL #-}-reduceL :: Storable x =>- (x -> acc -> Maybe acc) -> acc -> Vector x -> acc-reduceL f acc0 =- let recourse acc xt =- case xt of- [] -> acc- (x:xs) ->- let (acc',continue) = reduceLVector f acc x- in if continue- then recourse acc' xs- else acc'- in recourse acc0 . chunks----{- * Basic functions -}---{-# RULEZ- "Storable.append/repeat/repeat" forall size x.- append (repeat size x) (repeat size x) = repeat size x ;-- "Storable.append/repeat/replicate" forall size n x.- append (repeat size x) (replicate size n x) = repeat size x ;-- "Storable.append/replicate/repeat" forall size n x.- append (replicate size n x) (repeat size x) = repeat size x ;-- "Storable.append/replicate/replicate" forall size n m x.- append (replicate size n x) (replicate size m x) =- replicate size (n+m) x ;-- "Storable.mix/repeat/repeat" forall size x y.- mix (repeat size x) (repeat size y) = repeat size (x+y) ;-- #-}--{-# RULES- "Storable.length/cons" forall x xs.- length (cons x xs) = 1 + length xs ;-- "Storable.length/map" forall f xs.- length (map f xs) = length xs ;-- "Storable.map/cons" forall f x xs.- map f (cons x xs) = cons (f x) (map f xs) ;-- "Storable.map/repeat" forall size f x.- map f (repeat size x) = repeat size (f x) ;-- "Storable.map/replicate" forall size f x n.- map f (replicate size n x) = replicate size n (f x) ;-- "Storable.map/repeat" forall size f x.- map f (repeat size x) = repeat size (f x) ;-- {-- This can make things worse, if 'map' is applied to replicate,- since this can use of sharing.- It can also destroy the more important map/unfoldr fusion in- take n . map f . unfoldr g-- "Storable.take/map" forall n f x.- take n (map f x) = map f (take n x) ;- -}-- "Storable.take/repeat" forall size n x.- take n (repeat size x) = replicate size n x ;-- "Storable.take/take" forall n m xs.- take n (take m xs) = take (min n m) xs ;-- "Storable.drop/drop" forall n m xs.- drop n (drop m xs) = drop (n+m) xs ;-- "Storable.drop/take" forall n m xs.- drop n (take m xs) = take (max 0 (m-n)) (drop n xs) ;-- "Storable.map/mapAccumL/snd" forall g f acc0 xs.- map g (snd (mapAccumL f acc0 xs)) =- snd (mapAccumL (\acc a -> mapSnd g (f acc a)) acc0 xs) ;-- #-}--{- GHC says this is an orphaned rule- "Storable.map/mapAccumL/mapSnd" forall g f acc0 xs.- mapSnd (map g) (mapAccumL f acc0 xs) =- mapAccumL (\acc a -> mapSnd g (f acc a)) acc0 xs ;--}---{- * Fusable functions -}--scanLCrochet :: (Storable a, Storable b) =>- (a -> b -> a) -> a -> Vector b -> Vector a-scanLCrochet f start =- cons start .- crochetL (\x acc -> let y = f acc x in Just (y, y)) start--{-# INLINE mapCrochet #-}-mapCrochet :: (Storable a, Storable b) => (a -> b) -> (Vector a -> Vector b)-mapCrochet f = crochetL (\x _ -> Just (f x, ())) ()--{-# INLINE takeCrochet #-}-takeCrochet :: Storable a => Int -> Vector a -> Vector a-takeCrochet = crochetL (\x n -> toMaybe (n>0) (x, pred n))--{-# INLINE repeatUnfoldr #-}-repeatUnfoldr :: Storable a => ChunkSize -> a -> Vector a-repeatUnfoldr size = iterate size id--{-# INLINE replicateCrochet #-}-replicateCrochet :: Storable a => ChunkSize -> Int -> a -> Vector a-replicateCrochet size n = takeCrochet n . repeat size-----{--The "fromList/drop" rule is not quite accurate-because the chunk borders are moved.-Maybe 'ChunkSize' better is a list of chunks sizes.--}--{-# RULEZ- "fromList/zipWith"- forall size f (as :: Storable a => [a]) (bs :: Storable a => [a]).- fromList size (List.zipWith f as bs) =- zipWith size f (fromList size as) (fromList size bs) ;-- "fromList/drop" forall as n size.- fromList size (List.drop n as) =- drop n (fromList size as) ;- #-}----{- * Fused functions -}--type Unfoldr s a = (s -> Maybe (a,s), s)--{-# INLINE zipWithUnfoldr2 #-}-zipWithUnfoldr2 :: Storable z =>- ChunkSize- -> (x -> y -> z)- -> Unfoldr a x- -> Unfoldr b y- -> T z-zipWithUnfoldr2 size h (f,a) (g,b) =- unfoldr size- (\(a0,b0) -> liftM2 (\(x,a1) (y,b1) -> (h x y, (a1,b1))) (f a0) (g b0))--- (uncurry (liftM2 (\(x,a1) (y,b1) -> (h x y, (a1,b1)))) . (f *** g))- (a,b)--{- done by takeCrochet-{-# INLINE mapUnfoldr #-}-mapUnfoldr :: (Storable x, Storable y) =>- ChunkSize- -> (x -> y)- -> Unfoldr a x- -> T y-mapUnfoldr size g (f,a) =- unfoldr size (fmap (mapFst g) . f) a--}--{-# INLINE dropUnfoldr #-}-dropUnfoldr :: Storable x =>- ChunkSize- -> Int- -> Unfoldr a x- -> T x-dropUnfoldr size n (f,a0) =- maybe- empty- (unfoldr size f)- (nest n (\a -> fmap snd . f =<< a) (Just a0))---{- done by takeCrochet-{-# INLINE takeUnfoldr #-}-takeUnfoldr :: Storable x =>- ChunkSize- -> Int- -> Unfoldr a x- -> T x-takeUnfoldr size n0 (f,a0) =- unfoldr size- (\(a,n) ->- do guard (n>0)- (x,a') <- f a- return (x, (a', pred n)))- (a0,n0)--}---lengthUnfoldr :: Storable x =>- Unfoldr a x- -> Int-lengthUnfoldr (f,a0) =- let recourse n a =- maybe n (recourse (succ n) . snd) (f a)- in recourse 0 a0---{-# INLINE zipWithUnfoldr #-}-zipWithUnfoldr ::- (Storable b, Storable c) =>- (acc -> Maybe (a, acc))- -> (a -> b -> c)- -> acc- -> T b -> T c-zipWithUnfoldr f h a y =- crochetL (\y0 a0 ->- do (x0,a1) <- f a0- Just (h x0 y0, a1)) a y--{-# INLINE zipWithCrochetL #-}-zipWithCrochetL ::- (Storable x, Storable b, Storable c) =>- ChunkSize- -> (x -> acc -> Maybe (a, acc))- -> (a -> b -> c)- -> acc- -> T x -> T b -> T c-zipWithCrochetL size f h a x y =- crochetL (\(x0,y0) a0 ->- do (z0,a1) <- f x0 a0- Just (h z0 y0, a1))- a (zip size x y)---{-# INLINE crochetLCons #-}-crochetLCons ::- (Storable a, Storable b) =>- (a -> acc -> Maybe (b, acc))- -> acc- -> a -> T a -> T b-crochetLCons f a0 x xs =- maybe- empty- (\(y,a1) -> cons y (crochetL f a1 xs))- (f x a0)--{-# INLINE reduceLCons #-}-reduceLCons ::- (Storable a) =>- (a -> acc -> Maybe acc)- -> acc- -> a -> T a -> acc-reduceLCons f a0 x xs =- maybe a0 (flip (reduceL f) xs) (f x a0)------{-# RULES- "Storable.zipWith/share" forall size (h :: a->a->b) (x :: T a).- zipWith size h x x = map (\xi -> h xi xi) x ;---- "Storable.map/zipWith" forall size (f::c->d) (g::a->b->c) (x::T a) (y::T b).- "Storable.map/zipWith" forall size f g x y.- map f (zipWith size g x y) =- zipWith size (\xi yi -> f (g xi yi)) x y ;-- -- this rule lets map run on a different block structure- "Storable.zipWith/map,*" forall size f g x y.- zipWith size g (map f x) y =- zipWith size (\xi yi -> g (f xi) yi) x y ;-- "Storable.zipWith/*,map" forall size f g x y.- zipWith size g x (map f y) =- zipWith size (\xi yi -> g xi (f yi)) x y ;--- "Storable.drop/unfoldr" forall size f a n.- drop n (unfoldr size f a) =- dropUnfoldr size n (f,a) ;-- "Storable.take/unfoldr" forall size f a n.- take n (unfoldr size f a) =--- takeUnfoldr size n (f,a) ;- takeCrochet n (unfoldr size f a) ;-- "Storable.length/unfoldr" forall size f a.- length (unfoldr size f a) = lengthUnfoldr (f,a) ;-- "Storable.map/unfoldr" forall size g f a.- map g (unfoldr size f a) =--- mapUnfoldr size g (f,a) ;- mapCrochet g (unfoldr size f a) ;-- "Storable.map/iterate" forall size g f a.- map g (iterate size f a) =- mapCrochet g (iterate size f a) ;--{-- "Storable.zipWith/unfoldr,unfoldr" forall sizeA sizeB f g h a b n.- zipWith n h (unfoldr sizeA f a) (unfoldr sizeB g b) =- zipWithUnfoldr2 n h (f,a) (g,b) ;--}-- -- block boundaries are changed here, so it changes lazy behaviour- "Storable.zipWith/unfoldr,*" forall sizeA sizeB f h a y.- zipWith sizeA h (unfoldr sizeB f a) y =- zipWithUnfoldr f h a y ;-- -- block boundaries are changed here, so it changes lazy behaviour- "Storable.zipWith/*,unfoldr" forall sizeA sizeB f h a y.- zipWith sizeA h y (unfoldr sizeB f a) =- zipWithUnfoldr f (flip h) a y ;-- "Storable.crochetL/unfoldr" forall size f g a b.- crochetL g b (unfoldr size f a) =- unfoldr size (\(a0,b0) ->- do (y0,a1) <- f a0- (z0,b1) <- g y0 b0- Just (z0, (a1,b1))) (a,b) ;-- "Storable.reduceL/unfoldr" forall size f g a b.- reduceL g b (unfoldr size f a) =- snd- (FList.recourse (\(a0,b0) ->- do (y,a1) <- f a0- b1 <- g y b0- Just (a1, b1)) (a,b)) ;-- "Storable.crochetL/cons" forall g b x xs.- crochetL g b (cons x xs) =- crochetLCons g b x xs ;-- "Storable.reduceL/cons" forall g b x xs.- reduceL g b (cons x xs) =- reduceLCons g b x xs ;----- "Storable.take/crochetL" forall f a x n.- take n (crochetL f a x) =- takeCrochet n (crochetL f a x) ;-- "Storable.length/crochetL" forall f a x.- length (crochetL f a x) = length x ;-- "Storable.map/crochetL" forall g f a x.- map g (crochetL f a x) =- mapCrochet g (crochetL f a x) ;-- "Storable.zipWith/crochetL,*" forall size f h a x y.- zipWith size h (crochetL f a x) y =- zipWithCrochetL size f h a x y ;-- "Storable.zipWith/*,crochetL" forall size f h a x y.- zipWith size h y (crochetL f a x) =- zipWithCrochetL size f (flip h) a x y ;-- "Storable.crochetL/crochetL" forall f g a b x.- crochetL g b (crochetL f a x) =- crochetL (\x0 (a0,b0) ->- do (y0,a1) <- f x0 a0- (z0,b1) <- g y0 b0- Just (z0, (a1,b1))) (a,b) x ;-- "Storable.reduceL/crochetL" forall f g a b x.- reduceL g b (crochetL f a x) =- snd- (reduceL (\x0 (a0,b0) ->- do (y,a1) <- f x0 a0- b1 <- g y b0- Just (a1, b1)) (a,b) x) ;- #-}---}--{- * IO -}--{- |-Read the rest of a file lazily and-provide the reason of termination as IOError.-If IOError is EOF (check with @System.Error.isEOFError err@),-then the file was read successfully.-Only access the final IOError after you have consumed the file contents,-since finding out the terminating reason forces to read the entire file.-Make also sure you read the file completely,-because it is only closed when the file end is reached-(or an exception is encountered).--TODO:-In ByteString.Lazy the chunk size is reduced-if data is not immediately available.-Maybe we should adapt that behaviour-but when working with realtime streams-that may mean that the chunks are very small.--}-hGetContentsAsync :: Storable a =>- ChunkSize -> Handle -> IO (IOError, Vector a)-hGetContentsAsync (ChunkSize size) h =- let go =- Unsafe.interleaveIO $- flip catch (\err -> return (err,[])) $- do v <- V.hGet h size- if V.null v- then hClose h >>- return (Exc.mkIOError Exc.eofErrorType- "StorableVector.Lazy.hGetContentsAsync" (Just h) Nothing, [])- else fmap (mapSnd (v:)) go-{-- Unsafe.interleaveIO $- flip catch (\err -> return (err,[])) $- liftM2 (\ chunk ~(err,rest) -> (err,chunk:rest))- (V.hGet h size) go--}- in fmap (mapSnd SV) go--hGetContentsSync :: Storable a =>- ChunkSize -> Handle -> IO (Vector a)-hGetContentsSync (ChunkSize size) h =- let go =- do v <- V.hGet h size- if V.null v- then return []- else fmap (v:) go- in fmap SV go--hPut :: Storable a => Handle -> Vector a -> IO ()-hPut h = mapM_ (V.hPut h) . chunks--{--*Data.StorableVector.Lazy> print . mapSnd (length :: Vector Data.Int.Int16 -> Int) =<< readFileAsync (ChunkSize 1000) "dist/build/libHSstorablevector-0.1.3.a"-(dist/build/libHSstorablevector-0.1.3.a: hGetBuf: illegal operation (handle is closed),0)--}-{- |-The file can only closed after all values are consumed.-That is you must always assert that you consume all elements of the stream,-and that no values are missed due to lazy evaluation.-This requirement makes this function useless in many applications.--}-readFileAsync :: Storable a => ChunkSize -> FilePath -> IO (IOError, Vector a)-readFileAsync size path =- openBinaryFile path ReadMode >>= hGetContentsAsync size--writeFile :: Storable a => FilePath -> Vector a -> IO ()-writeFile path =- bracket (openBinaryFile path WriteMode) hClose . flip hPut--appendFile :: Storable a => FilePath -> Vector a -> IO ()-appendFile path =- bracket (openBinaryFile path AppendMode) hClose . flip hPut---{-# NOINLINE moduleError #-}-moduleError :: String -> String -> a-moduleError fun msg =- error ("Data.StorableVector.Lazy." List.++ fun List.++ ':':' ':msg)
− Data/StorableVector/Lazy/Builder.hs
@@ -1,159 +0,0 @@-{-# LANGUAGE Rank2Types #-}-{- |-Build a lazy storable vector by incrementally adding an element.-This is analogous to Data.Binary.Builder for Data.ByteString.Lazy.--Attention:-This implementation is still almost 3 times slower-than constructing a lazy storable vector using unfoldr-in our Chorus speed test.--}-module Data.StorableVector.Lazy.Builder (- Builder,- toLazyStorableVector,- put,- flush,- ) where--import qualified Data.StorableVector as SV-import qualified Data.StorableVector.Lazy as SVL-import qualified Data.StorableVector.ST.Strict as STV--- import qualified Data.StorableVector.ST.Lazy as STVL--import Data.StorableVector.Lazy (ChunkSize, )-import Control.Monad (liftM2, )-import Control.Monad.ST.Strict (ST, runST, )-import Data.Monoid (Monoid(mempty, mappend), )--import Foreign.Storable (Storable, )--import qualified System.Unsafe as Unsafe---{--Given an initial buffer and a function that generates the rest of the vector,-a 'Builder' generates the whole vector.-The idea is inspired by Data.Binary.Builder.--We use the strict ST monad by default-and only rare 'Unsafe.interleaveST',-since this is more efficient than using lazy ST everywhere.--Before that approach I tried to achieve this with a lazy State monad.-I found this more comprehensible but it was very slow-and had a space leak, when the last chunk shall be handled correctly.--}-newtype Builder a =- Builder {run :: forall s.- ChunkSize ->- (Buffer s a -> ST s [SV.Vector a]) ->- (Buffer s a -> ST s [SV.Vector a])- }--type Buffer s a = (STV.Vector s a, Int)----- instance Monoid (Builder a) where-{--Storable constraint not needed in the current implementation,-but who knows what will be in future ...--}-instance Storable a => Monoid (Builder a) where- {-# INLINE mempty #-}- {-# INLINE mappend #-}- mempty = Builder (\_ -> id)- mappend x y = Builder (\cs -> run x cs . run y cs)---{- |-> toLazyStorableVector (ChunkSize 7) $ Data.Monoid.mconcat $ map put ['a'..'z']--}-{-# INLINE toLazyStorableVector #-}-toLazyStorableVector :: Storable a =>- ChunkSize -> Builder a -> SVL.Vector a-toLazyStorableVector cs bld =- SVL.fromChunks $- runST (run bld cs (fmap (:[]) . fixVector) =<< newChunk cs)---{-# INLINE put #-}-put :: Storable a => a -> Builder a-put a =- Builder (\cs cont (v0,i0) ->- do STV.unsafeWrite v0 i0 a- let i1 = succ i0- if i1 < STV.length v0- then- cont (v0, i1)- else- liftM2 (:)- -- we could call 'flush' here, but this requires an extra 'SV.take'- (STV.unsafeFreeze v0)- (Unsafe.interleaveST $- cont =<< newChunk cs)- )--{--put :: Storable a => a -> Builder a-put a =- Builder (\cs cont (v0,i0) ->- if i0 < STV.length v0- then- do STV.write v0 i0 a- cont (v0, succ i0)- else- liftM2 (:)- -- we could call 'flush' here, but this requires an extra 'SV.take'- (STV.unsafeFreeze v0)- (Unsafe.interleaveST $- do (v1,i1) <- newChunk cs- STV.write v1 i1 a- cont (v1, succ i1))- )--}--{-- lazyToStrictST $- liftM2 (:)- -- we could call 'flush' here, but this requires an extra 'SV.take'- (STVL.unsafeFreeze v0)- (strictToLazyST $- do (v1,i1) <- newChunk cs- STV.write v1 i1 a- cont (v1, succ i1))--}--{--Prelude Control.Monad.ST.Lazy> Control.Monad.ST.runST (lazyToStrictST $ Monad.liftM2 (,) (strictToLazyST $ return 'a') (strictToLazyST (undefined::Monad m => m Char)))-*** Exception: Prelude.undefined--}--{- |-Set a laziness break.--}-{-# INLINE flush #-}-flush :: Storable a => Builder a-flush =- Builder (\cs cont vi0 ->- liftM2 (:)- (fixVector vi0)- (Unsafe.interleaveST $ cont =<< newChunk cs)-{-- lazyToStrictST $- liftM2 (:)- (strictToLazyST $ fixVector vi0)- (strictToLazyST $ cont =<< newChunk cs)--}- )--{-# INLINE newChunk #-}-newChunk :: (Storable a) =>- ChunkSize -> ST s (Buffer s a)-newChunk (SVL.ChunkSize size) =- fmap (flip (,) 0) $ STV.new_ size--{-# INLINE fixVector #-}-fixVector :: (Storable a) =>- Buffer s a -> ST s (SV.Vector a)-fixVector ~(v1,i1) =- fmap (SV.take i1) $ STV.unsafeFreeze v1
− Data/StorableVector/Lazy/Pattern.hs
@@ -1,371 +0,0 @@-{- |-Functions for 'StorableVector' that allow control of the size of individual chunks.--This is import for an application like the following:-You want to mix audio signals that are relatively shifted.-The structure of chunks of three streams may be illustrated as:--> [____] [____] [____] [____] ...-> [____] [____] [____] [____] ...-> [____] [____] [____] [____] ...--When we mix the streams (@zipWith3 (\x y z -> x+y+z)@)-with respect to the chunk structure of the first signal,-computing the first chunk requires full evaluation of all leading chunks of the stream.-However the last value of the third leading chunk-is much later in time than the last value of the first leading chunk.-We like to reduce these dependencies using a different chunk structure,-say--> [____] [____] [____] [____] ...-> [__] [____] [____] [____] ...-> [] [____] [____] [____] ...---}-module Data.StorableVector.Lazy.Pattern (- Vector,- ChunkSize,- chunkSize,- defaultChunkSize,- LazySize,-- empty,- singleton,- pack,- unpack,- packWith,- unpackWith,- unfoldrN,- iterateN,- cycle,- replicate,- null,- length,- cons,- append,- concat,- map,- reverse,- foldl,- foldl',- any,- all,- maximum,- minimum,- viewL,- viewR,- switchL,- switchR,- scanl,- mapAccumL,- mapAccumR,- crochetL,- take,- drop,- splitAt,- takeVectorPattern,- splitAtVectorPattern,- dropMarginRem,- dropMargin,- dropWhile,- takeWhile,- span,- filter,- zipWith,- zipWith3,- zipWith4,- zipWithSize,- zipWithSize3,- zipWithSize4,-{-- pad,- cancelNullVector,--}- ) where--import Numeric.NonNegative.Class ((-|))-import qualified Numeric.NonNegative.Chunky as LS-import qualified Data.StorableVector.Lazy as LSV-import qualified Data.StorableVector as V--import Data.StorableVector.Lazy (Vector(SV), ChunkSize(ChunkSize))--import Data.StorableVector.Lazy (- chunkSize, defaultChunkSize,- empty, singleton, unpack, unpackWith, cycle,- null, cons, append, concat, map, reverse,- foldl, foldl', any, all, maximum, minimum,- viewL, viewR, switchL, switchR,- scanl, mapAccumL, mapAccumR, crochetL,- dropMarginRem, dropMargin,- dropWhile, takeWhile, span, filter, - zipWith, zipWith3, zipWith4, - )--import qualified Data.List as List--import qualified Data.List.HT as ListHT-import Data.Tuple.HT (mapPair, mapFst, forcePair, swap, )--import Control.Monad (liftM2, liftM3, liftM4, guard, )--import Foreign.Storable (Storable)--import Prelude hiding- (length, (++), iterate, foldl, map, repeat, replicate, null,- zip, zipWith, zipWith3, drop, take, splitAt, takeWhile, dropWhile, reverse,- any, all, concat, cycle, filter, maximum, minimum, scanl, span, )-{--import Data.Maybe (Maybe(Just, Nothing), )-import Prelude (Int, (.), ($), fst, snd, (<=), flip, curry, return, fmap, not, uncurry, )--}---type LazySize = LS.T ChunkSize---- * Introducing and eliminating 'Vector's--{--Actually, this is lazy enough:--> LSV.unpack $ pack (LS.fromChunks [10,15]) (['a'..'y'] List.++ Prelude.undefined)-"abcdefghijklmnopqrstuvwxy"--}-pack :: (Storable a) => LazySize -> [a] -> Vector a-pack size =- fst . unfoldrN size ListHT.viewL---{-# INLINE packWith #-}-packWith :: (Storable b) => LazySize -> (a -> b) -> [a] -> Vector b-packWith size f =- fst . unfoldrN size (fmap (mapFst f) . ListHT.viewL)---{--{-# INLINE unfoldrNAlt #-}-unfoldrNAlt :: (Storable b) =>- LazySize- -> (a -> Maybe (b,a))- -> a- -> (Vector b, Maybe a)-unfoldrNAlt (LS.Cons size) f x =- let go sz y =- case sz of- [] -> ([], y)- (ChunkSize s : ss) ->- maybe- ([], Nothing)- ((\(c,a1) -> mapFst (c:) $ go ss a1) .- V.unfoldrN s (fmap (mapSnd f)))- (f y)- in mapFst SV $ go size (Just x)--}--{-# INLINE unfoldrN #-}-unfoldrN :: (Storable b) =>- LazySize- -> (a -> Maybe (b,a))- -> a- -> (Vector b, Maybe a)-unfoldrN size f =- let go sz y =- forcePair $- case sz of- [] -> ([], y)- (ChunkSize s : ss) ->- let m =- do a0 <- y- let p = V.unfoldrN s f a0- guard (not (V.null (fst p)))- return p- in case m of- Nothing -> ([], Nothing)- Just (c,a1) -> mapFst (c:) $ go ss a1- in mapFst SV . go (LS.toChunks size) . Just---{-# INLINE iterateN #-}-iterateN :: Storable a => LazySize -> (a -> a) -> a -> Vector a-iterateN size f =- fst . unfoldrN size (\x -> Just (x, f x))--{--Tries to be time and memory efficient-by reusing subvectors of a chunk-until a larger chunk is needed.-However, it can be a memory leak-if a huge chunk is followed by many little ones.--}-replicate :: Storable a => LazySize -> a -> Vector a-replicate size x =- SV $ snd $- List.mapAccumL- (\v (ChunkSize m) ->- if m <= V.length v- then (v, V.take m v)- else let v1 = V.replicate m x- in (v1,v1))- V.empty $- LS.toChunks size--{--replicate :: Storable a => LazySize -> a -> Vector a-replicate size x =- SV $ List.map (\(ChunkSize m) -> V.replicate m x) (LS.toChunks size)--}----- * Basic interface--length :: Vector a -> LazySize-length = LS.fromChunks . List.map chunkLength . LSV.chunks--chunkLength :: V.Vector a -> ChunkSize-chunkLength = ChunkSize . V.length--decrementLimit :: V.Vector a -> LazySize -> LazySize-decrementLimit x y =- y -| LS.fromNumber (chunkLength x)--intFromChunkSize :: ChunkSize -> Int-intFromChunkSize (ChunkSize x) = x--intFromLazySize :: LazySize -> Int-intFromLazySize =- List.sum . List.map intFromChunkSize . LS.toChunks------ * sub-vectors--{- |-Generates laziness breaks-wherever either the lazy length number or the vector has a chunk boundary.--}-{-# INLINE take #-}-take :: (Storable a) => LazySize -> Vector a -> Vector a-take n = fst . splitAt n--{- |-Preserves the chunk pattern of the lazy vector.--}-{-# INLINE takeVectorPattern #-}-takeVectorPattern :: (Storable a) => LazySize -> Vector a -> Vector a-takeVectorPattern _ (SV []) = empty-takeVectorPattern n (SV (x:xs)) =- if List.null (LS.toChunks n)- then empty- else- let remain = decrementLimit x n- in SV $ uncurry (:) $- if LS.isNull remain- then (V.take (intFromLazySize n) x, [])- else- (x, LSV.chunks $ take remain $ LSV.fromChunks xs)--{-# INLINE drop #-}-drop :: (Storable a) => LazySize -> Vector a -> Vector a-drop size xs =- List.foldl (flip (LSV.drop . intFromChunkSize)) xs (LS.toChunks size)--{-# INLINE splitAt #-}-splitAt ::- (Storable a) => LazySize -> Vector a -> (Vector a, Vector a)-splitAt size xs =- mapFst LSV.concat $ swap $- List.mapAccumL- (\xs0 n ->- swap $ LSV.splitAt (intFromChunkSize n) xs0)- xs (LS.toChunks size)--{-# INLINE splitAtVectorPattern #-}-splitAtVectorPattern ::- (Storable a) => LazySize -> Vector a -> (Vector a, Vector a)-splitAtVectorPattern n0 =- forcePair .- if List.null (LS.toChunks n0)- then (,) empty- else- let recourse n xt =- forcePair $- case xt of- [] -> ([], [])- (x:xs) ->- let remain = decrementLimit x n- in if LS.isNull remain- then mapPair ((:[]), (:xs)) $- V.splitAt (intFromLazySize n) x- else mapFst (x:) $ recourse remain xs- in mapPair (SV, SV) . recourse n0 . LSV.chunks---{-# INLINE [0] zipWithSize #-}-zipWithSize :: (Storable a, Storable b, Storable c) =>- LazySize- -> (a -> b -> c)- -> Vector a- -> Vector b- -> Vector c-zipWithSize size f =- curry (fst . unfoldrN size (\(xt,yt) ->- liftM2- (\(x,xs) (y,ys) -> (f x y, (xs,ys)))- (viewL xt)- (viewL yt)))--{-# INLINE zipWithSize3 #-}-zipWithSize3 ::- (Storable a, Storable b, Storable c, Storable d) =>- LazySize -> (a -> b -> c -> d) ->- (Vector a -> Vector b -> Vector c -> Vector d)-zipWithSize3 size f s0 s1 s2 =- fst $ unfoldrN size (\(xt,yt,zt) ->- liftM3- (\(x,xs) (y,ys) (z,zs) ->- (f x y z, (xs,ys,zs)))- (viewL xt)- (viewL yt)- (viewL zt))- (s0,s1,s2)--{-# INLINE zipWithSize4 #-}-zipWithSize4 ::- (Storable a, Storable b, Storable c, Storable d, Storable e) =>- LazySize -> (a -> b -> c -> d -> e) ->- (Vector a -> Vector b -> Vector c -> Vector d -> Vector e)-zipWithSize4 size f s0 s1 s2 s3 =- fst $ unfoldrN size (\(xt,yt,zt,wt) ->- liftM4- (\(x,xs) (y,ys) (z,zs) (w,ws) ->- (f x y z w, (xs,ys,zs,ws)))- (viewL xt)- (viewL yt)- (viewL zt)- (viewL wt))- (s0,s1,s2,s3)--{--{- |-Ensure a minimal length of the list by appending pad values.--}-{-# ONLINE pad #-}-pad :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a-pad size y n0 =- let recourse n xt =- if n<=0- then xt- else- case xt of- [] -> chunks $ replicate size n y- x:xs -> x : recourse (n - V.length x) xs- in SV . recourse n0 . chunks--padAlt :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a-padAlt size x n xs =- append xs- (let m = length xs- in if n>m- then replicate size (n-m) x- else empty)--}
− Data/StorableVector/Lazy/Pointer.hs
@@ -1,20 +0,0 @@-{- |-In principle you can traverse through a lazy storable vector-using repeated calls to @viewL@.-However this needs a bit of pointer arrangement and allocation.-This data structure makes the inner loop faster,-that consists of traversing through a chunk.--}-module Data.StorableVector.Lazy.Pointer (- Pointer, cons, viewL, switchL,- ) where--import Data.StorableVector.Lazy.PointerPrivate (Pointer(..), viewL, switchL, )-import qualified Data.StorableVector.Lazy as VL--import Foreign.Storable (Storable)---{-# INLINE cons #-}-cons :: Storable a => VL.Vector a -> Pointer a-cons = VL.pointer
− Data/StorableVector/Lazy/PointerPrivate.hs
@@ -1,42 +0,0 @@-module Data.StorableVector.Lazy.PointerPrivate where--import qualified Data.StorableVector.Pointer as VP-import qualified Data.StorableVector as V-import qualified Data.StorableVector.Base as VB--import Foreign.Storable (Storable)---data Pointer a =- Pointer {- chunks :: [VB.Vector a],- ptr :: {-# UNPACK #-} !(VP.Pointer a)- }---empty :: Storable a => Pointer a-empty =- Pointer [] (VP.cons V.empty)--{-# INLINE cons #-}-cons :: Storable a => [VB.Vector a] -> Pointer a-cons [] = empty-cons (c:cs) = Pointer cs (VP.cons c)--{-# INLINE viewL #-}-viewL :: Storable a => Pointer a -> Maybe (a, Pointer a)-viewL = switchL Nothing (curry Just)--{-# INLINE switchL #-}-switchL :: Storable a =>- b -> (a -> Pointer a -> b) -> Pointer a -> b-switchL n j =- let recourse p =- let ct = chunks p- in VP.switchL- (case ct of- [] -> n- (c:cs) -> recourse (Pointer cs (VP.cons c)))- (\a cp -> j a (Pointer ct cp))- (ptr p)- in recourse
− Data/StorableVector/Lazy/PointerPrivateIndex.hs
@@ -1,38 +0,0 @@-{--Alternative to PointerPrivate implemented at a higher level.--}-module Data.StorableVector.Lazy.PointerPrivateIndex where--import qualified Data.StorableVector as V-import qualified Data.StorableVector.Base as VB--import Foreign.Storable (Storable)---data Pointer a =- Pointer {chunks :: ![VB.Vector a], index :: !Int}---{-# INLINE cons #-}-cons :: Storable a => [VB.Vector a] -> Pointer a-cons = flip Pointer 0--{-# INLINE viewL #-}-viewL :: Storable a => Pointer a -> Maybe (a, Pointer a)-viewL = switchL Nothing (curry Just)--{-# INLINE switchL #-}-switchL :: Storable a =>- b -> (a -> Pointer a -> b) -> Pointer a -> b-switchL n j =- let recourse p =- let s = chunks p- in case s of- [] -> n- (c:cs) ->- let i = index p- d = i - V.length c- in if d < 0- then j (VB.unsafeIndex c i) (Pointer s (i+1))- else recourse (Pointer cs d)- in recourse
− Data/StorableVector/Pointer.hs
@@ -1,52 +0,0 @@-{- |-In principle you can traverse through a storable vector-using repeated calls to @viewL@ or using @index@.-However this needs a bit of pointer arrangement and allocation.-This data structure should make loops optimally fast.--}-module Data.StorableVector.Pointer where---- import qualified Data.StorableVector as V-import qualified Data.StorableVector.Base as VB--import qualified Foreign.ForeignPtr as FPtr-import Foreign.Marshal.Array (advancePtr, )-import Foreign.Storable (Storable, peek, )-import Foreign (Ptr, )-import qualified System.Unsafe as Unsafe---{--The reference to the ForeignPtr asserts,-that the array is maintained and thus is not garbage collected.-The Ptr we use for traversing would not achieve this.--}-{- |-We might have name the data type iterator.--}-data Pointer a =- Pointer {- fptr :: {-# UNPACK #-} !(FPtr.ForeignPtr a),- ptr :: {-# UNPACK #-} !(Ptr a),- left :: {-# UNPACK #-} !Int- }---{-# INLINE cons #-}-cons :: Storable a => VB.Vector a -> Pointer a-cons (VB.SV fp s l) =- Pointer fp (advancePtr (Unsafe.foreignPtrToPtr fp) s) l---{-# INLINE viewL #-}-viewL :: Storable a => Pointer a -> Maybe (a, Pointer a)-viewL = switchL Nothing (curry Just)--{-# INLINE switchL #-}-switchL :: Storable a =>- b -> (a -> Pointer a -> b) -> Pointer a -> b-switchL n j (Pointer fp p l) =- if l<=0- then n- else j (VB.inlinePerformIO (peek p)) (Pointer fp (advancePtr p 1) (l-1))--- Unsafe.performIO at this place would make SpeedPointer test 0.5 s slower
− Data/StorableVector/Private.hs
@@ -1,151 +0,0 @@-{- |-Functions that may be useful, but I'm uncertain.--}-module Data.StorableVector.Private where---import Data.StorableVector (empty, unfoldrN, viewL, length, )-import Data.StorableVector.Base--import qualified Data.Strictness.HT as Strict--import Foreign.Storable (Storable(..))--import qualified System.Unsafe as Unsafe--import Control.DeepSeq (NFData, rnf, deepseq, )--import Prelude hiding (length, )----{- |-This implementation is based on viewL-and thus not as fast as possible.--}-zipWithViewL :: (Storable a, Storable b, Storable c) =>- (a -> b -> c) -> Vector a -> Vector b -> Vector c-zipWithViewL f ps0 qs0 =- fst $ unfoldrN- (min (length ps0) (length qs0))- (\(ps,qs) ->- do (ph,pt) <- viewL ps- (qh,qt) <- viewL qs- return (f ph qh, (pt,qt)))- (ps0,qs0)---zipWithIndex :: (Storable a, Storable b, Storable c) =>- (a -> b -> c) -> Vector a -> Vector b -> Vector c-zipWithIndex f ps qs =- fst $ unfoldrN- (min (length ps) (length qs))- (\i -> Just (f (unsafeIndex ps i) (unsafeIndex qs i), succ i))- 0---unfoldrStrictN :: (Storable b, NFData a) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)--- unfoldrStrictN :: (Storable b) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)-unfoldrStrictN i f x0 =- if i <= 0- then (empty, Just x0)- else Unsafe.performIO $ createAndTrim' i $ \p -> go p 0 x0- {-- go must not be strict in the accumulator- since otherwise packN would be too strict.- -}- where- go = Strict.arguments3 $ \p n -> \x ->- if n == i- then return (0, n, Just x)- else- case f x of- Nothing -> return (0, n, Nothing)- Just (w,x') -> do poke p w--- go (incPtr p) (n+1) $! x'- go (incPtr p) (n+1) (x' `deepseq` x')--- seq (rnf x') (((go $! incPtr p) $! n+1) $! x')-{-# INLINE unfoldrStrictN #-}--unfoldrTransitionN :: (Storable b) => Int -> (a -> a) -> (a -> Maybe b) -> a -> (Vector b, a)-unfoldrTransitionN n trans emit x =- if n <= 0- then (empty, x)- else Unsafe.performIO $ createAndTrim' n $ \p ->- case emit x of- Nothing -> return (0, n, x)- Just y0 -> poke p y0 >>- {-- go must not be strict in the accumulator- since otherwise packN would be too strict.- -}- let go = Strict.arguments2 $ \p0 i0 -> \x0 ->- {-- We run 'emit' in order to evaluate the new state.- We need to return this new state- also in case the array is full.- The drawback is, that the whole vector becomes undefined- if only the state after the last element is undefined.- This is the same situation as in an unfoldr with strict state.- -}- let i1 = i0-1- x1 = trans x0- in case emit x1 of- Nothing -> return (0, n-i1, x1)- Just y1 ->- if i1 == 0- then return (0, n, x1)- else- let p1 = incPtr p0- in do poke p1 y1- go p1 i1 x1-{-- let i1 = i0-1- in if i1 == 0- then return (0, n, x0)- else- let x1 = trans x0- p1 = incPtr p0- in case emit x1 of- Nothing -> return (0, n-i1, x1)- Just y1 -> do poke p1 y1- go p1 i1 x1--}- in go p n x-{-# INLINE unfoldrTransitionN #-}---- | /O(n)/ Like 'unfoldrN' this function builds a 'Vector' from a seed--- value. However, it does always return a state value.--- The vector construction can be aborted either by reaching--- the given maximum size or by returning 'Nothing' as element.------ The following equation relates 'unfoldrN' and 'unfoldrStateN':------ > unfoldrN n f s ==--- > unfoldrStateN n--- > (maybe (error "state will be always Just")--- > ((\a -> (fmap fst a, fmap snd a)) . f))--- > (Just s)------ It is not possible to express 'unfoldrNState' in terms of 'unfoldrN'.----unfoldrStateN :: (Storable b) => Int -> (a -> (Maybe b, a)) -> a -> (Vector b, a)-unfoldrStateN i f x0 =- if i <= 0- then (empty, x0)- else Unsafe.performIO $ createAndTrim' i $ \p -> go p 0 x0- {-- go must not be strict in the accumulator- since otherwise packN would be too strict.- -}- where- go = Strict.arguments2 $ \p n -> \x ->- if n == i- then return (0, n, x)- else- let (my,x') = f x- in case my of- Nothing -> return (0, n, x)- Just w -> do poke p w- go (incPtr p) (n+1) x'-{-# INLINE unfoldrStateN #-}
− Data/StorableVector/ST/Lazy.hs
@@ -1,152 +0,0 @@-{-# LANGUAGE Rank2Types #-}-{- |-Module : Data.StorableVector.ST.Strict-License : BSD-style-Maintainer : haskell@henning-thielemann.de-Stability : experimental-Portability : portable, requires ffi-Tested with : GHC 6.4.1--Interface for access to a mutable StorableVector.--}-module Data.StorableVector.ST.Lazy (- Vector,- new,- new_,- read,- write,- modify,- unsafeRead,- unsafeWrite,- unsafeModify,- freeze,- unsafeFreeze,- thaw,- VST.length,- runSTVector,- mapST,- mapSTLazy,- ) where---- import qualified Data.StorableVector.Base as V-import qualified Data.StorableVector as VS-import qualified Data.StorableVector.Lazy as VL--import qualified Data.StorableVector.ST.Strict as VST--import Data.StorableVector.ST.Strict (Vector)---import qualified Control.Monad.ST.Lazy as ST-import Control.Monad.ST.Lazy (ST)--import Foreign.Storable (Storable)---- import Prelude (Int, ($), (+), return, const, )-import Prelude hiding (read, length, )------ * access to mutable storable vector--{-# INLINE new #-}-new :: (Storable e) =>- Int -> e -> ST s (Vector s e)-new n x = ST.strictToLazyST (VST.new n x)--{-# INLINE new_ #-}-new_ :: (Storable e) =>- Int -> ST s (Vector s e)-new_ n = ST.strictToLazyST (VST.new_ n)--{- |-> Control.Monad.ST.runST (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; read arr 3)--}-{-# INLINE read #-}-read :: (Storable e) =>- Vector s e -> Int -> ST s e-read xs n = ST.strictToLazyST (VST.read xs n)--{- |-> VS.unpack $ runSTVector (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; return arr)--}-{-# INLINE write #-}-write :: (Storable e) =>- Vector s e -> Int -> e -> ST s ()-write xs n x = ST.strictToLazyST (VST.write xs n x)--{-# INLINE modify #-}-modify :: (Storable e) =>- Vector s e -> Int -> (e -> e) -> ST s ()-modify xs n f = ST.strictToLazyST (VST.modify xs n f)---{-# INLINE unsafeRead #-}-unsafeRead :: (Storable e) =>- Vector s e -> Int -> ST s e-unsafeRead xs n = ST.strictToLazyST (VST.unsafeRead xs n)--{-# INLINE unsafeWrite #-}-unsafeWrite :: (Storable e) =>- Vector s e -> Int -> e -> ST s ()-unsafeWrite xs n x = ST.strictToLazyST (VST.unsafeWrite xs n x)--{-# INLINE unsafeModify #-}-unsafeModify :: (Storable e) =>- Vector s e -> Int -> (e -> e) -> ST s ()-unsafeModify xs n f = ST.strictToLazyST (VST.unsafeModify xs n f)---{-# INLINE freeze #-}-freeze :: (Storable e) =>- Vector s e -> ST s (VS.Vector e)-freeze xs = ST.strictToLazyST (VST.freeze xs)--{-# INLINE unsafeFreeze #-}-unsafeFreeze :: (Storable e) =>- Vector s e -> ST s (VS.Vector e)-unsafeFreeze xs = ST.strictToLazyST (VST.unsafeFreeze xs)--{-# INLINE thaw #-}-thaw :: (Storable e) =>- VS.Vector e -> ST s (Vector s e)-thaw xs = ST.strictToLazyST (VST.thaw xs)----{-# INLINE runSTVector #-}-runSTVector :: (Storable e) =>- (forall s. ST s (Vector s e)) -> VS.Vector e-runSTVector m = VST.runSTVector (ST.lazyToStrictST m)------ * operations on immutable storable vector within ST monad--{- |-> :module + Data.STRef-> VS.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapST (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VS.pack [1,2,3,4::Data.Int.Int16]))--}-{-# INLINE mapST #-}-mapST :: (Storable a, Storable b) =>- (a -> ST s b) -> VS.Vector a -> ST s (VS.Vector b)-mapST f xs =- ST.strictToLazyST (VST.mapST (ST.lazyToStrictST . f) xs)---{- |-> *Data.StorableVector.ST.Strict Data.STRef> VL.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [1,2,3,4::Data.Int.Int16]))-> "abcd"--The following should not work on infinite streams,-since we are in 'ST' with strict '>>='.-But it works. Why?--> *Data.StorableVector.ST.Strict Data.STRef.Lazy> VL.unpack $ Control.Monad.ST.Lazy.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [0::Data.Int.Int16 ..]))-> "Interrupted.--}-{-# INLINE mapSTLazy #-}-mapSTLazy :: (Storable a, Storable b) =>- (a -> ST s b) -> VL.Vector a -> ST s (VL.Vector b)-mapSTLazy f (VL.SV xs) =- fmap VL.SV $ mapM (mapST f) xs
− Data/StorableVector/ST/Private.hs
@@ -1,54 +0,0 @@-{- |-Module : Data.StorableVector.ST.Strict-License : BSD-style-Maintainer : haskell@henning-thielemann.de-Stability : experimental-Portability : portable, requires ffi-Tested with : GHC 6.4.1---}-module Data.StorableVector.ST.Private where--import qualified Data.StorableVector.Base as V--import Data.StorableVector.Memory (mallocForeignPtrArray, )--import Control.Monad.ST.Strict (ST, )--import Foreign.Ptr (Ptr, )-import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )-import Foreign.Storable (Storable, )--import qualified System.Unsafe as Unsafe---- import Prelude (Int, ($), (+), return, const, )-import Prelude hiding (read, length, )---data Vector s a =- SV {-# UNPACK #-} !(ForeignPtr a)- {-# UNPACK #-} !Int -- length----- | Wrapper of mallocForeignPtrArray.-create :: (Storable a) => Int -> (Ptr a -> IO ()) -> IO (Vector s a)-create l f = do- fp <- mallocForeignPtrArray l- withForeignPtr fp f- return $! SV fp l--{-# INLINE unsafeCreate #-}-unsafeCreate :: (Storable a) => Int -> (Ptr a -> IO ()) -> ST s (Vector s a)-unsafeCreate l f = Unsafe.ioToST $ create l f--{--This function must be in ST monad,-since it is usually called-as termination of a series of write accesses to the vector.-We must assert that no read access to the V.Vector can happen-before the end of the write accesses.-(And the caller must assert, that he actually never writes again into that vector.)--}-{-# INLINE unsafeToVector #-}-unsafeToVector :: Vector s a -> ST s (V.Vector a)-unsafeToVector (SV x l) = return (V.SV x 0 l)
− Data/StorableVector/ST/Strict.hs
@@ -1,287 +0,0 @@-{-# LANGUAGE Rank2Types #-}-{- |-Module : Data.StorableVector.ST.Strict-License : BSD-style-Maintainer : haskell@henning-thielemann.de-Stability : experimental-Portability : portable, requires ffi-Tested with : GHC 6.4.1--Interface for access to a mutable StorableVector.--}-module Data.StorableVector.ST.Strict (- Vector,- new,- new_,- read,- write,- modify,- maybeRead,- maybeWrite,- maybeModify,- unsafeRead,- unsafeWrite,- unsafeModify,- freeze,- unsafeFreeze,- thaw,- length,- runSTVector,- mapST,- mapSTLazy,- ) where--import Data.StorableVector.ST.Private- (Vector(SV), create, unsafeCreate, unsafeToVector, )-import qualified Data.StorableVector.Base as V-import qualified Data.StorableVector as VS-import qualified Data.StorableVector.Lazy as VL--import Control.Monad.ST.Strict (ST, runST, )--import Foreign.Ptr (Ptr, )-import Foreign.ForeignPtr (withForeignPtr, )-import Foreign.Storable (Storable(peek, poke))-import Foreign.Marshal.Array (advancePtr, copyArray, )-import qualified System.Unsafe as Unsafe--import qualified Data.Traversable as Traversable-import Data.Maybe.HT (toMaybe, )-import Data.Maybe (isJust, )---- import Prelude (Int, ($), (+), return, const, )-import Prelude hiding (read, length, )----- * access to mutable storable vector--{-# INLINE new #-}-new :: (Storable e) =>- Int -> e -> ST s (Vector s e)-new n x =- unsafeCreate n $- let {-# INLINE go #-}- go m p =- if m>0- then poke p x >> go (pred m) (V.incPtr p)- else return ()- in go n--{-# INLINE new_ #-}-new_ :: (Storable e) =>- Int -> ST s (Vector s e)-new_ n =- unsafeCreate n (const (return ()))---{- |-> Control.Monad.ST.runST (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; read arr 3)--}-{-# INLINE read #-}-read :: (Storable e) =>- Vector s e -> Int -> ST s e-read v n =- access "read" v n $ unsafeRead v n--{- |-> VS.unpack $ runSTVector (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; return arr)--}-{-# INLINE write #-}-write :: (Storable e) =>- Vector s e -> Int -> e -> ST s ()-write v n x =- access "write" v n $ unsafeWrite v n x--{- |-> VS.unpack $ runSTVector (do arr <- new 10 'a'; Monad.mapM_ (\n -> modify arr (mod n 8) succ) [0..10]; return arr)--}-{-# INLINE modify #-}-modify :: (Storable e) =>- Vector s e -> Int -> (e -> e) -> ST s ()-modify v n f =- access "modify" v n $ unsafeModify v n f--{-# INLINE access #-}-access :: (Storable e) =>- String -> Vector s e -> Int -> ST s a -> ST s a-access name (SV _v l) n act =- if 0<=n && n<l- then act- else error ("StorableVector.ST." ++ name ++ ": index out of range")---{- |-Returns @Just e@, when the element @e@ could be read-and 'Nothing' if the index was out of range.-This way you can avoid duplicate index checks-that may be needed when using 'read'.--> Control.Monad.ST.runST (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; read arr 3)--In future 'maybeRead' will replace 'read'.--}-{-# INLINE maybeRead #-}-maybeRead :: (Storable e) =>- Vector s e -> Int -> ST s (Maybe e)-maybeRead v n =- maybeAccess v n $ unsafeRead v n--{- |-Returns 'True' if the element could be written-and 'False' if the index was out of range.--> runSTVector (do arr <- new_ 10; foldr (\c go i -> maybeWrite arr i c >>= \cont -> if cont then go (succ i) else return arr) (error "unreachable") ['a'..] 0)--In future 'maybeWrite' will replace 'write'.--}-{-# INLINE maybeWrite #-}-maybeWrite :: (Storable e) =>- Vector s e -> Int -> e -> ST s Bool-maybeWrite v n x =- fmap isJust $ maybeAccess v n $ unsafeWrite v n x--{- |-Similar to 'maybeWrite'.--In future 'maybeModify' will replace 'modify'.--}-{-# INLINE maybeModify #-}-maybeModify :: (Storable e) =>- Vector s e -> Int -> (e -> e) -> ST s Bool-maybeModify v n f =- fmap isJust $ maybeAccess v n $ unsafeModify v n f--{-# INLINE maybeAccess #-}-maybeAccess :: (Storable e) =>- Vector s e -> Int -> ST s a -> ST s (Maybe a)-maybeAccess (SV _v l) n act =- Traversable.sequence $ toMaybe (0<=n && n<l) act-{-- if 0<=n && n<l- then fmap Just act- else return Nothing--}--{-# INLINE unsafeRead #-}-unsafeRead :: (Storable e) =>- Vector s e -> Int -> ST s e-unsafeRead v n =- unsafeAccess v n $ peek--{-# INLINE unsafeWrite #-}-unsafeWrite :: (Storable e) =>- Vector s e -> Int -> e -> ST s ()-unsafeWrite v n x =- unsafeAccess v n $ \p -> poke p x--{-# INLINE unsafeModify #-}-unsafeModify :: (Storable e) =>- Vector s e -> Int -> (e -> e) -> ST s ()-unsafeModify v n f =- unsafeAccess v n $ \p -> poke p . f =<< peek p--{-# INLINE unsafeAccess #-}-unsafeAccess :: (Storable e) =>- Vector s e -> Int -> (Ptr e -> IO a) -> ST s a-unsafeAccess (SV v _l) n act =- Unsafe.ioToST (withForeignPtr v $ \p -> act (advancePtr p n))---{-# INLINE freeze #-}-freeze :: (Storable e) =>- Vector s e -> ST s (VS.Vector e)-freeze (SV x l) =- Unsafe.ioToST $- V.create l $ \p ->- withForeignPtr x $ \f ->- copyArray p f (fromIntegral l)--{- |-This is like 'freeze' but it does not copy the vector.-You must make sure that you never write again to the array.-It is best to use 'unsafeFreeze' only at the end of a block,-that is run by 'runST'.--}-{-# INLINE unsafeFreeze #-}-unsafeFreeze :: (Storable e) =>- Vector s e -> ST s (VS.Vector e)-unsafeFreeze = unsafeToVector---{-# INLINE thaw #-}-thaw :: (Storable e) =>- VS.Vector e -> ST s (Vector s e)-thaw v =- Unsafe.ioToST $- V.withStartPtr v $ \f l ->- create l $ \p ->- copyArray p f (fromIntegral l)---{-# INLINE length #-}-length ::- Vector s e -> Int-length (SV _v l) = l---{-# INLINE runSTVector #-}-runSTVector :: (Storable e) =>- (forall s. ST s (Vector s e)) -> VS.Vector e-runSTVector m =- runST (unsafeToVector =<< m)------ * operations on immutable storable vector within ST monad--{- |-> :module + Data.STRef-> VS.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapST (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VS.pack [1,2,3,4::Data.Int.Int16]))--}-{-# INLINE mapST #-}-mapST :: (Storable a, Storable b) =>- (a -> ST s b) -> VS.Vector a -> ST s (VS.Vector b)-mapST f (V.SV px sx n) =- let {-# INLINE go #-}- go l q p =- if l>0- then- do Unsafe.ioToST . poke p =<< f =<< Unsafe.ioToST (peek q)- go (pred l) (advancePtr q 1) (advancePtr p 1)- else return ()- in do ys@(SV py _) <- new_ n- go n- (Unsafe.foreignPtrToPtr px `advancePtr` sx)- (Unsafe.foreignPtrToPtr py)- unsafeToVector ys--{--mapST f xs@(V.SV v s l) =- let go l q p =- if l>0- then- do poke p =<< stToIO . f =<< peek q- go (pred l) (advancePtr q 1) (advancePtr p 1)- else return ()- n = VS.length xs- in return $ V.unsafeCreate n $ \p ->- withForeignPtr v $ \q -> go n (advancePtr q s) p--}---{- |-> *Data.StorableVector.ST.Strict Data.STRef> VL.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [1,2,3,4::Data.Int.Int16]))-> "abcd"--The following should not work on infinite streams,-since we are in 'ST' with strict '>>='.-But it works. Why?--> *Data.StorableVector.ST.Strict Data.STRef> VL.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [0::Data.Int.Int16 ..]))-> "Interrupted.--}-{-# INLINE mapSTLazy #-}-mapSTLazy :: (Storable a, Storable b) =>- (a -> ST s b) -> VL.Vector a -> ST s (VL.Vector b)-mapSTLazy f (VL.SV xs) =- fmap VL.SV $ mapM (mapST f) xs
+ speedtest/Data/StorableVector/Private.hs view
@@ -0,0 +1,151 @@+{- |+Functions that may be useful, but I'm uncertain.+-}+module Data.StorableVector.Private where+++import Data.StorableVector (empty, unfoldrN, viewL, length, )+import Data.StorableVector.Base++import qualified Data.Strictness.HT as Strict++import Foreign.Storable (Storable(..))++import qualified System.Unsafe as Unsafe++import Control.DeepSeq (NFData, rnf, deepseq, )++import Prelude hiding (length, )++++{- |+This implementation is based on viewL+and thus not as fast as possible.+-}+zipWithViewL :: (Storable a, Storable b, Storable c) =>+ (a -> b -> c) -> Vector a -> Vector b -> Vector c+zipWithViewL f ps0 qs0 =+ fst $ unfoldrN+ (min (length ps0) (length qs0))+ (\(ps,qs) ->+ do (ph,pt) <- viewL ps+ (qh,qt) <- viewL qs+ return (f ph qh, (pt,qt)))+ (ps0,qs0)+++zipWithIndex :: (Storable a, Storable b, Storable c) =>+ (a -> b -> c) -> Vector a -> Vector b -> Vector c+zipWithIndex f ps qs =+ fst $ unfoldrN+ (min (length ps) (length qs))+ (\i -> Just (f (unsafeIndex ps i) (unsafeIndex qs i), succ i))+ 0+++unfoldrStrictN :: (Storable b, NFData a) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)+-- unfoldrStrictN :: (Storable b) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)+unfoldrStrictN i f x0 =+ if i <= 0+ then (empty, Just x0)+ else Unsafe.performIO $ createAndTrim' i $ \p -> go p 0 x0+ {-+ go must not be strict in the accumulator+ since otherwise packN would be too strict.+ -}+ where+ go = Strict.arguments3 $ \p n -> \x ->+ if n == i+ then return (0, n, Just x)+ else+ case f x of+ Nothing -> return (0, n, Nothing)+ Just (w,x') -> do poke p w+-- go (incPtr p) (n+1) $! x'+ go (incPtr p) (n+1) (x' `deepseq` x')+-- seq (rnf x') (((go $! incPtr p) $! n+1) $! x')+{-# INLINE unfoldrStrictN #-}++unfoldrTransitionN :: (Storable b) => Int -> (a -> a) -> (a -> Maybe b) -> a -> (Vector b, a)+unfoldrTransitionN n trans emit x =+ if n <= 0+ then (empty, x)+ else Unsafe.performIO $ createAndTrim' n $ \p ->+ case emit x of+ Nothing -> return (0, n, x)+ Just y0 -> poke p y0 >>+ {-+ go must not be strict in the accumulator+ since otherwise packN would be too strict.+ -}+ let go = Strict.arguments2 $ \p0 i0 -> \x0 ->+ {-+ We run 'emit' in order to evaluate the new state.+ We need to return this new state+ also in case the array is full.+ The drawback is, that the whole vector becomes undefined+ if only the state after the last element is undefined.+ This is the same situation as in an unfoldr with strict state.+ -}+ let i1 = i0-1+ x1 = trans x0+ in case emit x1 of+ Nothing -> return (0, n-i1, x1)+ Just y1 ->+ if i1 == 0+ then return (0, n, x1)+ else+ let p1 = incPtr p0+ in do poke p1 y1+ go p1 i1 x1+{-+ let i1 = i0-1+ in if i1 == 0+ then return (0, n, x0)+ else+ let x1 = trans x0+ p1 = incPtr p0+ in case emit x1 of+ Nothing -> return (0, n-i1, x1)+ Just y1 -> do poke p1 y1+ go p1 i1 x1+-}+ in go p n x+{-# INLINE unfoldrTransitionN #-}++-- | /O(n)/ Like 'unfoldrN' this function builds a 'Vector' from a seed+-- value. However, it does always return a state value.+-- The vector construction can be aborted either by reaching+-- the given maximum size or by returning 'Nothing' as element.+--+-- The following equation relates 'unfoldrN' and 'unfoldrStateN':+--+-- > unfoldrN n f s ==+-- > unfoldrStateN n+-- > (maybe (error "state will be always Just")+-- > ((\a -> (fmap fst a, fmap snd a)) . f))+-- > (Just s)+--+-- It is not possible to express 'unfoldrNState' in terms of 'unfoldrN'.+--+unfoldrStateN :: (Storable b) => Int -> (a -> (Maybe b, a)) -> a -> (Vector b, a)+unfoldrStateN i f x0 =+ if i <= 0+ then (empty, x0)+ else Unsafe.performIO $ createAndTrim' i $ \p -> go p 0 x0+ {-+ go must not be strict in the accumulator+ since otherwise packN would be too strict.+ -}+ where+ go = Strict.arguments2 $ \p n -> \x ->+ if n == i+ then return (0, n, x)+ else+ let (my,x') = f x+ in case my of+ Nothing -> return (0, n, x)+ Just w -> do poke p w+ go (incPtr p) (n+1) x'+{-# INLINE unfoldrStateN #-}
+ src/Data/StorableVector.hs view
@@ -0,0 +1,1571 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+--+-- Module : StorableVector+-- Copyright : (c) The University of Glasgow 2001,+-- (c) David Roundy 2003-2005,+-- (c) Simon Marlow 2005+-- (c) Don Stewart 2005-2006+-- (c) Bjorn Bringert 2006+-- (c) Spencer Janssen 2006+-- (c) Henning Thielemann 2008-2013+--+--+-- License : BSD-style+--+-- Maintainer : Henning Thielemann+-- Stability : experimental+-- Portability : portable, requires ffi and cpp+-- Tested with : GHC 6.4.1 and Hugs March 2005+--++--+-- | A time and space-efficient implementation of vectors using+-- packed arrays, suitable for high performance use, both in terms+-- of large data quantities, or high speed requirements. Vectors+-- are encoded as strict arrays, held in a 'ForeignPtr',+-- and can be passed between C and Haskell with little effort.+--+-- This module is intended to be imported @qualified@, to avoid name+-- clashes with "Prelude" functions. eg.+--+-- > import qualified Data.StorableVector as V+--+-- Original GHC implementation by Bryan O\'Sullivan. Rewritten to use+-- UArray by Simon Marlow. Rewritten to support slices and use+-- ForeignPtr by David Roundy. Polished and extended by Don Stewart.+-- Generalized to any Storable value by Spencer Janssen.+-- Chunky lazy stream, also with chunk pattern control,+-- mutable access in ST monad, Builder monoid by Henning Thieleman.++module Data.StorableVector (++ -- * The @Vector@ type+ Vector,++ -- * Introducing and eliminating 'Vector's+ empty,+ singleton,+ pack,+ unpack,+ packN,+ packWith,+ unpackWith,++ -- * Basic interface+ cons,+ snoc,+ append,+ head,+ last,+ tail,+ init,+ null,+ length,+ viewL,+ viewR,+ switchL,+ switchR,++ -- * Transforming 'Vector's+ map,+ reverse,+ intersperse,+ transpose,++ -- * Reducing 'Vector's (folds)+ foldl,+ foldl',+ foldl1,+ foldl1',+ foldr,+ foldr1,++ -- ** Special folds+ concat,+ concatMap,+ monoidConcatMap,+ any,+ all,+ maximum,+ minimum,++ -- * Building 'Vector's+ -- ** Scans+ scanl,+ scanl1,+ scanr,+ scanr1,++ -- ** Accumulating maps+ mapAccumL,+ mapAccumR,+ mapIndexed,++ -- ** Unfolding 'Vector's+ replicate,+ iterateN,+ unfoldr,+ unfoldrN,+ unfoldrResultN,+ sample,++ -- * Substrings++ -- ** Breaking strings+ take,+ drop,+ splitAt,+ takeWhile,+ dropWhile,+ span,+ spanEnd,+ break,+ breakEnd,+ group,+ groupBy,+ inits,+ tails,++ -- ** Breaking into many substrings+ split,+ splitWith,+ tokens,++ -- ** Joining strings+ join,++ -- * Predicates+ isPrefixOf,+ isSuffixOf,++ -- * Searching 'Vector's++ -- ** Searching by equality+ elem,+ notElem,++ -- ** Searching with a predicate+ find,+ filter,++ -- * Indexing 'Vector's+ index,+ elemIndex,+ elemIndices,+ elemIndexEnd,+ findIndex,+ findIndices,+ count,+ findIndexOrEnd,++ -- * Zipping and unzipping 'Vector's+ zip,+ zipWith,+ zipWith3,+ zipWith4,+ unzip,+ copy,++ -- * Interleaved 'Vector's+ sieve,+ deinterleave,+ interleave,++ -- * IO+ hGet,+ hPut,+ readFile,+ writeFile,+ appendFile,++ ) where++import Data.StorableVector.Base++import qualified System.Unsafe as Unsafe++import Control.Exception (assert, bracket, )+import System.IO (IO, FilePath, Handle, IOMode(..),+ openBinaryFile, hClose, hFileSize,+ hGetBuf, hPutBuf, )++import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )+import Foreign.Marshal.Array (advancePtr, copyArray, withArray, )+import Foreign.Ptr (Ptr, minusPtr, )+import Foreign.Storable (Storable(..))++import qualified Test.QuickCheck as QC++import qualified Control.Monad.Trans.Cont as MC+import Control.Monad (mplus, guard, when, liftM2, liftM3, liftM4,+ mapM, sequence_, return, (=<<), (>>=), (>>), )+import Data.Functor (fmap, )+import Data.Monoid (Monoid, mempty, mappend, mconcat, )++import qualified Data.List as List+import qualified Data.List.HT as ListHT+import qualified Data.Strictness.HT as Strict+import Text.Show (show, )+import Data.Function (flip, id, const, ($), (.), )+import Data.List (and, (++), )+import Data.Tuple.HT (mapSnd, )+import Data.Tuple (uncurry, curry, fst, snd, )+import Data.Either (Either(Left, Right), )+import Data.Maybe.HT (toMaybe, )+import Data.Maybe (Maybe(Just, Nothing), maybe, fromMaybe, isJust, )+import Data.Bool.HT (if', )+import Data.Bool (Bool(False, True), not, otherwise, (&&), (||), )+import Data.Ord (Ord, min, max, (<), (<=), (>), (>=), )+import Data.Eq (Eq, (==), (/=), )++import qualified Prelude as P+import Prelude+ (String, Int, (*), (-), (+), div, mod,+ fromIntegral, error, undefined, )+++-- -----------------------------------------------------------------------------++instance (Storable a, Eq a) => Eq (Vector a) where+ (==) = equal++instance (Storable a) => Monoid (Vector a) where+ mempty = empty+ mappend = append+ mconcat = concat++instance (Storable a, QC.Arbitrary a) => QC.Arbitrary (Vector a) where+ arbitrary = pack `fmap` QC.arbitrary++-- | /O(n)/ Equality on the 'Vector' type.+equal :: (Storable a, Eq a) => Vector a -> Vector a -> Bool+equal a b =+ Unsafe.performIO $+ withStartPtr a $ \paf la ->+ withStartPtr b $ \pbf lb ->+ if la /= lb+ then+ return False+ else+ if paf == pbf+ then return True+ else+ let go = Strict.arguments3 $ \p q l ->+ if l==0+ then return True+ else+ do x <- peek p+ y <- peek q+ if x==y+ then go (incPtr p) (incPtr q) (l-1)+ else return False+ in go paf pbf la+{-# INLINE equal #-}++-- -----------------------------------------------------------------------------+-- Introducing and eliminating 'Vector's++-- | /O(1)/ The empty 'Vector'+empty :: (Storable a) => Vector a+empty = unsafeCreate 0 $ const $ return ()+{-# NOINLINE empty #-}++-- | /O(1)/ Construct a 'Vector' containing a single element+singleton :: (Storable a) => a -> Vector a+singleton c = unsafeCreate 1 $ \p -> poke p c+{-# INLINE singleton #-}++-- | /O(n)/ Convert a '[a]' into a 'Vector a'.+--+pack :: (Storable a) => [a] -> Vector a+pack str = unsafeCreate (P.length str) $ \p -> go p str+ where+ go = Strict.arguments2 $ \p ->+ ListHT.switchL+ (return ())+ (\x xs -> poke p x >> go (incPtr p) xs)++-- | /O(n)/ Convert first @n@ elements of a '[a]' into a 'Vector a'.+--+packN :: (Storable a) => Int -> [a] -> (Vector a, [a])+packN n =+ mapSnd (fromMaybe []) . unfoldrN n ListHT.viewL++-- | /O(n)/ Converts a 'Vector a' to a '[a]'.+unpack :: (Storable a) => Vector a -> [a]+unpack = foldr (:) []+{-# INLINE unpack #-}++------------------------------------------------------------------------++-- | /O(n)/ Convert a list into a 'Vector' using a conversion function+packWith :: (Storable b) => (a -> b) -> [a] -> Vector b+packWith k str = unsafeCreate (P.length str) $ \p -> go p str+ where+ go = Strict.arguments2 $ \p ->+ ListHT.switchL+ (return ())+ (\x xs -> poke p (k x) >> go (incPtr p) xs)+ -- less space than pokeElemOff+{-# INLINE packWith #-}++{-+*Data.StorableVector> List.take 10 $ unpackWith id $ pack [0..10000000::Int]+[0,1,2,3,4,5,6,7,8,9]+(19.18 secs, 2327851592 bytes)+-}+-- | /O(n)/ Convert a 'Vector' into a list using a conversion function+unpackWith :: (Storable a) => (a -> b) -> Vector a -> [b]+unpackWith f = foldr ((:) . f) []+{-# INLINE unpackWith #-}++{-+That's too inefficient, since it builds the list from back to front,+that is, in a too strict manner.++-- | /O(n)/ Convert a 'Vector' into a list using a conversion function+unpackWith :: (Storable a) => (a -> b) -> Vector a -> [b]+unpackWith _ (SV _ _ 0) = []+unpackWith k v@(SV ps s l) = inlinePerformIO $ withStartPtr v $ \p ->+ go p (l - 1) []+ where+ STRICT3(go)+ go p 0 acc = peek p >>= \e -> return (k e : acc)+ go p n acc = peekElemOff p n >>= \e -> go p (n-1) (k e : acc)+{-# INLINE unpackWith #-}+++*Data.StorableVector> List.take 10 $ unpack $ pack [0..10000000::Int]+[0,1,2,3,4,5,6,7,8,9]+(18.57 secs, 2323959948 bytes)+*Data.StorableVector> unpack $ take 10 $ pack [0..10000000::Int]+[0,1,2,3,4,5,6,7,8,9]+(18.40 secs, 2324002120 bytes)+*Data.StorableVector> List.take 10 $ unpackWith id $ pack [0..10000000::Int]+Interrupted.+-}++-- ---------------------------------------------------------------------+-- Basic interface++-- | /O(1)/ Test whether a 'Vector' is empty.+null :: Vector a -> Bool+null (SV _ _ l) = assert (l >= 0) $ l <= 0+{-# INLINE null #-}++-- ---------------------------------------------------------------------+-- | /O(1)/ 'length' returns the length of a 'Vector' as an 'Int'.+length :: Vector a -> Int+length (SV _ _ l) = assert (l >= 0) $ l++--+-- length/loop fusion. When taking the length of any fuseable loop,+-- rewrite it as a foldl', and thus avoid allocating the result buffer+-- worth around 10% in speed testing.+--++{-# INLINE [1] length #-}++------------------------------------------------------------------------++-- | /O(n)/ 'cons' is analogous to (:) for lists, but of different+-- complexity, as it requires a memcpy.+cons :: (Storable a) => a -> Vector a -> Vector a+cons c v =+ unsafeWithStartPtr v $ \f l ->+ create (l + 1) $ \p -> do+ poke p c+ copyArray (incPtr p) f (fromIntegral l)+{-# INLINE cons #-}++-- | /O(n)/ Append an element to the end of a 'Vector'+snoc :: (Storable a) => Vector a -> a -> Vector a+snoc v c =+ unsafeWithStartPtr v $ \f l ->+ create (l + 1) $ \p -> do+ copyArray p f l+ pokeElemOff p l c+{-# INLINE snoc #-}++-- | /O(1)/ Extract the first element of a 'Vector', which must be non-empty.+-- An exception will be thrown in the case of an empty 'Vector'.+head :: (Storable a) => Vector a -> a+head =+ withNonEmptyVector "head" $ \ p s _l -> foreignPeek p s+{-# INLINE head #-}++-- | /O(1)/ Extract the elements after the head of a 'Vector', which must be non-empty.+-- An exception will be thrown in the case of an empty 'Vector'.+tail :: (Storable a) => Vector a -> Vector a+tail =+ withNonEmptyVector "tail" $ \ p s l -> SV p (s+1) (l-1)+{-# INLINE tail #-}++laxTail :: (Storable a) => Vector a -> Vector a+laxTail v@(SV fp s l) =+ if l<=0+ then v+ else SV fp (s+1) (l-1)+{-# INLINE laxTail #-}++-- | /O(1)/ Extract the last element of a 'Vector', which must be finite and non-empty.+-- An exception will be thrown in the case of an empty 'Vector'.+last :: (Storable a) => Vector a -> a+last =+ withNonEmptyVector "last" $ \ p s l -> foreignPeek p (s+l-1)+{-# INLINE last #-}++-- | /O(1)/ Return all the elements of a 'Vector' except the last one.+-- An exception will be thrown in the case of an empty 'Vector'.+init :: Vector a -> Vector a+init =+ withNonEmptyVector "init" $ \ p s l -> SV p s (l-1)+{-# INLINE init #-}++-- | /O(n)/ Append two Vectors+append :: (Storable a) => Vector a -> Vector a -> Vector a+append xs ys =+ if' (null xs) ys $+ if' (null ys) xs $+ concat [xs,ys]+{-# INLINE append #-}++-- ---------------------------------------------------------------------+-- Transformations++-- | /O(n)/ 'map' @f xs@ is the 'Vector' obtained by applying @f@ to each+-- element of @xs@.+map :: (Storable a, Storable b) => (a -> b) -> Vector a -> Vector b+map f v =+ unsafeWithStartPtr v $ \a len ->+ create len $ \p ->+ let go = Strict.arguments3 $+ \ n p1 p2 ->+ when (n>0) $+ do poke p2 . f =<< peek p1+ go (n-1) (incPtr p1) (incPtr p2)+ in go len a p+{-# INLINE map #-}++{-+mapByIndex :: (Storable a, Storable b) => (a -> b) -> Vector a -> Vector b+mapByIndex f v = inlinePerformIO $ withStartPtr v $ \a len ->+ create len $ \p2 ->+ let go = Strict.arguments1 $ \ n ->+ when (n<len) $+ do pokeElemOff p2 n . f =<< peekElemOff a n+ go (n+1)+ in go 0+-}++-- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order.+reverse :: (Storable a) => Vector a -> Vector a+reverse v =+ unsafeWithStartPtr v $ \f l ->+ create l $ \p ->+ sequence_ [peekElemOff f i >>= pokeElemOff p (l - i - 1)+ | i <- [0 .. l - 1]]++-- | /O(n)/ The 'intersperse' function takes a element and a+-- 'Vector' and \`intersperses\' that element between the elements of+-- the 'Vector'. It is analogous to the intersperse function on+-- Lists.+intersperse :: (Storable a) => a -> Vector a -> Vector a+intersperse c = pack . List.intersperse c . unpack++-- | The 'transpose' function transposes the rows and columns of its+-- 'Vector' argument.+transpose :: (Storable a) => [Vector a] -> [Vector a]+transpose ps = P.map pack (List.transpose (P.map unpack ps))++-- ---------------------------------------------------------------------+-- Reducing 'Vector's++-- | 'foldl', applied to a binary operator, a starting value (typically+-- the left-identity of the operator), and a Vector, reduces the+-- 'Vector' using the binary operator, from left to right.+-- This function is subject to array fusion.+foldl :: (Storable a) => (b -> a -> b) -> b -> Vector a -> b+foldl f v xs =+ foldr (\x k acc -> k (f acc x)) id xs v+{-# INLINE foldl #-}++-- | 'foldl\'' is like 'foldl', but strict in the accumulator.+foldl' :: (Storable a) => (b -> a -> b) -> b -> Vector a -> b+foldl' f b v =+ Unsafe.performIO $ withStartPtr v $ \ptr l ->+ let q = ptr `advancePtr` l+ go = Strict.arguments2 $ \p z ->+ if p == q+ then return z+ else go (incPtr p) . f z =<< peek p+ in go ptr b+{-# INLINE foldl' #-}++-- | 'foldr', applied to a binary operator, a starting value+-- (typically the right-identity of the operator), and a 'Vector',+-- reduces the 'Vector' using the binary operator, from right to left.+-- However, it is not the same as 'foldl' applied to the reversed vector.+-- Actually 'foldr' starts processing with the first element,+-- and thus can be used for efficiently building a singly linked list+-- by @foldr (:) [] vec@.+-- Unfortunately 'foldr' is quite slow for low-level loops,+-- since GHC (up to 6.12.1) cannot detect the loop.+foldr :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b+foldr = foldrByLoop+{-# INLINE foldr #-}++{-+*Data.StorableVector> List.length $ foldrBySwitch (:) [] $ replicate 1000000 'a'+1000000+(11.29 secs, 1183476300 bytes)+*Data.StorableVector> List.length $ foldrByIO (:) [] $ replicate 1000000 'a'+1000000+(7.86 secs, 1033901140 bytes)+*Data.StorableVector> List.length $ foldrByIndex (:) [] $ replicate 1000000 'a'+1000000+(7.86 secs, 914340420 bytes)+*Data.StorableVector> List.length $ foldrByLoop (:) [] $ replicate 1000000 'a'+1000000+(6.38 secs, 815355460 bytes)+-}+{-+We cannot simply increment the pointer,+since ForeignPtr cannot be incremented.+We also cannot convert from ForeignPtr to Ptr+and increment that instead,+because we need to keep the reference to ForeignPtr,+otherwise memory might be freed.+We can also not perform loop entirely in strict IO,+since this eat up the stack quickly+and 'foldr' might be used to build a list lazily.+-}+foldrByLoop :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b+foldrByLoop f z (SV fp s l) =+ let end = s+l+ go = Strict.arguments1 $ \k ->+ if k<end+ then f (foreignPeek fp k) (go (succ k))+ else z+ in go s+{-# INLINE foldrByLoop #-}++{-+foldrByIO :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b+foldrByIO f z v@(SV fp _ _) =+ unsafeWithStartPtr v $+ let go = Strict.arguments2 $ \p l ->+ Unsafe.interleaveIO $+ if l>0+ then liftM2 f (peek p) (go (incPtr p) (pred l))+ else touchForeignPtr fp >> return z+ in go+{-# INLINE foldrByIO #-}++foldrByIndex :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b+foldrByIndex k z xs =+ let recourse n =+ if n < length xs+ then k (unsafeIndex xs n) (recourse (succ n))+ else z+ in recourse 0+{-# INLINE foldrByIndex #-}++{-+This implementation is a bit inefficient,+since switchL creates a new Vector structure+instead of just incrementing an index.+-}+foldrBySwitch :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b+foldrBySwitch k z =+ let recourse = switchL z (\h t -> k h (recourse t))+ in recourse+{-# INLINE foldrBySwitch #-}+-}+++-- | 'foldl1' is a variant of 'foldl' that has no starting value+-- argument, and thus must be applied to non-empty 'Vector's.+-- This function is subject to array fusion.+-- An exception will be thrown in the case of an empty 'Vector'.+foldl1 :: (Storable a) => (a -> a -> a) -> Vector a -> a+foldl1 f =+ switchL+ (errorEmpty "foldl1")+ (foldl f)+{-# INLINE foldl1 #-}++-- | 'foldl1\'' is like 'foldl1', but strict in the accumulator.+-- An exception will be thrown in the case of an empty 'Vector'.+foldl1' :: (Storable a) => (a -> a -> a) -> Vector a -> a+foldl1' f =+ switchL+ (errorEmpty "foldl1'")+ (foldl' f)+{-# INLINE foldl1' #-}++-- | 'foldr1' is a variant of 'foldr' that has no starting value argument,+-- and thus must be applied to non-empty 'Vector's+-- An exception will be thrown in the case of an empty 'Vector'.+foldr1 :: (Storable a) => (a -> a -> a) -> Vector a -> a+foldr1 f =+ switchR+ (errorEmpty "foldr1")+ (flip (foldr f))+{-# INLINE foldr1 #-}++-- ---------------------------------------------------------------------+-- Special folds++-- | /O(n)/ Concatenate a list of 'Vector's.+concat :: (Storable a) => [Vector a] -> Vector a+concat [] = empty+concat [ps] = ps+concat xs = unsafeCreate len $ \ptr -> go ptr xs+ where len = P.sum . P.map length $ xs+ go =+ Strict.arguments2 $ \ptr ->+ ListHT.switchL+ (return ())+ (\v ps -> do+ withStartPtr v $ copyArray ptr+ go (ptr `advancePtr` length v) ps)++-- | Map a function over a 'Vector' and concatenate the results+concatMap :: (Storable a, Storable b) => (a -> Vector b) -> Vector a -> Vector b+concatMap f = concat . unpackWith f+{-# INLINE concatMap #-}++-- | This is like @mconcat . map f@,+-- but in many cases the result of @f@ will not be storable.+monoidConcatMap :: (Storable a, Monoid m) => (a -> m) -> Vector a -> m+monoidConcatMap f =+ foldr (mappend . f) mempty+{-# INLINE monoidConcatMap #-}++-- | /O(n)/ Applied to a predicate and a 'Vector', 'any' determines if+-- any element of the 'Vector' satisfies the predicate.+any :: (Storable a) => (a -> Bool) -> Vector a -> Bool+any f = foldr ((||) . f) False+{-# INLINE any #-}++-- | /O(n)/ Applied to a predicate and a 'Vector', 'all' determines+-- if all elements of the 'Vector' satisfy the predicate.+all :: (Storable a) => (a -> Bool) -> Vector a -> Bool+all f = foldr ((&&) . f) True+{-# INLINE all #-}++------------------------------------------------------------------------++-- | /O(n)/ 'maximum' returns the maximum value from a 'Vector'+-- This function will fuse.+-- An exception will be thrown in the case of an empty 'Vector'.+maximum :: (Storable a, Ord a) => Vector a -> a+maximum = foldl1' max++-- | /O(n)/ 'minimum' returns the minimum value from a 'Vector'+-- This function will fuse.+-- An exception will be thrown in the case of an empty 'Vector'.+minimum :: (Storable a, Ord a) => Vector a -> a+minimum = foldl1' min++------------------------------------------------------------------------++switchL :: Storable a => b -> (a -> Vector a -> b) -> Vector a -> b+switchL n j x =+ if null x+ then n+ else j (unsafeHead x) (unsafeTail x)+{-# INLINE switchL #-}++switchR :: Storable a => b -> (Vector a -> a -> b) -> Vector a -> b+switchR n j x =+ if null x+ then n+ else j (unsafeInit x) (unsafeLast x)+{-# INLINE switchR #-}++viewL :: Storable a => Vector a -> Maybe (a, Vector a)+viewL = switchL Nothing (curry Just)+{-# INLINE viewL #-}++viewR :: Storable a => Vector a -> Maybe (Vector a, a)+viewR = switchR Nothing (curry Just)+{-# INLINE viewR #-}++-- | The 'mapAccumL' function behaves like a combination of 'map' and+-- 'foldl'; it applies a function to each element of a 'Vector',+-- passing an accumulating parameter from left to right, and returning a+-- final value of this accumulator together with the new list.+mapAccumL :: (Storable a, Storable b) => (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)+mapAccumL f acc0 as0 =+ let (bs, Just (acc2, _)) =+ unfoldrN (length as0)+ (\(acc,as) ->+ fmap+ (\(asHead,asTail) ->+ let (acc1,b) = f acc asHead+ in (b, (acc1, asTail)))+ (viewL as))+ (acc0,as0)+ in (acc2, bs)+{-# INLINE mapAccumL #-}++-- | The 'mapAccumR' function behaves like a combination of 'map' and+-- 'foldr'; it applies a function to each element of a 'Vector',+-- passing an accumulating parameter from right to left, and returning a+-- final value of this accumulator together with the new 'Vector'.+mapAccumR :: (Storable a, Storable b) => (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)+mapAccumR f acc0 as0 =+ let (bs, Just (acc2, _)) =+ unfoldlN (length as0)+ (\(acc,as) ->+ fmap+ (\(asInit,asLast) ->+ let (acc1,b) = f acc asLast+ in (b, (acc1, asInit)))+ (viewR as))+ (acc0,as0)+ in (acc2, bs)+{-# INLINE mapAccumR #-}++-- | /O(n)/ map functions, provided with the index at each position+mapIndexed :: (Storable a, Storable b) => (Int -> a -> b) -> Vector a -> Vector b+mapIndexed f = snd . mapAccumL (\i e -> (i + 1, f i e)) 0+{-# INLINE mapIndexed #-}++-- ---------------------------------------------------------------------+-- Building 'Vector's++-- | 'scanl' is similar to 'foldl', but returns a list of successive+-- reduced values from the left. This function will fuse.+--+-- > scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...]+--+-- Note that+--+-- > last (scanl f z xs) == foldl f z xs.+scanl :: (Storable a, Storable b) => (a -> b -> a) -> a -> Vector b -> Vector a+scanl f acc0 as0 =+ fst $+ unfoldrN (succ (length as0))+ (fmap $ \(acc,as) ->+ (acc,+ fmap+ (\(asHead,asTail) ->+ (f acc asHead, asTail))+ (viewL as)))+ (Just (acc0, as0))++-- less efficient but much more comprehensible+-- scanl f z ps =+-- cons z (snd (mapAccumL (\acc a -> let b = f acc a in (b,b)) z ps))++ -- n.b. haskell's List scan returns a list one bigger than the+ -- input, so we need to snoc here to get some extra space, however,+ -- it breaks map/up fusion (i.e. scanl . map no longer fuses)+{-# INLINE scanl #-}++-- | 'scanl1' is a variant of 'scanl' that has no starting value argument.+-- This function will fuse.+--+-- > scanl1 f [x1, x2, ...] == [x1, x1 `f` x2, ...]+scanl1 :: (Storable a) => (a -> a -> a) -> Vector a -> Vector a+scanl1 f = switchL empty (scanl f)+{-# INLINE scanl1 #-}++-- | scanr is the right-to-left dual of scanl.+scanr :: (Storable a, Storable b) => (a -> b -> b) -> b -> Vector a -> Vector b+scanr f acc0 as0 =+ fst $+ unfoldlN (succ (length as0))+ (fmap $ \(acc,as) ->+ (acc,+ fmap+ (\(asInit,asLast) ->+ (f asLast acc, asInit))+ (viewR as)))+ (Just (acc0, as0))+{-# INLINE scanr #-}++-- | 'scanr1' is a variant of 'scanr' that has no starting value argument.+scanr1 :: (Storable a) => (a -> a -> a) -> Vector a -> Vector a+scanr1 f = switchR empty (flip (scanl f))+{-# INLINE scanr1 #-}++-- ---------------------------------------------------------------------+-- Unfolds and replicates++-- | /O(n)/ 'replicate' @n x@ is a 'Vector' of length @n@ with @x@+-- the value of every element.+--+{- nice implementation+replicate :: (Storable a) => Int -> a -> Vector a+replicate n c =+ fst $ unfoldrN n (const $ Just (c, ())) ()+-}++{-+fast implementation++Maybe it could be made even faster by plainly copying the bit pattern of the first element.+Since there is no function like 'memset',+we could not warrant that the implementation is really efficient+for the actual machine we run on.+-}+replicate :: (Storable a) => Int -> a -> Vector a+replicate n c =+ if n <= 0+ then empty+ else unsafeCreate n $+ let go = Strict.arguments2 $ \i p ->+ if i == 0+ then return ()+ else poke p c >> go (pred i) (incPtr p)+ in go n+{-# INLINE replicate #-}+{-+For 'replicate 10000000 (42::Int)' generates:++Main_zdwa_info:+ movl (%ebp),%eax+ testl %eax,%eax+ jne .LcfIQ+ movl $ghczmprim_GHCziUnit_Z0T_closure+1,%esi+ addl $8,%ebp+ jmp *(%ebp)+.LcfIQ:+ movl 4(%ebp),%ecx+ movl $42,(%ecx)+ decl %eax+ addl $4,4(%ebp)+ movl %eax,(%ebp)+ jmp Main_zdwa_info++that is, the inner loop consists of 9 instructions,+where I would write something like:+ # counter in %ecx+ testl %ecx+ jz skip_loop+ movl $42,%ebx+start_loop:+ movl %ebx,(%edx)+ addl $4,%edx+ loop start_loop+skip_loop:++and need only 3 instructions in the loop.+-}+++-- | /O(n)/ 'iterateN' @n f x@ is a 'Vector' of length @n@+-- where the elements of @x@ are generated by repeated application of @f@.+--+iterateN :: (Storable a) => Int -> (a -> a) -> a -> Vector a+iterateN n f =+ fst . unfoldrN n (\a -> Just (a, f a))+{-# INLINE iterateN #-}++-- | /O(n)/, where /n/ is the length of the result. The 'unfoldr'+-- function is analogous to the List \'unfoldr\'. 'unfoldr' builds a+-- 'Vector' from a seed value. The function takes the element and+-- returns 'Nothing' if it is done producing the 'Vector or returns+-- 'Just' @(a,b)@, in which case, @a@ is the next element in the 'Vector',+-- and @b@ is the seed value for further production.+--+-- Examples:+--+-- > unfoldr (\x -> if x <= 5 then Just (x, x + 1) else Nothing) 0+-- > == pack [0, 1, 2, 3, 4, 5]+--+unfoldr :: (Storable b) => (a -> Maybe (b, a)) -> a -> Vector b+unfoldr f = concat . unfoldChunk 32 64+ where unfoldChunk n n' x =+ case unfoldrN n f x of+ (s, mx) -> s : maybe [] (unfoldChunk n' (n+n')) mx+{-# INLINE unfoldr #-}++-- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a 'Vector' from a seed+-- value. However, the length of the result is limited by the first+-- argument to 'unfoldrN'. This function is more efficient than 'unfoldr'+-- when the maximum length of the result is known.+--+-- The following equation relates 'unfoldrN' and 'unfoldr':+--+-- > fst (unfoldrN n f s) == take n (unfoldr f s)+--+unfoldrN :: (Storable b) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)+unfoldrN n f x0 =+ if n <= 0+ then (empty, Just x0)+ else Unsafe.performIO $ createAndTrim' n $ \p -> go p n x0+ {-+ go must not be strict in the accumulator+ since otherwise packN would be too strict.+ -}+ where+ go = Strict.arguments2 $ \p i -> \x ->+ if i == 0+ then return (0, n-i, Just x)+ else+ case f x of+ Nothing -> return (0, n-i, Nothing)+ Just (w,x') -> do poke p w+ go (incPtr p) (i-1) x'+{-# INLINE unfoldrN #-}++{-+Examples:++f i = Just (i::Char, succ i)++f i = toMaybe (i<='p') (i::Char, succ i)++-}+-- | /O(n)/ Like 'unfoldrN' this function builds a 'Vector'+-- from a seed value with limited size.+-- Additionally it returns a value, that depends on the state,+-- but is not necessarily the state itself.+-- If end of vector and end of the generator coincide,+-- then the result is as if only the end of vector is reached.+--+-- Example:+--+-- > unfoldrResultN 30 Char.ord (\c -> if c>'z' then Left 1000 else Right (c, succ c)) 'a'+--+-- The following equation relates 'unfoldrN' and 'unfoldrResultN':+--+-- > unfoldrN n f s ==+-- > unfoldrResultN n Just+-- > (maybe (Left Nothing) Right . f) s+--+-- It is not possible to express 'unfoldrResultN' in terms of 'unfoldrN'.+--+unfoldrResultN :: (Storable b) => Int -> (a -> c) -> (a -> Either c (b, a)) -> a -> (Vector b, c)+unfoldrResultN i g f x0 =+ if i <= 0+ then (empty, g x0)+ else Unsafe.performIO $ createAndTrim' i $ \p -> go p 0 x0+ {-+ go must not be strict in the accumulator+ since otherwise packN would be too strict.+ -}+ where+ go = Strict.arguments2 $ \p n -> \a0 ->+ if n == i+ then return (0, n, g a0)+ else+ case f a0 of+ Left c -> return (0, n, c)+ Right (b,a1) -> do poke p b+ go (incPtr p) (n+1) a1+{-# INLINE unfoldrResultN #-}++unfoldlN :: (Storable b) => Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)+unfoldlN i f x0+ | i < 0 = (empty, Just x0)+ | otherwise = Unsafe.performIO $ createAndTrim' i $ \p -> go (p `advancePtr` i) i x0+ where go = Strict.arguments2 $ \p n -> \x ->+ if n == 0+ then return (n, i, Just x)+ else+ case f x of+ Nothing -> return (n, i, Nothing)+ Just (w,x') ->+ let p' = p `advancePtr` (-1)+ in do poke p' w+ go p' (n-1) x'+{-# INLINE unfoldlN #-}+++-- | /O(n)/, where /n/ is the length of the result.+-- This function constructs a vector by evaluating a function+-- that depends on the element index.+-- It is a special case of 'unfoldrN' and can in principle be parallelized.+--+-- Examples:+--+-- > sample 26 (\x -> chr(ord 'a'+x))+-- > == pack "abcdefghijklmnopqrstuvwxyz"+--+sample :: (Storable a) => Int -> (Int -> a) -> Vector a+sample n f =+ fst $ unfoldrN n (\i -> Just (f i, succ i)) 0+{-# INLINE sample #-}+++-- ---------------------------------------------------------------------+-- Substrings++-- | /O(1)/ 'take' @n@, applied to a 'Vector' @xs@, returns the prefix+-- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@.+take :: (Storable a) => Int -> Vector a -> Vector a+take n ps@(SV x s l)+ | n <= 0 = empty+ | n >= l = ps+ | otherwise = SV x s n+{-# INLINE take #-}++-- | /O(1)/ 'drop' @n xs@ returns the suffix of @xs@ after the first @n@+-- elements, or 'empty' if @n > 'length' xs@.+drop :: (Storable a) => Int -> Vector a -> Vector a+drop n ps@(SV x s l)+ | n <= 0 = ps+ | n >= l = empty+ | otherwise = SV x (s+n) (l-n)+{-# INLINE drop #-}++-- | /O(1)/ 'splitAt' @n xs@ is equivalent to @('take' n xs, 'drop' n xs)@.+splitAt :: (Storable a) => Int -> Vector a -> (Vector a, Vector a)+splitAt n ps@(SV x s l)+ | n <= 0 = (empty, ps)+ | n >= l = (ps, empty)+ | otherwise = (SV x s n, SV x (s+n) (l-n))+{-# INLINE splitAt #-}++-- | 'takeWhile', applied to a predicate @p@ and a 'Vector' @xs@,+-- returns the longest prefix (possibly empty) of @xs@ of elements that+-- satisfy @p@.+takeWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a+takeWhile f ps = unsafeTake (findIndexOrEnd (not . f) ps) ps+{-# INLINE takeWhile #-}++-- | 'dropWhile' @p xs@ returns the suffix remaining after 'takeWhile' @p xs@.+dropWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a+dropWhile f ps = unsafeDrop (findIndexOrEnd (not . f) ps) ps+{-# INLINE dropWhile #-}++-- | 'break' @p@ is equivalent to @'span' ('not' . p)@.+break :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)+break p ps = case findIndexOrEnd p ps of n -> (unsafeTake n ps, unsafeDrop n ps)+{-# INLINE break #-}++-- | 'breakEnd' behaves like 'break' but from the end of the 'Vector'+--+-- breakEnd p == spanEnd (not.p)+breakEnd :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)+breakEnd p ps = splitAt (findFromEndUntil p ps) ps++-- | 'span' @p xs@ breaks the 'Vector' into two segments. It is+-- equivalent to @('takeWhile' p xs, 'dropWhile' p xs)@+span :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)+span p ps = break (not . p) ps+{-# INLINE span #-}++-- | 'spanEnd' behaves like 'span' but from the end of the 'Vector'.+-- We have+--+-- > spanEnd (not.isSpace) "x y z" == ("x y ","z")+--+-- and+--+-- > spanEnd (not . isSpace) ps+-- > ==+-- > let (x,y) = span (not.isSpace) (reverse ps) in (reverse y, reverse x)+--+spanEnd :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)+spanEnd p ps = splitAt (findFromEndUntil (not.p) ps) ps++-- | /O(n)/ Splits a 'Vector' into components delimited by+-- separators, where the predicate returns True for a separator element.+-- The resulting components do not contain the separators. Two adjacent+-- separators result in an empty component in the output. eg.+--+-- > splitWith (=='a') "aabbaca" == ["","","bb","c",""]+-- > splitWith (=='a') [] == []+--+splitWith :: (Storable a) => (a -> Bool) -> Vector a -> [Vector a]+splitWith _ (SV _ _ 0) = []+splitWith p ps = loop ps+ where+ loop =+ uncurry (:) .+ mapSnd (switchL [] (\ _ t -> loop t)) .+ break p+{-# INLINE splitWith #-}++-- | /O(n)/ Break a 'Vector' into pieces separated by the+-- argument, consuming the delimiter. I.e.+--+-- > split '\n' "a\nb\nd\ne" == ["a","b","d","e"]+-- > split 'a' "aXaXaXa" == ["","X","X","X"]+-- > split 'x' "x" == ["",""]+--+-- and+--+-- > join [c] . split c == id+-- > split == splitWith . (==)+--+-- As for all splitting functions in this library, this function does+-- not copy the substrings, it just constructs new 'Vector's that+-- are slices of the original.+--+split :: (Storable a, Eq a) => a -> Vector a -> [Vector a]+split w v = splitWith (w==) v+{-# INLINE split #-}++-- | Like 'splitWith', except that sequences of adjacent separators are+-- treated as a single separator. eg.+--+-- > tokens (=='a') "aabbaca" == ["bb","c"]+--+tokens :: (Storable a) => (a -> Bool) -> Vector a -> [Vector a]+tokens f = P.filter (not.null) . splitWith f+{-# INLINE tokens #-}++-- | The 'group' function takes a 'Vector' and returns a list of+-- 'Vector's such that the concatenation of the result is equal to the+-- argument. Moreover, each sublist in the result contains only equal+-- elements. For example,+--+-- > group "Mississippi" = ["M","i","ss","i","ss","i","pp","i"]+--+-- It is a special case of 'groupBy', which allows the programmer to+-- supply their own equality test. It is about 40% faster than+-- /groupBy (==)/+group :: (Storable a, Eq a) => Vector a -> [Vector a]+group xs =+ switchL []+ (\ h _ ->+ let (ys, zs) = span (== h) xs+ in ys : group zs)+ xs++-- | The 'groupBy' function is the non-overloaded version of 'group'.+groupBy :: (Storable a) => (a -> a -> Bool) -> Vector a -> [Vector a]+groupBy k xs =+ switchL []+ (\ h t ->+ let n = 1 + findIndexOrEnd (not . k h) t+ in unsafeTake n xs : groupBy k (unsafeDrop n xs))+ xs+{-# INLINE groupBy #-}+++-- | /O(n)/ The 'join' function takes a 'Vector' and a list of+-- 'Vector's and concatenates the list after interspersing the first+-- argument between each element of the list.+join :: (Storable a) => Vector a -> [Vector a] -> Vector a+join s = concat . List.intersperse s+{-# INLINE join #-}++-- ---------------------------------------------------------------------+-- Indexing 'Vector's++-- | /O(1)/ 'Vector' index (subscript) operator, starting from 0.+index :: (Storable a) => Vector a -> Int -> a+index ps n+ | n < 0 = moduleError "index" ("negative index: " ++ show n)+ | n >= length ps = moduleError "index" ("index too large: " ++ show n+ ++ ", length = " ++ show (length ps))+ | otherwise = ps `unsafeIndex` n+{-# INLINE index #-}++-- | /O(n)/ The 'elemIndex' function returns the index of the first+-- element in the given 'Vector' which is equal to the query+-- element, or 'Nothing' if there is no such element.+elemIndex :: (Storable a, Eq a) => a -> Vector a -> Maybe Int+elemIndex c = findIndex (c==)+{-# INLINE elemIndex #-}++-- | /O(n)/ The 'elemIndexEnd' function returns the last index of the+-- element in the given 'Vector' which is equal to the query+-- element, or 'Nothing' if there is no such element. The following+-- holds:+--+-- > elemIndexEnd c xs ==+-- > (-) (length xs - 1) `fmap` elemIndex c (reverse xs)+--+elemIndexEnd :: (Storable a, Eq a) => a -> Vector a -> Maybe Int+elemIndexEnd c =+ fst .+ foldl+ (\(ri,i) x -> (if c==x then Just i else ri, succ i))+ (Nothing,0)+{-# INLINE elemIndexEnd #-}++-- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning+-- the indices of all elements equal to the query element, in ascending order.+elemIndices :: (Storable a, Eq a) => a -> Vector a -> [Int]+elemIndices c = findIndices (c==)+{-# INLINE elemIndices #-}++-- | count returns the number of times its argument appears in the 'Vector'+--+-- > count = length . elemIndices+--+-- But more efficiently than using length on the intermediate list.+count :: (Storable a, Eq a) => a -> Vector a -> Int+count w =+ foldl (flip $ \c -> if c==w then succ else id) 0+{-+count w sv =+ List.length $ elemIndices w sv+-}+{-# INLINE count #-}++-- | The 'findIndex' function takes a predicate and a 'Vector' and+-- returns the index of the first element in the 'Vector'+-- satisfying the predicate.+findIndex :: (Storable a) => (a -> Bool) -> Vector a -> Maybe Int+findIndex p xs =+ {- The implementation is in principle the same as for findIndices,+ but we use the First monoid, instead of the List/append monoid.+ We could also implement findIndex in terms of monoidConcatMap. -}+ foldr+ (\x k n ->+ toMaybe (p x) n `mplus` k (succ n))+ (const Nothing) xs 0+{-# INLINE findIndex #-}++-- | The 'findIndices' function extends 'findIndex', by returning the+-- indices of all elements satisfying the predicate, in ascending order.+findIndices :: (Storable a) => (a -> Bool) -> Vector a -> [Int]+findIndices p xs =+ foldr+ (\x k n ->+ (if p x then (n:) else id)+ (k (succ n)))+ (const []) xs 0+{-# INLINE findIndices #-}++-- | 'findIndexOrEnd' is a variant of findIndex, that returns the length+-- of the string if no element is found, rather than Nothing.+findIndexOrEnd :: (Storable a) => (a -> Bool) -> Vector a -> Int+findIndexOrEnd p xs =+ foldr+ (\x k n ->+ if p x then n else k (succ n))+ id xs 0+{-# INLINE findIndexOrEnd #-}++-- ---------------------------------------------------------------------+-- Searching Vectors++-- | /O(n)/ 'elem' is the 'Vector' membership predicate.+elem :: (Storable a, Eq a) => a -> Vector a -> Bool+elem c ps = isJust $ elemIndex c ps+{-# INLINE elem #-}++-- | /O(n)/ 'notElem' is the inverse of 'elem'+notElem :: (Storable a, Eq a) => a -> Vector a -> Bool+notElem c ps = not (elem c ps)+{-# INLINE notElem #-}++-- | /O(n)/ 'filter', applied to a predicate and a 'Vector',+-- returns a 'Vector' containing those elements that satisfy the+-- predicate. This function is subject to array fusion.+filter :: (Storable a) => (a -> Bool) -> Vector a -> Vector a+filter p (SV fp s l) =+ let end = s+l+ in fst $+ unfoldrN l+ (let go = Strict.arguments1 $ \k0 ->+ do guard (k0<end)+ let x = foreignPeek fp k0+ k1 = succ k0+ if p x+ then Just (x,k1)+ else go k1+ in go)+ s+{-# INLINE filter #-}++-- | /O(n)/ The 'find' function takes a predicate and a 'Vector',+-- and returns the first element in matching the predicate, or 'Nothing'+-- if there is no such element.+--+-- > find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> Nothing+--+find :: (Storable a) => (a -> Bool) -> Vector a -> Maybe a+find f p = fmap (unsafeIndex p) (findIndex f p)+{-# INLINE find #-}++-- ---------------------------------------------------------------------+-- Searching for substrings++-- | /O(n)/ The 'isPrefixOf' function takes two 'Vector' and returns 'True'+-- iff the first is a prefix of the second.+isPrefixOf :: (Storable a, Eq a) => Vector a -> Vector a -> Bool+isPrefixOf x@(SV _ _ l1) y@(SV _ _ l2) =+ l1 <= l2 && x == unsafeTake l1 y++-- | /O(n)/ The 'isSuffixOf' function takes two 'Vector's and returns 'True'+-- iff the first is a suffix of the second.+--+-- The following holds:+--+-- > isSuffixOf x y == reverse x `isPrefixOf` reverse y+--+isSuffixOf :: (Storable a, Eq a) => Vector a -> Vector a -> Bool+isSuffixOf x@(SV _ _ l1) y@(SV _ _ l2) =+ l1 <= l2 && x == unsafeDrop (l2 - l1) y++-- ---------------------------------------------------------------------+-- Zipping++-- | /O(n)/ 'zip' takes two 'Vector's and returns a list of+-- corresponding pairs of elements. If one input 'Vector' is short,+-- excess elements of the longer 'Vector' are discarded. This is+-- equivalent to a pair of 'unpack' operations.+zip :: (Storable a, Storable b) => Vector a -> Vector b -> [(a, b)]+zip ps qs =+ maybe [] id $+ do (ph,pt) <- viewL ps+ (qh,qt) <- viewL qs+ return ((ph,qh) : zip pt qt)++-- | 'zipWith' generalises 'zip' by zipping with the function given as+-- the first argument, instead of a tupling function. For example,+-- @'zipWith' (+)@ is applied to two 'Vector's to produce the list of+-- corresponding sums.+zipWith :: (Storable a, Storable b, Storable c) =>+ (a -> b -> c) -> Vector a -> Vector b -> Vector c+zipWith f as bs =+ unsafeWithStartPtr as $ \pa0 la ->+ withStartPtr bs $ \pb0 lb ->+ let len = min la lb+ in create len $ \p0 ->+ let go = Strict.arguments4 $ \n p pa pb ->+ when (n>0) $+ liftM2 f (peek pa) (peek pb) >>= poke p >>+ go (pred n) (incPtr p) (incPtr pa) (incPtr pb)+ in go len p0 pa0 pb0+++-- zipWith f ps qs = pack $ List.zipWith f (unpack ps) (unpack qs)+{-# INLINE zipWith #-}++-- | Like 'zipWith' but for three input vectors+zipWith3 :: (Storable a, Storable b, Storable c, Storable d) =>+ (a -> b -> c -> d) -> Vector a -> Vector b -> Vector c -> Vector d+zipWith3 f as bs cs =+ unsafeWithStartPtr as $ \pa0 la ->+ withStartPtr bs $ \pb0 lb ->+ withStartPtr cs $ \pc0 lc ->+ let len = la `min` lb `min` lc+ in create len $ \p0 ->+ let go = Strict.arguments5 $ \n p pa pb pc ->+ when (n>0) $+ liftM3 f (peek pa) (peek pb) (peek pc) >>= poke p >>+ go (pred n) (incPtr p) (incPtr pa) (incPtr pb) (incPtr pc)+ in go len p0 pa0 pb0 pc0+{-# INLINE zipWith3 #-}++-- | Like 'zipWith' but for four input vectors+-- If you need even more input vectors,+-- you might write a function yourselve using unfoldrN and viewL.+zipWith4 :: (Storable a, Storable b, Storable c, Storable d, Storable e) =>+ (a -> b -> c -> d -> e) -> Vector a -> Vector b -> Vector c -> Vector d -> Vector e+zipWith4 f as bs cs ds =+ unsafeWithStartPtr as $ \pa0 la ->+ withStartPtr bs $ \pb0 lb ->+ withStartPtr cs $ \pc0 lc ->+ withStartPtr ds $ \pd0 ld ->+ let len = la `min` lb `min` lc `min` ld+ in create len $ \p0 ->+ let go =+ Strict.arguments2 $ \n p ->+ Strict.arguments4 $ \pa pb pc pd ->+ when (n>0) $+ liftM4 f (peek pa) (peek pb) (peek pc) (peek pd) >>= poke p >>+ go (pred n) (incPtr p) (incPtr pa) (incPtr pb) (incPtr pc) (incPtr pd)+ in go len p0 pa0 pb0 pc0 pd0+{-# INLINE zipWith4 #-}++-- | /O(n)/ 'unzip' transforms a list of pairs of elements into a pair of+-- 'Vector's. Note that this performs two 'pack' operations.+unzip :: (Storable a, Storable b) => [(a, b)] -> (Vector a, Vector b)+unzip ls = (pack (P.map fst ls), pack (P.map snd ls))+{-# INLINE unzip #-}++-- ---------------------------------------------------------------------+-- Interleaved 'Vector's++-- | /O(l/n)/ 'sieve' selects every 'n'th element.+sieve :: (Storable a) => Int -> Vector a -> Vector a+sieve n (SV fp s l) =+ let end = s+l+ in fst $+ unfoldrN (- div (-l) n)+ (Strict.arguments1 $ \k0 ->+ do guard (k0<end)+ Just (foreignPeek fp k0, k0 + n))+ s+{-# INLINE sieve #-}++-- | /O(n)/+-- Returns n sieved vectors with successive starting elements.+-- @deinterleave 3 (pack ['a'..'k']) = [pack "adgj", pack "behk", pack "cfi"]@+-- This is the same as 'Data.List.HT.sliceHorizontal'.+deinterleave :: (Storable a) => Int -> Vector a -> [Vector a]+deinterleave n =+ P.map (sieve n) . P.take n . P.iterate laxTail++-- | /O(n)/+-- Almost the inverse of deinterleave.+-- Restriction is that all input vector must have equal length.+-- @interleave [pack "adgj", pack "behk", pack "cfil"] = pack ['a'..'l']@+interleave :: (Storable a) => [Vector a] -> Vector a+interleave vs =+ Unsafe.performIO $+ MC.runContT+ (do+ pls <- mapM (\v -> MC.ContT (withStartPtr v . curry)) vs+ let (ps,ls) = P.unzip pls+ ptrs <- MC.ContT (withArray ps)+ if and (ListHT.mapAdjacent (==) ls)+ then return (ptrs, P.sum ls)+ else moduleError "interleave" "all input vectors must have the same length")+ (\(ptrs, totalLength) -> create totalLength $ \p ->+ let n = P.length vs+ pEnd = advancePtr p totalLength+ go = Strict.arguments3 $ \k0 j p0 -> do+ poke p0 =<< flip peekElemOff j =<< peekElemOff ptrs k0+ let p1 = advancePtr p0 1+ k1 = succ k0+ when (p1 < pEnd) $+ if k1 < n+ then go k1 j p1+ else go 0 (succ j) p1+ in go 0 0 p)+{-# INLINE interleave #-}+++-- ---------------------------------------------------------------------+-- Special lists++-- | /O(n)/ Return all initial segments of the given 'Vector', shortest first.+inits :: (Storable a) => Vector a -> [Vector a]+inits (SV x s l) = List.map (SV x s) [0..l]++-- | /O(n)/ Return all final segments of the given 'Vector', longest first.+tails :: (Storable a) => Vector a -> [Vector a]+tails p =+ switchL [empty] (\ _ t -> p : tails t) p++-- ---------------------------------------------------------------------+-- ** Ordered 'Vector's++-- ---------------------------------------------------------------------+-- Low level constructors++-- | /O(n)/ Make a copy of the 'Vector' with its own storage.+-- This is mainly useful to allow the rest of the data pointed+-- to by the 'Vector' to be garbage collected, for example+-- if a large string has been read in, and only a small part of it+-- is needed in the rest of the program.+copy :: (Storable a) => Vector a -> Vector a+copy v =+ unsafeWithStartPtr v $ \f l ->+ create l $ \p ->+ copyArray p f (fromIntegral l)++++-- ---------------------------------------------------------------------+-- IO++-- | Outputs a 'Vector' to the specified 'Handle'.+hPut :: (Storable a) => Handle -> Vector a -> IO ()+hPut h v =+ when (not (null v)) $+ withStartPtr v $ \ ptrS l ->+ let ptrE = advancePtr ptrS l+ -- use advancePtr and minusPtr in order to respect alignment+ in hPutBuf h ptrS (minusPtr ptrE ptrS)++-- | Read a 'Vector' directly from the specified 'Handle'. This+-- is far more efficient than reading the characters into a list+-- and then using 'pack'.+--+hGet :: (Storable a) => Handle -> Int -> IO (Vector a)+hGet _ 0 = return empty+hGet h l =+ createAndTrim l $ \p ->+ let elemType :: Ptr a -> a+ elemType _ = undefined+ roundUp m n = n + mod (-n) m+ sizeOfElem =+ roundUp+ (alignment (elemType p))+ (sizeOf (elemType p))+ in fmap (flip div sizeOfElem) $+ hGetBuf h p (l * sizeOfElem)+{-+ createAndTrim l $ \p ->+ fmap (flip div (incPtr p `minusPtr` p)) $+ hGetBuf h p (advancePtr p l `minusPtr` p)+-}++-- | Read an entire file strictly into a 'Vector'. This is far more+-- efficient than reading the characters into a 'String' and then using+-- 'pack'. It also may be more efficient than opening the file and+-- reading it using hGet. Files are read using 'binary mode' on Windows.+--+readFile :: (Storable a) => FilePath -> IO (Vector a)+readFile f =+ bracket (openBinaryFile f ReadMode) hClose+ (\h -> hGet h . fromIntegral =<< hFileSize h)++-- | Write a 'Vector' to a file.+writeFile :: (Storable a) => FilePath -> Vector a -> IO ()+writeFile f txt =+ bracket (openBinaryFile f WriteMode) hClose+ (\h -> hPut h txt)++-- | Append a 'Vector' to a file.+appendFile :: (Storable a) => FilePath -> Vector a -> IO ()+appendFile f txt =+ bracket (openBinaryFile f AppendMode) hClose+ (\h -> hPut h txt)+++-- ---------------------------------------------------------------------+-- Internal utilities+++-- These definitions of succ and pred do not check for overflow+-- and are faster than their counterparts from Enum class.+succ :: Int -> Int+succ n = n+1+{-# INLINE succ #-}++pred :: Int -> Int+pred n = n-1+{-# INLINE pred #-}++unsafeWithStartPtr :: Storable a => Vector a -> (Ptr a -> Int -> IO b) -> b+unsafeWithStartPtr v f =+ Unsafe.performIO (withStartPtr v f)+{-# INLINE unsafeWithStartPtr #-}++foreignPeek :: Storable a => ForeignPtr a -> Int -> a+foreignPeek fp k =+ inlinePerformIO $ withForeignPtr fp $ flip peekElemOff k+{-# INLINE foreignPeek #-}++withNonEmptyVector ::+ String -> (ForeignPtr a -> Int -> Int -> b) -> Vector a -> b+withNonEmptyVector fun f (SV x s l) =+ if l <= 0+ then errorEmpty fun+ else f x s l+{-# INLINE withNonEmptyVector #-}++-- Common up near identical calls to `error' to reduce the number+-- constant strings created when compiled:+errorEmpty :: String -> a+errorEmpty fun = moduleError fun "empty Vector"+{-# NOINLINE errorEmpty #-}++moduleError :: String -> String -> a+moduleError fun msg = error ("Data.StorableVector." ++ fun ++ ':':' ':msg)+{-# NOINLINE moduleError #-}++-- Find from the end of the string using predicate+findFromEndUntil :: (Storable a) => (a -> Bool) -> Vector a -> Int+findFromEndUntil = Strict.arguments2 $ \f ps@(SV x s l) ->+ if null ps then 0+ else if f (last ps) then l+ else findFromEndUntil f (SV x s (l-1))
+ src/Data/StorableVector/Base.hs view
@@ -0,0 +1,225 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE DeriveDataTypeable #-}+--+-- Module : Data.StorableVector.Base+-- License : BSD-style+-- Maintainer : dons@cse.unsw.edu.au+-- Stability : experimental+-- Portability : portable, requires ffi and cpp+-- Tested with : GHC 6.4.1 and Hugs March 2005+-- ++-- | A module containing semi-public StorableVector internals. This exposes+-- the StorableVector representation and low level construction functions.+-- Modules which extend the StorableVector system will need to use this module+-- while ideally most users will be able to make do with the public interface+-- modules.+--+module Data.StorableVector.Base (++ -- * The @Vector@ type and representation+ Vector(..), -- instances: Eq, Ord, Show, Read, Data, Typeable++ -- * Unchecked access+ unsafeHead, -- :: Vector a -> a+ unsafeTail, -- :: Vector a -> Vector a+ unsafeLast, -- :: Vector a -> a+ unsafeInit, -- :: Vector a -> Vector a+ unsafeIndex, -- :: Vector a -> Int -> a+ unsafeTake, -- :: Int -> Vector a -> Vector a+ unsafeDrop, -- :: Int -> Vector a -> Vector a++ -- * Low level introduction and elimination+ create, -- :: Int -> (Ptr a -> IO ()) -> IO (Vector a)+ createAndTrim, -- :: Int -> (Ptr a -> IO Int) -> IO (Vector a)+ createAndTrim', -- :: Int -> (Ptr a -> IO (Int, Int, b)) -> IO (Vector a, b)++ unsafeCreate, -- :: Int -> (Ptr a -> IO ()) -> Vector a++ fromForeignPtr, -- :: ForeignPtr a -> Int -> Vector a+ toForeignPtr, -- :: Vector a -> (ForeignPtr a, Int, Int)+ withStartPtr, -- :: Vector a -> (Ptr a -> Int -> IO b) -> IO b+ incPtr, -- :: Ptr a -> Ptr a++ inlinePerformIO++ ) where++import Foreign.Ptr (Ptr)+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )+import Foreign.Marshal.Array (advancePtr, copyArray)+import Foreign.Storable (Storable(peekElemOff))++import Data.StorableVector.Memory (mallocForeignPtrArray, )++import Control.Exception (assert)++#if defined(__GLASGOW_HASKELL__)+import Data.Generics (Data, Typeable)+import GHC.Base (realWorld#)+import GHC.IO (IO(IO), )+#endif++import qualified System.Unsafe as Unsafe++-- CFILES stuff is Hugs only+{-# CFILES cbits/fpstring.c #-}++-- -----------------------------------------------------------------------------++-- | A space-efficient representation of a vector, supporting many efficient+-- operations.+--+-- Instances of Eq, Ord, Read, Show, Data, Typeable+--+data Vector a = SV {-# UNPACK #-} !(ForeignPtr a)+ {-# UNPACK #-} !Int -- offset+ {-# UNPACK #-} !Int -- length+#if defined(__GLASGOW_HASKELL__)+ deriving (Data, Typeable)+#endif++-- ---------------------------------------------------------------------+--+-- Extensions to the basic interface+--++-- | A variety of 'head' for non-empty Vectors. 'unsafeHead' omits the+-- check for the empty case, so there is an obligation on the programmer+-- to provide a proof that the Vector is non-empty.+unsafeHead :: (Storable a) => Vector a -> a+unsafeHead (SV x s l) = assert (l > 0) $+ inlinePerformIO $ withForeignPtr x $ \p -> peekElemOff p s+{-# INLINE unsafeHead #-}++-- | A variety of 'tail' for non-empty Vectors. 'unsafeTail' omits the+-- check for the empty case. As with 'unsafeHead', the programmer must+-- provide a separate proof that the Vector is non-empty.+unsafeTail :: (Storable a) => Vector a -> Vector a+unsafeTail (SV ps s l) = assert (l > 0) $ SV ps (s+1) (l-1)+{-# INLINE unsafeTail #-}++-- | A variety of 'last' for non-empty Vectors. 'unsafeLast' omits the+-- check for the empty case, so there is an obligation on the programmer+-- to provide a proof that the Vector is non-empty.+unsafeLast :: (Storable a) => Vector a -> a+unsafeLast (SV x s l) = assert (l > 0) $+ inlinePerformIO $ withForeignPtr x $ \p -> peekElemOff p (s+l-1)+{-# INLINE unsafeLast #-}++-- | A variety of 'init' for non-empty Vectors. 'unsafeInit' omits the+-- check for the empty case. As with 'unsafeLast', the programmer must+-- provide a separate proof that the Vector is non-empty.+unsafeInit :: (Storable a) => Vector a -> Vector a+unsafeInit (SV ps s l) = assert (l > 0) $ SV ps s (l-1)+{-# INLINE unsafeInit #-}++-- | Unsafe 'Vector' index (subscript) operator, starting from 0, returning a+-- single element. This omits the bounds check, which means there is an+-- accompanying obligation on the programmer to ensure the bounds are checked in+-- some other way.+unsafeIndex :: (Storable a) => Vector a -> Int -> a+unsafeIndex (SV x s l) i = assert (i >= 0 && i < l) $+ inlinePerformIO $ withForeignPtr x $ \p -> peekElemOff p (s+i)+{-# INLINE unsafeIndex #-}++-- | A variety of 'take' which omits the checks on @n@ so there is an+-- obligation on the programmer to provide a proof that @0 <= n <= 'length' xs@.+unsafeTake :: (Storable a) => Int -> Vector a -> Vector a+unsafeTake n (SV x s l) = assert (0 <= n && n <= l) $ SV x s n+{-# INLINE unsafeTake #-}++-- | A variety of 'drop' which omits the checks on @n@ so there is an+-- obligation on the programmer to provide a proof that @0 <= n <= 'length' xs@.+unsafeDrop :: (Storable a) => Int -> Vector a -> Vector a+unsafeDrop n (SV x s l) = assert (0 <= n && n <= l) $ SV x (s+n) (l-n)+{-# INLINE unsafeDrop #-}+++instance (Storable a, Show a) => Show (Vector a) where+ showsPrec p xs@(SV _ _ l) =+ showParen (p>=10)+ (showString "Vector.pack " .+ showsPrec 10 (map (unsafeIndex xs) [0..(l-1)]))+++-- ---------------------------------------------------------------------+-- Low level constructors++-- | /O(1)/ Build a Vector from a ForeignPtr+fromForeignPtr :: ForeignPtr a -> Int -> Vector a+fromForeignPtr fp l = SV fp 0 l++-- | /O(1)/ Deconstruct a ForeignPtr from a Vector+toForeignPtr :: Vector a -> (ForeignPtr a, Int, Int)+toForeignPtr (SV ps s l) = (ps, s, l)++-- | Run an action that is initialized+-- with a pointer to the first element to be used.+withStartPtr :: Storable a => Vector a -> (Ptr a -> Int -> IO b) -> IO b+withStartPtr (SV x s l) f =+ withForeignPtr x $ \p -> f (p `advancePtr` s) l+{-# INLINE withStartPtr #-}++incPtr :: (Storable a) => Ptr a -> Ptr a+incPtr v = advancePtr v 1+{-# INLINE incPtr #-}++-- | A way of creating Vectors outside the IO monad. The @Int@+-- argument gives the final size of the Vector. Unlike+-- 'createAndTrim' the Vector is not reallocated if the final size+-- is less than the estimated size.+unsafeCreate :: (Storable a) => Int -> (Ptr a -> IO ()) -> Vector a+unsafeCreate l f = Unsafe.performIO (create l f)+{-# INLINE unsafeCreate #-}++-- | Wrapper of mallocForeignPtrArray.+create :: (Storable a) => Int -> (Ptr a -> IO ()) -> IO (Vector a)+create l f = do+ fp <- mallocForeignPtrArray l+ withForeignPtr fp $ \p -> f p+ return $! SV fp 0 l++-- | Given the maximum size needed and a function to make the contents+-- of a Vector, createAndTrim makes the 'Vector'. The generating+-- function is required to return the actual final size (<= the maximum+-- size), and the resulting byte array is realloced to this size.+--+-- createAndTrim is the main mechanism for creating custom, efficient+-- Vector functions, using Haskell or C functions to fill the space.+--+createAndTrim :: (Storable a) => Int -> (Ptr a -> IO Int) -> IO (Vector a)+createAndTrim l f = do+ fp <- mallocForeignPtrArray l+ withForeignPtr fp $ \p -> do+ l' <- f p+ if assert (l' <= l) $ l' >= l+ then return $! SV fp 0 l+ else create l' $ \p' -> copyArray p' p l'++createAndTrim' :: (Storable a) => Int + -> (Ptr a -> IO (Int, Int, b))+ -> IO (Vector a, b)+createAndTrim' l f = do+ fp <- mallocForeignPtrArray l+ withForeignPtr fp $ \p -> do+ (off, l', res) <- f p+ if assert (l' <= l) $ l' >= l+ then return $! (SV fp 0 l, res)+ else do ps <- create l' $ \p' -> copyArray p' (p `advancePtr` off) l'+ return $! (ps, res)++-- | Just like Unsafe.performIO, but we inline it. Big performance gains as+-- it exposes lots of things to further inlining. /Very unsafe/. In+-- particular, you should do no memory allocation inside an+-- 'inlinePerformIO' block. On Hugs this is just @Unsafe.performIO@.+--+{-# INLINE inlinePerformIO #-}+inlinePerformIO :: IO a -> a+#if defined(__GLASGOW_HASKELL__)+inlinePerformIO (IO m) = case m realWorld# of (# _, r #) -> r+#else+inlinePerformIO = Unsafe.performIO+#endif
+ src/Data/StorableVector/Cursor.hs view
@@ -0,0 +1,353 @@+{-# LANGUAGE ExistentialQuantification #-}+{- |+Simulate a list with strict elements by a more efficient array structure.+-}+module Data.StorableVector.Cursor where++import Control.Exception (assert, )+import Control.Monad.Trans.State (StateT(StateT), runStateT, )+import Data.IORef (IORef, newIORef, readIORef, writeIORef, )++import Foreign.Storable (Storable(peekElemOff, pokeElemOff))+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )+-- import Foreign.Ptr (Ptr)+import Data.StorableVector.Memory (mallocForeignPtrArray, )++import Control.Monad (when)+import Data.Maybe (isNothing)++import qualified System.Unsafe as Unsafe++import qualified Data.List.HT as ListHT+import Data.Tuple.HT (mapSnd, )++import Prelude hiding (length, foldr, zipWith, take, drop, )+++{-+ToDo:+I think that the state should be Storable as well+and that the IORef should be replaced by a ForeignPtr.+I hope that this is more efficient.+With this restriction @s@ cannot be e.g. a function type+but this would kill performance anyway.+Functions that need this flexibility may fall back to other data structures+(lists or chunky StorableVectors) and convert to the Cursor structure later.+-}+-- | Cf. StreamFusion Data.Stream+data Generator a =+ forall s. -- Seq s =>+ Generator+ !(StateT s Maybe a) -- compute next value+ {-# UNPACK #-}+ !(IORef (Maybe s)) -- current state++{- |+This simulates a+@ data StrictList a = Elem !a (StrictList a) | End @+by an array and some unsafe hacks.+-}+data Buffer a =+ Buffer {+ memory :: {-# UNPACK #-} !(ForeignPtr a),+ size :: {-# UNPACK #-} !Int, -- size of allocated memory, I think I only need it for debugging+ gen :: !(Generator a), -- we need this indirection for the existential type in Generator+ cursor :: {-# UNPACK #-} !(IORef Int)+ }++{- |+Vector is a part of a buffer.+-}+data Vector a =+ Vector {+ buffer :: {-# UNPACK #-} !(Buffer a),+ start :: {-# UNPACK #-} !Int, -- invariant: start <= cursor+ maxLen :: {-# UNPACK #-} !Int -- invariant: start+maxLen <= size buffer+ }+++-- * construction++{-# INLINE create #-}+create :: (Storable a) => Int -> Generator a -> Buffer a+create l g = Unsafe.performIO (createIO l g)++-- | Wrapper of mallocForeignPtrArray.+createIO :: (Storable a) => Int -> Generator a -> IO (Buffer a)+createIO l g = do+ fp <- mallocForeignPtrArray l+ cur <- newIORef 0+ return $! Buffer fp l g cur+++{- |+@ unfoldrNTerm 20 (\n -> Just (n, succ n)) 'a' @+-}+unfoldrNTerm :: (Storable b) =>+ Int -> (a -> Maybe (b, a)) -> a -> Vector b+unfoldrNTerm l f x0 =+ Unsafe.performIO (unfoldrNTermIO l f x0)++unfoldrNTermIO :: (Storable b) =>+ Int -> (a -> Maybe (b, a)) -> a -> IO (Vector b)+unfoldrNTermIO l f x0 =+ do ref <- newIORef (Just x0)+ buf <- createIO l (Generator (StateT f) ref)+ return (Vector buf 0 l)++unfoldrN :: (Storable b) =>+ Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)+unfoldrN l f x0 =+ Unsafe.performIO (unfoldrNIO l f x0)++unfoldrNIO :: (Storable b) =>+ Int -> (a -> Maybe (b, a)) -> a -> IO (Vector b, Maybe a)+unfoldrNIO l f x0 =+ do ref <- newIORef (Just x0)+ buf <- createIO l (Generator (StateT f) ref)+ s <- Unsafe.interleaveIO $+ do evaluateToIO l buf+ readIORef ref+ return (Vector buf 0 l, s)+{-+unfoldrNIO :: (Storable b) =>+ Int -> (a -> Maybe (b, a)) -> a -> IO (Vector b, Maybe a)+unfoldrNIO l f x0 =+ do y <- unfoldrNTermIO l f x0+-- evaluateTo l y+ let (Generator _ ref) = gen (buffer y)+ s <- readIORef ref+ return (y, s)++Data/StorableVector/Cursor.hs:98:10:+ My brain just exploded.+ I can't handle pattern bindings for existentially-quantified constructors.+ In the binding group+ (Generator _ ref) = gen (buffer y)+ In the definition of `unfoldrNIO':+ unfoldrNIO l f x0+ = do+ y <- unfoldrNTermIO l f x0+ let (Generator _ ref) = gen (buffer y)+ s <- readIORef ref+ return (y, s)+-}+++{-+unfoldrN :: (Storable b) =>+ Int -> (a -> Maybe (b, a)) -> a -> (Vector b, Maybe a)+unfoldrN i f x0 =+ let y = unfoldrNTerm i f x0+ in (y, getFinalState y)++getFinalState :: (Storable b) =>+ Vector b -> Maybe a+getFinalState y =+ Unsafe.performIO $+ ...+-}+++{-# INLINE pack #-}+pack :: (Storable a) => Int -> [a] -> Vector a+pack n = unfoldrNTerm n ListHT.viewL+++{-# INLINE cons #-}+{- |+This is expensive and should not be used to construct lists iteratively!+A recursion-enabling 'cons' would be 'consN'+that allocates a buffer of given size,+initializes the leading cell and sets the buffer pointer to the next cell.+-}+cons :: Storable a =>+ a -> Vector a -> Vector a+cons x xs =+ unfoldrNTerm (succ (maxLen xs))+ (\(mx0,xs0) ->+ fmap (mapSnd ((,) Nothing)) $+ maybe+ (viewL xs0)+ (\x0 -> Just (x0, xs0))+ mx0) $+ (Just x, xs)+++{-# INLINE zipWith #-}+zipWith :: (Storable a, Storable b, Storable c) =>+ (a -> b -> c) -> Vector a -> Vector b -> Vector c+zipWith f ps0 qs0 =+ zipNWith (min (maxLen ps0) (maxLen qs0)) f ps0 qs0++-- zipWith f ps qs = pack $ List.zipWith f (unpack ps) (unpack qs)++{-# INLINE zipNWith #-}+zipNWith :: (Storable a, Storable b, Storable c) =>+ Int -> (a -> b -> c) -> Vector a -> Vector b -> Vector c+zipNWith n f ps0 qs0 =+ unfoldrNTerm n+ (\(ps,qs) ->+ do (ph,pt) <- viewL ps+ (qh,qt) <- viewL qs+ return (f ph qh, (pt,qt)))+ (ps0,qs0)+{-+let f2 = zipNWith 15 (+) f0 f1; f1 = cons 1 f2; f0 = cons (0::Int) f1 in f0++*Data.StorableVector.Cursor> let xs = unfoldrNTerm 20 (\n -> Just (n, succ n)) (0::Int)+*Data.StorableVector.Cursor> let ys = unfoldrNTerm 20 (\n -> Just (n, 2*n)) (1::Int)+*Data.StorableVector.Cursor> zipWith (+) xs ys+-}+++++-- * inspection++-- | evaluate next value in a buffer+advanceIO :: Storable a =>+ Buffer a -> IO (Maybe a)+advanceIO (Buffer p sz (Generator n s) cr) =+ do c <- readIORef cr+ assert (c < sz) $+ do writeIORef cr (succ c)+ ms <- readIORef s+ case ms of+ Nothing -> return Nothing+ Just s0 ->+ case runStateT n s0 of+ Nothing ->+ writeIORef s Nothing >>+ return Nothing+ Just (a,s1) ->+ writeIORef s (Just s1) >>+ withForeignPtr p (\q -> pokeElemOff q c a) >>+ return (Just a)++{-+It is tempting to turn this into a simple loop without the IORefs.+This could be compiled to an efficient strict loop,+but it would fail if the vector content depends on its own,+like in @fix (consN 1000 'a')@.+-}+-- | evaluate all values up to a given position+evaluateToIO :: Storable a =>+ Int -> Buffer a -> IO ()+evaluateToIO l buf@(Buffer _p _sz _g cr) =+ whileM+ (fmap (<l) (readIORef cr))+ (advanceIO buf)++whileM :: Monad m => m Bool -> m a -> m ()+whileM p f =+ let recourse =+ do b <- p+ when b (f >> recourse)+ in recourse++{-# INLINE switchL #-}+switchL :: Storable a => b -> (a -> Vector a -> b) -> Vector a -> b+switchL n j v = maybe n (uncurry j) (viewL v)+++{-+If it returns False the list can be empty anyway.+-}+obviousNullIO :: Vector a -> IO Bool+obviousNullIO (Vector (Buffer _ _ (Generator _ s) _) _ ml) =+ assert (ml >= 0) $+ do b <- readIORef s+ return (ml == 0 || isNothing b)++{-+obviousNullIO :: Vector a -> IO Bool+obviousNullIO (Vector (Buffer _ sz (Generator _ s) _) st _) =+ do b <- readIORef s+ return (st >= sz || isNothing b)+-}+-- assert (l >= 0) $ l <= 0++{-# INLINE viewL #-}+viewL :: Storable a => Vector a -> Maybe (a, Vector a)+viewL v = Unsafe.performIO (viewLIO v)++{-# INLINE viewLIO #-}+viewLIO :: Storable a => Vector a -> IO (Maybe (a, Vector a))+viewLIO (Vector buf st ml) =+ do c <- readIORef (cursor buf)+ fmap (fmap (\a -> (a, Vector buf (succ st) (pred ml)))) $+ assert (st <= c) $+ if st == c+ then advanceIO buf+ else fmap Just $ withForeignPtr (memory buf) (\p -> peekElemOff p st)+++{-# INLINE foldr #-}+foldr :: (Storable a) => (a -> b -> b) -> b -> Vector a -> b+foldr k z =+ let recourse = switchL z (\h t -> k h (recourse t))+ in recourse++-- | /O(n)/ Converts a 'Vector a' to a '[a]'.+{-# INLINE unpack #-}+unpack :: (Storable a) => Vector a -> [a]+unpack = foldr (:) []+++instance (Show a, Storable a) => Show (Vector a) where+ showsPrec p x = showsPrec p (unpack x)+++{-# INLINE null #-}+{-+This can hardly be simplified.+In order to check the list for emptiness,+we have to try to calculate the next element.+It is not enough to check whether the state is Nothing,+because when we try to compute the next value, this can be Nothing.+-}+null :: Storable a => Vector a -> Bool+null = switchL True (const (const False))+++{-+toVector :: Storable a => Vector a -> VS.Vector a+toVector v =+ VS.Cons (memory (buffer v)) ()+-}++-- length++drop :: (Storable a) => Int -> Vector a -> Vector a+drop n v = Unsafe.performIO $ dropIO n v++dropIO :: (Storable a) => Int -> Vector a -> IO (Vector a)+dropIO n v =+ assert (n>=0) $+ let pos = min (maxLen v) (start v + n)+ in do evaluateToIO pos (buffer v)+ return (Vector (buffer v) pos (max 0 (maxLen v - n)))++take :: (Storable a) => Int -> Vector a -> Vector a+take n v =+ assert (n>=0) $+ v{maxLen = min n (maxLen v)}++{-+let x = unfoldrNTerm 10 (\c -> Just (c,succ c)) 'a'+let x = unfoldrNTerm 10 (\c -> Just (sum [c..100000],succ c)) (0::Int)+-}+++{- |+For the sake of laziness it may allocate considerably more memory than needed,+if it filters out very much.+-}+{-# INLINE filter #-}+filter :: (Storable a) => (a -> Bool) -> Vector a -> Vector a+filter p xs0 =+ unfoldrNTerm (maxLen xs0)+ (let recourse = switchL Nothing (\x xs -> if p x then Just (x,xs) else recourse xs)+ in recourse)+ xs0
+ src/Data/StorableVector/Lazy.hs view
@@ -0,0 +1,1371 @@+{- |+Chunky signal stream build on StorableVector.++Hints for fusion:+ - Higher order functions should always be inlined in the end+ in order to turn them into machine loops+ instead of calling a function in an inner loop.+-}+module Data.StorableVector.Lazy where++import qualified Data.List as List+import qualified Data.StorableVector as V+import qualified Data.StorableVector.Base as VB+import qualified Data.StorableVector.Lazy.PointerPrivate as Ptr++import qualified Numeric.NonNegative.Class as NonNeg++import qualified Data.List.HT as ListHT+import Data.Tuple.HT (mapPair, mapFst, mapSnd, swap, )+import Data.Maybe.HT (toMaybe, )+import Data.Maybe (fromMaybe, )++import Foreign.Storable (Storable)++import Data.Monoid (Monoid, mempty, mappend, mconcat, )+-- import Control.Arrow ((***))+import Control.Monad (liftM, liftM2, liftM3, liftM4, {- guard, -} )+++import System.IO (openBinaryFile, IOMode(WriteMode, ReadMode, AppendMode),+ hClose, Handle)+import Control.Exception (bracket, catch, )++import qualified System.IO.Error as Exc+import qualified System.Unsafe as Unsafe++import Test.QuickCheck (Arbitrary(..))+++{-+import Prelude hiding+ (length, (++), concat, iterate, foldl, map, repeat, replicate, null,+ zip, zipWith, zipWith3, drop, take, splitAt, takeWhile, dropWhile, reverse)+-}++import qualified Prelude as P++import Data.Either (Either(Left, Right), either, )+import Data.Maybe (Maybe(Just, Nothing), maybe, )+import Data.Function (const, flip, ($), (.), )+import Data.Tuple (fst, snd, uncurry, )+import Data.Bool (Bool(True,False), not, (&&), )+import Data.Ord (Ord, (<), (>), (<=), (>=), min, max, )+import Data.Eq (Eq, (==), )+import Control.Monad (mapM_, fmap, (=<<), (>>=), (>>), return, )+import Text.Show (Show, showsPrec, showParen, showString, show, )+import Prelude+ (IO, error, IOError,+ FilePath, String, succ,+ Num, Int, sum, (+), (-), divMod, mod, fromInteger, )++++newtype Vector a = SV {chunks :: [V.Vector a]}+++instance (Storable a) => Monoid (Vector a) where+ mempty = empty+ mappend = append+ mconcat = concat++instance (Storable a, Eq a) => Eq (Vector a) where+ (==) = equal++instance (Storable a, Show a) => Show (Vector a) where+ showsPrec p xs =+ showParen (p>=10)+ (showString "VectorLazy.fromChunks " .+ showsPrec 10 (chunks xs))++instance (Storable a, Arbitrary a) => Arbitrary (Vector a) where+ arbitrary = liftM2 pack arbitrary arbitrary+++-- for a list of chunk sizes see "Data.StorableVector.LazySize".+newtype ChunkSize = ChunkSize Int+ deriving (Eq, Ord, Show)++instance Arbitrary ChunkSize where+ arbitrary = fmap (ChunkSize . max 1 . min 2048) arbitrary++{-+ToDo:+Since non-negative-0.1 we have the Monoid superclass for NonNeg.+Maybe we do not need the Num instance anymore.+-}+instance Num ChunkSize where+ (ChunkSize x) + (ChunkSize y) =+ ChunkSize (x+y)+ (-) = moduleError "ChunkSize.-" "intentionally unimplemented"+ (*) = moduleError "ChunkSize.*" "intentionally unimplemented"+ abs = moduleError "ChunkSize.abs" "intentionally unimplemented"+ signum = moduleError "ChunkSize.signum" "intentionally unimplemented"+ fromInteger = ChunkSize . fromInteger++instance Monoid ChunkSize where+ mempty = ChunkSize 0+ mappend (ChunkSize x) (ChunkSize y) = ChunkSize (x+y)+ mconcat = ChunkSize . sum . List.map (\(ChunkSize c) -> c)++instance NonNeg.C ChunkSize where+ split = NonNeg.splitDefault (\(ChunkSize c) -> c) ChunkSize++chunkSize :: Int -> ChunkSize+chunkSize x =+ ChunkSize $+ if x>0+ then x+ else moduleError "chunkSize" ("no positive number: " List.++ show x)++defaultChunkSize :: ChunkSize+defaultChunkSize =+ ChunkSize 1024++++-- * Introducing and eliminating 'Vector's++{-# INLINE empty #-}+empty :: (Storable a) => Vector a+empty = SV []++{-# INLINE singleton #-}+singleton :: (Storable a) => a -> Vector a+singleton x = SV [V.singleton x]++fromChunks :: (Storable a) => [V.Vector a] -> Vector a+fromChunks = SV++pack :: (Storable a) => ChunkSize -> [a] -> Vector a+pack size = unfoldr size ListHT.viewL++unpack :: (Storable a) => Vector a -> [a]+unpack = List.concatMap V.unpack . chunks+++{-# INLINE packWith #-}+packWith :: (Storable b) => ChunkSize -> (a -> b) -> [a] -> Vector b+packWith size f =+ unfoldr size (fmap (mapFst f) . ListHT.viewL)++{-# INLINE unpackWith #-}+unpackWith :: (Storable a) => (a -> b) -> Vector a -> [b]+unpackWith f = List.concatMap (V.unpackWith f) . chunks+++{-# INLINE unfoldr #-}+unfoldr :: (Storable b) =>+ ChunkSize ->+ (a -> Maybe (b,a)) ->+ a ->+ Vector b+unfoldr (ChunkSize size) f =+ SV .+ List.unfoldr (cancelNullVector . V.unfoldrN size f =<<) .+ Just++{- |+Example:++> *Data.StorableVector.Lazy> unfoldrResult (ChunkSize 5) (\c -> if c>'z' then Left (Char.ord c) else Right (c, succ c)) 'a'+> (VectorLazy.fromChunks [Vector.pack "abcde",Vector.pack "fghij",Vector.pack "klmno",Vector.pack "pqrst",Vector.pack "uvwxy",Vector.pack "z"],123)+-}+{-# INLINE unfoldrResult #-}+unfoldrResult :: (Storable b) =>+ ChunkSize ->+ (a -> Either c (b, a)) ->+ a ->+ (Vector b, c)+unfoldrResult (ChunkSize size) f =+ let recourse a0 =+ let (chunk, a1) =+ V.unfoldrResultN size Right (either (Left . Left) Right . f) a0+ in either+ ((,) (if V.null chunk then [] else [chunk]))+ (mapFst (chunk :) . recourse) a1+ in mapFst SV . recourse+++{-# INLINE sample #-}+sample :: (Storable a) => ChunkSize -> (Int -> a) -> Vector a+sample size f =+ unfoldr size (\i -> Just (f i, succ i)) 0++{-# INLINE sampleN #-}+sampleN :: (Storable a) => ChunkSize -> Int -> (Int -> a) -> Vector a+sampleN size n f =+ unfoldr size (\i -> toMaybe (i<n) (f i, succ i)) 0+++{-# INLINE iterate #-}+iterate :: Storable a => ChunkSize -> (a -> a) -> a -> Vector a+iterate size f = unfoldr size (\x -> Just (x, f x))++repeat :: Storable a => ChunkSize -> a -> Vector a+repeat (ChunkSize size) =+ SV . List.repeat . V.replicate size++cycle :: Storable a => Vector a -> Vector a+cycle =+ SV . List.cycle . chunks++replicate :: Storable a => ChunkSize -> Int -> a -> Vector a+replicate (ChunkSize size) n x =+ let (numChunks, rest) = divMod n size+ in append+ (SV (List.replicate numChunks (V.replicate size x)))+ (fromChunk (V.replicate rest x))+++++-- * Basic interface++{-# INLINE null #-}+null :: (Storable a) => Vector a -> Bool+null = List.null . chunks++length :: Vector a -> Int+length = sum . List.map V.length . chunks++equal :: (Storable a, Eq a) => Vector a -> Vector a -> Bool+equal (SV xs0) (SV ys0) =+ let recourse (x:xs) (y:ys) =+ let l = min (V.length x) (V.length y)+ (xPrefix, xSuffix) = V.splitAt l x+ (yPrefix, ySuffix) = V.splitAt l y+ build z zs =+ if V.null z then zs else z:zs+ in xPrefix == yPrefix &&+ recourse (build xSuffix xs) (build ySuffix ys)+ recourse [] [] = True+ -- this requires that chunks will always be non-empty+ recourse _ _ = False+ in recourse xs0 ys0++index :: (Storable a) => Vector a -> Int -> a+index (SV xs) n =+ if n < 0+ then+ moduleError "index"+ ("negative index: " List.++ show n)+ else+ List.foldr+ (\x k m0 ->+ let m1 = m0 - V.length x+ in if m1 < 0+ then VB.unsafeIndex x m0+ else k m1)+ (\m -> moduleError "index"+ ("index too large: " List.++ show n+ List.++ ", length = " List.++ show (n-m)))+ xs n+++{-# NOINLINE [0] cons #-}+cons :: Storable a => a -> Vector a -> Vector a+cons x = SV . (V.singleton x :) . chunks++infixr 5 `append`++{-# NOINLINE [0] append #-}+append :: Storable a => Vector a -> Vector a -> Vector a+append (SV xs) (SV ys) = SV (xs List.++ ys)+++{- |+@extendL size x y@+prepends the chunk @x@ and merges it with the first chunk of @y@+if the total size is at most @size@.+This way you can prepend small chunks+while asserting a reasonable average size for chunks.+-}+extendL :: Storable a => ChunkSize -> V.Vector a -> Vector a -> Vector a+extendL (ChunkSize size) x (SV yt) =+ SV $+ maybe+ [x]+ (\(y,ys) ->+ if V.length x + V.length y <= size+ then V.append x y : ys+ else x:yt)+ (ListHT.viewL yt)+++concat :: (Storable a) => [Vector a] -> Vector a+concat = SV . List.concat . List.map chunks+++-- * Transformations++{-# INLINE map #-}+map :: (Storable x, Storable y) =>+ (x -> y)+ -> Vector x+ -> Vector y+map f = SV . List.map (V.map f) . chunks+++reverse :: Storable a => Vector a -> Vector a+reverse =+ SV . List.reverse . List.map V.reverse . chunks+++-- * Reducing 'Vector's++{-# INLINE foldl #-}+foldl :: Storable b => (a -> b -> a) -> a -> Vector b -> a+foldl f x0 = List.foldl (V.foldl f) x0 . chunks++{-# INLINE foldl' #-}+foldl' :: Storable b => (a -> b -> a) -> a -> Vector b -> a+foldl' f x0 = List.foldl' (V.foldl f) x0 . chunks++{-# INLINE foldr #-}+foldr :: Storable b => (b -> a -> a) -> a -> Vector b -> a+foldr f x0 = List.foldr (flip (V.foldr f)) x0 . chunks+++{-# INLINE monoidConcatMap #-}+monoidConcatMap :: (Storable a, Monoid m) => (a -> m) -> Vector a -> m+monoidConcatMap f =+ List.foldr (mappend . V.monoidConcatMap f) mempty . chunks++{-# INLINE any #-}+any :: (Storable a) => (a -> Bool) -> Vector a -> Bool+any p = List.any (V.any p) . chunks++{-# INLINE all #-}+all :: (Storable a) => (a -> Bool) -> Vector a -> Bool+all p = List.all (V.all p) . chunks++maximum :: (Storable a, Ord a) => Vector a -> a+maximum =+ List.maximum . List.map V.maximum . chunks+-- List.foldl1' max . List.map V.maximum . chunks++minimum :: (Storable a, Ord a) => Vector a -> a+minimum =+ List.minimum . List.map V.minimum . chunks+-- List.foldl1' min . List.map V.minimum . chunks++{-+sum :: (Storable a, Num a) => Vector a -> a+sum =+ List.sum . List.map V.sum . chunks++product :: (Storable a, Num a) => Vector a -> a+product =+ List.product . List.map V.product . chunks+-}+++-- * inspecting a vector++{-# INLINE pointer #-}+pointer :: Storable a => Vector a -> Ptr.Pointer a+pointer = Ptr.cons . chunks++{-# INLINE viewL #-}+viewL :: Storable a => Vector a -> Maybe (a, Vector a)+viewL (SV xs0) =+ do (x,xs) <- ListHT.viewL xs0+ (y,ys) <- V.viewL x+ return (y, append (fromChunk ys) (SV xs))++{-# INLINE viewR #-}+viewR :: Storable a => Vector a -> Maybe (Vector a, a)+viewR (SV xs0) =+ do xsp <- ListHT.viewR xs0+ let (xs,x) = xsp+{-+ do ~(xs,x) <- ListHT.viewR xs0+-}+ let (ys,y) = fromMaybe (moduleError "viewR" "last chunk empty") (V.viewR x)+ return (append (SV xs) (fromChunk ys), y)++{-# INLINE switchL #-}+switchL :: Storable a => b -> (a -> Vector a -> b) -> Vector a -> b+switchL n j =+ maybe n (uncurry j) . viewL++{-# INLINE switchR #-}+switchR :: Storable a => b -> (Vector a -> a -> b) -> Vector a -> b+switchR n j =+ maybe n (uncurry j) . viewR+++{-+viewLSafe :: Storable a => Vector a -> Maybe (a, Vector a)+viewLSafe (SV xs0) =+ -- dropWhile would be unnecessary if we require that all chunks are non-empty+ do (x,xs) <- ListHT.viewL (List.dropWhile V.null xs0)+ (y,ys) <- viewLVector x+ return (y, append (fromChunk ys) (SV xs))++viewRSafe :: Storable a => Vector a -> Maybe (Vector a, a)+viewRSafe (SV xs0) =+ -- dropWhile would be unnecessary if we require that all chunks are non-empty+ do (xs,x) <- ListHT.viewR (dropWhileRev V.null xs0)+ (ys,y) <- V.viewR x+ return (append (SV xs) (fromChunk ys), y)+-}+++{-# INLINE scanl #-}+scanl :: (Storable a, Storable b) =>+ (a -> b -> a) -> a -> Vector b -> Vector a+scanl f start =+ cons start . snd .+ mapAccumL (\acc -> (\b -> (b,b)) . f acc) start++{-# INLINE mapAccumL #-}+mapAccumL :: (Storable a, Storable b) =>+ (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)+mapAccumL f start =+ mapSnd SV .+ List.mapAccumL (V.mapAccumL f) start .+ chunks++{-# INLINE mapAccumR #-}+mapAccumR :: (Storable a, Storable b) =>+ (acc -> a -> (acc, b)) -> acc -> Vector a -> (acc, Vector b)+mapAccumR f start =+ mapSnd SV .+ List.mapAccumR (V.mapAccumR f) start .+ chunks++{-# INLINE crochetLChunk #-}+crochetLChunk :: (Storable x, Storable y) =>+ (x -> acc -> Maybe (y, acc))+ -> acc+ -> V.Vector x+ -> (V.Vector y, Maybe acc)+crochetLChunk f acc0 x0 =+ mapSnd (fmap fst) $+ V.unfoldrN+ (V.length x0)+ (\(acc,xt) ->+ do (x,xs) <- V.viewL xt+ (y,acc') <- f x acc+ return (y, (acc',xs)))+ (acc0, x0)++{-# INLINE crochetL #-}+crochetL :: (Storable x, Storable y) =>+ (x -> acc -> Maybe (y, acc))+ -> acc+ -> Vector x+ -> Vector y+crochetL f acc0 =+ SV . List.unfoldr (\(xt,acc) ->+ do (x,xs) <- ListHT.viewL xt+ acc' <- acc+ return $ mapSnd ((,) xs) $ crochetLChunk f acc' x) .+ flip (,) (Just acc0) .+ chunks++++-- * sub-vectors++{-# INLINE take #-}+take :: (Storable a) => Int -> Vector a -> Vector a+{- this order of pattern matches is certainly the most lazy one+> take 4 (pack (chunkSize 2) $ "abcd" List.++ undefined)+VectorLazy.fromChunks [Vector.pack "ab",Vector.pack "cd"]+-}+take 0 _ = empty+take _ (SV []) = empty+take n (SV (x:xs)) =+ let m = V.length x+ in if m<=n+ then SV $ (x:) $ chunks $ take (n-m) $ SV xs+ else fromChunk (V.take n x)++{- |+Take n values from the end of the vector in a memory friendly way.+@takeEnd n xs@ should perform the same as @drop (length xs - n) xs@,+but the latter one would have to materialise @xs@ completely.+In contrast to that+@takeEnd@ should hold only chunks of about @n@ elements at any time point.+-}+{-# INLINE takeEnd #-}+takeEnd :: (Storable a) => Int -> Vector a -> Vector a+takeEnd n xs =+ -- cf. Pattern.drop+ List.foldl (flip drop) xs $ List.map V.length $ chunks $ drop n xs++{-# INLINE drop #-}+drop :: (Storable a) => Int -> Vector a -> Vector a+drop _ (SV []) = empty+drop n (SV (x:xs)) =+ let m = V.length x+ in if m<=n+ then drop (n-m) (SV xs)+ else SV (V.drop n x : xs)++{-# INLINE splitAt #-}+splitAt :: (Storable a) => Int -> Vector a -> (Vector a, Vector a)+splitAt n0 =+ {- this order of pattern matches is certainly the most lazy one+ > splitAt 4 (pack (chunkSize 2) $ "abcd" List.++ undefined)+ (VectorLazy.fromChunks [Vector.pack "ab",Vector.pack "cd"],VectorLazy.fromChunks *** Exception: Prelude.undefined+ -}+ let recourse 0 xs = ([], xs)+ recourse _ [] = ([], [])+ recourse n (x:xs) =+ let m = V.length x+ in if m<=n+ then mapFst (x:) $ recourse (n-m) xs+ else mapPair ((:[]), (:xs)) $ V.splitAt n x+ in mapPair (SV, SV) . recourse n0 . chunks++++{-# INLINE dropMarginRem #-}+-- I have used this in an inner loop thus I prefer inlining+{- |+@dropMarginRem n m xs@+drops at most the first @m@ elements of @xs@+and ensures that @xs@ still contains @n@ elements.+Additionally returns the number of elements that could not be dropped+due to the margin constraint.+That is @dropMarginRem n m xs == (k,ys)@ implies @length xs - m == length ys - k@.+Requires @length xs >= n@.+-}+dropMarginRem :: (Storable a) => Int -> Int -> Vector a -> (Int, Vector a)+dropMarginRem n m xs =+ List.foldl'+ (\(mi,xsi) k -> (mi-k, drop k xsi))+ (m,xs)+ (List.map V.length $ chunks $ take m $ drop n xs)++{-+This implementation does only walk once through the dropped prefix.+It is maximally lazy and minimally space consuming.+-}+{-# INLINE dropMargin #-}+dropMargin :: (Storable a) => Int -> Int -> Vector a -> Vector a+dropMargin n m xs =+ List.foldl' (flip drop) xs+ (List.map V.length $ chunks $ take m $ drop n xs)++++{-# INLINE dropWhile #-}+dropWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a+dropWhile _ (SV []) = empty+dropWhile p (SV (x:xs)) =+ let y = V.dropWhile p x+ in if V.null y+ then dropWhile p (SV xs)+ else SV (y:xs)++{-# INLINE takeWhile #-}+takeWhile :: (Storable a) => (a -> Bool) -> Vector a -> Vector a+takeWhile _ (SV []) = empty+takeWhile p (SV (x:xs)) =+ let y = V.takeWhile p x+ in if V.length y < V.length x+ then fromChunk y+ else SV (x : chunks (takeWhile p (SV xs)))+++{-# INLINE span #-}+span :: (Storable a) => (a -> Bool) -> Vector a -> (Vector a, Vector a)+span p =+ let recourse [] = ([],[])+ recourse (x:xs) =+ let (y,z) = V.span p x+ in if V.null z+ then mapFst (x:) (recourse xs)+ else (chunks $ fromChunk y, (z:xs))+ in mapPair (SV, SV) . recourse . chunks+{-+span _ (SV []) = (empty, empty)+span p (SV (x:xs)) =+ let (y,z) = V.span p x+ in if V.length y == 0+ then mapFst (SV . (x:) . chunks) (span p (SV xs))+ else (SV [y], SV (z:xs))+-}+++-- * other functions+++{-# INLINE filter #-}+filter :: (Storable a) => (a -> Bool) -> Vector a -> Vector a+filter p =+ SV . List.filter (not . V.null) . List.map (V.filter p) . chunks+++{- |+Generates laziness breaks+wherever one of the input signals has a chunk boundary.+-}+{-# INLINE zipWith #-}+zipWith :: (Storable a, Storable b, Storable c) =>+ (a -> b -> c)+ -> Vector a+ -> Vector b+ -> Vector c+zipWith f as0 bs0 =+ let recourse at@(a:_) bt@(b:_) =+ let z = V.zipWith f a b+ n = V.length z+ in z : recourse+ (chunks $ drop n $ fromChunks at)+ (chunks $ drop n $ fromChunks bt)+ recourse _ _ = []+ in fromChunks $ recourse (chunks as0) (chunks bs0)++{-# INLINE zipWith3 #-}+zipWith3 :: (Storable a, Storable b, Storable c, Storable d) =>+ (a -> b -> c -> d)+ -> Vector a+ -> Vector b+ -> Vector c+ -> Vector d+zipWith3 f as0 bs0 cs0 =+ let recourse at@(a:_) bt@(b:_) ct@(c:_) =+ let z = V.zipWith3 f a b c+ n = V.length z+ in z : recourse+ (chunks $ drop n $ fromChunks at)+ (chunks $ drop n $ fromChunks bt)+ (chunks $ drop n $ fromChunks ct)+ recourse _ _ _ = []+ in fromChunks $ recourse (chunks as0) (chunks bs0) (chunks cs0)++{-# INLINE zipWith4 #-}+zipWith4 :: (Storable a, Storable b, Storable c, Storable d, Storable e) =>+ (a -> b -> c -> d -> e)+ -> Vector a+ -> Vector b+ -> Vector c+ -> Vector d+ -> Vector e+zipWith4 f as0 bs0 cs0 ds0 =+ let recourse at@(a:_) bt@(b:_) ct@(c:_) dt@(d:_) =+ let z = V.zipWith4 f a b c d+ n = V.length z+ in z : recourse+ (chunks $ drop n $ fromChunks at)+ (chunks $ drop n $ fromChunks bt)+ (chunks $ drop n $ fromChunks ct)+ (chunks $ drop n $ fromChunks dt)+ recourse _ _ _ _ = []+ in fromChunks $+ recourse (chunks as0) (chunks bs0) (chunks cs0) (chunks ds0)+++{- |+Preserves chunk pattern of the last argument.+-}+{-# INLINE zipWithLastPattern #-}+zipWithLastPattern :: (Storable a, Storable b, Storable c) =>+ (a -> b -> c)+ -> Vector a+ -> Vector b+ -> Vector c+zipWithLastPattern f =+ crochetL (\y -> liftM (mapFst (flip f y)) . Ptr.viewL)+ . pointer++{- |+Preserves chunk pattern of the last argument.+-}+{-# INLINE zipWithLastPattern3 #-}+zipWithLastPattern3 ::+ (Storable a, Storable b, Storable c, Storable d) =>+ (a -> b -> c -> d) ->+ (Vector a -> Vector b -> Vector c -> Vector d)+zipWithLastPattern3 f s0 s1 =+ crochetL (\z (xt,yt) ->+ liftM2+ (\(x,xs) (y,ys) -> (f x y z, (xs,ys)))+ (Ptr.viewL xt)+ (Ptr.viewL yt))+ (pointer s0, pointer s1)++{- |+Preserves chunk pattern of the last argument.+-}+{-# INLINE zipWithLastPattern4 #-}+zipWithLastPattern4 ::+ (Storable a, Storable b, Storable c, Storable d, Storable e) =>+ (a -> b -> c -> d -> e) ->+ (Vector a -> Vector b -> Vector c -> Vector d -> Vector e)+zipWithLastPattern4 f s0 s1 s2 =+ crochetL (\w (xt,yt,zt) ->+ liftM3+ (\(x,xs) (y,ys) (z,zs) -> (f x y z w, (xs,ys,zs)))+ (Ptr.viewL xt)+ (Ptr.viewL yt)+ (Ptr.viewL zt))+ (pointer s0, pointer s1, pointer s2)+++{-# INLINE zipWithSize #-}+zipWithSize :: (Storable a, Storable b, Storable c) =>+ ChunkSize+ -> (a -> b -> c)+ -> Vector a+ -> Vector b+ -> Vector c+zipWithSize size f s0 s1 =+ unfoldr size (\(xt,yt) ->+ liftM2+ (\(x,xs) (y,ys) -> (f x y, (xs,ys)))+ (Ptr.viewL xt)+ (Ptr.viewL yt))+ (pointer s0, pointer s1)++{-# INLINE zipWithSize3 #-}+zipWithSize3 ::+ (Storable a, Storable b, Storable c, Storable d) =>+ ChunkSize -> (a -> b -> c -> d) ->+ (Vector a -> Vector b -> Vector c -> Vector d)+zipWithSize3 size f s0 s1 s2 =+ unfoldr size (\(xt,yt,zt) ->+ liftM3+ (\(x,xs) (y,ys) (z,zs) ->+ (f x y z, (xs,ys,zs)))+ (Ptr.viewL xt)+ (Ptr.viewL yt)+ (Ptr.viewL zt))+ (pointer s0, pointer s1, pointer s2)++{-# INLINE zipWithSize4 #-}+zipWithSize4 ::+ (Storable a, Storable b, Storable c, Storable d, Storable e) =>+ ChunkSize -> (a -> b -> c -> d -> e) ->+ (Vector a -> Vector b -> Vector c -> Vector d -> Vector e)+zipWithSize4 size f s0 s1 s2 s3 =+ unfoldr size (\(xt,yt,zt,wt) ->+ liftM4+ (\(x,xs) (y,ys) (z,zs) (w,ws) ->+ (f x y z w, (xs,ys,zs,ws)))+ (Ptr.viewL xt)+ (Ptr.viewL yt)+ (Ptr.viewL zt)+ (Ptr.viewL wt))+ (pointer s0, pointer s1, pointer s2, pointer s3)+++-- * interleaved vectors++{-# INLINE sieve #-}+sieve :: (Storable a) => Int -> Vector a -> Vector a+sieve n =+ fromChunks . List.filter (not . V.null) . snd .+ List.mapAccumL+ (\offset chunk ->+ (mod (offset - V.length chunk) n,+ V.sieve n $ V.drop offset chunk)) 0 .+ chunks++{-# INLINE deinterleave #-}+deinterleave :: (Storable a) => Int -> Vector a -> [Vector a]+deinterleave n =+ P.map (sieve n) . P.take n . P.iterate (switchL empty (flip const))++{- |+Interleave lazy vectors+while maintaining the chunk pattern of the first vector.+All input vectors must have the same length.+-}+{-# INLINE interleaveFirstPattern #-}+interleaveFirstPattern :: (Storable a) => [Vector a] -> Vector a+interleaveFirstPattern [] = empty+interleaveFirstPattern vss@(vs:_) =+ let pattern = List.map V.length $ chunks vs+ split xs =+ snd $+ List.mapAccumL+ (\x n -> swap $ mapFst (V.concat . chunks) $ splitAt n x)+ xs pattern+ in fromChunks $ List.map V.interleave $+ List.transpose $ List.map split vss++{-+interleaveFirstPattern vss@(vs:_) =+ fromChunks . snd .+ List.mapAccumL+ (\xss n ->+ swap $+ mapFst (V.interleave . List.map (V.concat . chunks)) $+ List.unzip $ List.map (splitAt n) xss)+ vss .+ List.map V.length . chunks $ vs+-}++++{- |+Ensure a minimal length of the list by appending pad values.+-}+{- disabled INLINE pad -}+pad :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a+pad size y n0 =+ let recourse n xt =+ if n<=0+ then xt+ else+ case xt of+ [] -> chunks $ replicate size n y+ x:xs -> x : recourse (n - V.length x) xs+ in SV . recourse n0 . chunks++padAlt :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a+padAlt size x n xs =+ append xs+ (let m = length xs+ in if n>m+ then replicate size (n-m) x+ else empty)++++++-- * Helper functions for StorableVector+++{-# INLINE cancelNullVector #-}+cancelNullVector :: (V.Vector a, b) -> Maybe (V.Vector a, b)+cancelNullVector y =+ toMaybe (not (V.null (fst y))) y++-- if the chunk has length zero, an empty sequence is generated+{-# INLINE fromChunk #-}+fromChunk :: (Storable a) => V.Vector a -> Vector a+fromChunk x =+ if V.null x+ then empty+ else SV [x]++++{-+reduceLVector :: Storable x =>+ (x -> acc -> Maybe acc) -> acc -> Vector x -> (acc, Bool)+reduceLVector f acc0 x =+ let recourse i acc =+ if i < V.length x+ then (acc, True)+ else+ maybe+ (acc, False)+ (recourse (succ i))+ (f (V.index x i) acc)+ in recourse 0 acc0+++++{- * Fundamental functions -}++{-+Usage of 'unfoldr' seems to be clumsy but that covers all cases,+like different block sizes in source and destination list.+-}+crochetLSize :: (Storable x, Storable y) =>+ ChunkSize+ -> (x -> acc -> Maybe (y, acc))+ -> acc+ -> T x+ -> T y+crochetLSize size f =+ curry (unfoldr size (\(acc,xt) ->+ do (x,xs) <- viewL xt+ (y,acc') <- f x acc+ return (y, (acc',xs))))++crochetListL :: (Storable y) =>+ ChunkSize+ -> (x -> acc -> Maybe (y, acc))+ -> acc+ -> [x]+ -> T y+crochetListL size f =+ curry (unfoldr size (\(acc,xt) ->+ do (x,xs) <- ListHT.viewL xt+ (y,acc') <- f x acc+ return (y, (acc',xs))))++++{-# NOINLINE [0] crochetFusionListL #-}+crochetFusionListL :: (Storable y) =>+ ChunkSize+ -> (x -> acc -> Maybe (y, acc))+ -> acc+ -> FList.T x+ -> T y+crochetFusionListL size f =+ curry (unfoldr size (\(acc,xt) ->+ do (x,xs) <- FList.viewL xt+ (y,acc') <- f x acc+ return (y, (acc',xs))))+++{-# INLINE [0] reduceL #-}+reduceL :: Storable x =>+ (x -> acc -> Maybe acc) -> acc -> Vector x -> acc+reduceL f acc0 =+ let recourse acc xt =+ case xt of+ [] -> acc+ (x:xs) ->+ let (acc',continue) = reduceLVector f acc x+ in if continue+ then recourse acc' xs+ else acc'+ in recourse acc0 . chunks++++{- * Basic functions -}+++{-# RULEZ+ "Storable.append/repeat/repeat" forall size x.+ append (repeat size x) (repeat size x) = repeat size x ;++ "Storable.append/repeat/replicate" forall size n x.+ append (repeat size x) (replicate size n x) = repeat size x ;++ "Storable.append/replicate/repeat" forall size n x.+ append (replicate size n x) (repeat size x) = repeat size x ;++ "Storable.append/replicate/replicate" forall size n m x.+ append (replicate size n x) (replicate size m x) =+ replicate size (n+m) x ;++ "Storable.mix/repeat/repeat" forall size x y.+ mix (repeat size x) (repeat size y) = repeat size (x+y) ;++ #-}++{-# RULES+ "Storable.length/cons" forall x xs.+ length (cons x xs) = 1 + length xs ;++ "Storable.length/map" forall f xs.+ length (map f xs) = length xs ;++ "Storable.map/cons" forall f x xs.+ map f (cons x xs) = cons (f x) (map f xs) ;++ "Storable.map/repeat" forall size f x.+ map f (repeat size x) = repeat size (f x) ;++ "Storable.map/replicate" forall size f x n.+ map f (replicate size n x) = replicate size n (f x) ;++ "Storable.map/repeat" forall size f x.+ map f (repeat size x) = repeat size (f x) ;++ {-+ This can make things worse, if 'map' is applied to replicate,+ since this can use of sharing.+ It can also destroy the more important map/unfoldr fusion in+ take n . map f . unfoldr g++ "Storable.take/map" forall n f x.+ take n (map f x) = map f (take n x) ;+ -}++ "Storable.take/repeat" forall size n x.+ take n (repeat size x) = replicate size n x ;++ "Storable.take/take" forall n m xs.+ take n (take m xs) = take (min n m) xs ;++ "Storable.drop/drop" forall n m xs.+ drop n (drop m xs) = drop (n+m) xs ;++ "Storable.drop/take" forall n m xs.+ drop n (take m xs) = take (max 0 (m-n)) (drop n xs) ;++ "Storable.map/mapAccumL/snd" forall g f acc0 xs.+ map g (snd (mapAccumL f acc0 xs)) =+ snd (mapAccumL (\acc a -> mapSnd g (f acc a)) acc0 xs) ;++ #-}++{- GHC says this is an orphaned rule+ "Storable.map/mapAccumL/mapSnd" forall g f acc0 xs.+ mapSnd (map g) (mapAccumL f acc0 xs) =+ mapAccumL (\acc a -> mapSnd g (f acc a)) acc0 xs ;+-}+++{- * Fusable functions -}++scanLCrochet :: (Storable a, Storable b) =>+ (a -> b -> a) -> a -> Vector b -> Vector a+scanLCrochet f start =+ cons start .+ crochetL (\x acc -> let y = f acc x in Just (y, y)) start++{-# INLINE mapCrochet #-}+mapCrochet :: (Storable a, Storable b) => (a -> b) -> (Vector a -> Vector b)+mapCrochet f = crochetL (\x _ -> Just (f x, ())) ()++{-# INLINE takeCrochet #-}+takeCrochet :: Storable a => Int -> Vector a -> Vector a+takeCrochet = crochetL (\x n -> toMaybe (n>0) (x, pred n))++{-# INLINE repeatUnfoldr #-}+repeatUnfoldr :: Storable a => ChunkSize -> a -> Vector a+repeatUnfoldr size = iterate size id++{-# INLINE replicateCrochet #-}+replicateCrochet :: Storable a => ChunkSize -> Int -> a -> Vector a+replicateCrochet size n = takeCrochet n . repeat size+++++{-+The "fromList/drop" rule is not quite accurate+because the chunk borders are moved.+Maybe 'ChunkSize' better is a list of chunks sizes.+-}++{-# RULEZ+ "fromList/zipWith"+ forall size f (as :: Storable a => [a]) (bs :: Storable a => [a]).+ fromList size (List.zipWith f as bs) =+ zipWith size f (fromList size as) (fromList size bs) ;++ "fromList/drop" forall as n size.+ fromList size (List.drop n as) =+ drop n (fromList size as) ;+ #-}++++{- * Fused functions -}++type Unfoldr s a = (s -> Maybe (a,s), s)++{-# INLINE zipWithUnfoldr2 #-}+zipWithUnfoldr2 :: Storable z =>+ ChunkSize+ -> (x -> y -> z)+ -> Unfoldr a x+ -> Unfoldr b y+ -> T z+zipWithUnfoldr2 size h (f,a) (g,b) =+ unfoldr size+ (\(a0,b0) -> liftM2 (\(x,a1) (y,b1) -> (h x y, (a1,b1))) (f a0) (g b0))+-- (uncurry (liftM2 (\(x,a1) (y,b1) -> (h x y, (a1,b1)))) . (f *** g))+ (a,b)++{- done by takeCrochet+{-# INLINE mapUnfoldr #-}+mapUnfoldr :: (Storable x, Storable y) =>+ ChunkSize+ -> (x -> y)+ -> Unfoldr a x+ -> T y+mapUnfoldr size g (f,a) =+ unfoldr size (fmap (mapFst g) . f) a+-}++{-# INLINE dropUnfoldr #-}+dropUnfoldr :: Storable x =>+ ChunkSize+ -> Int+ -> Unfoldr a x+ -> T x+dropUnfoldr size n (f,a0) =+ maybe+ empty+ (unfoldr size f)+ (nest n (\a -> fmap snd . f =<< a) (Just a0))+++{- done by takeCrochet+{-# INLINE takeUnfoldr #-}+takeUnfoldr :: Storable x =>+ ChunkSize+ -> Int+ -> Unfoldr a x+ -> T x+takeUnfoldr size n0 (f,a0) =+ unfoldr size+ (\(a,n) ->+ do guard (n>0)+ (x,a') <- f a+ return (x, (a', pred n)))+ (a0,n0)+-}+++lengthUnfoldr :: Storable x =>+ Unfoldr a x+ -> Int+lengthUnfoldr (f,a0) =+ let recourse n a =+ maybe n (recourse (succ n) . snd) (f a)+ in recourse 0 a0+++{-# INLINE zipWithUnfoldr #-}+zipWithUnfoldr ::+ (Storable b, Storable c) =>+ (acc -> Maybe (a, acc))+ -> (a -> b -> c)+ -> acc+ -> T b -> T c+zipWithUnfoldr f h a y =+ crochetL (\y0 a0 ->+ do (x0,a1) <- f a0+ Just (h x0 y0, a1)) a y++{-# INLINE zipWithCrochetL #-}+zipWithCrochetL ::+ (Storable x, Storable b, Storable c) =>+ ChunkSize+ -> (x -> acc -> Maybe (a, acc))+ -> (a -> b -> c)+ -> acc+ -> T x -> T b -> T c+zipWithCrochetL size f h a x y =+ crochetL (\(x0,y0) a0 ->+ do (z0,a1) <- f x0 a0+ Just (h z0 y0, a1))+ a (zip size x y)+++{-# INLINE crochetLCons #-}+crochetLCons ::+ (Storable a, Storable b) =>+ (a -> acc -> Maybe (b, acc))+ -> acc+ -> a -> T a -> T b+crochetLCons f a0 x xs =+ maybe+ empty+ (\(y,a1) -> cons y (crochetL f a1 xs))+ (f x a0)++{-# INLINE reduceLCons #-}+reduceLCons ::+ (Storable a) =>+ (a -> acc -> Maybe acc)+ -> acc+ -> a -> T a -> acc+reduceLCons f a0 x xs =+ maybe a0 (flip (reduceL f) xs) (f x a0)++++++{-# RULES+ "Storable.zipWith/share" forall size (h :: a->a->b) (x :: T a).+ zipWith size h x x = map (\xi -> h xi xi) x ;++-- "Storable.map/zipWith" forall size (f::c->d) (g::a->b->c) (x::T a) (y::T b).+ "Storable.map/zipWith" forall size f g x y.+ map f (zipWith size g x y) =+ zipWith size (\xi yi -> f (g xi yi)) x y ;++ -- this rule lets map run on a different block structure+ "Storable.zipWith/map,*" forall size f g x y.+ zipWith size g (map f x) y =+ zipWith size (\xi yi -> g (f xi) yi) x y ;++ "Storable.zipWith/*,map" forall size f g x y.+ zipWith size g x (map f y) =+ zipWith size (\xi yi -> g xi (f yi)) x y ;+++ "Storable.drop/unfoldr" forall size f a n.+ drop n (unfoldr size f a) =+ dropUnfoldr size n (f,a) ;++ "Storable.take/unfoldr" forall size f a n.+ take n (unfoldr size f a) =+-- takeUnfoldr size n (f,a) ;+ takeCrochet n (unfoldr size f a) ;++ "Storable.length/unfoldr" forall size f a.+ length (unfoldr size f a) = lengthUnfoldr (f,a) ;++ "Storable.map/unfoldr" forall size g f a.+ map g (unfoldr size f a) =+-- mapUnfoldr size g (f,a) ;+ mapCrochet g (unfoldr size f a) ;++ "Storable.map/iterate" forall size g f a.+ map g (iterate size f a) =+ mapCrochet g (iterate size f a) ;++{-+ "Storable.zipWith/unfoldr,unfoldr" forall sizeA sizeB f g h a b n.+ zipWith n h (unfoldr sizeA f a) (unfoldr sizeB g b) =+ zipWithUnfoldr2 n h (f,a) (g,b) ;+-}++ -- block boundaries are changed here, so it changes lazy behaviour+ "Storable.zipWith/unfoldr,*" forall sizeA sizeB f h a y.+ zipWith sizeA h (unfoldr sizeB f a) y =+ zipWithUnfoldr f h a y ;++ -- block boundaries are changed here, so it changes lazy behaviour+ "Storable.zipWith/*,unfoldr" forall sizeA sizeB f h a y.+ zipWith sizeA h y (unfoldr sizeB f a) =+ zipWithUnfoldr f (flip h) a y ;++ "Storable.crochetL/unfoldr" forall size f g a b.+ crochetL g b (unfoldr size f a) =+ unfoldr size (\(a0,b0) ->+ do (y0,a1) <- f a0+ (z0,b1) <- g y0 b0+ Just (z0, (a1,b1))) (a,b) ;++ "Storable.reduceL/unfoldr" forall size f g a b.+ reduceL g b (unfoldr size f a) =+ snd+ (FList.recourse (\(a0,b0) ->+ do (y,a1) <- f a0+ b1 <- g y b0+ Just (a1, b1)) (a,b)) ;++ "Storable.crochetL/cons" forall g b x xs.+ crochetL g b (cons x xs) =+ crochetLCons g b x xs ;++ "Storable.reduceL/cons" forall g b x xs.+ reduceL g b (cons x xs) =+ reduceLCons g b x xs ;+++++ "Storable.take/crochetL" forall f a x n.+ take n (crochetL f a x) =+ takeCrochet n (crochetL f a x) ;++ "Storable.length/crochetL" forall f a x.+ length (crochetL f a x) = length x ;++ "Storable.map/crochetL" forall g f a x.+ map g (crochetL f a x) =+ mapCrochet g (crochetL f a x) ;++ "Storable.zipWith/crochetL,*" forall size f h a x y.+ zipWith size h (crochetL f a x) y =+ zipWithCrochetL size f h a x y ;++ "Storable.zipWith/*,crochetL" forall size f h a x y.+ zipWith size h y (crochetL f a x) =+ zipWithCrochetL size f (flip h) a x y ;++ "Storable.crochetL/crochetL" forall f g a b x.+ crochetL g b (crochetL f a x) =+ crochetL (\x0 (a0,b0) ->+ do (y0,a1) <- f x0 a0+ (z0,b1) <- g y0 b0+ Just (z0, (a1,b1))) (a,b) x ;++ "Storable.reduceL/crochetL" forall f g a b x.+ reduceL g b (crochetL f a x) =+ snd+ (reduceL (\x0 (a0,b0) ->+ do (y,a1) <- f x0 a0+ b1 <- g y b0+ Just (a1, b1)) (a,b) x) ;+ #-}++-}++{- * IO -}++{- |+Read the rest of a file lazily and+provide the reason of termination as IOError.+If IOError is EOF (check with @System.Error.isEOFError err@),+then the file was read successfully.+Only access the final IOError after you have consumed the file contents,+since finding out the terminating reason forces to read the entire file.+Make also sure you read the file completely,+because it is only closed when the file end is reached+(or an exception is encountered).++TODO:+In ByteString.Lazy the chunk size is reduced+if data is not immediately available.+Maybe we should adapt that behaviour+but when working with realtime streams+that may mean that the chunks are very small.+-}+hGetContentsAsync :: Storable a =>+ ChunkSize -> Handle -> IO (IOError, Vector a)+hGetContentsAsync (ChunkSize size) h =+ let go =+ Unsafe.interleaveIO $+ flip catch (\err -> return (err,[])) $+ do v <- V.hGet h size+ if V.null v+ then hClose h >>+ return (Exc.mkIOError Exc.eofErrorType+ "StorableVector.Lazy.hGetContentsAsync" (Just h) Nothing, [])+ else fmap (mapSnd (v:)) go+{-+ Unsafe.interleaveIO $+ flip catch (\err -> return (err,[])) $+ liftM2 (\ chunk ~(err,rest) -> (err,chunk:rest))+ (V.hGet h size) go+-}+ in fmap (mapSnd SV) go++hGetContentsSync :: Storable a =>+ ChunkSize -> Handle -> IO (Vector a)+hGetContentsSync (ChunkSize size) h =+ let go =+ do v <- V.hGet h size+ if V.null v+ then return []+ else fmap (v:) go+ in fmap SV go++hPut :: Storable a => Handle -> Vector a -> IO ()+hPut h = mapM_ (V.hPut h) . chunks++{-+*Data.StorableVector.Lazy> print . mapSnd (length :: Vector Data.Int.Int16 -> Int) =<< readFileAsync (ChunkSize 1000) "dist/build/libHSstorablevector-0.1.3.a"+(dist/build/libHSstorablevector-0.1.3.a: hGetBuf: illegal operation (handle is closed),0)+-}+{- |+The file can only closed after all values are consumed.+That is you must always assert that you consume all elements of the stream,+and that no values are missed due to lazy evaluation.+This requirement makes this function useless in many applications.+-}+readFileAsync :: Storable a => ChunkSize -> FilePath -> IO (IOError, Vector a)+readFileAsync size path =+ openBinaryFile path ReadMode >>= hGetContentsAsync size++writeFile :: Storable a => FilePath -> Vector a -> IO ()+writeFile path =+ bracket (openBinaryFile path WriteMode) hClose . flip hPut++appendFile :: Storable a => FilePath -> Vector a -> IO ()+appendFile path =+ bracket (openBinaryFile path AppendMode) hClose . flip hPut+++{-# NOINLINE moduleError #-}+moduleError :: String -> String -> a+moduleError fun msg =+ error ("Data.StorableVector.Lazy." List.++ fun List.++ ':':' ':msg)
+ src/Data/StorableVector/Lazy/Builder.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE Rank2Types #-}+{- |+Build a lazy storable vector by incrementally adding an element.+This is analogous to Data.Binary.Builder for Data.ByteString.Lazy.++Attention:+This implementation is still almost 3 times slower+than constructing a lazy storable vector using unfoldr+in our Chorus speed test.+-}+module Data.StorableVector.Lazy.Builder (+ Builder,+ toLazyStorableVector,+ put,+ flush,+ ) where++import qualified Data.StorableVector as SV+import qualified Data.StorableVector.Lazy as SVL+import qualified Data.StorableVector.ST.Strict as STV+-- import qualified Data.StorableVector.ST.Lazy as STVL++import Data.StorableVector.Lazy (ChunkSize, )+import Control.Monad (liftM2, )+import Control.Monad.ST.Strict (ST, runST, )+import Data.Monoid (Monoid(mempty, mappend), )++import Foreign.Storable (Storable, )++import qualified System.Unsafe as Unsafe+++{-+Given an initial buffer and a function that generates the rest of the vector,+a 'Builder' generates the whole vector.+The idea is inspired by Data.Binary.Builder.++We use the strict ST monad by default+and only rare 'Unsafe.interleaveST',+since this is more efficient than using lazy ST everywhere.++Before that approach I tried to achieve this with a lazy State monad.+I found this more comprehensible but it was very slow+and had a space leak, when the last chunk shall be handled correctly.+-}+newtype Builder a =+ Builder {run :: forall s.+ ChunkSize ->+ (Buffer s a -> ST s [SV.Vector a]) ->+ (Buffer s a -> ST s [SV.Vector a])+ }++type Buffer s a = (STV.Vector s a, Int)+++-- instance Monoid (Builder a) where+{-+Storable constraint not needed in the current implementation,+but who knows what will be in future ...+-}+instance Storable a => Monoid (Builder a) where+ {-# INLINE mempty #-}+ {-# INLINE mappend #-}+ mempty = Builder (\_ -> id)+ mappend x y = Builder (\cs -> run x cs . run y cs)+++{- |+> toLazyStorableVector (ChunkSize 7) $ Data.Monoid.mconcat $ map put ['a'..'z']+-}+{-# INLINE toLazyStorableVector #-}+toLazyStorableVector :: Storable a =>+ ChunkSize -> Builder a -> SVL.Vector a+toLazyStorableVector cs bld =+ SVL.fromChunks $+ runST (run bld cs (fmap (:[]) . fixVector) =<< newChunk cs)+++{-# INLINE put #-}+put :: Storable a => a -> Builder a+put a =+ Builder (\cs cont (v0,i0) ->+ do STV.unsafeWrite v0 i0 a+ let i1 = succ i0+ if i1 < STV.length v0+ then+ cont (v0, i1)+ else+ liftM2 (:)+ -- we could call 'flush' here, but this requires an extra 'SV.take'+ (STV.unsafeFreeze v0)+ (Unsafe.interleaveST $+ cont =<< newChunk cs)+ )++{-+put :: Storable a => a -> Builder a+put a =+ Builder (\cs cont (v0,i0) ->+ if i0 < STV.length v0+ then+ do STV.write v0 i0 a+ cont (v0, succ i0)+ else+ liftM2 (:)+ -- we could call 'flush' here, but this requires an extra 'SV.take'+ (STV.unsafeFreeze v0)+ (Unsafe.interleaveST $+ do (v1,i1) <- newChunk cs+ STV.write v1 i1 a+ cont (v1, succ i1))+ )+-}++{-+ lazyToStrictST $+ liftM2 (:)+ -- we could call 'flush' here, but this requires an extra 'SV.take'+ (STVL.unsafeFreeze v0)+ (strictToLazyST $+ do (v1,i1) <- newChunk cs+ STV.write v1 i1 a+ cont (v1, succ i1))+-}++{-+Prelude Control.Monad.ST.Lazy> Control.Monad.ST.runST (lazyToStrictST $ Monad.liftM2 (,) (strictToLazyST $ return 'a') (strictToLazyST (undefined::Monad m => m Char)))+*** Exception: Prelude.undefined+-}++{- |+Set a laziness break.+-}+{-# INLINE flush #-}+flush :: Storable a => Builder a+flush =+ Builder (\cs cont vi0 ->+ liftM2 (:)+ (fixVector vi0)+ (Unsafe.interleaveST $ cont =<< newChunk cs)+{-+ lazyToStrictST $+ liftM2 (:)+ (strictToLazyST $ fixVector vi0)+ (strictToLazyST $ cont =<< newChunk cs)+-}+ )++{-# INLINE newChunk #-}+newChunk :: (Storable a) =>+ ChunkSize -> ST s (Buffer s a)+newChunk (SVL.ChunkSize size) =+ fmap (flip (,) 0) $ STV.new_ size++{-# INLINE fixVector #-}+fixVector :: (Storable a) =>+ Buffer s a -> ST s (SV.Vector a)+fixVector ~(v1,i1) =+ fmap (SV.take i1) $ STV.unsafeFreeze v1
+ src/Data/StorableVector/Lazy/Pattern.hs view
@@ -0,0 +1,371 @@+{- |+Functions for 'StorableVector' that allow control of the size of individual chunks.++This is import for an application like the following:+You want to mix audio signals that are relatively shifted.+The structure of chunks of three streams may be illustrated as:++> [____] [____] [____] [____] ...+> [____] [____] [____] [____] ...+> [____] [____] [____] [____] ...++When we mix the streams (@zipWith3 (\x y z -> x+y+z)@)+with respect to the chunk structure of the first signal,+computing the first chunk requires full evaluation of all leading chunks of the stream.+However the last value of the third leading chunk+is much later in time than the last value of the first leading chunk.+We like to reduce these dependencies using a different chunk structure,+say++> [____] [____] [____] [____] ...+> [__] [____] [____] [____] ...+> [] [____] [____] [____] ...++-}+module Data.StorableVector.Lazy.Pattern (+ Vector,+ ChunkSize,+ chunkSize,+ defaultChunkSize,+ LazySize,++ empty,+ singleton,+ pack,+ unpack,+ packWith,+ unpackWith,+ unfoldrN,+ iterateN,+ cycle,+ replicate,+ null,+ length,+ cons,+ append,+ concat,+ map,+ reverse,+ foldl,+ foldl',+ any,+ all,+ maximum,+ minimum,+ viewL,+ viewR,+ switchL,+ switchR,+ scanl,+ mapAccumL,+ mapAccumR,+ crochetL,+ take,+ drop,+ splitAt,+ takeVectorPattern,+ splitAtVectorPattern,+ dropMarginRem,+ dropMargin,+ dropWhile,+ takeWhile,+ span,+ filter,+ zipWith,+ zipWith3,+ zipWith4,+ zipWithSize,+ zipWithSize3,+ zipWithSize4,+{-+ pad,+ cancelNullVector,+-}+ ) where++import Numeric.NonNegative.Class ((-|))+import qualified Numeric.NonNegative.Chunky as LS+import qualified Data.StorableVector.Lazy as LSV+import qualified Data.StorableVector as V++import Data.StorableVector.Lazy (Vector(SV), ChunkSize(ChunkSize))++import Data.StorableVector.Lazy (+ chunkSize, defaultChunkSize,+ empty, singleton, unpack, unpackWith, cycle,+ null, cons, append, concat, map, reverse,+ foldl, foldl', any, all, maximum, minimum,+ viewL, viewR, switchL, switchR,+ scanl, mapAccumL, mapAccumR, crochetL,+ dropMarginRem, dropMargin,+ dropWhile, takeWhile, span, filter, + zipWith, zipWith3, zipWith4, + )++import qualified Data.List as List++import qualified Data.List.HT as ListHT+import Data.Tuple.HT (mapPair, mapFst, forcePair, swap, )++import Control.Monad (liftM2, liftM3, liftM4, guard, )++import Foreign.Storable (Storable)++import Prelude hiding+ (length, (++), iterate, foldl, map, repeat, replicate, null,+ zip, zipWith, zipWith3, drop, take, splitAt, takeWhile, dropWhile, reverse,+ any, all, concat, cycle, filter, maximum, minimum, scanl, span, )+{-+import Data.Maybe (Maybe(Just, Nothing), )+import Prelude (Int, (.), ($), fst, snd, (<=), flip, curry, return, fmap, not, uncurry, )+-}+++type LazySize = LS.T ChunkSize++-- * Introducing and eliminating 'Vector's++{-+Actually, this is lazy enough:++> LSV.unpack $ pack (LS.fromChunks [10,15]) (['a'..'y'] List.++ Prelude.undefined)+"abcdefghijklmnopqrstuvwxy"+-}+pack :: (Storable a) => LazySize -> [a] -> Vector a+pack size =+ fst . unfoldrN size ListHT.viewL+++{-# INLINE packWith #-}+packWith :: (Storable b) => LazySize -> (a -> b) -> [a] -> Vector b+packWith size f =+ fst . unfoldrN size (fmap (mapFst f) . ListHT.viewL)+++{-+{-# INLINE unfoldrNAlt #-}+unfoldrNAlt :: (Storable b) =>+ LazySize+ -> (a -> Maybe (b,a))+ -> a+ -> (Vector b, Maybe a)+unfoldrNAlt (LS.Cons size) f x =+ let go sz y =+ case sz of+ [] -> ([], y)+ (ChunkSize s : ss) ->+ maybe+ ([], Nothing)+ ((\(c,a1) -> mapFst (c:) $ go ss a1) .+ V.unfoldrN s (fmap (mapSnd f)))+ (f y)+ in mapFst SV $ go size (Just x)+-}++{-# INLINE unfoldrN #-}+unfoldrN :: (Storable b) =>+ LazySize+ -> (a -> Maybe (b,a))+ -> a+ -> (Vector b, Maybe a)+unfoldrN size f =+ let go sz y =+ forcePair $+ case sz of+ [] -> ([], y)+ (ChunkSize s : ss) ->+ let m =+ do a0 <- y+ let p = V.unfoldrN s f a0+ guard (not (V.null (fst p)))+ return p+ in case m of+ Nothing -> ([], Nothing)+ Just (c,a1) -> mapFst (c:) $ go ss a1+ in mapFst SV . go (LS.toChunks size) . Just+++{-# INLINE iterateN #-}+iterateN :: Storable a => LazySize -> (a -> a) -> a -> Vector a+iterateN size f =+ fst . unfoldrN size (\x -> Just (x, f x))++{-+Tries to be time and memory efficient+by reusing subvectors of a chunk+until a larger chunk is needed.+However, it can be a memory leak+if a huge chunk is followed by many little ones.+-}+replicate :: Storable a => LazySize -> a -> Vector a+replicate size x =+ SV $ snd $+ List.mapAccumL+ (\v (ChunkSize m) ->+ if m <= V.length v+ then (v, V.take m v)+ else let v1 = V.replicate m x+ in (v1,v1))+ V.empty $+ LS.toChunks size++{-+replicate :: Storable a => LazySize -> a -> Vector a+replicate size x =+ SV $ List.map (\(ChunkSize m) -> V.replicate m x) (LS.toChunks size)+-}+++-- * Basic interface++length :: Vector a -> LazySize+length = LS.fromChunks . List.map chunkLength . LSV.chunks++chunkLength :: V.Vector a -> ChunkSize+chunkLength = ChunkSize . V.length++decrementLimit :: V.Vector a -> LazySize -> LazySize+decrementLimit x y =+ y -| LS.fromNumber (chunkLength x)++intFromChunkSize :: ChunkSize -> Int+intFromChunkSize (ChunkSize x) = x++intFromLazySize :: LazySize -> Int+intFromLazySize =+ List.sum . List.map intFromChunkSize . LS.toChunks++++-- * sub-vectors++{- |+Generates laziness breaks+wherever either the lazy length number or the vector has a chunk boundary.+-}+{-# INLINE take #-}+take :: (Storable a) => LazySize -> Vector a -> Vector a+take n = fst . splitAt n++{- |+Preserves the chunk pattern of the lazy vector.+-}+{-# INLINE takeVectorPattern #-}+takeVectorPattern :: (Storable a) => LazySize -> Vector a -> Vector a+takeVectorPattern _ (SV []) = empty+takeVectorPattern n (SV (x:xs)) =+ if List.null (LS.toChunks n)+ then empty+ else+ let remain = decrementLimit x n+ in SV $ uncurry (:) $+ if LS.isNull remain+ then (V.take (intFromLazySize n) x, [])+ else+ (x, LSV.chunks $ take remain $ LSV.fromChunks xs)++{-# INLINE drop #-}+drop :: (Storable a) => LazySize -> Vector a -> Vector a+drop size xs =+ List.foldl (flip (LSV.drop . intFromChunkSize)) xs (LS.toChunks size)++{-# INLINE splitAt #-}+splitAt ::+ (Storable a) => LazySize -> Vector a -> (Vector a, Vector a)+splitAt size xs =+ mapFst LSV.concat $ swap $+ List.mapAccumL+ (\xs0 n ->+ swap $ LSV.splitAt (intFromChunkSize n) xs0)+ xs (LS.toChunks size)++{-# INLINE splitAtVectorPattern #-}+splitAtVectorPattern ::+ (Storable a) => LazySize -> Vector a -> (Vector a, Vector a)+splitAtVectorPattern n0 =+ forcePair .+ if List.null (LS.toChunks n0)+ then (,) empty+ else+ let recourse n xt =+ forcePair $+ case xt of+ [] -> ([], [])+ (x:xs) ->+ let remain = decrementLimit x n+ in if LS.isNull remain+ then mapPair ((:[]), (:xs)) $+ V.splitAt (intFromLazySize n) x+ else mapFst (x:) $ recourse remain xs+ in mapPair (SV, SV) . recourse n0 . LSV.chunks+++{-# INLINE [0] zipWithSize #-}+zipWithSize :: (Storable a, Storable b, Storable c) =>+ LazySize+ -> (a -> b -> c)+ -> Vector a+ -> Vector b+ -> Vector c+zipWithSize size f =+ curry (fst . unfoldrN size (\(xt,yt) ->+ liftM2+ (\(x,xs) (y,ys) -> (f x y, (xs,ys)))+ (viewL xt)+ (viewL yt)))++{-# INLINE zipWithSize3 #-}+zipWithSize3 ::+ (Storable a, Storable b, Storable c, Storable d) =>+ LazySize -> (a -> b -> c -> d) ->+ (Vector a -> Vector b -> Vector c -> Vector d)+zipWithSize3 size f s0 s1 s2 =+ fst $ unfoldrN size (\(xt,yt,zt) ->+ liftM3+ (\(x,xs) (y,ys) (z,zs) ->+ (f x y z, (xs,ys,zs)))+ (viewL xt)+ (viewL yt)+ (viewL zt))+ (s0,s1,s2)++{-# INLINE zipWithSize4 #-}+zipWithSize4 ::+ (Storable a, Storable b, Storable c, Storable d, Storable e) =>+ LazySize -> (a -> b -> c -> d -> e) ->+ (Vector a -> Vector b -> Vector c -> Vector d -> Vector e)+zipWithSize4 size f s0 s1 s2 s3 =+ fst $ unfoldrN size (\(xt,yt,zt,wt) ->+ liftM4+ (\(x,xs) (y,ys) (z,zs) (w,ws) ->+ (f x y z w, (xs,ys,zs,ws)))+ (viewL xt)+ (viewL yt)+ (viewL zt)+ (viewL wt))+ (s0,s1,s2,s3)++{-+{- |+Ensure a minimal length of the list by appending pad values.+-}+{-# ONLINE pad #-}+pad :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a+pad size y n0 =+ let recourse n xt =+ if n<=0+ then xt+ else+ case xt of+ [] -> chunks $ replicate size n y+ x:xs -> x : recourse (n - V.length x) xs+ in SV . recourse n0 . chunks++padAlt :: (Storable a) => ChunkSize -> a -> Int -> Vector a -> Vector a+padAlt size x n xs =+ append xs+ (let m = length xs+ in if n>m+ then replicate size (n-m) x+ else empty)+-}
+ src/Data/StorableVector/Lazy/Pointer.hs view
@@ -0,0 +1,20 @@+{- |+In principle you can traverse through a lazy storable vector+using repeated calls to @viewL@.+However this needs a bit of pointer arrangement and allocation.+This data structure makes the inner loop faster,+that consists of traversing through a chunk.+-}+module Data.StorableVector.Lazy.Pointer (+ Pointer, cons, viewL, switchL,+ ) where++import Data.StorableVector.Lazy.PointerPrivate (Pointer(..), viewL, switchL, )+import qualified Data.StorableVector.Lazy as VL++import Foreign.Storable (Storable)+++{-# INLINE cons #-}+cons :: Storable a => VL.Vector a -> Pointer a+cons = VL.pointer
+ src/Data/StorableVector/Lazy/PointerPrivate.hs view
@@ -0,0 +1,42 @@+module Data.StorableVector.Lazy.PointerPrivate where++import qualified Data.StorableVector.Pointer as VP+import qualified Data.StorableVector as V+import qualified Data.StorableVector.Base as VB++import Foreign.Storable (Storable)+++data Pointer a =+ Pointer {+ chunks :: [VB.Vector a],+ ptr :: {-# UNPACK #-} !(VP.Pointer a)+ }+++empty :: Storable a => Pointer a+empty =+ Pointer [] (VP.cons V.empty)++{-# INLINE cons #-}+cons :: Storable a => [VB.Vector a] -> Pointer a+cons [] = empty+cons (c:cs) = Pointer cs (VP.cons c)++{-# INLINE viewL #-}+viewL :: Storable a => Pointer a -> Maybe (a, Pointer a)+viewL = switchL Nothing (curry Just)++{-# INLINE switchL #-}+switchL :: Storable a =>+ b -> (a -> Pointer a -> b) -> Pointer a -> b+switchL n j =+ let recourse p =+ let ct = chunks p+ in VP.switchL+ (case ct of+ [] -> n+ (c:cs) -> recourse (Pointer cs (VP.cons c)))+ (\a cp -> j a (Pointer ct cp))+ (ptr p)+ in recourse
+ src/Data/StorableVector/Lazy/PointerPrivateIndex.hs view
@@ -0,0 +1,38 @@+{-+Alternative to PointerPrivate implemented at a higher level.+-}+module Data.StorableVector.Lazy.PointerPrivateIndex where++import qualified Data.StorableVector as V+import qualified Data.StorableVector.Base as VB++import Foreign.Storable (Storable)+++data Pointer a =+ Pointer {chunks :: ![VB.Vector a], index :: !Int}+++{-# INLINE cons #-}+cons :: Storable a => [VB.Vector a] -> Pointer a+cons = flip Pointer 0++{-# INLINE viewL #-}+viewL :: Storable a => Pointer a -> Maybe (a, Pointer a)+viewL = switchL Nothing (curry Just)++{-# INLINE switchL #-}+switchL :: Storable a =>+ b -> (a -> Pointer a -> b) -> Pointer a -> b+switchL n j =+ let recourse p =+ let s = chunks p+ in case s of+ [] -> n+ (c:cs) ->+ let i = index p+ d = i - V.length c+ in if d < 0+ then j (VB.unsafeIndex c i) (Pointer s (i+1))+ else recourse (Pointer cs d)+ in recourse
+ src/Data/StorableVector/Pointer.hs view
@@ -0,0 +1,52 @@+{- |+In principle you can traverse through a storable vector+using repeated calls to @viewL@ or using @index@.+However this needs a bit of pointer arrangement and allocation.+This data structure should make loops optimally fast.+-}+module Data.StorableVector.Pointer where++-- import qualified Data.StorableVector as V+import qualified Data.StorableVector.Base as VB++import qualified Foreign.ForeignPtr as FPtr+import Foreign.Marshal.Array (advancePtr, )+import Foreign.Storable (Storable, peek, )+import Foreign (Ptr, )+import qualified System.Unsafe as Unsafe+++{-+The reference to the ForeignPtr asserts,+that the array is maintained and thus is not garbage collected.+The Ptr we use for traversing would not achieve this.+-}+{- |+We might have name the data type iterator.+-}+data Pointer a =+ Pointer {+ fptr :: {-# UNPACK #-} !(FPtr.ForeignPtr a),+ ptr :: {-# UNPACK #-} !(Ptr a),+ left :: {-# UNPACK #-} !Int+ }+++{-# INLINE cons #-}+cons :: Storable a => VB.Vector a -> Pointer a+cons (VB.SV fp s l) =+ Pointer fp (advancePtr (Unsafe.foreignPtrToPtr fp) s) l+++{-# INLINE viewL #-}+viewL :: Storable a => Pointer a -> Maybe (a, Pointer a)+viewL = switchL Nothing (curry Just)++{-# INLINE switchL #-}+switchL :: Storable a =>+ b -> (a -> Pointer a -> b) -> Pointer a -> b+switchL n j (Pointer fp p l) =+ if l<=0+ then n+ else j (VB.inlinePerformIO (peek p)) (Pointer fp (advancePtr p 1) (l-1))+-- Unsafe.performIO at this place would make SpeedPointer test 0.5 s slower
+ src/Data/StorableVector/ST/Lazy.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE Rank2Types #-}+{- |+Module : Data.StorableVector.ST.Strict+License : BSD-style+Maintainer : haskell@henning-thielemann.de+Stability : experimental+Portability : portable, requires ffi+Tested with : GHC 6.4.1++Interface for access to a mutable StorableVector.+-}+module Data.StorableVector.ST.Lazy (+ Vector,+ new,+ new_,+ read,+ write,+ modify,+ unsafeRead,+ unsafeWrite,+ unsafeModify,+ freeze,+ unsafeFreeze,+ thaw,+ VST.length,+ runSTVector,+ mapST,+ mapSTLazy,+ ) where++-- import qualified Data.StorableVector.Base as V+import qualified Data.StorableVector as VS+import qualified Data.StorableVector.Lazy as VL++import qualified Data.StorableVector.ST.Strict as VST++import Data.StorableVector.ST.Strict (Vector)+++import qualified Control.Monad.ST.Lazy as ST+import Control.Monad.ST.Lazy (ST)++import Foreign.Storable (Storable)++-- import Prelude (Int, ($), (+), return, const, )+import Prelude hiding (read, length, )++++-- * access to mutable storable vector++{-# INLINE new #-}+new :: (Storable e) =>+ Int -> e -> ST s (Vector s e)+new n x = ST.strictToLazyST (VST.new n x)++{-# INLINE new_ #-}+new_ :: (Storable e) =>+ Int -> ST s (Vector s e)+new_ n = ST.strictToLazyST (VST.new_ n)++{- |+> Control.Monad.ST.runST (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; read arr 3)+-}+{-# INLINE read #-}+read :: (Storable e) =>+ Vector s e -> Int -> ST s e+read xs n = ST.strictToLazyST (VST.read xs n)++{- |+> VS.unpack $ runSTVector (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; return arr)+-}+{-# INLINE write #-}+write :: (Storable e) =>+ Vector s e -> Int -> e -> ST s ()+write xs n x = ST.strictToLazyST (VST.write xs n x)++{-# INLINE modify #-}+modify :: (Storable e) =>+ Vector s e -> Int -> (e -> e) -> ST s ()+modify xs n f = ST.strictToLazyST (VST.modify xs n f)+++{-# INLINE unsafeRead #-}+unsafeRead :: (Storable e) =>+ Vector s e -> Int -> ST s e+unsafeRead xs n = ST.strictToLazyST (VST.unsafeRead xs n)++{-# INLINE unsafeWrite #-}+unsafeWrite :: (Storable e) =>+ Vector s e -> Int -> e -> ST s ()+unsafeWrite xs n x = ST.strictToLazyST (VST.unsafeWrite xs n x)++{-# INLINE unsafeModify #-}+unsafeModify :: (Storable e) =>+ Vector s e -> Int -> (e -> e) -> ST s ()+unsafeModify xs n f = ST.strictToLazyST (VST.unsafeModify xs n f)+++{-# INLINE freeze #-}+freeze :: (Storable e) =>+ Vector s e -> ST s (VS.Vector e)+freeze xs = ST.strictToLazyST (VST.freeze xs)++{-# INLINE unsafeFreeze #-}+unsafeFreeze :: (Storable e) =>+ Vector s e -> ST s (VS.Vector e)+unsafeFreeze xs = ST.strictToLazyST (VST.unsafeFreeze xs)++{-# INLINE thaw #-}+thaw :: (Storable e) =>+ VS.Vector e -> ST s (Vector s e)+thaw xs = ST.strictToLazyST (VST.thaw xs)++++{-# INLINE runSTVector #-}+runSTVector :: (Storable e) =>+ (forall s. ST s (Vector s e)) -> VS.Vector e+runSTVector m = VST.runSTVector (ST.lazyToStrictST m)++++-- * operations on immutable storable vector within ST monad++{- |+> :module + Data.STRef+> VS.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapST (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VS.pack [1,2,3,4::Data.Int.Int16]))+-}+{-# INLINE mapST #-}+mapST :: (Storable a, Storable b) =>+ (a -> ST s b) -> VS.Vector a -> ST s (VS.Vector b)+mapST f xs =+ ST.strictToLazyST (VST.mapST (ST.lazyToStrictST . f) xs)+++{- |+> *Data.StorableVector.ST.Strict Data.STRef> VL.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [1,2,3,4::Data.Int.Int16]))+> "abcd"++The following should not work on infinite streams,+since we are in 'ST' with strict '>>='.+But it works. Why?++> *Data.StorableVector.ST.Strict Data.STRef.Lazy> VL.unpack $ Control.Monad.ST.Lazy.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [0::Data.Int.Int16 ..]))+> "Interrupted.+-}+{-# INLINE mapSTLazy #-}+mapSTLazy :: (Storable a, Storable b) =>+ (a -> ST s b) -> VL.Vector a -> ST s (VL.Vector b)+mapSTLazy f (VL.SV xs) =+ fmap VL.SV $ mapM (mapST f) xs
+ src/Data/StorableVector/ST/Private.hs view
@@ -0,0 +1,54 @@+{- |+Module : Data.StorableVector.ST.Strict+License : BSD-style+Maintainer : haskell@henning-thielemann.de+Stability : experimental+Portability : portable, requires ffi+Tested with : GHC 6.4.1++-}+module Data.StorableVector.ST.Private where++import qualified Data.StorableVector.Base as V++import Data.StorableVector.Memory (mallocForeignPtrArray, )++import Control.Monad.ST.Strict (ST, )++import Foreign.Ptr (Ptr, )+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, )+import Foreign.Storable (Storable, )++import qualified System.Unsafe as Unsafe++-- import Prelude (Int, ($), (+), return, const, )+import Prelude hiding (read, length, )+++data Vector s a =+ SV {-# UNPACK #-} !(ForeignPtr a)+ {-# UNPACK #-} !Int -- length+++-- | Wrapper of mallocForeignPtrArray.+create :: (Storable a) => Int -> (Ptr a -> IO ()) -> IO (Vector s a)+create l f = do+ fp <- mallocForeignPtrArray l+ withForeignPtr fp f+ return $! SV fp l++{-# INLINE unsafeCreate #-}+unsafeCreate :: (Storable a) => Int -> (Ptr a -> IO ()) -> ST s (Vector s a)+unsafeCreate l f = Unsafe.ioToST $ create l f++{-+This function must be in ST monad,+since it is usually called+as termination of a series of write accesses to the vector.+We must assert that no read access to the V.Vector can happen+before the end of the write accesses.+(And the caller must assert, that he actually never writes again into that vector.)+-}+{-# INLINE unsafeToVector #-}+unsafeToVector :: Vector s a -> ST s (V.Vector a)+unsafeToVector (SV x l) = return (V.SV x 0 l)
+ src/Data/StorableVector/ST/Strict.hs view
@@ -0,0 +1,287 @@+{-# LANGUAGE Rank2Types #-}+{- |+Module : Data.StorableVector.ST.Strict+License : BSD-style+Maintainer : haskell@henning-thielemann.de+Stability : experimental+Portability : portable, requires ffi+Tested with : GHC 6.4.1++Interface for access to a mutable StorableVector.+-}+module Data.StorableVector.ST.Strict (+ Vector,+ new,+ new_,+ read,+ write,+ modify,+ maybeRead,+ maybeWrite,+ maybeModify,+ unsafeRead,+ unsafeWrite,+ unsafeModify,+ freeze,+ unsafeFreeze,+ thaw,+ length,+ runSTVector,+ mapST,+ mapSTLazy,+ ) where++import Data.StorableVector.ST.Private+ (Vector(SV), create, unsafeCreate, unsafeToVector, )+import qualified Data.StorableVector.Base as V+import qualified Data.StorableVector as VS+import qualified Data.StorableVector.Lazy as VL++import Control.Monad.ST.Strict (ST, runST, )++import Foreign.Ptr (Ptr, )+import Foreign.ForeignPtr (withForeignPtr, )+import Foreign.Storable (Storable(peek, poke))+import Foreign.Marshal.Array (advancePtr, copyArray, )+import qualified System.Unsafe as Unsafe++import qualified Data.Traversable as Traversable+import Data.Maybe.HT (toMaybe, )+import Data.Maybe (isJust, )++-- import Prelude (Int, ($), (+), return, const, )+import Prelude hiding (read, length, )+++-- * access to mutable storable vector++{-# INLINE new #-}+new :: (Storable e) =>+ Int -> e -> ST s (Vector s e)+new n x =+ unsafeCreate n $+ let {-# INLINE go #-}+ go m p =+ if m>0+ then poke p x >> go (pred m) (V.incPtr p)+ else return ()+ in go n++{-# INLINE new_ #-}+new_ :: (Storable e) =>+ Int -> ST s (Vector s e)+new_ n =+ unsafeCreate n (const (return ()))+++{- |+> Control.Monad.ST.runST (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; read arr 3)+-}+{-# INLINE read #-}+read :: (Storable e) =>+ Vector s e -> Int -> ST s e+read v n =+ access "read" v n $ unsafeRead v n++{- |+> VS.unpack $ runSTVector (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; return arr)+-}+{-# INLINE write #-}+write :: (Storable e) =>+ Vector s e -> Int -> e -> ST s ()+write v n x =+ access "write" v n $ unsafeWrite v n x++{- |+> VS.unpack $ runSTVector (do arr <- new 10 'a'; Monad.mapM_ (\n -> modify arr (mod n 8) succ) [0..10]; return arr)+-}+{-# INLINE modify #-}+modify :: (Storable e) =>+ Vector s e -> Int -> (e -> e) -> ST s ()+modify v n f =+ access "modify" v n $ unsafeModify v n f++{-# INLINE access #-}+access :: (Storable e) =>+ String -> Vector s e -> Int -> ST s a -> ST s a+access name (SV _v l) n act =+ if 0<=n && n<l+ then act+ else error ("StorableVector.ST." ++ name ++ ": index out of range")+++{- |+Returns @Just e@, when the element @e@ could be read+and 'Nothing' if the index was out of range.+This way you can avoid duplicate index checks+that may be needed when using 'read'.++> Control.Monad.ST.runST (do arr <- new_ 10; Monad.zipWithM_ (write arr) [9,8..0] ['a'..]; read arr 3)++In future 'maybeRead' will replace 'read'.+-}+{-# INLINE maybeRead #-}+maybeRead :: (Storable e) =>+ Vector s e -> Int -> ST s (Maybe e)+maybeRead v n =+ maybeAccess v n $ unsafeRead v n++{- |+Returns 'True' if the element could be written+and 'False' if the index was out of range.++> runSTVector (do arr <- new_ 10; foldr (\c go i -> maybeWrite arr i c >>= \cont -> if cont then go (succ i) else return arr) (error "unreachable") ['a'..] 0)++In future 'maybeWrite' will replace 'write'.+-}+{-# INLINE maybeWrite #-}+maybeWrite :: (Storable e) =>+ Vector s e -> Int -> e -> ST s Bool+maybeWrite v n x =+ fmap isJust $ maybeAccess v n $ unsafeWrite v n x++{- |+Similar to 'maybeWrite'.++In future 'maybeModify' will replace 'modify'.+-}+{-# INLINE maybeModify #-}+maybeModify :: (Storable e) =>+ Vector s e -> Int -> (e -> e) -> ST s Bool+maybeModify v n f =+ fmap isJust $ maybeAccess v n $ unsafeModify v n f++{-# INLINE maybeAccess #-}+maybeAccess :: (Storable e) =>+ Vector s e -> Int -> ST s a -> ST s (Maybe a)+maybeAccess (SV _v l) n act =+ Traversable.sequence $ toMaybe (0<=n && n<l) act+{-+ if 0<=n && n<l+ then fmap Just act+ else return Nothing+-}++{-# INLINE unsafeRead #-}+unsafeRead :: (Storable e) =>+ Vector s e -> Int -> ST s e+unsafeRead v n =+ unsafeAccess v n $ peek++{-# INLINE unsafeWrite #-}+unsafeWrite :: (Storable e) =>+ Vector s e -> Int -> e -> ST s ()+unsafeWrite v n x =+ unsafeAccess v n $ \p -> poke p x++{-# INLINE unsafeModify #-}+unsafeModify :: (Storable e) =>+ Vector s e -> Int -> (e -> e) -> ST s ()+unsafeModify v n f =+ unsafeAccess v n $ \p -> poke p . f =<< peek p++{-# INLINE unsafeAccess #-}+unsafeAccess :: (Storable e) =>+ Vector s e -> Int -> (Ptr e -> IO a) -> ST s a+unsafeAccess (SV v _l) n act =+ Unsafe.ioToST (withForeignPtr v $ \p -> act (advancePtr p n))+++{-# INLINE freeze #-}+freeze :: (Storable e) =>+ Vector s e -> ST s (VS.Vector e)+freeze (SV x l) =+ Unsafe.ioToST $+ V.create l $ \p ->+ withForeignPtr x $ \f ->+ copyArray p f (fromIntegral l)++{- |+This is like 'freeze' but it does not copy the vector.+You must make sure that you never write again to the array.+It is best to use 'unsafeFreeze' only at the end of a block,+that is run by 'runST'.+-}+{-# INLINE unsafeFreeze #-}+unsafeFreeze :: (Storable e) =>+ Vector s e -> ST s (VS.Vector e)+unsafeFreeze = unsafeToVector+++{-# INLINE thaw #-}+thaw :: (Storable e) =>+ VS.Vector e -> ST s (Vector s e)+thaw v =+ Unsafe.ioToST $+ V.withStartPtr v $ \f l ->+ create l $ \p ->+ copyArray p f (fromIntegral l)+++{-# INLINE length #-}+length ::+ Vector s e -> Int+length (SV _v l) = l+++{-# INLINE runSTVector #-}+runSTVector :: (Storable e) =>+ (forall s. ST s (Vector s e)) -> VS.Vector e+runSTVector m =+ runST (unsafeToVector =<< m)++++-- * operations on immutable storable vector within ST monad++{- |+> :module + Data.STRef+> VS.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapST (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VS.pack [1,2,3,4::Data.Int.Int16]))+-}+{-# INLINE mapST #-}+mapST :: (Storable a, Storable b) =>+ (a -> ST s b) -> VS.Vector a -> ST s (VS.Vector b)+mapST f (V.SV px sx n) =+ let {-# INLINE go #-}+ go l q p =+ if l>0+ then+ do Unsafe.ioToST . poke p =<< f =<< Unsafe.ioToST (peek q)+ go (pred l) (advancePtr q 1) (advancePtr p 1)+ else return ()+ in do ys@(SV py _) <- new_ n+ go n+ (Unsafe.foreignPtrToPtr px `advancePtr` sx)+ (Unsafe.foreignPtrToPtr py)+ unsafeToVector ys++{-+mapST f xs@(V.SV v s l) =+ let go l q p =+ if l>0+ then+ do poke p =<< stToIO . f =<< peek q+ go (pred l) (advancePtr q 1) (advancePtr p 1)+ else return ()+ n = VS.length xs+ in return $ V.unsafeCreate n $ \p ->+ withForeignPtr v $ \q -> go n (advancePtr q s) p+-}+++{- |+> *Data.StorableVector.ST.Strict Data.STRef> VL.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [1,2,3,4::Data.Int.Int16]))+> "abcd"++The following should not work on infinite streams,+since we are in 'ST' with strict '>>='.+But it works. Why?++> *Data.StorableVector.ST.Strict Data.STRef> VL.unpack $ Control.Monad.ST.runST (do ref <- newSTRef 'a'; mapSTLazy (\ _n -> do c <- readSTRef ref; modifySTRef ref succ; return c) (VL.pack VL.defaultChunkSize [0::Data.Int.Int16 ..]))+> "Interrupted.+-}+{-# INLINE mapSTLazy #-}+mapSTLazy :: (Storable a, Storable b) =>+ (a -> ST s b) -> VL.Vector a -> ST s (VL.Vector b)+mapSTLazy f (VL.SV xs) =+ fmap VL.SV $ mapM (mapST f) xs
storablevector.cabal view
@@ -1,5 +1,5 @@ Name: storablevector-Version: 0.2.8.3+Version: 0.2.9 Category: Data Synopsis: Fast, packed, strict storable arrays with a list interface like ByteString Description:@@ -25,9 +25,10 @@ Homepage: http://www.haskell.org/haskellwiki/Storable_Vector Stability: Experimental Build-Type: Simple-Tested-With: GHC==6.8.2, GHC==6.12.3, GHC==7.4.1, GHC==7.6.2+Tested-With: GHC==6.8.2, GHC==6.12.3+Tested-With: GHC==7.4.1, GHC==7.6.2, GHC==7.8.2 Tested-With: JHC==0.7.3-Cabal-Version: >=1.6+Cabal-Version: >=1.14 Extra-Source-Files: foreign-ptr/fast/Data/StorableVector/Memory.hs foreign-ptr/slow/Data/StorableVector/Memory.hs@@ -39,13 +40,7 @@ Flag separateSYB description: Data.Generics available in separate package. -Flag functorInstance- description: Use a custom Functor instance for pairs and functions -Flag buildTests- description: Build test executables- default: False- Source-Repository head type: darcs location: http://code.haskell.org/storablevector/@@ -53,13 +48,14 @@ Source-Repository this type: darcs location: http://code.haskell.org/storablevector/- tag: 0.2.8.3+ tag: 0.2.9 + Library Build-Depends: non-negative >=0.1 && <0.2, utility-ht >=0.0.5 && <0.1,- transformers >=0.2 && <0.4,+ transformers >=0.2 && <0.5, unsafe >=0.0 && <0.1, QuickCheck >=1 && <3 @@ -81,9 +77,9 @@ Else Build-Depends: base >=1 && <2 - Extensions: CPP, ForeignFunctionInterface GHC-Options: -Wall -funbox-strict-fields- Hs-Source-Dirs: .+ Default-Language: Haskell98+ Hs-Source-Dirs: src Exposed-Modules: Data.StorableVector@@ -108,59 +104,53 @@ Data.StorableVector.Lazy.PointerPrivateIndex --Executable test+Test-Suite test+ Type: exitcode-stdio-1.0 GHC-Options: -Wall -funbox-strict-fields- Hs-Source-Dirs: ., foreign-ptr/slow, tests- Main-Is: tests.hs- Other-Modules: QuickCheckUtils, Instances- Extensions: CPP, ForeignFunctionInterface- If flag(buildTests)+ Default-Language: Haskell98+ Hs-Source-Dirs: tests+ Main-Is: Test.hs+ Other-Modules: Test.Utility+ Build-Depends:+ storablevector,+ bytestring >=0.9 && <0.11,+ QuickCheck >=1 && <3+ If flag(splitBase) Build-Depends:- bytestring >=0.9 && <0.11,- QuickCheck >=1 && <3- If flag(splitBase)- Build-Depends: random >=1.0 && <1.1- If flag(functorInstance)- Hs-Source-Dirs: tests-2- Build-Depends: base >=3 && <4- Else- Hs-Source-Dirs: tests-1- Build-Depends: base >=4 && <5- Else- Hs-Source-Dirs: tests-1- Build-Depends: base >=1.0 && <2+ random >=1.0 && <1.1,+ base >=3 && <5 Else- Buildable: False+ Hs-Source-Dirs: tests-1+ Build-Depends: base >=1.0 && <2 -Executable speedtest+Benchmark speedtest+ Type: exitcode-stdio-1.0 GHC-Options: -Wall -- -fvia-C -optc-ffast-math -optc-O3 -optc-ftree-vectorize Main-Is: SpeedTestChorus.hs Other-Modules: Data.StorableVector.Private- Extensions: CPP, ForeignFunctionInterface- Hs-Source-Dirs: ., foreign-ptr/slow, speedtest- If flag(buildTests)- Build-Depends:- sample-frame >=0.0.1 && <0.1,- deepseq >=1.1 && <1.4- If flag(splitBase)- Build-Depends: base >= 3 && <5- Else- Build-Depends: base >= 1.0 && < 2+ Default-Language: Haskell98+ Hs-Source-Dirs: speedtest+ Build-Depends:+ storablevector,+ sample-frame >=0.0.1 && <0.1,+ deepseq >=1.1 && <1.4+ If flag(splitBase)+ Build-Depends: base >= 3 && <5 Else- Buildable: False+ Build-Depends: base >= 1.0 && < 2 -Executable speedpointer+Benchmark speedpointer+ Type: exitcode-stdio-1.0 GHC-Options: -Wall Main-Is: Pointer.hs- Extensions: CPP, ForeignFunctionInterface- Hs-Source-Dirs: ., foreign-ptr/slow, speedtest- If flag(buildTests)- If flag(splitBase)- Build-Depends: base >=3 && <5- Else- Build-Depends: base >=1.0 && <2+ Default-Language: Haskell98+ Hs-Source-Dirs: speedtest+ Build-Depends:+ storablevector,+ utility-ht+ If flag(splitBase)+ Build-Depends: base >=3 && <5 Else- Buildable: False+ Build-Depends: base >=1.0 && <2
− tests-2/Instances.hs
@@ -1,1 +0,0 @@-module Instances where
− tests/QuickCheckUtils.hs
@@ -1,225 +0,0 @@-{-# OPTIONS_GHC -O -fglasgow-exts #-}------ Uses multi-param type classes----module QuickCheckUtils where--import Instances ()--import Test.QuickCheck--- import Test.QuickCheck (Arbitrary(arbitrary, coarbitrary), variant, choose, sized, (==>), Property, )-import Text.Show.Functions ()-import System.Random (RandomGen, StdGen, Random, newStdGen, split, randomR, random, )--import Control.Monad (liftM2)-import Data.Char (ord)-import Data.Word (Word8)-import Data.Int (Int64)-import System.IO (hFlush, stdout, )--import qualified Data.ByteString as P-import qualified Data.StorableVector as V-import qualified Data.List as List--import qualified Data.ByteString.Char8 as PC---- Enable this to get verbose test output. Including the actual tests.-debug = False--mytest :: Testable a => a -> Int -> IO ()-mytest a n = mycheck defaultConfig- { configMaxTest=n- , configEvery= \n args -> if debug then show n ++ ":\n" ++ unlines args else [] } a--mycheck :: Testable a => Config -> a -> IO ()-mycheck config a =- do rnd <- newStdGen- mytests config (evaluate a) rnd 0 0 []--mytests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]] -> IO ()-mytests config gen rnd0 ntest nfail stamps- | ntest == configMaxTest config = do done "OK," ntest stamps- | nfail == configMaxFail config = do done "Arguments exhausted after" ntest stamps- | otherwise =- do putStr (configEvery config ntest (arguments result)) >> hFlush stdout- case ok result of- Nothing ->- mytests config gen rnd1 ntest (nfail+1) stamps- Just True ->- mytests config gen rnd1 (ntest+1) nfail (stamp result:stamps)- Just False ->- putStr ( "Falsifiable after "- ++ show ntest- ++ " tests:\n"- ++ unlines (arguments result)- ) >> hFlush stdout- where- result = generate (configSize config ntest) rnd2 gen- (rnd1,rnd2) = split rnd0--done :: String -> Int -> [[String]] -> IO ()-done mesg ntest stamps =- do putStr ( mesg ++ " " ++ show ntest ++ " tests" ++ table )- where- table = display- . map entry- . reverse- . List.sort- . map pairLength- . List.group- . List.sort- . filter (not . null)- $ stamps-- display [] = ".\n"- display [x] = " (" ++ x ++ ").\n"- display xs = ".\n" ++ unlines (map (++ ".") xs)-- pairLength xss@(xs:_) = (length xss, xs)- entry (n, xs) = percentage n ntest- ++ " "- ++ concat (List.intersperse ", " xs)-- percentage n m = show ((100 * n) `div` m) ++ "%"----------------------------------------------------------------------------instance Arbitrary Char where- arbitrary = choose ('a', 'i')- coarbitrary c = variant (ord c `rem` 4)--instance Arbitrary Word8 where- arbitrary = choose (97, 105)- coarbitrary c = variant (fromIntegral ((fromIntegral c) `rem` 4))--instance Arbitrary Int64 where- arbitrary = sized $ \n -> choose (-fromIntegral n,fromIntegral n)- coarbitrary n = variant (fromIntegral (if n >= 0 then 2*n else 2*(-n) + 1))--{--instance Arbitrary Char where- arbitrary = choose ('\0', '\255') -- since we have to test words, unlines too- coarbitrary c = variant (ord c `rem` 16)--instance Arbitrary Word8 where- arbitrary = choose (minBound, maxBound)- coarbitrary c = variant (fromIntegral ((fromIntegral c) `rem` 16))--}--instance Random Word8 where- randomR = integralRandomR- random = randomR (minBound,maxBound)--instance Random Int64 where- randomR = integralRandomR- random = randomR (minBound,maxBound)--integralRandomR :: (Integral a, RandomGen g) => (a,a) -> g -> (a,g)-integralRandomR (a,b) g = case randomR (fromIntegral a :: Integer,- fromIntegral b :: Integer) g of- (x,g) -> (fromIntegral x, g)--instance Arbitrary V where- arbitrary = V.pack `fmap` arbitrary- coarbitrary s = coarbitrary (V.unpack s)--instance Arbitrary P.ByteString where- arbitrary = P.pack `fmap` arbitrary- coarbitrary s = coarbitrary (P.unpack s)--------------------------------------------------------------------------------- We're doing two forms of testing here. Firstly, model based testing.--- For our Lazy and strict bytestring types, we have model types:------ i.e. Lazy == Byte--- \\ //--- List ------ That is, the Lazy type can be modeled by functions in both the Byte--- and List type. For each of the 3 models, we have a set of tests that--- check those types match.------ The Model class connects a type and its model type, via a conversion--- function. -------class Model a b where- model :: a -> b -- get the abstract value from a concrete value------- Connecting our Lazy and Strict types to their models. We also check--- the data invariant on Lazy types.------ These instances represent the arrows in the above diagram----instance Model P [W] where model = P.unpack-instance Model P [Char] where model = PC.unpack-instance Model V [W] where model = V.unpack-instance Model V P where model = P.pack . V.unpack---- Types are trivially modeled by themselves-instance Model Bool Bool where model = id-instance Model Int Int where model = id-instance Model Int64 Int64 where model = id-instance Model Int64 Int where model = fromIntegral-instance Model Word8 Word8 where model = id-instance Model Ordering Ordering where model = id-instance Model Char Char where model = id---- More structured types are modeled recursively, using the NatTrans class from Gofer.-class (Functor f, Functor g) => NatTrans f g where- eta :: f a -> g a---- The transformation of the same type is identity-instance NatTrans [] [] where eta = id-instance NatTrans Maybe Maybe where eta = id-instance NatTrans ((->) X) ((->) X) where eta = id-instance NatTrans ((->) W) ((->) W) where eta = id-instance NatTrans ((->) Char) ((->) Char) where eta = id---- We have a transformation of pairs, if the pairs are in Model-instance Model f g => NatTrans ((,) f) ((,) g) where eta (f,a) = (model f, a)---- And finally, we can take any (m a) to (n b), if we can Model m n, and a b-instance (NatTrans m n, Model a b) => Model (m a) (n b) where model x = fmap model (eta x)------------------------------------------------------------------------------ Some short hand.-type X = Int-type W = Word8-type P = P.ByteString-type V = V.Vector Word8-------------------------------------------------------------------------------- These comparison functions handle wrapping and equality.------ A single class for these would be nice, but note that they differe in--- the number of arguments, and those argument types, so we'd need HList--- tricks. See here: http://okmij.org/ftp/Haskell/vararg-fn.lhs-----eq1 f g = \a ->- model (f a) == g (model a)-eq2 f g = \a b ->- model (f a b) == g (model a) (model b)-eq3 f g = \a b c ->- model (f a b c) == g (model a) (model b) (model c)-eq4 f g = \a b c d ->- model (f a b c d) == g (model a) (model b) (model c) (model d)-eq5 f g = \a b c d e ->- model (f a b c d e) == g (model a) (model b) (model c) (model d) (model e)------- And for functions that take non-null input----eqnotnull1 f g = \x -> (not (isNull x)) ==> eq1 f g x-eqnotnull2 f g = \x y -> (not (isNull y)) ==> eq2 f g x y-eqnotnull3 f g = \x y z -> (not (isNull z)) ==> eq3 f g x y z--class IsNull t where isNull :: t -> Bool-instance IsNull P.ByteString where isNull = P.null-instance IsNull V where isNull = V.null
+ tests/Test.hs view
@@ -0,0 +1,212 @@+import qualified Data.StorableVector as V+import qualified Data.ByteString as P+import Test.QuickCheck (Property, quickCheck, )+import Test.Utility+ (V, W, X, P, applyId, applyModel,+ eq0, eq1, eq2, eqnotnull1, eqnotnull2, eqnotnull3, )+import Text.Printf (printf)+++-- * compare Data.StorableVector <=> ByteString++limit :: Int -> Int+limit = flip mod 10000++prop_concatVP :: [V] -> Bool+prop_nullVP :: V -> Bool+prop_reverseVP :: V -> Bool+prop_transposeVP :: [V] -> Bool+prop_groupVP :: V -> Bool+prop_initsVP :: V -> Bool+prop_tailsVP :: V -> Bool+prop_allVP :: (W -> Bool) -> V -> Bool+prop_anyVP :: (W -> Bool) -> V -> Bool+prop_appendVP :: V -> V -> Bool+prop_breakVP :: (W -> Bool) -> V -> Bool+prop_concatMapVP :: (W -> V) -> V -> Bool+prop_consVP :: W -> V -> Bool+prop_countVP :: W -> V -> Bool+prop_dropVP :: X -> V -> Bool+prop_dropWhileVP :: (W -> Bool) -> V -> Bool+prop_filterVP :: (W -> Bool) -> V -> Bool+prop_findVP :: (W -> Bool) -> V -> Bool+prop_findIndexVP :: (W -> Bool) -> V -> Bool+prop_findIndicesVP :: (W -> Bool) -> V -> Bool+prop_isPrefixOfVP :: V -> V -> Bool+prop_mapVP :: (W -> W) -> V -> Bool+prop_replicateVP :: X -> W -> Bool+prop_iterateVP :: X -> (W -> W) -> W -> Bool+prop_snocVP :: V -> W -> Bool+prop_spanVP :: (W -> Bool) -> V -> Bool+prop_splitVP :: W -> V -> Bool+prop_splitAtVP :: X -> V -> Bool+prop_takeVP :: X -> V -> Bool+prop_takeWhileVP :: (W -> Bool) -> V -> Bool+prop_elemVP :: W -> V -> Bool+prop_notElemVP :: W -> V -> Bool+prop_elemIndexVP :: W -> V -> Bool+prop_elemIndicesVP :: W -> V -> Bool+prop_lengthVP :: V -> Bool+prop_headVP :: V -> Property+prop_initVP :: V -> Property+prop_lastVP :: V -> Property+prop_maximumVP :: V -> Property+prop_minimumVP :: V -> Property+prop_tailVP :: V -> Property+prop_foldl1VP :: (W -> W -> W) -> V -> Property+prop_foldl1VP' :: (W -> W -> W) -> V -> Property+prop_foldr1VP :: (W -> W -> W) -> V -> Property+prop_scanlVP :: (W -> W -> W) -> W -> V -> Property+prop_scanrVP :: (W -> W -> W) -> W -> V -> Property+prop_eqVP :: V -> V -> Bool+prop_foldlVP :: (X -> W -> X) -> X -> V -> Bool+prop_foldlVP' :: (X -> W -> X) -> X -> V -> Bool+prop_foldrVP :: (W -> X -> X) -> X -> V -> Bool+prop_mapAccumLVP :: (X -> W -> (X, W)) -> X -> V -> Bool+prop_mapAccumRVP :: (X -> W -> (X, W)) -> X -> V -> Bool+prop_zipWithVP :: (W -> W -> W) -> V -> V -> Bool++prop_concatVP = V.concat `eq1` P.concat+prop_nullVP = V.null `eq1` P.null+prop_reverseVP = V.reverse `eq1` P.reverse+prop_transposeVP = V.transpose `eq1` P.transpose+prop_groupVP = V.group `eq1` P.group+prop_initsVP = V.inits `eq1` P.inits+prop_tailsVP = V.tails `eq1` P.tails+prop_allVP = V.all `eq2` P.all+prop_anyVP = V.any `eq2` P.any+prop_appendVP = V.append `eq2` P.append+prop_breakVP = V.break `eq2` P.break+prop_concatMapVP = V.concatMap `eq2` P.concatMap+prop_consVP = V.cons `eq2` P.cons+prop_countVP = V.count `eq2` P.count+prop_dropVP = V.drop `eq2` P.drop+prop_dropWhileVP = V.dropWhile `eq2` P.dropWhile+prop_filterVP = V.filter `eq2` P.filter+prop_findVP = V.find `eq2` P.find+prop_findIndexVP = V.findIndex `eq2` P.findIndex+prop_findIndicesVP = V.findIndices `eq2` P.findIndices+prop_isPrefixOfVP = V.isPrefixOf `eq2` P.isPrefixOf+prop_mapVP = V.map `eq2` P.map+prop_replicateVP = (\n -> V.replicate n `eq1` P.replicate n) . limit+prop_iterateVP = (\n f -> V.iterateN n f `eq1` P.pack . take n . iterate f) . limit+prop_snocVP = V.snoc `eq2` P.snoc+prop_spanVP = V.span `eq2` P.span+prop_splitVP = V.split `eq2` P.split+prop_splitAtVP = V.splitAt `eq2` P.splitAt+prop_takeVP = V.take `eq2` P.take+prop_takeWhileVP = V.takeWhile `eq2` P.takeWhile+prop_elemVP = V.elem `eq2` P.elem+prop_notElemVP = V.notElem `eq2` P.notElem+prop_elemIndexVP = V.elemIndex `eq2` P.elemIndex+prop_elemIndicesVP = V.elemIndices `eq2` P.elemIndices+prop_lengthVP = V.length `eq1` P.length++prop_headVP = V.head `eqnotnull1` P.head+prop_initVP = V.init `eqnotnull1` P.init+prop_lastVP = V.last `eqnotnull1` P.last+prop_maximumVP = V.maximum `eqnotnull1` P.maximum+prop_minimumVP = V.minimum `eqnotnull1` P.minimum+prop_tailVP = V.tail `eqnotnull1` P.tail+prop_foldl1VP = V.foldl1 `eqnotnull2` P.foldl1+prop_foldl1VP' = V.foldl1' `eqnotnull2` P.foldl1'+prop_foldr1VP = V.foldr1 `eqnotnull2` P.foldr1+prop_scanlVP = V.scanl `eqnotnull3` P.scanl+prop_scanrVP = V.scanr `eqnotnull3` P.scanr++prop_eqVP =+ eq2+ ((==) :: V -> V -> Bool)+ ((==) :: P -> P -> Bool)+prop_foldlVP f b as =+ uncurry eq0+ ((V.foldl, P.foldl) `applyId` f `applyId` b `applyModel` as)+prop_foldlVP' f b as =+ uncurry eq0+ ((V.foldl', P.foldl') `applyId` f `applyId` b `applyModel` as)+prop_foldrVP f b as =+ uncurry eq0+ ((V.foldr, P.foldr) `applyId` f `applyId` b `applyModel` as)+prop_mapAccumLVP f b as =+ uncurry eq0+ ((V.mapAccumL, P.mapAccumL) `applyId` f `applyId` b `applyModel` as)+prop_mapAccumRVP f b as =+ uncurry eq0+ ((V.mapAccumR, P.mapAccumR) `applyId` f `applyId` b `applyModel` as)+prop_zipWithVP f xs ys =+ uncurry eq0+ ((V.zipWith f, \x y -> P.pack (P.zipWith f x y)) `applyModel` xs `applyModel` ys)++prop_unfoldrVP :: Int -> (X -> Maybe (W, X)) -> X -> Bool+prop_unfoldrVP n0 f =+ let n = limit n0+ in eq1+ (fst . V.unfoldrN n f)+ (fst . P.unfoldrN n f)+++vp_tests :: [(String, IO ())]+vp_tests =+ ("all", quickCheck prop_allVP) :+ ("any", quickCheck prop_anyVP) :+ ("append", quickCheck prop_appendVP) :+ ("concat", quickCheck prop_concatVP) :+ ("cons", quickCheck prop_consVP) :+ ("eq", quickCheck prop_eqVP) :+ ("filter", quickCheck prop_filterVP) :+ ("find", quickCheck prop_findVP) :+ ("findIndex", quickCheck prop_findIndexVP) :+ ("findIndices", quickCheck prop_findIndicesVP) :+ ("foldl", quickCheck prop_foldlVP) :+ ("foldl'", quickCheck prop_foldlVP') :+ ("foldl1", quickCheck prop_foldl1VP) :+ ("foldl1'", quickCheck prop_foldl1VP') :+ ("foldr", quickCheck prop_foldrVP) :+ ("foldr1", quickCheck prop_foldr1VP) :+ ("mapAccumL", quickCheck prop_mapAccumLVP) :+ ("mapAccumR", quickCheck prop_mapAccumRVP) :+ ("zipWith", quickCheck prop_zipWithVP) :+ ("unfoldr", quickCheck prop_unfoldrVP) :+ ("head", quickCheck prop_headVP) :+ ("init", quickCheck prop_initVP) :+ ("isPrefixOf", quickCheck prop_isPrefixOfVP) :+ ("last", quickCheck prop_lastVP) :+ ("length", quickCheck prop_lengthVP) :+ ("map", quickCheck prop_mapVP) :+ ("maximum", quickCheck prop_maximumVP) :+ ("minimum", quickCheck prop_minimumVP) :+ ("null", quickCheck prop_nullVP) :+ ("reverse", quickCheck prop_reverseVP) :+ ("snoc", quickCheck prop_snocVP) :+ ("tail", quickCheck prop_tailVP) :+ ("scanl", quickCheck prop_scanlVP) :+ ("scanr", quickCheck prop_scanrVP) :+ ("transpose", quickCheck prop_transposeVP) :+ ("replicate", quickCheck prop_replicateVP) :+ ("iterateN", quickCheck prop_iterateVP) :+ ("take", quickCheck prop_takeVP) :+ ("drop", quickCheck prop_dropVP) :+ ("splitAt", quickCheck prop_splitAtVP) :+ ("takeWhile", quickCheck prop_takeWhileVP) :+ ("dropWhile", quickCheck prop_dropWhileVP) :+ ("break", quickCheck prop_breakVP) :+ ("span", quickCheck prop_spanVP) :+ ("split", quickCheck prop_splitVP) :+ ("count", quickCheck prop_countVP) :+ ("group", quickCheck prop_groupVP) :+ ("inits", quickCheck prop_initsVP) :+ ("tails", quickCheck prop_tailsVP) :+ ("elem", quickCheck prop_elemVP) :+ ("notElem", quickCheck prop_notElemVP) :+ ("elemIndex", quickCheck prop_elemIndexVP) :+ ("elemIndices", quickCheck prop_elemIndicesVP) :+ ("concatMap", quickCheck prop_concatMapVP) :+ []+++main :: IO ()+main = run vp_tests++run :: [(String, IO ())] -> IO ()+run tests = do+ mapM_ (\(s,a) -> printf "%-25s: " s >> a) tests
+ tests/Test/Utility.hs view
@@ -0,0 +1,140 @@+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+module Test.Utility where++import Test.QuickCheck (Property, (==>), )+import Text.Show.Functions ()++import Data.Word (Word8)+import Data.Int (Int64)++import qualified Data.ByteString as P+import qualified Data.StorableVector as V++import qualified Data.ByteString.Char8 as PC+++{- |+Class allows to compare StorableVector implementation+with implementations for similar data structures,+here ByteString and [Word8].+-}+class Model a b where+ model :: a -> b++instance Model P [W] where model = P.unpack+instance Model P [Char] where model = PC.unpack+instance Model V [W] where model = V.unpack+instance Model V P where model = P.pack . V.unpack++instance Model Bool Bool where model = id+instance Model Int Int where model = id+instance Model Int64 Int64 where model = id+instance Model Int64 Int where model = fromIntegral+instance Model Word8 Word8 where model = id+instance Model Ordering Ordering where model = id+instance Model Char Char where model = id++instance (Model x y) => Model (a -> x) (a -> y) where+ model f = model . f++instance (Model x y) => Model (Maybe x) (Maybe y) where+ model = fmap model++instance (Model x y) => Model [x] [y] where+ model = fmap model++instance+ (Model x0 y0, Model x1 y1) =>+ Model (x0,x1) (y0,y1) where+ model (x0,x1) = (model x0, model x1)+++type X = Int+type W = Word8+type P = P.ByteString+type V = V.Vector Word8+++infixl 0 `applyId`, `applyModel`++applyId :: (a -> x, a -> y) -> a -> (x,y)+applyId (f,g) a = (f a, g a)++applyModel :: (Model x0 y0) => (x0 -> x, y0 -> y) -> x0 -> (x,y)+applyModel (f,g) x = (f x, g $ model x)+++{-+These comparison functions handle wrapping and equality automatically.+-}+eq0 ::+ (Model x y, Eq y) =>+ x -> y -> Bool++eq1 ::+ (Model x1 y1, Model x y, Eq y) =>+ (x1 -> x) -> (y1 -> y) -> x1 -> Bool++eq2 ::+ (Model x2 y2, Model x1 y1, Model x y, Eq y) =>+ (x2 -> x1 -> x) -> (y2 -> y1 -> y) -> x2 -> x1 -> Bool++eq3 ::+ (Model x3 y3, Model x2 y2, Model x1 y1, Model x y, Eq y) =>+ (x3 -> x2 -> x1 -> x) -> (y3 -> y2 -> y1 -> y) -> x3 -> x2 -> x1 -> Bool++eq4 ::+ (Model x4 y4, Model x3 y3, Model x2 y2, Model x1 y1, Model x y, Eq y) =>+ (x4 -> x3 -> x2 -> x1 -> x) ->+ (y4 -> y3 -> y2 -> y1 -> y) ->+ x4 -> x3 -> x2 -> x1 -> Bool++eq5 ::+ (Model x5 y5, Model x4 y4, Model x3 y3, Model x2 y2, Model x1 y1, Model x y, Eq y) =>+ (x5 -> x4 -> x3 -> x2 -> x1 -> x) ->+ (y5 -> y4 -> y3 -> y2 -> y1 -> y) ->+ x5 -> x4 -> x3 -> x2 -> x1 -> Bool+++infix 4 `eq1`, `eq2`, `eq3`, `eq4`, `eq5`++eq0 f g =+ model f == g+eq1 f g = \a ->+ model (f a) == g (model a)+eq2 f g = \a b ->+ model (f a b) == g (model a) (model b)+eq3 f g = \a b c ->+ model (f a b c) == g (model a) (model b) (model c)+eq4 f g = \a b c d ->+ model (f a b c d) == g (model a) (model b) (model c) (model d)+eq5 f g = \a b c d e ->+ model (f a b c d e) == g (model a) (model b) (model c) (model d) (model e)+++{-+Handle functions that require non-empty input.+-}+eqnotnull1 ::+ (Model x y, Model x1 y1, IsNull x1, Eq y) =>+ (x1 -> x) -> (y1 -> y) -> x1 -> Property++eqnotnull2 ::+ (Model x y, Model x1 y1, Model x2 y2, IsNull x1, Eq y) =>+ (x2 -> x1 -> x) -> (y2 -> y1 -> y) -> x2 -> x1 -> Property++eqnotnull3 ::+ (Model x y, Model x1 y1, Model x2 y2, Model x3 y3, IsNull x1,+ Eq y) =>+ (x3 -> x2 -> x1 -> x) ->+ (y3 -> y2 -> y1 -> y) ->+ x3 -> x2 -> x1 -> Property++eqnotnull1 f g = \x -> not (isNull x) ==> eq1 f g x+eqnotnull2 f g = \x y -> not (isNull y) ==> eq2 f g x y+eqnotnull3 f g = \x y z -> not (isNull z) ==> eq3 f g x y z++class IsNull t where isNull :: t -> Bool+instance IsNull P.ByteString where isNull = P.null+instance IsNull V where isNull = V.null
− tests/tests.hs
@@ -1,160 +0,0 @@-{-# OPTIONS_GHC -O #-}-import qualified Data.StorableVector as V-import qualified Data.ByteString as P-import QuickCheckUtils- (V, W, X, P, mytest,- eq1, eq2, eq3, eqnotnull1, eqnotnull2, eqnotnull3, )-import Text.Printf (printf)-import System.Environment (getArgs)------- Data.StorableVector <=> ByteString-----prop_concatVP = (V.concat :: [V] -> V) `eq1` P.concat-prop_nullVP = (V.null :: V -> Bool) `eq1` P.null-prop_reverseVP = (V.reverse :: V -> V) `eq1` P.reverse-prop_transposeVP = (V.transpose :: [V] -> [V]) `eq1` P.transpose-prop_groupVP = (V.group :: V -> [V]) `eq1` P.group-prop_initsVP = (V.inits :: V -> [V]) `eq1` P.inits-prop_tailsVP = (V.tails :: V -> [V]) `eq1` P.tails-prop_allVP = (V.all :: (W -> Bool) -> V -> Bool) `eq2` P.all-prop_anyVP = (V.any :: (W -> Bool) -> V -> Bool) `eq2` P.any-prop_appendVP = (V.append :: V -> V -> V) `eq2` P.append-prop_breakVP = (V.break :: (W -> Bool) -> V -> (V, V)) `eq2` P.break-prop_concatMapVP = (V.concatMap :: (W -> V) -> V -> V) `eq2` P.concatMap-prop_consVP = (V.cons :: W -> V -> V) `eq2` P.cons-prop_countVP = (V.count :: W -> V -> X) `eq2` P.count-prop_dropVP = (V.drop :: X -> V -> V) `eq2` P.drop-prop_dropWhileVP = (V.dropWhile :: (W -> Bool) -> V -> V) `eq2` P.dropWhile-prop_filterVP = (V.filter :: (W -> Bool) -> V -> V) `eq2` P.filter-prop_findVP = (V.find :: (W -> Bool) -> V -> Maybe W) `eq2` P.find-prop_findIndexVP = (V.findIndex :: (W -> Bool) -> V -> Maybe X) `eq2` P.findIndex-prop_findIndicesVP = (V.findIndices :: (W -> Bool) -> V -> [X]) `eq2` P.findIndices-prop_isPrefixOfVP = (V.isPrefixOf :: V -> V -> Bool) `eq2` P.isPrefixOf-prop_mapVP = (V.map :: (W -> W) -> V -> V) `eq2` P.map-prop_replicateVP = (V.replicate :: X -> W -> V) `eq2` P.replicate-prop_iterateVP = (V.iterateN :: X -> (W -> W) -> W -> V) `eq3` (\n f -> P.pack . take n . iterate f)-prop_snocVP = (V.snoc :: V -> W -> V) `eq2` P.snoc-prop_spanVP = (V.span :: (W -> Bool) -> V -> (V, V)) `eq2` P.span-prop_splitVP = (V.split :: W -> V -> [V]) `eq2` P.split-prop_splitAtVP = (V.splitAt :: X -> V -> (V, V)) `eq2` P.splitAt-prop_takeVP = (V.take :: X -> V -> V) `eq2` P.take-prop_takeWhileVP = (V.takeWhile :: (W -> Bool) -> V -> V) `eq2` P.takeWhile-prop_elemVP = (V.elem :: W -> V -> Bool) `eq2` P.elem-prop_notElemVP = (V.notElem :: W -> V -> Bool) `eq2` P.notElem-prop_elemIndexVP = (V.elemIndex :: W -> V -> Maybe X) `eq2` P.elemIndex-prop_elemIndicesVP = (V.elemIndices :: W -> V -> [X])`eq2` P.elemIndices-prop_lengthVP = (V.length :: V -> X) `eq1` P.length--prop_headVP = (V.head :: V -> W) `eqnotnull1` P.head-prop_initVP = (V.init :: V -> V) `eqnotnull1` P.init-prop_lastVP = (V.last :: V -> W) `eqnotnull1` P.last-prop_maximumVP = (V.maximum :: V -> W) `eqnotnull1` P.maximum-prop_minimumVP = (V.minimum :: V -> W) `eqnotnull1` P.minimum-prop_tailVP = (V.tail :: V -> V) `eqnotnull1` P.tail-prop_foldl1VP = (V.foldl1 :: (W -> W -> W) -> V -> W) `eqnotnull2` P.foldl1-prop_foldl1VP' = (V.foldl1' :: (W -> W -> W) -> V -> W) `eqnotnull2` P.foldl1'-prop_foldr1VP = (V.foldr1 :: (W -> W -> W) -> V -> W) `eqnotnull2` P.foldr1-prop_scanlVP = (V.scanl :: (W -> W -> W) -> W -> V -> V) `eqnotnull3` P.scanl-prop_scanrVP = (V.scanr :: (W -> W -> W) -> W -> V -> V) `eqnotnull3` P.scanr--prop_eqVP = eq2- ((==) :: V -> V -> Bool)- ((==) :: P -> P -> Bool)-prop_foldlVP = eq3- (V.foldl :: (X -> W -> X) -> X -> V -> X)- (P.foldl :: (X -> W -> X) -> X -> P -> X)-prop_foldlVP' = eq3- (V.foldl' :: (X -> W -> X) -> X -> V -> X)- (P.foldl' :: (X -> W -> X) -> X -> P -> X)-prop_foldrVP = eq3- (V.foldr :: (W -> X -> X) -> X -> V -> X)- (P.foldr :: (W -> X -> X) -> X -> P -> X)-prop_mapAccumLVP = eq3- (V.mapAccumL :: (X -> W -> (X,W)) -> X -> V -> (X, V))- (P.mapAccumL :: (X -> W -> (X,W)) -> X -> P -> (X, P))-prop_mapAccumRVP = eq3- (V.mapAccumR :: (X -> W -> (X,W)) -> X -> V -> (X, V))- (P.mapAccumR :: (X -> W -> (X,W)) -> X -> P -> (X, P))-prop_zipWithVP = eq3- (V.zipWith :: (W -> W -> W) -> V -> V -> V)--- (P.zipWith :: (W -> W -> W) -> P -> P -> P)- (\f x y -> P.pack (P.zipWith f x y) :: P)--prop_unfoldrVP = eq3- ((\n f a -> V.take (fromIntegral n) $- V.unfoldr f a) :: Int -> (X -> Maybe (W,X)) -> X -> V)- ((\n f a -> fst $- P.unfoldrN n f a) :: Int -> (X -> Maybe (W,X)) -> X -> P)----------------------------------------------------------------------------- StorableVector <=> ByteString--vp_tests =- [("all", mytest prop_allVP)- ,("any", mytest prop_anyVP)- ,("append", mytest prop_appendVP)- ,("concat", mytest prop_concatVP)- ,("cons", mytest prop_consVP)- ,("eq", mytest prop_eqVP)- ,("filter", mytest prop_filterVP)- ,("find", mytest prop_findVP)- ,("findIndex", mytest prop_findIndexVP)- ,("findIndices", mytest prop_findIndicesVP)- ,("foldl", mytest prop_foldlVP)- ,("foldl'", mytest prop_foldlVP')- ,("foldl1", mytest prop_foldl1VP)- ,("foldl1'", mytest prop_foldl1VP')- ,("foldr", mytest prop_foldrVP)- ,("foldr1", mytest prop_foldr1VP)- ,("mapAccumL", mytest prop_mapAccumLVP)- ,("mapAccumR", mytest prop_mapAccumRVP)- ,("zipWith", mytest prop_zipWithVP)- -- ,("unfoldr", mytest prop_unfoldrVP)- ,("head", mytest prop_headVP)- ,("init", mytest prop_initVP)- ,("isPrefixOf", mytest prop_isPrefixOfVP)- ,("last", mytest prop_lastVP)- ,("length", mytest prop_lengthVP)- ,("map", mytest prop_mapVP)- ,("maximum ", mytest prop_maximumVP)- ,("minimum" , mytest prop_minimumVP)- ,("null", mytest prop_nullVP)- ,("reverse", mytest prop_reverseVP)- ,("snoc", mytest prop_snocVP)- ,("tail", mytest prop_tailVP)- ,("scanl", mytest prop_scanlVP)- ,("scanr", mytest prop_scanrVP)- ,("transpose", mytest prop_transposeVP)- ,("replicate", mytest prop_replicateVP)- ,("iterateN", mytest prop_iterateVP)- ,("take", mytest prop_takeVP)- ,("drop", mytest prop_dropVP)- ,("splitAt", mytest prop_splitAtVP)- ,("takeWhile", mytest prop_takeWhileVP)- ,("dropWhile", mytest prop_dropWhileVP)- ,("break", mytest prop_breakVP)- ,("span", mytest prop_spanVP)- ,("split", mytest prop_splitVP)- ,("count", mytest prop_countVP)- ,("group", mytest prop_groupVP)- ,("inits", mytest prop_initsVP)- ,("tails", mytest prop_tailsVP)- ,("elem", mytest prop_elemVP)- ,("notElem", mytest prop_notElemVP)- ,("elemIndex", mytest prop_elemIndexVP)- ,("elemIndices", mytest prop_elemIndicesVP)- ,("concatMap", mytest prop_concatMapVP)- ]----------------------------------------------------------------------------- The entry point--main = run vp_tests--run :: [(String, Int -> IO ())] -> IO ()-run tests = do- x <- getArgs- let n = if null x then 100 else read . head $ x- mapM_ (\(s,a) -> printf "%-25s: " s >> a n) tests