packages feed

discrimination 0.4 → 0.4.1

raw patch · 14 files changed

+568/−87 lines, 14 filesdep +QuickCheckdep +ghc-bignumdep +quickcheck-instancesdep −profunctorsdep −transformers-compatdep −voiddep ~arraydep ~basedep ~containersnew-uploaderPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: QuickCheck, ghc-bignum, quickcheck-instances, splitmix, tasty, tasty-quickcheck, vector-algorithms

Dependencies removed: profunctors, transformers-compat, void

Dependency ranges changed: array, base, containers, contravariant, deepseq, hashable, integer-gmp, primitive, promises, semigroups, transformers, vector

API changes (from Hackage documentation)

- Data.Discrimination.Grouping: word8s :: Integer -> [Word8]
+ Data.Discrimination.Grouping: instance Data.Discrimination.Grouping.Grouping GHC.Types.Ordering
+ Data.Discrimination.Grouping: instance Data.Discrimination.Grouping.Grouping a => Data.Discrimination.Grouping.Grouping (GHC.Base.NonEmpty a)
+ Data.Discrimination.Grouping: instance Data.Discrimination.Grouping.Grouping1 GHC.Base.NonEmpty
+ Data.Discrimination.Internal: integerCases :: Integer -> Either (Int, [Word]) (Either Int (Int, [Word]))
+ Data.Discrimination.Internal: naturalCases :: Natural -> Either Word (Int, [Word])
+ Data.Discrimination.Sorting: instance Data.Discrimination.Sorting.Sorting GHC.Types.Ordering
+ Data.Discrimination.Sorting: instance Data.Discrimination.Sorting.Sorting a => Data.Discrimination.Sorting.Sorting (GHC.Base.NonEmpty a)
+ Data.Discrimination.Sorting: instance Data.Discrimination.Sorting.Sorting1 GHC.Base.NonEmpty

Files

