packages feed

pomaps 0.0.0.4 → 0.0.1.0

raw patch · 15 files changed

+903/−820 lines, 15 filesdep ~containers

Dependency ranges changed: containers

Files

CHANGELOG.md view
@@ -1,7 +1,7 @@-# Change log
-
-`pomaps` follows the [PVP][1].
-The change log is available [on GitHub][2].
-
-[1]: https://pvp.haskell.org/
-[2]: https://github.com/sgraf812/pomaps/releases
+# Change log++`pomaps` follows the [PVP][1].+The change log is available [on GitHub][2].++[1]: https://pvp.haskell.org/+[2]: https://github.com/sgraf812/pomaps/releases
− README.md
@@ -1,16 +0,0 @@-# [`pomaps`][pomaps] [![Build Status](https://travis-ci.org/sgraf812/pomaps.svg?branch=master)](https://travis-ci.org/sgraf812/pomaps) [![Hackage](https://img.shields.io/hackage/v/pomaps.svg)](https://hackage.haskell.org/package/pomaps)
-
-Reasonably fast maps (and possibly sets) based on keys satisfying [`PartialOrd`](https://hackage.haskell.org/package/lattices-1.6.0/docs/Algebra-PartialOrd.html#t:PartialOrd).
-
-This package tries to load off as much work as possible to the excellent [`containers`](https://hackage.haskell.org/package/containers) library, in order to achieve acceptable performance.
-The interface is kept as similar to [`Data.Map.{Strict,Lazy}`](https://hackage.haskell.org/package/containers/docs/Data-Map-Strict.html) as possible, which is an excuse for somewhat lacking documentation.
-
-`POMap`s basically store a decomposition of totally ordered chains (e.g. something `Map`s can handle).
-Functionality and strictness properties should be pretty much covered by the testsuite.
-But it's not battle-tested yet, so if you encounter space leaks in the implementation, let me know.
-
-A rather naive implementation leads to `O(w*n*log n)` lookups, where `w` is the width of the decomposition (which should be the size of the biggest anti-chain).
-This is enough for me at the moment to get things going, but there is room for improvement ([Sorting and Selection in Posets](https://arxiv.org/abs/0707.1532)).
-Let me know if things are too slow and I'll see what I can do!
-
-[pomaps]: https://github.com/sgraf812/pomaps
bench/Main.hs view
@@ -1,77 +1,77 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-
-import           Algebra.PartialOrd
-import           Control.Arrow      (first)
-import           Control.DeepSeq
-import           Criterion.Main
-import qualified Data.POMap.Lazy    as L
-import qualified Data.POMap.Strict  as S
-import qualified Data.Vector        as V
-import           System.Random
-
-newtype Divisibility
-  = Div { _unDiv :: Int }
-  deriving (Eq, Num, Show, Read, NFData)
-
-instance PartialOrd Divisibility where
-  leq (Div a) (Div b) = b `mod` a == 0
-
-instance Bounded Divisibility where
-  minBound = Div 1
-  maxBound = Div maxBound
-
-instance Random Divisibility where
-  randomR (Div l, Div h) = first Div . randomR (l, h)
-  random = randomR (minBound, maxBound)
-
-genElems :: Int -> [(Divisibility, Int)]
-genElems n = zip (randoms (mkStdGen 0) :: [Divisibility]) [1 :: Int .. n]
-
-main :: IO ()
-main = defaultMain
-  [ bgroup "insert"
-      [ bgroup s
-          [ env
-            (pure (genElems n))
-            (bench (show n) . whnf (foldr (uncurry insert) L.empty))
-          | n <- [100, 1000, 2000]
-          ]
-      | (s, insert) <- [("Lazy", L.insert), ("Strict", S.insert)]
-      ]
-  , bgroup "lookup(present)"
-      [ env
-        (let elems = genElems n
-             m = L.fromList elems
-             k = fst (elems !! (length elems `div` 2))
-         in pure (m, k))
-        (\ ~(m, k) -> bench (show n) (whnf (L.lookup k) m))
-      | n <- [100, 1000, 2000]
-      ]
-  , bgroup "lookup(absent)"
-      [ env
-        (let elems = genElems n
-             m = L.fromList elems
-             k = fst (random (mkStdGen (-1)))
-         in pure (m, k))
-        (\ ~(m, k) -> bench (show n) (whnf (L.lookup k) m))
-      | n <- [100, 1000, 2000]
-      ]
-  , bgroup "Vector.lookup(present)"
-      [ env
-        (let elems = genElems n
-             v = V.fromListN n elems
-             k = fst (elems !! (length elems `div` 2))
-         in pure (v, k))
-        (\ ~(v, k) -> bench (show n) (whnf (V.find ((== k) . fst)) v))
-      | n <- [100, 1000, 2000]
-      ]
-  , bgroup "Vector.lookup(absent)"
-      [ env
-        (let elems = genElems n
-             v = V.fromListN n elems
-             k = fst (random (mkStdGen (-1)))
-         in pure (v, k))
-        (\ ~(v, k) -> bench (show n) (whnf (V.find ((== k) . fst)) v))
-      | n <- [100, 1000, 2000]
-      ]
-  ]
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++import           Algebra.PartialOrd+import           Control.Arrow      (first)+import           Control.DeepSeq+import           Criterion.Main+import qualified Data.POMap.Lazy    as L+import qualified Data.POMap.Strict  as S+import qualified Data.Vector        as V+import           System.Random++newtype Divisibility+  = Div { _unDiv :: Int }+  deriving (Eq, Num, Show, Read, NFData)++instance PartialOrd Divisibility where+  leq (Div a) (Div b) = b `mod` a == 0++instance Bounded Divisibility where+  minBound = Div 1+  maxBound = Div maxBound++instance Random Divisibility where+  randomR (Div l, Div h) = first Div . randomR (l, h)+  random = randomR (minBound, maxBound)++genElems :: Int -> [(Divisibility, Int)]+genElems n = zip (randoms (mkStdGen 0) :: [Divisibility]) [1 :: Int .. n]++main :: IO ()+main = defaultMain+  [ bgroup "insert"+      [ bgroup s+          [ env+            (pure (genElems n))+            (bench (show n) . whnf (foldr (uncurry insert) L.empty))+          | n <- [100, 1000, 2000]+          ]+      | (s, insert) <- [("Lazy", L.insert), ("Strict", S.insert)]+      ]+  , bgroup "lookup(present)"+      [ env+        (let elems = genElems n+             m = L.fromList elems+             k = fst (elems !! (length elems `div` 2))+         in pure (m, k))+        (\ ~(m, k) -> bench (show n) (whnf (L.lookup k) m))+      | n <- [100, 1000, 2000]+      ]+  , bgroup "lookup(absent)"+      [ env+        (let elems = genElems n+             m = L.fromList elems+             k = fst (random (mkStdGen (-1)))+         in pure (m, k))+        (\ ~(m, k) -> bench (show n) (whnf (L.lookup k) m))+      | n <- [100, 1000, 2000]+      ]+  , bgroup "Vector.lookup(present)"+      [ env+        (let elems = genElems n+             v = V.fromListN n elems+             k = fst (elems !! (length elems `div` 2))+         in pure (v, k))+        (\ ~(v, k) -> bench (show n) (whnf (V.find ((== k) . fst)) v))+      | n <- [100, 1000, 2000]+      ]+  , bgroup "Vector.lookup(absent)"+      [ env+        (let elems = genElems n+             v = V.fromListN n elems+             k = fst (random (mkStdGen (-1)))+         in pure (v, k))+        (\ ~(v, k) -> bench (show n) (whnf (V.find ((== k) . fst)) v))+      | n <- [100, 1000, 2000]+      ]+  ]
lattices/Algebra/PartialOrd.hs view
@@ -1,154 +1,154 @@-{-# LANGUAGE Safe #-}
-----------------------------------------------------------------------------
--- |
--- Module      :  Algebra.PartialOrd
--- Copyright   :  (C) 2010-2015 Maximilian Bolingbroke
--- License     :  BSD-3-Clause (see the file LICENSE)
---
--- Maintainer  :  Oleg Grenrus <oleg.grenrus@iki.fi>
---
-----------------------------------------------------------------------------
-module Algebra.PartialOrd (
-    -- * Partial orderings
-    PartialOrd(..),
-    partialOrdEq,
-
-    -- * Fixed points of chains in partial orders
-    lfpFrom, unsafeLfpFrom,
-    gfpFrom, unsafeGfpFrom
-  ) where
-
-import qualified Data.IntMap as IM
-import qualified Data.IntSet as IS
-import qualified Data.Map    as M
-import qualified Data.Set    as S
-import           Data.Void   (Void)
-
--- | A partial ordering on sets
--- (<http://en.wikipedia.org/wiki/Partially_ordered_set>) is a set equipped
--- with a binary relation, `leq`, that obeys the following laws
---
--- @
--- Reflexive:     a ``leq`` a
--- Antisymmetric: a ``leq`` b && b ``leq`` a ==> a == b
--- Transitive:    a ``leq`` b && b ``leq`` c ==> a ``leq`` c
--- @
---
--- Two elements of the set are said to be `comparable` when they are are
--- ordered with respect to the `leq` relation. So
---
--- @
--- `comparable` a b ==> a ``leq`` b || b ``leq`` a
--- @
---
--- If `comparable` always returns true then the relation `leq` defines a
--- total ordering (and an `Ord` instance may be defined). Any `Ord` instance is
--- trivially an instance of `PartialOrd`. 'Algebra.Lattice.Ordered' provides a
--- convenient wrapper to satisfy 'PartialOrd' given 'Ord'.
---
--- As an example consider the partial ordering on sets induced by set
--- inclusion.  Then for sets `a` and `b`,
---
--- @
--- a ``leq`` b
--- @
---
--- is true when `a` is a subset of `b`.  Two sets are `comparable` if one is a
--- subset of the other. Concretely
---
--- @
--- a = {1, 2, 3}
--- b = {1, 3, 4}
--- c = {1, 2}
---
--- a ``leq`` a = `True`
--- a ``leq`` b = `False`
--- a ``leq`` c = `False`
--- b ``leq`` a = `False`
--- b ``leq`` b = `True`
--- b ``leq`` c = `False`
--- c ``leq`` a = `True`
--- c ``leq`` b = `False`
--- c ``leq`` c = `True`
---
--- `comparable` a b = `False`
--- `comparable` a c = `True`
--- `comparable` b c = `False`
--- @
-class Eq a => PartialOrd a where
-    -- | The relation that induces the partial ordering
-    leq :: a -> a -> Bool
-
-    -- | Whether two elements are ordered with respect to the relation. A
-    -- default implementation is given by
-    --
-    -- > comparable x y = leq x y || leq y x
-    comparable :: a -> a -> Bool
-    comparable x y = leq x y || leq y x
-
--- | The equality relation induced by the partial-order structure. It must obey
--- the laws
--- @
--- Reflexive:  a == a
--- Transitive: a == b && b == c ==> a == c
--- @
-partialOrdEq :: PartialOrd a => a -> a -> Bool
-partialOrdEq x y = leq x y && leq y x
-
-instance PartialOrd () where
-    leq _ _ = True
-
-instance PartialOrd Void where
-    leq _ _ = True
-
-instance Ord a => PartialOrd (S.Set a) where
-    leq = S.isSubsetOf
-
-instance PartialOrd IS.IntSet where
-    leq = IS.isSubsetOf
-
-instance (Ord k, PartialOrd v) => PartialOrd (M.Map k v) where
-    leq = M.isSubmapOfBy leq
-
-instance PartialOrd v => PartialOrd (IM.IntMap v) where
-    leq = IM.isSubmapOfBy leq
-
-instance (PartialOrd a, PartialOrd b) => PartialOrd (a, b) where
-    -- NB: *not* a lexical ordering. This is because for some component partial orders, lexical
-    -- ordering is incompatible with the transitivity axiom we require for the derived partial order
-    (x1, y1) `leq` (x2, y2) = x1 `leq` x2 && y1 `leq` y2
-
--- | Least point of a partially ordered monotone function. Checks that the function is monotone.
-lfpFrom :: PartialOrd a => a -> (a -> a) -> a
-lfpFrom = lfpFrom' leq
-
--- | Least point of a partially ordered monotone function. Does not checks that the function is monotone.
-unsafeLfpFrom :: Eq a => a -> (a -> a) -> a
-unsafeLfpFrom = lfpFrom' (\_ _ -> True)
-
-{-# INLINE lfpFrom' #-}
-lfpFrom' :: Eq a => (a -> a -> Bool) -> a -> (a -> a) -> a
-lfpFrom' check init_x f = go init_x
-  where go x | x' == x      = x
-             | x `check` x' = go x'
-             | otherwise    = error "lfpFrom: non-monotone function"
-          where x' = f x
-
-
--- | Greatest fixed point of a partially ordered antinone function. Checks that the function is antinone.
-{-# INLINE gfpFrom #-}
-gfpFrom :: PartialOrd a => a -> (a -> a) -> a
-gfpFrom = gfpFrom' leq
-
--- | Greatest fixed point of a partially ordered antinone function. Does not check that the function is antinone.
-{-# INLINE unsafeGfpFrom #-}
-unsafeGfpFrom :: Eq a => a -> (a -> a) -> a
-unsafeGfpFrom = gfpFrom' (\_ _ -> True)
-
-{-# INLINE gfpFrom' #-}
-gfpFrom' :: Eq a => (a -> a -> Bool) -> a -> (a -> a) -> a
-gfpFrom' check init_x f = go init_x
-  where go x | x' == x      = x
-             | x' `check` x = go x'
-             | otherwise    = error "gfpFrom: non-antinone function"
-          where x' = f x
+{-# LANGUAGE Safe #-}+----------------------------------------------------------------------------+-- |+-- Module      :  Algebra.PartialOrd+-- Copyright   :  (C) 2010-2015 Maximilian Bolingbroke+-- License     :  BSD-3-Clause (see the file LICENSE)+--+-- Maintainer  :  Oleg Grenrus <oleg.grenrus@iki.fi>+--+----------------------------------------------------------------------------+module Algebra.PartialOrd (+    -- * Partial orderings+    PartialOrd(..),+    partialOrdEq,++    -- * Fixed points of chains in partial orders+    lfpFrom, unsafeLfpFrom,+    gfpFrom, unsafeGfpFrom+  ) where++import qualified Data.IntMap as IM+import qualified Data.IntSet as IS+import qualified Data.Map    as M+import qualified Data.Set    as S+import           Data.Void   (Void)++-- | A partial ordering on sets+-- (<http://en.wikipedia.org/wiki/Partially_ordered_set>) is a set equipped+-- with a binary relation, `leq`, that obeys the following laws+--+-- @+-- Reflexive:     a ``leq`` a+-- Antisymmetric: a ``leq`` b && b ``leq`` a ==> a == b+-- Transitive:    a ``leq`` b && b ``leq`` c ==> a ``leq`` c+-- @+--+-- Two elements of the set are said to be `comparable` when they are are+-- ordered with respect to the `leq` relation. So+--+-- @+-- `comparable` a b ==> a ``leq`` b || b ``leq`` a+-- @+--+-- If `comparable` always returns true then the relation `leq` defines a+-- total ordering (and an `Ord` instance may be defined). Any `Ord` instance is+-- trivially an instance of `PartialOrd`. 'Algebra.Lattice.Ordered' provides a+-- convenient wrapper to satisfy 'PartialOrd' given 'Ord'.+--+-- As an example consider the partial ordering on sets induced by set+-- inclusion.  Then for sets `a` and `b`,+--+-- @+-- a ``leq`` b+-- @+--+-- is true when `a` is a subset of `b`.  Two sets are `comparable` if one is a+-- subset of the other. Concretely+--+-- @+-- a = {1, 2, 3}+-- b = {1, 3, 4}+-- c = {1, 2}+--+-- a ``leq`` a = `True`+-- a ``leq`` b = `False`+-- a ``leq`` c = `False`+-- b ``leq`` a = `False`+-- b ``leq`` b = `True`+-- b ``leq`` c = `False`+-- c ``leq`` a = `True`+-- c ``leq`` b = `False`+-- c ``leq`` c = `True`+--+-- `comparable` a b = `False`+-- `comparable` a c = `True`+-- `comparable` b c = `False`+-- @+class Eq a => PartialOrd a where+    -- | The relation that induces the partial ordering+    leq :: a -> a -> Bool++    -- | Whether two elements are ordered with respect to the relation. A+    -- default implementation is given by+    --+    -- > comparable x y = leq x y || leq y x+    comparable :: a -> a -> Bool+    comparable x y = leq x y || leq y x++-- | The equality relation induced by the partial-order structure. It must obey+-- the laws+-- @+-- Reflexive:  a == a+-- Transitive: a == b && b == c ==> a == c+-- @+partialOrdEq :: PartialOrd a => a -> a -> Bool+partialOrdEq x y = leq x y && leq y x++instance PartialOrd () where+    leq _ _ = True++instance PartialOrd Void where+    leq _ _ = True++instance Ord a => PartialOrd (S.Set a) where+    leq = S.isSubsetOf++instance PartialOrd IS.IntSet where+    leq = IS.isSubsetOf++instance (Ord k, PartialOrd v) => PartialOrd (M.Map k v) where+    leq = M.isSubmapOfBy leq++instance PartialOrd v => PartialOrd (IM.IntMap v) where+    leq = IM.isSubmapOfBy leq++instance (PartialOrd a, PartialOrd b) => PartialOrd (a, b) where+    -- NB: *not* a lexical ordering. This is because for some component partial orders, lexical+    -- ordering is incompatible with the transitivity axiom we require for the derived partial order+    (x1, y1) `leq` (x2, y2) = x1 `leq` x2 && y1 `leq` y2++-- | Least point of a partially ordered monotone function. Checks that the function is monotone.+lfpFrom :: PartialOrd a => a -> (a -> a) -> a+lfpFrom = lfpFrom' leq++-- | Least point of a partially ordered monotone function. Does not checks that the function is monotone.+unsafeLfpFrom :: Eq a => a -> (a -> a) -> a+unsafeLfpFrom = lfpFrom' (\_ _ -> True)++{-# INLINE lfpFrom' #-}+lfpFrom' :: Eq a => (a -> a -> Bool) -> a -> (a -> a) -> a+lfpFrom' check init_x f = go init_x+  where go x | x' == x      = x+             | x `check` x' = go x'+             | otherwise    = error "lfpFrom: non-monotone function"+          where x' = f x+++-- | Greatest fixed point of a partially ordered antinone function. Checks that the function is antinone.+{-# INLINE gfpFrom #-}+gfpFrom :: PartialOrd a => a -> (a -> a) -> a+gfpFrom = gfpFrom' leq++-- | Greatest fixed point of a partially ordered antinone function. Does not check that the function is antinone.+{-# INLINE unsafeGfpFrom #-}+unsafeGfpFrom :: Eq a => a -> (a -> a) -> a+unsafeGfpFrom = gfpFrom' (\_ _ -> True)++{-# INLINE gfpFrom' #-}+gfpFrom' :: Eq a => (a -> a -> Bool) -> a -> (a -> a) -> a+gfpFrom' check init_x f = go init_x+  where go x | x' == x      = x+             | x' `check` x = go x'+             | otherwise    = error "gfpFrom: non-antinone function"+          where x' = f x
pomaps.cabal view
@@ -1,5 +1,5 @@ name:           pomaps-version:        0.0.0.4+version:        0.0.1.0 synopsis:       Maps and sets of partial orders category:       Data Structures homepage:       https://github.com/sgraf812/pomaps#readme@@ -13,16 +13,15 @@ extra-source-files:     CHANGELOG.md     LICENSE.md-    README.md     stack.yaml  description:   Maps (and sets) indexed by keys satisfying <https://hackage.haskell.org/package/lattices/docs/Algebra-PartialOrd.html#t:PartialOrd PartialOrd>.   .   The goal is to provide asymptotically better data structures than simple association lists or lookup tables.-  Asymptotics depend on the partial order used as keys, its /width/ \(w\) specifically (the size of the biggest anti-chain).+  Asymptotics depend on the partial order used as keys, its width /w/ specifically (the size of the biggest anti-chain).   .-  For partial orders with great width, this package won't provide any benefit over using association lists, so benchmark for your use-case!+  For partial orders of great width, this package won't provide any benefit over using association lists, so benchmark for your use-case!  source-repository head   type: git@@ -42,10 +41,10 @@     , ghc-prim >= 0.4 && < 0.6     , deepseq >= 1.1 && < 1.5     -- We depend on the internal modules of containers, -    -- so we have to track development real close.+    -- so we have to track development really close.     -- Data.Map.Internal is only available since 0.5.9,     -- of which 0.5.9.2 is the first safe version-    , containers >= 0.5.9.2 && <= 0.5.11.0+    , containers >= 0.5.9.2 && <= 0.6.0.1   if !flag(no-lattices)     build-depends:      -- We need PartialOrd instances for ()
src/Data/POMap/Internal.hs view
@@ -103,8 +103,7 @@ instance (PartialOrd k, Read k, Read e) => Read (POMap k e) where
   readPrec = parens $ prec 10 $ do
     Ident "fromList" <- lexP
-    xs <- readPrec
-    return (fromListImpl (proxy# :: Proxy# 'Lazy) xs)
+    fromListImpl (proxy# :: Proxy# 'Lazy) <$> readPrec
 
   readListPrec = readListPrecDefault
 
@@ -1113,6 +1112,47 @@   . unzip
   . fmap (Map.partitionWithKey p)
   $ d
+
+-- | \(\mathcal{O}(log n)\). Take while a predicate on the keys holds.
+-- The user is responsible for ensuring that for all keys @j@ and @k@ in the map,
+-- @j \< k ==\> p j \>= p k@. See note at 'spanAntitone'.
+--
+-- @
+-- takeWhileAntitone p = 'filterWithKey' (\k _ -> p k)
+-- @
+--
+-- @since 0.0.1.0
+takeWhileAntitone :: (k -> Bool) -> POMap k v -> POMap k v
+takeWhileAntitone p = mkPOMap . fmap (Map.Strict.takeWhileAntitone p) . chainDecomposition
+
+-- | \(\mathcal{O}(log n)\). Drop while a predicate on the keys holds.
+-- The user is responsible for ensuring that for all keys @j@ and @k@ in the map,
+-- @j \< k ==\> p j \>= p k@. See note at 'spanAntitone'.
+--
+-- @
+-- dropWhileAntitone p = 'filterWithKey' (\k -> not (p k))
+-- @
+--
+-- @since 0.0.1.0
+dropWhileAntitone :: (k -> Bool) -> POMap k v -> POMap k v
+dropWhileAntitone p = mkPOMap . fmap (Map.Strict.dropWhileAntitone p) . chainDecomposition
+
+-- | \(\mathcal{O}(log n)\). Divide a map at the point where a predicate on the keys stops holding.
+-- The user is responsible for ensuring that for all keys @j@ and @k@ in the map,
+-- @j \< k ==\> p j \>= p k@.
+--
+-- @
+-- spanAntitone p xs = 'partitionWithKey' (\k _ -> p k) xs
+-- @
+--
+-- Note: if @p@ is not actually antitone, then @spanAntitone@ will split the map
+-- at some /unspecified/ point where the predicate switches from holding to not
+-- holding (where the predicate is seen to hold before the first key and to fail
+-- after the last key).
+--
+-- @since 0.0.1.0
+spanAntitone :: (k -> Bool) -> POMap k v -> (POMap k v, POMap k v)
+spanAntitone p = (mkPOMap *** mkPOMap) . unzip . fmap (Map.Strict.spanAntitone p) . chainDecomposition
 
 mapMaybe :: SingIAreWeStrict s => Proxy# s -> (a -> Maybe b) -> POMap k a -> POMap k b
 mapMaybe s f = mapMaybeWithKey s (const f)
src/Data/POMap/Lazy.hs view
@@ -171,6 +171,10 @@   , Impl.partition
   , Impl.partitionWithKey
 
+  , Impl.takeWhileAntitone
+  , Impl.dropWhileAntitone
+  , Impl.spanAntitone
+
   , mapMaybe
   , mapMaybeWithKey
   , mapEither
src/Data/POMap/Strict.hs view
@@ -175,6 +175,10 @@   , Impl.partition
   , Impl.partitionWithKey
 
+  , Impl.takeWhileAntitone
+  , Impl.dropWhileAntitone
+  , Impl.spanAntitone
+
   , mapMaybe
   , mapMaybeWithKey
   , mapEither
src/Data/POSet.hs view
@@ -1,117 +1,117 @@--- |
--- Module      :  Data.POSet
--- Copyright   :  (c) Sebastian Graf 2017
--- License     :  MIT
--- Maintainer  :  sgraf1337@gmail.com
--- Portability :  portable
---
--- A reasonably efficient implementation of partially ordered sets.
---
--- These modules are intended to be imported qualified, to avoid name
--- clashes with Prelude functions, e.g.
---
--- > import qualified Data.POSet as POSet
---
--- The implementation of 'POSet' is based on a decomposition of
--- chains (totally ordered submaps), inspired by
--- [\"Sorting and Selection in Posets\"](https://arxiv.org/abs/0707.1532).
---
--- Operation comments contain the operation time complexity in
--- [Big-O notation](http://en.wikipedia.org/wiki/Big_O_notation) and
--- commonly refer to two characteristics of the poset from which keys are drawn:
--- The number of elements in the set \(n\) and the /width/ \(w\) of the poset,
--- referring to the size of the biggest anti-chain (set of incomparable elements).
---
--- Generally speaking, lookup and mutation operations incur an additional
--- factor of \(\mathcal{O}(w)\) compared to their counter-parts in "Data.Set".
---
--- Note that for practical applications, the width of the poset should be
--- in the order of \(w\in \mathcal{O}(\frac{n}{\log n})\), otherwise a simple lookup list
--- is asymptotically superior.
--- Even if that holds, the constants might be too big to be useful for any \(n\) that can
--- can happen in practice.
---
--- The following examples assume the following definitions for a set on the divisibility
--- relation on `Int`egers:
---
--- @
--- {-\# LANGUAGE GeneralizedNewtypeDeriving \#-}
---
--- import           Algebra.PartialOrd
--- import           Data.POSet (POSet)
--- import qualified Data.POSet as POSet
---
--- newtype Divisibility
---   = Div Int
---   deriving (Eq, Read, Show, Num)
---
--- default (Divisibility)
---
--- instance 'PartialOrd' Divisibility where
---   Div a \`leq\` Div b = b \`mod\` a == 0
---
--- type DivSet = POSet Divisibility
---
--- -- We want integer literals to be interpreted as 'Divisibility's
--- -- and default 'empty's to DivSet.
--- default (Divisibility, DivSet)
--- @
---
--- 'Divisility' is actually an example for a 'PartialOrd' that should not be used as keys of 'POSet'.
--- Its width is \(w=\frac{n}{2}\in\Omega(n)\)!
-
-module Data.POSet
-  (
-  -- * Set type
-    Impl.POSet
-  -- * Query
-  , Foldable.null
-  , Impl.size
-  , Impl.member
-  , Impl.notMember
-  , Impl.lookupLT
-  , Impl.lookupGT
-  , Impl.lookupLE
-  , Impl.lookupGE
-  , Impl.isSubsetOf
-  , Impl.isProperSubsetOf
-
-  -- * Construction
-  , Impl.empty
-  , Impl.singleton
-  , Impl.insert
-  , Impl.delete
-
-  -- * Combine
-  , Impl.union
-  , Impl.unions
-  , Impl.difference
-  , Impl.intersection
-
-  -- * Filter
-  , Impl.filter
-  , Impl.partition
-
-  -- * Map
-  , Impl.map
-  , Impl.mapMonotonic
-
-  -- * Folds
-  , Foldable.foldr
-  , Foldable.foldl
-  -- ** Strict folds
-  , Impl.foldr'
-  , Impl.foldl'
-
-  -- * Min\/Max
-  , Impl.lookupMin
-  , Impl.lookupMax
-
-  -- * Conversion
-  , Impl.elems
-  , Impl.toList
-  , Impl.fromList
-  ) where
-
-import qualified Data.Foldable       as Foldable
-import qualified Data.POSet.Internal as Impl
+-- |+-- Module      :  Data.POSet+-- Copyright   :  (c) Sebastian Graf 2017+-- License     :  MIT+-- Maintainer  :  sgraf1337@gmail.com+-- Portability :  portable+--+-- A reasonably efficient implementation of partially ordered sets.+--+-- These modules are intended to be imported qualified, to avoid name+-- clashes with Prelude functions, e.g.+--+-- > import qualified Data.POSet as POSet+--+-- The implementation of 'POSet' is based on a decomposition of+-- chains (totally ordered submaps), inspired by+-- [\"Sorting and Selection in Posets\"](https://arxiv.org/abs/0707.1532).+--+-- Operation comments contain the operation time complexity in+-- [Big-O notation](http://en.wikipedia.org/wiki/Big_O_notation) and+-- commonly refer to two characteristics of the poset from which keys are drawn:+-- The number of elements in the set \(n\) and the /width/ \(w\) of the poset,+-- referring to the size of the biggest anti-chain (set of incomparable elements).+--+-- Generally speaking, lookup and mutation operations incur an additional+-- factor of \(\mathcal{O}(w)\) compared to their counter-parts in "Data.Set".+--+-- Note that for practical applications, the width of the poset should be+-- in the order of \(w\in \mathcal{O}(\frac{n}{\log n})\), otherwise a simple lookup list+-- is asymptotically superior.+-- Even if that holds, the constants might be too big to be useful for any \(n\) that can+-- can happen in practice.+--+-- The following examples assume the following definitions for a set on the divisibility+-- relation on `Int`egers:+--+-- @+-- {-\# LANGUAGE GeneralizedNewtypeDeriving \#-}+--+-- import           Algebra.PartialOrd+-- import           Data.POSet (POSet)+-- import qualified Data.POSet as POSet+--+-- newtype Divisibility+--   = Div Int+--   deriving (Eq, Read, Show, Num)+--+-- default (Divisibility)+--+-- instance 'PartialOrd' Divisibility where+--   Div a \`leq\` Div b = b \`mod\` a == 0+--+-- type DivSet = POSet Divisibility+--+-- -- We want integer literals to be interpreted as 'Divisibility's+-- -- and default 'empty's to DivSet.+-- default (Divisibility, DivSet)+-- @+--+-- 'Divisility' is actually an example for a 'PartialOrd' that should not be used as keys of 'POSet'.+-- Its width is \(w=\frac{n}{2}\in\Omega(n)\)!++module Data.POSet+  (+  -- * Set type+    Impl.POSet+  -- * Query+  , Foldable.null+  , Impl.size+  , Impl.member+  , Impl.notMember+  , Impl.lookupLT+  , Impl.lookupGT+  , Impl.lookupLE+  , Impl.lookupGE+  , Impl.isSubsetOf+  , Impl.isProperSubsetOf++  -- * Construction+  , Impl.empty+  , Impl.singleton+  , Impl.insert+  , Impl.delete++  -- * Combine+  , Impl.union+  , Impl.unions+  , Impl.difference+  , Impl.intersection++  -- * Filter+  , Impl.filter+  , Impl.partition++  -- * Map+  , Impl.map+  , Impl.mapMonotonic++  -- * Folds+  , Foldable.foldr+  , Foldable.foldl+  -- ** Strict folds+  , Impl.foldr'+  , Impl.foldl'++  -- * Min\/Max+  , Impl.lookupMin+  , Impl.lookupMax++  -- * Conversion+  , Impl.elems+  , Impl.toList+  , Impl.fromList+  ) where++import qualified Data.Foldable       as Foldable+import qualified Data.POSet.Internal as Impl
src/Data/POSet/Internal.hs view
@@ -1,356 +1,397 @@-{-# LANGUAGE TypeApplications #-}
-{-# LANGUAGE TypeFamilies     #-}
-
--- | This module doesn't respect the PVP!
--- Breaking changes may happen at any minor version (>= *.*.m.*)
-
-module Data.POSet.Internal where
-
-import           Algebra.PartialOrd
-import           Control.DeepSeq    (NFData (rnf))
-import qualified Data.List          as List
-import           Data.POMap.Lazy    (POMap)
-import qualified Data.POMap.Lazy    as POMap
-import           GHC.Exts           (coerce)
-import qualified GHC.Exts
-import           Text.Read          (Lexeme (Ident), Read (..), lexP, parens,
-                                     prec, readListPrecDefault)
-
--- $setup
--- This is some setup code for @doctest@.
--- >>> :set -XGeneralizedNewtypeDeriving
--- >>> import           Algebra.PartialOrd
--- >>> import           Data.POSet
--- >>> :{
---   newtype Divisibility
---     = Div Int
---     deriving (Eq, Num)
---   instance Show Divisibility where
---     show (Div a) = show a
---   instance PartialOrd Divisibility where
---     Div a `leq` Div b = b `mod` a == 0
---   type DivSet = POSet Divisibility
---   default (Divisibility, DivSet)
--- :}
-
--- | A set of partially ordered values @k@.
-newtype POSet k
-  = POSet (POMap k ())
-
---
--- * Instances
---
-
-instance PartialOrd k => Eq (POSet k) where
-  POSet a == POSet b = a == b
-
-instance PartialOrd k => PartialOrd (POSet k) where
-  POSet a `leq` POSet b = a `leq` b
-
-instance Show a => Show (POSet a) where
-  showsPrec p xs = showParen (p > 10) $
-    showString "fromList " . shows (toList xs)
-
-instance (Read a, PartialOrd a) => Read (POSet a) where
-  readPrec = parens $ prec 10 $ do
-    Ident "fromList" <- lexP
-    xs <- readPrec
-    return (fromList xs)
-
-  readListPrec = readListPrecDefault
-
-instance NFData a => NFData (POSet a) where
-  rnf (POSet m) = rnf m
-
-instance Foldable POSet where
-  foldr f = coerce (POMap.foldrWithKey @_ @() (\k _ acc -> f k acc))
-  {-# INLINE foldr #-}
-  foldl f = coerce (POMap.foldlWithKey @_ @_ @() (\k acc _ -> f k acc))
-  {-# INLINE foldl #-}
-  null m = size m == 0
-  {-# INLINE null #-}
-  length = size
-  {-# INLINE length #-}
-
-instance PartialOrd k => GHC.Exts.IsList (POSet k) where
-  type Item (POSet k) = k
-  fromList = fromList
-  toList = toList
-
---
--- * Query
---
-
--- | \(\mathcal{O}(1)\). The number of elements in this set.
-size :: POSet k -> Int
-size = coerce (POMap.size @_ @())
-{-# INLINE size #-}
-
--- | \(\mathcal{O}(w)\).
--- The width \(w\) of the chain decomposition in the internal
--- data structure.
--- This is always at least as big as the size of the biggest possible
--- anti-chain.
-width :: POSet k -> Int
-width = coerce (POMap.width @_ @())
-{-# INLINE width #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- Is the key a member of the map? See also 'notMember'.
-member :: PartialOrd k => k -> POSet k -> Bool
-member = coerce (POMap.member @_ @())
-{-# INLINE member #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- Is the key not a member of the map? See also 'member'.
-notMember :: PartialOrd k => k -> POSet k -> Bool
-notMember = coerce (POMap.notMember @_ @())
-{-# INLINE notMember #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- Find the largest set of keys smaller than the given one and
--- return the corresponding list of (key, value) pairs.
---
--- Note that the following examples assume the @Divisibility@
--- partial order defined at the top.
---
--- >>> lookupLT 3 (fromList [3, 5])
--- []
--- >>> lookupLT 6 (fromList [3, 5])
--- [3]
-lookupLT :: PartialOrd k => k -> POSet k -> [k]
-lookupLT k = List.map @(_,()) fst . coerce (POMap.lookupLT @_ @() k)
-{-# INLINE lookupLT #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- Find the largest key smaller or equal to the given one and return
--- the corresponding list of (key, value) pairs.
---
--- Note that the following examples assume the @Divisibility@
--- partial order defined at the top.
---
--- >>> lookupLE 2  (fromList [3, 5])
--- []
--- >>> lookupLE 3  (fromList [3, 5])
--- [3]
--- >>> lookupLE 10 (fromList [3, 5])
--- [5]
-lookupLE :: PartialOrd k => k -> POSet k -> [k]
-lookupLE k = List.map @(_,()) fst . coerce (POMap.lookupLE @_ @() k)
-{-# INLINE lookupLE #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- Find the smallest key greater or equal to the given one and return
--- the corresponding list of (key, value) pairs.
---
--- Note that the following examples assume the @Divisibility@
--- partial order defined at the top.
---
--- >>> lookupGE 3 (fromList [3, 5])
--- [3]
--- >>> lookupGE 5 (fromList [3, 10])
--- [10]
--- >>> lookupGE 6 (fromList [3, 5])
--- []
-lookupGE :: PartialOrd k => k -> POSet k -> [k]
-lookupGE k = List.map @(_,()) fst . coerce (POMap.lookupGE @_ @() k)
-{-# INLINE lookupGE #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- Find the smallest key greater than the given one and return the
--- corresponding list of (key, value) pairs.
---
--- Note that the following examples assume the @Divisibility@
--- partial order defined at the top.
---
--- >>> lookupGT 3 (fromList [6, 5])
--- [6]
--- >>> lookupGT 5 (fromList [3, 5])
--- []
-lookupGT :: PartialOrd k => k -> POSet k -> [k]
-lookupGT k = List.map @(_,()) fst . coerce (POMap.lookupGT @_ @() k)
-{-# INLINE lookupGT #-}
-
--- | \(\mathcal{O}(n_2 w_1 n_1 \log n_1)\).
--- @(s1 `isSubsetOf` s2)@ tells whether @s1@ is a subset of @s2@.
-isSubsetOf :: PartialOrd k => POSet k -> POSet k -> Bool
-isSubsetOf = coerce (POMap.isSubmapOf @_ @())
-{-# INLINE isSubsetOf #-}
-
--- | \(\mathcal{O}(n_2 w_1 n_1 \log n_1)\).
--- Is this a proper subset? (ie. a subset but not equal).
-isProperSubsetOf :: PartialOrd k => POSet k -> POSet k -> Bool
-isProperSubsetOf = coerce (POMap.isProperSubmapOf @_ @())
-{-# INLINE isProperSubsetOf #-}
-
---
--- * Construction
---
-
--- | \(\mathcal{O}(1)\). The empty set.
-empty :: POSet k
-empty = POSet POMap.empty
-{-# INLINE empty #-}
-
--- | \(\mathcal{O}(1)\). A set with a single element.
-singleton :: k -> POSet k
-singleton k = POSet (POMap.singleton k ())
-{-# INLINE singleton #-}
--- INLINE means we don't need to SPECIALIZE
-
--- | \(\mathcal{O}(w\log n)\).
--- If the key is already present in the map, the associated value is
--- replaced with the supplied value. 'insert' is equivalent to
--- @'insertWith' 'const'@.
-insert :: (PartialOrd k) => k -> POSet k -> POSet k
-insert k = coerce (POMap.insert k ())
-{-# INLINE insert #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- Delete an element from a set.
-delete :: (PartialOrd k) => k -> POSet k -> POSet k
-delete = coerce (POMap.delete @_ @())
-{-# INLINE delete #-}
-
---
--- * Combine
---
-
--- ** Union
-
--- | \(\mathcal{O}(wn\log n)\), where \(n=\max(n_1,n_2)\) and \(w=\max(w_1,w_2)\).
--- The union of two sets, preferring the first set when
--- equal elements are encountered.
-union :: PartialOrd k => POSet k -> POSet k -> POSet k
-union = coerce (POMap.union @_ @())
-{-# INLINE union #-}
-
--- | \(\mathcal{O}(wn\log n)\), where \(n=\max_i n_i\) and \(w=\max_i w_i\).
--- The union of a list of sets: (@'unions' == 'foldl' 'union' 'empty'@).
-unions :: PartialOrd k => [POSet k] -> POSet k
-unions = coerce (POMap.unions @_ @())
-{-# INLINE unions #-}
-
--- ** Difference
-
--- | \(\mathcal{O}(wn\log n)\), where \(n=\max(n_1,n_2)\) and \(w=\max(w_1,w_2)\).
--- Difference of two sets.
-difference :: PartialOrd k => POSet k -> POSet k -> POSet k
-difference = coerce (POMap.difference @_ @() @())
-{-# INLINE difference #-}
-
--- ** Intersection
-
--- | \(\mathcal{O}(wn\log n)\), where \(n=\max(n_1,n_2)\) and \(w=\max(w_1,w_2)\).
--- The intersection of two sets.
--- Elements of the result come from the first set, so for example
---
--- >>> data AB = A | B deriving Show
--- >>> instance Eq AB where _ == _ = True
--- >>> instance PartialOrd AB where _ `leq` _ = True
--- >>> singleton A `intersection` singleton B
--- fromList [A]
--- >>> singleton B `intersection` singleton A
--- fromList [B]
-intersection :: PartialOrd k => POSet k -> POSet k -> POSet k
-intersection = coerce (POMap.intersection @_ @() @())
-{-# INLINE intersection #-}
-
---
--- * Filter
---
-
--- | \(\mathcal{O}(n)\).
--- Filter all elements that satisfy the predicate.
-filter :: (k -> Bool) -> POSet k -> POSet k
-filter f = coerce (POMap.filterWithKey @_ @() (\k _ -> f k))
-{-# INLINE filter #-}
-
--- | \(\mathcal{O}(n)\).
--- Partition the set into two sets, one with all elements that satisfy
--- the predicate and one with all elements that don't satisfy the predicate.
-partition :: (k -> Bool) -> POSet k -> (POSet k, POSet k)
-partition f = coerce (POMap.partitionWithKey @_ @() (\k _ -> f k))
-{-# INLINE partition #-}
-
---
--- * Map
---
-
--- | \(\mathcal{O}(wn\log n)\).
--- @'map' f s@ is the set obtained by applying @f@ to each element of @s@.
---
--- It's worth noting that the size of the result may be smaller if,
--- for some @(x,y)@, @x \/= y && f x == f y@
-map :: PartialOrd k2 => (k1 -> k2) -> POSet k1 -> POSet k2
-map = coerce (POMap.mapKeys @_ @_ @())
-{-# INLINE map #-}
-
--- | \(\mathcal{O}(n)\).
--- @'mapMonotonic' f s == 'map' f s@, but works only when @f@ is strictly increasing.
--- /The precondition is not checked./
--- Semi-formally, for every chain @ls@ in @s@ we have:
---
--- > and [x < y ==> f x < f y | x <- ls, y <- ls]
--- >                     ==> mapMonotonic f s == map f s
-mapMonotonic :: (k1 -> k2) -> POSet k1 -> POSet k2
-mapMonotonic = coerce (POMap.mapKeysMonotonic @_ @_ @())
-{-# INLINE mapMonotonic #-}
-
---
--- * Folds
---
-
--- | \(\mathcal{O}(n)\).
--- A strict version of 'foldr'. Each application of the operator is
--- evaluated before using the result in the next application. This
--- function is strict in the starting value.
-foldr' :: (a -> b -> b) -> b -> POSet a -> b
-foldr' f = coerce (POMap.foldrWithKey' @_ @()  (\k _ acc -> f k acc))
-{-# INLINE foldr' #-}
-
--- | \(\mathcal{O}(n)\).
--- A strict version of 'foldl'. Each application of the operator is
--- evaluated before using the result in the next application. This
--- function is strict in the starting value.
-foldl' :: (b -> a -> b) -> b -> POSet a -> b
-foldl' f = coerce (POMap.foldlWithKey' @_ @_ @()  (\k acc _ -> f k acc))
-{-# INLINE foldl' #-}
-
---
--- * Min/Max
---
-
--- | \(\mathcal{O}(w\log n)\).
--- The minimal keys of the set.
-lookupMin :: PartialOrd k => POSet k -> [k]
-lookupMin = List.map @(_,()) fst . coerce (POMap.lookupMin @_ @())
-{-# INLINE lookupMin #-}
-
--- | \(\mathcal{O}(w\log n)\).
--- The maximal keys of the set.
-lookupMax :: PartialOrd k => POSet k -> [k]
-lookupMax = List.map @(_,()) fst . coerce (POMap.lookupMax @_ @())
-{-# INLINE lookupMax #-}
-
---
--- * Conversion
---
-
--- | \(\mathcal{O}(n)\).
--- The elements of a set in unspecified order.
-elems :: POSet k -> [k]
-elems = coerce (POMap.keys @_ @())
-{-# INLINE elems #-}
-
--- | \(\mathcal{O}(n)\).
--- The elements of a set in unspecified order.
-toList :: POSet k -> [k]
-toList = coerce (POMap.keys @_ @())
-{-# INLINE toList #-}
-
--- | \(\mathcal{O}(wn\log n)\).
--- Build a set from a list of keys.
-fromList :: (PartialOrd k) => [k] -> POSet k
-fromList = coerce (POMap.fromList @_ @()) . List.map (\k -> (k, ()))
-{-# INLINE fromList #-}
+{-# LANGUAGE TupleSections    #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilies     #-}++-- | This module doesn't respect the PVP!+-- Breaking changes may happen at any minor version (>= *.*.m.*)++module Data.POSet.Internal where++import           Algebra.PartialOrd+import           Control.DeepSeq    (NFData (rnf))+import qualified Data.List          as List+import           Data.POMap.Lazy    (POMap)+import qualified Data.POMap.Lazy    as POMap+import           GHC.Exts           (coerce)+import qualified GHC.Exts+import           Text.Read          (Lexeme (Ident), Read (..), lexP, parens,+                                     prec, readListPrecDefault)++-- $setup+-- This is some setup code for @doctest@.+-- >>> :set -XGeneralizedNewtypeDeriving+-- >>> import           Algebra.PartialOrd+-- >>> import           Data.POSet+-- >>> :{+--   newtype Divisibility+--     = Div Int+--     deriving (Eq, Num)+--   instance Show Divisibility where+--     show (Div a) = show a+--   instance PartialOrd Divisibility where+--     Div a `leq` Div b = b `mod` a == 0+--   type DivSet = POSet Divisibility+--   default (Divisibility, DivSet)+-- :}++-- | A set of partially ordered values @k@.+newtype POSet k+  = POSet (POMap k ())++--+-- * Instances+--++instance PartialOrd k => Eq (POSet k) where+  POSet a == POSet b = a == b++instance PartialOrd k => PartialOrd (POSet k) where+  POSet a `leq` POSet b = a `leq` b++instance Show a => Show (POSet a) where+  showsPrec p xs = showParen (p > 10) $+    showString "fromList " . shows (toList xs)++instance (Read a, PartialOrd a) => Read (POSet a) where+  readPrec = parens $ prec 10 $ do+    Ident "fromList" <- lexP+    fromList <$> readPrec++  readListPrec = readListPrecDefault++instance NFData a => NFData (POSet a) where+  rnf (POSet m) = rnf m++instance Foldable POSet where+  foldr f = coerce (POMap.foldrWithKey @_ @() (\k _ acc -> f k acc))+  {-# INLINE foldr #-}+  foldl f = coerce (POMap.foldlWithKey @_ @_ @() (\k acc _ -> f k acc))+  {-# INLINE foldl #-}+  null m = size m == 0+  {-# INLINE null #-}+  length = size+  {-# INLINE length #-}++instance PartialOrd k => GHC.Exts.IsList (POSet k) where+  type Item (POSet k) = k+  fromList = fromList+  toList = toList++--+-- * Query+--++-- | \(\mathcal{O}(1)\). The number of elements in this set.+size :: POSet k -> Int+size = coerce (POMap.size @_ @())+{-# INLINE size #-}++-- | \(\mathcal{O}(w)\).+-- The width \(w\) of the chain decomposition in the internal+-- data structure.+-- This is always at least as big as the size of the biggest possible+-- anti-chain.+width :: POSet k -> Int+width = coerce (POMap.width @_ @())+{-# INLINE width #-}++-- | \(\mathcal{O}(w\log n)\).+-- Is the key a member of the map? See also 'notMember'.+member :: PartialOrd k => k -> POSet k -> Bool+member = coerce (POMap.member @_ @())+{-# INLINE member #-}++-- | \(\mathcal{O}(w\log n)\).+-- Is the key not a member of the map? See also 'member'.+notMember :: PartialOrd k => k -> POSet k -> Bool+notMember = coerce (POMap.notMember @_ @())+{-# INLINE notMember #-}++-- | \(\mathcal{O}(w\log n)\).+-- Find the largest set of keys smaller than the given one and+-- return the corresponding list of (key, value) pairs.+--+-- Note that the following examples assume the @Divisibility@+-- partial order defined at the top.+--+-- >>> lookupLT 3 (fromList [3, 5])+-- []+-- >>> lookupLT 6 (fromList [3, 5])+-- [3]+lookupLT :: PartialOrd k => k -> POSet k -> [k]+lookupLT k = List.map @(_,()) fst . coerce (POMap.lookupLT @_ @() k)+{-# INLINE lookupLT #-}++-- | \(\mathcal{O}(w\log n)\).+-- Find the largest key smaller or equal to the given one and return+-- the corresponding list of (key, value) pairs.+--+-- Note that the following examples assume the @Divisibility@+-- partial order defined at the top.+--+-- >>> lookupLE 2  (fromList [3, 5])+-- []+-- >>> lookupLE 3  (fromList [3, 5])+-- [3]+-- >>> lookupLE 10 (fromList [3, 5])+-- [5]+lookupLE :: PartialOrd k => k -> POSet k -> [k]+lookupLE k = List.map @(_,()) fst . coerce (POMap.lookupLE @_ @() k)+{-# INLINE lookupLE #-}++-- | \(\mathcal{O}(w\log n)\).+-- Find the smallest key greater or equal to the given one and return+-- the corresponding list of (key, value) pairs.+--+-- Note that the following examples assume the @Divisibility@+-- partial order defined at the top.+--+-- >>> lookupGE 3 (fromList [3, 5])+-- [3]+-- >>> lookupGE 5 (fromList [3, 10])+-- [10]+-- >>> lookupGE 6 (fromList [3, 5])+-- []+lookupGE :: PartialOrd k => k -> POSet k -> [k]+lookupGE k = List.map @(_,()) fst . coerce (POMap.lookupGE @_ @() k)+{-# INLINE lookupGE #-}++-- | \(\mathcal{O}(w\log n)\).+-- Find the smallest key greater than the given one and return the+-- corresponding list of (key, value) pairs.+--+-- Note that the following examples assume the @Divisibility@+-- partial order defined at the top.+--+-- >>> lookupGT 3 (fromList [6, 5])+-- [6]+-- >>> lookupGT 5 (fromList [3, 5])+-- []+lookupGT :: PartialOrd k => k -> POSet k -> [k]+lookupGT k = List.map @(_,()) fst . coerce (POMap.lookupGT @_ @() k)+{-# INLINE lookupGT #-}++-- | \(\mathcal{O}(n_2 w_1 n_1 \log n_1)\).+-- @(s1 `isSubsetOf` s2)@ tells whether @s1@ is a subset of @s2@.+isSubsetOf :: PartialOrd k => POSet k -> POSet k -> Bool+isSubsetOf = coerce (POMap.isSubmapOf @_ @())+{-# INLINE isSubsetOf #-}++-- | \(\mathcal{O}(n_2 w_1 n_1 \log n_1)\).+-- Is this a proper subset? (ie. a subset but not equal).+isProperSubsetOf :: PartialOrd k => POSet k -> POSet k -> Bool+isProperSubsetOf = coerce (POMap.isProperSubmapOf @_ @())+{-# INLINE isProperSubsetOf #-}++--+-- * Construction+--++-- | \(\mathcal{O}(1)\). The empty set.+empty :: POSet k+empty = POSet POMap.empty+{-# INLINE empty #-}++-- | \(\mathcal{O}(1)\). A set with a single element.+singleton :: k -> POSet k+singleton k = POSet (POMap.singleton k ())+{-# INLINE singleton #-}+-- INLINE means we don't need to SPECIALIZE++-- | \(\mathcal{O}(w\log n)\).+-- If the key is already present in the map, the associated value is+-- replaced with the supplied value. 'insert' is equivalent to+-- @'insertWith' 'const'@.+insert :: (PartialOrd k) => k -> POSet k -> POSet k+insert k = coerce (POMap.insert k ())+{-# INLINE insert #-}++-- | \(\mathcal{O}(w\log n)\).+-- Delete an element from a set.+delete :: (PartialOrd k) => k -> POSet k -> POSet k+delete = coerce (POMap.delete @_ @())+{-# INLINE delete #-}++--+-- * Combine+--++-- ** Union++-- | \(\mathcal{O}(wn\log n)\), where \(n=\max(n_1,n_2)\) and \(w=\max(w_1,w_2)\).+-- The union of two sets, preferring the first set when+-- equal elements are encountered.+union :: PartialOrd k => POSet k -> POSet k -> POSet k+union = coerce (POMap.union @_ @())+{-# INLINE union #-}++-- | \(\mathcal{O}(wn\log n)\), where \(n=\max_i n_i\) and \(w=\max_i w_i\).+-- The union of a list of sets: (@'unions' == 'foldl' 'union' 'empty'@).+unions :: PartialOrd k => [POSet k] -> POSet k+unions = coerce (POMap.unions @_ @())+{-# INLINE unions #-}++-- ** Difference++-- | \(\mathcal{O}(wn\log n)\), where \(n=\max(n_1,n_2)\) and \(w=\max(w_1,w_2)\).+-- Difference of two sets.+difference :: PartialOrd k => POSet k -> POSet k -> POSet k+difference = coerce (POMap.difference @_ @() @())+{-# INLINE difference #-}++-- ** Intersection++-- | \(\mathcal{O}(wn\log n)\), where \(n=\max(n_1,n_2)\) and \(w=\max(w_1,w_2)\).+-- The intersection of two sets.+-- Elements of the result come from the first set, so for example+--+-- >>> data AB = A | B deriving Show+-- >>> instance Eq AB where _ == _ = True+-- >>> instance PartialOrd AB where _ `leq` _ = True+-- >>> singleton A `intersection` singleton B+-- fromList [A]+-- >>> singleton B `intersection` singleton A+-- fromList [B]+intersection :: PartialOrd k => POSet k -> POSet k -> POSet k+intersection = coerce (POMap.intersection @_ @() @())+{-# INLINE intersection #-}++--+-- * Filter+--++-- | \(\mathcal{O}(n)\).+-- Filter all elements that satisfy the predicate.+filter :: (k -> Bool) -> POSet k -> POSet k+filter f = coerce (POMap.filterWithKey @_ @() (\k _ -> f k))+{-# INLINE filter #-}++-- | \(\mathcal{O}(n)\).+-- Partition the set into two sets, one with all elements that satisfy+-- the predicate and one with all elements that don't satisfy the predicate.+partition :: (k -> Bool) -> POSet k -> (POSet k, POSet k)+partition f = coerce (POMap.partitionWithKey @_ @() (\k _ -> f k))+{-# INLINE partition #-}++-- | \(\mathcal{O}(log n)\). Take while a predicate on the keys holds.+-- The user is responsible for ensuring that for all elements @j@ and @k@ in the set,+-- @j \< k ==\> p j \>= p k@. See note at 'spanAntitone'.+--+-- @+-- takeWhileAntitone p = 'filter' p+-- @+--+-- @since 0.0.1.0+takeWhileAntitone :: (k -> Bool) -> POSet k -> POSet k+takeWhileAntitone = coerce (POMap.takeWhileAntitone @_ @())++-- | \(\mathcal{O}(log n)\). Drop while a predicate on the keys holds.+-- The user is responsible for ensuring that for all elements @j@ and @k@ in the set,+-- @j \< k ==\> p j \>= p k@. See note at 'spanAntitone'.+--+-- @+-- dropWhileAntitone p = 'filter' (not . p)+-- @+--+-- @since 0.0.1.0+dropWhileAntitone :: (k -> Bool) -> POSet k -> POSet k+dropWhileAntitone = coerce (POMap.dropWhileAntitone @_ @())++-- | \(\mathcal{O}(log n)\). Divide a set at the point where a predicate on the keys stops holding.+-- The user is responsible for ensuring that for all elements @j@ and @k@ in the set,+-- @j \< k ==\> p j \>= p k@.+--+-- @+-- spanAntitone p xs = 'partition' p xs+-- @+--+-- Note: if @p@ is not actually antitone, then @spanAntitone@ will split the set+-- at some /unspecified/ point where the predicate switches from holding to not+-- holding (where the predicate is seen to hold before the first element and to fail+-- after the last element).+--+-- @since 0.0.1.0+spanAntitone :: (k -> Bool) -> POSet k -> (POSet k, POSet k)+spanAntitone = coerce (POMap.spanAntitone @_ @())++--+-- * Map+--++-- | \(\mathcal{O}(wn\log n)\).+-- @'map' f s@ is the set obtained by applying @f@ to each element of @s@.+--+-- It's worth noting that the size of the result may be smaller if,+-- for some @(x,y)@, @x \/= y && f x == f y@+map :: PartialOrd k2 => (k1 -> k2) -> POSet k1 -> POSet k2+map = coerce (POMap.mapKeys @_ @_ @())+{-# INLINE map #-}++-- | \(\mathcal{O}(n)\).+-- @'mapMonotonic' f s == 'map' f s@, but works only when @f@ is strictly increasing.+-- /The precondition is not checked./+-- Semi-formally, for every chain @ls@ in @s@ we have:+--+-- > and [x < y ==> f x < f y | x <- ls, y <- ls]+-- >                     ==> mapMonotonic f s == map f s+mapMonotonic :: (k1 -> k2) -> POSet k1 -> POSet k2+mapMonotonic = coerce (POMap.mapKeysMonotonic @_ @_ @())+{-# INLINE mapMonotonic #-}++--+-- * Folds+--++-- | \(\mathcal{O}(n)\).+-- A strict version of 'foldr'. Each application of the operator is+-- evaluated before using the result in the next application. This+-- function is strict in the starting value.+foldr' :: (a -> b -> b) -> b -> POSet a -> b+foldr' f = coerce (POMap.foldrWithKey' @_ @()  (\k _ acc -> f k acc))+{-# INLINE foldr' #-}++-- | \(\mathcal{O}(n)\).+-- A strict version of 'foldl'. Each application of the operator is+-- evaluated before using the result in the next application. This+-- function is strict in the starting value.+foldl' :: (b -> a -> b) -> b -> POSet a -> b+foldl' f = coerce (POMap.foldlWithKey' @_ @_ @()  (\k acc _ -> f k acc))+{-# INLINE foldl' #-}++--+-- * Min/Max+--++-- | \(\mathcal{O}(w\log n)\).+-- The minimal keys of the set.+lookupMin :: PartialOrd k => POSet k -> [k]+lookupMin = List.map @(_,()) fst . coerce (POMap.lookupMin @_ @())+{-# INLINE lookupMin #-}++-- | \(\mathcal{O}(w\log n)\).+-- The maximal keys of the set.+lookupMax :: PartialOrd k => POSet k -> [k]+lookupMax = List.map @(_,()) fst . coerce (POMap.lookupMax @_ @())+{-# INLINE lookupMax #-}++--+-- * Conversion+--++-- | \(\mathcal{O}(n)\).+-- The elements of a set in unspecified order.+elems :: POSet k -> [k]+elems = coerce (POMap.keys @_ @())+{-# INLINE elems #-}++-- | \(\mathcal{O}(n)\).+-- The elements of a set in unspecified order.+toList :: POSet k -> [k]+toList = coerce (POMap.keys @_ @())+{-# INLINE toList #-}++-- | \(\mathcal{O}(wn\log n)\).+-- Build a set from a list of keys.+fromList :: (PartialOrd k) => [k] -> POSet k+fromList = coerce (POMap.fromList @_ @()) . List.map (, ())+{-# INLINE fromList #-}
stack.yaml view
@@ -1,63 +1,63 @@-# This file was automatically generated by 'stack init'
-#
-# Some commonly used options have been documented as comments in this file.
-# For advanced use and comprehensive documentation of the format, please see:
-# http://docs.haskellstack.org/en/stable/yaml_configuration/
-
-# Resolver to choose a 'specific' stackage snapshot or a compiler version.
-# A snapshot resolver dictates the compiler version and the set of packages
-# to be used for project dependencies. For example:
-#
-# resolver: lts-3.5
-# resolver: nightly-2015-09-21
-# resolver: ghc-7.10.2
-# resolver: ghcjs-0.1.0_ghc-7.10.2
-# resolver:
-#  name: custom-snapshot
-#  location: "./custom-snapshot.yaml"
-resolver: lts-11.1
-
-# User packages to be built.
-# Various formats can be used as shown in the example below.
-#
-# packages:
-# - some-directory
-# - https://example.com/foo/bar/baz-0.0.2.tar.gz
-# - location:
-#    git: https://github.com/commercialhaskell/stack.git
-#    commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
-# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
-#   extra-dep: true
-#  subdirs:
-#  - auto-update
-#  - wai
-#
-# A package marked 'extra-dep: true' will only be built if demanded by a
-# non-dependency (i.e. a user package), and its test suites and benchmarks
-# will not be run. This is useful for tweaking upstream packages.
-packages:
-- '.'
-# Dependency packages to be pulled from upstream that are not in the resolver
-# (e.g., acme-missiles-0.3)
-extra-deps: []
-
-# Extra package databases containing global packages
-extra-package-dbs: []
-
-# Control whether we use the GHC we find on the path
-# system-ghc: true
-#
-# Require a specific version of stack, using version ranges
-# require-stack-version: -any # Default
-# require-stack-version: ">=1.4"
-#
-# Override the architecture used by stack, especially useful on Windows
-# arch: i386
-# arch: x86_64
-#
-# Extra directories used by stack for building
-# extra-include-dirs: [/path/to/dir]
-# extra-lib-dirs: [/path/to/dir]
-#
-# Allow a newer minor version of GHC than the snapshot specifies
-# compiler-check: newer-minor
+# This file was automatically generated by 'stack init'+#+# Some commonly used options have been documented as comments in this file.+# For advanced use and comprehensive documentation of the format, please see:+# http://docs.haskellstack.org/en/stable/yaml_configuration/++# Resolver to choose a 'specific' stackage snapshot or a compiler version.+# A snapshot resolver dictates the compiler version and the set of packages+# to be used for project dependencies. For example:+#+# resolver: lts-3.5+# resolver: nightly-2015-09-21+# resolver: ghc-7.10.2+# resolver: ghcjs-0.1.0_ghc-7.10.2+# resolver:+#  name: custom-snapshot+#  location: "./custom-snapshot.yaml"+resolver: lts-11.1++# User packages to be built.+# Various formats can be used as shown in the example below.+#+# packages:+# - some-directory+# - https://example.com/foo/bar/baz-0.0.2.tar.gz+# - location:+#    git: https://github.com/commercialhaskell/stack.git+#    commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a+# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a+#   extra-dep: true+#  subdirs:+#  - auto-update+#  - wai+#+# A package marked 'extra-dep: true' will only be built if demanded by a+# non-dependency (i.e. a user package), and its test suites and benchmarks+# will not be run. This is useful for tweaking upstream packages.+packages:+- '.'+# Dependency packages to be pulled from upstream that are not in the resolver+# (e.g., acme-missiles-0.3)+extra-deps: []++# Extra package databases containing global packages+extra-package-dbs: []++# Control whether we use the GHC we find on the path+# system-ghc: true+#+# Require a specific version of stack, using version ranges+# require-stack-version: -any # Default+# require-stack-version: ">=1.4"+#+# Override the architecture used by stack, especially useful on Windows+# arch: i386+# arch: x86_64+#+# Extra directories used by stack for building+# extra-include-dirs: [/path/to/dir]+# extra-lib-dirs: [/path/to/dir]+#+# Allow a newer minor version of GHC than the snapshot specifies+# compiler-check: newer-minor
tests/Data/POMap/Properties.hs view
@@ -17,7 +17,6 @@ import qualified Data.List               as List
 import qualified Data.Maybe              as Maybe
 import           Data.Monoid             (Dual (..), Endo (..), Sum (..))
-import           Data.Ord                (comparing)
 import           Data.POMap.Arbitrary    ()
 import           Data.POMap.Divisibility
 import           Data.POMap.Lazy
@@ -29,7 +28,7 @@ type DivMap v = POMap Divisibility v
 
 instance {-# OVERLAPPING #-} Eq v => Eq (DivMap v) where
-  (==) = (==) `on` List.sortBy (comparing (unDiv . fst)) . toList
+  (==) = (==) `on` List.sortOn (unDiv . fst) . toList
 
 div' :: Int -> DivMap Integer
 div' = fromList . divisibility
@@ -52,7 +51,7 @@ makeEntries = fmap (Div &&& id)
 
 shouldBeSameEntries :: (Eq v, Show v) => [(Divisibility, v)] -> [(Divisibility, v)] -> Expectation
-shouldBeSameEntries = shouldBe `on` List.sortBy (comparing (unDiv . fst))
+shouldBeSameEntries = shouldBe `on` List.sortOn (unDiv . fst)
 
 isAntichain :: PartialOrd k => [k] -> Bool
 isAntichain []     = True
@@ -399,6 +398,18 @@       let p k v = odd (unDiv k + v)
       it "partitionWithKey p = filterWithKey p &&& filterWithKey ((not .) . p)" $ property $ \(m :: DivMap Integer) ->
         partitionWithKey p m `shouldBe` (filterWithKey p &&& filterWithKey ((not .) . p)) m
+    describe "takeWhileAntitone" $ do
+      let p k = unDiv k < 50
+      it "takeWhileAntitone p = filterWithKey (\\k _ -> p k)" $ property $ \(m :: DivMap Int) ->
+        takeWhileAntitone p m `shouldBe` filterWithKey (\k _ -> p k) m
+    describe "dropWhileAntitone" $ do
+      let p k = unDiv k < 50
+      it "dropWhileAntitone p = filterWithKey (\\k _ -> not (p k))" $ property $ \(m :: DivMap Int) ->
+        dropWhileAntitone p m `shouldBe` filterWithKey (\k _ -> not (p k)) m
+    describe "spanAntitone" $ do
+      let p k = unDiv k < 50
+      it "spanAntitone p = partitionWithKey (\\k _ -> p k)" $ property $ \(m :: DivMap Int) ->
+        spanAntitone p m `shouldBe` partitionWithKey (\k _ -> p k) m
     describe "mapMaybe" $ do
       let f v = if odd v then Just (v + 1) else Nothing
       it "mapMaybe f = fromList . Maybe.mapMaybe (traverse f) . toList" $ property $ \(m :: DivMap Int) ->
tests/Data/POMap/Strictness.hs view
@@ -19,7 +19,7 @@ type DivMap v = L.POMap Divisibility v
 
 instance {-# OVERLAPPING #-} Eq v => Eq (DivMap v) where
-  (==) = (==) `on` List.sortBy (comparing (unDiv . fst)) . toList
+  (==) = (==) `on` List.sortOn (unDiv . fst) . toList
 
 shouldBeBottom :: a -> Expectation
 shouldBeBottom x = isBottom x `shouldBe` True
tests/Main.hs view
@@ -1,13 +1,13 @@-import qualified Data.POMap.Properties
-import qualified Data.POMap.Strictness
-import qualified Test.Tasty
-import           Test.Tasty.Hspec
-
-main :: IO ()
-main = do
-  props <- testSpec "properties" (parallel Data.POMap.Properties.spec)
-  strict <- testSpec "strictness" (parallel Data.POMap.Strictness.spec)
-  Test.Tasty.defaultMain $ Test.Tasty.testGroup "pomaps"
-    [ props
-    , strict
-    ]
+import qualified Data.POMap.Properties+import qualified Data.POMap.Strictness+import qualified Test.Tasty+import           Test.Tasty.Hspec++main :: IO ()+main = do+  props <- testSpec "properties" (parallel Data.POMap.Properties.spec)+  strict <- testSpec "strictness" (parallel Data.POMap.Strictness.spec)+  Test.Tasty.defaultMain $ Test.Tasty.testGroup "pomaps"+    [ props+    , strict+    ]
tests/doctest-driver.hs view
@@ -1,5 +1,5 @@-import           System.FilePath.Glob (glob)
-import           Test.DocTest         (doctest)
-
-main :: IO ()
-main = glob "src/**/*.hs" >>= doctest
+import           System.FilePath.Glob (glob)+import           Test.DocTest         (doctest)++main :: IO ()+main = glob "src/**/*.hs" >>= doctest