primitive-containers (empty) → 0.2.0
raw patch · 29 files changed
+4348/−0 lines, 29 filesdep +QuickCheckdep +basedep +containerssetup-changed
Dependencies added: QuickCheck, base, containers, contiguous, gauge, ghc-prim, primitive, primitive-containers, primitive-sort, quickcheck-classes, random, tasty, tasty-quickcheck
Files
- ChangeLog.md +3/−0
- LICENSE +30/−0
- README.md +1/−0
- Setup.hs +2/−0
- benchmark-gauge/Main.hs +135/−0
- primitive-containers.cabal +88/−0
- src/Data/Concatenation.hs +27/−0
- src/Data/Diet/Map/Internal.hs +395/−0
- src/Data/Diet/Map/Lifted/Lifted.hs +77/−0
- src/Data/Diet/Map/Unboxed/Lifted.hs +79/−0
- src/Data/Diet/Set.hs +27/−0
- src/Data/Diet/Set/Internal.hs +550/−0
- src/Data/Diet/Set/Lifted.hs +114/−0
- src/Data/Diet/Set/Unboxed.hs +120/−0
- src/Data/Diet/Unbounded/Set/Internal.hs +254/−0
- src/Data/Diet/Unbounded/Set/Lifted.hs +46/−0
- src/Data/Map/Internal.hs +414/−0
- src/Data/Map/Lifted/Lifted.hs +185/−0
- src/Data/Map/Subset/Internal.hs +170/−0
- src/Data/Map/Subset/Lifted.hs +39/−0
- src/Data/Map/Unboxed/Lifted.hs +191/−0
- src/Data/Map/Unboxed/Unboxed.hs +206/−0
- src/Data/Map/Unboxed/Unlifted.hs +102/−0
- src/Data/Set/Internal.hs +259/−0
- src/Data/Set/Lifted.hs +56/−0
- src/Data/Set/Lifted/Internal.hs +102/−0
- src/Data/Set/Unboxed.hs +134/−0
- src/Data/Set/Unlifted.hs +74/−0
- test/Main.hs +468/−0
+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for primitive-containers++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Andrew Martin (c) 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Andrew Martin nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,1 @@+# primitive-containers
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ benchmark-gauge/Main.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE BangPatterns #-}++{-# OPTIONS_GHC -O2 #-}++import Gauge.Main+import System.Random (randoms,mkStdGen)+import Data.Foldable (foldMap)+import Data.Maybe (fromMaybe)+import Data.Bool (bool)+import qualified GHC.Exts as E+import qualified Data.Set.Unboxed as DSU+import qualified Data.Set.Lifted as DSL+import qualified Data.Map.Unboxed.Unboxed as DMUU+import qualified Data.Map.Lifted.Lifted as DMLL+import qualified Data.Map.Strict as M+import qualified Data.IntMap.Strict as IM+import qualified Data.Set as S++main :: IO ()+main = defaultMain+ [ bgroup "Map"+ [ bgroup "lookup" + [ bench "primitive-unboxed-unboxed" $ whnf lookupAllUnboxed bigUnboxedMap+ , bench "containers-map" $ whnf lookupAllContainers bigContainersMap+ , bench "containers-intmap" $ whnf lookupAllIntContainers bigContainersIntMap+ ]+ , bgroup "fold"+ [ bench "primitive-unboxed-unboxed" $ whnf (DMUU.foldlWithKey' reduction 0) bigUnboxedMap+ , bench "primitive-lifted-lifted" $ whnf (DMLL.foldlWithKey' reduction 0) bigLiftedMap+ , bench "containers-map" $ whnf (M.foldlWithKey' reduction 0) bigContainersMap+ ]+ ]+ , bgroup "Set"+ [ bgroup "lookup" + [ bench "primitive-unboxed" $ whnf lookupAllSetUnboxed bigUnboxedSet+ , bench "primitive-lifted" $ whnf lookupAllSetLifted bigLiftedSet+ ]+ , bgroup "fold"+ [ bench "primitive-unboxed" $ whnf (DSU.foldl' (+) 0) bigUnboxedSet+ , bench "containers-set" $ whnf (S.foldl' (+) 0) bigContainersSet+ ]+ , bgroup "concat"+ [ bgroup "fold"+ [ bench "20" $ whnf (foldMap DSU.singleton) randomArray20+ , bench "200" $ whnf (foldMap DSU.singleton) randomArray200+ , bench "2000" $ whnf (foldMap DSU.singleton) randomArray2000+ ]+ , bgroup "fromList"+ [ bench "20" $ whnf (E.fromList :: [Word] -> DSU.Set Word) randomArray20+ , bench "200" $ whnf (E.fromList :: [Word] -> DSU.Set Word) randomArray200+ , bench "2000" $ whnf (E.fromList :: [Word] -> DSU.Set Word) randomArray2000+ ]+ , bgroup "fromAscList"+ [ bench "20" $ whnf (E.fromList :: [Word] -> DSU.Set Word) ascArray20+ , bench "200" $ whnf (E.fromList :: [Word] -> DSU.Set Word) ascArray200+ , bench "2000" $ whnf (E.fromList :: [Word] -> DSU.Set Word) ascArray2000+ ]+ ]+ ]+ ]++reduction :: Int -> Int -> Int -> Int+reduction x y z = x + y + z++bigNumber :: Int+bigNumber = 100000++bigContainersSet :: S.Set Int+bigContainersSet = E.fromList (map (\x -> x `mod` (bigNumber * 2)) (take bigNumber (randoms (mkStdGen 75843))))++bigUnboxedSet :: DSU.Set Int+bigUnboxedSet = E.fromList (map (\x -> x `mod` (bigNumber * 2)) (take bigNumber (randoms (mkStdGen 75843))))++bigLiftedSet :: DSL.Set Int+bigLiftedSet = E.fromList (map (\x -> x `mod` (bigNumber * 2)) (take bigNumber (randoms (mkStdGen 75843))))++bigUnboxedMap :: DMUU.Map Int Int+bigUnboxedMap = E.fromList (map (\x -> (x `mod` (bigNumber * 2),x)) (take bigNumber (randoms (mkStdGen 75843))))++bigLiftedMap :: DMLL.Map Int Int+bigLiftedMap = E.fromList (map (\x -> (x `mod` (bigNumber * 2),x)) (take bigNumber (randoms (mkStdGen 75843))))++bigContainersMap :: M.Map Int Int+bigContainersMap = M.fromList (map (\x -> (x `mod` (bigNumber * 2),x)) (take bigNumber (randoms (mkStdGen 75843))))++bigContainersIntMap :: IM.IntMap Int+bigContainersIntMap = IM.fromList (map (\x -> (x `mod` (bigNumber * 2),x)) (take bigNumber (randoms (mkStdGen 75843))))++lookupAllUnboxed :: DMUU.Map Int Int -> Int+lookupAllUnboxed m = go 0 0 where+ go !acc !n = if n < bigNumber+ then go (acc + fromMaybe 0 (DMUU.lookup n m)) (n + 1)+ else acc++lookupAllSetUnboxed :: DSU.Set Int -> Int+lookupAllSetUnboxed m = go 0 0 where+ go !acc !n = if n < bigNumber+ then go (acc + bool 2 3 (DSU.member n m)) (n + 1)+ else acc++lookupAllSetLifted :: DSL.Set Int -> Int+lookupAllSetLifted m = go 0 0 where+ go !acc !n = if n < bigNumber+ then go (acc + bool 2 3 (DSL.member n m)) (n + 1)+ else acc++lookupAllContainers :: M.Map Int Int -> Int+lookupAllContainers m = go 0 0 where+ go !acc !n = if n < bigNumber+ then go (acc + fromMaybe 0 (M.lookup n m)) (n + 1)+ else acc++lookupAllIntContainers :: IM.IntMap Int -> Int+lookupAllIntContainers m = go 0 0 where+ go !acc !n = if n < bigNumber+ then go (acc + fromMaybe 0 (IM.lookup n m)) (n + 1)+ else acc++ascArray20 :: [Word]+ascArray20 = take 20 (enumFrom 0)++ascArray200 :: [Word]+ascArray200 = take 200 (enumFrom 0)++ascArray2000 :: [Word]+ascArray2000 = take 2000 (enumFrom 0)++randomArray20 :: [Word]+randomArray20 = take 20 (randoms (mkStdGen 75843))++randomArray200 :: [Word]+randomArray200 = take 200 (randoms (mkStdGen 75843))++randomArray2000 :: [Word]+randomArray2000 = take 2000 (randoms (mkStdGen 75843))
+ primitive-containers.cabal view
@@ -0,0 +1,88 @@+name: primitive-containers+version: 0.2.0+description: Please see the README on Github at <https://github.com/andrewthad/primitive-containers>+homepage: https://github.com/andrewthad/primitive-containers+bug-reports: https://github.com/andrewthad/primitive-containers/issues+author: Andrew Martin+maintainer: andrew.thaddeus@gmail.com+copyright: 2018 Andrew Martin+license: BSD3+license-file: LICENSE+build-type: Simple+cabal-version: >= 2.0++extra-source-files:+ ChangeLog.md+ README.md++source-repository head+ type: git+ location: https://github.com/andrewthad/primitive-containers++library+ hs-source-dirs:+ src+ build-depends:+ base >=4.9 && <5+ , primitive >= 0.6.4+ , primitive-sort >= 0.1 && < 0.2+ , contiguous >= 0.2 && < 0.3+ exposed-modules:+ Data.Diet.Map.Lifted.Lifted+ Data.Diet.Map.Unboxed.Lifted+ Data.Diet.Set+ Data.Diet.Set.Lifted+ Data.Diet.Set.Unboxed+ Data.Diet.Unbounded.Set.Lifted+ Data.Map.Lifted.Lifted+ Data.Map.Unboxed.Lifted+ Data.Map.Unboxed.Unboxed+ Data.Map.Unboxed.Unlifted+ Data.Set.Lifted+ Data.Set.Unboxed+ Data.Set.Unlifted+ Data.Map.Subset.Lifted+ other-modules:+ Data.Concatenation+ Data.Diet.Map.Internal+ Data.Diet.Set.Internal+ Data.Diet.Unbounded.Set.Internal+ Data.Map.Internal+ Data.Map.Subset.Internal+ Data.Set.Internal+ Data.Set.Lifted.Internal+ ghc-options: -O2 -Wall+ default-language: Haskell2010++test-suite test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ build-depends:+ base+ , QuickCheck+ , containers+ , primitive+ , primitive-containers+ , quickcheck-classes >= 0.4.12+ , tasty+ , tasty-quickcheck+ ghc-options: -Wall -O2+ default-language: Haskell2010++benchmark gauge+ default-language: Haskell2010+ hs-source-dirs:+ benchmark-gauge+ main-is: Main.hs+ type: exitcode-stdio-1.0+ ghc-options: -Wall -O2+ build-depends:+ base >= 4.8 && < 4.12+ , primitive+ , primitive-containers+ , ghc-prim+ , gauge+ , random+ , containers+
+ src/Data/Concatenation.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Data.Concatenation+ ( concatSized+ ) where++import qualified Data.List as L++concatSized :: forall m.+ (m -> Int) -- size function + -> m+ -> (m -> m -> m)+ -> [m]+ -> m+concatSized size empty combine = go [] where+ go :: [m] -> [m] -> m+ go !stack [] = L.foldl' combine empty (L.reverse stack)+ go !stack (x : xs) = if size x > 0+ then go (pushStack x stack) xs+ else go stack xs+ pushStack :: m -> [m] -> [m]+ pushStack x [] = [x]+ pushStack x (s : ss) = if size x >= size s+ then pushStack (combine s x) ss+ else x : s : ss+
+ src/Data/Diet/Map/Internal.hs view
@@ -0,0 +1,395 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE MagicHash #-}++{-# OPTIONS_GHC -O2 -Wall #-}+module Data.Diet.Map.Internal+ ( Map+ , empty+ , singleton+ , map+ , append+ , lookup+ , concat+ , equals+ , showsPrec+ , liftShowsPrec2+ -- list conversion+ , fromListN+ , fromList+ , fromListAppend+ , fromListAppendN+ , toList+ ) where++import Prelude hiding (lookup,showsPrec,concat,map)++import Control.Applicative (liftA2)+import Control.Monad.ST (ST,runST)+import Data.Semigroup (Semigroup)+import Data.Foldable (foldl')+import Text.Show (showListWith)+import Data.Primitive.Contiguous (Contiguous,Element,Mutable)+import qualified Data.List as L+import qualified Data.Semigroup as SG+import qualified Prelude as P+import qualified Data.Primitive.Contiguous as I+import qualified Data.Concatenation as C++-- The key array is twice as long as the value array since+-- everything is stored as a range. Also, figure out how to+-- unpack these two arguments at some point.+data Map karr varr k v = Map !(karr k) !(varr v)++empty :: (Contiguous karr, Contiguous varr) => Map karr varr k v+empty = Map I.empty I.empty++map :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v, Element varr w) => (v -> w) -> Map karr varr k v -> Map karr varr k w+map f (Map k v) = Map k (I.map f v)++equals :: (Contiguous karr, Element karr k, Eq k, Contiguous varr, Element varr v, Eq v) => Map karr varr k v -> Map karr varr k v -> Bool+equals (Map k1 v1) (Map k2 v2) = I.equals k1 k2 && I.equals v1 v2++fromListN :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Eq v) => Int -> [(k,k,v)] -> Map karr varr k v+fromListN = fromListWithN (\_ a -> a)++fromList :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Eq v) => [(k,k,v)] -> Map karr varr k v+fromList = fromListN 1++fromListAppendN :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Semigroup v, Eq v) => Int -> [(k,k,v)] -> Map karr varr k v+fromListAppendN = fromListWithN (SG.<>)++fromListAppend :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Semigroup v, Eq v) => [(k,k,v)] -> Map karr varr k v+fromListAppend = fromListAppendN 1++fromListWithN :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Eq v) => (v -> v -> v) -> Int -> [(k,k,v)] -> Map karr varr k v+fromListWithN combine _ xs =+ concatWith combine (P.map (\(lo,hi,v) -> singleton lo hi v) xs)++concat :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Semigroup v, Eq v) => [Map karr varr k v] -> Map karr varr k v+concat = concatWith (SG.<>)++singleton :: forall karr varr k v. (Contiguous karr, Element karr k,Ord k,Contiguous varr, Element varr v) => k -> k -> v -> Map karr varr k v+singleton !lo !hi !v = if lo <= hi+ then Map+ ( runST $ do+ !(arr :: Mutable karr s k) <- I.new 2+ I.write arr 0 lo+ I.write arr 1 hi+ I.unsafeFreeze arr+ )+ ( runST $ do+ !(arr :: Mutable varr s v) <- I.new 1+ I.write arr 0 v+ I.unsafeFreeze arr+ )+ else empty++lookup :: forall karr varr k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => k -> Map karr varr k v -> Maybe v+lookup a (Map keys vals) = go 0 (I.size vals - 1) where+ go :: Int -> Int -> Maybe v+ go !start !end = if end <= start+ then if end == start+ then + let !valLo = I.index keys (2 * start)+ !valHi = I.index keys (2 * start + 1)+ in if a >= valLo && a <= valHi+ then case I.index# vals start of+ (# v #) -> Just v+ else Nothing+ else Nothing+ else+ let !mid = div (end + start + 1) 2+ !valLo = I.index keys (2 * mid)+ in case P.compare a valLo of+ LT -> go start (mid - 1)+ EQ -> case I.index# vals mid of+ (# v #) -> Just v+ GT -> go mid end+{-# INLINEABLE lookup #-}+++append :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Semigroup v, Eq v) => Map karr varr k v -> Map karr varr k v -> Map karr varr k v+append (Map ksA vsA) (Map ksB vsB) =+ case unionArrWith (SG.<>) ksA vsA ksB vsB of+ (k,v) -> Map k v++appendWith :: (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Eq v) => (v -> v -> v) -> Map karr varr k v -> Map karr varr k v -> Map karr varr k v+appendWith combine (Map ksA vsA) (Map ksB vsB) =+ case unionArrWith combine ksA vsA ksB vsB of+ (k,v) -> Map k v+ + +unionArrWith :: forall karr varr k v. (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Eq v)+ => (v -> v -> v)+ -> karr k -- keys a+ -> varr v -- values a+ -> karr k -- keys b+ -> varr v -- values b+ -> (karr k, varr v)+unionArrWith combine keysA valsA keysB valsB+ | I.size valsA < 1 = (keysB,valsB)+ | I.size valsB < 1 = (keysA,valsA)+ | otherwise = runST action+ where+ action :: forall s. ST s (karr k, varr v)+ action = do+ let !szA = I.size valsA+ !szB = I.size valsB+ !(keysDst :: Mutable karr s k) <- I.new (max szA szB * 8)+ !(valsDst :: Mutable varr s v) <- I.new (max szA szB * 4)+ let writeKeyRange :: Int -> k -> k -> ST s ()+ writeKeyRange !ix !lo !hi = do+ I.write keysDst (2 * ix) lo+ I.write keysDst (2 * ix + 1) hi+ writeDstHiKey :: Int -> k -> ST s ()+ writeDstHiKey !ix !hi = I.write keysDst (2 * ix + 1) hi+ writeDstValue :: Int -> v -> ST s ()+ writeDstValue !ix !v = I.write valsDst ix v+ readDstHiKey :: Int -> ST s k+ readDstHiKey !ix = I.read keysDst (2 * ix + 1)+ readDstVal :: Int -> ST s v+ readDstVal !ix = I.read valsDst ix+ indexLoKeyA :: Int -> k+ indexLoKeyA !ix = I.index keysA (ix * 2)+ indexLoKeyB :: Int -> k+ indexLoKeyB !ix = I.index keysB (ix * 2)+ indexHiKeyA :: Int -> k+ indexHiKeyA !ix = I.index keysA (ix * 2 + 1)+ indexHiKeyB :: Int -> k+ indexHiKeyB !ix = I.index keysB (ix * 2 + 1)+ indexValueA :: Int -> v+ indexValueA !ix = I.index valsA ix+ indexValueB :: Int -> v+ indexValueB !ix = I.index valsB ix+ -- In the go functon, ixDst is always at least one. Similarly,+ -- all key arguments are always greater than minBound.+ let go :: Int -> k -> k -> v -> Int -> k -> k -> v -> Int -> ST s Int+ go !ixA !loA !hiA !valA !ixB !loB !hiB !valB !ixDst = do+ prevHi <- readDstHiKey (ixDst - 1) + prevVal <- readDstVal (ixDst - 1) + case compare loA loB of+ LT -> do+ let (upper,ixA') = if hiA < loB+ then (hiA,ixA + 1)+ else (pred loB,ixA)+ ixDst' <- if pred loA == prevHi && valA == prevVal+ then do+ writeDstHiKey (ixDst - 1) upper+ return ixDst+ else do+ writeKeyRange ixDst loA upper+ writeDstValue ixDst valA+ return (ixDst + 1)+ if ixA' < szA+ then do+ let (loA',hiA') = if hiA < loB+ then (indexLoKeyA ixA',indexHiKeyA ixA')+ else (loB,hiA)+ go ixA' loA' hiA' (indexValueA ixA') ixB loB hiB valB ixDst'+ else copyB ixB loB hiB valB ixDst'+ GT -> do+ let (upper,ixB') = if hiB < loA+ then (hiB,ixB + 1)+ else (pred loA,ixB)+ ixDst' <- if pred loB == prevHi && valB == prevVal+ then do+ writeDstHiKey (ixDst - 1) upper+ return ixDst+ else do+ writeKeyRange ixDst loB upper+ writeDstValue ixDst valB+ return (ixDst + 1)+ if ixB' < szB+ then do+ let (loB',hiB') = if hiB < loA+ then (indexLoKeyB ixB',indexHiKeyB ixB')+ else (loA,hiB)+ go ixA loA hiA valA ixB' loB' hiB' (indexValueB ixB') ixDst'+ else copyA ixA loA hiA valA ixDst'+ EQ -> do+ let valCombination = combine valA valB+ case compare hiA hiB of+ LT -> do+ ixDst' <- if pred loA == prevHi && valCombination == prevVal+ then do+ writeDstHiKey (ixDst - 1) hiA+ return ixDst+ else do+ writeKeyRange ixDst loA hiA+ writeDstValue ixDst valCombination+ return (ixDst + 1)+ let ixA' = ixA + 1+ loB' = succ hiA+ if ixA' < szA+ then go ixA' (indexLoKeyA ixA') (indexHiKeyA ixA') (indexValueA ixA') ixB loB' hiB valB ixDst'+ else copyB ixB loB' hiB valB ixDst'+ GT -> do+ ixDst' <- if pred loB == prevHi && valCombination == prevVal+ then do+ writeDstHiKey (ixDst - 1) hiB+ return ixDst+ else do+ writeKeyRange ixDst loB hiB+ writeDstValue ixDst valCombination+ return (ixDst + 1)+ let ixB' = ixB + 1+ loA' = succ hiB+ if ixB' < szB+ then go ixA loA' hiA valA ixB' (indexLoKeyB ixB') (indexHiKeyB ixB') (indexValueB ixB') ixDst'+ else copyA ixA loA' hiA valA ixDst'+ EQ -> do+ ixDst' <- if pred loB == prevHi && valCombination == prevVal+ then do+ writeDstHiKey (ixDst - 1) hiB+ return ixDst+ else do+ writeKeyRange ixDst loB hiB+ writeDstValue ixDst valCombination+ return (ixDst + 1)+ let ixA' = ixA + 1+ ixB' = ixB + 1+ if ixA' < szA+ then if ixB' < szB+ then go ixA' (indexLoKeyA ixA') (indexHiKeyA ixA') (indexValueA ixA') ixB' (indexLoKeyB ixB') (indexHiKeyB ixB') (indexValueB ixB') ixDst'+ else copyA ixA' (indexLoKeyA ixA') (indexHiKeyA ixA') (indexValueA ixA') ixDst'+ else if ixB' < szB+ then copyB ixB' (indexLoKeyB ixB') (indexHiKeyB ixB') (indexValueB ixB') ixDst'+ else return ixDst'+ copyB :: Int -> k -> k -> v -> Int -> ST s Int+ copyB !ixB !loB !hiB !valB !ixDst = do+ prevHi <- readDstHiKey (ixDst - 1) + prevVal <- readDstVal (ixDst - 1) + ixDst' <- if pred loB == prevHi && valB == prevVal+ then do+ writeDstHiKey (ixDst - 1) hiB+ return ixDst+ else do+ writeKeyRange ixDst loB hiB+ writeDstValue ixDst valB+ return (ixDst + 1)+ let ixB' = ixB + 1+ remaining = szB - ixB'+ I.copy keysDst (ixDst' * 2) keysB (ixB' * 2) (remaining * 2)+ I.copy valsDst ixDst' valsB ixB' remaining+ return (ixDst' + remaining)+ copyA :: Int -> k -> k -> v -> Int -> ST s Int+ copyA !ixA !loA !hiA !valA !ixDst = do+ prevHi <- readDstHiKey (ixDst - 1) + prevVal <- readDstVal (ixDst - 1) + ixDst' <- if pred loA == prevHi && valA == prevVal+ then do+ writeDstHiKey (ixDst - 1) hiA+ return ixDst+ else do+ writeKeyRange ixDst loA hiA+ writeDstValue ixDst valA+ return (ixDst + 1)+ let ixA' = ixA + 1+ remaining = szA - ixA'+ I.copy keysDst (ixDst' * 2) keysA (ixA' * 2) (remaining * 2)+ I.copy valsDst ixDst' valsA ixA' remaining+ return (ixDst' + remaining)+ let !loA0 = indexLoKeyA 0+ !loB0 = indexLoKeyB 0+ !hiA0 = indexHiKeyA 0+ !hiB0 = indexHiKeyB 0+ !valA0 = indexValueA 0+ !valB0 = indexValueB 0+ total <- case compare loA0 loB0 of+ LT -> if hiA0 < loB0+ then do+ writeKeyRange 0 loA0 hiA0+ writeDstValue 0 valA0+ if 1 < szA+ then go 1 (indexLoKeyA 1) (indexHiKeyA 1) (indexValueA 1) 0 loB0 hiB0 valB0 1+ else copyB 0 loB0 hiB0 valB0 1+ else do+ -- here we know that hiA > loA+ let !upperA = pred loB0+ writeKeyRange 0 loA0 upperA+ writeDstValue 0 valA0+ go 0 loB0 hiA0 valA0 0 loB0 hiB0 valB0 1+ EQ -> case compare hiA0 hiB0 of+ LT -> do+ writeKeyRange 0 loA0 hiA0+ writeDstValue 0 (combine valA0 valB0)+ if 1 < szA+ then go 1 (indexLoKeyA 1) (indexHiKeyA 1) (indexValueA 1) 0 (succ hiA0) hiB0 valB0 1+ else copyB 0 (succ hiA0) hiB0 valB0 1+ GT -> do+ writeKeyRange 0 loB0 hiB0+ writeDstValue 0 (combine valA0 valB0)+ if 1 < szB+ then go 0 (succ hiB0) hiA0 valA0 1 (indexLoKeyB 1) (indexHiKeyB 1) (indexValueB 1) 1+ else copyA 0 (succ hiB0) hiA0 valA0 1+ EQ -> do+ writeKeyRange 0 loA0 hiA0+ writeDstValue 0 (combine valA0 valB0)+ if 1 < szA+ then if 1 < szB+ then go 1 (indexLoKeyA 1) (indexHiKeyA 1) (indexValueA 1) 1 (indexLoKeyB 1) (indexHiKeyB 1) (indexValueB 1) 1+ else copyA 1 (indexLoKeyA 1) (indexHiKeyA 1) (indexValueA 1) 1+ else if 1 < szB+ then copyB 1 (indexLoKeyB 1) (indexHiKeyB 1) (indexValueB 1) 1+ else return 1+ GT -> if hiB0 < loA0+ then do+ writeKeyRange 0 loB0 hiB0+ writeDstValue 0 valB0+ if 1 < szB+ then go 0 loA0 hiA0 valA0 1 (indexLoKeyB 1) (indexHiKeyB 1) (indexValueB 1) 1+ else copyA 0 loA0 hiA0 valA0 1+ else do+ let !upperB = pred loA0+ writeKeyRange 0 loB0 upperB+ writeDstValue 0 valB0+ go 0 loA0 hiA0 valA0 0 loA0 hiB0 valB0 1+ !keysFinal <- I.resize keysDst (total * 2)+ !valsFinal <- I.resize valsDst total+ liftA2 (,) (I.unsafeFreeze keysFinal) (I.unsafeFreeze valsFinal)++concatWith :: forall karr varr k v. (Contiguous karr, Element karr k, Ord k, Enum k, Contiguous varr, Element varr v, Eq v)+ => (v -> v -> v)+ -> [Map karr varr k v]+ -> Map karr varr k v+concatWith combine = C.concatSized size empty (appendWith combine)++size :: (Contiguous varr, Element varr v) => Map karr varr k v -> Int+size (Map _ vals) = I.size vals ++toList :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v) => Map karr varr k v -> [(k,k,v)]+toList = foldrWithKey (\lo hi v xs -> (lo,hi,v) : xs) []++foldrWithKey :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v) => (k -> k -> v -> b -> b) -> b -> Map karr varr k v -> b+foldrWithKey f z (Map keys vals) =+ let !sz = I.size vals+ go !i+ | i == sz = z+ | otherwise =+ let !lo = I.index keys (i * 2)+ !hi = I.index keys (i * 2 + 1)+ !v = I.index vals i+ in f lo hi v (go (i + 1))+ in go 0++showsPrec :: (Contiguous karr, Element karr k, Show k, Contiguous varr, Element varr v, Show v) => Int -> Map karr varr k v -> ShowS+showsPrec p xs = showParen (p > 10) $+ showString "fromList " . shows (toList xs)++liftShowsPrec2 :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v) => (Int -> k -> ShowS) -> ([k] -> ShowS) -> (Int -> v -> ShowS) -> ([v] -> ShowS) -> Int -> Map karr varr k v -> ShowS+liftShowsPrec2 showsPrecK _ showsPrecV _ p xs = showParen (p > 10) $+ showString "fromList " . showListWith (\(a,b,c) -> show_tuple [showsPrecK 0 a, showsPrecK 0 b, showsPrecV 0 c]) (toList xs)++-- implementation copied from GHC.Show+show_tuple :: [ShowS] -> ShowS+show_tuple ss = id+ . showChar '('+ . foldr1 (\s r -> s . showChar ',' . r) ss+ . showChar ')'++
+ src/Data/Diet/Map/Lifted/Lifted.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Diet.Map.Lifted.Lifted+ ( Map+ , singleton+ , lookup+ -- * List Conversion+ , fromList+ , fromListAppend+ , fromListN+ , fromListAppendN+ ) where++import Prelude hiding (lookup,map)++import Data.Semigroup (Semigroup)+import Data.Functor.Classes (Show2(..))+import Data.Primitive (Array)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Diet.Map.Internal as I++newtype Map k v = Map (I.Map Array Array k v)++-- | /O(1)/ Create a diet map with a single element.+singleton :: Ord k+ => k -- ^ inclusive lower bound+ -> k -- ^ inclusive upper bound+ -> v -- ^ value+ -> Map k v+singleton lo hi v = Map (I.singleton lo hi v)++-- | /O(log n)/ Lookup the value at a key in the map.+lookup :: Ord k => k -> Map k v -> Maybe v+lookup a (Map s) = I.lookup a s++instance (Show k, Show v) => Show (Map k v) where+ showsPrec p (Map m) = I.showsPrec p m++instance (Eq k, Eq v) => Eq (Map k v) where+ Map x == Map y = I.equals x y++instance (Ord k, Enum k, Semigroup v, Eq v) => Semigroup (Map k v) where+ Map x <> Map y = Map (I.append x y)++instance (Ord k, Enum k, Semigroup v, Eq v) => Monoid (Map k v) where+ mempty = Map I.empty+ mappend = (SG.<>)+ mconcat = Map . I.concat . E.coerce++instance (Ord k, Enum k, Eq v) => E.IsList (Map k v) where+ type Item (Map k v) = (k,k,v)+ fromListN n = Map . I.fromListN n+ fromList = Map . I.fromList+ toList (Map s) = I.toList s++fromList :: (Ord k, Enum k, Eq v) => [(k,k,v)] -> Map k v+fromList = Map . I.fromList++fromListN :: (Ord k, Enum k, Eq v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,k,v)] -- ^ key-value pairs+ -> Map k v+fromListN n = Map . I.fromListN n++fromListAppend :: (Ord k, Enum k, Semigroup v, Eq v) => [(k,k,v)] -> Map k v+fromListAppend = Map . I.fromListAppend++fromListAppendN :: (Ord k, Enum k, Semigroup v, Eq v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,k,v)] -- ^ key-value pairs+ -> Map k v+fromListAppendN n = Map . I.fromListAppendN n
+ src/Data/Diet/Map/Unboxed/Lifted.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Diet.Map.Unboxed.Lifted+ ( Map+ , singleton+ , lookup+ -- * List Conversion+ , fromList+ , fromListAppend+ , fromListN+ , fromListAppendN+ ) where++import Prelude hiding (lookup,map)++import Data.Semigroup (Semigroup)+import Data.Primitive.Types (Prim)+import Data.Functor.Classes (Show2(..))+import Data.Primitive.PrimArray (PrimArray)+import Data.Primitive.Array (Array)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Diet.Map.Internal as I++newtype Map k v = Map (I.Map PrimArray Array k v)++-- | /O(1)/ Create a diet map with a single element.+singleton :: (Prim k,Ord k)+ => k -- ^ inclusive lower bound+ -> k -- ^ inclusive upper bound+ -> v -- ^ value+ -> Map k v+singleton lo hi v = Map (I.singleton lo hi v)++-- | /O(log n)/ Lookup the value at a key in the map.+lookup :: (Prim k, Ord k) => k -> Map k v -> Maybe v+lookup a (Map s) = I.lookup a s++instance (Prim k, Show k, Show v) => Show (Map k v) where+ showsPrec p (Map m) = I.showsPrec p m++instance (Prim k, Eq k, Eq v) => Eq (Map k v) where+ Map x == Map y = I.equals x y++instance (Prim k, Ord k, Enum k, Semigroup v, Eq v) => Semigroup (Map k v) where+ Map x <> Map y = Map (I.append x y)++instance (Prim k, Ord k, Enum k, Semigroup v, Eq v) => Monoid (Map k v) where+ mempty = Map I.empty+ mappend = (SG.<>)+ mconcat = Map . I.concat . E.coerce++instance (Prim k, Ord k, Enum k, Eq v) => E.IsList (Map k v) where+ type Item (Map k v) = (k,k,v)+ fromListN n = Map . I.fromListN n+ fromList = Map . I.fromList+ toList (Map s) = I.toList s++fromList :: (Ord k, Enum k, Prim k, Eq v) => [(k,k,v)] -> Map k v+fromList = Map . I.fromList++fromListN :: (Ord k, Enum k, Prim k, Eq v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,k,v)] -- ^ key-value pairs+ -> Map k v+fromListN n = Map . I.fromListN n++fromListAppend :: (Ord k, Enum k, Prim k, Semigroup v, Eq v) => [(k,k,v)] -> Map k v+fromListAppend = Map . I.fromListAppend++fromListAppendN :: (Ord k, Enum k, Prim k, Semigroup v, Eq v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,k,v)] -- ^ key-value pairs+ -> Map k v+fromListAppendN n = Map . I.fromListAppendN n
+ src/Data/Diet/Set.hs view
@@ -0,0 +1,27 @@+{-|++The modules in this hierarchy implement sets of nonoverlapping,+nonadjacent intervals. In the literature, one such implementation of+these is known as+<http://web.engr.oregonstate.edu/~erwig/diet/ Discrete Interval Encoding Trees>+(DIETs). This implementation is discussed in+<http://web.engr.oregonstate.edu/~erwig/papers/Diet_JFP98.pdf Diets for Fat Sets>,+Martin Erwig. Journal of Functional Programming, Vol. 8, No. 6, 627-632, 1998.+In this package, we use the term diet set to refer to not just that one+implementation but to any set of nonoverlapping, nonadjacent intervals.++These are not the same as interval sets. An interval set preserves+the original intervals that the user inserted into the set. A diet set+will coalesce adjacent or overlapping ranges. For example:++>>> ⦃[2,6]⦄ ⋄ ⦃[1,3]⦄ ⋄ ⦃[8,11]⦄ ⋄ ⦃[12,12]⦄ +⦃[1,6],[8,12]⦄++The implementation in this packages is optimized for reads. Building+a diet set is expensive since the array-backed implementation cannot+do any sharing when it creates a new data structure. However, testing+for membership is @O(log n)@. ++-}++module Data.Diet.Set () where
+ src/Data/Diet/Set/Internal.hs view
@@ -0,0 +1,550 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -O2 -Wall #-}+module Data.Diet.Set.Internal+ ( Set+ , empty+ , singleton+ , append+ , member+ , concat+ , equals+ , showsPrec+ , difference+ , foldr+ , size+ -- unsafe indexing+ , locate+ , slice+ , indexLower+ , indexUpper+ -- splitting+ , aboveExclusive+ , aboveInclusive+ , belowInclusive+ , belowExclusive+ , betweenInclusive+ -- list conversion+ , fromListN+ , fromList+ , toList+ ) where++import Prelude hiding (lookup,showsPrec,concat,map,foldr)++import Control.Monad.ST (ST,runST)+import Data.Primitive.Contiguous (Contiguous,Element,Mutable)+import qualified Data.Foldable as F+import qualified Prelude as P+import qualified Data.Primitive.Contiguous as I+import qualified Data.Concatenation as C++newtype Set arr a = Set (arr a)++empty :: Contiguous arr => Set arr a+empty = Set I.empty++equals :: (Contiguous arr, Element arr a, Eq a) => Set arr a -> Set arr a -> Bool+equals (Set x) (Set y) = I.equals x y++fromListN :: (Contiguous arr, Element arr a, Ord a, Enum a) => Int -> [(a,a)] -> Set arr a+fromListN _ xs = concat (P.map (uncurry singleton) xs)++fromList :: (Contiguous arr, Element arr a, Ord a, Enum a) => [(a,a)] -> Set arr a+fromList = fromListN 1++concat :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => [Set arr a]+ -> Set arr a+concat = C.concatSized size empty append++singleton :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a -- ^ lower inclusive bound+ -> a -- ^ upper inclusive bound+ -> Set arr a+singleton !lo !hi = if lo <= hi+ then uncheckedSingleton lo hi+ else empty++-- precondition: lo must be less than or equal to hi+uncheckedSingleton :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a -- ^ lower inclusive bound+ -> a -- ^ upper inclusive bound+ -> Set arr a+uncheckedSingleton lo hi = runST $ do+ !(arr :: Mutable arr s a) <- I.new 2+ I.write arr 0 lo+ I.write arr 1 hi+ r <- I.unsafeFreeze arr+ return (Set r)++member :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a+ -> Set arr a+ -> Bool+member a (Set arr) = go 0 ((div (I.size arr) 2) - 1) where+ go :: Int -> Int -> Bool+ go !start !end = if end <= start+ then if end == start+ then + let !valLo = I.index arr (2 * start)+ !valHi = I.index arr (2 * start + 1)+ in a >= valLo && a <= valHi+ else False+ else+ let !mid = div (end + start + 1) 2+ !valLo = I.index arr (2 * mid)+ in case P.compare a valLo of+ LT -> go start (mid - 1)+ EQ -> True+ GT -> go mid end+{-# INLINEABLE member #-}++-- This may segfault if given something out of bounds+indexLower :: (Contiguous arr, Element arr a)+ => Int+ -> Set arr a+ -> a +indexLower ix (Set arr) = I.index arr (ix * 2)++-- This may segfault if given something out of bounds+indexUpper :: (Contiguous arr, Element arr a)+ => Int+ -> Set arr a+ -> a +indexUpper ix (Set arr) = I.index arr (ix * 2 + 1)++-- This may segfault if given bad indices. You are allow to give+-- a high index that is one less than the low index though.+slice :: (Contiguous arr, Element arr a)+ => Int -- inclusive low index+ -> Int -- inclusive high index+ -> Set arr a+ -> Set arr a+slice loIx hiIx (Set arr) = Set (I.clone arr (loIx * 2) ((hiIx - loIx + 1) * 2))++-- This is exported for use in Unbounded Diet Sets, but it should+-- be considered an internal function since it provided an index+-- into the set.+-- Right means that the needle was found. The index provided is the+-- index of the range that contains it [0,n). Left means that the needle+-- was not contained by any of the ranges. The index provided is+-- the index of the range to its right [0,n]+locate :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a+ -> Set arr a+ -> Either Int Int+locate a (Set arr) = go 0 ((div (I.size arr) 2) - 1) where+ go :: Int -> Int -> Either Int Int+ go !start !end = if end <= start+ then if end == start+ then + let !valLo = I.index arr (2 * start)+ !valHi = I.index arr (2 * start + 1)+ in if (a >= valLo)+ then if a <= valHi+ then Right start+ else Left (start + 1)+ else Left start + else Left 0+ else+ let !mid = div (end + start + 1) 2+ !valLo = I.index arr (2 * mid)+ in case P.compare a valLo of+ LT -> go start (mid - 1)+ EQ -> Right mid+ GT -> go mid end++betweenInclusive :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a -- ^ inclusive lower bound+ -> a -- ^ inclusive upper bound+ -> Set arr a+ -> Set arr a+betweenInclusive lo hi (Set arr)+ | hi < lo = empty+ | I.size arr > 0 && I.index arr 0 >= lo && I.index arr (I.size arr - 1) <= hi = Set arr+ | otherwise = case locate lo (Set arr) of+ Left ixLo -> case locate hi (Set arr) of+ Left ixHi -> Set (I.clone arr (ixLo * 2) ((ixHi - ixLo) * 2))+ Right ixHi -> runST $ do+ let len = ixHi - ixLo + 1+ res <- I.new (len * 2)+ rightLo <- I.indexM arr (ixHi * 2)+ I.copy res 0 arr (ixLo * 2) (len * 2 - 2)+ I.write res (len * 2 - 2) rightLo+ I.write res (len * 2 - 1) hi+ r <- I.unsafeFreeze res+ return (Set r)+ Right ixLo -> case locate hi (Set arr) of+ Left ixHi -> runST $ do+ let len = ixHi - ixLo+ (res :: Mutable arr s a) <- I.new (len * 2)+ leftHi <- I.indexM arr (ixLo * 2 + 1)+ I.write res 0 lo+ I.write res 1 leftHi+ I.copy res 2 arr (ixLo * 2 + 2) (len * 2 - 2)+ r <- I.unsafeFreeze res+ return (Set r)+ Right ixHi -> if ixLo == ixHi+ then uncheckedSingleton lo hi+ else runST $ do+ let len = ixHi - ixLo + 1+ (res :: Mutable arr s a) <- I.new (len * 2)+ leftHi <- I.indexM arr (ixLo * 2 + 1)+ I.write res 0 lo+ I.write res 1 leftHi+ I.copy res 2 arr (ixLo * 2 + 2) (len * 2 - 4)+ rightLo <- I.indexM arr (ixHi * 2)+ I.write res (len * 2 - 2) rightLo+ I.write res (len * 2 - 1) hi+ r <- I.unsafeFreeze res+ return (Set r)+ ++aboveInclusive :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a -- ^ inclusive lower bound+ -> Set arr a+ -> Set arr a+aboveInclusive x (Set arr) = case locate x (Set arr) of+ Left ix -> if ix == 0+ then Set arr+ else Set (I.clone arr (ix * 2) (I.size arr - ix * 2))+ Right ix ->+ let lo = I.index arr (ix * 2)+ hi = I.index arr (ix * 2 + 1)+ in if lo == x+ then if ix == 0+ then Set arr+ else Set (I.clone arr (ix * 2) (I.size arr - ix * 2))+ else runST $ do+ (result :: Mutable arr s a) <- I.new (I.size arr - ix * 2)+ I.write result 0 x+ I.write result 1 hi+ I.copy result 2 arr ((ix + 1) * 2) (I.size arr - ix * 2 - 2)+ r <- I.unsafeFreeze result+ return (Set r)++aboveExclusive :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => a -- ^ exclusive lower bound+ -> Set arr a+ -> Set arr a+aboveExclusive x (Set arr) = case locate x (Set arr) of+ Left ix -> if ix == 0+ then Set arr+ else Set (I.clone arr (ix * 2) (I.size arr - ix * 2))+ Right ix ->+ let hi = I.index arr (ix * 2 + 1)+ in if hi == x+ then Set (I.clone arr ((ix + 1) * 2) (I.size arr - (ix + 1) * 2))+ else runST $ do+ (result :: Mutable arr s a) <- I.new (I.size arr - ix * 2)+ I.write result 0 (succ x)+ I.write result 1 hi+ I.copy result 2 arr ((ix + 1) * 2) (I.size arr - ix * 2 - 2)+ r <- I.unsafeFreeze result+ return (Set r)+++belowInclusive :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a -- ^ inclusive upper bound+ -> Set arr a+ -> Set arr a+belowInclusive x (Set arr) = case locate x (Set arr) of+ Left ix -> if ix * 2 == I.size arr+ then Set arr+ else Set (I.clone arr 0 (ix * 2))+ Right ix ->+ let lo = I.index arr (ix * 2)+ hi = I.index arr (ix * 2 + 1)+ in if hi == x+ then if ix * 2 == I.size arr - 2+ then Set arr+ else Set (I.clone arr 0 ((ix + 1) * 2))+ else runST $ do+ result <- I.new ((ix + 1) * 2)+ I.copy result 0 arr 0 (ix * 2)+ I.write result (ix * 2) lo+ I.write result (ix * 2 + 1) x+ r <- I.unsafeFreeze result+ return (Set r)++belowExclusive :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => a -- ^ exclusive upper bound+ -> Set arr a+ -> Set arr a+belowExclusive x (Set arr) = case locate x (Set arr) of+ Left ix -> if ix * 2 == I.size arr+ then Set arr+ else Set (I.clone arr 0 (ix * 2))+ Right ix ->+ let lo = I.index arr (ix * 2)+ in if lo == x+ then Set (I.clone arr 0 (ix * 2))+ else runST $ do+ result <- I.new ((ix + 1) * 2)+ I.copy result 0 arr 0 (ix * 2)+ I.write result (ix * 2) lo+ I.write result (ix * 2 + 1) (pred x)+ r <- I.unsafeFreeze result+ return (Set r)++append :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => Set arr a+ -> Set arr a+ -> Set arr a+append (Set keysA) (Set keysB)+ | szA < 1 = Set keysB+ | szB < 1 = Set keysA+ | otherwise = runST action+ where+ !szA = div (I.size keysA) 2+ !szB = div (I.size keysB) 2+ action :: forall s. ST s (Set arr a)+ action = do+ !(keysDst :: Mutable arr s a) <- I.new (max szA szB * 8)+ let writeKeyRange :: Int -> a -> a -> ST s ()+ writeKeyRange !ix !lo !hi = do+ I.write keysDst (2 * ix) lo+ I.write keysDst (2 * ix + 1) hi+ writeDstHiKey :: Int -> a -> ST s ()+ writeDstHiKey !ix !hi = I.write keysDst (2 * ix + 1) hi+ readDstHiKey :: Int -> ST s a+ readDstHiKey !ix = I.read keysDst (2 * ix + 1)+ indexLoKeyA :: Int -> a+ indexLoKeyA !ix = I.index keysA (ix * 2)+ indexLoKeyB :: Int -> a+ indexLoKeyB !ix = I.index keysB (ix * 2)+ indexHiKeyA :: Int -> a+ indexHiKeyA !ix = I.index keysA (ix * 2 + 1)+ indexHiKeyB :: Int -> a+ indexHiKeyB !ix = I.index keysB (ix * 2 + 1)+ -- In the go functon, ixDst is always at least one. Similarly,+ -- all key arguments are always greater than minBound.+ let go :: Int -> a -> a -> Int -> a -> a -> Int -> ST s Int+ go !ixA !loA !hiA !ixB !loB !hiB !ixDst = do+ prevHi <- readDstHiKey (ixDst - 1) + case compare loA loB of+ LT -> do+ let (upper,ixA') = if hiA < loB+ then (hiA,ixA + 1)+ else (pred loB,ixA)+ ixDst' <- if pred loA == prevHi+ then do+ writeDstHiKey (ixDst - 1) upper+ return ixDst+ else do+ writeKeyRange ixDst loA upper+ return (ixDst + 1)+ if ixA' < szA+ then do+ let (loA',hiA') = if hiA < loB+ then (indexLoKeyA ixA',indexHiKeyA ixA')+ else (loB,hiA)+ go ixA' loA' hiA' ixB loB hiB ixDst'+ else copyB ixB loB hiB ixDst'+ GT -> do+ let (upper,ixB') = if hiB < loA+ then (hiB,ixB + 1)+ else (pred loA,ixB)+ ixDst' <- if pred loB == prevHi+ then do+ writeDstHiKey (ixDst - 1) upper+ return ixDst+ else do+ writeKeyRange ixDst loB upper+ return (ixDst + 1)+ if ixB' < szB+ then do+ let (loB',hiB') = if hiB < loA+ then (indexLoKeyB ixB',indexHiKeyB ixB')+ else (loA,hiB)+ go ixA loA hiA ixB' loB' hiB' ixDst'+ else copyA ixA loA hiA ixDst'+ EQ -> do+ case compare hiA hiB of+ LT -> do+ ixDst' <- if pred loA == prevHi+ then do+ writeDstHiKey (ixDst - 1) hiA+ return ixDst+ else do+ writeKeyRange ixDst loA hiA+ return (ixDst + 1)+ let ixA' = ixA + 1+ loB' = succ hiA+ if ixA' < szA+ then go ixA' (indexLoKeyA ixA') (indexHiKeyA ixA') ixB loB' hiB ixDst'+ else copyB ixB loB' hiB ixDst'+ GT -> do+ ixDst' <- if pred loB == prevHi+ then do+ writeDstHiKey (ixDst - 1) hiB+ return ixDst+ else do+ writeKeyRange ixDst loB hiB+ return (ixDst + 1)+ let ixB' = ixB + 1+ loA' = succ hiB+ if ixB' < szB+ then go ixA loA' hiA ixB' (indexLoKeyB ixB') (indexHiKeyB ixB') ixDst'+ else copyA ixA loA' hiA ixDst'+ EQ -> do+ ixDst' <- if pred loB == prevHi+ then do+ writeDstHiKey (ixDst - 1) hiB+ return ixDst+ else do+ writeKeyRange ixDst loB hiB+ return (ixDst + 1)+ let ixA' = ixA + 1+ ixB' = ixB + 1+ if ixA' < szA+ then if ixB' < szB+ then go ixA' (indexLoKeyA ixA') (indexHiKeyA ixA') ixB' (indexLoKeyB ixB') (indexHiKeyB ixB') ixDst'+ else copyA ixA' (indexLoKeyA ixA') (indexHiKeyA ixA') ixDst'+ else if ixB' < szB+ then copyB ixB' (indexLoKeyB ixB') (indexHiKeyB ixB') ixDst'+ else return ixDst'+ copyB :: Int -> a -> a -> Int -> ST s Int+ copyB !ixB !loB !hiB !ixDst = do+ prevHi <- readDstHiKey (ixDst - 1) + ixDst' <- if pred loB == prevHi+ then do+ writeDstHiKey (ixDst - 1) hiB+ return ixDst+ else do+ writeKeyRange ixDst loB hiB+ return (ixDst + 1)+ let ixB' = ixB + 1+ remaining = szB - ixB'+ I.copy keysDst (ixDst' * 2) keysB (ixB' * 2) (remaining * 2)+ return (ixDst' + remaining)+ copyA :: Int -> a -> a -> Int -> ST s Int+ copyA !ixA !loA !hiA !ixDst = do+ prevHi <- readDstHiKey (ixDst - 1) + ixDst' <- if pred loA == prevHi+ then do+ writeDstHiKey (ixDst - 1) hiA+ return ixDst+ else do+ writeKeyRange ixDst loA hiA+ return (ixDst + 1)+ let ixA' = ixA + 1+ remaining = szA - ixA'+ I.copy keysDst (ixDst' * 2) keysA (ixA' * 2) (remaining * 2)+ return (ixDst' + remaining)+ let !loA0 = indexLoKeyA 0+ !loB0 = indexLoKeyB 0+ !hiA0 = indexHiKeyA 0+ !hiB0 = indexHiKeyB 0+ total <- case compare loA0 loB0 of+ LT -> if hiA0 < loB0+ then do+ writeKeyRange 0 loA0 hiA0+ if 1 < szA+ then go 1 (indexLoKeyA 1) (indexHiKeyA 1) 0 loB0 hiB0 1+ else copyB 0 loB0 hiB0 1+ else do+ -- here we know that hiA > loA+ let !upperA = pred loB0+ writeKeyRange 0 loA0 upperA+ go 0 loB0 hiA0 0 loB0 hiB0 1+ EQ -> case compare hiA0 hiB0 of+ LT -> do+ writeKeyRange 0 loA0 hiA0+ if 1 < szA+ then go 1 (indexLoKeyA 1) (indexHiKeyA 1) 0 (succ hiA0) hiB0 1+ else copyB 0 (succ hiA0) hiB0 1+ GT -> do+ writeKeyRange 0 loB0 hiB0+ if 1 < szB+ then go 0 (succ hiB0) hiA0 1 (indexLoKeyB 1) (indexHiKeyB 1) 1+ else copyA 0 (succ hiB0) hiA0 1+ EQ -> do+ writeKeyRange 0 loA0 hiA0+ if 1 < szA+ then if 1 < szB+ then go 1 (indexLoKeyA 1) (indexHiKeyA 1) 1 (indexLoKeyB 1) (indexHiKeyB 1) 1+ else copyA 1 (indexLoKeyA 1) (indexHiKeyA 1) 1+ else if 1 < szB+ then copyB 1 (indexLoKeyB 1) (indexHiKeyB 1) 1+ else return 1+ GT -> if hiB0 < loA0+ then do+ writeKeyRange 0 loB0 hiB0+ if 1 < szB+ then go 0 loA0 hiA0 1 (indexLoKeyB 1) (indexHiKeyB 1) 1+ else copyA 0 loA0 hiA0 1+ else do+ let !upperB = pred loA0+ writeKeyRange 0 loB0 upperB+ go 0 loA0 hiA0 0 loA0 hiB0 1+ !keysFinal <- I.resize keysDst (total * 2)+ fmap Set (I.unsafeFreeze keysFinal)++difference :: forall a arr. (Contiguous arr, Element arr a, Ord a, Enum a)+ => Set arr a+ -> Set arr a+ -> Set arr a+difference setA@(Set arrA) setB@(Set arrB)+ | szA == 0 = empty+ | szB == 0 = setA+ | otherwise =+ let inners :: Int -> [Set arr a]+ inners !ix = if ix < szB - 1+ then+ let inner = betweenInclusive+ (succ (I.index arrB (2 * ix + 1)))+ (pred (I.index arrB (2 * ix + 2)))+ (Set arrA)+ in inner : inners (ix + 1) + else []+ lowestA = I.index arrA 0+ highestA = I.index arrA (szA * 2 - 1)+ lowestB = I.index arrB 0+ highestB = I.index arrB (szB * 2 - 1)+ -- TODO: if we ever add exclusive variants of below+ -- and above, we should switch to using them here.+ lowFragment = if lowestA < lowestB+ then [belowInclusive (pred lowestB) (Set arrA)]+ else []+ highFragment = if highestA > highestB+ then [aboveInclusive (succ highestB) (Set arrA)]+ else []+ -- we should use a more efficient concat since+ -- we know everything is ordered.+ in concat (lowFragment ++ inners 0 ++ highFragment)+ where+ !szA = size setA+ !szB = size setB++size :: (Contiguous arr, Element arr a) => Set arr a -> Int+size (Set arr) = quot (I.size arr) 2++toList :: (Contiguous arr, Element arr a) => Set arr a -> [(a,a)]+toList = foldr (\lo hi xs -> (lo,hi) : xs) []++foldr :: (Contiguous arr, Element arr a) => (a -> a -> b -> b) -> b -> Set arr a -> b+foldr f z (Set arr) =+ let !sz = div (I.size arr) 2+ go !i+ | i == sz = z+ | otherwise =+ let !lo = I.index arr (i * 2)+ !hi = I.index arr (i * 2 + 1)+ in f lo hi (go (i + 1))+ in go 0+{-# INLINABLE foldr #-}++showsPrec :: (Contiguous arr, Element arr a, Show a)+ => Int+ -> Set arr a+ -> ShowS+showsPrec p xs = showParen (p > 10) $+ showString "fromList " . shows (toList xs)+
+ src/Data/Diet/Set/Lifted.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Diet.Set.Lifted+ ( Set+ , singleton+ , member+ , difference+ -- * Split+ , aboveInclusive+ , belowInclusive+ , betweenInclusive+ -- * Folds+ , foldr+ -- * List Conversion+ , fromList+ , fromListN+ ) where++import Prelude hiding (lookup,map,foldr)++import Data.Semigroup (Semigroup)+import Data.Primitive (Array)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Diet.Set.Internal as I++newtype Set a = Set (I.Set Array a)++-- | /O(1)/ Create a diet set with a single element.+singleton :: Ord a+ => a -- ^ inclusive lower bound+ -> a -- ^ inclusive upper bound+ -> Set a+singleton lo hi = Set (I.singleton lo hi)++-- | /O(log n)/ Returns @True@ if the element is a member of the diet set.+member :: Ord a => a -> Set a -> Bool+member a (Set s) = I.member a s++instance Show a => Show (Set a) where+ showsPrec p (Set s) = I.showsPrec p s++instance Eq a => Eq (Set a) where+ Set x == Set y = I.equals x y++instance Ord a => Ord (Set a) where+ compare (Set xs) (Set ys) = compare (I.toList xs) (I.toList ys)++instance (Ord a, Enum a) => Semigroup (Set a) where+ Set x <> Set y = Set (I.append x y)++instance (Ord a, Enum a) => Monoid (Set a) where+ mempty = Set I.empty+ mappend = (SG.<>)+ mconcat = Set . I.concat . E.coerce++instance (Ord a, Enum a) => E.IsList (Set a) where+ type Item (Set a) = (a,a)+ fromListN n = Set . I.fromListN n+ fromList = Set . I.fromList+ toList (Set s) = I.toList s++fromList :: (Ord a, Enum a) => [(a,a)] -> Set a+fromList = Set . I.fromList++fromListN :: (Ord a, Enum a)+ => Int -- ^ expected size of resulting diet 'Set'+ -> [(a,a)] -- ^ key-value pairs+ -> Set a+fromListN n = Set . I.fromListN n++-- | /O(n + m*log n)/ Subtract the subtrahend of size @m@ from the+-- minuend of size @n@. It should be possible to improve the improve+-- the performance of this to /O(n + m)/. Anyone interested in doing+-- this should open a PR.+difference :: (Ord a, Enum a)+ => Set a -- ^ minuend+ -> Set a -- ^ subtrahend+ -> Set a+difference (Set x) (Set y) = Set (I.difference x y)++foldr :: (a -> a -> b -> b) -> b -> Set a -> b+foldr f z (Set arr) = I.foldr f z arr++-- | /O(n)/ The subset where all elements are greater than+-- or equal to the given value. +aboveInclusive :: (Ord a)+ => a -- ^ inclusive lower bound+ -> Set a+ -> Set a+aboveInclusive x (Set s) = Set (I.aboveInclusive x s)++-- | /O(n)/ The subset where all elements are less than+-- or equal to the given value. +belowInclusive :: (Ord a)+ => a -- ^ inclusive upper bound+ -> Set a+ -> Set a+belowInclusive x (Set s) = Set (I.belowInclusive x s)++-- | /O(n)/ The subset where all elements are greater than+-- or equal to the lower bound and less than or equal to+-- the upper bound.+betweenInclusive :: (Ord a)+ => a -- ^ inclusive lower bound+ -> a -- ^ inclusive upper bound+ -> Set a+ -> Set a+betweenInclusive x y (Set s) = Set (I.betweenInclusive x y s)+
+ src/Data/Diet/Set/Unboxed.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Diet.Set.Unboxed+ ( Set+ , singleton+ , member+ , difference+ -- * Split+ , aboveInclusive+ , belowInclusive+ , betweenInclusive+ -- * Folds+ , foldr+ -- * List Conversion+ , toList+ , fromList+ , fromListN+ ) where++import Prelude hiding (lookup,map,foldr)++import Data.Semigroup (Semigroup)+import Data.Functor.Classes (Show2(..))+import Data.Primitive.Types (Prim)+import Data.Primitive.PrimArray (PrimArray)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Diet.Set.Internal as I++newtype Set a = Set (I.Set PrimArray a)++-- | /O(1)/ Create a diet set with a single element.+singleton :: (Ord a, Prim a)+ => a -- ^ inclusive lower bound+ -> a -- ^ inclusive upper bound+ -> Set a+singleton lo hi = Set (I.singleton lo hi)++-- | /O(log n)/ Lookup the value at a key in the map.+member :: (Ord a, Prim a) => a -> Set a -> Bool+member a (Set s) = I.member a s++instance (Show a, Prim a) => Show (Set a) where+ showsPrec p (Set s) = I.showsPrec p s++instance (Eq a, Prim a) => Eq (Set a) where+ Set x == Set y = I.equals x y++instance (Ord a, Prim a) => Ord (Set a) where+ compare (Set xs) (Set ys) = compare (I.toList xs) (I.toList ys)++instance (Ord a, Enum a, Prim a) => Semigroup (Set a) where+ Set x <> Set y = Set (I.append x y)++instance (Ord a, Enum a, Prim a) => Monoid (Set a) where+ mempty = Set I.empty+ mappend = (SG.<>)+ mconcat = Set . I.concat . E.coerce++instance (Ord a, Enum a, Prim a) => E.IsList (Set a) where+ type Item (Set a) = (a,a)+ fromListN n = Set . I.fromListN n+ fromList = Set . I.fromList+ toList (Set s) = I.toList s++toList :: Prim a => Set a -> [(a,a)]+toList (Set x) = I.toList x++fromList :: (Ord a, Enum a, Prim a) => [(a,a)] -> Set a+fromList = Set . I.fromList++fromListN :: (Ord a, Enum a, Prim a)+ => Int -- ^ expected size of resulting diet 'Set'+ -> [(a,a)] -- ^ key-value pairs+ -> Set a+fromListN n = Set . I.fromListN n++-- | /O(n + m*log n)/ Subtract the subtrahend of size @m@ from the+-- minuend of size @n@. It should be possible to improve the improve+-- the performance of this to /O(n + m)/. Anyone interested in doing+-- this should open a PR.+difference :: (Ord a, Enum a, Prim a)+ => Set a -- ^ minuend+ -> Set a -- ^ subtrahend+ -> Set a+difference (Set x) (Set y) = Set (I.difference x y)++foldr :: Prim a => (a -> a -> b -> b) -> b -> Set a -> b+foldr f z (Set arr) = I.foldr f z arr++-- | /O(n)/ The subset where all elements are greater than+-- or equal to the given value. +aboveInclusive :: (Ord a, Prim a)+ => a -- ^ inclusive lower bound+ -> Set a+ -> Set a+aboveInclusive x (Set s) = Set (I.aboveInclusive x s)++-- | /O(n)/ The subset where all elements are less than+-- or equal to the given value. +belowInclusive :: (Ord a, Prim a)+ => a -- ^ inclusive upper bound+ -> Set a+ -> Set a+belowInclusive x (Set s) = Set (I.belowInclusive x s)++-- | /O(n)/ The subset where all elements are greater than+-- or equal to the lower bound and less than or equal to+-- the upper bound.+betweenInclusive :: (Ord a, Prim a)+ => a -- ^ inclusive lower bound+ -> a -- ^ inclusive upper bound+ -> Set a+ -> Set a+betweenInclusive x y (Set s) = Set (I.betweenInclusive x y s)+
+ src/Data/Diet/Unbounded/Set/Internal.hs view
@@ -0,0 +1,254 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-# OPTIONS_GHC -Wall #-}++module Data.Diet.Unbounded.Set.Internal+ ( Set+ , empty+ , singleton+ , append+ , member+ , equals+ , showsPrec+ ) where++import Prelude hiding (showsPrec)++import Data.Primitive.Contiguous (Contiguous,Element,Mutable)++import qualified Data.Diet.Set.Internal as S+import qualified Data.Primitive.Contiguous as I++-- todo: switch to using an unboxed sum instead of+-- Maybe once GHC 8.4.3 becomes prevalent.+--+-- If the first Maybe is Just, then everything from negative+-- infinity (whatever that may mean for the type at hand) up+-- to the value is included in the set. It works similarly+-- for the second Maybe and positive infinity. Internally,+-- we must uphold the invariant that the range up from negative+-- infinity and the one up to positive infinity do not overlap+-- with the diet set in the middle and that they are not+-- adjacent to it (according to the Enum instance).+--+-- The second data constructor, SetAll, means that all values+-- of type @a@ are included in the Set. We do actually need+-- a separate data constructor for this since there is no+-- way to communicate it with the first one.+data Set arr a+ = SetSome !(Maybe a) !(S.Set arr a) !(Maybe a)+ | SetAll++empty :: Contiguous arr => Set arr a+empty = SetSome Nothing S.empty Nothing++equals :: (Contiguous arr, Element arr a, Eq a) => Set arr a -> Set arr a -> Bool+equals SetAll SetAll = True+equals SetAll (SetSome _ _ _) = False+equals (SetSome _ _ _) SetAll = False+equals (SetSome a b c) (SetSome x y z) = a == x && c == z && S.equals b y++singleton :: (Contiguous arr, Element arr a, Ord a)+ => Maybe a -- ^ lower inclusive bound, @Nothing@ means @-∞@+ -> Maybe a -- ^ upper inclusive bound, @Nothing@ means @+∞@+ -> Set arr a+singleton Nothing Nothing = SetAll+singleton Nothing (Just hi) = SetSome (Just hi) S.empty Nothing+singleton (Just lo) Nothing = SetSome Nothing S.empty (Just lo)+singleton (Just lo) (Just hi) = SetSome Nothing (S.singleton lo hi) Nothing++append :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => Set arr a+ -> Set arr a+ -> Set arr a+append SetAll _ = SetAll+append (SetSome _ _ _) SetAll = SetAll+append (SetSome Nothing a Nothing) (SetSome Nothing b Nothing) =+ SetSome Nothing (S.append a b) Nothing+append (SetSome (Just infHiA) a Nothing) (SetSome Nothing b Nothing) =+ let (infHi, trimmedB) = establishInfinityHi infHiA b+ in SetSome (Just infHi) (S.append a trimmedB) Nothing+append (SetSome Nothing a Nothing) (SetSome (Just infHiB) b Nothing) =+ let (infHi, trimmedA) = establishInfinityHi infHiB a+ in SetSome (Just infHi) (S.append trimmedA b) Nothing+append (SetSome (Just infHiA) a Nothing) (SetSome (Just infHiB) b Nothing) =+ case compare infHiA infHiB of+ EQ -> SetSome (Just infHiA) (S.append a b) Nothing+ LT -> + let (infHi, trimmedA) = establishInfinityHi infHiB a+ in SetSome (Just infHi) (S.append trimmedA b) Nothing+ GT -> + let (infHi, trimmedB) = establishInfinityHi infHiA b+ in SetSome (Just infHi) (S.append a trimmedB) Nothing+append (SetSome Nothing a (Just infLoA)) (SetSome Nothing b Nothing) =+ let (infLo, trimmedB) = establishInfinityLo infLoA b+ in SetSome Nothing (S.append a trimmedB) (Just infLo)+append (SetSome Nothing a Nothing) (SetSome Nothing b (Just infLoB)) =+ let (infLo, trimmedA) = establishInfinityLo infLoB a+ in SetSome Nothing (S.append trimmedA b) (Just infLo)+append (SetSome Nothing a (Just infLoA)) (SetSome Nothing b (Just infLoB)) =+ case compare infLoA infLoB of+ EQ -> SetSome Nothing (S.append a b) (Just infLoB)+ LT -> + let (infLo, trimmedB) = establishInfinityLo infLoA b+ in SetSome Nothing (S.append a trimmedB) (Just infLo)+ GT -> + let (infLo, trimmedA) = establishInfinityLo infLoB a+ in SetSome Nothing (S.append trimmedA b) (Just infLo)+append (SetSome (Just infHiA) a (Just infLoA)) (SetSome Nothing b Nothing) =+ case establishInfinityBoth infHiA infLoA b of+ Nothing -> SetAll+ Just (infHi,infLo,trimmedB) -> SetSome (Just infHi) (S.append a trimmedB) (Just infLo)+append (SetSome Nothing a Nothing) (SetSome (Just infHiB) b (Just infLoB)) =+ case establishInfinityBoth infHiB infLoB a of+ Nothing -> SetAll+ Just (infHi,infLo,trimmedA) -> SetSome (Just infHi) (S.append trimmedA b) (Just infLo)+append (SetSome (Just infHiA) a (Just infLoA)) (SetSome (Just infHiB) b (Just infLoB)) =+ generalAppend (max infHiA infHiB) (min infLoA infLoB) a b+append (SetSome Nothing a (Just infLoA)) (SetSome (Just infHiB) b (Just infLoB)) =+ generalAppend infHiB (min infLoA infLoB) a b+append (SetSome (Just infHiA) a (Just infLoA)) (SetSome Nothing b (Just infLoB)) =+ generalAppend infHiA (min infLoA infLoB) a b+append (SetSome (Just infHiA) a Nothing) (SetSome (Just infHiB) b (Just infLoB)) =+ generalAppend (max infHiA infHiB) infLoB a b+append (SetSome (Just infHiA) a (Just infLoA)) (SetSome (Just infHiB) b Nothing) =+ generalAppend (max infHiA infHiB) infLoA a b+append (SetSome Nothing a (Just infLoA)) (SetSome (Just infHiB) b Nothing) =+ generalAppend infHiB infLoA a b+append (SetSome (Just infHiA) a Nothing) (SetSome Nothing b (Just infLoB)) =+ generalAppend infHiA infLoB a b++generalAppend :: (Contiguous arr, Ord a, Enum a, Element arr a)+ => a -> a -> S.Set arr a -> S.Set arr a -> Set arr a+generalAppend infHiX infLoX a b =+ case establishInfinityBoth infHiX infLoX (S.append a b) of+ Nothing -> SetAll+ Just (infHi,infLo,trimmed) -> SetSome (Just infHi) trimmed (Just infLo)++-- This takes an value @a@ which is the upper bound of (-∞,a] range.+-- It also takes a diet set. It removes everything from the set+-- that is contained by the up-from-negative-infinity range, and+-- it also removes a range adjacent to @a@. If a range adjacent to+-- @a@ was removed, then the returned value will be the upper bound+-- of the removed adjacent range.+establishInfinityHi :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => a -- upper bound from negative infinity+ -> S.Set arr a -- diet set+ -> (a, S.Set arr a) -- new upper bound, trimmed diet set+establishInfinityHi a s = case locateAdjacentAbove a s of+ Right ix ->+ let upper = S.indexUpper ix s+ in (upper,S.slice (ix + 1) (S.size s - 1) s)+ Left ix -> (a,S.slice ix (S.size s - 1) s)++establishInfinityLo :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => a -- lower bound from positive infinity+ -> S.Set arr a -- diet set+ -> (a, S.Set arr a) -- new lower bound, trimmed diet set+establishInfinityLo a s = case locateAdjacentBelow a s of+ Right ix ->+ let lower = S.indexLower ix s+ in (lower,S.slice 0 (ix - 1) s)+ Left ix -> (a, S.slice 0 ix s)++-- this is a tweaked version of locate. If the element+-- isn't found in the diet set, it looks at its predecessor+-- to see if it is present so that we can collapse a maximal+-- number of ranges. Left gives the index of the range to+-- the left of (meaning: less than) the element.+--+-- Right: [0,n-1]+-- Left: [-1,n-1]+locateAdjacentBelow :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => a -- lower bound from positive infinity+ -> S.Set arr a -- diet set+ -> Either Int Int+locateAdjacentBelow a s = case S.locate a s of+ Right ix -> Right ix+ Left ix -> if ix == 0+ then Left (-1)+ else if S.indexUpper (ix - 1) s == pred a+ then Right (ix - 1)+ else Left (ix - 1)++-- this is a tweaked version of locate. If the element+-- isn't found in the diet set, it looks at its successor+-- to see if it is present so that we can collapse a maximal+-- number of ranges. Left gives the index of the range to+-- the right of (meaning: greater than) the element.+--+-- Right: [0,n-1]+-- Left: [0,n]+locateAdjacentAbove :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => a -- upper bound from negative infinity+ -> S.Set arr a -- diet set+ -> Either Int Int+locateAdjacentAbove a s = case S.locate a s of+ Right ix -> Right ix+ Left ix -> if ix == S.size s+ then Left ix+ else if S.indexLower ix s == succ a+ then Right ix+ else Left ix++establishInfinityBoth :: forall arr a. (Contiguous arr, Element arr a, Ord a, Enum a)+ => a -- upper bound from negative infinity+ -> a -- lower bound from positive infinity+ -> S.Set arr a -- diet set+ -> Maybe (a, a, S.Set arr a) -- new upper bound, new lower bound, trimmed diet set+establishInfinityBoth negInfHi posInfLo s = if posInfLo <= negInfHi+ then Nothing+ else case locateAdjacentAbove negInfHi s of+ Left loIx -> case locateAdjacentBelow posInfLo s of+ Left hiIx -> Just (negInfHi,posInfLo,S.slice loIx hiIx s)+ Right hiIx -> Just (negInfHi,S.indexLower hiIx s,S.slice loIx (hiIx - 1) s)+ Right loIx -> case locateAdjacentBelow posInfLo s of+ Left hiIx -> Just (S.indexUpper loIx s,posInfLo,S.slice (loIx + 1) hiIx s)+ Right hiIx -> if hiIx <= loIx+ then Nothing+ else Just (S.indexUpper loIx s, S.indexLower hiIx s, S.slice (loIx + 1) (hiIx - 1) s)+ +member :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => a+ -> Set arr a+ -> Bool+member _ SetAll = True+member x (SetSome negInfHi s posInfLo) =+ maybe False (\hi -> hi >= x) negInfHi+ || maybe False (\lo -> lo <= x) posInfLo+ || S.member x s+{-# INLINEABLE member #-}++showsPrec :: (Contiguous arr, Element arr a, Show a)+ => Int+ -> Set arr a+ -> ShowS+showsPrec _ SetAll = showString "[(-∞,+∞)]"+showsPrec p (SetSome negInfHi s posInfLo) = showParen (p > 10) $+ showString "fromList " . showListInf shows negInfHi (S.toList s) posInfLo++showListInf :: (a -> ShowS) -> Maybe a -> [(a,a)] -> Maybe a -> ShowS+showListInf showx mnegInfHi [] mposInfLo s = case mnegInfHi of+ Nothing -> case mposInfLo of+ Nothing -> "[]" ++ s+ Just posInfLo -> '[' : showPosInfLo showx posInfLo (']' : s)+ Just negInfHi -> case mposInfLo of+ Nothing -> '[' : showNegInfHi showx negInfHi (']' : s)+ Just posInfLo -> '[' : showNegInfHi showx negInfHi (',' : showPosInfLo showx posInfLo (']' : s))+showListInf showx mnegInfHi ((a0,b0):xs) mposInfLo s =+ '[' : maybe id (\negInfHi s' -> showNegInfHi showx negInfHi (',' : s')) mnegInfHi ('(' : showx a0 (',' : showx b0 (')' : showl xs)))+ where+ showl [] = maybe id (\posInfLo -> showChar ',' . showPosInfLo showx posInfLo) mposInfLo (']' : s)+ showl ((a,b):ys) = ',' : '(' : showx a (',' : showx b (')' : showl ys))++showNegInfHi :: (a -> ShowS) -> a -> ShowS+showNegInfHi showx x s = "(-∞," ++ showx x (")" ++ s)++showPosInfLo :: (a -> ShowS) -> a -> ShowS+showPosInfLo showx x s = '(' : (showx x (",+∞)" ++ s))+
+ src/Data/Diet/Unbounded/Set/Lifted.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Diet.Unbounded.Set.Lifted+ ( Set+ , singleton+ , member+ ) where++import Data.Semigroup (Semigroup)+import Data.Primitive (Array)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Diet.Unbounded.Set.Internal as I++newtype Set a = Set (I.Set Array a)++instance Eq a => Eq (Set a) where+ Set x == Set y = I.equals x y++instance (Ord a, Enum a) => Semigroup (Set a) where+ Set x <> Set y = Set (I.append x y)++instance (Ord a, Enum a) => Monoid (Set a) where+ mempty = Set (I.empty)+ mappend = (SG.<>)++instance Show a => Show (Set a) where+ showsPrec p (Set s) = I.showsPrec p s++-- | /O(1)/ Create an unbounded diet set with a single element.+singleton :: Ord a+ => Maybe a -- ^ lower inclusive bound, @Nothing@ means @-∞@+ -> Maybe a -- ^ upper inclusive bound, @Nothing@ means @+∞@+ -> Set a+singleton lo hi = Set (I.singleton lo hi)++-- | /O(log n)/ Returns @True@ if the element is a member of the diet set.+member :: Ord a => a -> Set a -> Bool+member a (Set s) = I.member a s+++
+ src/Data/Map/Internal.hs view
@@ -0,0 +1,414 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-}++{-# OPTIONS_GHC -O2 -Wall #-}+module Data.Map.Internal+ ( Map+ , empty+ , singleton+ , map+ , mapMaybe+ -- * Folds+ , foldlWithKey'+ , foldrWithKey'+ , foldMapWithKey'+ -- * Monadic Folds+ , foldlWithKeyM'+ , foldrWithKeyM'+ , foldlMapWithKeyM'+ , foldrMapWithKeyM'+ -- * Functions+ , append+ , lookup+ , showsPrec+ , equals+ , compare+ , toList+ , concat+ , size+ -- * List Conversion+ , fromListN+ , fromList+ , fromListAppend+ , fromListAppendN+ -- * Array Conversion+ , unsafeFreezeZip+ ) where++import Prelude hiding (compare,showsPrec,lookup,map,concat)++import Control.Applicative (liftA2)+import Control.Monad.ST (ST,runST)+import Data.Semigroup (Semigroup)+import Data.Foldable (foldl')+import Data.Primitive.Contiguous (Contiguous,Mutable,Element)+import Data.Primitive.Sort (sortUniqueTaggedMutable)+import qualified Data.List as L+import qualified Data.Semigroup as SG+import qualified Prelude as P+import qualified Data.Primitive.Contiguous as I+import qualified Data.Concatenation as C++-- TODO: Do some sneakiness with UnliftedRep+data Map karr varr k v = Map !(karr k) !(varr v)++empty :: (Contiguous karr, Contiguous varr) => Map karr varr k v+empty = Map I.empty I.empty++singleton :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v) => k -> v -> Map karr varr k v+singleton k v = Map+ ( runST $ do+ arr <- I.new 1+ I.write arr 0 k+ I.unsafeFreeze arr+ )+ ( runST $ do+ arr <- I.new 1+ I.write arr 0 v+ I.unsafeFreeze arr+ )++equals :: (Contiguous karr, Element karr k, Eq k, Contiguous varr, Element varr v, Eq v) => Map karr varr k v -> Map karr varr k v -> Bool+equals (Map k1 v1) (Map k2 v2) = I.equals k1 k2 && I.equals v1 v2++compare :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v, Ord v) => Map karr varr k v -> Map karr varr k v -> Ordering+compare m1 m2 = P.compare (toList m1) (toList m2)++fromListWithN :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => (v -> v -> v) -> Int -> [(k,v)] -> Map karr varr k v+fromListWithN combine n xs =+ case xs of+ [] -> empty+ (k,v) : ys ->+ let (leftovers, result) = fromAscListWith combine (max 1 n) k v ys+ in concatWith combine (result : P.map (uncurry singleton) leftovers)++fromListN :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => Int -> [(k,v)] -> Map karr varr k v+fromListN = fromListWithN (\_ a -> a)++fromList :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => [(k,v)] -> Map karr varr k v+fromList = fromListN 1++fromListAppendN :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v, Semigroup v) => Int -> [(k,v)] -> Map karr varr k v+fromListAppendN = fromListWithN (SG.<>)++fromListAppend :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v, Semigroup v) => [(k,v)] -> Map karr varr k v+fromListAppend = fromListAppendN 1++fromAscListWith :: forall karr varr k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+ => (v -> v -> v)+ -> Int -- initial size of buffer, must be 1 or higher+ -> k -- first key+ -> v -- first value+ -> [(k,v)] -- elements+ -> ([(k,v)], Map karr varr k v)+fromAscListWith combine !n !k0 !v0 xs0 = runST $ do+ keys0 <- I.new n+ vals0 <- I.new n+ I.write keys0 0 k0+ I.write vals0 0 v0+ let go :: forall s. Int -> k -> Int -> Mutable karr s k -> Mutable varr s v -> [(k,v)] -> ST s ([(k,v)], Map karr varr k v)+ go !ix !_ !sz !keys !vals [] = if ix == sz+ then do+ arrKeys <- I.unsafeFreeze keys+ arrVals <- I.unsafeFreeze vals+ return ([],Map arrKeys arrVals)+ else do+ keys' <- I.resize keys ix+ arrKeys <- I.unsafeFreeze keys'+ vals' <- I.resize vals ix+ arrVals <- I.unsafeFreeze vals'+ return ([],Map arrKeys arrVals)+ go !ix !old !sz !keys !vals ((k,v) : xs) = if ix < sz+ then case P.compare k old of+ GT -> do+ I.write keys ix k+ I.write vals ix v+ go (ix + 1) k sz keys vals xs+ EQ -> do+ !oldVal <- I.read vals (ix - 1)+ let !newVal = combine oldVal v+ I.write vals (ix - 1) newVal+ go ix k sz keys vals xs+ LT -> do+ keys' <- I.resize keys ix+ arrKeys <- I.unsafeFreeze keys'+ vals' <- I.resize vals ix+ arrVals <- I.unsafeFreeze vals'+ return ((k,v) : xs,Map arrKeys arrVals)+ else do+ let sz' = sz * 2+ keys' <- I.resize keys sz'+ vals' <- I.resize vals sz'+ go ix old sz' keys' vals' ((k,v) : xs)+ go 1 k0 n keys0 vals0 xs0+++map :: (Contiguous varr, Element varr v, Element varr w) => (v -> w) -> Map karr varr k v -> Map karr varr k w+map f (Map k v) = Map k (I.map f v)++-- | /O(n)/ Drop elements for which the predicate returns 'Nothing'.+mapMaybe :: forall karr varr k v w. (Contiguous karr, Element karr k, Contiguous varr, Element varr v, Element varr w)+ => (v -> Maybe w)+ -> Map karr varr k v+ -> Map karr varr k w+mapMaybe f (Map ks vs) = runST $ do+ let !sz = I.size vs+ !(karr :: Mutable karr s k) <- I.new sz+ !(varr :: Mutable varr s w) <- I.new sz+ let go !ixSrc !ixDst = if ixSrc < sz+ then do+ a <- I.indexM vs ixSrc+ case f a of+ Nothing -> go (ixSrc + 1) ixDst+ Just !b -> do+ I.write varr ixDst b+ I.write karr ixDst =<< I.indexM ks ixSrc+ go (ixSrc + 1) (ixDst + 1)+ else return ixDst+ dstLen <- go 0 0+ ksFinal <- I.resize karr dstLen >>= I.unsafeFreeze+ vsFinal <- I.resize varr dstLen >>= I.unsafeFreeze+ return (Map ksFinal vsFinal)++showsPrec :: (Contiguous karr, Element karr k, Show k, Contiguous varr, Element varr v, Show v) => Int -> Map karr varr k v -> ShowS+showsPrec p xs = showParen (p > 10) $+ showString "fromList " . shows (toList xs)++toList :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v) => Map karr varr k v -> [(k,v)]+toList = foldrWithKey (\k v xs -> (k,v) : xs) []++foldrWithKey :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v) => (k -> v -> b -> b) -> b -> Map karr varr k v -> b+foldrWithKey f z (Map keys vals) =+ let !sz = I.size vals+ go !i+ | i == sz = z+ | otherwise =+ let !k = I.index keys i+ !v = I.index vals i+ in f k v (go (i + 1))+ in go 0++concat :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v, Semigroup v) => [Map karr varr k v] -> Map karr varr k v+concat = concatWith (SG.<>)++concatWith :: forall karr varr k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+ => (v -> v -> v)+ -> [Map karr varr k v]+ -> Map karr varr k v+concatWith combine = C.concatSized size empty (appendWith combine)++appendWith :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v, Ord k) => (v -> v -> v) -> Map karr varr k v -> Map karr varr k v -> Map karr varr k v+appendWith combine (Map ksA vsA) (Map ksB vsB) =+ case unionArrWith combine ksA vsA ksB vsB of+ (k,v) -> Map k v+ +append :: (Contiguous karr, Element karr k, Contiguous varr, Element varr v, Ord k, Semigroup v) => Map karr varr k v -> Map karr varr k v -> Map karr varr k v+append (Map ksA vsA) (Map ksB vsB) =+ case unionArrWith (SG.<>) ksA vsA ksB vsB of+ (k,v) -> Map k v+ +unionArrWith :: forall karr varr k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+ => (v -> v -> v)+ -> karr k -- keys a+ -> varr v -- values a+ -> karr k -- keys b+ -> varr v -- values b+ -> (karr k, varr v)+unionArrWith combine keysA valsA keysB valsB+ | I.size valsA < 1 = (keysB,valsB)+ | I.size valsB < 1 = (keysA,valsA)+ | otherwise = runST $ do+ let !szA = I.size valsA+ !szB = I.size valsB+ !(keysDst :: Mutable karr s k) <- I.new (szA + szB)+ !(valsDst :: Mutable varr s v) <- I.new (szA + szB)+ let go !ixA !ixB !ixDst = if ixA < szA+ then if ixB < szB+ then do+ let !keyA = I.index keysA ixA+ !keyB = I.index keysB ixB+ !valA = I.index valsA ixA+ !valB = I.index valsB ixB+ case P.compare keyA keyB of+ EQ -> do+ I.write keysDst ixDst keyA+ let !r = combine valA valB+ I.write valsDst ixDst r+ go (ixA + 1) (ixB + 1) (ixDst + 1)+ LT -> do+ I.write keysDst ixDst keyA+ I.write valsDst ixDst valA+ go (ixA + 1) ixB (ixDst + 1)+ GT -> do+ I.write keysDst ixDst keyB+ I.write valsDst ixDst valB+ go ixA (ixB + 1) (ixDst + 1)+ else do+ I.copy keysDst ixDst keysA ixA (szA - ixA)+ I.copy valsDst ixDst valsA ixA (szA - ixA)+ return (ixDst + (szA - ixA))+ else if ixB < szB+ then do+ I.copy keysDst ixDst keysB ixB (szB - ixB)+ I.copy valsDst ixDst valsB ixB (szB - ixB)+ return (ixDst + (szB - ixB))+ else return ixDst+ !total <- go 0 0 0+ !keysFinal <- I.resize keysDst total+ !valsFinal <- I.resize valsDst total+ liftA2 (,) (I.unsafeFreeze keysFinal) (I.unsafeFreeze valsFinal)+ +lookup :: forall karr varr k v. (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v) => k -> Map karr varr k v -> Maybe v+lookup a (Map arr vals) = go 0 (I.size vals - 1) where+ go :: Int -> Int -> Maybe v+ go !start !end = if end < start+ then Nothing+ else+ let !mid = div (end + start) 2+ !(# v #) = I.index# arr mid+ in case P.compare a v of+ LT -> go start (mid - 1)+ EQ -> case I.index# vals mid of+ (# r #) -> Just r+ GT -> go (mid + 1) end+{-# INLINEABLE lookup #-}++size :: (Contiguous varr, Element varr v) => Map karr varr k v -> Int+size (Map _ arr) = I.size arr++-- | Sort and deduplicate the key array, preserving the last value associated+-- with each key. The argument arrays may not be reused after being passed+-- to this function.+unsafeFreezeZip :: (Contiguous karr, Element karr k, Ord k, Contiguous varr, Element varr v)+ => Mutable karr s k+ -> Mutable varr s v+ -> ST s (Map karr varr k v)+unsafeFreezeZip keys0 vals0 = do+ (keys1,vals1) <- sortUniqueTaggedMutable keys0 vals0+ keys2 <- I.unsafeFreeze keys1+ vals2 <- I.unsafeFreeze vals1+ return (Map keys2 vals2)+{-# INLINEABLE unsafeFreezeZip #-}++foldlWithKeyM' :: forall karr varr k v m b. (Monad m, Contiguous karr, Element karr k, Contiguous varr, Element varr v)+ => (b -> k -> v -> m b)+ -> b+ -> Map karr varr k v+ -> m b+foldlWithKeyM' f b0 (Map ks vs) = go 0 b0+ where+ !len = I.size vs+ go :: Int -> b -> m b+ go !ix !acc = if ix < len+ then+ let (# k #) = I.index# ks ix+ (# v #) = I.index# vs ix+ in f acc k v >>= go (ix + 1)+ else return acc+{-# INLINEABLE foldlWithKeyM' #-}++foldrWithKeyM' :: forall karr varr k v m b. (Monad m, Contiguous karr, Element karr k, Contiguous varr, Element varr v)+ => (k -> v -> b -> m b)+ -> b+ -> Map karr varr k v+ -> m b+foldrWithKeyM' f b0 (Map ks vs) = go (I.size vs - 1) b0+ where+ go :: Int -> b -> m b+ go !ix !acc = if ix >= 0+ then+ let (# k #) = I.index# ks ix+ (# v #) = I.index# vs ix+ in f k v acc >>= go (ix - 1)+ else return acc+{-# INLINEABLE foldrWithKeyM' #-}++foldlMapWithKeyM' :: forall karr varr k v m b. (Monad m, Contiguous karr, Element karr k, Contiguous varr, Element varr v, Monoid b)+ => (k -> v -> m b)+ -> Map karr varr k v+ -> m b+foldlMapWithKeyM' f (Map ks vs) = go 0 mempty+ where+ !len = I.size vs+ go :: Int -> b -> m b+ go !ix !accl = if ix < len+ then+ let (# k #) = I.index# ks ix+ (# v #) = I.index# vs ix+ in do+ accr <- f k v+ go (ix + 1) (mappend accl accr)+ else return accl+{-# INLINEABLE foldlMapWithKeyM' #-}++foldrMapWithKeyM' :: forall karr varr k v m b. (Monad m, Contiguous karr, Element karr k, Contiguous varr, Element varr v, Monoid b)+ => (k -> v -> m b)+ -> Map karr varr k v+ -> m b+foldrMapWithKeyM' f (Map ks vs) = go (I.size vs - 1) mempty+ where+ go :: Int -> b -> m b+ go !ix !accr = if ix >= 0+ then+ let (# k #) = I.index# ks ix+ (# v #) = I.index# vs ix+ in do+ accl <- f k v+ go (ix + 1) (mappend accl accr)+ else return accr+{-# INLINEABLE foldrMapWithKeyM' #-}++foldMapWithKey' :: forall karr varr k v b. (Contiguous karr, Element karr k, Contiguous varr, Element varr v, Monoid b)+ => (k -> v -> b)+ -> Map karr varr k v+ -> b+foldMapWithKey' f (Map ks vs) = go 0 mempty+ where+ !len = I.size vs+ go :: Int -> b -> b+ go !ix !accl = if ix < len+ then + let (# k #) = I.index# ks ix+ (# v #) = I.index# vs ix+ in go (ix + 1) (mappend accl (f k v))+ else accl+{-# INLINEABLE foldMapWithKey' #-}++foldlWithKey' :: forall karr varr k v b. (Contiguous karr, Element karr k, Contiguous varr, Element varr v)+ => (b -> k -> v -> b) + -> b+ -> Map karr varr k v+ -> b+foldlWithKey' f b0 (Map ks vs) = go 0 b0+ where+ !len = I.size vs+ go :: Int -> b -> b+ go !ix !acc = if ix < len+ then + let (# k #) = I.index# ks ix+ (# v #) = I.index# vs ix+ in go (ix + 1) (f acc k v)+ else acc+{-# INLINEABLE foldlWithKey' #-}++foldrWithKey' :: forall karr varr k v b. (Contiguous karr, Element karr k, Contiguous varr, Element varr v)+ => (k -> v -> b -> b)+ -> b+ -> Map karr varr k v+ -> b+foldrWithKey' f b0 (Map ks vs) = go (I.size vs - 1) b0+ where+ go :: Int -> b -> b+ go !ix !acc = if ix >= 0+ then+ let (# k #) = I.index# ks ix+ (# v #) = I.index# vs ix+ in go (ix - 1) (f k v acc)+ else acc+{-# INLINEABLE foldrWithKey' #-}+
+ src/Data/Map/Lifted/Lifted.hs view
@@ -0,0 +1,185 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 -Wall #-}+module Data.Map.Lifted.Lifted+ ( Map+ , singleton+ , lookup+ , size+ , map+ , mapMaybe+ -- * Folds+ , foldlWithKey'+ , foldrWithKey'+ , foldMapWithKey'+ -- * Monadic Folds+ , foldlWithKeyM'+ , foldrWithKeyM'+ , foldlMapWithKeyM'+ , foldrMapWithKeyM'+ -- * List Conversion+ , fromList+ , fromListAppend+ , fromListN+ , fromListAppendN+ ) where++import Prelude hiding (lookup,map)++import Data.Semigroup (Semigroup)+import Data.Primitive.Array (Array)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Map.Internal as I++-- | A map from keys @k@ to values @v@. The key type and the value+-- type must both have 'Prim' instances.+newtype Map k v = Map (I.Map Array Array k v)++instance (Ord k, Semigroup v) => Semigroup (Map k v) where+ Map x <> Map y = Map (I.append x y)++instance (Ord k, Semigroup v) => Monoid (Map k v) where+ mempty = Map I.empty+ mappend = (SG.<>)+ mconcat = Map . I.concat . E.coerce++instance (Eq k, Eq v) => Eq (Map k v) where+ Map x == Map y = I.equals x y++instance (Ord k, Ord v) => Ord (Map k v) where+ compare (Map x) (Map y) = I.compare x y++instance Ord k => E.IsList (Map k v) where+ type Item (Map k v) = (k,v)+ fromListN n = Map . I.fromListN n+ fromList = Map . I.fromList+ toList (Map s) = I.toList s++instance (Show k, Show v) => Show (Map k v) where+ showsPrec p (Map s) = I.showsPrec p s++-- | /O(log n)/ Lookup the value at a key in the map.+lookup :: Ord k => k -> Map k v -> Maybe v+lookup a (Map s) = I.lookup a s++-- | /O(1)/ Create a map with a single element.+singleton :: k -> v -> Map k v+singleton k v = Map (I.singleton k v)++-- | /O(n*log n)/ Create a map from a list of key-value pairs.+-- If the list contains more than one value for the same key,+-- the last value is retained. If the keys in the argument are+-- in nondescending order, this algorithm runs in /O(n)/ time instead.+fromList :: Ord k => [(k,v)] -> Map k v+fromList = Map . I.fromList++-- | /O(n*log n)/ This function has the same behavior as 'fromList'+-- regardless of whether or not the expected size is accurate. Additionally,+-- negative sizes are handled correctly. The expected size is used as the+-- size of the initially allocated buffer when building the 'Map'. If the+-- keys in the argument are in nondescending order, this algorithm runs+-- in /O(n)/ time.+fromListN :: Ord k+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListN n = Map . I.fromListN n++-- | /O(n*log n)/ This function has the same behavior as 'fromList',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppend :: (Ord k, Semigroup v) => [(k,v)] -> Map k v+fromListAppend = Map . I.fromListAppend++-- | /O(n*log n)/ This function has the same behavior as 'fromListN',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppendN :: (Ord k, Semigroup v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListAppendN n = Map . I.fromListAppendN n++-- | /O(1)/ The number of elements in the map.+size :: Map k v -> Int+size (Map m) = I.size m++-- | /O(n)/ Map over the values in the map.+map ::+ (v -> w)+ -> Map k v+ -> Map k w+map f (Map m) = Map (I.map f m)++-- | /O(n)/ Drop elements for which the predicate returns 'Nothing'.+mapMaybe ::+ (v -> Maybe w)+ -> Map k v+ -> Map k w+mapMaybe f (Map m) = Map (I.mapMaybe f m)++-- | /O(n)/ Left monadic fold over the keys and values of the map. This fold+-- is strict in the accumulator.+foldlWithKeyM' :: Monad m+ => (b -> k -> v -> m b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> m b+foldlWithKeyM' f b0 (Map m) = I.foldlWithKeyM' f b0 m++-- | /O(n)/ Right monadic fold over the keys and values of the map. This fold+-- is strict in the accumulator.+foldrWithKeyM' :: Monad m+ => (k -> v -> b -> m b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> m b+foldrWithKeyM' f b0 (Map m) = I.foldrWithKeyM' f b0 m++-- | /O(n)/ Monadic left fold over the keys and values of the map with a strict+-- monoidal accumulator. The monoidal accumulator is appended to the left+-- after each reduction.+foldlMapWithKeyM' :: (Monad m, Monoid b)+ => (k -> v -> m b) -- ^ reduction+ -> Map k v -- ^ map+ -> m b+foldlMapWithKeyM' f (Map m) = I.foldlMapWithKeyM' f m++-- | /O(n)/ Monadic right fold over the keys and values of the map with a strict+-- monoidal accumulator. The monoidal accumulator is appended to the right+-- after each reduction.+foldrMapWithKeyM' :: (Monad m, Monoid b)+ => (k -> v -> m b) -- ^ reduction+ -> Map k v -- ^ map+ -> m b+foldrMapWithKeyM' f (Map m) = I.foldrMapWithKeyM' f m++-- | /O(n)/ Fold over the keys and values of the map with a strict monoidal+-- accumulator. This function does not have left and right variants since+-- the associativity required by a monoid instance means that both variants+-- would always produce the same result.+foldMapWithKey' :: Monoid b+ => (k -> v -> b) -- ^ reduction + -> Map k v -- ^ map+ -> b+foldMapWithKey' f (Map m) = I.foldMapWithKey' f m++-- | /O(n)/ Left fold over the keys and values with a strict accumulator.+foldlWithKey' ::+ (b -> k -> v -> b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> b+foldlWithKey' f b0 (Map m) = I.foldlWithKey' f b0 m++-- | /O(n)/ Right fold over the keys and values with a strict accumulator.+foldrWithKey' ::+ (k -> v -> b -> b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> b+foldrWithKey' f b0 (Map m) = I.foldrWithKey' f b0 m
+ src/Data/Map/Subset/Internal.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-}++module Data.Map.Subset.Internal+ ( Map+ , lookup+ , empty+ , singleton+ , toList+ , fromList+ ) where++import Prelude hiding (lookup,concat)++import Data.Primitive.Contiguous (Contiguous,Element)+import Data.Set.Internal (Set(..))+import Data.Bifunctor (first)+import Data.Semigroup (Semigroup,(<>),First(..))++import qualified Data.Primitive.Contiguous as A+import qualified Data.Set.Internal as S+import qualified Data.Set.Lifted.Internal as SL+import qualified Data.Semigroup as SG+import qualified Prelude as P+import qualified Data.Foldable as F++-- There are two invariants for Map.+--+-- 1. The children of any Map may only contain keys that are+-- greater than the key in their parent.+-- 2. A parent's two children must not be equal.+--+-- This type cannot be a Functor since it needs to uses+-- an Eq instance for a kind of simple compression.+data Map k v+ = MapElement !k !(Map k v) !(Map k v)+ | MapValue !v+ | MapEmpty+ deriving (Eq,Ord)++instance (Semigroup v, Eq v, Ord k) => Semigroup (Map k v) where+ (<>) = append++instance (Semigroup v, Eq v, Ord k) => Monoid (Map k v) where+ mempty = empty+ mappend = (SG.<>)+ -- mconcat = concat ++instance (Show k, Show v) => Show (Map k v) where+ showsPrec p xs = showParen (p > 10) $+ showString "fromList " . shows (P.map (first SL.Set) (toList xs))++-- the functon f must satisfy the following:+-- a == b <=> f a == f b+unsafeMapValues :: (v -> w) -> Map k v -> Map k w+unsafeMapValues f = go where+ go MapEmpty = MapEmpty+ go (MapValue x) = MapValue (f x)+ go (MapElement k present absent) =+ MapElement k (go present) (go absent)++toList :: (Contiguous arr, Element arr k)+ => Map k v+ -> [(Set arr k,v)]+toList = foldrWithKey (\k v xs -> (k,v) : xs) []++fromList :: (Contiguous arr, Element arr k, Ord k, Eq v)+ => [(Set arr k,v)]+ -> Map k v+fromList = unsafeMapValues getFirst . concat . P.map (\(s,v) -> singleton s (First v))++concat :: (Ord k,Semigroup v,Eq v)+ => [Map k v]+ -> Map k v+concat = F.foldl' (\r x -> append r x) empty++foldrWithKey :: (Contiguous arr, Element arr k)+ => (Set arr k -> v -> b -> b)+ -> b+ -> Map k v+ -> b+foldrWithKey f b0 = go 0 [] b0 where+ go !_ !_ b MapEmpty = b+ go !n !xs b (MapValue v) = f (Set (A.unsafeFromListReverseN n xs)) v b+ go !n !xs b (MapElement k present absent) =+ go (n + 1) (k : xs) (go n xs b absent) present++empty :: Map k v+empty = MapEmpty++singleton :: (Eq v, Contiguous arr, Element arr k)+ => Set arr k+ -> v+ -> Map k v+singleton s v = S.foldr (\k m -> MapElement k m empty) (MapValue v) s+ +lookup :: forall arr k v. (Ord k, Contiguous arr, Element arr k)+ => Set arr k+ -> Map k v+ -> Maybe v+{-# INLINABLE lookup #-}+lookup (Set arr) = go 0 where+ !sz = A.size arr+ go :: Int -> Map k v -> Maybe v+ go !_ MapEmpty = Nothing+ go !_ (MapValue v) = Just v+ go !ix (MapElement element present absent) =+ choose ix element present absent+ choose :: Int -> k -> Map k v -> Map k v -> Maybe v+ choose !ix element present absent = if ix < sz+ then + let (# k #) = A.index# arr ix+ in case compare k element of+ EQ -> go (ix + 1) present+ LT -> choose (ix + 1) element present absent+ GT -> go ix absent+ else followAbsent absent++followAbsent :: Map k v -> Maybe v+followAbsent (MapElement _ _ x) = followAbsent x+followAbsent (MapValue v) = Just v+followAbsent MapEmpty = Nothing++augment :: (Eq k, Eq v) => (v -> v) -> v -> Map k v -> Map k v+augment _ v MapEmpty = MapValue v+augment f _ (MapValue x) = MapValue (f x)+augment f v (MapElement k present absent) =+ let present' = augment f v present+ absent' = augment f v absent+ in if present' == absent'+ then present' + else MapElement k present' absent'++append :: forall k v. (Semigroup v, Eq v, Ord k) => Map k v -> Map k v -> Map k v+append = go where+ go :: Map k v -> Map k v -> Map k v+ go MapEmpty m = m+ go (MapValue x) (MapValue y) = MapValue (x <> y)+ go (MapValue x) MapEmpty = MapValue x+ go (MapValue x) (MapElement elemY presentY absentY) =+ augment (x SG.<>) x (MapElement elemY presentY absentY)+ go (MapElement elemX presentX absentX) MapEmpty =+ MapElement elemX presentX absentX+ go (MapElement elemX presentX absentX) (MapValue y) =+ augment (SG.<> y) y (MapElement elemX presentX absentX)+ go (MapElement elemX presentX absentX) (MapElement elemY presentY absentY) = case compare elemX elemY of+ EQ -> + let present = go presentX presentY+ absent = go absentX absentY+ in if present == absent+ then present+ else MapElement elemX present absent+ LT ->+ let present = go presentX (MapElement elemY presentY absentY)+ absent = go absentX (MapElement elemY presentY absentY)+ in if present == absent+ then present+ else MapElement elemX present absent+ GT ->+ let present = go (MapElement elemX presentX absentX) presentY+ absent = go (MapElement elemX presentX absentX) absentY+ in if present == absent+ then present+ else MapElement elemY present absent+
+ src/Data/Map/Subset/Lifted.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-}++module Data.Map.Subset.Lifted+ ( I.Map+ , singleton+ , lookup+ , toList+ , fromList+ ) where++import Prelude hiding (lookup)++import Data.Map.Subset.Internal (Map)+import Data.Set.Lifted.Internal (Set(..))+import Data.Bifunctor (first)+import Data.Semigroup (Semigroup)++import qualified Data.Map.Subset.Internal as I++singleton :: (Monoid v, Eq v)+ => Set k+ -> v+ -> Map k v+singleton (Set s) v = I.singleton s v++lookup :: Ord k => Set k -> Map k v -> Maybe v+lookup (Set s) m = I.lookup s m++toList :: Map k v -> [(Set k,v)]+toList = map (first Set) . I.toList++fromList :: (Ord k, Eq v, Semigroup v) => [(Set k,v)] -> Map k v+fromList = I.fromList . map (first getSet)
+ src/Data/Map/Unboxed/Lifted.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Map.Unboxed.Lifted+ ( Map+ , singleton+ , lookup+ , size+ , map+ , mapMaybe+ -- * Folds+ , foldMapWithKey'+ -- * Monadic Folds+ , foldlWithKeyM'+ , foldrWithKeyM'+ , foldlMapWithKeyM'+ , foldrMapWithKeyM'+ -- * List Conversion+ , fromList+ , fromListAppend+ , fromListN+ , fromListAppendN+ -- * Array Conversion+ , unsafeFreezeZip+ ) where++import Prelude hiding (lookup,map)++import Control.Monad.ST (ST)+import Data.Semigroup (Semigroup)+import Data.Primitive.Types (Prim)+import Data.Primitive (PrimArray,Array,MutablePrimArray,MutableArray)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Map.Internal as I++-- | A map from keys @k@ to values @v@. The key type must have a+-- 'Prim' instance and the value type is unconstrained.+newtype Map k v = Map (I.Map PrimArray Array k v)++-- | This fails the functor laws since fmap is strict.+instance Prim k => Functor (Map k) where+ fmap = map++instance (Prim k, Ord k, Semigroup v) => Semigroup (Map k v) where+ Map x <> Map y = Map (I.append x y)++instance (Prim k, Ord k, Semigroup v) => Monoid (Map k v) where+ mempty = Map I.empty+ mappend = (SG.<>)+ mconcat = Map . I.concat . E.coerce++instance (Prim k, Eq k, Eq v) => Eq (Map k v) where+ Map x == Map y = I.equals x y++instance (Prim k, Ord k, Ord v) => Ord (Map k v) where+ compare (Map x) (Map y) = I.compare x y++instance (Prim k, Ord k) => E.IsList (Map k v) where+ type Item (Map k v) = (k,v)+ fromListN n = Map . I.fromListN n+ fromList = Map . I.fromList+ toList (Map s) = I.toList s++instance (Prim k, Show k, Show v) => Show (Map k v) where+ showsPrec p (Map s) = I.showsPrec p s++-- | /O(log n)/ Lookup the value at a key in the map.+lookup :: (Prim k, Ord k) => k -> Map k v -> Maybe v+lookup a (Map s) = I.lookup a s++-- | /O(1)/ Create a map with a single element.+singleton :: (Prim k) => k -> v -> Map k v+singleton k v = Map (I.singleton k v)++-- | /O(n*log n)/ Create a map from a list of key-value pairs.+-- If the list contains more than one value for the same key,+-- the last value is retained. If the keys in the argument are+-- in nondescending order, this algorithm runs in /O(n)/ time instead.+fromList :: (Prim k, Ord k) => [(k,v)] -> Map k v+fromList = Map . I.fromList++-- | /O(n*log n)/ This function has the same behavior as 'fromList'+-- regardless of whether or not the expected size is accurate. Additionally,+-- negative sizes are handled correctly. The expected size is used as the+-- size of the initially allocated buffer when building the 'Map'. If the+-- keys in the argument are in nondescending order, this algorithm runs+-- in /O(n)/ time.+fromListN :: (Prim k, Ord k)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListN n = Map . I.fromListN n++-- | /O(n*log n)/ This function has the same behavior as 'fromList',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppend :: (Prim k, Ord k, Semigroup v) => [(k,v)] -> Map k v+fromListAppend = Map . I.fromListAppend++-- | /O(n*log n)/ This function has the same behavior as 'fromListN',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppendN :: (Prim k, Ord k, Semigroup v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListAppendN n = Map . I.fromListAppendN n++-- | /O(1)/ The number of elements in the map.+size :: Map k v -> Int+size (Map m) = I.size m++-- | /O(n)/ Map over the values in the map.+map :: Prim k+ => (v -> w)+ -> Map k v+ -> Map k w+map f (Map m) = Map (I.map f m)++-- | /O(n)/ Drop elements for which the predicate returns 'Nothing'.+mapMaybe :: Prim k+ => (v -> Maybe w)+ -> Map k v+ -> Map k w+mapMaybe f (Map m) = Map (I.mapMaybe f m)++-- | /O(n)/ Left monadic fold over the keys and values of the map. This fold+-- is strict in the accumulator.+foldlWithKeyM' :: (Monad m, Prim k)+ => (b -> k -> v -> m b)+ -> b+ -> Map k v+ -> m b+foldlWithKeyM' f b0 (Map m) = I.foldlWithKeyM' f b0 m++-- | /O(n)/ Right monadic fold over the keys and values of the map. This fold+-- is strict in the accumulator.+foldrWithKeyM' :: (Monad m, Prim k)+ => (k -> v -> b -> m b)+ -> b+ -> Map k v+ -> m b+foldrWithKeyM' f b0 (Map m) = I.foldrWithKeyM' f b0 m++-- | /O(n)/ Monadic left fold over the keys and values of the map with a strict+-- monoidal accumulator. The monoidal accumulator is appended to the left+-- after each reduction.+foldlMapWithKeyM' :: (Monad m, Monoid b, Prim k)+ => (k -> v -> m b)+ -> Map k v+ -> m b+foldlMapWithKeyM' f (Map m) = I.foldlMapWithKeyM' f m++-- | /O(n)/ Monadic right fold over the keys and values of the map with a strict+-- monoidal accumulator. The monoidal accumulator is appended to the right+-- after each reduction.+foldrMapWithKeyM' :: (Monad m, Monoid b, Prim k)+ => (k -> v -> m b) -- ^ reduction+ -> Map k v -- ^ map+ -> m b+foldrMapWithKeyM' f (Map m) = I.foldrMapWithKeyM' f m++-- | /O(n)/ Fold over the keys and values of the map with a strict monoidal+-- accumulator. This function does not have left and right variants since+-- the associativity required by a monoid instance means that both variants+-- would always produce the same result.+foldMapWithKey' :: (Monoid b, Prim k)+ => (k -> v -> b)+ -> Map k v+ -> b+foldMapWithKey' f (Map m) = I.foldMapWithKey' f m++-- | /O(n*log n)/ Zip an array of keys with an array of values. If they are+-- not the same length, the longer one will be truncated to match the shorter+-- one. This function sorts and deduplicates the array of keys, preserving the+-- last value associated with each key. The argument arrays may not be+-- reused after being passed to this function.+--+-- This is by far the fastest way to create a map, since the functions backing it+-- are aggressively specialized. It internally uses a hybrid of mergesort and+-- insertion sort provided by the @primitive-sort@ package. It generates much+-- less garbage than any of the @fromList@ variants. +unsafeFreezeZip :: (Ord k, Prim k)+ => MutablePrimArray s k+ -> MutableArray s v+ -> ST s (Map k v)+unsafeFreezeZip keys vals = fmap Map (I.unsafeFreezeZip keys vals)
+ src/Data/Map/Unboxed/Unboxed.hs view
@@ -0,0 +1,206 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 -Wall #-}+module Data.Map.Unboxed.Unboxed+ ( Map+ , singleton+ , lookup+ , size+ , map+ , mapMaybe+ -- * Folds+ , foldlWithKey'+ , foldrWithKey'+ , foldMapWithKey'+ -- * Monadic Folds+ , foldlWithKeyM'+ , foldrWithKeyM'+ , foldlMapWithKeyM'+ , foldrMapWithKeyM'+ -- * List Conversion+ , fromList+ , fromListAppend+ , fromListN+ , fromListAppendN+ -- * Array Conversion+ , unsafeFreezeZip+ ) where++import Prelude hiding (lookup,map)++import Control.Monad.ST (ST)+import Data.Semigroup (Semigroup)+import Data.Primitive.Types (Prim)+import Data.Primitive.PrimArray (PrimArray,MutablePrimArray)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Map.Internal as I++-- | A map from keys @k@ to values @v@. The key type and the value+-- type must both have 'Prim' instances.+newtype Map k v = Map (I.Map PrimArray PrimArray k v)++instance (Prim k, Ord k, Prim v, Semigroup v) => Semigroup (Map k v) where+ Map x <> Map y = Map (I.append x y)++instance (Prim k, Ord k, Prim v, Semigroup v) => Monoid (Map k v) where+ mempty = Map I.empty+ mappend = (SG.<>)+ mconcat = Map . I.concat . E.coerce++instance (Prim k, Eq k, Prim v, Eq v) => Eq (Map k v) where+ Map x == Map y = I.equals x y++instance (Prim k, Ord k, Prim v, Ord v) => Ord (Map k v) where+ compare (Map x) (Map y) = I.compare x y++instance (Prim k, Ord k, Prim v) => E.IsList (Map k v) where+ type Item (Map k v) = (k,v)+ fromListN n = Map . I.fromListN n+ fromList = Map . I.fromList+ toList (Map s) = I.toList s++instance (Prim k, Show k, Prim v, Show v) => Show (Map k v) where+ showsPrec p (Map s) = I.showsPrec p s++-- | /O(log n)/ Lookup the value at a key in the map.+lookup :: (Prim k, Ord k, Prim v) => k -> Map k v -> Maybe v+lookup a (Map s) = I.lookup a s++-- | /O(1)/ Create a map with a single element.+singleton :: (Prim k, Prim v) => k -> v -> Map k v+singleton k v = Map (I.singleton k v)++-- | /O(n*log n)/ Create a map from a list of key-value pairs.+-- If the list contains more than one value for the same key,+-- the last value is retained. If the keys in the argument are+-- in nondescending order, this algorithm runs in /O(n)/ time instead.+fromList :: (Prim k, Ord k, Prim v) => [(k,v)] -> Map k v+fromList = Map . I.fromList++-- | /O(n*log n)/ This function has the same behavior as 'fromList'+-- regardless of whether or not the expected size is accurate. Additionally,+-- negative sizes are handled correctly. The expected size is used as the+-- size of the initially allocated buffer when building the 'Map'. If the+-- keys in the argument are in nondescending order, this algorithm runs+-- in /O(n)/ time.+fromListN :: (Prim k, Ord k, Prim v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListN n = Map . I.fromListN n++-- | /O(n*log n)/ This function has the same behavior as 'fromList',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppend :: (Prim k, Ord k, Prim v, Semigroup v) => [(k,v)] -> Map k v+fromListAppend = Map . I.fromListAppend++-- | /O(n*log n)/ This function has the same behavior as 'fromListN',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppendN :: (Prim k, Ord k, Prim v, Semigroup v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListAppendN n = Map . I.fromListAppendN n++-- | /O(1)/ The number of elements in the map.+size :: Prim v => Map k v -> Int+size (Map m) = I.size m++-- | /O(n)/ Map over the values in the map.+map :: (Prim k, Prim v, Prim w)+ => (v -> w)+ -> Map k v+ -> Map k w+map f (Map m) = Map (I.map f m)++-- | /O(n)/ Drop elements for which the predicate returns 'Nothing'.+mapMaybe :: (Prim k, Prim v, Prim w)+ => (v -> Maybe w)+ -> Map k v+ -> Map k w+mapMaybe f (Map m) = Map (I.mapMaybe f m)++-- | /O(n)/ Left monadic fold over the keys and values of the map. This fold+-- is strict in the accumulator.+foldlWithKeyM' :: (Monad m, Prim k, Prim v)+ => (b -> k -> v -> m b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> m b+foldlWithKeyM' f b0 (Map m) = I.foldlWithKeyM' f b0 m++-- | /O(n)/ Right monadic fold over the keys and values of the map. This fold+-- is strict in the accumulator.+foldrWithKeyM' :: (Monad m, Prim k, Prim v)+ => (k -> v -> b -> m b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> m b+foldrWithKeyM' f b0 (Map m) = I.foldrWithKeyM' f b0 m++-- | /O(n)/ Monadic left fold over the keys and values of the map with a strict+-- monoidal accumulator. The monoidal accumulator is appended to the left+-- after each reduction.+foldlMapWithKeyM' :: (Monad m, Monoid b, Prim k, Prim v)+ => (k -> v -> m b) -- ^ reduction+ -> Map k v -- ^ map+ -> m b+foldlMapWithKeyM' f (Map m) = I.foldlMapWithKeyM' f m++-- | /O(n)/ Monadic right fold over the keys and values of the map with a strict+-- monoidal accumulator. The monoidal accumulator is appended to the right+-- after each reduction.+foldrMapWithKeyM' :: (Monad m, Monoid b, Prim k, Prim v)+ => (k -> v -> m b) -- ^ reduction+ -> Map k v -- ^ map+ -> m b+foldrMapWithKeyM' f (Map m) = I.foldrMapWithKeyM' f m++-- | /O(n)/ Fold over the keys and values of the map with a strict monoidal+-- accumulator. This function does not have left and right variants since+-- the associativity required by a monoid instance means that both variants+-- would always produce the same result.+foldMapWithKey' :: (Monoid b, Prim k, Prim v)+ => (k -> v -> b) -- ^ reduction + -> Map k v -- ^ map+ -> b+foldMapWithKey' f (Map m) = I.foldMapWithKey' f m++-- | /O(n)/ Left fold over the keys and values with a strict accumulator.+foldlWithKey' :: (Prim k, Prim v)+ => (b -> k -> v -> b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> b+foldlWithKey' f b0 (Map m) = I.foldlWithKey' f b0 m++-- | /O(n)/ Right fold over the keys and values with a strict accumulator.+foldrWithKey' :: (Prim k, Prim v)+ => (k -> v -> b -> b) -- ^ reduction+ -> b -- ^ initial accumulator+ -> Map k v -- ^ map+ -> b+foldrWithKey' f b0 (Map m) = I.foldrWithKey' f b0 m++-- | /O(n*log n)/ Zip an array of keys with an array of values. If they are+-- not the same length, the longer one will be truncated to match the shorter+-- one. This function sorts and deduplicates the array of keys, preserving the+-- last value associated with each key. The argument arrays may not be+-- reused after being passed to this function.+--+-- This is by far the fastest way to create a map, since the functions backing it+-- are aggressively specialized. It internally uses a hybrid of mergesort and+-- insertion sort provided by the @primitive-sort@ package. It generates much+-- less garbage than any of the @fromList@ variants. +unsafeFreezeZip :: (Ord k, Prim k, Prim v)+ => MutablePrimArray s k+ -> MutablePrimArray s v+ -> ST s (Map k v)+unsafeFreezeZip keys vals = fmap Map (I.unsafeFreezeZip keys vals)+
+ src/Data/Map/Unboxed/Unlifted.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Map.Unboxed.Unlifted+ ( Map+ , singleton+ , lookup+ , size+ -- * List Conversion+ , fromList+ , fromListAppend+ , fromListN+ , fromListAppendN+ ) where++import Prelude hiding (lookup)++import Data.Semigroup (Semigroup)+import Data.Primitive.Types (Prim)+import Data.Primitive.UnliftedArray (PrimUnlifted,UnliftedArray)+import Data.Primitive (PrimArray)+import qualified GHC.Exts as E+import qualified Data.Semigroup as SG+import qualified Data.Map.Internal as I++-- | A map from keys @k@ to values @v@. The key type and the value+-- type must both have 'Prim' instances.+newtype Map k v = Map (I.Map PrimArray UnliftedArray k v)++instance (Prim k, Ord k, PrimUnlifted v, Semigroup v) => Semigroup (Map k v) where+ Map x <> Map y = Map (I.append x y)++instance (Prim k, Ord k, PrimUnlifted v, Semigroup v) => Monoid (Map k v) where+ mempty = Map I.empty+ mappend = (SG.<>)+ mconcat = Map . I.concat . E.coerce++instance (Prim k, Eq k, PrimUnlifted v, Eq v) => Eq (Map k v) where+ Map x == Map y = I.equals x y++instance (Prim k, Ord k, PrimUnlifted v, Ord v) => Ord (Map k v) where+ compare (Map x) (Map y) = I.compare x y++instance (Prim k, Ord k, PrimUnlifted v) => E.IsList (Map k v) where+ type Item (Map k v) = (k,v)+ fromListN n = Map . I.fromListN n+ fromList = Map . I.fromList+ toList (Map s) = I.toList s++instance (Prim k, Show k, PrimUnlifted v, Show v) => Show (Map k v) where+ showsPrec p (Map s) = I.showsPrec p s++-- | /O(log n)/ Lookup the value at a key in the map.+lookup :: (Prim k, Ord k, PrimUnlifted v) => k -> Map k v -> Maybe v+lookup a (Map s) = I.lookup a s++-- | /O(1)/ Create a map with a single element.+singleton :: (Prim k, PrimUnlifted v) => k -> v -> Map k v+singleton k v = Map (I.singleton k v)++-- | /O(n*log n)/ Create a map from a list of key-value pairs.+-- If the list contains more than one value for the same key,+-- the last value is retained. If the keys in the argument are+-- in nondescending order, this algorithm runs in /O(n)/ time instead.+fromList :: (Prim k, Ord k, PrimUnlifted v) => [(k,v)] -> Map k v+fromList = Map . I.fromList++-- | /O(n*log n)/ This function has the same behavior as 'fromList'+-- regardless of whether or not the expected size is accurate. Additionally,+-- negative sizes are handled correctly. The expected size is used as the+-- size of the initially allocated buffer when building the 'Map'. If the+-- keys in the argument are in nondescending order, this algorithm runs+-- in /O(n)/ time.+fromListN :: (Prim k, Ord k, PrimUnlifted v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListN n = Map . I.fromListN n++-- | /O(n*log n)/ This function has the same behavior as 'fromList',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppend :: (Prim k, Ord k, PrimUnlifted v, Semigroup v) => [(k,v)] -> Map k v+fromListAppend = Map . I.fromListAppend++-- | /O(n*log n)/ This function has the same behavior as 'fromListN',+-- but it combines values with the 'Semigroup' instances instead of+-- choosing the last occurrence.+fromListAppendN :: (Prim k, Ord k, PrimUnlifted v, Semigroup v)+ => Int -- ^ expected size of resulting 'Map'+ -> [(k,v)] -- ^ key-value pairs+ -> Map k v+fromListAppendN n = Map . I.fromListAppendN n++-- | /O(1)/ The number of elements in the map.+size :: PrimUnlifted v => Map k v -> Int+size (Map m) = I.size m++
+ src/Data/Set/Internal.hs view
@@ -0,0 +1,259 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UndecidableInstances #-}++{-# OPTIONS_GHC -Wall #-}+module Data.Set.Internal+ ( Set(..)+ , empty+ , singleton+ , difference+ , append+ , member+ , showsPrec+ , equals+ , compare+ , fromListN+ , fromList+ , toList+ , size+ , concat+ -- * Folds+ , foldr+ , foldl'+ , foldr'+ , foldMap'+ , foldlM'+ ) where++import Prelude hiding (compare,showsPrec,concat,foldr)+import qualified Prelude as P++import Control.Monad.ST (ST,runST)+import Data.Primitive.UnliftedArray (PrimUnlifted(..))+import Data.Primitive.Contiguous (Contiguous,Mutable,Element)+import qualified Data.Primitive.Contiguous as A+import qualified Data.Concatenation as C++newtype Set arr a = Set (arr a)++instance Contiguous arr => PrimUnlifted (Set arr a) where+ toArrayArray# (Set a) = A.unlift a+ fromArrayArray# a = Set (A.lift a)++append :: (Contiguous arr, Element arr a, Ord a) => Set arr a -> Set arr a -> Set arr a+append (Set x) (Set y) = Set (unionArr x y)+ +empty :: Contiguous arr => Set arr a+empty = Set A.empty++equals :: (Contiguous arr, Element arr a, Eq a) => Set arr a -> Set arr a -> Bool+equals (Set x) (Set y) = A.equals x y++compare :: (Contiguous arr, Element arr a, Ord a) => Set arr a -> Set arr a -> Ordering+compare (Set x) (Set y) = compareArr x y++fromListN :: (Contiguous arr, Element arr a, Ord a) => Int -> [a] -> Set arr a+fromListN n xs = -- fromList xs+ case xs of+ [] -> empty+ y : ys ->+ let (leftovers, result) = fromAscList (max 1 n) y ys+ in concat (result : P.map singleton leftovers)++fromList :: (Contiguous arr, Element arr a, Ord a) => [a] -> Set arr a+fromList = fromListN 1++difference :: forall a arr. (Contiguous arr, Element arr a, Ord a)+ => Set arr a+ -> Set arr a+ -> Set arr a+difference s1@(Set arr1) s2@(Set arr2)+ | sz1 == 0 = empty+ | sz2 == 0 = s1+ | otherwise = runST $ do+ dst <- A.new sz1+ let go !ix1 !ix2 !dstIx = if ix2 < sz2+ then if ix1 < sz1+ then do+ v1 <- A.indexM arr1 ix1+ v2 <- A.indexM arr2 ix2+ case P.compare v1 v2 of+ EQ -> go (ix1 + 1) (ix2 + 1) dstIx+ LT -> do+ A.write dst dstIx v1+ go (ix1 + 1) ix2 (dstIx + 1)+ GT -> go ix1 (ix2 + 1) dstIx+ else return dstIx+ else do+ let !remaining = sz1 - ix1+ A.copy dst dstIx arr1 ix1 remaining+ return (dstIx + remaining)+ dstSz <- go 0 0 0+ dstFrozen <- A.resize dst dstSz >>= A.unsafeFreeze+ return (Set dstFrozen)+ where+ !sz1 = size s1+ !sz2 = size s2++fromAscList :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => Int -- initial size of buffer, must be 1 or higher+ -> a -- first element+ -> [a] -- elements+ -> ([a], Set arr a)+fromAscList !n x0 xs0 = runST $ do+ marr0 <- A.new n+ A.write marr0 0 x0+ let go :: forall s. Int -> a -> Int -> Mutable arr s a -> [a] -> ST s ([a], Set arr a)+ go !ix !_ !sz !marr [] = if ix == sz+ then do+ arr <- A.unsafeFreeze marr+ return ([],Set arr)+ else do+ marr' <- A.resize marr ix+ arr <- A.unsafeFreeze marr'+ return ([],Set arr)+ go !ix !old !sz !marr (x : xs) = if ix < sz+ then case P.compare x old of+ GT -> do+ A.write marr ix x+ go (ix + 1) x sz marr xs+ EQ -> go ix x sz marr xs+ LT -> do+ marr' <- A.resize marr ix+ arr <- A.unsafeFreeze marr'+ return (x : xs,Set arr)+ else do+ let sz' = sz * 2+ marr' <- A.resize marr sz'+ go ix old sz' marr' (x : xs)+ go 1 x0 n marr0 xs0++showsPrec :: (Contiguous arr, Element arr a, Show a) => Int -> Set arr a -> ShowS+showsPrec p xs = showParen (p > 10) $+ showString "fromList " . shows (toList xs)++toList :: (Contiguous arr, Element arr a) => Set arr a -> [a]+toList = foldr (:) []++member :: forall arr a. (Contiguous arr, Element arr a, Ord a) => a -> Set arr a -> Bool+member a (Set arr) = go 0 (A.size arr - 1) where+ go :: Int -> Int -> Bool+ go !start !end = if end < start+ then False+ else+ let !mid = div (end + start) 2+ !v = A.index arr mid+ in case P.compare a v of+ LT -> go start (mid - 1)+ EQ -> True+ GT -> go (mid + 1) end+{-# INLINEABLE member #-}++concat :: forall arr a. (Contiguous arr, Element arr a, Ord a) => [Set arr a] -> Set arr a+concat = C.concatSized size empty append++compareArr :: (Contiguous arr, Element arr a, Ord a)+ => arr a+ -> arr a+ -> Ordering+compareArr arrA arrB = go 0 where+ go :: Int -> Ordering+ go !ix = if ix < A.size arrA+ then if ix < A.size arrB+ then mappend (P.compare (A.index arrA ix) (A.index arrB ix)) (go (ix + 1))+ else GT+ else if ix < A.size arrB+ then LT+ else EQ++singleton :: (Contiguous arr, Element arr a) => a -> Set arr a+singleton a = Set $ runST $ do+ arr <- A.new 1+ A.write arr 0 a+ A.unsafeFreeze arr++unionArr :: forall arr a. (Contiguous arr, Element arr a, Ord a)+ => arr a -- array x+ -> arr a -- array y+ -> arr a+unionArr arrA arrB+ | szA < 1 = arrB+ | szB < 1 = arrA+ | otherwise = runST $ do+ !(arrDst :: Mutable arr s a) <- A.new (szA + szB)+ let go !ixA !ixB !ixDst = if ixA < szA+ then if ixB < szB+ then do+ let !a = A.index arrA ixA+ !b = A.index arrB ixB+ case P.compare a b of+ EQ -> do+ A.write arrDst ixDst a+ go (ixA + 1) (ixB + 1) (ixDst + 1)+ LT -> do+ A.write arrDst ixDst a+ go (ixA + 1) ixB (ixDst + 1)+ GT -> do+ A.write arrDst ixDst b+ go ixA (ixB + 1) (ixDst + 1)+ else do+ A.copy arrDst ixDst arrA ixA (szA - ixA)+ return (ixDst + (szA - ixA))+ else if ixB < szB+ then do+ A.copy arrDst ixDst arrB ixB (szB - ixB)+ return (ixDst + (szB - ixB))+ else return ixDst+ total <- go 0 0 0+ arrFinal <- A.resize arrDst total+ A.unsafeFreeze arrFinal+ where+ !szA = A.size arrA+ !szB = A.size arrB++size :: (Contiguous arr, Element arr a) => Set arr a -> Int+size (Set arr) = A.size arr++foldr :: (Contiguous arr, Element arr a)+ => (a -> b -> b)+ -> b+ -> Set arr a+ -> b+foldr f b0 (Set arr) = A.foldr f b0 arr+{-# INLINEABLE foldr #-}++foldl' :: (Contiguous arr, Element arr a)+ => (b -> a -> b)+ -> b+ -> Set arr a+ -> b+foldl' f b0 (Set arr) = A.foldl' f b0 arr+{-# INLINEABLE foldl' #-}++foldr' :: (Contiguous arr, Element arr a)+ => (a -> b -> b)+ -> b+ -> Set arr a+ -> b+foldr' f b0 (Set arr) = A.foldr' f b0 arr+{-# INLINEABLE foldr' #-}++foldMap' :: (Contiguous arr, Element arr a, Monoid m)+ => (a -> m)+ -> Set arr a+ -> m+foldMap' f (Set arr) = A.foldMap' f arr+{-# INLINEABLE foldMap' #-}++foldlM' :: (Contiguous arr, Element arr a, Monad m)+ => (b -> a -> m b)+ -> b+ -> Set arr a+ -> m b+foldlM' f b0 (Set arr) = A.foldlM' f b0 arr+{-# INLINEABLE foldlM' #-}
+ src/Data/Set/Lifted.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++module Data.Set.Lifted+ ( Set+ , singleton+ , member+ , size+ , difference+ , (\\)+ -- * List Conversion+ , LI.toList+ , LI.fromList+ -- * Folds+ , LI.foldr+ , LI.foldl'+ , LI.foldr'+ , foldMap'+ ) where++import Prelude hiding (foldr)+import Data.Semigroup (Semigroup)+import Data.Set.Lifted.Internal (Set(..))+import qualified Data.Set.Internal as I+import qualified Data.Set.Lifted.Internal as LI++-- | The difference of two sets.+difference :: Ord a => Set a -> Set a -> Set a+difference (Set x) (Set y) = Set (I.difference x y)++-- | Infix operator for 'difference'.+(\\) :: Ord a => Set a -> Set a -> Set a+(\\) (Set x) (Set y) = Set (I.difference x y)++-- | Test whether or not an element is present in a set.+member :: Ord a => a -> Set a -> Bool+member a (Set s) = I.member a s++-- | Construct a set with a single element.+singleton :: a -> Set a+singleton = Set . I.singleton++-- | The number of elements in the set.+size :: Set a -> Int+size (Set s) = I.size s++-- | Strict monoidal fold over the elements in the set.+foldMap' :: Monoid m+ => (a -> m)+ -> Set a+ -> m+foldMap' f (Set arr) = I.foldMap' f arr+
+ src/Data/Set/Lifted/Internal.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++module Data.Set.Lifted.Internal+ ( Set(..)+ , toList+ , fromList+ , foldr+ , foldl'+ , foldr'+ ) where++import Prelude hiding (foldr)++import Data.Primitive.UnliftedArray (PrimUnlifted(..))+import Data.Functor.Classes (Eq1(liftEq),Show1(liftShowsPrec))+import Data.Primitive (Array)+import Data.Semigroup (Semigroup)+import Text.Show (showListWith)++import qualified Data.Foldable as F+import qualified Data.Semigroup as SG+import qualified Data.Set.Internal as I+import qualified GHC.Exts as E++newtype Set a = Set { getSet :: I.Set Array a }++instance F.Foldable Set where+ foldr = foldr+ foldl' = foldl'+ foldr' = foldr'++instance PrimUnlifted (Set a) where+ toArrayArray# (Set x) = toArrayArray# x+ fromArrayArray# y = Set (fromArrayArray# y)++instance Ord a => Semigroup (Set a) where+ Set x <> Set y = Set (I.append x y)+ stimes = SG.stimesIdempotentMonoid+ sconcat xs = Set (I.concat (E.coerce (F.toList xs)))++instance Ord a => Monoid (Set a) where+ mempty = Set I.empty+ mappend = (SG.<>)+ mconcat xs = Set (I.concat (E.coerce xs))++instance Eq a => Eq (Set a) where+ Set x == Set y = I.equals x y++instance Eq1 Set where+ liftEq f a b = liftEq f (toList a) (toList b)++instance Ord a => Ord (Set a) where+ compare (Set x) (Set y) = I.compare x y++instance Ord a => E.IsList (Set a) where+ type Item (Set a) = a+ fromListN n = Set . I.fromListN n+ fromList = Set . I.fromList+ toList = toList++instance Show a => Show (Set a) where+ showsPrec p (Set s) = I.showsPrec p s++instance Show1 Set where+ liftShowsPrec f _ p s = showParen (p > 10) $+ showString "fromList " . showListWith (f 0) (toList s)++-- | Convert a set to a list. The elements are given in ascending order.+toList :: Set a -> [a]+toList (Set s) = I.toList s++-- | Convert a list to a set.+fromList :: Ord a => [a] -> Set a+fromList = Set . I.fromList++-- | Right fold over the elements in the set. This is lazy in the accumulator.+foldr :: + (a -> b -> b)+ -> b+ -> Set a+ -> b+foldr f b0 (Set s) = I.foldr f b0 s++-- | Strict left fold over the elements in the set.+foldl' :: + (b -> a -> b)+ -> b+ -> Set a+ -> b+foldl' f b0 (Set s) = I.foldl' f b0 s++-- | Strict right fold over the elements in the set.+foldr' :: + (a -> b -> b)+ -> b+ -> Set a+ -> b+foldr' f b0 (Set s) = I.foldr' f b0 s
+ src/Data/Set/Unboxed.hs view
@@ -0,0 +1,134 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -Wall #-}+module Data.Set.Unboxed+ ( Set+ , singleton+ , member+ , size+ , difference+ , (\\)+ -- * List Conversion+ , toList+ , fromList+ -- * Folds+ , foldr+ , foldl'+ , foldr'+ , foldMap'+ ) where++import Prelude hiding (foldr)+import Data.Primitive.Types (Prim)+import Data.Primitive.UnliftedArray (PrimUnlifted(..))+import Data.Primitive.PrimArray (PrimArray)+import Data.Semigroup (Semigroup)+import qualified Data.Foldable as F+import qualified Data.Semigroup as SG+import qualified GHC.Exts as E+import qualified Data.Set.Internal as I++-- | A set of elements.+newtype Set a = Set (I.Set PrimArray a)++instance PrimUnlifted (Set a) where+ toArrayArray# (Set x) = toArrayArray# x+ fromArrayArray# y = Set (fromArrayArray# y)++instance (Prim a, Ord a) => Semigroup (Set a) where+ Set x <> Set y = Set (I.append x y)+ stimes = SG.stimesIdempotentMonoid+ sconcat xs = Set (I.concat (E.coerce (F.toList xs)))++instance (Prim a, Ord a) => Monoid (Set a) where+ mempty = Set I.empty+ mappend = (SG.<>)+ mconcat xs = Set (I.concat (E.coerce xs))++instance (Prim a, Eq a) => Eq (Set a) where+ Set x == Set y = I.equals x y++instance (Prim a, Ord a) => Ord (Set a) where+ compare (Set x) (Set y) = I.compare x y++-- | The functions that convert a list to a 'Set' are asymptotically+-- better that using @'foldMap' 'singleton'@, with a cost of /O(n*log n)/+-- rather than /O(n^2)/. If the input list is sorted, even if duplicate+-- elements are present, the algorithm further improves to /O(n)/. The+-- fastest option available is calling 'fromListN' on a presorted list+-- and passing the correct size size of the resulting 'Set'. However, even+-- if an incorrect size is given to this function,+-- it will still correctly convert the list into a 'Set'.+instance (Prim a, Ord a) => E.IsList (Set a) where+ type Item (Set a) = a+ fromListN n = Set . I.fromListN n+ fromList = Set . I.fromList+ toList = toList++instance (Prim a, Show a) => Show (Set a) where+ showsPrec p (Set s) = I.showsPrec p s++-- | The difference of two sets.+difference :: (Ord a, Prim a) => Set a -> Set a -> Set a+difference (Set x) (Set y) = Set (I.difference x y)++-- | Infix operator for 'difference'.+(\\) :: (Ord a, Prim a) => Set a -> Set a -> Set a+(\\) (Set x) (Set y) = Set (I.difference x y)+-- | Test whether or not an element is present in a set.+member :: (Prim a, Ord a) => a -> Set a -> Bool+member a (Set s) = I.member a s++-- | Construct a set with a single element.+singleton :: Prim a => a -> Set a+singleton = Set . I.singleton++-- | Convert a set to a list. The elements are given in ascending order.+toList :: Prim a => Set a -> [a]+toList (Set s) = I.toList s++-- | Convert a list to a set.+fromList :: (Ord a, Prim a) => [a] -> Set a+fromList xs = Set (I.fromList xs)++-- | The number of elements in the set.+size :: Prim a => Set a -> Int+size (Set s) = I.size s++-- | Right fold over the elements in the set. This is lazy in the accumulator.+foldr :: Prim a+ => (a -> b -> b)+ -> b+ -> Set a+ -> b+foldr f b0 (Set s) = I.foldr f b0 s++-- | Strict left fold over the elements in the set.+foldl' :: Prim a+ => (b -> a -> b)+ -> b+ -> Set a+ -> b+foldl' f b0 (Set s) = I.foldl' f b0 s++-- | Strict right fold over the elements in the set.+foldr' :: Prim a+ => (a -> b -> b)+ -> b+ -> Set a+ -> b+foldr' f b0 (Set s) = I.foldr' f b0 s++-- | Strict monoidal fold over the elements in the set.+foldMap' :: (Monoid m, Prim a)+ => (a -> m)+ -> Set a+ -> m+foldMap' f (Set arr) = I.foldMap' f arr+++
+ src/Data/Set/Unlifted.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Set.Unlifted+ ( Set+ , singleton+ , member+ , size+ ) where++import Data.Primitive.UnliftedArray (UnliftedArray, PrimUnlifted(..))+import Data.Semigroup (Semigroup)+import qualified Data.Foldable as F+import qualified Data.Semigroup as SG+import qualified GHC.Exts as E+import qualified Data.Set.Internal as I++-- | A set of elements.+newtype Set a = Set (I.Set UnliftedArray a)++instance PrimUnlifted (Set a) where+ toArrayArray# (Set x) = toArrayArray# x+ fromArrayArray# y = Set (fromArrayArray# y)++instance (PrimUnlifted a, Ord a) => Semigroup (Set a) where+ Set x <> Set y = Set (I.append x y)+ stimes = SG.stimesIdempotentMonoid+ sconcat xs = Set (I.concat (E.coerce (F.toList xs)))++instance (PrimUnlifted a, Ord a) => Monoid (Set a) where+ mempty = Set I.empty+ mappend = (SG.<>)+ mconcat xs = Set (I.concat (E.coerce xs))++instance (PrimUnlifted a, Eq a) => Eq (Set a) where+ Set x == Set y = I.equals x y++instance (PrimUnlifted a, Ord a) => Ord (Set a) where+ compare (Set x) (Set y) = I.compare x y++-- | The functions that convert a list to a 'Set' are asymptotically+-- better that using @'foldMap' 'singleton'@, with a cost of /O(n*log n)/+-- rather than /O(n^2)/. If the input list is sorted, even if duplicate+-- elements are present, the algorithm further improves to /O(n)/. The+-- fastest option available is calling 'fromListN' on a presorted list+-- and passing the correct size size of the resulting 'Set'. However, even+-- if an incorrect size is given to this function,+-- it will still correctly convert the list into a 'Set'.+instance (PrimUnlifted a, Ord a) => E.IsList (Set a) where+ type Item (Set a) = a+ fromListN n = Set . I.fromListN n+ fromList = Set . I.fromList+ toList (Set s) = I.toList s++instance (PrimUnlifted a, Show a) => Show (Set a) where+ showsPrec p (Set s) = I.showsPrec p s++-- | Test for membership in the set.+member :: (PrimUnlifted a, Ord a) => a -> Set a -> Bool+member a (Set s) = I.member a s++-- | Construct a set with a single element.+singleton :: PrimUnlifted a => a -> Set a+singleton = Set . I.singleton++-- | The number of elements in the set.+size :: PrimUnlifted a => Set a -> Int+size (Set s) = I.size s++
+ test/Main.hs view
@@ -0,0 +1,468 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}++{-# OPTIONS_GHC -fno-warn-orphans #-}++import Data.Primitive+import Data.Primitive.UnliftedArray (PrimUnlifted)+import Data.Word+import Data.Proxy (Proxy(..))+import Data.Int++import Test.Tasty (defaultMain,testGroup,TestTree)+import Test.QuickCheck (Arbitrary,Gen,(===),(==>))+import Data.List.NonEmpty (NonEmpty((:|)))+import Control.Monad (forM)+import qualified Test.Tasty.QuickCheck as TQC+import qualified Test.QuickCheck as QC+import qualified Test.QuickCheck.Classes as QCC+import qualified Data.Semigroup as SG+import qualified Data.Map as M+import qualified Data.Set as S+import qualified Data.Foldable as F+import qualified GHC.Exts as E+import qualified Test.QuickCheck.Classes.IsList as QCCL++import qualified Data.Set.Unboxed as SU+import qualified Data.Set.Lifted as SL+import qualified Data.Set.Unlifted as SUL+import qualified Data.Map.Unboxed.Unboxed as MUU+import qualified Data.Diet.Map.Unboxed.Lifted as DMUL+import qualified Data.Diet.Map.Lifted.Lifted as DMLL+import qualified Data.Diet.Set.Lifted as DSL+import qualified Data.Diet.Unbounded.Set.Lifted as DUSL+import qualified Data.Map.Subset.Lifted as MSL++main :: IO ()+main = defaultMain $ testGroup "Data"+ [ testGroup "Set"+ [ testGroup "Unboxed"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SU.Set Int16)))+ , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SU.Set Int16)))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (SU.Set Int16)))+ , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SU.Set Int16)))+ , TQC.testProperty "member" (memberProp @Int16 E.fromList SU.member)+ ]+ , testGroup "Lifted"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SL.Set Integer)))+ , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SL.Set Integer)))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (SL.Set Integer)))+ , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SL.Set Integer)))+ , TQC.testProperty "member" (memberProp @Integer E.fromList SL.member)+ , TQC.testProperty "foldr" (QCCL.foldrProp int32 SL.foldr)+ , TQC.testProperty "foldl'" (QCCL.foldlProp int16 SL.foldl')+ , TQC.testProperty "foldr'" (QCCL.foldrProp int32 SL.foldr')+ , TQC.testProperty "difference" differenceProp+ ]+ , testGroup "Unlifted"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (SUL.Set (PrimArray Int16))))+ , lawsToTest (QCC.ordLaws (Proxy :: Proxy (SUL.Set (PrimArray Int16))))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (SUL.Set (PrimArray Int16))))+ , lawsToTest (QCC.isListLaws (Proxy :: Proxy (SUL.Set (PrimArray Int16))))+ , TQC.testProperty "member" (memberProp @(PrimArray Int16) E.fromList SUL.member)+ ]+ ]+ , testGroup "Map"+ [ testGroup "Unboxed"+ [ testGroup "Unboxed"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (MUU.Map Word32 Int)))+ , lawsToTest (QCC.ordLaws (Proxy :: Proxy (MUU.Map Word32 Int)))+ , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy (MUU.Map Word32 Word)))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (MUU.Map Word32 Int)))+ , lawsToTest (QCC.isListLaws (Proxy :: Proxy (MUU.Map Word32 Int)))+ , TQC.testProperty "lookup" (lookupProp @Word32 @Int E.fromList MUU.lookup)+ , TQC.testProperty "foldlWithKey'" (mapFoldAgreement MUU.foldlWithKey' M.foldlWithKey)+ , TQC.testProperty "foldrWithKey'" (mapFoldAgreement MUU.foldrWithKey' M.foldrWithKey)+ , TQC.testProperty "foldMapWithKey'" (mapFoldMonoidAgreement MUU.foldMapWithKey' M.foldMapWithKey)+ ]+ ]+ ]+ , testGroup "Diet"+ [ testGroup "Unbounded"+ [ testGroup "Set"+ [ testGroup "Lifted"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (DUSL.Set Word8)))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (DUSL.Set Word8)))+ ]+ ]+ ]+ , testGroup "Set"+ [ testGroup "Lifted"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (DSL.Set Word16)))+ , lawsToTest (QCC.ordLaws (Proxy :: Proxy (DSL.Set Word16)))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (DSL.Set Word16)))+ , lawsToTest (QCC.isListLaws (Proxy :: Proxy (DSL.Set Word16)))+ , TQC.testProperty "member" (dietMemberProp @Word8 E.fromList DSL.member)+ , TQC.testProperty "difference" dietSetDifferenceProp+ , TQC.testProperty "aboveInclusive" dietSetAboveProp+ , testGroup "belowInclusive"+ [ TQC.testProperty "basic" dietSetBelowProp+ , TQC.testProperty "lowest" dietSetBelowLowestProp+ , TQC.testProperty "highest" dietSetBelowHighestProp+ ]+ , testGroup "betweenInclusive"+ [ TQC.testProperty "basic" dietSetBetweenProp+ , TQC.testProperty "border" dietSetBetweenBorderProp+ , TQC.testProperty "inside" dietSetBetweenBorderNearProp+ ]+ ]+ ]+ , testGroup "Map"+ [ testGroup "Subset"+ [ testGroup "Lifted"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (MSL.Map Integer (SG.Sum Integer))))+ , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy (MSL.Map Integer (SG.First Integer))))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (MSL.Map Integer (SG.Sum Integer))))+ , TQC.testProperty "lookup" subsetMapLookupProp+ ]+ ]+ , testGroup "Lifted"+ [ testGroup "Lifted"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (DMLL.Map Word8 Integer)))+ , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy (DMLL.Map Word8 Word)))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (DMLL.Map Word8 Int)))+ , lawsToTest (QCC.isListLaws (Proxy :: Proxy (DMLL.Map Word8 Integer)))+ , TQC.testProperty "lookup" (dietLookupPropA @Word8 @Int E.fromList DMLL.lookup)+ , TQC.testProperty "doubleton" dietDoubletonProp+ , TQC.testProperty "valid" dietValidProp+ ]+ ]+ , testGroup "Unboxed"+ [ testGroup "Lifted"+ [ lawsToTest (QCC.eqLaws (Proxy :: Proxy (DMUL.Map Word8 Integer)))+ , lawsToTest (QCC.semigroupLaws (Proxy :: Proxy (DMUL.Map Word8 Word)))+ , lawsToTest (QCC.commutativeMonoidLaws (Proxy :: Proxy (DMUL.Map Word8 Int)))+ , lawsToTest (QCC.isListLaws (Proxy :: Proxy (DMUL.Map Word8 Integer)))+ , TQC.testProperty "lookup" (dietLookupPropA @Word32 @Int E.fromList DMUL.lookup)+ ]+ ]+ ]+ ]+ ]++int16 :: Proxy Int16+int16 = Proxy++int32 :: Proxy Int32+int32 = Proxy++subsetMapLookupProp :: QC.Property+subsetMapLookupProp = QC.property $ \(xs :: MSL.Map Integer Integer) ->+ let xs' = MSL.toList xs+ in all (\(k,v) -> MSL.lookup k xs == Just v) xs' === True++dietSetDifferenceProp :: QC.Property+dietSetDifferenceProp = QC.property $ \(xs :: DSL.Set Word8) (ys :: DSL.Set Word8) ->+ let xs' = dietSetToSet xs+ ys' = dietSetToSet ys+ in DSL.difference xs ys === DSL.fromList (map (\x -> (x,x)) (S.toList (S.difference xs' ys')))++dietSetAboveProp :: QC.Property+dietSetAboveProp = QC.property $ \(y :: Word8) (ys :: DSL.Set Word8) ->+ let ys' = dietSetToSet ys+ (_,isMember,c) = S.splitMember y ys'+ r = if isMember then S.insert y c else c+ in DSL.aboveInclusive y ys === DSL.fromList (map (\x -> (x,x)) (S.toList r))++dietSetBelowProp :: QC.Property+dietSetBelowProp = QC.property $ \(y :: Word8) (ys :: DSL.Set Word8) ->+ let ys' = dietSetToSet ys+ (c,isMember,_) = S.splitMember y ys'+ r = if isMember then S.insert y c else c+ in DSL.belowInclusive y ys === DSL.fromList (map (\x -> (x,x)) (S.toList r))++dietSetBelowLowestProp :: QC.Property+dietSetBelowLowestProp = QC.property $ \(ys :: DSL.Set Word8) ->+ let ys' = dietSetToSet ys+ in case S.lookupMin ys' of+ Nothing -> QC.property QC.Discard+ Just y -> + let (c,isMember,_) = S.splitMember y ys'+ r = if isMember then S.insert y c else c+ in QC.property (DSL.belowInclusive y ys === DSL.fromList (map (\x -> (x,x)) (S.toList r)))++dietSetBelowHighestProp :: QC.Property+dietSetBelowHighestProp = QC.property $ \(ys :: DSL.Set Word8) ->+ let ys' = dietSetToSet ys+ in case S.lookupMax ys' of+ Nothing -> QC.property QC.Discard+ Just y -> + let (c,isMember,_) = S.splitMember y ys'+ r = if isMember then S.insert y c else c+ in QC.property (DSL.belowInclusive y ys === DSL.fromList (map (\x -> (x,x)) (S.toList r)))++dietSetBetweenProp :: QC.Property+dietSetBetweenProp = QC.property $ \(x :: Word8) (y :: Word8) (ys :: DSL.Set Word8) ->+ (x <= y)+ ==> + ( let ys' = dietSetToSet ys+ r = S.filter (\e -> e >= x && e <= y) ys'+ in DSL.betweenInclusive x y ys === DSL.fromList (map (\z -> (z,z)) (S.toList r))+ )++dietSetBetweenBorderProp :: QC.Property+dietSetBetweenBorderProp = QC.property $ \(ys :: DSL.Set Word8) ->+ let ys' = dietSetToSet ys+ in case S.lookupMax ys' of+ Nothing -> QC.property QC.Discard+ Just hi -> case S.lookupMin ys' of+ Nothing -> QC.property QC.Discard+ Just lo -> + let r = S.filter (\e -> e >= lo && e <= hi) ys'+ in DSL.betweenInclusive lo hi ys === DSL.fromList (map (\z -> (z,z)) (S.toList r))++dietSetBetweenBorderNearProp :: QC.Property+dietSetBetweenBorderNearProp = QC.property $ \(ys :: DSL.Set Word8) ->+ let ys' = dietSetToSet ys+ in ( S.size ys' > 1+ ==>+ ( let hi = pred (S.findMax ys')+ lo = succ (S.findMin ys')+ r = S.filter (\e -> e >= lo && e <= hi) ys'+ in DSL.betweenInclusive lo hi ys === DSL.fromList (map (\z -> (z,z)) (S.toList r))+ )+ )++-- This enumerates all of the element contained by all ranges+-- in the diet set.+dietSetToSet :: (Enum a, Ord a) => DSL.Set a -> S.Set a+dietSetToSet = DSL.foldr+ (\lo hi s -> S.fromList (enumFromTo lo hi) <> s)+ mempty++differenceProp :: QC.Property+differenceProp = QC.property $ \(xs :: S.Set Word8) (ys :: S.Set Word8) ->+ let xs' = SL.fromList (S.toList xs)+ ys' = SL.fromList (S.toList ys)+ in SL.toList (SL.difference xs' ys') === S.toList (S.difference xs ys)++mapFoldMonoidAgreement ::+ ((Int -> Int -> [Int]) -> MUU.Map Int Int -> [Int])+ -> ((Int -> Int -> [Int]) -> M.Map Int Int -> [Int])+ -> QC.Property+mapFoldMonoidAgreement foldPrim foldContainer = QC.property $ \(xs :: [(Int,Int)]) ->+ let p = E.fromList xs+ c = E.fromList xs+ func x y = [x + y]+ in foldPrim func p === foldContainer func c++mapFoldAgreement ::+ ((Int -> Int -> Int -> Int) -> Int -> MUU.Map Int Int -> Int)+ -> ((Int -> Int -> Int -> Int) -> Int -> M.Map Int Int -> Int)+ -> QC.Property+mapFoldAgreement foldPrim foldContainer = QC.property $ \(xs :: [(Int,Int)]) ->+ let p = E.fromList xs+ c = E.fromList xs+ -- we just need the function to be non-commutative+ func x y z = y - (2 * x) - (3 * z)+ in foldPrim func 42 p === foldContainer func 42 c++memberProp :: forall a t. (Arbitrary a, Show a) => ([a] -> t a) -> (a -> t a -> Bool) -> QC.Property+memberProp containerFromList containerMember = QC.property $ \(xs :: [a]) ->+ let c = containerFromList xs+ in all (\x -> containerMember x c) xs === True++lookupProp :: forall k v t. (Arbitrary k, Show k, Ord k, Arbitrary v, Show v, Eq v) => ([(k,v)] -> t k v) -> (k -> t k v -> Maybe v) -> QC.Property+lookupProp containerFromList containerLookup = QC.property $ \(xs :: [(k,v)]) ->+ let ys = M.fromList xs+ c = containerFromList xs+ in all (\(x,_) -> containerLookup x c == M.lookup x ys) xs === True++dietMemberProp :: forall a t. (Arbitrary a, Show a, Ord a, Arbitrary a, Show (t a)) => ([(a,a)] -> t a) -> (a -> t a -> Bool) -> QC.Property+dietMemberProp containerFromList containerLookup = QC.property $ \(xs :: [a]) ->+ let c = containerFromList (map (\a -> (a,a)) xs)+ in QC.counterexample ("original list: " ++ show xs ++ "; diet set: " ++ show c) (all (\x -> containerLookup x c == True) xs === True)++dietLookupPropA :: forall k v t. (Arbitrary k, Show k, Ord k, Arbitrary v, Show v, Eq v, Show (t k v)) => ([(k,k,v)] -> t k v) -> (k -> t k v -> Maybe v) -> QC.Property+dietLookupPropA containerFromList containerLookup = QC.property $ \(xs :: [(k,v)]) ->+ let ys = M.fromList xs+ c = containerFromList (map (\(k,v) -> (k,k,v)) xs)+ in QC.counterexample ("original list: " ++ show xs ++ "; diet map: " ++ show c) (all (\(x,_) -> containerLookup x c == M.lookup x ys) xs === True)++dietDoubletonProp :: QC.Property+dietDoubletonProp = QC.property $ \(loA :: Word8) (hiA :: Word8) (valA :: Int) (loB :: Word8) (hiB :: Word8) (valB :: Int) ->+ (hiA >= loA && hiB >= loB)+ ==>+ (simpleDoubletonToList loA hiA valA loB hiB valB === E.toList (DMLL.singleton loA hiA valA <> DMLL.singleton loB hiB valB))++dietValidProp :: QC.Property+dietValidProp = QC.property $ \(xs :: DMLL.Map Word8 Int) ->+ True === validDietTriples (E.toList xs)++simpleDoubletonToList :: (Ord k, Enum k, Semigroup v, Eq v) => k -> k -> v -> k -> k -> v -> [(k,k,v)]+simpleDoubletonToList key1A key2A valA key1B key2B valB =+ let loA = min key1A key2A+ hiA = max key1A key2A+ loB = min key1B key2B+ hiB = max key1B key2B+ in deduplicate $ case compare loA loB of+ LT -> case compare hiA loB of+ LT -> [(loA,hiA,valA),(loB,hiB,valB)]+ EQ -> case compare hiA hiB of+ LT -> [(loA,pred loB,valA),(loB,hiA,valA SG.<> valB),(succ hiA,hiB,valB)]+ EQ -> [(loA,pred loB,valA),(loB,hiA,valA SG.<> valB)]+ GT -> error "simpleDoubletonToList: invariant violated"+ GT -> case compare hiA hiB of+ LT -> [(loA,pred loB,valA),(loB,hiA,valA SG.<> valB),(succ hiA,hiB,valB)]+ EQ -> [(loA,pred loB,valA),(loB,hiA,valA SG.<> valB)]+ GT -> [(loA,pred loB,valA),(loB,hiB,valA SG.<> valB),(succ hiB,hiA,valA)]+ EQ -> case compare hiA hiB of+ LT -> [(loA,hiA,valA SG.<> valB),(succ hiA, hiB, valB)]+ GT -> [(loB,hiB,valA SG.<> valB),(succ hiB, hiA, valA)]+ EQ -> [(loA,hiA,valA SG.<> valB)]+ GT -> case compare hiB loA of+ LT -> [(loB,hiB,valB),(loA,hiA,valA)]+ EQ -> case compare hiB hiA of+ LT -> [(loB,pred loA,valB),(loA,hiB,valA SG.<> valB),(succ hiB,hiA,valA)]+ EQ -> [(loB,pred loA,valB),(loA,hiB,valA SG.<> valB)]+ GT -> error "simpleDoubletonToList: invariant violated"+ GT -> case compare hiB hiA of+ LT -> [(loB,pred loA,valB),(loA,hiB,valA SG.<> valB),(succ hiB,hiA,valA)]+ EQ -> [(loB,pred loA,valB),(loA,hiB,valA SG.<> valB)]+ GT -> [(loB,pred loA,valB),(loA,hiA,valA SG.<> valB),(succ hiA,hiB,valB)]++validDietTriples :: (Enum k,Eq k,Eq v) => [(k,k,v)] -> Bool+validDietTriples xs = deduplicate xs == xs++deduplicate :: (Enum k,Eq k, Eq v) => [(k,k,v)] -> [(k,k,v)]+deduplicate [] = []+deduplicate (x : xs) = F.toList (deduplicateNonEmpty (x :| xs))++deduplicateNonEmpty :: (Enum k, Eq k, Eq v) => NonEmpty (k,k,v) -> NonEmpty (k,k,v)+deduplicateNonEmpty ((lo,hi,v) :| xs) = case xs of+ y : ys -> case deduplicateNonEmpty (y :| ys) of+ (lo',hi',v') :| xs' -> if v == v' && pred lo' == hi+ then (lo,hi',v) :| xs'+ else (lo,hi,v) :| ((lo',hi',v') : xs')+ [] -> (lo,hi,v) :| []++lawsToTest :: QCC.Laws -> TestTree+lawsToTest (QCC.Laws name pairs) = testGroup name (map (uncurry TQC.testProperty) pairs)++instance (Arbitrary a, Prim a) => Arbitrary (PrimArray a) where+ arbitrary = fmap E.fromList QC.arbitrary++instance (Arbitrary a, Prim a, Ord a) => Arbitrary (SU.Set a) where+ arbitrary = fmap E.fromList QC.arbitrary++instance (Arbitrary a, PrimUnlifted a, Ord a) => Arbitrary (SUL.Set a) where+ arbitrary = fmap E.fromList QC.arbitrary++instance (Arbitrary a, Ord a) => Arbitrary (SL.Set a) where+ arbitrary = fmap E.fromList QC.arbitrary++instance (Arbitrary k, Prim k, Ord k, Arbitrary v, Prim v) => Arbitrary (MUU.Map k v) where+ arbitrary = fmap E.fromList QC.arbitrary++instance (Arbitrary k, Ord k, Enum k, Bounded k, Arbitrary v, Semigroup v, Eq v) => Arbitrary (DMLL.Map k v) where+ arbitrary = DMLL.fromListAppend <$> QC.vectorOf 10 arbitraryOrderedPairValue+ shrink x = map E.fromList (QC.shrink (E.toList x))+ +instance (Arbitrary k, Ord k, Arbitrary v, Eq v, Semigroup v) => Arbitrary (MSL.Map k v) where+ arbitrary = do+ len <- QC.choose (0,4)+ xs <- QC.vectorOf len $ do+ n <- QC.choose (0,3)+ ys <- QC.vector n+ v <- QC.arbitrary+ return (SL.fromList ys, v)+ return (MSL.fromList xs)+ shrink x =+ [ MSL.fromList (drop 1 y)+ ]+ where y = MSL.toList x++instance (Arbitrary k, Prim k, Ord k, Enum k, Bounded k, Arbitrary v, Semigroup v, Eq v) => Arbitrary (DMUL.Map k v) where+ arbitrary = do+ sz <- QC.choose (0,10)+ k <- QC.arbitrary+ xs <- increasingOrderedPairsHelper sz k+ ys <- forM xs $ \(lo,hi) -> do+ v <- QC.arbitrary+ return (lo,hi,v)+ return (DMUL.fromListAppend ys)+ shrink x = map E.fromList (QC.shrink (E.toList x))+ +instance (Arbitrary a, Ord a, Enum a, Bounded a) => Arbitrary (DSL.Set a) where+ arbitrary = DSL.fromList <$> QC.vectorOf 7 arbitraryOrderedPair+ shrink x = map E.fromList (QC.shrink (E.toList x))++instance (Arbitrary a, Ord a, Enum a, Bounded a) => Arbitrary (DUSL.Set a) where+ arbitrary = do+ sz <- QC.choose (0,7)+ k <- QC.arbitrary+ foldMap (\(lo,hi) -> DUSL.singleton (Just lo) (Just hi)) <$> increasingOrderedPairsHelper sz k+ +increasingOrderedPairsHelper :: (Ord k, Enum k, Bounded k) => Int -> k -> Gen [(k,k)]+increasingOrderedPairsHelper n k = if n > 0+ then case atLeastTwoGreaterThan k of+ Nothing -> return []+ Just vals -> do+ lo <- QC.elements vals+ hi <- QC.elements (equalToOrGreaterThan lo)+ xs <- increasingOrderedPairsHelper (n - 1) hi+ return ((lo,hi) : xs)+ else return []++equalToOrGreaterThan :: (Ord a, Bounded a, Enum a) => a -> [a]+equalToOrGreaterThan a0 =+ let a1 = if a0 < maxBound then succ a0 else a0+ a2 = if a1 < maxBound then succ a1 else a1+ a3 = if a2 < maxBound then succ a2 else a2+ in [a0,a1,a2,a3]++atLeastTwoGreaterThan :: (Enum a, Bounded a, Ord a) => a -> Maybe [a]+atLeastTwoGreaterThan a0 = do+ if a0 < maxBound+ then+ let a1 = succ a0+ in if a1 < maxBound+ then+ let a2 = succ a1+ a3 = if a2 < maxBound then succ a2 else a2+ a4 = if a3 < maxBound then succ a3 else a3+ in Just [a2,a3,a4]+ else Nothing+ else Nothing++arbitraryOrderedPair :: (Ord k, Enum k, Bounded k, Arbitrary k) => Gen (k,k)+arbitraryOrderedPair = do+ a0 <- QC.arbitrary+ let a1 = if a0 < maxBound then succ a0 else a0+ a2 = if a1 < maxBound then succ a1 else a1+ a3 = if a2 < maxBound then succ a2 else a2+ a' <- QC.elements [a0,a1,a2,a3]+ return (a0,a')++arbitraryOrderedPairValue :: (Ord k, Enum k, Bounded k, Arbitrary k, Arbitrary v) => Gen (k,k,v)+arbitraryOrderedPairValue = do+ (lo,hi) <- arbitraryOrderedPair+ v <- QC.arbitrary+ return (lo,hi,v)++instance SG.Semigroup Word where+ w <> _ = w++instance SG.Semigroup Int where+ (<>) = (+)++instance Monoid Int where+ mempty = 0+ mappend = (SG.<>)+ +instance SG.Semigroup Integer where+ (<>) = (+)++instance Monoid Integer where+ mempty = 0+ mappend = (SG.<>)++deriving instance Arbitrary a => Arbitrary (SG.First a)+