packages feed

extended-containers 0.1.0.0 → 0.1.1.0

raw patch · 14 files changed

+495/−352 lines, 14 filesdep +deepseqdep +primitivedep −transformersdep −vectorPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: deepseq, primitive

Dependencies removed: transformers, vector

API changes (from Hackage documentation)

- Data.AMT: instance (a Data.Type.Equality.~ GHC.Types.Char) => Data.String.IsString (Data.AMT.Vector a)
+ Data.AMT: head :: Vector a -> Maybe a
+ Data.AMT: instance (a GHC.Types.~ GHC.Types.Char) => Data.String.IsString (Data.AMT.Vector a)
+ Data.AMT: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Data.AMT.Tree a)
+ Data.AMT: instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (Data.AMT.Vector a)
+ Data.PrioHeap: instance (Control.DeepSeq.NFData k, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.PrioHeap.Pair k a)
+ Data.PrioHeap: instance (Control.DeepSeq.NFData k, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.PrioHeap.PrioHeap k a)
+ Data.PrioHeap: instance (Control.DeepSeq.NFData k, Control.DeepSeq.NFData a) => Control.DeepSeq.NFData (Data.PrioHeap.Tree k a)

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# 0.1.1.0++* Add `Data.AMT.head`+* Add `NFData` instances for `Vector`, `Heap`, `PrioHeap`++* Add `deepseq`, `primitive` dependencies+* Remove `transformers`, `vector` dependencies++# 0.1.0.0++* Initial release
README.md view
@@ -1,9 +1,11 @@ # extended-containers  This package provides container data structures, including heaps and array mapped tries.+For [`lens`](https://hackage.haskell.org/package/lens) instances, see [`extended-containers-lens`](https://hackage.haskell.org/package/extended-containers-lens). +See [`extended-containers` on Hackage](https://hackage.haskell.org/package/extended-containers) for more information.+ ## Plans  * add a `Data.Deque` module * add sorting to `Data.AMT`-* make an `extended-containers-lens` package for [`lens`](https://hackage.haskell.org/package/lens) instances
extended-containers.cabal view
@@ -1,60 +1,66 @@-name:                extended-containers-version:             0.1.0.0-synopsis:            Heap and Vector container types+name:               extended-containers+version:            0.1.1.0+synopsis:           Heap and Vector container types description:   This package contains general-purpose implementations of various immutable container types   including vectors, heaps and priority heaps.-homepage:            https://github.com/konsumlamm/extended-containers#readme-bug-reports:         https://github.com/konsumlamm/extended-containers/issues-license:             BSD3-license-file:        LICENSE-author:              konsumlamm-maintainer:          konsumlamm@gmail.com-copyright:           2019 konsumlamm-build-type:          Simple-extra-source-files:  README.md-category:            Data Structures-cabal-version:       >= 1.10+homepage:           https://github.com/konsumlamm/extended-containers+bug-reports:        https://github.com/konsumlamm/extended-containers/issues+license:            BSD3+license-file:       LICENSE+author:             konsumlamm+maintainer:         konsumlamm@gmail.com+copyright:          2019-2021 konsumlamm+build-type:         Simple+extra-source-files:+  CHANGELOG.md+  README.md+category:           Data Structures+cabal-version:      2.0 tested-with:-  GHC == 8.0.1,   GHC == 8.0.2,   GHC == 8.2.2,-  GHC == 8.4.3,   GHC == 8.4.4,-  GHC == 8.6.3,-  GHC == 8.6.4,   GHC == 8.6.5,-  GHC == 8.8.2,-  GHC == 8.8.3+  GHC == 8.8.3,+  GHC == 8.10.4,+  GHC == 9.0.1  source-repository head   type:     git   location: https://github.com/konsumlamm/extended-containers.git  library-  hs-source-dirs:      src+  hs-source-dirs:       src   exposed-modules:     Data.AMT     Data.Heap     Data.PrioHeap   other-modules:     Data.Heap.Internal+    Util.Internal.Array+    Util.Internal.Indexed     Util.Internal.StrictList-  ghc-options:         -O2 -Wall -Wno-name-shadowing -Wredundant-constraints+  ghc-options:          -O2 -Wall -Wno-name-shadowing -Wredundant-constraints   build-depends:-    base         >= 4.9 && < 5,-    transformers >= 0.5.2 && < 0.6,-    vector       >= 0.11 && < 0.13-  default-language:    Haskell2010+    base       >= 4.9 && < 5,+    deepseq   ^>= 1.4.2,+    primitive ^>= 0.7.1+  default-language:     Haskell2010  test-suite test-  hs-source-dirs:      test-  main-is:             Spec.hs-  type:                exitcode-stdio-1.0-  ghc-options:         -Wall -Wno-orphans+  hs-source-dirs:       test+  main-is:              Spec.hs+  other-modules:+    Data.AMT.Spec+    Data.Heap.Spec+    Data.PrioHeap.Spec+  type:                 exitcode-stdio-1.0+  ghc-options:          -Wall -Wno-orphans -Wno-type-defaults   build-depends:     base                >= 4.9 && < 5,     extended-containers,     hspec               >= 2.2.4 && < 2.8,     QuickCheck          >= 2.8.2 && < 2.15-  default-language:    Haskell2010+  default-language:     Haskell2010+  default-extensions:   ExtendedDefaultRules
src/Data/AMT.hs view
@@ -1,7 +1,5 @@ {-# LANGUAGE CPP #-}-#ifdef __GLASGOW_HASKELL__ {-# LANGUAGE TypeFamilies #-}-#endif  {- | = Finite vectors@@ -13,14 +11,16 @@  This module should be imported qualified, to avoid name clashes with the 'Prelude'. -> import qualified Data.AMT as Vector- == Performance  The worst case running time complexities are given, with /n/ referring the the number of elements in the vector. A 'Vector' is particularly efficient for applications that require a lot of indexing and updates.-All logarithms are base 16, which means that /O(log n)/ behaves like /O(1)/ in practice.+All logarithms are base 16, which means that /O(log n)/ behaves more like /O(1)/ in practice. +For a similar container with efficient concatenation and splitting, but slower indexing and updates,+see [Seq](https://hackage.haskell.org/package/containers/docs/Data-Sequence.html) from the+[containers](https://hackage.haskell.org/package/containers) package.+ == Warning  The length of a 'Vector' must not exceed @'maxBound' :: 'Int'@.@@ -28,7 +28,8 @@  == Implementation -The implementation of 'Vector' uses array mapped tries.+The implementation of 'Vector' uses array mapped tries. For a good explanation,+see [this blog post](https://hypirion.com/musings/understanding-persistent-vector-pt-1). -}  module Data.AMT@@ -40,9 +41,8 @@     , unfoldr, unfoldl, iterateN     , (<|), (|>), (><)     -- * Deconstruction/Subranges-    , viewl-    , viewr-    , last+    , viewl, viewr+    , head, last     , take     -- * Indexing     , lookup, index@@ -76,61 +76,59 @@ import Data.Bits import Data.Foldable (foldl', toList) import Data.Functor.Classes-import Data.Functor.Compose-import Data.Functor.Identity import Data.List.NonEmpty (NonEmpty(..), (!!)) import qualified Data.List.NonEmpty as L import Data.Maybe (fromMaybe) #if !(MIN_VERSION_base(4,11,0)) import Data.Semigroup (Semigroup((<>))) #endif-#ifdef __GLASGOW_HASKELL__ import Data.String (IsString)-#endif import Data.Traversable (mapAccumL)-#ifdef __GLASGOW_HASKELL__ import GHC.Exts (IsList) import qualified GHC.Exts as Exts-#endif-import Prelude hiding ((!!), last, lookup, map, replicate, tail, take, unzip, unzip3, zip, zipWith, zip3, zipWith3)-import qualified Prelude as P+import Prelude hiding ((!!), head, last, lookup, map, replicate, tail, take, unzip, unzip3, zip, zipWith, zip3, zipWith3) import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec) -import Control.Monad.Trans.State.Strict (state, evalState)-import qualified Data.Vector as V-import qualified Data.Vector.Mutable as M+import Control.DeepSeq (NFData(..)) +import qualified Util.Internal.Array as A+import Util.Internal.Indexed (Indexed(..), evalIndexed)+ infixr 5 >< infixr 5 <| infixl 5 |>  data Tree a-    = Internal !(V.Vector (Tree a))-    | Leaf !(V.Vector a)+    = Internal !(A.Array (Tree a)) -- never empty+    | Leaf !(A.Array a)  -- | An array mapped trie. data Vector a     = Empty     | Root-        {-# UNPACK #-} !Int  -- size-        {-# UNPACK #-} !Int  -- offset (number of elements in the tree)-        {-# UNPACK #-} !Int  -- height (of the tree)-        !(Tree a)  -- tree-        !(NonEmpty a)  -- tail (reversed)+        {-# UNPACK #-} !Int  -- ^ size+        {-# UNPACK #-} !Int  -- ^ offset (number of elements in the tree)+        {-# UNPACK #-} !Int  -- ^ height (of the tree)+        !(Tree a)  -- ^ tree+        !(NonEmpty a)  -- ^ tail (reversed) +instance NFData a => NFData (Tree a) where+    rnf (Internal v) = rnf v+    rnf (Leaf v) = rnf v+ errorNegativeLength :: String -> a errorNegativeLength s = error $ "AMT." ++ s ++ ": expected a nonnegative length" --- The number of bits used per level.+-- | The number of bits used per level. bits :: Int bits = 4 {-# INLINE bits #-} --- The maximum size of the tail.+-- | The maximum size of the tail. tailSize :: Int tailSize = 1 `shiftL` bits --- The mask used to extract the index into the array.+-- | The mask used to extract the index into the array. mask :: Int mask = tailSize - 1 @@ -145,47 +143,40 @@     liftReadsPrec rp rl = readsData $ readsUnaryWith (liftReadsPrec rp rl) "fromList" fromList  instance Read a => Read (Vector a) where-#ifdef __GLASGOW_HASKELL__     readPrec = parens $ prec 10 $ do         Ident "fromList" <- lexP         xs <- readPrec         pure (fromList xs)-#else-    readsPrec = readsPrec1-    {-# INLINE readsPrec #-}-#endif  instance Eq1 Vector where     liftEq f v1 v2 = length v1 == length v2 && liftEq f (toList v1) (toList v2)  instance Eq a => Eq (Vector a) where     (==) = eq1-    {-# INLINE (==) #-}  instance Ord1 Vector where     liftCompare f v1 v2 = liftCompare f (toList v1) (toList v2)  instance Ord a => Ord (Vector a) where     compare = compare1-    {-# INLINE compare #-}  instance Semigroup (Vector a) where     (<>) = (><)-    {-# INLINE (<>) #-}  instance Monoid (Vector a) where     mempty = empty-    {-# INLINE mempty #-}      mappend = (<>)-    {-# INLINE mappend #-}  instance Foldable Vector where-    foldr _ acc Empty = acc-    foldr f acc (Root _ _ _ tree tail) = foldrTree tree (foldr f acc (L.reverse tail))+    foldr f acc = go       where+        go Empty = acc+        go (Root _ _ _ tree tail) = foldrTree tree (foldr f acc (L.reverse tail))+         foldrTree (Internal v) acc' = foldr foldrTree acc' v         foldrTree (Leaf v) acc' = foldr f acc' v+    {-# INLINE foldr #-}      null Empty = True     null Root{} = False@@ -197,34 +188,33 @@  instance Functor Vector where     fmap = map-    {-# INLINE fmap #-}  instance Traversable Vector where-    traverse _ Empty = pure Empty-    traverse f (Root s offset h tree tail) =-        Root s offset h <$> traverseTree tree <*> (L.reverse <$> traverse f (L.reverse tail))+    traverse f = go       where+        go Empty = pure empty+        go (Root s offset h tree (x :| tail)) =+            Root s offset h <$> traverseTree tree <*> (flip (:|) <$> traverseReverse tail <*> f x)++        traverseReverse [] = pure []+        traverseReverse (x : xs) = flip (:) <$> traverseReverse xs <*> f x+         traverseTree (Internal v) = Internal <$> traverse traverseTree v         traverseTree (Leaf v) = Leaf <$> traverse f v+    {-# INLINE traverse #-} -#ifdef __GLASGOW_HASKELL__ instance IsList (Vector a) where     type Item (Vector a) = a      fromList = fromList-    {-# INLINE fromList #-}      toList = toList-    {-# INLINE toList #-}  instance a ~ Char => IsString (Vector a) where     fromString = fromList-    {-# INLINE fromString #-}-#endif  instance Applicative Vector where     pure = singleton-    {-# INLINE pure #-}      fs <*> xs = foldl' (\acc f -> acc >< map f xs) empty fs @@ -233,46 +223,41 @@  instance Alternative Vector where     empty = empty-    {-# INLINE empty #-}      (<|>) = (><)-    {-# INLINE (<|>) #-}  instance MonadPlus Vector  instance MonadFail Vector where     fail _ = empty-    {-# INLINE fail #-}  instance MonadZip Vector where     mzip = zip-    {-# INLINE mzip #-}      mzipWith = zipWith-    {-# INLINE mzipWith #-}      munzip = unzip-    {-# INLINE munzip #-} +instance NFData a => NFData (Vector a) where+    rnf Empty = ()+    rnf (Root _ _ _ tree tail) = rnf tree `seq` rnf tail + -- | /O(1)/. The empty vector. -- -- > empty = fromList [] empty :: Vector a empty = Empty-{-# INLINE empty #-}  -- | /O(1)/. A vector with a single element. -- -- > singleton x = fromList [x] singleton :: a -> Vector a-singleton x = Root 1 0 0 (Leaf V.empty) (x :| [])-{-# INLINE singleton #-}+singleton x = Root 1 0 0 (Leaf A.empty) (x :| [])  -- | /O(n * log n)/. Create a new vector from a list. fromList :: [a] -> Vector a fromList = foldl' (|>) empty-{-# INLINE fromList #-}  -- | Create a new vector of the given length from a function. fromFunction :: Int -> (Int -> a) -> Vector a@@ -281,12 +266,14 @@     go i acc         | i < n = go (i + 1) (acc |> f i)         | otherwise = acc-{-# INLINE fromFunction #-}  -- | /O(n * log n)/. @replicate n x@ is a vector consisting of n copies of x. replicate :: Int -> a -> Vector a-replicate n = if n < 0 then errorNegativeLength "replicate" else runIdentity . replicateA n . Identity-{-# INLINE replicate #-}+replicate n x = if n < 0 then errorNegativeLength "replicate" else go 0 empty+  where+    go i acc+        | i < n = go (i + 1) (acc |> x)+        | otherwise = acc  -- | @replicateA@ is an 'Applicative' version of 'replicate'. replicateA :: Applicative f => Int -> f a -> f (Vector a)@@ -295,7 +282,6 @@     go i acc         | i < n = go (i + 1) ((|>) <$> acc <*> x)         | otherwise = acc-{-# INLINE replicateA #-}  -- | /O(n * log n)/. Build a vector from left to right by repeatedly applying a function to a seed value. unfoldr :: (b -> Maybe (a, b)) -> b -> Vector a@@ -317,8 +303,11 @@  -- | Constructs a vector by repeatedly applying a function to a seed value. iterateN :: Int -> (a -> a) -> a -> Vector a-iterateN n f x = if n < 0 then errorNegativeLength "iterateN" else replicateA n (state (\y -> (y, f y))) `evalState` x-{-# INLINE iterateN #-}+iterateN n f x = if n < 0 then errorNegativeLength "iterateN" else go 0 x empty+  where+    go i y acc+        | i < n = go (i + 1) (f y) (acc |> y)+        | otherwise = acc  -- | /O(n * log n)/. Add an element to the left end of the vector. (<|) :: a -> Vector a -> Vector a@@ -326,30 +315,30 @@  -- | /O(n * log n)/. The first element and the vector without the first element or 'Nothing' if the vector is empty. viewl :: Vector a -> Maybe (a, Vector a)-viewl Empty = Nothing-viewl v@Root{} =-    let ls = toList v-    in Just (head ls, fromList $ P.tail ls)+viewl v = case toList v of+    [] -> Nothing+    x : xs -> Just (x, fromList xs)  -- | /O(log n)/. Add an element to the right end of the vector. (|>) :: Vector a -> a -> Vector a Empty |> x = singleton x Root s offset h tree tail |> x     | s .&. mask /= 0 = Root (s + 1) offset h tree (x L.<| tail)-    | offset == 0 = Root (s + 1) s (h + 1) (Leaf $ V.fromList (toList $ L.reverse tail)) (x :| [])-    | offset == 1 `shiftL` (bits * h) = Root (s + 1) s (h + 1) (Internal $ V.fromList [tree, newPath h]) (x :| [])-    | otherwise = Root (s + 1) s h (insertTail (bits * (h - 1)) tree) (x :| [])+    -- tail is full+    | offset == 0 = Root (s + 1) s h (Leaf $ A.fromTail tailSize tail) (x :| [])+    | offset == 1 `shiftL` (bits * (h + 1)) = Root (s + 1) s (h + 1) (Internal $ A.fromList2 tree (newPath h)) (x :| [])+    | otherwise = Root (s + 1) s h (insertTail (bits * h) tree) (x :| [])   where     -- create a new path from the old tail-    newPath 1 = Leaf $ V.fromList (toList $ L.reverse tail)-    newPath h = Internal $ V.singleton (newPath (h - 1))+    newPath 0 = Leaf $ A.fromTail tailSize tail+    newPath h = Internal $ A.singleton (newPath (h - 1))      insertTail sh (Internal v)-        | index < V.length v = Internal $ V.modify (\v -> M.modify v (insertTail (sh - bits)) index) v-        | otherwise = Internal $ V.snoc v (newPath (sh `div` bits))+        | idx < length v = Internal $ A.adjust idx (insertTail (sh - bits)) v+        | otherwise = Internal $ A.snoc v (newPath (sh `div` bits - 1))       where-        index = offset `shiftR` sh .&. mask-    insertTail _ (Leaf _) = Leaf $ V.fromList (toList $ L.reverse tail)+        idx = offset `shiftR` sh .&. mask+    insertTail _ (Leaf _) = Leaf $ A.fromTail tailSize tail  -- | /O(log n)/. The vector without the last element and the last element or 'Nothing' if the vector is empty. viewr :: Vector a -> Maybe (Vector a, a)@@ -357,31 +346,37 @@ viewr (Root s offset h tree (x :| tail))     | not (null tail) = Just (Root (s - 1) offset h tree (L.fromList tail), x)     | s == 1 = Just (Empty, x)-    | s == tailSize + 1 = Just (Root (s - 1) 0 0 (Leaf V.empty) (getTail tree), x)-    | otherwise =-        let sh = bits * (h - 1)-        in Just (normalize $ Root (s - 1) (offset - tailSize) h (unsnocTree sh tree) (getTail tree), x)+    | s == tailSize + 1 = Just (Root (s - 1) 0 0 (Leaf A.empty) (getTail tree), x)+    | otherwise = Just (normalize $ Root (s - 1) (offset - tailSize) h (initTree (bits * h) tree) (getTail tree), x)   where-    index' = offset - tailSize - 1+    idx = offset - tailSize - 1 -    unsnocTree sh (Internal v) =-        let subIndex = index' `shiftR` sh .&. mask-            new = V.take (subIndex + 1) v-        in Internal $ V.modify (\v -> M.modify v (unsnocTree (sh - bits)) subIndex) new-    unsnocTree _ (Leaf v) = Leaf v+    initTree sh (Internal v) =+        let subIndex = idx `shiftR` sh .&. mask+            new = A.take (subIndex + 1) v+        in Internal $ A.adjust subIndex (initTree (sh - bits)) new+    initTree _ (Leaf v) = Leaf v -    getTail (Internal v) = getTail (V.last v)-    getTail (Leaf v) = L.fromList . reverse $ toList v+    getTail (Internal v) = getTail (A.last v)+    getTail (Leaf v) = A.toTail v      normalize (Root s offset h (Internal v) tail)-        | length v == 1 = Root s offset (h - 1) (v V.! 0) tail+        | length v == 1 = Root s offset (h - 1) (A.head v) tail     normalize v = v +-- | /O(log n)/. The first element in the vector or 'Nothing' if the vector is empty.+head :: Vector a -> Maybe a+head Empty = Nothing+head (Root _ 0 _ _ tail) = Just (L.last tail) -- offset 0, all elements are in the tail+head (Root _ _ _ tree _) = Just (headTree tree)+  where+    headTree (Internal v) = headTree (A.head v)+    headTree (Leaf v) = A.head v+ -- | /O(1)/. The last element in the vector or 'Nothing' if the vector is empty. last :: Vector a -> Maybe a last Empty = Nothing last (Root _ _ _ _ (x :| _)) = Just x-{-# INLINE last #-}  -- | /O(log n)/. Take the first n elements of the vector or the vector if n is larger than the length of the vector. -- Returns the empty vector if n is negative.@@ -391,39 +386,38 @@     | n <= 0 = Empty     | n >= s = root     | n > offset = Root n offset h tree (L.fromList $ L.drop (s - n) tail)-    | n <= tailSize = Root n 0 0 (Leaf V.empty) (getTail (bits * (h - 1)) tree)+    | n <= tailSize = Root n 0 0 (Leaf A.empty) (getTail (bits * h) tree)     | otherwise =-        let sh = bits * (h - 1)+        let sh = bits * h         in normalize $ Root n ((n - 1) .&. complement mask) h (takeTree sh tree) (getTail sh tree)  -- n - 1 because if 'n .&. mask == 0', we need to subtract tailSize   where-    -- index of the last element in the new vector-    index = n - 1+    idx = n - 1 -- index of the last element in the new vector -    index' = index - tailSize+    idx' = idx - tailSize      takeTree sh (Internal v) =-        let subIndex = index' `shiftR` sh .&. mask-            new = V.take (subIndex + 1) v-        in Internal $ V.modify (\v -> M.modify v (takeTree (sh - bits)) subIndex) new+        let subIndex = idx' `shiftR` sh .&. mask+            new = A.take (subIndex + 1) v+        in Internal $ A.adjust subIndex (takeTree (sh - bits)) new     takeTree _ (Leaf v) = Leaf v -    getTail sh (Internal v) = getTail (sh - bits) (v V.! (index `shiftR` sh .&. mask))-    getTail _ (Leaf v) = L.fromList . reverse . P.take (index .&. mask + 1) $ toList v+    getTail sh (Internal v) = getTail (sh - bits) (A.index (idx `shiftR` sh .&. mask) v)+    getTail _ (Leaf v) = A.toTail $ A.take (idx .&. mask + 1) v      normalize (Root s offset h (Internal v) tail)-        | length v == 1 = normalize $ Root s offset (h - 1) (v V.! 0) tail+        | length v == 1 = normalize $ Root s offset (h - 1) (A.head v) tail     normalize v = v  -- | /O(log n)/. The element at the index or 'Nothing' if the index is out of range. lookup :: Int -> Vector a -> Maybe a lookup _ Empty = Nothing lookup i (Root s offset h tree tail)-    | i < 0 || i >= s = Nothing-    | i < offset = Just $ lookupTree (bits * (h - 1)) tree+    | i < 0 || i >= s = Nothing  -- index out of range+    | i < offset = Just $ lookupTree (bits * h) tree     | otherwise = Just $ tail !! (s - i - 1)   where-    lookupTree sh (Internal v) = lookupTree (sh - bits) (v V.! (i `shiftR` sh .&. mask))-    lookupTree _ (Leaf v) = v V.! (i .&. mask)+    lookupTree sh (Internal v) = lookupTree (sh - bits) (A.index (i `shiftR` sh .&. mask) v)+    lookupTree _ (Leaf v) = A.index (i .&. mask) v  -- | /O(log n)/. The element at the index. Calls 'error' if the index is out of range. index :: Int -> Vector a -> a@@ -434,7 +428,7 @@ (!?) = flip lookup {-# INLINE (!?) #-} --- | /O(log n)/. Flipped version of 'lookup'.+-- | /O(log n)/. Flipped version of 'index'. (!) :: Vector a -> Int -> a (!) = flip index {-# INLINE (!) #-}@@ -450,16 +444,16 @@ adjust :: Int -> (a -> a) -> Vector a -> Vector a adjust _ _ Empty = Empty adjust i f root@(Root s offset h tree tail)-    | i < 0 || i >= s = root-    | i < offset = Root s offset h (adjustTree (bits * (h - 1)) tree) tail+    | i < 0 || i >= s = root  -- index out of range+    | i < offset = Root s offset h (adjustTree (bits * h) tree) tail     | otherwise = let (l, x : r) = L.splitAt (s - i - 1) tail in Root s offset h tree (L.fromList $ l ++ (f x : r))   where     adjustTree sh (Internal v) =-        let index = i `shiftR` sh .&. mask-        in Internal $ V.modify (\v -> M.modify v (adjustTree (sh - bits)) index) v+        let idx = i `shiftR` sh .&. mask+        in Internal $ A.adjust idx (adjustTree (sh - bits)) v     adjustTree _ (Leaf v) =-        let index = i .&. mask-        in Leaf $ V.modify (\v -> M.modify v f index) v+        let idx = i .&. mask+        in Leaf $ A.adjust idx f v  -- | /O(m * log n)/. Concatenate two vectors. (><) :: Vector a -> Vector a -> Vector a@@ -479,18 +473,26 @@ -- | /O(n)/. Map a function that has access to the index of an element over the vector. mapWithIndex :: (Int -> a -> b) -> Vector a -> Vector b mapWithIndex f = snd . mapAccumL (\i x -> i `seq` (i + 1, f i x)) 0+{-# INLINE mapWithIndex #-}  -- | /O(n)/. Fold the values in the vector, using the given monoid. foldMapWithIndex :: Monoid m => (Int -> a -> m) -> Vector a -> m foldMapWithIndex f = foldrWithIndex (\i -> mappend . f i) mempty+{-# INLINE foldMapWithIndex #-}  -- | /O(n)/. Fold using the given left-associative function that has access to the index of an element. foldlWithIndex :: (b -> Int -> a -> b) -> b -> Vector a -> b-foldlWithIndex f acc v = foldl (\g x i -> i `seq` f (g (i - 1)) i x) (const acc) v (length v - 1)+foldlWithIndex f acc v = foldl f' (const acc) v (length v - 1)+  where+    f' g x i = i `seq` f (g (i - 1)) i x+{-# INLINE foldlWithIndex #-}  -- | /O(n)/. Fold using the given right-associative function that has access to the index of an element. foldrWithIndex :: (Int -> a -> b -> b) -> b -> Vector a -> b-foldrWithIndex f acc v = foldr (\x g i -> i `seq` f i x (g (i + 1))) (const acc) v 0+foldrWithIndex f acc v = foldr f' (const acc) v 0+  where+    f' x g i = i `seq` f i x (g (i + 1))+{-# INLINE foldrWithIndex #-}  -- | /O(n)/. A strict version of 'foldlWithIndex'. -- Each application of the function is evaluated before using the result in the next application.@@ -510,9 +512,10 @@  -- | /O(n)/. Traverse the vector with a function that has access to the index of an element. traverseWithIndex :: Applicative f => (Int -> a -> f b) -> Vector a -> f (Vector b)-traverseWithIndex f v = evalState (getCompose $ traverse (Compose . state . flip f') v) 0+traverseWithIndex f v = evalIndexed (traverse (Indexed . f') v) 0   where-    f' i x = i `seq` (f i x, i + 1)+    f' x i = i `seq` (f i x, i + 1)+{-# INLINE traverseWithIndex #-}  -- | /O(n)/. Pair each element in the vector with its index. indexed :: Vector a -> Vector (Int, a)@@ -545,7 +548,6 @@ -- | /O(n)/. Transforms a vector of pairs into a vector of first components and a vector of second components. unzip :: Vector (a, b) -> (Vector a, Vector b) unzip v = (map fst v, map snd v)-{-# INLINE unzip #-}  -- | /O(n)/. Takes a vector of triples and returns three vectors, analogous to 'unzip'. unzip3 :: Vector (a, b, c) -> (Vector a, Vector b, Vector c)@@ -554,9 +556,7 @@     fst3 (x, _, _) = x     snd3 (_, y, _) = y     trd3 (_, _, z) = z-{-# INLINE unzip3 #-}  -- | /O(n)/. Create a list of index-value pairs from the vector. toIndexedList :: Vector a -> [(Int, a)] toIndexedList = foldrWithIndex (curry (:)) []-{-# INLINE toIndexedList #-}
src/Data/Heap.hs view
@@ -15,9 +15,9 @@  == Implementation -The implementation uses skew binomial heaps, as described in+The implementation uses skew binomial heaps, as described by: -* Chris Okasaki, \"Purely Functional Data Structures\", 1998+* Chris Okasaki, \"Purely Functional Data Structures\", 1998. -}  module Data.Heap
src/Data/Heap/Internal.hs view
@@ -1,7 +1,5 @@ {-# LANGUAGE CPP #-}-#ifdef __GLASGOW_HASKELL__ {-# LANGUAGE TypeFamilies #-}-#endif  module Data.Heap.Internal     ( Heap(..)@@ -53,13 +51,13 @@ #if !(MIN_VERSION_base(4,11,0)) import Data.Semigroup (Semigroup((<>))) #endif-#ifdef __GLASGOW_HASKELL__ import GHC.Exts (IsList) import qualified GHC.Exts as Exts-#endif import Prelude hiding (break, drop, dropWhile, filter, map, reverse, span, splitAt, take, takeWhile) import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec) +import Control.DeepSeq (NFData(..))+ import Util.Internal.StrictList  -- | A skew binomial heap.@@ -79,15 +77,12 @@     , _children :: !(Forest a)     } +instance NFData a => NFData (Tree a) where+    rnf (Node _ x xs c) = rnf x `seq` rnf xs `seq` rnf c+ errorEmpty :: String -> a errorEmpty s = error $ "Heap." ++ s ++ ": empty heap" -instance Functor Tree where-    fmap f (Node r x xs c) = Node r (f x) (fmap f xs) (fmap (fmap f) c)--instance Foldable Tree where-    foldr f acc (Node _ x xs c) = f x (foldr f (foldr (flip (foldr f)) acc c) xs)- link :: Ord a => Tree a -> Tree a -> Tree a link t1@(Node r1 x1 xs1 c1) t2@(Node r2 x2 xs2 c2) = assert (r1 == r2) $     if x1 <= x2@@ -148,17 +143,12 @@  instance Show a => Show (Heap a) where     showsPrec = showsPrec1-    {-# INLINE showsPrec #-}  instance (Ord a, Read a) => Read (Heap a) where-#ifdef __GLASGOW_HASKELL__     readPrec = parens $ prec 10 $ do         Ident "fromList" <- lexP         xs <- readPrec         pure (fromList xs)-#else-    readsPrec = readsData $ readsUnaryWith readList "fromList" fromList-#endif  instance Ord a => Eq (Heap a) where     heap1 == heap2 = size heap1 == size heap2 && toAscList heap1 == toAscList heap2@@ -168,59 +158,63 @@  instance Ord a => Semigroup (Heap a) where     (<>) = union-    {-# INLINE (<>) #-}  instance Ord a => Monoid (Heap a) where     mempty = empty-    {-# INLINE mempty #-}      mappend = (<>)-    {-# INLINE mappend #-}  instance Foldable Heap where-    foldr _ acc Empty = acc-    foldr f acc (Heap _ x forest) = f x (foldr (flip (foldr f)) acc forest)+    foldr f acc = go+      where+        go Empty = acc+        go (Heap _ x forest) = f x (foldr foldTree acc forest) +        foldTree (Node _ x xs c) acc = f x (foldr f (foldr foldTree acc c) xs)+    {-# INLINE foldr #-}++    foldl f acc = go+      where+        go Empty = acc+        go (Heap _ x forest) = foldl foldTree (f acc x) forest++        foldTree acc (Node _ x xs c) = foldl foldTree (foldl f (f acc x) xs) c+    {-# INLINE foldl #-}+     null Empty = True     null Heap{} = False-    {-# INLINE null #-}      length = size-    {-# INLINE length #-}      minimum = findMin-    {-# INLINE minimum #-} -#ifdef __GLASGOW_HASKELL__ instance Ord a => IsList (Heap a) where     type Item (Heap a) = a      fromList = fromList-    {-# INLINE fromList #-}      toList = toList-    {-# INLINE toList #-}-#endif +instance NFData a => NFData (Heap a) where+    rnf Empty = ()+    rnf (Heap _ x forest) = rnf x `seq` rnf forest + -- | /O(1)/. The empty heap. -- -- > empty = fromList [] empty :: Heap a empty = Empty-{-# INLINE empty #-}  -- | /O(1)/. A heap with a single element. -- -- > singleton x = fromList [x] singleton :: a -> Heap a singleton x = Heap 1 x Nil-{-# INLINE singleton #-}  -- | /O(n)/. Create a heap from a list. fromList :: Ord a => [a] -> Heap a fromList = foldl' (flip insert) empty-{-# INLINE fromList #-}  -- | /O(1)/. Insert a new value into the heap. insert :: Ord a => a -> Heap a -> Heap a@@ -242,29 +236,26 @@ -- > unions = foldl union empty unions :: (Foldable f, Ord a) => f (Heap a) -> Heap a unions = foldl' union empty-{-# INLINE unions #-}  -- | /O(n)/. Map a function over the heap. map :: Ord b => (a -> b) -> Heap a -> Heap b map f = fromList . fmap f . toList-{-# INLINE map #-}  -- | /O(n)/, Map an increasing function over the heap. The precondition is not checked. mapMonotonic :: (a -> b) -> Heap a -> Heap b mapMonotonic _ Empty = Empty-mapMonotonic f (Heap s x forest) = Heap s (f x) (fmap (fmap f) forest)-{-# INLINE mapMonotonic #-}+mapMonotonic f (Heap s x forest) = Heap s (f x) (fmap mapTree forest)+  where+    mapTree (Node r x xs c) = Node r (f x) (fmap f xs) (fmap mapTree c)  -- | /O(n)/. Filter all elements that satisfy the predicate. filter :: Ord a => (a -> Bool) -> Heap a -> Heap a filter f = foldl' (\acc x -> if f x then insert x acc else acc) empty-{-# INLINE filter #-}  -- | /O(n)/. Partition the heap into two heaps, one with all elements that satisfy the predicate -- and one with all elements that don't satisfy the predicate. partition :: Ord a => (a -> Bool) -> Heap a -> (Heap a, Heap a) partition f = foldl' (\(h1, h2) x -> if f x then (insert x h1, h2) else (h1, insert x h2)) (empty, empty)-{-# INLINE partition #-}  -- | /O(n * log n)/. Fold the values in the heap in order, using the given monoid. foldMapOrd :: (Ord a, Monoid m) => (a -> m) -> Heap a -> m@@ -277,6 +268,7 @@     go h = case minView h of         Nothing -> acc         Just (x, h') -> f x (go h')+{-# INLINE foldrOrd #-}  -- | /O(n * log n)/. Fold the values in the heap in order, using the given left-associative function. foldlOrd :: Ord a => (b -> a -> b) -> b -> Heap a -> b@@ -285,6 +277,7 @@     go acc h = case minView h of         Nothing -> acc         Just (x, h') -> go (f acc x) h'+{-# INLINE foldlOrd #-}  -- | /O(n * log n)/. A strict version of 'foldrOrd'. -- Each application of the function is evaluated before using the result in the next application.@@ -306,7 +299,6 @@ size :: Heap a -> Int size Empty = 0 size (Heap s _ _) = s-{-# INLINE size #-}  -- | /O(n)/. Is the value a member of the heap? member :: Ord a => a -> Heap a -> Bool@@ -321,34 +313,28 @@  -- | /O(log n)/. The minimal element in the heap. Calls 'error' if the heap is empty. findMin :: Heap a -> a-findMin Empty = error "findMin: empty heap"-findMin (Heap _ x _) = x-{-# INLINE findMin #-}+findMin heap = fromMaybe (errorEmpty "findMin") (lookupMin heap)  -- | /O(log n)/. The minimal element in the heap or 'Nothing' if the heap is empty. lookupMin :: Heap a -> Maybe a lookupMin Empty = Nothing lookupMin (Heap _ x _) = Just $! x-{-# INLINE lookupMin #-}  -- | /O(log n)/. Delete the minimal element. Returns the empty heap if the heap is empty. deleteMin :: Ord a => Heap a -> Heap a deleteMin Empty = Empty deleteMin (Heap s _ f) = fromForest (s - 1) f-{-# INLINE deleteMin #-}  -- | /O(log n)/. Delete and find the minimal element. Calls 'error' if the heap is empty. -- -- > deleteFindMin heap = (findMin heap, deleteMin heap) deleteFindMin :: Ord a => Heap a -> (a, Heap a) deleteFindMin heap = fromMaybe (errorEmpty "deleteFindMin") (minView heap)-{-# INLINE deleteFindMin #-}  -- | /O(log n)/. Retrieves the minimal element of the heap and the heap stripped of that element or 'Nothing' if the heap is empty. minView :: Ord a => Heap a -> Maybe (a, Heap a) minView Empty = Nothing minView (Heap s x f) = Just (x, fromForest (s - 1) f)-{-# INLINE minView #-}  -- | /O(n * log n)/. @take n heap@ takes the @n@ smallest elements of @heap@, in ascending order. --@@ -423,14 +409,11 @@ -- | /O(n * log n)/. Create a descending list from the heap. toAscList :: Ord a => Heap a -> [a] toAscList = foldrOrd (:) []-{-# INLINE toAscList #-}  -- | /O(n * log n)/. Create a descending list from the heap. toDescList :: Ord a => Heap a -> [a] toDescList = foldlOrd (flip (:)) []-{-# INLINE toDescList #-}  -- | /O(n * log n)/. Sort a list using a heap. The sort is unstable. heapsort :: Ord a => [a] -> [a] heapsort = toAscList . fromList-{-# INLINE heapsort #-}
src/Data/PrioHeap.hs view
@@ -1,7 +1,5 @@ {-# LANGUAGE CPP #-}-#ifdef __GLASGOW_HASKELL__ {-# LANGUAGE TypeFamilies #-}-#endif  {- | = Finite priority heaps@@ -20,9 +18,9 @@  == Implementation -The implementation uses skew binomial heaps, as described in+The implementation uses skew binomial heaps, as described by: -* Chris Okasaki, \"Purely Functional Data Structures\", 1998+* Chris Okasaki, \"Purely Functional Data Structures\", 1998. -}  module Data.PrioHeap@@ -85,13 +83,13 @@ #if !(MIN_VERSION_base(4,11,0)) import Data.Semigroup (Semigroup((<>))) #endif-#ifdef __GLASGOW_HASKELL__ import GHC.Exts (IsList) import qualified GHC.Exts as Exts-#endif import Prelude hiding (break, drop, dropWhile, filter, map, reverse, span, splitAt, take, takeWhile, uncurry) import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec) +import Control.DeepSeq (NFData(..))+ import qualified Data.Heap.Internal as Heap import Util.Internal.StrictList @@ -116,6 +114,12 @@     , _children :: !(Forest k a)     } +instance (NFData k, NFData a) => NFData (Pair k a) where+    rnf (Pair k x) = rnf k `seq` rnf x++instance (NFData k, NFData a) => NFData (Tree k a) where+    rnf (Node _ k x xs c) = rnf k `seq` rnf x `seq` rnf xs `seq` rnf c+ errorEmpty :: String -> a errorEmpty s = error $ "PrioHeap." ++ s ++ ": empty heap" @@ -186,11 +190,9 @@  instance Show k => Show1 (PrioHeap k) where     liftShowsPrec = liftShowsPrec2 showsPrec showList-    {-# INLINE liftShowsPrec #-}  instance (Show k, Show a) => Show (PrioHeap k a) where     showsPrec = showsPrec2-    {-# INLINE showsPrec #-}  instance (Ord k, Read k) => Read1 (PrioHeap k) where     liftReadsPrec rp rl = readsData $ readsUnaryWith (liftReadsPrec rp' rl') "fromList" fromList@@ -199,44 +201,33 @@         rl' = liftReadList rp rl  instance (Ord k, Read k, Read a) => Read (PrioHeap k a) where-#ifdef __GLASGOW_HASKELL__     readPrec = parens $ prec 10 $ do         Ident "fromList" <- lexP         xs <- readPrec         pure (fromList xs)-#else-    readsPrec = readsPrec1-    {-# INLINE readPrec #-}-#endif  instance Ord k => Eq1 (PrioHeap k) where     liftEq f heap1 heap2 = size heap1 == size heap2 && liftEq (liftEq f) (toAscList heap1) (toAscList heap2)  instance (Ord k, Eq a) => Eq (PrioHeap k a) where     (==) = eq1-    {-# INLINE (==) #-}  instance Ord k => Ord1 (PrioHeap k) where     liftCompare f heap1 heap2 = liftCompare (liftCompare f) (toAscList heap1) (toAscList heap2)  instance (Ord k, Ord a) => Ord (PrioHeap k a) where     compare = compare1-    {-# INLINE compare #-}  instance Ord k => Semigroup (PrioHeap k a) where     (<>) = union-    {-# INLINE (<>) #-}  instance Ord k => Monoid (PrioHeap k a) where     mempty = empty-    {-# INLINE mempty #-}      mappend = (<>)-    {-# INLINE mappend #-}  instance Functor (PrioHeap k) where     fmap = map-    {-# INLINE fmap #-}  instance Foldable (PrioHeap k) where     foldMap f = foldMapWithKey (const f)@@ -256,45 +247,40 @@      null Empty = True     null Heap{} = False-    {-# INLINE null #-}      length = size-    {-# INLINE length #-}  instance Traversable (PrioHeap k) where     traverse f = traverseWithKey (const f)     {-# INLINE traverse #-} -#ifdef __GLASGOW_HASKELL__ instance Ord k => IsList (PrioHeap k a) where     type Item (PrioHeap k a) = (k, a)      fromList = fromList-    {-# INLINE fromList #-}      toList = toList-    {-# INLINE toList #-}-#endif +instance (NFData k, NFData a) => NFData (PrioHeap k a) where+    rnf Empty = ()+    rnf (Heap _ k x forest) = rnf k `seq` rnf x `seq` rnf forest + -- | /O(1)/. The empty heap. -- -- > empty = fromList [] empty :: PrioHeap k a empty = Empty-{-# INLINE empty #-}  -- | /O(1)/. A heap with a single element. -- -- > singleton x = fromList [x] singleton :: k -> a -> PrioHeap k a singleton k x = Heap 1 k x Nil-{-# INLINE singleton #-}  -- | /O(n * log n)/. Create a heap from a list. fromList :: Ord k => [(k, a)] -> PrioHeap k a fromList = foldl' (\acc (key, x) -> insert key x acc) empty-{-# INLINE fromList #-}  -- | /O(1)/. Insert a new key and value into the heap. insert :: Ord k => k -> a -> PrioHeap k a -> PrioHeap k a@@ -316,7 +302,6 @@ -- > unions = foldl union empty unions :: (Foldable f, Ord k) => f (PrioHeap k a) -> PrioHeap k a unions = foldl' union empty-{-# INLINE unions #-}  -- | /O(n)/. Map a function over the heap. map :: (a -> b) -> PrioHeap k a -> PrioHeap k b@@ -329,14 +314,16 @@ mapWithKey f (Heap s key x forest) = Heap s key (f key x) (fmap mapTree forest)   where     mapTree (Node r key x xs c) = Node r key (f key x) (fmap mapPair xs) (fmap mapTree c)+     mapPair (Pair key x) = Pair key (f key x)-{-# INLINE mapWithKey #-}  -- | /O(n)/. Traverse the heap with a function that has access to the key associated with a value. traverseWithKey :: Applicative f => (k -> a -> f b) -> PrioHeap k a -> f (PrioHeap k b)-traverseWithKey _ Empty = pure Empty-traverseWithKey f (Heap s key x forest) = Heap s key <$> f key x <*> traverse traverseTree forest+traverseWithKey f = go   where+    go Empty = pure Empty+    go (Heap s key x forest) = Heap s key <$> f key x <*> traverse traverseTree forest+     traverseTree (Node r key x xs c) = Node r key <$> f key x <*> traverse traversePair xs <*> traverse traverseTree c     traversePair (Pair key x) = Pair key <$> f key x {-# INLINE traverseWithKey #-}@@ -406,17 +393,23 @@  -- | /O(n)/. Fold the keys and values in the heap, using the given right-associative function. foldrWithKey :: (k -> a -> b -> b) -> b -> PrioHeap k a -> b-foldrWithKey _ acc Empty = acc-foldrWithKey f acc (Heap _ key x forest) = f key x (foldr foldTree acc forest)+foldrWithKey f acc = go   where+    go Empty = acc+    go (Heap _ key x forest) = f key x (foldr foldTree acc forest)+     foldTree (Node _ key x xs c) acc = f key x (foldr (uncurry f) (foldr foldTree acc c) xs)+{-# INLINE foldrWithKey #-}  -- | /O(n)/. Fold the keys and values in the heap, using the given left-associative function. foldlWithKey :: (b -> k -> a -> b) -> b -> PrioHeap k a -> b-foldlWithKey _ acc Empty = acc-foldlWithKey f acc (Heap _ key x forest) = foldl foldTree (f acc key x) forest+foldlWithKey f acc = go   where+    go Empty = acc+    go (Heap _ key x forest) = foldl foldTree (f acc key x) forest+     foldTree acc (Node _ key x xs c) = foldl foldTree (foldl (uncurry . f) (f acc key x) xs) c+{-# INLINE foldlWithKey #-}  -- | /O(n)/. A strict version of 'foldrWithKey'. -- Each application of the function is evaluated before using the result in the next application.@@ -504,7 +497,6 @@ size :: PrioHeap k a -> Int size Empty = 0 size (Heap s _ _ _) = s-{-# INLINE size #-}  -- | /O(n)/. Is the key a member of the heap? member :: Ord k => k -> PrioHeap k a -> Bool@@ -513,7 +505,7 @@   where     kx `elemTree` (Node _ ky _ ys c) = kx <= ky && (any (\(Pair a _) -> kx == a) ys || any (kx `elemTree`) c) --- | /O(n)/. Is the value not a member of the heap?+-- | /O(n)/. Is the key not a member of the heap? notMember :: Ord k => k -> PrioHeap k a -> Bool notMember key = not . member key @@ -531,12 +523,10 @@ lookupMin :: PrioHeap k a -> Maybe (k, a) lookupMin Empty = Nothing lookupMin (Heap _ key x _) = Just (key, x)-{-# INLINE lookupMin #-}  -- | /O(1)/. The minimal element in the heap. Calls 'error' if the heap is empty. findMin :: PrioHeap k a -> (k, a) findMin heap = fromMaybe (errorEmpty "findMin") (lookupMin heap)-{-# INLINE findMin #-}  -- | /O(log n)/. Delete the minimal element. Returns the empty heap if the heap is empty. deleteMin :: Ord k => PrioHeap k a -> PrioHeap k a@@ -548,7 +538,6 @@ -- > deleteFindMin heap = (findMin heap, deleteMin heap) deleteFindMin :: Ord k => PrioHeap k a -> ((k, a), PrioHeap k a) deleteFindMin heap = fromMaybe (errorEmpty "deleteFindMin") (minView heap)-{-# INLINE deleteFindMin #-}  -- | /O(log n)/. Update the value at the minimal key. updateMin :: Ord k => (a -> Maybe a) -> PrioHeap k a -> PrioHeap k a@@ -566,7 +555,6 @@ minView :: Ord k => PrioHeap k a -> Maybe ((k, a), PrioHeap k a) minView Empty = Nothing minView (Heap s key x f) = Just ((key, x), fromForest (s - 1) f)-{-# INLINE minView #-}  -- | /O(n * log n)/. @take n heap@ takes the @n@ smallest elements of @heap@, in ascending order. --
+ src/Util/Internal/Array.hs view
@@ -0,0 +1,79 @@+-- | The functions in this module perform no bounds checking.++module Util.Internal.Array+    ( Array+    , empty+    , singleton+    , snoc+    , index+    , head, last+    , adjust+    , take+    , fromList2+    , fromTail+    , toTail+    ) where++import qualified Data.List.NonEmpty as L+import Prelude hiding (head, last, take)++import Data.Primitive.SmallArray++type Array a = SmallArray a++empty :: Array a+empty = mempty++singleton :: a -> Array a+singleton x = runSmallArray $ newSmallArray 1 x++snoc :: Array a -> a -> Array a+snoc arr x = runSmallArray $ do+    let size = length arr+    arr' <- newSmallArray (size + 1) x+    copySmallArray arr' 0 arr 0 size+    pure arr'++index :: Int -> Array a -> a+index = flip indexSmallArray++head :: Array a -> a+head arr = indexSmallArray arr 0++last :: Array a -> a+last arr = indexSmallArray arr (length arr - 1)++-- | Update the element at the specified index.+adjust :: Int -> (a -> a) -> Array a -> Array a+adjust i f arr = runSmallArray $ do+    arr' <- thawSmallArray arr 0 (length arr)+    let x = indexSmallArray arr i+    writeSmallArray arr' i (f x)+    pure arr'+{-# INLINE adjust #-}++take :: Int -> Array a -> Array a+take n arr = cloneSmallArray arr 0 n++fromList2 :: a -> a -> Array a+fromList2 x y = runSmallArray $ do+    arr <- newSmallArray 2 x+    writeSmallArray arr 1 y+    pure arr++-- | Convert a full tail into an array.+--+-- > fromTail = A.fromListN tailSize . reverse . toList+fromTail :: Int -> L.NonEmpty a -> Array a+fromTail size (x L.:| xs) = runSmallArray $ do+    arr <- newSmallArray size x+    let loop _ [] = pure ()+        loop i (y : ys) = writeSmallArray arr i y *> loop (i - 1) ys+    loop (size - 2) xs+    pure arr++-- | Convert an array into a tail.+--+-- > toTail = L.fromList . reverse . toList+toTail :: Array a -> L.NonEmpty a+toTail = L.fromList . foldl (flip (:)) []
+ src/Util/Internal/Indexed.hs view
@@ -0,0 +1,18 @@+module Util.Internal.Indexed where++-- | > Compose (State Int) f a+newtype Indexed f a = Indexed { runIndexed :: Int -> (f a, Int) }++instance Functor f => Functor (Indexed f) where+    fmap f (Indexed sf) = Indexed $ \s -> let (x, s') = sf s in (fmap f x, s')++instance Applicative f => Applicative (Indexed f) where+    pure x = Indexed $ (,) (pure x)++    Indexed sfa <*> Indexed sfb = Indexed $ \s ->+        let (f, s') = sfa s+            (x, s'') = sfb s'+        in (f <*> x, s'')++evalIndexed :: Indexed f a -> Int -> f a+evalIndexed (Indexed sf) x = fst (sf x)
src/Util/Internal/StrictList.hs view
@@ -1,3 +1,5 @@+-- | Used by "Data.Heap" and "Data.PrioHeap".+ module Util.Internal.StrictList     ( List(..)     , reverse@@ -5,6 +7,8 @@  import Prelude hiding (reverse) +import Control.DeepSeq (NFData(..))+ -- | A strict list. data List a = Nil | !a `Cons` !(List a) @@ -30,6 +34,10 @@         go Nil = pure Nil         go (x `Cons` xs) = Cons <$> f x <*> go xs     {-# INLINE traverse #-}++instance NFData a => NFData (List a) where+    rnf Nil = ()+    rnf (x `Cons` xs) = rnf x `seq` rnf xs  reverse :: List a -> List a reverse = rev Nil
+ test/Data/AMT/Spec.hs view
@@ -0,0 +1,52 @@+module Data.AMT.Spec+    ( spec+    ) where++import Data.Foldable (toList)++import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck++import qualified Data.AMT as V++default (Int)++instance Arbitrary a => Arbitrary (V.Vector a) where+    arbitrary = fmap V.fromList arbitrary++unsnoc :: [a] -> Maybe ([a], a)+unsnoc [] = Nothing+unsnoc ls = Just (init ls, last ls)++(!?) :: [a] -> Int -> Maybe a+ls !? i+    | i < 0 || i >= length ls = Nothing+    | otherwise = Just (ls !! i)++spec :: Spec+spec = describe "Data.AMT" $ do+    prop "satisfies `fromList . toList == id`" $ \v -> V.fromList (toList v) === v+    prop "satisfies `toList . fromList == id`" $ \ls -> toList (V.fromList ls) === ls++    describe "length" $ do+        prop "returns the length" $ \ls -> length (V.fromList ls) === length ls+        it "returns 0 for the empty vector" $ length V.empty `shouldBe` 0++    describe "snoc" $ do+        prop "appends an element to the back" $ \v x -> toList (v V.|> x) === toList v ++ [x]+        prop "works for the empty vector" $ \x -> V.empty V.|> x `shouldBe` V.singleton x++    describe "unsnoc" $ do+        prop "analyzes the back of the vector" $ \v -> V.viewr v === fmap (\(xs, x) -> (V.fromList xs, x)) (unsnoc (toList v))+        it "returns Nothing for the empty vector" $ V.viewr V.empty `shouldBe` Nothing++    describe "take" $ do+        prop "takes the first n elements" $ \n xs -> V.take n (V.fromList xs) === V.fromList (take n xs)+        prop "returns the empty vector for non-positive n" $ \(NonPositive n) v -> V.take n v === V.empty+        prop "does nothing for the empty vector" $ \n -> V.take n V.empty === V.empty++    describe "lookup" $ do+        prop "returns the ith element" $ \i v -> V.lookup i v === toList v !? i+        prop "returns Nothing for negative indices" $ \(Negative i) v -> V.lookup i v === Nothing+        prop "returns Nothing for the empty vector" $ \i -> V.lookup i V.empty === Nothing
+ test/Data/Heap/Spec.hs view
@@ -0,0 +1,47 @@+module Data.Heap.Spec+    ( spec+    ) where++import Data.Bifunctor (bimap)+import Data.Foldable (toList)+import Data.List (partition, sort, uncons)++import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck++import qualified Data.Heap as H++default (Int)++instance (Arbitrary a, Ord a) => Arbitrary (H.Heap a) where+    arbitrary = fmap H.fromList arbitrary++spec :: Spec+spec = describe "Data.Heap" $ do+        prop "satisfies `fromList . toList == id`" $ \h -> H.fromList (toList h) === h++        describe "size" $ do+            prop "returns the size" $ \h -> H.size h === length (toList h)+            it "returns 0 for the empty heap" $ H.size H.empty `shouldBe` 0++        describe "union" $ do+            prop "returns the union of two heaps" $ \xs ys -> H.union (H.fromList xs) (H.fromList ys) === H.fromList (xs ++ ys)+            prop "empty heap is neutral element" $ \h -> H.union h H.empty === h .&&. H.union H.empty h === h++        describe "insert" $ do+            prop "inserts an element" $ \xs x -> H.insert x (H.fromList xs) === H.fromList (x : xs)+            prop "works for the empty heap" $ \x -> H.insert x H.empty === H.singleton x++        describe "deleteMin" $ do+            prop "deletes the minimum element" $ \xs -> H.deleteMin (H.fromList xs) === maybe H.empty (H.fromList . snd) (uncons (sort xs))+            it "works for the empty heap" $ H.deleteMin H.empty `shouldBe` H.empty++        describe "filter" $ do+            prop "filters the elements that satisfy the predicate" $ \xs -> H.filter even (H.fromList xs) === H.fromList (filter even xs)++        describe "partition" $ do+            prop "partitions the elements based on the predicate" $ \xs -> H.partition even (H.fromList xs) === bimap H.fromList H.fromList (partition even xs)++        describe "heapsort" $ do+            prop "sorts a list" $ \ls -> H.heapsort ls === sort ls
+ test/Data/PrioHeap/Spec.hs view
@@ -0,0 +1,47 @@+module Data.PrioHeap.Spec+    ( spec+    ) where++import Data.Bifunctor (bimap)+import Data.Foldable (toList)+import Data.List (partition, sort, uncons)++import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Test.QuickCheck++import qualified Data.PrioHeap as P++default (Int)++instance (Arbitrary k, Arbitrary a, Ord k) => Arbitrary (P.PrioHeap k a) where+    arbitrary = fmap P.fromList arbitrary++fromList :: [(Int, ())] -> P.PrioHeap Int ()+fromList = P.fromList++spec :: Spec+spec = describe "Data.PrioHeap" $ do+        prop "satisfies `fromList . toList == id`" $ \h -> fromList (P.toList h) === h++        describe "size" $ do+            prop "returns the size" $ \h -> P.size h === length (toList h)+            it "returns 0 for the empty heap" $ P.size P.empty `shouldBe` 0++        describe "union" $ do+            prop "returns the union of two heaps" $ \xs ys -> P.union (fromList xs) (fromList ys) === fromList (xs ++ ys)+            prop "empty heap is neutral element" $ \h -> P.union h P.empty === h .&&. P.union P.empty h === h++        describe "insert" $ do+            prop "inserts an element" $ \xs x -> P.insert x () (fromList xs) === fromList ((x, ()) : xs)+            prop "works for the empty heap" $ \k v -> P.insert k v P.empty === P.singleton k v++        describe "deleteMin" $ do+            prop "deletes the minimum element" $ \xs -> P.deleteMin (fromList xs) === maybe P.empty (fromList . snd) (uncons (sort xs))+            it "works for the empty heap" $ P.deleteMin P.empty `shouldBe` P.empty++        describe "filterWithKey" $ do+            prop "filters the elements that satisfy the predicate" $ \xs -> P.filterWithKey (\k () -> even k) (fromList xs) === fromList (filter (even . fst) xs)++        describe "partitionWithKey" $ do+            prop "partitions the elements based on the predicate" $ \xs -> P.partitionWithKey (\k () -> even k) (fromList xs) === bimap fromList fromList (partition (even . fst) xs)
test/Spec.hs view
@@ -1,109 +1,11 @@-{-# LANGUAGE ScopedTypeVariables #-}--import Data.Bifunctor (bimap)-import Data.Foldable (toList)-import Data.List (partition, sort)--import Test.Hspec-import Test.QuickCheck--import Data.AMT (Vector)-import qualified Data.AMT as V-import Data.Heap (Heap)-import qualified Data.Heap as H-import Data.PrioHeap (PrioHeap)-import qualified Data.PrioHeap as P--instance Arbitrary a => Arbitrary (Vector a) where-    arbitrary = fmap V.fromList arbitrary--instance (Arbitrary a, Ord a) => Arbitrary (Heap a) where-    arbitrary = fmap H.fromList arbitrary--instance (Arbitrary k, Arbitrary a, Ord k) => Arbitrary (PrioHeap k a) where-    arbitrary = fmap P.fromList arbitrary--uncons :: [a] -> Maybe (a, [a])-uncons [] = Nothing-uncons (x : xs) = Just (x, xs)+import Test.Hspec (hspec) -unsnoc :: [a] -> Maybe ([a], a)-unsnoc [] = Nothing-unsnoc xs@(_ : _) = Just (init xs, last xs)+import qualified Data.AMT.Spec as AMT+import qualified Data.Heap.Spec as Heap+import qualified Data.PrioHeap.Spec as PrioHeap  main :: IO () main = hspec $ do-    describe "Data.AMT" $ do-        it "satisfies `fromList . toList == id`" $-            property $ \(v :: Vector Int) -> V.fromList (toList v) === v-        it "satisfies `toList . fromList == id`" $-            property $ \(ls :: [Int]) -> toList (V.fromList ls) === ls-        describe "length" $ do-            it "returns the length" $-                property $ \(v :: Vector Int) -> length v === length (toList v)-            it "returns 0 for the empty vector" $-                length V.empty `shouldBe` 0-        describe "snoc" $ do-            it "appends an element to the back" $-                property $ \(v :: Vector Int) x -> toList (v V.|> x) === toList v ++ [x]-            it "works for the empty vector" $-                property $ \(x :: Int) -> V.empty V.|> x `shouldBe` V.singleton x-        describe "unsnoc" $ do-            it "analyzes the back of the vector" $-                property $ \(v :: Vector Int) -> V.viewr v === fmap (\(xs, x) -> (V.fromList xs, x)) (unsnoc (toList v))-            it "returns Nothing for the empty vector" $-                V.viewr V.empty `shouldBe` (Nothing :: Maybe (Vector Int, Int))-        describe "take" $-            it "takes the first n elements" $-                property $ \n (xs :: [Int]) -> V.take n (V.fromList xs) === V.fromList (take n xs)--    describe "Data.Heap" $ do-        it "satisfies `fromList . toList == id`" $-            property $ \(h :: Heap Int) -> H.fromList (toList h) === h-        describe "size" $ do-            it "returns the size" $-                property $ \(h :: Heap Int) -> H.size h === length (toList h)-            it "returns 0 for the empty heap" $-                H.size H.empty `shouldBe` 0-        describe "union" $-            it "returns the union of two heaps" $-                property $ \(xs :: [Int]) (ys :: [Int]) -> H.fromList xs `H.union` H.fromList ys === H.fromList (xs ++ ys)-        describe "insert" $-            it "inserts an element" $-                property $ \(xs :: [Int]) (x :: Int) -> H.insert x (H.fromList xs) === H.fromList (x : xs)-        describe "deleteMin" $-            it "deletes the minimum element" $-                property $ \(xs :: [Int]) -> H.deleteMin (H.fromList xs) === maybe H.empty (H.fromList . snd) (uncons (sort xs))-        describe "filter" $-            it "filters the elements that satisfy the predicate" $-                property $ \(xs :: [Int]) -> H.filter even (H.fromList xs) === H.fromList (filter even xs)-        describe "partition" $-            it "partitions the elements based on the predicate" $-                property $ \(xs :: [Int]) -> H.partition even (H.fromList xs) === bimap H.fromList H.fromList (partition even xs)-        describe "heapsort" $-            it "sorts a list" $-                property $ \(ls :: [Int]) -> H.heapsort ls === sort ls--    describe "Data.PrioHeap" $ do-        it "satisfies `fromList . toList == id`" $-            property $ \(h :: PrioHeap Int ()) -> P.fromList (P.toList h) === h-        describe "size" $ do-            it "returns the size" $-                property $ \(h :: PrioHeap Int Int) -> P.size h === length (toList h)-            it "returns 0 for the empty heap" $-                P.size P.empty `shouldBe` 0-        describe "union" $-            it "returns the union of two heaps" $-                property $ \(xs :: [(Int, ())]) (ys :: [(Int, ())]) -> P.fromList xs `P.union` P.fromList ys === P.fromList (xs ++ ys)-        describe "insert" $-            it "inserts an element" $-                property $ \(xs :: [(Int, ())]) (x :: Int) -> P.insert x () (P.fromList xs) === P.fromList ((x, ()) : xs)-        describe "deleteMin" $-            it "deletes the minimum element" $-                property $ \(xs :: [(Int, ())]) -> P.deleteMin (P.fromList xs) === maybe P.empty (P.fromList . snd) (uncons (sort xs))-        describe "filterWithKey" $-            it "filters the elements that satisfy the predicate" $-                property $ \(xs :: [(Int, ())]) -> P.filterWithKey (const . even) (P.fromList xs) === P.fromList (filter (even . fst) xs)-        describe "partitionWithKey" $-            it "partitions the elements based on the predicate" $-                property $ \(xs :: [(Int, ())]) -> P.partitionWithKey (const . even) (P.fromList xs) === bimap P.fromList P.fromList (partition (even . fst) xs)+    AMT.spec+    Heap.spec+    PrioHeap.spec