vector-hashtables-0.1.1.3/0000755000000000000000000000000007346545000013461 5ustar0000000000000000vector-hashtables-0.1.1.3/LICENSE0000644000000000000000000000277007346545000014474 0ustar0000000000000000Copyright Author name here (c) 2017 All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Author name here nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.vector-hashtables-0.1.1.3/README.md0000644000000000000000000001735007346545000014746 0ustar0000000000000000# vector-hashtables [![Hackage](https://img.shields.io/hackage/v/vector-hashtables.svg?label=Hackage)](https://hackage.haskell.org/package/vector-hashtables) [![Stackage Nightly Version](https://www.stackage.org/package/vector-hashtables/badge/nightly?label=Stackage/Nightly)](https://www.stackage.org/package/vector-hashtables) [![Stackage LTS Version](https://www.stackage.org/package/vector-hashtables/badge/lts?label=Stackage/LTS)](https://www.stackage.org/package/vector-hashtables) [![Build Status](https://github.com/klapaucius/vector-hashtables/workflows/Haskell-CI/badge.svg)](https://github.com/klapaucius/vector-hashtables/actions?query=workflow%3Ahaskell-ci) A brief history of this library is given in [this blog post](https://an-pro.org/posts/12-vector-hashtables.html). ## Benchmarks vs `hashtables` (and `vector` where relevant) Benchmarks below are produced under GHC 9.2.5, and can be reproduced locally with ```shellsession cabal bench --benchmark-options="--csv results.csv" bench-show report results.csv ``` (You will need the `bench-show` tool, which is available from Hackage.) ``` Benchmark default(ns) ----------------------------------------------------------------- ------------ Comparison/1000/insert/hashtables basic 47112.15 Comparison/1000/insert/vector-hashtables boxed 22941.55 Comparison/1000/insert/vector-hashtables unboxed keys 22338.54 Comparison/1000/insert/vector-hashtables 19460.33 Comparison/1000/insert/mutable vector boxed 3216.07 Comparison/1000/insert/mutable vector 659.46 Comparison/1000/insert (resize)/hashtables basic 158026.86 Comparison/1000/insert (resize)/vector-hashtables boxed 42164.21 Comparison/1000/insert (resize)/vector-hashtables unboxed keys 40334.65 Comparison/1000/insert (resize)/vector-hashtables 37761.08 Comparison/1000/insert, delete/hashtables basic 85218.96 Comparison/1000/insert, delete/vector-hashtables 36199.84 Comparison/1000/find/hashtables basic 33857.19 Comparison/1000/find/vector-hashtables 14205.24 Comparison/1000/find/vector-hashtables (frozen) 12257.88 Comparison/1000/lookupIndex/hashtables basic 31842.70 Comparison/1000/lookupIndex/vector-hashtables 14140.17 Comparison/1000/fromList/hashtables basic 161048.28 Comparison/1000/fromList/vector-hashtables 48052.28 Comparison/1000/toList/hashtables basic 9569.82 Comparison/1000/toList/vector-hashtables 12600.58 Comparison/10000/insert/hashtables basic 983062.29 Comparison/10000/insert/vector-hashtables boxed 225218.39 Comparison/10000/insert/vector-hashtables unboxed keys 212758.35 Comparison/10000/insert/vector-hashtables 186573.02 Comparison/10000/insert/mutable vector boxed 37415.65 Comparison/10000/insert/mutable vector 6956.86 Comparison/10000/insert (resize)/hashtables basic 1696364.18 Comparison/10000/insert (resize)/vector-hashtables boxed 479122.17 Comparison/10000/insert (resize)/vector-hashtables unboxed keys 422759.56 Comparison/10000/insert (resize)/vector-hashtables 352441.24 Comparison/10000/insert, delete/hashtables basic 1426337.13 Comparison/10000/insert, delete/vector-hashtables 359228.91 Comparison/10000/find/hashtables basic 330890.67 Comparison/10000/find/vector-hashtables 141525.60 Comparison/10000/find/vector-hashtables (frozen) 122542.86 Comparison/10000/lookupIndex/hashtables basic 327065.33 Comparison/10000/lookupIndex/vector-hashtables 142678.55 Comparison/10000/fromList/hashtables basic 1641384.65 Comparison/10000/fromList/vector-hashtables 542530.31 Comparison/10000/toList/hashtables basic 143188.55 Comparison/10000/toList/vector-hashtables 274318.45 Comparison/100000/insert/hashtables basic 10907947.88 Comparison/100000/insert/vector-hashtables boxed 6932748.81 Comparison/100000/insert/vector-hashtables unboxed keys 5307166.72 Comparison/100000/insert/vector-hashtables 2368482.76 Comparison/100000/insert/mutable vector boxed 1794351.95 Comparison/100000/insert/mutable vector 69989.51 Comparison/100000/insert (resize)/hashtables basic 22232168.04 Comparison/100000/insert (resize)/vector-hashtables boxed 11420001.24 Comparison/100000/insert (resize)/vector-hashtables unboxed keys 9496935.97 Comparison/100000/insert (resize)/vector-hashtables 5988321.89 Comparison/100000/insert, delete/hashtables basic 15119684.92 Comparison/100000/insert, delete/vector-hashtables 4058662.48 Comparison/100000/find/hashtables basic 3473800.70 Comparison/100000/find/vector-hashtables 1431873.00 Comparison/100000/find/vector-hashtables (frozen) 1225155.68 Comparison/100000/lookupIndex/hashtables basic 3235041.49 Comparison/100000/lookupIndex/vector-hashtables 1439338.81 Comparison/100000/fromList/hashtables basic 22917643.59 Comparison/100000/fromList/vector-hashtables 8603353.43 Comparison/100000/toList/hashtables basic 5336633.37 Comparison/100000/toList/vector-hashtables 9377042.71 Comparison/1000000/insert/hashtables basic 109193301.77 Comparison/1000000/insert/vector-hashtables boxed 77176586.83 Comparison/1000000/insert/vector-hashtables unboxed keys 59037764.29 Comparison/1000000/insert/vector-hashtables 28535981.51 Comparison/1000000/insert/mutable vector boxed 29709444.79 Comparison/1000000/insert/mutable vector 866220.55 Comparison/1000000/insert (resize)/hashtables basic 260585504.79 Comparison/1000000/insert (resize)/vector-hashtables boxed 127608418.79 Comparison/1000000/insert (resize)/vector-hashtables unboxed keys 96059752.01 Comparison/1000000/insert (resize)/vector-hashtables 57038345.24 Comparison/1000000/insert, delete/hashtables basic 153182769.34 Comparison/1000000/insert, delete/vector-hashtables 47664402.90 Comparison/1000000/find/hashtables basic 35821229.20 Comparison/1000000/find/vector-hashtables 15711648.56 Comparison/1000000/find/vector-hashtables (frozen) 12885372.25 Comparison/1000000/lookupIndex/hashtables basic 32210529.62 Comparison/1000000/lookupIndex/vector-hashtables 14911308.87 Comparison/1000000/fromList/hashtables basic 446988819.28 Comparison/1000000/fromList/vector-hashtables 92793579.75 Comparison/1000000/toList/hashtables basic 66679253.65 Comparison/1000000/toList/vector-hashtables 105470198.31 ``` vector-hashtables-0.1.1.3/Setup.hs0000644000000000000000000000005607346545000015116 0ustar0000000000000000import Distribution.Simple main = defaultMain vector-hashtables-0.1.1.3/bench/0000755000000000000000000000000007346545000014540 5ustar0000000000000000vector-hashtables-0.1.1.3/bench/Main.hs0000644000000000000000000002526307346545000015770 0ustar0000000000000000{-# LANGUAGE BangPatterns #-} {-# LANGUAGE TypeFamilies #-} module Main (main) where import qualified Data.Vector.Storable as V import qualified Data.Vector.Storable.Mutable as VM import qualified Data.Vector.Mutable as BV import Control.Monad.Primitive (PrimMonad(PrimState)) import qualified Data.HashTable.IO as H import Criterion.Main (bench, bgroup, defaultMain, nfIO) import Criterion (Benchmark) import qualified Data.Vector.Hashtables.Internal as VH vh :: Int -> IO (VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int) vh n = do ht <- VH.initialize n :: IO (VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 return ht fvh :: Int -> IO (VH.FrozenDictionary V.Vector Int V.Vector Int) fvh n = do h <- vh n c <- VH.clone h VH.unsafeFreeze c bh :: Int -> IO (H.BasicHashTable Int Int) bh n = do ht <- H.newSized n :: IO (H.BasicHashTable Int Int) let go !i | i <= n = H.insert ht i i >> go (i + 1) | otherwise = return () go 0 return ht fl :: Int -> [(Int, Int)] fl n = mkPair <$> [0 .. n] where mkPair !x = (x, x) vhfind :: Int -> VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int -> IO Int vhfind n ht = do let go !i !s | i <= n = do x <- VH.findEntry ht i go (i + 1) (s + x) | otherwise = return s go 0 0 fvhfind :: Int -> VH.FrozenDictionary V.Vector Int V.Vector Int -> IO Int fvhfind n ht = return $ go 0 0 where go !i !s | i <= n = go (i + 1) (s + VH.findElem ht i) | otherwise = s bhfind :: Int -> H.BasicHashTable Int Int -> IO Int bhfind n ht = do let go !i !s | i <= n = do Just x <- H.lookup ht i go (i + 1) (s + x) | otherwise = return s go 0 0 htb :: Int -> IO () htb n = do ht <- H.newSized n :: IO (H.BasicHashTable Int Int) let go !i | i <= n = H.insert ht i i >> go (i + 1) | otherwise = return () go 0 vht :: Int -> IO () vht n = do ht <- VH.initialize n :: IO (VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 vhtd :: Int -> IO () vhtd n = do ht <- VH.initialize n :: IO (VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 let go1 !i | i <= n = VH.delete ht i >> go1 (i + 1) | otherwise = return () go1 0 htbd :: Int -> IO () htbd n = do ht <- H.newSized n :: IO (H.BasicHashTable Int Int) let go !i | i <= n = H.insert ht i i >> go (i + 1) | otherwise = return () go 0 let go1 !i | i <= n = H.delete ht i >> go1 (i + 1) | otherwise = return () go1 0 vhtb :: Int -> IO () vhtb n = do ht <- VH.initialize n :: IO (VH.Dictionary (PrimState IO) BV.MVector Int BV.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 vhtk :: Int -> IO () vhtk n = do ht <- VH.initialize n :: IO (VH.Dictionary (PrimState IO) VM.MVector Int BV.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 htbg :: Int -> IO () htbg n = do ht <- H.newSized 1 :: IO (H.BasicHashTable Int Int) let go !i | i <= n = H.insert ht i i >> go (i + 1) | otherwise = return () go 0 vhtg :: Int -> IO () vhtg n = do ht <- VH.initialize 1 :: IO (VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 vhtbg :: Int -> IO () vhtbg n = do ht <- VH.initialize 1 :: IO (VH.Dictionary (PrimState IO) BV.MVector Int BV.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 vhtkg :: Int -> IO () vhtkg n = do ht <- VH.initialize 1 :: IO (VH.Dictionary (PrimState IO) VM.MVector Int BV.MVector Int) let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 mvb :: Int -> IO () mvb n = do ht <- BV.new (n+1) let go !i | i <= n = BV.write ht i i >> go (i + 1) | otherwise = return () go 0 mv :: Int -> IO () mv n = do ht <- VM.new (n+1) let go !i | i <= n = VM.write ht i i >> go (i + 1) | otherwise = return () go 0 bhfromList l = do _bht <- H.fromList l :: IO (H.BasicHashTable Int Int) return () vhfromList l = do _ht <- VH.fromList l :: IO (VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int) return () bhlookupIndex :: Int -> H.BasicHashTable Int Int -> IO Int bhlookupIndex n ht = do let go !i !s | i <= n = do Just x <- H.lookupIndex ht i go (i + 1) (s + fromIntegral x) | otherwise = return s go 0 0 vhlookupIndex :: Int -> VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int -> IO Int vhlookupIndex n ht = do let go !i !s | i <= n = do Just x <- VH.lookupIndex ht i go (i + 1) (s + x) | otherwise = return s go 0 0 bhtoList = H.toList vhtoList = VH.toList bgc :: Int -> IO Benchmark bgc n = do h <- vh n h2 <- bh n fh <- fvh n let l = fl n return $ bgroup (show n) [ bgroup "insert" [ bench "hashtables basic" $ nfIO (htb n) , bench "vector-hashtables boxed" $ nfIO (vhtb n) , bench "vector-hashtables unboxed keys" $ nfIO (vhtk n) , bench "vector-hashtables" $ nfIO (vht n) , bench "mutable vector boxed" $ nfIO (mvb n) , bench "mutable vector" $ nfIO (mv n) ] , bgroup "insert (resize)" [ bench "hashtables basic" $ nfIO (htbg n) , bench "vector-hashtables boxed" $ nfIO (vhtbg n) , bench "vector-hashtables unboxed keys" $ nfIO (vhtkg n) , bench "vector-hashtables" $ nfIO (vhtg n) ] , bgroup "insert, delete" [ bench "hashtables basic" $ nfIO (htbd n) , bench "vector-hashtables" $ nfIO (vhtd n) ] , bgroup "find" [ bench "hashtables basic" $ nfIO (bhfind n h2) , bench "vector-hashtables" $ nfIO (vhfind n h) , bench "vector-hashtables (frozen)" $ nfIO (fvhfind n fh) ] , bgroup "lookupIndex" [ bench "hashtables basic" $ nfIO (bhlookupIndex n h2) , bench "vector-hashtables" $ nfIO (vhlookupIndex n h) ] , bgroup "fromList" [ bench "hashtables basic" $ nfIO (bhfromList l) , bench "vector-hashtables" $ nfIO (vhfromList l) ] , bgroup "toList" [ bench "hashtables basic" $ nfIO (bhtoList h2) , bench "vector-hashtables" $ nfIO (vhtoList h) ]] main :: IO () main = do let inputs = [1000,10000,100000,1000000] comparisonBench <- mapM bgc inputs utilitiesBench <- mapM utilities inputs defaultMain [ bgroup "Comparison" comparisonBench , bgroup "Utilities" utilitiesBench ] -- ** Utilities benchmark utilities n = do -- utilities input data hAt' <- vh n hInsert <- vh n hDelete <- vh n hLookup <- vh n hLookup' <- vh n hLookupIndex <- vh n hNull <- vh n hLength <- vh n hSize <- vh n hMember <- vh n hFindWithDefault <- vh n hAlter <- vh n hAlterM <- vh n hUnion1 <- vh n hUnion2 <- vh n hDifference1 <- vh n hDifference2 <- vh n hIntersection1 <- vh n hIntersection2 <- vh n hFromList <- VH.toList =<< vh n hToList <- vh n return $ bgroup (show n) [ bench "at'" $ nfIO (bhuat' n hAt') , bench "insert" $ nfIO (bhuinsert n hInsert) , bench "delete" $ nfIO (bhudelete n hDelete) , bench "lookup" $ nfIO (bhulookup n hLookup) , bench "lookup'" $ nfIO (bhulookup' n hLookup') , bench "lookupIndex" $ nfIO (bhulookupIndex n hLookupIndex) , bench "null" $ nfIO (bhunull hNull) , bench "length" $ nfIO (bhulength hLength) , bench "size" $ nfIO (bhusize hSize) , bench "member" $ nfIO (bhumember n hMember) , bench "findWithDefault" $ nfIO (bhufindWithDefault n hFindWithDefault) , bench "alter" $ nfIO (bhualter n hAlter) , bench "alterM" $ nfIO (bhualterM n hAlterM) , bench "union" $ nfIO (bhuunion hUnion1 hUnion2) , bench "difference" $ nfIO (bhudifference hDifference1 hDifference2) , bench "intersection" $ nfIO (bhuintersection hIntersection1 hIntersection2) , bench "fromList" $ nfIO (bhufromList hFromList) , bench "toList" $ nfIO (VH.toList hToList) ] bhuat' n ht = do let go !i | i <= n = VH.at' ht i >> go (i + 1) | otherwise = return () go 0 bhuinsert n ht = do let go !i | i <= n = VH.insert ht i i >> go (i + 1) | otherwise = return () go 0 bhudelete n ht = do let go !i | i <= n = VH.delete ht i >> go (i + 1) | otherwise = return () go 0 bhulookup n ht = do let go !i | i <= n = VH.lookup ht i >> go (i + 1) | otherwise = return () go 0 bhulookup' n ht = do let go !i | i <= n = VH.lookup' ht i >> go (i + 1) | otherwise = return () go 0 bhulookupIndex n ht = do let go !i | i <= n = VH.lookupIndex ht i >> go (i + 1) | otherwise = return () go 0 bhunull = VH.null bhulength = VH.length bhusize = VH.size bhumember n ht = do let go !i | i <= n = VH.member ht i >> go (i + 1) | otherwise = return () go 0 bhufindWithDefault n ht = do let go !i | i <= n = VH.findWithDefault ht 0 i >> go (i + 1) | otherwise = return () go 0 bhualter n ht = do let go !i | i <= n = VH.alter ht (fmap succ) i >> go (i + 1) | otherwise = return () go 0 bhualterM :: Int -> VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int -> IO () bhualterM n ht = do let f = return . fmap succ go !i | i <= n = VH.alterM ht f i >> go (i + 1) | otherwise = return () go 0 bhuunion ht1 ht2 = VH.union ht1 ht2 >> return () bhudifference ht1 ht2 = VH.difference ht1 ht2 >> return () bhuintersection ht1 ht2 = VH.intersection ht1 ht2 >> return () bhufromList htlist = do ht <- VH.fromList htlist :: IO (VH.Dictionary (PrimState IO) VM.MVector Int VM.MVector Int) return () bhutoList ht = VH.toList ht >> return () vector-hashtables-0.1.1.3/changelog.md0000644000000000000000000000174207346545000015736 0ustar0000000000000000# 0.1.1.3 (2023-04-23) * cleanup the cabal file * CI based on Haskell-CI (see [#15](https://github.com/klapaucius/vector-hashtables/pull/15)) * readme: minimal improvement of benchmark data presentation (see [#16](https://github.com/klapaucius/vector-hashtables/pull/16)) * bump hspec to <2.12 (see [#17](https://github.com/klapaucius/vector-hashtables/pull/17)) # 0.1.1.2 (2023-01-31) - Relax `hspec` boundaries (see [#14](https://github.com/klapaucius/vector-hashtables/pull/14)). - Set lower bound for `primtive` (see [#12](https://github.com/klapaucius/vector-hashtables/pull/12)). # 0.1.1.1 (2021-09-10) - Optimise `insertWithIndex` function ([#10](https://github.com/klapaucius/vector-hashtables/pull/10)). # 0.1.1.0 (2021-09-10) - Add `alter` function to public interface ([#9](https://github.com/klapaucius/vector-hashtables/pull/9)). # 0.1.0.1 (2021-09-10) - Remove outdated executable in favor of benchmark. # 0.1.0.0 (2021-09-07) - Release vector-hastables to the world. vector-hashtables-0.1.1.3/src-gen/Data/Vector/Hashtables/Internal/0000755000000000000000000000000007346545000022764 5ustar0000000000000000vector-hashtables-0.1.1.3/src-gen/Data/Vector/Hashtables/Internal/Mask.hs0000644000000000000000000000057507346545000024222 0ustar0000000000000000{-| Module : Data.Vector.Hashtables.Internal.Mask Description : Provides arch-dependent mask for hashtables. Copyright : (c) klapaucius, swamp_agr, 2016-2021 License : BSD3 -} module Data.Vector.Hashtables.Internal.Mask where -- | 'Int' mask. For 32-bit it is equal to @0x7FFFFFFF@. Otherwise, @0x7FFFFFFFFFFFFFFF@. mask = 0x7FFFFFFFFFFFFFFF :: Int {-# INLINE mask #-} vector-hashtables-0.1.1.3/src-i386/Data/Vector/Hashtables/Internal/0000755000000000000000000000000007346545000022704 5ustar0000000000000000vector-hashtables-0.1.1.3/src-i386/Data/Vector/Hashtables/Internal/Mask.hs0000644000000000000000000000056507346545000024141 0ustar0000000000000000{-| Module : Data.Vector.Hashtables.Internal.Mask Description : Provides arch-dependent mask for hashtables. Copyright : (c) klapaucius, swamp_agr, 2016-2021 License : BSD3 -} module Data.Vector.Hashtables.Internal.Mask where -- | 'Int' mask. For 32-bit it is equal to @0x7FFFFFFF@. Otherwise, @0x7FFFFFFFFFFFFFFF@. mask = 0x7FFFFFFF :: Int {-# INLINE mask #-} vector-hashtables-0.1.1.3/src/Data/Primitive/PrimArray/0000755000000000000000000000000007346545000020777 5ustar0000000000000000vector-hashtables-0.1.1.3/src/Data/Primitive/PrimArray/Utils.hs0000644000000000000000000000360007346545000022432 0ustar0000000000000000{-| Module : Data.Primitive.PrimArray.Utils Description : Provides useful utilities for operating with mutable primitive arrays. Copyright : (c) klapaucius, swamp_agr, 2016-2021 License : BSD3 -} module Data.Primitive.PrimArray.Utils where import Data.Primitive.PrimArray import Control.Monad.Primitive import Data.Primitive replicate :: (PrimMonad m, Prim a) => Int -> a -> m (MutablePrimArray (PrimState m) a) replicate n x = do xs <- newPrimArray n setPrimArray xs 0 (sizeofMutablePrimArray xs) x return xs {-# INLINE replicate #-} clone :: (PrimMonad m, Prim a) => MutablePrimArray (PrimState m) a -> m (MutablePrimArray (PrimState m) a) clone xs = cloneMutablePrimArray xs 0 (sizeofMutablePrimArray xs) {-# INLINE clone #-} unsafeFreeze :: PrimMonad m => MutablePrimArray (PrimState m) a -> m (PrimArray a) unsafeFreeze = unsafeFreezePrimArray {-# INLINE unsafeFreeze #-} unsafeThaw :: PrimMonad m => PrimArray a -> m (MutablePrimArray (PrimState m) a) unsafeThaw = unsafeThawPrimArray {-# INLINE unsafeThaw #-} growWith :: (PrimMonad m, Prim a) => a -> MutablePrimArray (PrimState m) a -> Int -> m (MutablePrimArray (PrimState m) a) growWith a xs delta = do r <- growNoZ xs delta setPrimArray r (sizeofMutablePrimArray xs) delta a return r {-# INLINE growWith #-} growNoZ :: (PrimMonad m, Prim a) => MutablePrimArray (PrimState m) a -> Int -> m (MutablePrimArray (PrimState m) a) growNoZ xs delta = resizeMutablePrimArray xs (sizeofMutablePrimArray xs + delta) {-# INLINE growNoZ #-} freeze :: (PrimMonad m, Prim a) => MutablePrimArray (PrimState m) a -> m (PrimArray a) freeze xs = do r <- unsafeFreezePrimArray xs return $ clonePrimArray r 0 (sizeofPrimArray r) {-# INLINE freeze #-} length :: Prim a => MutablePrimArray s a -> Int length = sizeofMutablePrimArray {-# INLINE length #-} vector-hashtables-0.1.1.3/src/Data/Vector/0000755000000000000000000000000007346545000016363 5ustar0000000000000000vector-hashtables-0.1.1.3/src/Data/Vector/Hashtables.hs0000644000000000000000000000371407346545000021002 0ustar0000000000000000{-| Module : Data.Vector.Hashtables Description : Provides hashtables, basic interface and set of utilities. Copyright : (c) klapaucius, swamp_agr, 2016-2021 License : BSD3 -} module Data.Vector.Hashtables ( -- * Documentation -- $doc -- ** Usage -- $usage -- ** Types Dictionary (..) , FrozenDictionary (..) , findElem , Dictionary_ (..) , findEntry -- ** Construction , initialize , clone -- ** Basic interface , null , size , keys , values , lookup , lookup' , insert , delete , alter , alterM -- ** Combine -- *** Union , union , unionWith , unionWithKey -- *** Difference , difference , differenceWith -- *** Intersection , intersection , intersectionWith , intersectionWithKey -- ** Conversions -- *** Mutable , unsafeFreeze , unsafeThaw -- *** List , fromList , toList , module Control.Monad.Primitive ) where import Prelude hiding (null, lookup) import Control.Monad.Primitive import Data.Vector.Hashtables.Internal -- $doc -- -- - This package provides hashtable implementation similar to .NET Generic Dictionary implementation (at the time of 2015) . -- -- - It was originated as response to . -- -- - For more hashtables implementations see . -- $usage -- -- >>> import qualified Data.Vector.Storable.Mutable as VM -- >>> import qualified Data.Vector.Unboxed.Mutable as UM -- >>> import Data.Vector.Hashtables -- >>> type HashTable k v = Dictionary (PrimState IO) VM.MVector k UM.MVector v -- >>> ht <- initialize 0 :: IO (HashTable Int Int) -- >>> insert ht 0 1 -- vector-hashtables-0.1.1.3/src/Data/Vector/Hashtables/0000755000000000000000000000000007346545000020441 5ustar0000000000000000vector-hashtables-0.1.1.3/src/Data/Vector/Hashtables/Internal.hs0000644000000000000000000007376607346545000022574 0ustar0000000000000000{-| Module : Data.Vector.Hashtables.Internal Description : Provides internals of hashtables and set of utilities. Copyright : (c) klapaucius, swamp_agr, 2016-2021 License : BSD3 -} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-} module Data.Vector.Hashtables.Internal where import Control.Monad import Control.Monad.Primitive import Data.Bits import Data.Hashable import Data.Maybe import Data.Primitive.MutVar import Data.Vector.Generic (Mutable, Vector) import qualified Data.Vector.Generic as VI import Data.Vector.Generic.Mutable (MVector) import qualified Data.Vector.Generic.Mutable as V import qualified Data.Vector.Mutable as B import qualified Data.Vector.Storable as SI import qualified Data.Vector.Storable.Mutable as S import qualified Data.Vector.Unboxed as UI import qualified Data.Vector.Unboxed.Mutable as U import qualified GHC.Exts as Exts import Prelude hiding (length, lookup) import qualified Data.Primitive.PrimArray as A import qualified Data.Primitive.PrimArray.Utils as A import Data.Vector.Hashtables.Internal.Mask (mask) -- | Alias for 'MutablePrimArray s Int'. type IntArray s = A.MutablePrimArray s Int -- | Single-element mutable array of 'Dictionary_' with primitive state token parameterized with state, keys and values types. -- -- *Example*: -- -- >>> import qualified Data.Vector.Storable.Mutable as VM -- >>> import qualified Data.Vector.Unboxed.Mutable as UM -- >>> import Data.Vector.Hashtables -- >>> type HashTable k v = Dictionary (PrimState IO) VM.MVector k UM.MVector v -- -- Different vectors could be used for keys and values: -- -- - storable, -- - mutable, -- - unboxed. -- -- In most cases unboxed vectors should be used. Nevertheless, it is up to you to decide about final form of hastable. newtype Dictionary s ks k vs v = DRef { getDRef :: MutVar s (Dictionary_ s ks k vs v) } -- | Represents collection of hashtable internal primitive arrays and vectors. -- -- - hash codes, -- -- - references to the next element, -- -- - buckets, -- -- - keys -- -- - and values. -- data Dictionary_ s ks k vs v = Dictionary { hashCode, next, buckets, refs :: IntArray s, key :: ks s k, value :: vs s v } getCount, getFreeList, getFreeCount :: Int getCount = 0 getFreeList = 1 getFreeCount = 2 -- | Represents immutable dictionary as collection of immutable arrays and vectors. -- See 'unsafeFreeze' and 'unsafeThaw' for conversions from/to mutable dictionary. data FrozenDictionary ks k vs v = FrozenDictionary { fhashCode, fnext, fbuckets :: A.PrimArray Int, count, freeList, freeCount :: Int, fkey :: ks k, fvalue :: vs v } deriving (Eq, Ord, Show) -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Find dictionary entry by given key in immutable 'FrozenDictionary'. -- If entry not found @-1@ returned. findElem :: (Vector ks k, Vector vs v, Hashable k, Eq k) => FrozenDictionary ks k vs v -> k -> Int findElem FrozenDictionary{..} key' = go $ fbuckets !. (hashCode' `rem` A.sizeofPrimArray fbuckets) where hashCode' = hash key' .&. mask go i | i >= 0 = if fhashCode !. i == hashCode' && fkey !.~ i == key' then i else go $ fnext !. i | otherwise = -1 {-# INLINE findElem #-} -- | Infix version of @unsafeRead@. (!~) :: (MVector v a, PrimMonad m) => v (PrimState m) a -> Int -> m a (!~) = V.unsafeRead -- | Infix version of @unsafeIndex@. (!.~) :: (Vector v a) => v a -> Int -> a (!.~) = VI.unsafeIndex -- | Infix version of @unsafeWrite@. (<~~) :: (MVector v a, PrimMonad m) => v (PrimState m) a -> Int -> a -> m () (<~~) = V.unsafeWrite -- | Infix version of @readPrimArray@. (!) :: PrimMonad m => A.MutablePrimArray (PrimState m) Int -> Int -> m Int (!) = A.readPrimArray -- | Infix version of @indexPrimArray@. (!.) :: A.PrimArray Int -> Int -> Int (!.) = A.indexPrimArray -- | Infix version of @writePrimArray@. (<~) :: PrimMonad m => A.MutablePrimArray (PrimState m) Int -> Int -> Int -> m () (<~) = A.writePrimArray -- | /O(1)/ Dictionary with given capacity. initialize :: (MVector ks k, MVector vs v, PrimMonad m) => Int -> m (Dictionary (PrimState m) ks k vs v) initialize capacity = do let size = getPrime capacity hashCode <- A.replicate size 0 next <- A.replicate size 0 key <- V.new size value <- V.new size buckets <- A.replicate size (-1) refs <- A.replicate 3 0 refs <~ 1 $ -1 dr <- newMutVar Dictionary {..} return . DRef $ dr -- | Create a copy of mutable dictionary. clone :: (MVector ks k, MVector vs v, PrimMonad m) => Dictionary (PrimState m) ks k vs v -> m (Dictionary (PrimState m) ks k vs v) clone DRef {..} = do Dictionary {..} <- readMutVar getDRef hashCode <- A.clone hashCode next <- A.clone next key <- V.clone key value <- V.clone value buckets <- A.clone buckets refs <- A.clone refs dr <- newMutVar Dictionary {..} return . DRef $ dr -- | /O(1)/ Unsafe convert a mutable dictionary to an immutable one without copying. -- The mutable dictionary may not be used after this operation. unsafeFreeze :: (VI.Vector ks k, VI.Vector vs v, PrimMonad m) => Dictionary (PrimState m) (Mutable ks) k (Mutable vs) v -> m (FrozenDictionary ks k vs v) unsafeFreeze DRef {..} = do Dictionary {..} <- readMutVar getDRef fhashCode <- A.unsafeFreeze hashCode fnext <- A.unsafeFreeze next fbuckets <- A.unsafeFreeze buckets count <- refs ! getCount freeList <- refs ! getFreeList freeCount <- refs ! getFreeCount fkey <- VI.unsafeFreeze key fvalue <- VI.unsafeFreeze value return FrozenDictionary {..} -- | /O(1)/ Unsafely convert immutable 'FrozenDictionary' to a mutable 'Dictionary' without copying. -- The immutable dictionary may not be used after this operation. unsafeThaw :: (Vector ks k, Vector vs v, PrimMonad m) => FrozenDictionary ks k vs v -> m (Dictionary (PrimState m) (Mutable ks) k (Mutable vs) v) unsafeThaw FrozenDictionary {..} = do hashCode <- A.unsafeThaw fhashCode next <- A.unsafeThaw fnext buckets <- A.unsafeThaw fbuckets refs <- A.unsafeThaw $ A.primArrayFromListN 3 [count, freeList, freeCount] key <- VI.unsafeThaw fkey value <- VI.unsafeThaw fvalue dr <- newMutVar Dictionary {..} return . DRef $ dr -- | /O(n)/ Retrieve list of keys from 'Dictionary'. keys :: (Vector ks k, PrimMonad m) => Dictionary (PrimState m) (Mutable ks) k vs v -> m (ks k) keys DRef{..} = do Dictionary{..} <- readMutVar getDRef hcs <- A.freeze hashCode ks <- VI.freeze key count <- refs ! getCount return . VI.ifilter (\i _ -> hcs !. i >= 0) . VI.take count $ ks -- | /O(n)/ Retrieve list of values from 'Dictionary'. values :: (Vector vs v, PrimMonad m) => Dictionary (PrimState m) ks k (Mutable vs) v -> m (vs v) values DRef{..} = do Dictionary{..} <- readMutVar getDRef hcs <- A.freeze hashCode vs <- VI.freeze value count <- refs ! getCount return . VI.ifilter (\i _ -> hcs !. i >= 0) . VI.take count $ vs -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Find value by given key in 'Dictionary'. Throws an error if value not found. at :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m v at d k = do i <- findEntry d k if i >= 0 then do Dictionary{..} <- readMutVar . getDRef $ d value !~ i else error "KeyNotFoundException!" {-# INLINE at #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Find value by given key in 'Dictionary'. Like 'at'' but return 'Nothing' if value not found. at' :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m (Maybe v) at' d k = do i <- findEntry d k if i >= 0 then do Dictionary{..} <- readMutVar . getDRef $ d Just <$> value !~ i else pure Nothing {-# INLINE at' #-} atWithOrElse :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> (Dictionary (PrimState m) ks k vs v -> Int -> m a) -> (Dictionary (PrimState m) ks k vs v -> m a) -> m a atWithOrElse d k onFound onNothing = do i <- findEntry d k if i >= 0 then onFound d i else onNothing d {-# INLINE atWithOrElse #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Find dictionary entry by given key. If entry not found @-1@ returned. findEntry :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m Int findEntry d key' = do Dictionary{..} <- readMutVar . getDRef $ d let hashCode' = hash key' .&. mask go i | i >= 0 = do hc <- hashCode ! i if hc == hashCode' then do k <- key !~ i if k == key' then return i else go =<< next ! i else go =<< next ! i | otherwise = return $ -1 go =<< buckets ! (hashCode' `rem` A.length buckets) {-# INLINE findEntry #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Insert key and value in dictionary by key's hash. -- If entry with given key found value will be replaced. insert :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> v -> m () insert DRef{..} key' value' = do d@Dictionary{..} <- readMutVar getDRef let hashCode' = hash key' .&. mask targetBucket = hashCode' `rem` A.length buckets go i | i >= 0 = do hc <- hashCode ! i if hc == hashCode' then do k <- key !~ i if k == key' then value <~~ i $ value' else go =<< next ! i else go =<< next ! i | otherwise = addOrResize addOrResize = do freeCount <- refs ! getFreeCount if freeCount > 0 then do index <- refs ! getFreeList nxt <- next ! index refs <~ getFreeList $ nxt refs <~ getFreeCount $ freeCount - 1 add index targetBucket else do count <- refs ! getCount refs <~ getCount $ count + 1 if count == A.length next then do nd <- resize d count hashCode' key' value' writeMutVar getDRef nd else add (fromIntegral count) (fromIntegral targetBucket) add !index !targetBucket = do hashCode <~ index $ hashCode' b <- buckets ! targetBucket next <~ index $ b key <~~ index $ key' value <~~ index $ value' buckets <~ targetBucket $ index go =<< buckets ! targetBucket {-# INLINE insert #-} insertWithIndex :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Int -> Int -> k -> v -> MutVar (PrimState m) (Dictionary_ (PrimState m) ks k vs v) -> Dictionary_ (PrimState m) ks k vs v -> Int -> m () insertWithIndex !targetBucket !hashCode' key' value' getDRef d@Dictionary{..} = go where go i | i >= 0 = do hc <- hashCode ! i if hc == hashCode' then do k <- key !~ i if k == key' then value <~~ i $ value' else go =<< next ! i else go =<< next ! i | otherwise = addOrResize targetBucket hashCode' key' value' getDRef d {-# INLINE insertWithIndex #-} addOrResize :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Int -> Int -> k -> v -> MutVar (PrimState m) (Dictionary_ (PrimState m) ks k vs v) -> Dictionary_ (PrimState m) ks k vs v -> m () addOrResize !targetBucket !hashCode' !key' !value' dref d@Dictionary{..} = do freeCount <- refs ! getFreeCount if freeCount > 0 then do index <- refs ! getFreeList nxt <- next ! index refs <~ getFreeList $ nxt refs <~ getFreeCount $ freeCount - 1 add index targetBucket hashCode' key' value' d else do count <- refs ! getCount refs <~ getCount $ count + 1 if count == A.length next then do nd <- resize d count hashCode' key' value' writeMutVar dref nd else add (fromIntegral count) (fromIntegral targetBucket) hashCode' key' value' d {-# INLINE addOrResize #-} add :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Int -> Int -> Int -> k -> v -> Dictionary_ (PrimState m) ks k vs v -> m () add !index !targetBucket !hashCode' !key' !value' Dictionary{..} = do hashCode <~ index $ hashCode' b <- buckets ! targetBucket next <~ index $ b key <~~ index $ key' value <~~ index $ value' buckets <~ targetBucket $ index {-# INLINE add #-} resize Dictionary{..} index hashCode' key' value' = do let newSize = getPrime (index*2) delta = newSize - index buckets <- A.replicate newSize (-1) hashCode <- A.growNoZ hashCode delta next <- A.growNoZ next delta key <- V.grow key delta value <- V.grow value delta let go i | i < index = do hc <- hashCode ! i when (hc >= 0) $ do let bucket = hc `rem` newSize nx <- buckets ! bucket next <~ i $ nx buckets <~ bucket $ i go (i + 1) | otherwise = return () go 0 let targetBucket = hashCode' `rem` A.length buckets hashCode <~ index $ hashCode' b <- buckets ! targetBucket next <~ index $ b key <~~ index $ key' value <~~ index $ value' buckets <~ targetBucket $ index return Dictionary{..} {-# INLINE resize #-} class DeleteEntry xs where deleteEntry :: (MVector xs x, PrimMonad m) => xs (PrimState m) x -> Int -> m () instance DeleteEntry S.MVector where deleteEntry _ _ = return () instance DeleteEntry U.MVector where deleteEntry _ _ = return () instance DeleteEntry B.MVector where deleteEntry v i = v <~~ i $ undefined -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Delete entry from 'Dictionary' by given key. delete :: (Eq k, MVector ks k, MVector vs v, Hashable k, PrimMonad m, DeleteEntry ks, DeleteEntry vs) => Dictionary (PrimState m) ks k vs v -> k -> m () delete DRef{..} key' = do Dictionary{..} <- readMutVar getDRef let hashCode' = hash key' .&. mask bucket = hashCode' `rem` A.length buckets go !last !i | i >= 0 = do hc <- hashCode ! i k <- key !~ i if hc == hashCode' && k == key' then do nxt <- next ! i if last < 0 then buckets <~ bucket $ nxt else next <~ last $ nxt hashCode <~ i $ -1 next <~ i =<< refs ! getFreeList deleteEntry key i deleteEntry value i refs <~ getFreeList $ i fc <- refs ! getFreeCount refs <~ getFreeCount $ fc + 1 else go i =<< next ! i | otherwise = return () go (-1) =<< buckets ! bucket {-# INLINE delete #-} deleteWithIndex :: (Eq k, MVector ks k, MVector vs v, Hashable k, PrimMonad m, DeleteEntry ks, DeleteEntry vs) => Int -> Int -> Dictionary_ (PrimState m) ks k vs v -> k -> Int -> Int -> m () deleteWithIndex !bucket !hashCode' d@Dictionary{..} key' !last !i | i >= 0 = do hc <- hashCode ! i k <- key !~ i if hc == hashCode' && k == key' then do nxt <- next ! i if last < 0 then buckets <~ bucket $ nxt else next <~ last $ nxt hashCode <~ i $ -1 next <~ i =<< refs ! getFreeList deleteEntry key i deleteEntry value i refs <~ getFreeList $ i fc <- refs ! getFreeCount refs <~ getFreeCount $ fc + 1 else deleteWithIndex bucket hashCode' d key' i =<< next ! i | otherwise = return () {-# INLINE deleteWithIndex #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Find value by given key in 'Dictionary'. Like 'lookup'' but return 'Nothing' if value not found. lookup :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m (Maybe v) lookup = at' {-# INLINE lookup #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Find value by given key in 'Dictionary'. Throws an error if value not found. lookup' :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m v lookup' = at {-# INLINE lookup' #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Lookup the index of a key, which is its zero-based index in the sequence sorted by keys. -- The index is a number from 0 up to, but not including, the size of the dictionary. lookupIndex :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m (Maybe Int) lookupIndex ht k = do let safeIndex i | i < 0 = Nothing | otherwise = Just i return . safeIndex =<< findEntry ht k {-# INLINE lookupIndex #-} -- | /O(1)/ Return 'True' if dictionary is empty, 'False' otherwise. null :: (MVector ks k, PrimMonad m) => Dictionary (PrimState m) ks k vs v -> m Bool null ht = return . (== 0) =<< length ht {-# INLINE null #-} -- | /O(1)/ Return the number of non-empty entries of dictionary. length :: (MVector ks k, PrimMonad m) => Dictionary (PrimState m) ks k vs v -> m Int length DRef {..} = do Dictionary {..} <- readMutVar getDRef count <- refs ! getCount freeCount <- refs ! getFreeCount return (count - freeCount) {-# INLINE length #-} -- | /O(1)/ Return the number of non-empty entries of dictionary. Synonym of 'length'. size :: (MVector ks k, PrimMonad m) => Dictionary (PrimState m) ks k vs v -> m Int size = length {-# INLINE size #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- Return 'True' if the specified key is present in the dictionary, 'False' otherwise. member :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> k -> m Bool member ht k = return . (>= 0) =<< findEntry ht k {-# INLINE member #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- The expression @'findWithDefault' ht def k@ returns -- the value at key @k@ or returns default value @def@ -- when the key is not in the dictionary. findWithDefault :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> v -> k -> m v findWithDefault ht v k = return . fromMaybe v =<< at' ht k {-# INLINE findWithDefault #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- The expression (@'alter' ht f k@) alters the value @x@ at @k@, or absence thereof. -- 'alter' can be used to insert, delete, or update a value in a 'Dictionary'. -- -- > let f _ = Nothing -- > ht <- fromList [(5,"a"), (3,"b")] -- > alter ht f 7 -- > toList ht -- > [(3, "b"), (5, "a")] -- -- > ht <- fromList [(5,"a"), (3,"b")] -- > alter ht f 5 -- > toList ht -- > [(3 "b")] -- -- > let f _ = Just "c" -- > ht <- fromList [(5,"a"), (3,"b")] -- > alter ht f 7 -- > toList ht -- > [(3, "b"), (5, "a"), (7, "c")] -- -- > ht <- fromList [(5,"a"), (3,"b")] -- > alter ht f 5 -- > toList ht -- > [(3, "b"), (5, "c")] -- alter :: ( MVector ks k, MVector vs v, DeleteEntry ks, DeleteEntry vs , PrimMonad m, Hashable k, Eq k ) => Dictionary (PrimState m) ks k vs v -> (Maybe v -> Maybe v) -> k -> m () alter ht f k = do d@Dictionary{..} <- readMutVar . getDRef $ ht let hashCode' = hash k .&. mask targetBucket = hashCode' `rem` A.length buckets onFound' value' dict i = insertWithIndex targetBucket hashCode' k value' (getDRef ht) dict i onNothing' dict i = deleteWithIndex targetBucket hashCode' d k (-1) i onFound dict i = do d'@Dictionary{..} <- readMutVar . getDRef $ dict v <- value !~ i case f (Just v) of Nothing -> onNothing' d' i Just v' -> onFound' v' d' i onNothing dict = do d' <- readMutVar . getDRef $ dict case f Nothing of Nothing -> return () Just v' -> onFound' v' d' (-1) void $ atWithOrElse ht k onFound onNothing {-# INLINE alter #-} -- | /O(1)/ in the best case, /O(n)/ in the worst case. -- The expression (@'alterM' ht f k@) alters the value @x@ at @k@, or absence thereof. -- 'alterM' can be used to insert, delete, or update a value in a 'Dictionary' in the same @'PrimMonad' m@. alterM :: ( MVector ks k, MVector vs v, DeleteEntry ks, DeleteEntry vs , PrimMonad m, Hashable k, Eq k ) => Dictionary (PrimState m) ks k vs v -> (Maybe v -> m (Maybe v)) -> k -> m () alterM ht f k = do d@Dictionary{..} <- readMutVar . getDRef $ ht let hashCode' = hash k .&. mask targetBucket = hashCode' `rem` A.length buckets onFound' value' dict i = insertWithIndex targetBucket hashCode' k value' (getDRef ht) dict i onNothing' dict i = deleteWithIndex targetBucket hashCode' d k (-1) i onFound dict i = do d'@Dictionary{..} <- readMutVar . getDRef $ dict v <- value !~ i res <- f (Just v) case res of Nothing -> onNothing' d' i Just v' -> onFound' v' d' i onNothing dict = do d' <- readMutVar . getDRef $ dict res <- f Nothing case res of Nothing -> return () Just v' -> onFound' v' d' (-1) void $ atWithOrElse ht k onFound onNothing {-# INLINE alterM #-} -- * Combine -- | /O(min n m)/ in the best case, /O(min n m * max n m)/ in the worst case. -- The union of two maps. -- If a key occurs in both maps, -- the mapping from the first will be the mapping in the result. union :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> Dictionary (PrimState m) ks k vs v -> m (Dictionary (PrimState m) ks k vs v) union = unionWith const {-# INLINE union #-} -- | /O(min n m)/ in the best case, /O(min n m * max n m)/ in the worst case. -- The union of two maps. -- The provided function (first argument) will be used to compute the result. unionWith :: (MVector ks k, MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => (v -> v -> v) -> Dictionary (PrimState m) ks k vs v -> Dictionary (PrimState m) ks k vs v -> m (Dictionary (PrimState m) ks k vs v) unionWith f = unionWithKey (const f) {-# INLINE unionWith #-} -- | /O(min n m)/ in the best case, /O(min n m * max n m)/ in the worst case. -- The union of two maps. -- If a key occurs in both maps, -- the provided function (first argument) will be used to compute the result. unionWithKey :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => (k -> v -> v -> v) -> Dictionary (PrimState m) ks k vs v -> Dictionary (PrimState m) ks k vs v -> m (Dictionary (PrimState m) ks k vs v) unionWithKey f t1 t2 = do l1 <- length t1 l2 <- length t2 let smallest = min l1 l2 greatest = max l1 l2 g k v1 v2 = if smallest == l1 then f k v1 v2 else f k v2 v1 (tS, tG) = if smallest == l1 then (t1, t2) else (t2, t1) ht <- clone tG dictG <- readMutVar . getDRef $ tG dictS <- readMutVar . getDRef $ tS hcsS <- A.freeze . hashCode $ dictS let indices = catMaybes . zipWith collect [0 ..] . take smallest . A.primArrayToList $ hcsS collect i _ | hcsS !. i >= 0 = Just i | otherwise = Nothing go !i = do k <- key dictS !~ i v <- value dictS !~ i let hashCode' = hash k .&. mask targetBucket = hashCode' `rem` A.length (buckets dictG) onFound dict i = do d@Dictionary{..} <- readMutVar . getDRef $ dict vG <- value !~ i insertWithIndex targetBucket hashCode' k (g k v vG) (getDRef dict) d i onNothing dict = do d@Dictionary{..} <- readMutVar . getDRef $ dict insertWithIndex targetBucket hashCode' k v (getDRef dict) d =<< buckets ! targetBucket void $ atWithOrElse ht k onFound onNothing mapM_ go indices return ht {-# INLINE unionWithKey #-} -- * Difference and intersection -- | /O(n)/ in the best case, /O(n * m)/ in the worst case. -- Difference of two tables. Return elements of the first table -- not existing in the second. difference :: (MVector ks k, MVector vs v, MVector vs w, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> Dictionary (PrimState m) ks k vs w -> m (Dictionary (PrimState m) ks k vs v) difference a b = do kvs <- toList a ht <- initialize 10 mapM_ (go ht) kvs return ht where go ht (!k, !v) = do mv <- lookup b k case mv of Nothing -> insert ht k v _ -> return () {-# INLINE difference #-} -- | /O(n)/ in the best case, /O(n * m)/ in the worst case. -- Difference with a combining function. When two equal keys are -- encountered, the combining function is applied to the values of these keys. -- If it returns 'Nothing', the element is discarded (proper set difference). If -- it returns (@'Just' y@), the element is updated with a new value @y@. differenceWith :: (MVector ks k, MVector vs v, MVector vs w, PrimMonad m, Hashable k, Eq k) => (v -> w -> Maybe v) -> Dictionary (PrimState m) ks k vs v -> Dictionary (PrimState m) ks k vs w -> m (Dictionary (PrimState m) ks k vs v) differenceWith f a b = do kvs <- toList a ht <- initialize 10 mapM_ (go ht) kvs return ht where go ht (!k, !v) = do mv <- lookup b k case mv of Nothing -> insert ht k v Just w -> maybe (return ()) (insert ht k) (f v w) {-# INLINE differenceWith #-} -- | /O(n)/ in the best case, /O(n * m)/ in the worst case. -- Intersection of two maps. Return elements of the first -- map for keys existing in the second. intersection :: (MVector ks k, MVector vs v, MVector vs w, PrimMonad m, Hashable k, Eq k) => Dictionary (PrimState m) ks k vs v -> Dictionary (PrimState m) ks k vs w -> m (Dictionary (PrimState m) ks k vs v) intersection a b = do kvs <- toList a ht <- initialize 10 mapM_ (go ht) kvs return ht where go ht (!k, !v) = do mv <- lookup b k case mv of Nothing -> return () Just _ -> insert ht k v {-# INLINE intersection #-} -- | Intersection of two maps. If a key occurs in both maps -- the provided function is used to combine the values from the two -- maps. intersectionWith :: (MVector ks k, MVector vs v1, MVector vs v2, MVector vs v3, PrimMonad m, Hashable k, Eq k) => (v1 -> v2 -> v3) -> Dictionary (PrimState m) ks k vs v1 -> Dictionary (PrimState m) ks k vs v2 -> m (Dictionary (PrimState m) ks k vs v3) intersectionWith f a b = do kvs <- toList a ht <- initialize 10 mapM_ (go ht) kvs return ht where go ht (!k, !v) = do mv <- lookup b k case mv of Nothing -> return () Just w -> insert ht k (f v w) {-# INLINE intersectionWith #-} -- | Intersection of two maps. If a key occurs in both maps -- the provided function is used to combine the values from the two -- maps. intersectionWithKey :: (MVector ks k, MVector vs v1, MVector vs v2, MVector vs v3, PrimMonad m, Hashable k, Eq k) => (k -> v1 -> v2 -> v3) -> Dictionary (PrimState m) ks k vs v1 -> Dictionary (PrimState m) ks k vs v2 -> m (Dictionary (PrimState m) ks k vs v3) intersectionWithKey f a b = do kvs <- toList a ht <- initialize 10 mapM_ (go ht) kvs return ht where go ht (!k, !v) = do mv <- lookup b k case mv of Nothing -> return () Just w -> insert ht k (f k v w) {-# INLINE intersectionWithKey #-} -- * List conversions -- | /O(n)/ Convert list to a 'Dictionary'. fromList :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => [(k, v)] -> m (Dictionary (PrimState m) ks k vs v) fromList kv = do ht <- initialize 1 mapM_ (uncurry (insert ht)) kv return ht {-# INLINE fromList #-} -- | /O(n)/ Convert 'Dictionary' to a list. toList :: (MVector ks k, MVector vs v, PrimMonad m, Hashable k, Eq k) => (Dictionary (PrimState m) ks k vs v) -> m [(k, v)] toList DRef {..} = do Dictionary {..} <- readMutVar getDRef hcs <- A.freeze hashCode count <- refs ! getCount let go !i xs | i < 0 = return xs | hcs !. i < 0 = go (i - 1) xs | otherwise = do k <- key !~ i v <- value !~ i go (i - 1) ((k, v) : xs) {-# INLINE go #-} go (count - 1) [] {-# INLINE toList #-} -- * Extras primes :: UI.Vector Int primes = UI.fromList [ 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369, 8639249, 10367101, 12440537, 14928671, 17914409, 21497293, 25796759, 30956117, 37147349, 44576837, 53492207, 64190669, 77028803, 92434613, 110921543, 133105859, 159727031, 191672443, 230006941, 276008387, 331210079, 397452101, 476942527, 572331049, 686797261, 824156741, 988988137, 1186785773, 1424142949, 1708971541, 2050765853 ] getPrime :: Int -> Int getPrime n = fromJust $ UI.find (>= n) primes vector-hashtables-0.1.1.3/test/Data/Vector/0000755000000000000000000000000007346545000016553 5ustar0000000000000000vector-hashtables-0.1.1.3/test/Data/Vector/HashTablesSpec.hs0000644000000000000000000003440107346545000021742 0ustar0000000000000000{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} module Data.Vector.HashTablesSpec where import Control.Monad.Primitive import Data.Hashable (Hashable (hashWithSalt)) import qualified Data.List as L import Data.Primitive.MutVar import Data.Proxy (Proxy (..)) import qualified Data.Set as Set import Data.Vector.Generic (Mutable, Vector) import qualified Data.Vector.Generic as VI import Data.Vector.Generic.Mutable (MVector) import qualified Data.Vector.Mutable as M import qualified Data.Vector.Storable.Mutable as SM import qualified Data.Vector.Unboxed as U import qualified Data.Vector.Unboxed.Mutable as UM import GHC.Generics (Generic) import Test.Hspec.QuickCheck (modifyMaxSuccess) import Test.QuickCheck (Arbitrary (..), Gen, NonNegative (..), Positive (..), Property, choose, elements, forAll, generate, property, shuffle, vector) import Test.Hspec (Spec, describe, errorCall, it, shouldBe, shouldThrow) import qualified Data.Vector.Hashtables.Internal as VH newtype AlwaysCollide = AC Int deriving newtype (Arbitrary, SM.Storable, Num, Eq, Ord, Show) deriving stock Generic instance Hashable AlwaysCollide where hashWithSalt _ _ = 1 listN :: Int -> Gen [(Int, Int)] listN n = do keys <- vector n vals <- vector n let keys' = Set.toList (Set.fromList keys) return (zip keys' vals) shuffledListN :: Int -> Gen ([(Int, Int)], [(Int, Int)]) shuffledListN n = do testData <- listN n shuffledTestData <- shuffle testData return (testData, shuffledTestData) listsForRemoveN :: Int -> Gen ([(Int, Int)], [Int]) listsForRemoveN n = do testData <- listN n dropCount <- min (n - 1) <$> choose (1, n) let deleteData = fst <$> take dropCount testData return (testData, deleteData) twoListsN :: Int -> Gen ([(Int, Int)], [(Int, Int)]) twoListsN n = do list1 <- listN n list2 <- listN n return (list1, list2) spec :: Spec spec = mutableSpec *> storableMutableSpec *> storableKeysSpec *> unboxedKeysSpec class HashTableTest ks vs where specDescription :: Proxy ks -> Proxy vs -> String testInit :: Proxy ks -> Proxy vs -> Int -> IO (VH.Dictionary (PrimState IO) ks Int vs Int) testInsert :: (VH.Dictionary (PrimState IO) ks Int vs Int) -> Int -> Int -> IO () testAt :: (VH.Dictionary (PrimState IO) ks Int vs Int) -> Int -> IO Int testAt' :: (VH.Dictionary (PrimState IO) ks Int vs Int) -> Int -> IO (Maybe Int) testDelete :: (VH.Dictionary (PrimState IO) ks Int vs Int) -> Int -> IO () testInitCollide :: Proxy ks -> Proxy vs -> Int -> IO (VH.Dictionary (PrimState IO) ks AlwaysCollide vs Int) testInsertCollide :: (VH.Dictionary (PrimState IO) ks AlwaysCollide vs Int) -> AlwaysCollide -> Int -> IO () testAtCollide :: (VH.Dictionary (PrimState IO) ks AlwaysCollide vs Int) -> AlwaysCollide -> IO Int testFromList :: Proxy ks -> Proxy vs -> [(Int, Int)] -> IO (VH.Dictionary (PrimState IO) ks Int vs Int) testToList :: VH.Dictionary (PrimState IO) ks Int vs Int -> IO [(Int, Int)] testLength :: VH.Dictionary (PrimState IO) ks Int vs Int -> IO Int testNull :: VH.Dictionary (PrimState IO) ks Int vs Int -> IO Bool testMember :: VH.Dictionary (PrimState IO) ks Int vs Int -> Int -> IO Bool testAlter :: VH.Dictionary (PrimState IO) ks Int vs Int -> (Maybe Int -> Maybe Int) -> Int -> IO () testUnion :: VH.Dictionary (PrimState IO) ks Int vs Int -> VH.Dictionary (PrimState IO) ks Int vs Int -> IO (VH.Dictionary (PrimState IO) ks Int vs Int) testDifference :: VH.Dictionary (PrimState IO) ks Int vs Int -> VH.Dictionary (PrimState IO) ks Int vs Int -> IO (VH.Dictionary (PrimState IO) ks Int vs Int) testIntersection :: VH.Dictionary (PrimState IO) ks Int vs Int -> VH.Dictionary (PrimState IO) ks Int vs Int -> IO (VH.Dictionary (PrimState IO) ks Int vs Int) mkSpec :: forall ks vs. (HashTableTest ks vs) => Proxy ks -> Proxy vs -> Spec mkSpec ksp vsp = describe (specDescription ksp vsp) $ modifyMaxSuccess (const 1000) $ do it "lookup for inserted value at specific index returns value" $ property prop_insertLookup it "lookup for inserted value at specific index returns nothing" $ property prop_insertLookupNothing it "lookup for inserted value at specific index throws error" $ property prop_insertLookupError it "lookup for updated value at specific index returns updated value" $ property prop_insertUpdateLookup it "lookup for deleted value at specific index returns nothing" $ property prop_insertDeleteLookupNothing it "lookup for deleted value at specific index throws error" $ property prop_insertDeleteLookupError it "table size increases when multiple elements added" $ property prop_insertMultipleElements it "lookup for inserted value with hash collision returns value" $ property prop_insertLookupHashCollisions it "fromList . toList === id" $ property prop_fromListToList it "deleted entries are not present in key-value list after deleting from hashtable" $ property prop_insertDeleteKeysSize it "new table is null" $ property prop_newIsNull it "non-empty table is not null" $ property prop_fromListIsNotNull it "inserted key is table member" $ property prop_isMember it "deleted key is not a member" $ property prop_isNotMember it "when altering is nothing - key deleted from table" $ property prop_alterDelete it "when altering is just a result - key updated with result" $ property prop_alterUpdate it "intersection + symmetric difference of two tables is equal to union of two tables" $ property prop_union where prop_insertLookup :: HashTableTest ks vs => (Int, Int) -> IO () prop_insertLookup (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht x y v <- testAt ht x v `shouldBe` y prop_insertLookupNothing :: HashTableTest ks vs => (Int, Int) -> IO () prop_insertLookupNothing (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht (x + 1) y v <- testAt' ht x v `shouldBe` Nothing prop_insertLookupError :: HashTableTest ks vs => (Int, Int) -> IO () prop_insertLookupError (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht (x + 1) y testAt ht x `shouldThrow` errorCall "KeyNotFoundException!" prop_insertUpdateLookup :: HashTableTest ks vs => (Int, Int) -> IO () prop_insertUpdateLookup (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht x y testInsert ht x (y + 1) v <- testAt ht x v `shouldBe` (y + 1) prop_insertDeleteLookupNothing :: HashTableTest ks vs => (Int, Int) -> IO () prop_insertDeleteLookupNothing (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht x y testDelete ht x v <- testAt' ht x v `shouldBe` Nothing prop_insertDeleteLookupError :: HashTableTest ks vs => (Int, Int) -> IO () prop_insertDeleteLookupError (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht 0 1 testDelete ht 0 testAt ht 0 `shouldThrow` errorCall "KeyNotFoundException!" prop_insertMultipleElements :: HashTableTest ks vs => (NonNegative Int) -> Property prop_insertMultipleElements (NonNegative n) = forAll (listN n) $ \xs -> do ht <- testInit (Proxy @ks) (Proxy @vs) 2 mapM_ (uncurry (testInsert ht)) xs htl <- testLength ht htl `shouldBe` (length . Set.toList . Set.fromList) (fst <$> xs) prop_insertLookupHashCollisions :: HashTableTest ks vs => (AlwaysCollide, Int) -> (AlwaysCollide, Int) -> IO () prop_insertLookupHashCollisions (x1, y1) (x2, y2) = do ht <- testInitCollide (Proxy @ks) (Proxy @vs) 10 let x2' = if x1 /= x2 then x2 else x2 + 1 testInsertCollide ht x1 y1 testInsertCollide ht x2' y2 v <- testAtCollide ht x1 v `shouldBe` y1 prop_fromListToList :: NonNegative Int -> Property prop_fromListToList (NonNegative n) = forAll (shuffledListN n) $ \(xs, ys) -> do ht <- testFromList (Proxy @ks) (Proxy @vs) xs xs' <- testToList ht L.sort xs' `shouldBe` L.sort ys prop_insertDeleteKeysSize :: NonNegative Int -> Property prop_insertDeleteKeysSize (NonNegative n) = forAll (listsForRemoveN n) go where go (insertData, deleteData) = do ht <- testInit (Proxy @ks) (Proxy @vs) 2 mapM_ (uncurry (testInsert ht)) insertData mapM_ (testDelete ht) deleteData kvs <- testToList ht L.length insertData - L.length deleteData `shouldBe` L.length kvs prop_newIsNull :: IO () prop_newIsNull = do ht <- testInit (Proxy @ks) (Proxy @vs) 2 result <- testNull ht result `shouldBe` True prop_fromListIsNotNull :: Positive Int -> Property prop_fromListIsNotNull (Positive n) = forAll (listN n) $ \xs -> do ht <- testFromList (Proxy @ks) (Proxy @vs) xs result <- testNull ht result `shouldBe` False prop_isMember :: HashTableTest ks vs => (Int, Int) -> IO () prop_isMember (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht x y v <- testMember ht x v `shouldBe` True prop_isNotMember :: HashTableTest ks vs => (Int, Int) -> IO () prop_isNotMember (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht x y testDelete ht x v <- testMember ht x v `shouldBe` False prop_alterDelete :: HashTableTest ks vs => (Int, Int) -> IO () prop_alterDelete (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht x y testAlter ht (const Nothing) x v <- testMember ht x v `shouldBe` False prop_alterUpdate :: HashTableTest ks vs => (Int, Int) -> IO () prop_alterUpdate (x, y) = do ht <- testInit (Proxy @ks) (Proxy @vs) 10 testInsert ht x y testAlter ht (fmap negate) x v <- testAt ht x v `shouldBe` (negate y) prop_union :: Positive Int -> Property prop_union (Positive n) = forAll (twoListsN n) $ \(xs, ys) -> do ht1 <- testFromList (Proxy @ks) (Proxy @vs) xs ht2 <- testFromList (Proxy @ks) (Proxy @vs) ys u1 <- testUnion ht1 ht2 d1 <- testDifference ht1 ht2 d2 <- testDifference ht2 ht1 i <- testIntersection ht1 ht2 res <- do u2 <- testUnion d1 d2 testUnion i u2 resultList <- testToList res unionList <- testToList u1 Set.fromList resultList `shouldBe` Set.fromList unionList instance HashTableTest M.MVector M.MVector where specDescription _ _ = "Data.Vector.HashTables.Mutable keys and values" testInit _ _ n = VH.initialize n testInitCollide _ _ n = VH.initialize n testInsert = VH.insert testAt = VH.at testAt' = VH.at' testDelete = VH.delete testInsertCollide = VH.insert testAtCollide = VH.at testLength = VH.length testFromList _ _ = VH.fromList testToList = VH.toList testNull = VH.null testMember = VH.member testAlter = VH.alter testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection mutableSpec :: Spec mutableSpec = mkSpec (Proxy :: Proxy M.MVector) (Proxy :: Proxy M.MVector) instance HashTableTest SM.MVector SM.MVector where specDescription _ _ = "Data.Vector.HashTables.Storable.Mutable keys and values" testInit _ _ n = VH.initialize n testInitCollide _ _ n = VH.initialize n testInsert = VH.insert testAt = VH.at testAt' = VH.at' testDelete = VH.delete testInsertCollide = VH.insert testAtCollide = VH.at testLength = VH.length testFromList _ _ = VH.fromList testToList = VH.toList testNull = VH.null testMember = VH.member testAlter = VH.alter testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection storableMutableSpec :: Spec storableMutableSpec = mkSpec (Proxy @SM.MVector) (Proxy @SM.MVector) instance HashTableTest SM.MVector M.MVector where specDescription _ _ = "Data.Vector.HashTables.Mutable keys and Data.Vector.HashTables.Storable.Mutable values" testInit _ _ n = VH.initialize n testInitCollide _ _ n = VH.initialize n testInsert = VH.insert testAt = VH.at testAt' = VH.at' testDelete = VH.delete testInsertCollide = VH.insert testAtCollide = VH.at testLength = VH.length testFromList _ _ = VH.fromList testToList = VH.toList testNull = VH.null testMember = VH.member testAlter = VH.alter testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection storableKeysSpec :: Spec storableKeysSpec = mkSpec (Proxy @SM.MVector) (Proxy @M.MVector) instance HashTableTest M.MVector UM.MVector where specDescription _ _ = "Data.Vector.HashTables.Mutable keys and Data.Vector.HashTables.Unboxed.Mutable values" testInit _ _ n = VH.initialize n testInitCollide _ _ n = VH.initialize n testInsert = VH.insert testAt = VH.at testAt' = VH.at' testDelete = VH.delete testInsertCollide = VH.insert testAtCollide = VH.at testLength = VH.length testFromList _ _ = VH.fromList testToList = VH.toList testNull = VH.null testMember = VH.member testAlter = VH.alter testUnion = VH.union testDifference = VH.difference testIntersection = VH.intersection unboxedKeysSpec :: Spec unboxedKeysSpec = mkSpec (Proxy @M.MVector) (Proxy @UM.MVector) vector-hashtables-0.1.1.3/test/0000755000000000000000000000000007346545000014440 5ustar0000000000000000vector-hashtables-0.1.1.3/test/Spec.hs0000644000000000000000000000005407346545000015665 0ustar0000000000000000{-# OPTIONS_GHC -F -pgmF hspec-discover #-} vector-hashtables-0.1.1.3/vector-hashtables.cabal0000644000000000000000000000546307346545000020073 0ustar0000000000000000cabal-version: 2.0 name: vector-hashtables version: 0.1.1.3 synopsis: Efficient vector-based mutable hashtables implementation. description: This package provides efficient vector-based hashtable implementation similar to .NET Generic Dictionary implementation (at the time of 2015). . See "Data.Vector.Hashtables" for documentation. homepage: https://github.com/klapaucius/vector-hashtables#readme license: BSD3 license-file: LICENSE author: klapaucius maintainer: klapaucius, swamp_agr, ArtemPelenitsyn copyright: 2016-2023 klapaucius, swamp_agr category: Data build-type: Simple extra-doc-files: README.md, changelog.md extra-source-files: src-i386/Data/Vector/Hashtables/Internal/Mask.hs, src-gen/Data/Vector/Hashtables/Internal/Mask.hs tested-with: GHC == 9.4.4 GHC == 9.2.5 GHC == 9.0.2 GHC == 8.10.7 GHC == 8.8.4 GHC == 8.6.5 library if arch(i386) hs-source-dirs: src, src-i386 else hs-source-dirs: src, src-gen exposed-modules: Data.Vector.Hashtables, Data.Vector.Hashtables.Internal, Data.Vector.Hashtables.Internal.Mask, Data.Primitive.PrimArray.Utils ghc-options: -O2 build-depends: base >= 4.7 && < 5 , primitive >= 0.7.1.0 , vector , hashable default-language: Haskell2010 benchmark vector-hashtables-bench type: exitcode-stdio-1.0 hs-source-dirs: bench main-is: Main.hs ghc-options: -O2 -rtsopts build-depends: base , vector-hashtables , vector , primitive , criterion , hashtables , unordered-containers default-language: Haskell2010 test-suite vector-hashtables-test type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Spec.hs ghc-options: -threaded -rtsopts -with-rtsopts=-N default-language: Haskell2010 other-modules: Data.Vector.HashTablesSpec build-depends: base , primitive , containers , hashable , vector , vector-hashtables -- Additional dependencies build-depends: hspec >= 2.6.0 && < 2.12 , QuickCheck >= 2.12.6.1 && < 2.15 , quickcheck-instances >= 0.3.19 && < 0.4 build-tool-depends: hspec-discover:hspec-discover >= 2.6.0 && < 2.12 source-repository head type: git location: https://github.com/klapaucius/vector-hashtables