diet 0.0.1 → 0.1.0.0
raw patch · 11 files changed
+1612/−357 lines, 11 filesdep +QuickCheckdep +containersdep +contiguousdep ~basesetup-changednew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: QuickCheck, containers, contiguous, diet, primitive, quickcheck-classes, tasty, tasty-quickcheck
Dependency ranges changed: base
API changes (from Hackage documentation)
- Data.Set.Diet: data Diet a
- Data.Set.Diet: data Interval a
- Data.Set.Diet: delete :: (Ord a, Enum a) => a -> Diet a -> Diet a
- Data.Set.Diet: diet :: (b -> Interval a -> b -> b) -> b -> Diet a -> b
- Data.Set.Diet: empty :: Diet a
- Data.Set.Diet: fromList :: (Foldable t, Ord a, Enum a) => t a -> Diet a
- Data.Set.Diet: insert :: (Ord a, Enum a) => a -> Diet a -> Diet a
- Data.Set.Diet: instance (Eq a, Show a) => Show (Diet a)
- Data.Set.Diet: instance (Eq a, Show a) => Show (Interval a)
- Data.Set.Diet: instance Eq a => Eq (Diet a)
- Data.Set.Diet: instance Eq a => Eq (Interval a)
- Data.Set.Diet: instance Ord a => Ord (Diet a)
- Data.Set.Diet: instance Ord a => Ord (Interval a)
- Data.Set.Diet: interval :: Ord a => a -> a -> Interval a
- Data.Set.Diet: intervalMax :: Interval a -> a
- Data.Set.Diet: intervalMin :: Interval a -> a
- Data.Set.Diet: isPointed :: Eq a => Interval a -> Bool
- Data.Set.Diet: mapD :: Ord b => (a -> b) -> Diet a -> Diet b
- Data.Set.Diet: mapI :: Ord b => (a -> b) -> Interval a -> Interval b
- Data.Set.Diet: member :: Ix a => a -> Diet a -> Bool
- Data.Set.Diet: mergeI :: (Ord a, Enum a) => Interval a -> Interval a -> Maybe (Interval a)
- Data.Set.Diet: notMember :: Ix a => a -> Diet a -> Bool
- Data.Set.Diet: point :: a -> Interval a
- Data.Set.Diet: single :: a -> Diet a
- Data.Set.Diet: singleI :: Interval a -> Diet a
- Data.Set.Diet: size :: Ix a => Diet a -> Int
- Data.Set.Diet: toList :: Ix a => Diet a -> [a]
+ Data.Diet.Map.Lifted.Lifted: data Map k v
+ Data.Diet.Map.Lifted.Lifted: fromList :: (Ord k, Enum k, Eq v) => [(k, k, v)] -> Map k v
+ Data.Diet.Map.Lifted.Lifted: fromListAppend :: (Ord k, Enum k, Semigroup v, Eq v) => [(k, k, v)] -> Map k v
+ Data.Diet.Map.Lifted.Lifted: fromListAppendN :: (Ord k, Enum k, Semigroup v, Eq v) => Int -> [(k, k, v)] -> Map k v
+ Data.Diet.Map.Lifted.Lifted: fromListN :: (Ord k, Enum k, Eq v) => Int -> [(k, k, v)] -> Map k v
+ Data.Diet.Map.Lifted.Lifted: instance (GHC.Classes.Eq k, GHC.Classes.Eq v) => GHC.Classes.Eq (Data.Diet.Map.Lifted.Lifted.Map k v)
+ Data.Diet.Map.Lifted.Lifted: instance (GHC.Classes.Ord k, GHC.Enum.Enum k, Data.Semigroup.Semigroup v, GHC.Classes.Eq v) => Data.Semigroup.Semigroup (Data.Diet.Map.Lifted.Lifted.Map k v)
+ Data.Diet.Map.Lifted.Lifted: instance (GHC.Classes.Ord k, GHC.Enum.Enum k, Data.Semigroup.Semigroup v, GHC.Classes.Eq v) => GHC.Base.Monoid (Data.Diet.Map.Lifted.Lifted.Map k v)
+ Data.Diet.Map.Lifted.Lifted: instance (GHC.Classes.Ord k, GHC.Enum.Enum k, GHC.Classes.Eq v) => GHC.Exts.IsList (Data.Diet.Map.Lifted.Lifted.Map k v)
+ Data.Diet.Map.Lifted.Lifted: instance (GHC.Show.Show k, GHC.Show.Show v) => GHC.Show.Show (Data.Diet.Map.Lifted.Lifted.Map k v)
+ Data.Diet.Map.Lifted.Lifted: lookup :: Ord k => k -> Map k v -> Maybe v
+ Data.Diet.Map.Lifted.Lifted: singleton :: Ord k => k -> k -> v -> Map k v
+ Data.Diet.Map.Unboxed.Lifted: data Map k v
+ Data.Diet.Map.Unboxed.Lifted: fromList :: (Ord k, Enum k, Prim k, Eq v) => [(k, k, v)] -> Map k v
+ Data.Diet.Map.Unboxed.Lifted: fromListAppend :: (Ord k, Enum k, Prim k, Semigroup v, Eq v) => [(k, k, v)] -> Map k v
+ Data.Diet.Map.Unboxed.Lifted: fromListAppendN :: (Ord k, Enum k, Prim k, Semigroup v, Eq v) => Int -> [(k, k, v)] -> Map k v
+ Data.Diet.Map.Unboxed.Lifted: fromListN :: (Ord k, Enum k, Prim k, Eq v) => Int -> [(k, k, v)] -> Map k v
+ Data.Diet.Map.Unboxed.Lifted: instance (Data.Primitive.Types.Prim k, GHC.Classes.Eq k, GHC.Classes.Eq v) => GHC.Classes.Eq (Data.Diet.Map.Unboxed.Lifted.Map k v)
+ Data.Diet.Map.Unboxed.Lifted: instance (Data.Primitive.Types.Prim k, GHC.Classes.Ord k, GHC.Enum.Enum k, Data.Semigroup.Semigroup v, GHC.Classes.Eq v) => Data.Semigroup.Semigroup (Data.Diet.Map.Unboxed.Lifted.Map k v)
+ Data.Diet.Map.Unboxed.Lifted: instance (Data.Primitive.Types.Prim k, GHC.Classes.Ord k, GHC.Enum.Enum k, Data.Semigroup.Semigroup v, GHC.Classes.Eq v) => GHC.Base.Monoid (Data.Diet.Map.Unboxed.Lifted.Map k v)
+ Data.Diet.Map.Unboxed.Lifted: instance (Data.Primitive.Types.Prim k, GHC.Classes.Ord k, GHC.Enum.Enum k, GHC.Classes.Eq v) => GHC.Exts.IsList (Data.Diet.Map.Unboxed.Lifted.Map k v)
+ Data.Diet.Map.Unboxed.Lifted: instance (Data.Primitive.Types.Prim k, GHC.Show.Show k, GHC.Show.Show v) => GHC.Show.Show (Data.Diet.Map.Unboxed.Lifted.Map k v)
+ Data.Diet.Map.Unboxed.Lifted: lookup :: (Prim k, Ord k) => k -> Map k v -> Maybe v
+ Data.Diet.Map.Unboxed.Lifted: singleton :: (Prim k, Ord k) => k -> k -> v -> Map k v
+ Data.Diet.Set.Lifted: aboveInclusive :: (Ord a) => a -> Set a -> Set a
+ Data.Diet.Set.Lifted: belowInclusive :: (Ord a) => a -> Set a -> Set a
+ Data.Diet.Set.Lifted: betweenInclusive :: (Ord a) => a -> a -> Set a -> Set a
+ Data.Diet.Set.Lifted: data Set a
+ Data.Diet.Set.Lifted: difference :: (Ord a, Enum a) => Set a -> Set a -> Set a
+ Data.Diet.Set.Lifted: foldr :: (a -> a -> b -> b) -> b -> Set a -> b
+ Data.Diet.Set.Lifted: fromList :: (Ord a, Enum a) => [(a, a)] -> Set a
+ Data.Diet.Set.Lifted: fromListN :: (Ord a, Enum a) => Int -> [(a, a)] -> Set a
+ Data.Diet.Set.Lifted: instance (GHC.Classes.Ord a, GHC.Enum.Enum a) => Data.Semigroup.Semigroup (Data.Diet.Set.Lifted.Set a)
+ Data.Diet.Set.Lifted: instance (GHC.Classes.Ord a, GHC.Enum.Enum a) => GHC.Base.Monoid (Data.Diet.Set.Lifted.Set a)
+ Data.Diet.Set.Lifted: instance (GHC.Classes.Ord a, GHC.Enum.Enum a) => GHC.Exts.IsList (Data.Diet.Set.Lifted.Set a)
+ Data.Diet.Set.Lifted: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Diet.Set.Lifted.Set a)
+ Data.Diet.Set.Lifted: instance GHC.Classes.Ord a => GHC.Classes.Ord (Data.Diet.Set.Lifted.Set a)
+ Data.Diet.Set.Lifted: instance GHC.Show.Show a => GHC.Show.Show (Data.Diet.Set.Lifted.Set a)
+ Data.Diet.Set.Lifted: member :: Ord a => a -> Set a -> Bool
+ Data.Diet.Set.Lifted: singleton :: Ord a => a -> a -> Set a
+ Data.Diet.Set.Unboxed: data Set a
+ Data.Diet.Set.Unboxed: fromList :: (Ord a, Enum a, Prim a) => [(a, a)] -> Set a
+ Data.Diet.Set.Unboxed: fromListN :: (Ord a, Enum a, Prim a) => Int -> [(a, a)] -> Set a
+ Data.Diet.Set.Unboxed: instance (GHC.Classes.Eq a, Data.Primitive.Types.Prim a) => GHC.Classes.Eq (Data.Diet.Set.Unboxed.Set a)
+ Data.Diet.Set.Unboxed: instance (GHC.Classes.Ord a, Data.Primitive.Types.Prim a) => GHC.Classes.Ord (Data.Diet.Set.Unboxed.Set a)
+ Data.Diet.Set.Unboxed: instance (GHC.Classes.Ord a, GHC.Enum.Enum a, Data.Primitive.Types.Prim a) => Data.Semigroup.Semigroup (Data.Diet.Set.Unboxed.Set a)
+ Data.Diet.Set.Unboxed: instance (GHC.Classes.Ord a, GHC.Enum.Enum a, Data.Primitive.Types.Prim a) => GHC.Base.Monoid (Data.Diet.Set.Unboxed.Set a)
+ Data.Diet.Set.Unboxed: instance (GHC.Classes.Ord a, GHC.Enum.Enum a, Data.Primitive.Types.Prim a) => GHC.Exts.IsList (Data.Diet.Set.Unboxed.Set a)
+ Data.Diet.Set.Unboxed: instance (GHC.Show.Show a, Data.Primitive.Types.Prim a) => GHC.Show.Show (Data.Diet.Set.Unboxed.Set a)
+ Data.Diet.Set.Unboxed: member :: (Ord a, Prim a) => a -> Set a -> Bool
+ Data.Diet.Set.Unboxed: singleton :: (Ord a, Prim a) => a -> a -> Set a
+ Data.Diet.Set.Unboxed: toList :: Prim a => Set a -> [(a, a)]
Files
- LICENSE +25/−22
- Setup.hs +0/−1
- diet.cabal +60/−35
- src/Data/Diet/Map/Internal.hs +401/−0
- src/Data/Diet/Map/Lifted/Lifted.hs +76/−0
- src/Data/Diet/Map/Unboxed/Lifted.hs +78/−0
- src/Data/Diet/Set/Internal.hs +488/−0
- src/Data/Diet/Set/Lifted.hs +114/−0
- src/Data/Diet/Set/Unboxed.hs +73/−0
- src/Data/Set/Diet.hs +0/−299
- test/Main.hs +297/−0
LICENSE view
@@ -1,27 +1,30 @@-Copyright 2012 Tony Morris+Copyright Andrew Martin, chessai (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:-1. Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.-2. 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.-3. Neither the name of the author nor the names of his contributors- may be used to endorse or promote products derived from this software- without specific prior written permission.+modification, are permitted provided that the following conditions are met: -THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 AUTHORS 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.+ * 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.
Setup.hs view
@@ -1,3 +1,2 @@ import Distribution.Simple main = defaultMain-
diet.cabal view
@@ -1,38 +1,63 @@-Name: diet-Version: 0.0.1-License: BSD3-License-File: LICENSE-Author: Tony Morris <ʇǝu˙sıɹɹoɯʇ@ןןǝʞsɐɥ>-Maintainer: Tony Morris-Copyright: Tony Morris-Synopsis: Discrete Interval Encoding Tree-Category: Development-Description: Discrete Interval Encoding Tree described by Martin Erwig in /Diets for Fat Sets, January 1993/.-Homepage: https://github.com/tonymorris/diet-Cabal-Version: >= 1.6-Build-Type: Simple--Source-Repository head- Type: git- Location: git@github.com:tonymorris/diet.git--Flag small_base- Description: Choose the new, split-up base package.--Library- Build-Depends:- base < 5 && >= 3-- GHC-Options:- -Wall-- Hs-Source-Dirs:- src+name:+ diet+version:+ 0.1.0.0+synopsis:+ Discrete Interval Encoding Trees+description:+ Discrete Interval Encoding Tree described by Martin Erwig in /Diets for Fat Sets, January 1993/.+homepage:+ https://github.com/andrewthad/primitive-containers/+bug-reports:+ https://github.com/andrewthad/primitive-containers/issues+author:+ Andrew Martin, chessai+maintainer:+ andrew.thaddeus@gmail.com, chessai1996@gmail.com+copyright:+ 2018 Andrew Martin, 2018 chessai+license:+ BSD3+license-file:+ LICENSE+build-type:+ Simple+cabal-version: 2.0 - Exposed-Modules:- Data.Set.Diet+source-repository head+ type: git+ location: https://github.com/andrewthad/primitive-containers - GHC-Options: - -Wall- -funbox-strict-fields+library+ hs-source-dirs:+ src+ build-depends:+ base >=4.9 && <5+ , primitive >= 0.6.4+ , contiguous+ exposed-modules:+ Data.Diet.Set.Lifted+ Data.Diet.Set.Unboxed+ Data.Diet.Map.Lifted.Lifted+ Data.Diet.Map.Unboxed.Lifted+ other-modules:+ Data.Diet.Map.Internal+ Data.Diet.Set.Internal + ghc-options: -Wall -O2+ default-language: Haskell2010 +test-suite test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Main.hs+ build-depends:+ base+ , QuickCheck+ , containers+ , diet+ , primitive+ , quickcheck-classes >= 0.4.11.1+ , tasty+ , tasty-quickcheck+ ghc-options: -Wall -O2+ default-language: Haskell2010
+ src/Data/Diet/Map/Internal.hs view
@@ -0,0 +1,401 @@+{-# 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++-- 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 = go [] where+ go :: [Map karr varr k v] -> [Map karr varr k v] -> Map karr varr k v+ go !stack [] = foldl' (appendWith combine) empty (L.reverse stack)+ go !stack (x : xs) = if size x > 0+ then go (pushStack x stack) xs+ else go stack xs+ pushStack :: Map karr varr k v -> [Map karr varr k v] -> [Map karr varr k v]+ pushStack x [] = [x]+ pushStack x (s : ss) = if size x >= size s+ then pushStack (appendWith combine s x) ss+ else x : s : ss++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,76 @@+{-# 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.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,78 @@+{-# 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.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/Internal.hs view
@@ -0,0 +1,488 @@+{-# 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+ -- splitting+ , aboveInclusive+ , belowInclusive+ , 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++-- 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.+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 = go [] where+ go :: [Set arr a] -> [Set arr a] -> Set arr a+ go !stack [] = F.foldl' append empty stack+ go !stack (x : xs) = if size x > 0+ then go (pushStack x stack) xs+ else go stack xs+ pushStack :: Set arr a -> [Set arr a] -> [Set arr a]+ pushStack x [] = [x]+ pushStack x (s : ss) = if size x >= size s+ then pushStack (append x s) ss+ else x : s : ss++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 #-}++-- not exported since it provides an index into the underlying set+-- Right means that the needle was found. The index provided is the+-- index of the range that contains it [O,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 <- 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 <- 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 <- 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)++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+ 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)++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)/ Lookup the value at a key in the map.+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,73 @@+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -O2 #-}+module Data.Diet.Set.Unboxed+ ( Set+ , singleton+ , member+ -- * List Conversion+ , toList+ , fromList+ , fromListN+ ) where++import Prelude hiding (lookup,map)++import Data.Semigroup (Semigroup)+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+
− src/Data/Set/Diet.hs
@@ -1,299 +0,0 @@--- | Discrete Interval Encoding Tree described by Martin Erwig in /Diets for Fat Sets, January 1993/.-module Data.Set.Diet(- Interval-, point-, interval-, intervalMin-, intervalMax-, mergeI-, isPointed-, mapI-, Diet-, member-, notMember-, insert-, delete-, empty-, single-, singleI-, size-, diet-, toList-, fromList-, mapD-) where--import Data.Ix-import Data.Foldable(foldl', Foldable)---- | An interval with discrete values between.-data Interval a =- Interval a a- deriving (Eq, Ord)--instance (Eq a, Show a) => Show (Interval a) where- show (Interval a1 a2) =- '[' : show a1 ++ (if a1 == a2 then [] else '|' : show a2) ++ "]"---- | An interval with the same minimum and maximum.-point ::- a- -> Interval a-point a =- Interval a a---- | Construct an interval ensuring that the minimum is less than or equal to maximum.-interval ::- Ord a =>- a- -> a- -> Interval a-interval a1 a2 =- if a1 <= a2- then- Interval a1 a2- else- Interval a2 a1---- | The minimum of the interval.-intervalMin ::- Interval a- -> a-intervalMin (Interval a _) =- a---- | The maximum of the interval.-intervalMax ::- Interval a- -> a-intervalMax (Interval _ a) =- a---- | Merge two intervals if they are overlapping or adjacent.-mergeI ::- (Ord a, Enum a) =>- Interval a- -> Interval a- -> Maybe (Interval a)-mergeI (Interval a1 a2) (Interval aa1 aa2) =- if (a1 <= aa2 && succ a2 >= aa1) || (aa1 <= a2 && succ aa2 >= a1)- then- Just $ Interval (min a1 aa1) (max a2 aa2)- else- Nothing---- | Returns whether or not the interval has the same minimum and maximum.-isPointed ::- Eq a =>- Interval a- -> Bool-isPointed (Interval a1 a2) =- a1 == a2---- | Map a function across the minimum and maximum of the interval.-mapI ::- Ord b =>- (a -> b)- -> Interval a- -> Interval b-mapI f (Interval a1 a2) =- interval (f a1) (f a2)---- | A Discrete Interval Encoding Tree.-data Diet a =- Empty- | Node (Diet a) (Interval a) (Diet a)- deriving (Eq, Ord)--instance (Eq a, Show a) => Show (Diet a) where- showsPrec _ Empty =- id- showsPrec n (Node l i r) =- showsPrec n l . shows i . showsPrec n r---- | Test for membership in the interval tree.-member ::- Ix a =>- a- -> Diet a- -> Bool-member _ Empty =- False-member x (Node l (Interval a1 a2) r) =- inRange (a1, a2) x || member x (if x < a1 then l else r)---- | Test for non-membership in the interval tree.-notMember ::- Ix a =>- a- -> Diet a- -> Bool-notMember a =- not . member a---- | Insert an element into the interval tree.-insert ::- (Ord a, Enum a) =>- a- -> Diet a- -> Diet a-insert x Empty =- Node Empty (point x) Empty-insert x d@(Node l i@(Interval a1 a2) r)- | x < a1 =- if succ x == a1- then- let joinLeft md@(Node Empty _ _) =- md- joinLeft (Node ml mi@(Interval ma1 ma2) mr) =- let (ml', Interval ml1 ml2) = splitMax ml- in if succ ml2 == ma1- then- Node ml' (Interval ml1 ma2) mr- else- Node ml mi mr- joinLeft Empty =- error "Broken invariant @ Data.Set.Diet#joinLeft"- in joinLeft (Node l (Interval x a2) r)- else- Node (insert x l) i r- | x > a2 =- if succ a2 == x- then- let splitMin (Node Empty mi mr) =- (mr, mi)- splitMin (Node ml mi mr) =- let (md, mi') = splitMin ml- in (Node md mi mr, mi')- splitMin Empty =- error "Broken invariant @ Data.Set.Diet#splitMin"- joinRight jd@(Node _ _ Empty) =- jd- joinRight (Node jl ji@(Interval ja1 ja2) jr) =- let (jr', Interval jr1 jr2) = splitMin jr- in if succ ja2 == jr1- then- Node jl (Interval ja1 jr2) jr'- else- Node jl ji jr- joinRight Empty =- error "Broken invariant @ Data.Set.Diet#joinRight"- in joinRight (Node l (Interval a1 x) r)- else- Node l i (insert x r)- | otherwise =- d---- | Delete an element from the interval tree.-delete ::- (Ord a, Enum a) =>- a- -> Diet a- -> Diet a-delete _ Empty =- Empty-delete x (Node l i@(Interval a1 a2) r)- | x < a1 =- Node (delete x l) i r- | x > a2 =- Node l i (delete x r)- | x == a1 =- let merge ml Empty =- ml- merge Empty mr =- mr- merge ml mr =- let (ml', mi) = splitMax ml- in Node ml' mi mr- in if isPointed i- then- merge l r- else- Node l (Interval (succ a1) a2) r- | x == a2 =- Node l (Interval a1 (pred a2)) r- | otherwise =- Node l (Interval a1 (pred x)) (Node Empty (Interval (succ x) a2) r)---- | Construct an interval tree with no elements.-empty ::- Diet a-empty =- Empty---- | Construct an interval tree with a single element.-single ::- a- -> Diet a-single a =- Node Empty (point a) Empty---- | Construct an interval tree with a single interval.-singleI ::- Interval a- -> Diet a-singleI a =- Node Empty a Empty---- | Return the number of elements in the interval tree.-size ::- Ix a =>- Diet a- -> Int-size Empty =- 0-size (Node l (Interval a1 a2) r) =- sum [size l, rangeSize (a1, a2), size r]---- | Fold on the interval tree.-diet ::- (b -> Interval a -> b -> b)- -> b- -> Diet a- -> b-diet _ z Empty =- z-diet f z (Node l i r) =- f (diet f z l) i (diet f z r)---- | Return all elements of the interval tree as a list.-toList ::- Ix a =>- Diet a- -> [a]-toList =- diet (\l (Interval a1 a2) r -> concat [l, range (a1, a2), r]) []---- | Construct an interval tree with the elements of the list.-fromList ::- (Foldable t, Ord a, Enum a) =>- t a- -> Diet a-fromList =- foldl' (flip insert) Empty---- | Map a function across the interval tree.-mapD ::- Ord b =>- (a -> b)- -> Diet a- -> Diet b-mapD _ Empty =- Empty-mapD f (Node l i r) =- Node (mapD f l) (mapI f i) (mapD f r)---- BEGIN not exported--splitMax ::- Diet a- -> (Diet a, Interval a)-splitMax (Node l i Empty) =- (l, i)-splitMax (Node l i r) =- let (d, i') = splitMax r- in (Node l i d, i')-splitMax Empty =- error "Broken invariant @ Data.Set.Diet#splitMax"---- END not exported
+ test/Main.hs view
@@ -0,0 +1,297 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# 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.ST (ST)+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.Diet.Map.Unboxed.Lifted as DMUL+import qualified Data.Diet.Map.Lifted.Lifted as DMLL+import qualified Data.Diet.Set.Lifted as DSL++main :: IO ()+main = defaultMain $ testGroup "Data"+ [ testGroup "Diet"+ [ 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 "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++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) SG.<> s)+ mempty++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 SG.<> 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, SG.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 k, Ord k, Enum k, Bounded k, Arbitrary v, SG.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, Prim k, Ord k, Enum k, Bounded k, Arbitrary v, SG.Semigroup v, Eq v) => Arbitrary (DMUL.Map k v) where+ arbitrary = DMUL.fromListAppend <$> QC.vectorOf 10 arbitraryOrderedPairValue+ 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))+ +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.<>)+