.gitignore view
@@ -1,4 +1,7 @@ dist/+dist-newstyle/+.ghc.environment.*+cabal.project.local .hsenv/ docs wiki
CHANGELOG.markdown view
@@ -1,3 +1,11 @@+## 0.4.1 [2021-01-08]++* GHC-9.0 compatibility+* Added `Ordering` discrimination+* Fix `Sorting Int8` and `Sorting Int16` instances+* Fix `Integer` and `Natural` instances+* Add `NonEmpty` discrimination+ ## 0.4  * Added `Natural`, `Integer` and `()` discrimination
− HLint.hs
@@ -1,1 +0,0 @@-ignore "Eta reduce"
README.markdown view
@@ -1,7 +1,7 @@ discrimination ============== -[![Hackage](https://img.shields.io/hackage/v/discrimination.svg)](https://hackage.haskell.org/package/discrimination) [![Build Status](https://secure.travis-ci.org/ekmett/discrimination.png?branch=master)](http://travis-ci.org/ekmett/discrimination)+[![Hackage](https://img.shields.io/hackage/v/discrimination.svg)](https://hackage.haskell.org/package/discrimination) [![Build Status](https://github.com/ekmett/discrimination/workflows/Haskell-CI/badge.svg)](https://github.com/ekmett/discrimination/actions?query=workflow%3AHaskell-CI)  This package provides linear time sorting, partitioning, and joins for a wide array of Haskell data types. This work is based on a "final encoding" of the ideas presented in [multiple](http://hjemmesider.diku.dk/~henglein/papers/henglein2011a.pdf) [papers](http://hjemmesider.diku.dk/~henglein/papers/henglein2011c.pdf) and [talks](https://www.youtube.com/watch?v=sz9ZlZIRDAg) by [Fritz Henglein](http://hjemmesider.diku.dk/~henglein).
+ benchmarks/examples.hs view
@@ -0,0 +1,65 @@+module Main where++import Control.Exception (evaluate)+import Control.DeepSeq (rnf)+import Criterion.Main+import Criterion.Types++import qualified Data.List as L+import qualified System.Random.SplitMix as SM++import Utils++import Data.Discrimination (nub, sort)+++main :: IO ()+main = do+  let gen = SM.mkSMGen 0xc0de++  let xs100   = finiteList gen 100+  let xs1000  = finiteList gen 1000+  let xs10000 = finiteList gen 10000++  evaluate $ rnf xs100+  evaluate $ rnf xs1000+  evaluate $ rnf xs10000++  defaultMainWith (defaultConfig { timeLimit = 1 })+    [ bgroup "nub"+      [ bgroup "List.nub"+        [ bench "100" $ whnf (length . L.nub) xs100+        , bench "1000" $ whnf (length . L.nub) xs1000+        ]+      , bgroup "ordNub"+        [ bench "100" $ whnf (length . ordNub) xs100+        , bench "1000" $ whnf (length . ordNub) xs1000+        , bench "10000" $ whnf (length . ordNub) xs10000+        ]+      , bgroup "hashNub"+        [ bench "100" $ whnf (length . hashNub) xs100+        , bench "1000" $ whnf (length . hashNub) xs1000+        , bench "10000" $ whnf (length . hashNub) xs10000+        ]+      , bgroup "Discrimination.nub"+        [ bench "100" $ whnf (length . nub) xs100+        , bench "1000" $ whnf (length . nub) xs1000+        , bench "10000" $ whnf (length . nub) xs10000+        ]+      ]++    , bgroup "sort"+      [ bgroup "List.sort"+        [ bench "10000" $ whnf (length . L.sort) xs10000+        ]+      , bgroup "mergesort"+        [ bench "10000" $ whnf (length . mergesort) xs10000+        ]+      , bgroup "introsort"+        [ bench "10000" $ whnf (length . introsort) xs10000+        ]+      , bgroup "Discrimination.sort"+        [ bench "10000" $ whnf (length . sort) xs10000+        ]+      ]+    ]
+ common/Utils.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveAnyClass #-}+module Utils where++import Data.Word (Word64)+import Data.Hashable (Hashable (..))+import GHC.Generics (Generic)+import Control.DeepSeq (NFData)+import Data.Discrimination (Grouping, Sorting)++import qualified Data.HashSet as HS+import qualified Data.Set as Set+import qualified System.Random.SplitMix as SM++import Control.Monad.ST (runST)+import qualified Data.Vector as V+import qualified Data.Vector.Algorithms.Intro as Intro+import qualified Data.Vector.Algorithms.Merge as Merge+import qualified Data.Vector.Algorithms.Radix as Radix+++finiteList :: SM.SMGen -> Int -> [Word64]+finiteList gen n = take n $ go gen where+    go g = let (w,g') = SM.nextWord64 g in w : go g'++finiteListU :: SM.SMGen -> Int -> [UUID]+finiteListU gen n = take n $ go gen where+    go g0 = UUID w1 w2 : go g2 where+        (w1,g1) = SM.nextWord64 g0+        (w2,g2) = SM.nextWord64 g1++-- | Remove duplicates but keep elements in order.+--   O(n * log n)+--+-- From GHC's Util module+--+ordNub :: Ord a => [a] -> [a]+ordNub ys = go Set.empty ys+  where+    go _ [] = []+    go s (x:xs)+      | Set.member x s = go s xs+      | otherwise = x : go (Set.insert x s) xs++-- | Like 'ordNub' but using 'HashSet'.+hashNub :: (Eq a, Hashable a) => [a] -> [a]+hashNub ys = go HS.empty ys where+    go _ [] = []+    go s (x:xs)+        | HS.member x s = go s xs+        | otherwise     = x : go (HS.insert x s) xs++ -- | Sort via vector.+introsort :: Ord a => [a] -> [a]+introsort xs = runST $ do+    v <- V.unsafeThaw (V.fromList xs)+    Intro.sort v+    V.toList <$> V.unsafeFreeze v++mergesort :: Ord a => [a] -> [a]+mergesort xs = runST $ do+    v <- V.unsafeThaw (V.fromList xs)+    Merge.sort v+    V.toList <$> V.unsafeFreeze v++radixsort :: Radix.Radix a => [a] -> [a]+radixsort xs = runST $ do+    v <- V.unsafeThaw (V.fromList xs)+    Radix.sort v+    V.toList <$> V.unsafeFreeze v++-------------------------------------------------------------------------------+-- UUID+-------------------------------------------------------------------------------++data UUID = UUID !Word64 !Word64+  deriving (Eq, Ord, Generic, Show, NFData, Hashable, Grouping, Sorting)
discrimination.cabal view
@@ -1,6 +1,6 @@ name:          discrimination category:      Data, Sorting-version:       0.4+version:       0.4.1 license:       BSD3 cabal-version: >= 1.10 license-file:  LICENSE@@ -11,7 +11,7 @@ bug-reports:   http://github.com/ekmett/discrimination/issues copyright:     Copyright (C) 2014-2015 Edward A. Kmett build-type:    Simple-tested-with:   GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.2+tested-with:   GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.3 synopsis:      Fast generic linear-time sorting, joins and container construction. description:   This package provides fast, generic, linear-time discrimination and sorting.@@ -22,7 +22,6 @@   .gitignore   README.markdown   CHANGELOG.markdown-  HLint.hs  source-repository head   type: git@@ -43,31 +42,59 @@     Data.Discrimination.Sorting    build-depends:-    array         >= 0.5    && < 0.6,-    base          >= 4.8    && < 5,-    containers    >= 0.4    && < 0.7,-    contravariant >= 1.3.1  && < 2,-    deepseq       >= 1.3    && < 1.5,+    array         >= 0.5.1.0 && < 0.6,+    base          >= 4.8     && < 5,+    containers    >= 0.5.6.2 && < 0.7,+    contravariant >= 1.5.3   && < 2,+    deepseq       >= 1.4.1.1 && < 1.5,     ghc-prim,-    hashable      >= 1.2    && < 1.3,-    integer-gmp,-    primitive     >= 0.6.4  && < 0.8,-    profunctors   >= 5      && < 6,-    promises      >= 0.2    && < 0.4,-    semigroups    >= 0.16.2 && < 1,-    transformers  >= 0.2    && < 0.6,-    transformers-compat >= 0.3 && < 1,-    vector        >= 0.10   && < 0.13,-    void          >= 0.5    && < 1+    hashable      >= 1.2.7.0 && < 1.4,+    primitive     >= 0.7.1.0 && < 0.8,+    promises      >= 0.3     && < 0.4,+    transformers  >= 0.4.2.0 && < 0.6 +  if !impl(ghc >=8.0)+    build-depends: semigroups >= 0.18.5 && < 1++  if impl(ghc >= 9.0)+    build-depends: ghc-bignum >= 1.0 && < 1.1+  else+    build-depends: integer-gmp >= 1.0 && < 1.1++test-suite properties+  type:             exitcode-stdio-1.0+  main-is:          tests.hs+  other-modules:    Utils+  ghc-options:      -Wall -O2 -threaded+  hs-source-dirs:   test common+  default-language: Haskell2010+  build-depends:+    base >= 4.8,+    containers,+    criterion,+    deepseq,+    discrimination,+    hashable,+    QuickCheck >=2.14.2,+    quickcheck-instances,+    splitmix >=0.1 && <0.2,+    tasty,+    tasty-quickcheck,+    unordered-containers,+    vector,+    vector-algorithms++  if !impl(ghc >=8.0)+    build-depends: semigroups+ benchmark wordmap   type:             exitcode-stdio-1.0   main-is:          wordmap.hs-  ghc-options:      -Wall -O2 -threaded +  ghc-options:      -Wall -O2 -threaded   hs-source-dirs:   benchmarks   default-language: Haskell2010   build-depends:-    base >= 4.9,+    base >= 4.8,     containers,     criterion,     deepseq,@@ -75,3 +102,22 @@     ghc-prim,     unordered-containers,     primitive++benchmark examples+  type:             exitcode-stdio-1.0+  main-is:          examples.hs+  other-modules:    Utils+  ghc-options:      -Wall -O2 -threaded+  hs-source-dirs:   benchmarks common+  default-language: Haskell2010+  build-depends:+    base >= 4.8,+    containers,+    criterion,+    deepseq,+    discrimination,+    hashable,+    splitmix >=0.1 && <0.2,+    unordered-containers,+    vector,+    vector-algorithms
src/Data/Discrimination/Class.hs view
@@ -20,7 +20,7 @@   disc :: f a -> [(a, b)] -> [[b]]  instance Discriminating Sort where-  disc = runSort+  disc fa ab = runSort fa ab  instance Discriminating Group where   disc = runGroup
src/Data/Discrimination/Grouping.hs view
@@ -1,14 +1,9 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE GADTs #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Trustworthy #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ParallelListComp #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -26,15 +21,14 @@   , runGroup   -- * Internals   , hashing-  , word8s   ) where  import Control.Monad hiding (mapM_) import Control.Monad.Primitive import Control.Monad.ST-import Control.Monad.ST.Unsafe import Data.Complex import Data.Discrimination.Internal.WordMap as WordMap+import Data.Discrimination.Internal import Data.Foldable hiding (concat) import Data.Functor.Compose import Data.Functor.Contravariant@@ -42,18 +36,16 @@ import Data.Functor.Contravariant.Generic import Data.Hashable import Data.Int+import Data.List.NonEmpty (NonEmpty) import Data.Semigroup hiding (Any) import Data.Primitive.MutVar-import Data.Primitive.PrimArray import Data.Promise import Data.Proxy import Data.Ratio import Data.Typeable import Data.Void import Data.Word-import GHC.Integer.GMP.Internals-import GHC.Word-import Numeric.Natural+import Numeric.Natural (Natural) import Prelude hiding (read, concat, mapM_)  -- | Productive Stable Unordered Discriminator@@ -63,6 +55,12 @@              => (b -> m (b -> m ())) -> m (a -> b -> m ())   } deriving Typeable +-- Note: Group should be+--+--     type role Group representational+--+-- but it isn't due PrimMonad not implying higher-order Coercible constraint.+ instance Contravariant Group where   contramap f m = Group $ \k -> do     g <- getGroup m k@@ -124,6 +122,15 @@ --------------------------------------------------------------------------------  -- | 'Eq' equipped with a compatible stable unordered discriminator.+--+-- Law:+--+-- @+-- 'groupingEq' x y ≡ (x '==' y)+-- @+--+-- /Note:/ 'Eq' is a moral super class of 'Grouping'.+-- It isn't because of some missing instances. class Grouping a where   -- | For every surjection @f@,   --@@ -132,10 +139,8 @@   -- @    grouping :: Group a-#ifndef HLINT   default grouping :: Deciding Grouping a => Group a   grouping = deciding (Proxy :: Proxy Grouping) grouping-#endif  instance Grouping Void where grouping = lose id instance Grouping () where grouping = conquer@@ -152,25 +157,22 @@ instance Grouping Char where grouping = contramap (fromIntegral . fromEnum) groupingWord64  instance Grouping Bool+instance Grouping Ordering instance (Grouping a, Grouping b) => Grouping (a, b) instance (Grouping a, Grouping b, Grouping c) => Grouping (a, b, c) instance (Grouping a, Grouping b, Grouping c, Grouping d) => Grouping (a, b, c, d) instance Grouping a => Grouping [a]+instance Grouping a => Grouping (NonEmpty a) instance Grouping a => Grouping (Maybe a) instance (Grouping a, Grouping b) => Grouping (Either a b) instance Grouping a => Grouping (Complex a) where   grouping = divide (\(a :+ b) -> (a, b)) grouping grouping  instance Grouping Integer where-  grouping = contramap word8s grouping--word8s :: Integer -> [Word8]-word8s i = runST $ unsafeIOToST $ do-  p@(MutablePrimArray mba) :: MutablePrimArray RealWorld Word8 <- newPrimArray (fromIntegral $ W# (sizeInBaseInteger i 256#))-  _ <- exportIntegerToMutableByteArray i mba 0## 1#-  primArrayToList <$> unsafeFreezePrimArray p+  grouping = choose integerCases grouping (choose id grouping grouping) -instance Grouping Natural where grouping = contramap toInteger grouping+instance Grouping Natural where+  grouping = choose naturalCases grouping grouping  #if __GLASGOW_HASKELL__ >= 800 instance Grouping a => Grouping (Ratio a) where@@ -184,13 +186,12 @@  class Grouping1 f where   grouping1 :: Group a -> Group (f a)-#ifndef HLINT   default grouping1 :: Deciding1 Grouping f => Group a -> Group (f a)   grouping1 = deciding1 (Proxy :: Proxy Grouping) grouping-#endif  instance Grouping1 [] instance Grouping1 Maybe+instance Grouping1 NonEmpty instance Grouping a => Grouping1 (Either a) instance Grouping a => Grouping1 ((,) a) instance (Grouping a, Grouping b) => Grouping1 ((,,) a b)
src/Data/Discrimination/Internal.hs view
@@ -1,5 +1,7 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE ParallelListComp #-}+{-# LANGUAGE MagicHash #-} module Data.Discrimination.Internal   ( runs   , groupNum@@ -7,6 +9,8 @@   , updateBag   , updateSet   , spanEither+  , integerCases+  , naturalCases   ) where  import Data.Array as Array@@ -15,6 +19,19 @@ import qualified Data.List as List import Prelude hiding (read, concat) +import GHC.Word+import GHC.Exts+import Data.Primitive.Types (Prim)+import Data.Primitive.PrimArray++#ifdef MIN_VERSION_ghc_bignum+import GHC.Num.Integer+import GHC.Num.Natural+#else+import GHC.Natural+import GHC.Integer.GMP.Internals+#endif+ -------------------------------------------------------------------------------- -- * Utilities --------------------------------------------------------------------------------@@ -55,3 +72,44 @@ fromRight :: Either a b -> b fromRight (Right y) = y fromRight _ = error "unstable discriminator"++-------------------------------------------------------------------------------+-- * Integer and Natural+-------------------------------------------------------------------------------++integerCases :: Integer -> Either (Int,[Word]) (Either Int (Int,[Word]))+#ifdef MIN_VERSION_ghc_bignum+integerCases (IN b) = Left          $ decomposeBigNat b+integerCases (IS i) = Right . Left  $ I# i+integerCases (IP b) = Right . Right $ decomposeBigNat b+#else+integerCases (Jn# b) = Left          $ decomposeBigNat b+integerCases (S#  i) = Right . Left  $ I# i+integerCases (Jp# b) = Right . Right $ decomposeBigNat b+#endif+{-# INLINE integerCases #-}++naturalCases :: Natural -> Either Word (Int,[Word])+#ifdef MIN_VERSION_ghc_bignum+naturalCases (NS w) = Left $ W# w+naturalCases (NB b) = Right $ decomposeBigNat b+#else+naturalCases (NatS# w) = Left $ W# w+naturalCases (NatJ# b) = Right $ decomposeBigNat b+#endif+{-# INLINE naturalCases #-}++-- We need to reverse the limb array. Its stored least-significant word first+-- but for comparasion to work right we need most-significant words first.+#ifdef MIN_VERSION_ghc_bignum+decomposeBigNat :: ByteArray# -> (Int, [Word])+decomposeBigNat ba = let pa = PrimArray ba :: PrimArray Word in (sizeofPrimArray pa, primArrayToReverseList pa)+#else+decomposeBigNat :: BigNat -> (Int, [Word])+decomposeBigNat (BN# ba) = let pa = PrimArray ba :: PrimArray Word in (sizeofPrimArray pa, primArrayToReverseList pa)+#endif+{-# INLINE decomposeBigNat #-}++primArrayToReverseList :: Prim a => PrimArray a -> [a]+primArrayToReverseList xs = build (\c n -> foldlPrimArray (flip c) n xs)+{-# INLINE primArrayToReverseList #-}
src/Data/Discrimination/Internal/SmallArray.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE DeriveDataTypeable #-} -------------------------------------------------------------------------------- -- | -- Copyright   : (c) Edward Kmett 2015
src/Data/Discrimination/Internal/WordMap.hs view
@@ -3,11 +3,9 @@ {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE PatternGuards #-}-{-# LANGUAGE MultiWayIf #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Trustworthy #-}-{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-} {-# OPTIONS_GHC -Wall -funbox-strict-fields -fno-warn-orphans -fno-warn-type-defaults -O2 #-} #ifdef ST_HACK {-# OPTIONS_GHC -fno-full-laziness #-}
src/Data/Discrimination/Sorting.hs view
@@ -1,13 +1,8 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE MagicHash #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE MonoLocalBinds #-} {-# OPTIONS_GHC -fno-cse -fno-full-laziness #-}@@ -50,6 +45,7 @@ import Data.IntMap.Lazy as IntMap import Data.IntSet as IntSet import qualified Data.List as List+import Data.List.NonEmpty (NonEmpty) import Data.Map as Map import Data.Proxy import Data.Semigroup hiding (Any)@@ -57,9 +53,13 @@ import Data.Typeable import Data.Void import Data.Word-import Numeric.Natural+import Numeric.Natural (Natural) import Prelude hiding (read, concat) +-- $setup+-- >>> import qualified Data.Map as Map+-- >>> import qualified Data.IntMap as IntMap+ -------------------------------------------------------------------------------- -- * Common --------------------------------------------------------------------------------@@ -77,9 +77,7 @@   [(_, v)] -> [[v]]   _        -> f xs -#ifndef HLINT type role Sort representational-#endif  instance Contravariant Sort where   contramap f (Sort g) = Sort $ g . fmap (first f)@@ -108,6 +106,12 @@ --------------------------------------------------------------------------------  -- | 'Ord' equipped with a compatible stable, ordered discriminator.+--+-- Law:+--+-- @+-- 'sortingCompare' x y ≡ 'compare' x y+-- @ class Grouping a => Sorting a where   -- | For every strictly monotone-increasing function @f@:   --@@ -115,19 +119,17 @@   -- 'contramap' f 'sorting' ≡ 'sorting'   -- @   sorting :: Sort a-#ifndef HLINT   default sorting :: Deciding Sorting a => Sort a   sorting = deciding (Proxy :: Proxy Sorting) sorting-#endif  instance Sorting () where   sorting = conquer  instance Sorting Integer where-  sorting = contramap word8s sorting+  sorting = choose integerCases (desc sorting) (choose id sorting sorting)  instance Sorting Natural where-  sorting = contramap toInteger sorting+  sorting = choose naturalCases sorting sorting  instance Sorting Word8 where   sorting = contramap fromIntegral (sortingNat 256)@@ -155,10 +157,10 @@     | otherwise                        = contramap (fromIntegral :: Word -> Word64) sorting  instance Sorting Int8 where-  sorting = contramap (\x -> fromIntegral (x - minBound)) (sortingNat 256)+  sorting = contramap (\x -> fromIntegral x + 128) (sortingNat 256)  instance Sorting Int16 where-  sorting = contramap (\x -> fromIntegral (x - minBound)) (sortingNat 65536)+  sorting = contramap (\x -> fromIntegral x + 32768) (sortingNat 65536)  instance Sorting Int32 where   sorting = contramap (\x -> fromIntegral (x - minBound) :: Word32) sorting@@ -174,11 +176,11 @@     radices (c,b) = (x .&. 0x3ff, (unsafeShiftR x 10, (x,b))) where       x = fromEnum c --- TODO: Integer and Natural?- instance Sorting Void instance Sorting Bool+instance Sorting Ordering instance Sorting a => Sorting [a]+instance Sorting a => Sorting (NonEmpty a) instance Sorting a => Sorting (Maybe a) instance (Sorting a, Sorting b) => Sorting (Either a b) instance (Sorting a, Sorting b) => Sorting (a, b)@@ -189,15 +191,14 @@  class Grouping1 f => Sorting1 f  where   sorting1 :: Sort a -> Sort (f a)-#ifndef HLINT   default sorting1 :: Deciding1 Sorting f => Sort a -> Sort (f a)   sorting1 = deciding1 (Proxy :: Proxy Sorting) sorting-#endif  instance (Sorting1 f, Sorting1 g) => Sorting1 (Compose f g) where   sorting1 f = getCompose `contramap` sorting1 (sorting1 f)  instance Sorting1 []+instance Sorting1 NonEmpty instance Sorting1 Maybe instance Sorting a => Sorting1 (Either a) @@ -277,14 +278,21 @@ -- -- This is an asymptotically faster version of 'Data.Map.fromList', which exploits ordered discrimination. ----- >>> toMap [] == empty--- True+-- >>> toMap []+-- fromList [] -- -- >>> toMap [(5,"a"), (3 :: Int,"b"), (5, "c")]--- fromList [(5,"c"), (3,"b")]+-- fromList [(3,"b"),(5,"c")] --+-- >>> Map.fromList [(5,"a"), (3 :: Int,"b"), (5, "c")]+-- fromList [(3,"b"),(5,"c")]+-- -- >>> toMap [(5,"c"), (3,"b"), (5 :: Int, "a")]--- fromList [(5,"a"), (3,"b")]+-- fromList [(3,"b"),(5,"a")]+--+-- >>> Map.fromList [(5,"c"), (3,"b"), (5 :: Int, "a")]+-- fromList [(3,"b"),(5,"a")]+-- toMap :: Sorting k => [(k, v)] -> Map k v toMap kvs = Map.fromDistinctAscList $ last <$> runSort sorting [ (fst kv, kv) | kv <- kvs ] @@ -295,10 +303,13 @@ -- (Note: values combine in anti-stable order for compatibility with 'Data.Map.fromListWith') -- -- >>> toMapWith (++) [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5 :: Int,"c")]--- fromList [(3, "ab"), (5, "cba")]+-- fromList [(3,"ab"),(5,"cba")] ----- >>> toMapWith (++) [] == empty--- True+-- >>> Map.fromListWith (++) [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5 :: Int,"c")]+-- fromList [(3,"ab"),(5,"cba")]+--+-- >>> toMapWith (++) []+-- fromList [] toMapWith :: Sorting k => (v -> v -> v) -> [(k, v)] -> Map k v toMapWith f kvs0 = Map.fromDistinctAscList $ go <$> runSort sorting [ (fst kv, kv) | kv <- kvs0 ] where   go ((k,v):kvs) = (k, Prelude.foldl (flip (f . snd)) v kvs)@@ -312,10 +323,10 @@ -- -- >>> let f key new_value old_value = show key ++ ":" ++ new_value ++ "|" ++ old_value -- >>> toMapWithKey f [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5 :: Int,"c")]--- fromList [(3, "3:a|b"), (5, "5:c|5:b|a")]+-- fromList [(3,"3:a|b"),(5,"5:c|5:b|a")] ----- >>> toMapWithKey f [] == empty--- True+-- >>> toMapWithKey f []+-- fromList [] toMapWithKey :: Sorting k => (k -> v -> v -> v) -> [(k, v)] -> Map k v toMapWithKey f kvs0 = Map.fromDistinctAscList $ go <$> runSort sorting [ (fst kv, kv) | kv <- kvs0 ] where   go ((k,v):kvs) = (k, Prelude.foldl (flip (f k . snd)) v kvs)@@ -323,14 +334,21 @@  -- | /O(n)/. Construct an 'IntMap'. ----- >>> toIntMap [] == empty--- True+-- >>> toIntMap []+-- fromList [] -- -- >>> toIntMap [(5,"a"), (3,"b"), (5, "c")]--- fromList [(5,"c"), (3,"b")]+-- fromList [(3,"b"),(5,"c")] --+-- >>> IntMap.fromList [(5,"a"), (3,"b"), (5, "c")]+-- fromList [(3,"b"),(5,"c")]+-- -- >>> toIntMap [(5,"c"), (3,"b"), (5, "a")]--- fromList [(5,"a"), (3,"b")]+-- fromList [(3,"b"),(5,"a")]+--+-- >>> IntMap.fromList [(5,"c"), (3,"b"), (5, "a")]+-- fromList [(3,"b"),(5,"a")]+-- toIntMap :: [(Int, v)] -> IntMap v toIntMap kvs = IntMap.fromDistinctAscList $ last <$> runSort sorting [ (fst kv, kv) | kv <- kvs ] @@ -341,10 +359,13 @@ -- (Note: values combine in anti-stable order for compatibility with 'Data.IntMap.Lazy.fromListWith') -- -- >>> toIntMapWith (++) [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"c")]--- fromList [(3, "ab"), (5, "cba")]+-- fromList [(3,"ab"),(5,"cba")] ----- >>> toIntMapWith (++) [] == empty--- True+-- >>> IntMap.fromListWith (++) [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"c")]+-- fromList [(3,"ab"),(5,"cba")]+--+-- >>> toIntMapWith (++) []+-- fromList [] toIntMapWith :: (v -> v -> v) -> [(Int, v)] -> IntMap v toIntMapWith f kvs0 = IntMap.fromDistinctAscList $ go <$> runSort sorting [ (fst kv, kv) | kv <- kvs0 ] where   go ((k,v):kvs) = (k, Prelude.foldl (flip (f . snd)) v kvs)@@ -358,10 +379,13 @@ -- -- >>> let f key new_value old_value = show key ++ ":" ++ new_value ++ "|" ++ old_value -- >>> toIntMapWithKey f [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"c")]--- fromList [(3, "3:a|b"), (5, "5:c|5:b|a")]+-- fromList [(3,"3:a|b"),(5,"5:c|5:b|a")] ----- >>> toIntMapWithKey f [] == empty--- True+-- >>> IntMap.fromListWithKey f [(5,"a"), (5,"b"), (3,"b"), (3,"a"), (5,"c")]+-- fromList [(3,"3:a|b"),(5,"5:c|5:b|a")]+--+-- >>> toIntMapWithKey f []+-- fromList [] toIntMapWithKey :: (Int -> v -> v -> v) -> [(Int, v)] -> IntMap v toIntMapWithKey f kvs0 = IntMap.fromDistinctAscList $ go <$> runSort sorting [ (fst kv, kv) | kv <- kvs0 ] where   go ((k,v):kvs) = (k, Prelude.foldl (flip (f k . snd)) v kvs)
+ test/tests.hs view
@@ -0,0 +1,203 @@+{-# LANGUAGE ScopedTypeVariables #-}+module Main (main) where++import Data.Complex (Complex)+import Data.Int (Int8, Int16, Int32, Int64)+import Data.List.NonEmpty (NonEmpty)+import Data.Proxy (Proxy (..))+import Data.Typeable (Typeable, typeRep)+import Data.Word (Word8, Word16, Word32, Word64)+import Numeric.Natural (Natural)+import Test.QuickCheck (Arbitrary (..), Property, counterexample, label, (===),+                        sized, chooseInt, vectorOf)+import Test.QuickCheck.Instances ()+import Test.Tasty (defaultMain, testGroup, TestTree)+import Test.Tasty.QuickCheck (testProperty)++import qualified Data.List as L++import Data.Discrimination+import Utils++main :: IO ()+main = defaultMain $ testGroup "discrimination"+  [ testGroup "examples"+    [ testGroup "nub"+      [ testProperty "List.nub" $+        let prop :: [Word64] -> Property+            prop xs = L.nub xs === nub xs+        in prop++      , testProperty "ordNub" $+        let prop :: [Word64] -> Property+            prop xs = ordNub xs === nub xs+        in prop++      , testProperty "hashNub" $+        let prop :: [Word64] -> Property+            prop xs = hashNub xs === nub xs+        in prop+      ]++    , testGroup "sort"+      [ testProperty "List.sort" $+        let prop :: [Word64] -> Property+            prop xs = L.sort xs === sort xs+        in prop++      , testProperty "introsort" $+        -- for Word64 unstable sort works too+        let prop :: [Word64] -> Property+            prop xs = introsort xs === sort xs+        in prop++      , testProperty "mergesort" $+        let prop :: [Word64] -> Property+            prop xs = mergesort xs === sort xs+        in prop+      ]+    ]++  , testGroup "Grouping"+    [ testGrouping (Proxy :: Proxy ())+    , testGrouping (Proxy :: Proxy Int)+    , testGrouping (Proxy :: Proxy Int8)+    , testGrouping (Proxy :: Proxy Int16)+    , testGrouping (Proxy :: Proxy Int32)+    , testGrouping (Proxy :: Proxy Int64)+    , testGrouping (Proxy :: Proxy Word)+    , testGrouping (Proxy :: Proxy Word8)+    , testGrouping (Proxy :: Proxy Word16)+    , testGrouping (Proxy :: Proxy Word32)+    , testGrouping (Proxy :: Proxy Word64)+    , testGrouping (Proxy :: Proxy Bool)+    , testGrouping (Proxy :: Proxy Ordering)+    , testGrouping (Proxy :: Proxy (Word8,Word8))+    , testGrouping (Proxy :: Proxy (Word8,Word8,Word8))+    , testGrouping (Proxy :: Proxy (Word8,Word8,Word8,Word8))+    , testGrouping (Proxy :: Proxy Rational)+    , testGrouping (Proxy :: Proxy (Complex Word8))+    , testGrouping (Proxy :: Proxy (Maybe Word8))+    , testGrouping (Proxy :: Proxy (Either Word8 Word8))+    , testGrouping (Proxy :: Proxy Char)+    , testGrouping (Proxy :: Proxy String)+    , testGrouping (Proxy :: Proxy (NonEmpty Int))+    , testGrouping (Proxy :: Proxy Natural)+    , testGrouping (Proxy :: Proxy Integer)++    , testGrouping' listToNatural+    , testGrouping' listToInteger+    ]++  , testGroup "Sorting"+    [ testSorting (Proxy :: Proxy ())+    , testSorting (Proxy :: Proxy Int)+    , testSorting (Proxy :: Proxy Int8)+    , testSorting (Proxy :: Proxy Int16)+    , testSorting (Proxy :: Proxy Int32)+    , testSorting (Proxy :: Proxy Int64)+    , testSorting (Proxy :: Proxy Word)+    , testSorting (Proxy :: Proxy Word8)+    , testSorting (Proxy :: Proxy Word16)+    , testSorting (Proxy :: Proxy Word32)+    , testSorting (Proxy :: Proxy Word64)+    , testSorting (Proxy :: Proxy Bool)+    , testSorting (Proxy :: Proxy Ordering)+    , testSorting (Proxy :: Proxy (Word8,Word8))+    , testSorting (Proxy :: Proxy (Word8,Word8,Word8))+    , testSorting (Proxy :: Proxy (Word8,Word8,Word8,Word8))+    , testSorting (Proxy :: Proxy (Maybe Word8))+    , testSorting (Proxy :: Proxy (Either Word8 Word8))+    , testSorting (Proxy :: Proxy Char)+    , testSorting (Proxy :: Proxy String)+    , testSorting (Proxy :: Proxy (NonEmpty Int))+    , testSorting (Proxy :: Proxy Natural)+    , testSorting (Proxy :: Proxy Integer)++    , testSorting' listToNatural+    , testSorting' listToInteger+    ]+  ]++listToNatural :: SmallList Word64 -> Natural+listToNatural = L.foldl' (\x y -> x * 2 ^ (64 :: Int) + fromIntegral y) 0 . getSmallList++listToInteger :: SmallList Int64 -> Integer+listToInteger = L.foldl' (\x y -> x * 2 ^ (64 :: Int) + fromIntegral y) 0 . getSmallList++newtype SmallList a = SmallList { getSmallList :: [a] } deriving (Eq, Show)++instance Arbitrary a => Arbitrary (SmallList a) where+    arbitrary = sized $ \n -> do+        m <- chooseInt (0, min 10 n)+        SmallList <$> vectorOf m arbitrary++    shrink = fmap SmallList . shrink . getSmallList++testGrouping+  :: forall a. (Grouping a, Typeable a, Arbitrary a, Eq a, Show a)+  => Proxy a+  -> TestTree+testGrouping _ = testGrouping' (id :: a -> a)++testGrouping'+  :: forall a b. (Grouping b, Typeable a, Typeable b, Arbitrary a, Eq b, Show a, Show b)+  => (a -> b)+  -> TestTree+testGrouping' f = testGroup name+    [ testProperty "groupingEq" prop_eq+    , testProperty "nub"        prop_nub+    ]+  where+    tra = typeRep (Proxy :: Proxy a)+    trb = typeRep (Proxy :: Proxy b)+    name = if tra == trb then show tra else show trb ++ " from " ++ show tra++    prop_eq :: a -> a -> Property+    prop_eq x' y' =+        counterexample (show (x,y)) $+        label (show lhs) $+        lhs === groupingEq x y+      where+        x = f x'+        y = f y'+        lhs = x == y++    prop_nub :: [a] -> Property+    prop_nub xs' = L.nub xs === nub xs+      where+        xs = take 100 (map f xs')++testSorting+  :: forall a. (Sorting a, Typeable a, Arbitrary a, Ord a, Show a)+  => Proxy a+  -> TestTree+testSorting _ = testSorting' (id :: a -> a)++testSorting'+  :: forall a b. (Sorting b, Typeable a, Typeable b, Arbitrary a, Ord b, Show a, Show b)+  => (a -> b)+  -> TestTree+testSorting' f = testGroup name+    [ testProperty "sortingCompare" prop_cmp+    , testProperty "sort"           prop_sort+    ]+  where+    tra = typeRep (Proxy :: Proxy a)+    trb = typeRep (Proxy :: Proxy b)+    name = if tra == trb then show tra else show trb ++ " from " ++ show tra++    prop_cmp :: a -> a -> Property+    prop_cmp x' y' =+        counterexample (show (x,y)) $+        label (show lhs) $+        lhs === sortingCompare x y+      where+        x = f x'+        y = f y'+        lhs = compare x y++    prop_sort :: [a] -> Property+    prop_sort xs' = L.sort xs === sort xs+      where+        xs = map f xs'