From fb6d610613e720ea6b05b8d8a27ae5cf210cea08 Mon Sep 17 00:00:00 2001 From: Gary Burgess Date: Wed, 6 Jun 2018 20:38:23 +0100 Subject: [PATCH] Add deprecation notice --- .eslintrc.json | 28 -- .gitignore | 8 - .travis.yml | 23 -- LICENSE | 21 -- README.md | 17 +- bench/Bench/Data/Map.purs | 55 --- bench/Bench/Data/StrMap.purs | 27 -- bench/Bench/Main.purs | 21 -- bower.json | 35 -- package.json | 18 - src/Data/Map.purs | 642 --------------------------------- src/Data/Map/Gen.purs | 24 -- src/Data/StrMap.js | 125 ------- src/Data/StrMap.purs | 302 ---------------- src/Data/StrMap/Gen.purs | 23 -- src/Data/StrMap/ST.js | 37 -- src/Data/StrMap/ST.purs | 37 -- src/Data/StrMap/ST/Unsafe.js | 7 - src/Data/StrMap/ST/Unsafe.purs | 11 - src/Data/StrMap/Unsafe.js | 7 - src/Data/StrMap/Unsafe.purs | 10 - test/Test/Data/Map.purs | 335 ----------------- test/Test/Data/StrMap.purs | 252 ------------- test/Test/Main.purs | 19 - 24 files changed, 3 insertions(+), 2081 deletions(-) delete mode 100644 .eslintrc.json delete mode 100644 .gitignore delete mode 100644 .travis.yml delete mode 100644 LICENSE delete mode 100644 bench/Bench/Data/Map.purs delete mode 100644 bench/Bench/Data/StrMap.purs delete mode 100644 bench/Bench/Main.purs delete mode 100644 bower.json delete mode 100644 package.json delete mode 100644 src/Data/Map.purs delete mode 100644 src/Data/Map/Gen.purs delete mode 100644 src/Data/StrMap.js delete mode 100644 src/Data/StrMap.purs delete mode 100644 src/Data/StrMap/Gen.purs delete mode 100644 src/Data/StrMap/ST.js delete mode 100644 src/Data/StrMap/ST.purs delete mode 100644 src/Data/StrMap/ST/Unsafe.js delete mode 100644 src/Data/StrMap/ST/Unsafe.purs delete mode 100644 src/Data/StrMap/Unsafe.js delete mode 100644 src/Data/StrMap/Unsafe.purs delete mode 100644 test/Test/Data/Map.purs delete mode 100644 test/Test/Data/StrMap.purs delete mode 100644 test/Test/Main.purs diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 84cef4f0..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "parserOptions": { - "ecmaVersion": 5 - }, - "extends": "eslint:recommended", - "env": { - "commonjs": true - }, - "rules": { - "strict": [2, "global"], - "block-scoped-var": 2, - "consistent-return": 2, - "eqeqeq": [2, "smart"], - "guard-for-in": 2, - "no-caller": 2, - "no-extend-native": 2, - "no-loop-func": 2, - "no-new": 2, - "no-param-reassign": 2, - "no-return-assign": 2, - "no-unused-expressions": 2, - "no-use-before-define": 2, - "radix": [2, "always"], - "indent": [2, 2], - "quotes": [2, "double"], - "semi": [2, "always"] - } -} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 709fd096..00000000 --- a/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/.* -!/.gitignore -!/.eslintrc.json -!/.travis.yml -package-lock.json -/bower_components/ -/node_modules/ -/output/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4cbd5fde..00000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -language: node_js -dist: trusty -sudo: required -node_js: stable -env: - - PATH=$HOME/purescript:$PATH -install: - - TAG=$(wget -q -O - https://github.com/purescript/purescript/releases/latest --server-response --max-redirect 0 2>&1 | sed -n -e 's/.*Location:.*tag\///p') - - wget -O $HOME/purescript.tar.gz https://github.com/purescript/purescript/releases/download/$TAG/linux64.tar.gz - - tar -xvf $HOME/purescript.tar.gz -C $HOME/ - - chmod a+x $HOME/purescript - - npm install -g bower - - npm install -script: - - bower install --production - - npm run -s build - - bower install - - npm -s test -after_success: -- >- - test $TRAVIS_TAG && - echo $GITHUB_TOKEN | pulp login && - echo y | pulp publish --no-push diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 94158580..00000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 PureScript - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 1c8d27c5..68b23cff 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,5 @@ -# purescript-maps +# DEPRECATED -[![Latest release](http://img.shields.io/github/release/purescript/purescript-maps.svg)](https://github.com/purescript/purescript-maps/releases) -[![Build status](https://travis-ci.org/purescript/purescript-maps.svg?branch=master)](https://travis-ci.org/purescript/purescript-maps) +The library is no longer maintained under this repository, it has been merged into to [`purescript-ordered-collections`](https://github.com/purescript/purescript-ordered-collections). -Purely-functional map data structures. - -## Installation - -``` -bower install purescript-maps -``` - -## Documentation - -Module documentation is [published on Pursuit](http://pursuit.purescript.org/packages/purescript-maps). +[The previous releases](https://github.com/purescript-deprecated/purescript-maps/releases) will continue to work for older libraries that still depend on them. diff --git a/bench/Bench/Data/Map.purs b/bench/Bench/Data/Map.purs deleted file mode 100644 index a2197fc7..00000000 --- a/bench/Bench/Data/Map.purs +++ /dev/null @@ -1,55 +0,0 @@ -module Bench.Data.Map where - -import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) -import Performance.Minibench (bench, benchWith) - -import Data.Tuple (Tuple(..)) -import Data.List as L -import Data.Map as M - -benchMap :: Eff (console :: CONSOLE) Unit -benchMap = do - log "size" - log "---------------" - benchSize - - log "" - - log "fromFoldable" - log "------------" - benchFromFoldable - - where - - benchSize = do - let nats = L.range 0 999999 - natPairs = (flip Tuple) unit <$> nats - singletonMap = M.singleton 0 unit - smallMap = M.fromFoldable $ L.take 100 natPairs - midMap = M.fromFoldable $ L.take 10000 natPairs - bigMap = M.fromFoldable $ natPairs - - log "size: singleton map" - bench \_ -> M.size singletonMap - - log $ "size: small map (" <> show (M.size smallMap) <> ")" - bench \_ -> M.size smallMap - - log $ "size: midsize map (" <> show (M.size midMap) <> ")" - benchWith 100 \_ -> M.size midMap - - log $ "size: big map (" <> show (M.size bigMap) <> ")" - benchWith 10 \_ -> M.size bigMap - - benchFromFoldable = do - let natStrs = show <$> L.range 0 99999 - natPairs = (flip Tuple) unit <$> natStrs - shortPairList = L.take 10000 natPairs - - log $ "fromFoldable (" <> show (L.length shortPairList) <> ")" - benchWith 100 \_ -> M.fromFoldable shortPairList - - log $ "fromFoldable (" <> show (L.length natPairs) <> ")" - benchWith 10 \_ -> M.fromFoldable natPairs diff --git a/bench/Bench/Data/StrMap.purs b/bench/Bench/Data/StrMap.purs deleted file mode 100644 index 1c7f419a..00000000 --- a/bench/Bench/Data/StrMap.purs +++ /dev/null @@ -1,27 +0,0 @@ -module Bench.Data.StrMap where - -import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) -import Performance.Minibench (benchWith) - -import Data.Tuple (Tuple(..)) -import Data.List as L -import Data.StrMap as M - -benchStrMap :: Eff (console :: CONSOLE) Unit -benchStrMap = do - log "fromFoldable" - benchFromFoldable - - where - benchFromFoldable = do - let natStrs = show <$> L.range 0 99999 - natPairs = (flip Tuple) unit <$> natStrs - shortPairList = L.take 10000 natPairs - - log $ "fromFoldable (" <> show (L.length shortPairList) <> ")" - benchWith 100 \_ -> M.fromFoldable shortPairList - - log $ "fromFoldable (" <> show (L.length natPairs) <> ")" - benchWith 10 \_ -> M.fromFoldable natPairs diff --git a/bench/Bench/Main.purs b/bench/Bench/Main.purs deleted file mode 100644 index f8f641b0..00000000 --- a/bench/Bench/Main.purs +++ /dev/null @@ -1,21 +0,0 @@ -module Bench.Main where - -import Prelude -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (CONSOLE, log) - -import Bench.Data.Map (benchMap) -import Bench.Data.StrMap (benchStrMap) - -main :: Eff (console :: CONSOLE) Unit -main = do - log "Map" - log "===" - benchMap - - log "" - - - log "StrMap" - log "======" - benchStrMap diff --git a/bower.json b/bower.json deleted file mode 100644 index bbcda159..00000000 --- a/bower.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "purescript-maps", - "homepage": "https://github.com/purescript/purescript-maps", - "authors": [ - "Phil Freeman ", - "John A. De Goes " - ], - "description": "Purely functional maps implemented in PureScript", - "repository": { - "type": "git", - "url": "git://github.com/purescript/purescript-maps.git" - }, - "license": "MIT", - "ignore": [ - "**/.*", - "bower_components", - "node_modules", - "output", - "test", - "bower.json", - "package.json" - ], - "dependencies": { - "purescript-arrays": "^4.0.0", - "purescript-functions": "^3.0.0", - "purescript-lists": "^4.0.0", - "purescript-st": "^3.0.0", - "purescript-gen": "^1.1.0", - "purescript-foldable-traversable": "^3.6.1" - }, - "devDependencies": { - "purescript-quickcheck": "^4.0.0", - "purescript-minibench": "^1.0.0" - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 0373d0c9..00000000 --- a/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "private": true, - "scripts": { - "clean": "rimraf output && rimraf .pulp-cache", - "build": "eslint src && pulp build -- --censor-lib --strict", - "test": "pulp test", - - "bench:build": "purs compile 'bench/**/*.purs' 'src/**/*.purs' 'bower_components/*/src/**/*.purs'", - "bench:run": "node -e 'require(\"./output/Bench.Main/index.js\").main()'", - "bench": "npm run bench:build && npm run bench:run" - }, - "devDependencies": { - "eslint": "^3.17.1", - "pulp": "^10.0.4", - "purescript-psa": "^0.5.0-rc.1", - "rimraf": "^2.6.1" - } -} diff --git a/src/Data/Map.purs b/src/Data/Map.purs deleted file mode 100644 index e764370b..00000000 --- a/src/Data/Map.purs +++ /dev/null @@ -1,642 +0,0 @@ --- | This module defines a type of maps as balanced 2-3 trees, based on --- | - -module Data.Map - ( Map - , showTree - , empty - , isEmpty - , singleton - , checkValid - , insert - , lookup - , lookupLE - , lookupLT - , lookupGE - , lookupGT - , findMin - , findMax - , foldSubmap - , submap - , fromFoldable - , fromFoldableWith - , toUnfoldable - , toAscUnfoldable - , delete - , pop - , member - , alter - , update - , keys - , values - , union - , unionWith - , unions - , isSubmap - , size - , mapWithKey - , filterWithKey - , filterKeys - , filter - ) where - -import Prelude - -import Data.Eq (class Eq1) -import Data.Foldable (foldl, foldMap, foldr, class Foldable) -import Data.FoldableWithIndex (class FoldableWithIndex) -import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex) -import Data.List (List(..), (:), length, nub) -import Data.List.Lazy as LL -import Data.Maybe (Maybe(..), maybe, isJust, fromMaybe) -import Data.Monoid (class Monoid, mempty) -import Data.Ord (class Ord1) -import Data.Traversable (traverse, class Traversable) -import Data.TraversableWithIndex (class TraversableWithIndex, traverseWithIndex) -import Data.Tuple (Tuple(Tuple), snd, uncurry) -import Data.Unfoldable (class Unfoldable, unfoldr) -import Partial.Unsafe (unsafePartial) - --- | `Map k v` represents maps from keys of type `k` to values of type `v`. -data Map k v - = Leaf - | Two (Map k v) k v (Map k v) - | Three (Map k v) k v (Map k v) k v (Map k v) - --- Internal use -toAscArray :: forall k v. Map k v -> Array (Tuple k v) -toAscArray = toAscUnfoldable - -instance eq1Map :: Eq k => Eq1 (Map k) where - eq1 = eq - -instance eqMap :: (Eq k, Eq v) => Eq (Map k v) where - eq m1 m2 = toAscArray m1 == toAscArray m2 - -instance ord1Map :: Ord k => Ord1 (Map k) where - compare1 = compare - -instance ordMap :: (Ord k, Ord v) => Ord (Map k v) where - compare m1 m2 = compare (toAscArray m1) (toAscArray m2) - -instance showMap :: (Show k, Show v) => Show (Map k v) where - show m = "(fromFoldable " <> show (toAscArray m) <> ")" - -instance semigroupMap :: Ord k => Semigroup (Map k v) where - append = union - -instance monoidMap :: Ord k => Monoid (Map k v) where - mempty = empty - -instance functorMap :: Functor (Map k) where - map _ Leaf = Leaf - map f (Two left k v right) = Two (map f left) k (f v) (map f right) - map f (Three left k1 v1 mid k2 v2 right) = Three (map f left) k1 (f v1) (map f mid) k2 (f v2) (map f right) - -instance functorWithIndexMap :: FunctorWithIndex k (Map k) where - mapWithIndex _ Leaf = Leaf - mapWithIndex f (Two left k v right) = Two (mapWithIndex f left) k (f k v) (mapWithIndex f right) - mapWithIndex f (Three left k1 v1 mid k2 v2 right) = Three (mapWithIndex f left) k1 (f k1 v1) (mapWithIndex f mid) k2 (f k2 v2) (mapWithIndex f right) - -instance foldableMap :: Foldable (Map k) where - foldl f z m = foldl f z (values m) - foldr f z m = foldr f z (values m) - foldMap f m = foldMap f (values m) - -instance foldableWithIndexMap :: FoldableWithIndex k (Map k) where - foldlWithIndex f z m = foldl (uncurry <<< (flip f)) z $ asList $ toUnfoldable m - foldrWithIndex f z m = foldr (uncurry f) z $ asList $ toUnfoldable m - foldMapWithIndex f m = foldMap (uncurry f) $ asList $ toUnfoldable m - -asList :: forall k v. List (Tuple k v) -> List (Tuple k v) -asList = id - -instance traversableMap :: Traversable (Map k) where - traverse f Leaf = pure Leaf - traverse f (Two left k v right) = - Two <$> traverse f left - <*> pure k - <*> f v - <*> traverse f right - traverse f (Three left k1 v1 mid k2 v2 right) = - Three <$> traverse f left - <*> pure k1 - <*> f v1 - <*> traverse f mid - <*> pure k2 - <*> f v2 - <*> traverse f right - sequence = traverse id - -instance traversableWithIndexMap :: TraversableWithIndex k (Map k) where - traverseWithIndex f Leaf = pure Leaf - traverseWithIndex f (Two left k v right) = - Two <$> traverseWithIndex f left - <*> pure k - <*> f k v - <*> traverseWithIndex f right - traverseWithIndex f (Three left k1 v1 mid k2 v2 right) = - Three <$> traverseWithIndex f left - <*> pure k1 - <*> f k1 v1 - <*> traverseWithIndex f mid - <*> pure k2 - <*> f k2 v2 - <*> traverseWithIndex f right - --- | Render a `Map` as a `String` -showTree :: forall k v. Show k => Show v => Map k v -> String -showTree Leaf = "Leaf" -showTree (Two left k v right) = - "Two (" <> showTree left <> - ") (" <> show k <> - ") (" <> show v <> - ") (" <> showTree right <> ")" -showTree (Three left k1 v1 mid k2 v2 right) = - "Three (" <> showTree left <> - ") (" <> show k1 <> - ") (" <> show v1 <> - ") (" <> showTree mid <> - ") (" <> show k2 <> - ") (" <> show v2 <> - ") (" <> showTree right <> ")" - --- | An empty map -empty :: forall k v. Map k v -empty = Leaf - --- | Test if a map is empty -isEmpty :: forall k v. Map k v -> Boolean -isEmpty Leaf = true -isEmpty _ = false - --- | Create a map with one key/value pair -singleton :: forall k v. k -> v -> Map k v -singleton k v = Two Leaf k v Leaf - --- | Check whether the underlying tree satisfies the 2-3 invariant --- | --- | This function is provided for internal use. -checkValid :: forall k v. Map k v -> Boolean -checkValid tree = length (nub (allHeights tree)) == one - where - allHeights :: Map k v -> List Int - allHeights Leaf = pure zero - allHeights (Two left _ _ right) = map (\n -> n + one) (allHeights left <> allHeights right) - allHeights (Three left _ _ mid _ _ right) = map (\n -> n + one) (allHeights left <> allHeights mid <> allHeights right) - --- | Look up a value for the specified key -lookup :: forall k v. Ord k => k -> Map k v -> Maybe v -lookup k = go - where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v right) = - case comp k k1 of - EQ -> Just v - LT -> go left - _ -> go right - go (Three left k1 v1 mid k2 v2 right) = - case comp k k1 of - EQ -> Just v1 - c1 -> - case c1, comp k k2 of - _ , EQ -> Just v2 - LT, _ -> go left - _ , GT -> go right - _ , _ -> go mid - - --- | Look up a value for the specified key, or the greatest one less than it -lookupLE :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } -lookupLE k = go - where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right - LT -> go left - go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of - EQ -> Just { key: k2, value: v2 } - GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right - LT -> go $ Two left k1 v1 mid - --- | Look up a value for the greatest key less than the specified key -lookupLT :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } -lookupLT k = go - where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> findMax left - GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right - LT -> go left - go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of - EQ -> findMax $ Two left k1 v1 mid - GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right - LT -> go $ Two left k1 v1 mid - --- | Look up a value for the specified key, or the least one greater than it -lookupGE :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } -lookupGE k = go - where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go right - go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go $ Two mid k2 v2 right - --- | Look up a value for the least key greater than the specified key -lookupGT :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } -lookupGT k = go - where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> findMin right - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go right - go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of - EQ -> findMin $ Two mid k2 v2 right - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go $ Two mid k2 v2 right - --- | Returns the pair with the greatest key -findMax :: forall k v. Map k v -> Maybe { key :: k, value :: v } -findMax = go Nothing - where - go acc Leaf = acc - go _ (Two _ k1 v1 right) = go (Just { key: k1, value: v1 }) right - go _ (Three _ _ _ _ k2 v2 right) = go (Just { key: k2, value: v2 }) right - --- | Returns the pair with the least key -findMin :: forall k v. Map k v -> Maybe { key :: k, value :: v } -findMin = go Nothing - where - go acc Leaf = acc - go _ (Two left k1 v1 _) = go (Just { key: k1, value: v1 }) left - go _ (Three left k1 v1 _ _ _ _) = go (Just { key: k1, value: v1 }) left - --- | Fold over the entries of a given map where the key is between a lower and --- | an upper bound. Passing `Nothing` as either the lower or upper bound --- | argument means that the fold has no lower or upper bound, i.e. the fold --- | starts from (or ends with) the smallest (or largest) key in the map. --- | --- | ```purescript --- | foldSubmap (Just 1) (Just 2) (\_ v -> [v]) --- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"]) --- | == ["one", "two"] --- | --- | foldSubmap Nothing (Just 2) (\_ v -> [v]) --- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"]) --- | == ["zero", "one", "two"] --- | ``` -foldSubmap :: forall k v m. Ord k => Monoid m => Maybe k -> Maybe k -> (k -> v -> m) -> Map k v -> m -foldSubmap kmin kmax f = - let - tooSmall = - case kmin of - Just kmin' -> - \k -> k < kmin' - Nothing -> - const false - - tooLarge = - case kmax of - Just kmax' -> - \k -> k > kmax' - Nothing -> - const false - - inBounds = - case kmin, kmax of - Just kmin', Just kmax' -> - \k -> kmin' <= k && k <= kmax' - Just kmin', Nothing -> - \k -> kmin' <= k - Nothing, Just kmax' -> - \k -> k <= kmax' - Nothing, Nothing -> - const true - - -- We can take advantage of the invariants of the tree structure to reduce - -- the amount of work we need to do. For example, in the following tree: - -- - -- [2][4] - -- / | \ - -- / | \ - -- [1] [3] [5] - -- - -- If we are given a lower bound of 3, we do not need to inspect the left - -- subtree, because we know that every entry in it is less than or equal to - -- 2. Similarly, if we are given a lower bound of 5, we do not need to - -- inspect the central subtree, because we know that every entry in it must - -- be less than or equal to 4. - -- - -- Unfortunately we cannot extract `if cond then x else mempty` into a - -- function because of strictness. - go = case _ of - Leaf -> - mempty - Two left k v right -> - (if tooSmall k then mempty else go left) - <> (if inBounds k then f k v else mempty) - <> (if tooLarge k then mempty else go right) - Three left k1 v1 mid k2 v2 right -> - (if tooSmall k1 then mempty else go left) - <> (if inBounds k1 then f k1 v1 else mempty) - <> (if tooSmall k2 || tooLarge k1 then mempty else go mid) - <> (if inBounds k2 then f k2 v2 else mempty) - <> (if tooLarge k2 then mempty else go right) - in - go - --- | Returns a new map containing all entries of the given map which lie --- | between a given lower and upper bound, treating `Nothing` as no bound i.e. --- | including the smallest (or largest) key in the map, no matter how small --- | (or large) it is. For example: --- | --- | ```purescript --- | submap (Just 1) (Just 2) --- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"]) --- | == fromFoldable [Tuple 1 "one", Tuple 2 "two"] --- | --- | submap Nothing (Just 2) --- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"]) --- | == fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two"] --- | ``` --- | --- | The function is entirely specified by the following --- | property: --- | --- | ```purescript --- | Given any m :: Map k v, mmin :: Maybe k, mmax :: Maybe k, key :: k, --- | let m' = submap mmin mmax m in --- | if (maybe true (\min -> min <= key) mmin && --- | maybe true (\max -> max >= key) mmax) --- | then lookup key m == lookup key m' --- | else not (member key m') --- | ``` -submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v -submap kmin kmax = foldSubmap kmin kmax singleton - --- | Test if a key is a member of a map -member :: forall k v. Ord k => k -> Map k v -> Boolean -member k m = isJust (k `lookup` m) - -data TreeContext k v - = TwoLeft k v (Map k v) - | TwoRight (Map k v) k v - | ThreeLeft k v (Map k v) k v (Map k v) - | ThreeMiddle (Map k v) k v k v (Map k v) - | ThreeRight (Map k v) k v (Map k v) k v - -fromZipper :: forall k v. Ord k => List (TreeContext k v) -> Map k v -> Map k v -fromZipper Nil tree = tree -fromZipper (Cons x ctx) tree = - case x of - TwoLeft k1 v1 right -> fromZipper ctx (Two tree k1 v1 right) - TwoRight left k1 v1 -> fromZipper ctx (Two left k1 v1 tree) - ThreeLeft k1 v1 mid k2 v2 right -> fromZipper ctx (Three tree k1 v1 mid k2 v2 right) - ThreeMiddle left k1 v1 k2 v2 right -> fromZipper ctx (Three left k1 v1 tree k2 v2 right) - ThreeRight left k1 v1 mid k2 v2 -> fromZipper ctx (Three left k1 v1 mid k2 v2 tree) - -data KickUp k v = KickUp (Map k v) k v (Map k v) - --- | Insert or replace a key/value pair in a map -insert :: forall k v. Ord k => k -> v -> Map k v -> Map k v -insert k v = down Nil - where - comp :: k -> k -> Ordering - comp = compare - - down :: List (TreeContext k v) -> Map k v -> Map k v - down ctx Leaf = up ctx (KickUp Leaf k v Leaf) - down ctx (Two left k1 v1 right) = - case comp k k1 of - EQ -> fromZipper ctx (Two left k v right) - LT -> down (Cons (TwoLeft k1 v1 right) ctx) left - _ -> down (Cons (TwoRight left k1 v1) ctx) right - down ctx (Three left k1 v1 mid k2 v2 right) = - case comp k k1 of - EQ -> fromZipper ctx (Three left k v mid k2 v2 right) - c1 -> - case c1, comp k k2 of - _ , EQ -> fromZipper ctx (Three left k1 v1 mid k v right) - LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left - GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right - - up :: List (TreeContext k v) -> KickUp k v -> Map k v - up Nil (KickUp left k' v' right) = Two left k' v' right - up (Cons x ctx) kup = - case x, kup of - TwoLeft k1 v1 right, KickUp left k' v' mid -> fromZipper ctx (Three left k' v' mid k1 v1 right) - TwoRight left k1 v1, KickUp mid k' v' right -> fromZipper ctx (Three left k1 v1 mid k' v' right) - ThreeLeft k1 v1 c k2 v2 d, KickUp a k' v' b -> up ctx (KickUp (Two a k' v' b) k1 v1 (Two c k2 v2 d)) - ThreeMiddle a k1 v1 k2 v2 d, KickUp b k' v' c -> up ctx (KickUp (Two a k1 v1 b) k' v' (Two c k2 v2 d)) - ThreeRight a k1 v1 b k2 v2, KickUp c k' v' d -> up ctx (KickUp (Two a k1 v1 b) k2 v2 (Two c k' v' d)) - --- | Delete a key and its corresponding value from a map. -delete :: forall k v. Ord k => k -> Map k v -> Map k v -delete k m = maybe m snd (pop k m) - --- | Delete a key and its corresponding value from a map, returning the value --- | as well as the subsequent map. -pop :: forall k v. Ord k => k -> Map k v -> Maybe (Tuple v (Map k v)) -pop k = down Nil - where - comp :: k -> k -> Ordering - comp = compare - - down :: List (TreeContext k v) -> Map k v -> Maybe (Tuple v (Map k v)) - down = unsafePartial \ctx m -> case m of - Leaf -> Nothing - Two left k1 v1 right -> - case right, comp k k1 of - Leaf, EQ -> Just (Tuple v1 (up ctx Leaf)) - _ , EQ -> let max = maxNode left - in Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) - _ , LT -> down (Cons (TwoLeft k1 v1 right) ctx) left - _ , _ -> down (Cons (TwoRight left k1 v1) ctx) right - Three left k1 v1 mid k2 v2 right -> - let leaves = - case left, mid, right of - Leaf, Leaf, Leaf -> true - _ , _ , _ -> false - in case leaves, comp k k1, comp k k2 of - true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) - true, _ , EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) - _ , EQ, _ -> let max = maxNode left - in Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) - _ , _ , EQ -> let max = maxNode mid - in Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) - _ , LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left - _ , GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _ , _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right - - up :: List (TreeContext k v) -> Map k v -> Map k v - up = unsafePartial \ctxs tree -> - case ctxs of - Nil -> tree - Cons x ctx -> - case x, tree of - TwoLeft k1 v1 Leaf, Leaf -> fromZipper ctx (Two Leaf k1 v1 Leaf) - TwoRight Leaf k1 v1, Leaf -> fromZipper ctx (Two Leaf k1 v1 Leaf) - TwoLeft k1 v1 (Two m k2 v2 r), l -> up ctx (Three l k1 v1 m k2 v2 r) - TwoRight (Two l k1 v1 m) k2 v2, r -> up ctx (Three l k1 v1 m k2 v2 r) - TwoLeft k1 v1 (Three b k2 v2 c k3 v3 d), a -> fromZipper ctx (Two (Two a k1 v1 b) k2 v2 (Two c k3 v3 d)) - TwoRight (Three a k1 v1 b k2 v2 c) k3 v3, d -> fromZipper ctx (Two (Two a k1 v1 b) k2 v2 (Two c k3 v3 d)) - ThreeLeft k1 v1 Leaf k2 v2 Leaf, Leaf -> fromZipper ctx (Three Leaf k1 v1 Leaf k2 v2 Leaf) - ThreeMiddle Leaf k1 v1 k2 v2 Leaf, Leaf -> fromZipper ctx (Three Leaf k1 v1 Leaf k2 v2 Leaf) - ThreeRight Leaf k1 v1 Leaf k2 v2, Leaf -> fromZipper ctx (Three Leaf k1 v1 Leaf k2 v2 Leaf) - ThreeLeft k1 v1 (Two b k2 v2 c) k3 v3 d, a -> fromZipper ctx (Two (Three a k1 v1 b k2 v2 c) k3 v3 d) - ThreeMiddle (Two a k1 v1 b) k2 v2 k3 v3 d, c -> fromZipper ctx (Two (Three a k1 v1 b k2 v2 c) k3 v3 d) - ThreeMiddle a k1 v1 k2 v2 (Two c k3 v3 d), b -> fromZipper ctx (Two a k1 v1 (Three b k2 v2 c k3 v3 d)) - ThreeRight a k1 v1 (Two b k2 v2 c) k3 v3, d -> fromZipper ctx (Two a k1 v1 (Three b k2 v2 c k3 v3 d)) - ThreeLeft k1 v1 (Three b k2 v2 c k3 v3 d) k4 v4 e, a -> fromZipper ctx (Three (Two a k1 v1 b) k2 v2 (Two c k3 v3 d) k4 v4 e) - ThreeMiddle (Three a k1 v1 b k2 v2 c) k3 v3 k4 v4 e, d -> fromZipper ctx (Three (Two a k1 v1 b) k2 v2 (Two c k3 v3 d) k4 v4 e) - ThreeMiddle a k1 v1 k2 v2 (Three c k3 v3 d k4 v4 e), b -> fromZipper ctx (Three a k1 v1 (Two b k2 v2 c) k3 v3 (Two d k4 v4 e)) - ThreeRight a k1 v1 (Three b k2 v2 c k3 v3 d) k4 v4, e -> fromZipper ctx (Three a k1 v1 (Two b k2 v2 c) k3 v3 (Two d k4 v4 e)) - - maxNode :: Map k v -> { key :: k, value :: v } - maxNode = unsafePartial \m -> case m of - Two _ k' v Leaf -> { key: k', value: v } - Two _ _ _ right -> maxNode right - Three _ _ _ _ k' v Leaf -> { key: k', value: v } - Three _ _ _ _ _ _ right -> maxNode right - - - removeMaxNode :: List (TreeContext k v) -> Map k v -> Map k v - removeMaxNode = unsafePartial \ctx m -> - case m of - Two Leaf _ _ Leaf -> up ctx Leaf - Two left k' v right -> removeMaxNode (Cons (TwoRight left k' v) ctx) right - Three Leaf k1 v1 Leaf _ _ Leaf -> up (Cons (TwoRight Leaf k1 v1) ctx) Leaf - Three left k1 v1 mid k2 v2 right -> removeMaxNode (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right - - --- | Insert the value, delete a value, or update a value for a key in a map -alter :: forall k v. Ord k => (Maybe v -> Maybe v) -> k -> Map k v -> Map k v -alter f k m = case f (k `lookup` m) of - Nothing -> delete k m - Just v -> insert k v m - --- | Update or delete the value for a key in a map -update :: forall k v. Ord k => (v -> Maybe v) -> k -> Map k v -> Map k v -update f k m = alter (maybe Nothing f) k m - --- | Convert any foldable collection of key/value pairs to a map. --- | On key collision, later values take precedence over earlier ones. -fromFoldable :: forall f k v. Ord k => Foldable f => f (Tuple k v) -> Map k v -fromFoldable = foldl (\m (Tuple k v) -> insert k v m) empty - --- | Convert any foldable collection of key/value pairs to a map. --- | On key collision, the values are configurably combined. -fromFoldableWith :: forall f k v. Ord k => Foldable f => (v -> v -> v) -> f (Tuple k v) -> Map k v -fromFoldableWith f = foldl (\m (Tuple k v) -> alter (combine v) k m) empty where - combine v (Just v') = Just $ f v v' - combine v Nothing = Just v - --- | Convert a map to an unfoldable structure of key/value pairs -toUnfoldable :: forall f k v. Unfoldable f => Map k v -> f (Tuple k v) -toUnfoldable m = unfoldr go (m : Nil) where - go Nil = Nothing - go (hd : tl) = case hd of - Leaf -> go tl - Two left k v right -> - Just $ Tuple (Tuple k v) (left : right : tl) - Three left k1 v1 mid k2 v2 right -> - Just $ Tuple (Tuple k1 v1) (singleton k2 v2 : left : mid : right : tl) - --- | Convert a map to an unfoldable structure of key/value pairs where the keys are in ascending order -toAscUnfoldable :: forall f k v. Unfoldable f => Map k v -> f (Tuple k v) -toAscUnfoldable m = unfoldr go (m : Nil) where - go Nil = Nothing - go (hd : tl) = case hd of - Leaf -> go tl - Two Leaf k v Leaf -> - Just $ Tuple (Tuple k v) tl - Two Leaf k v right -> - Just $ Tuple (Tuple k v) (right : tl) - Two left k v right -> - go $ left : singleton k v : right : tl - Three left k1 v1 mid k2 v2 right -> - go $ left : singleton k1 v1 : mid : singleton k2 v2 : right : tl - --- | Get a list of the keys contained in a map -keys :: forall k v. Map k v -> List k -keys Leaf = Nil -keys (Two left k _ right) = keys left <> pure k <> keys right -keys (Three left k1 _ mid k2 _ right) = keys left <> pure k1 <> keys mid <> pure k2 <> keys right - --- | Get a list of the values contained in a map -values :: forall k v. Map k v -> List v -values Leaf = Nil -values (Two left _ v right) = values left <> pure v <> values right -values (Three left _ v1 mid _ v2 right) = values left <> pure v1 <> values mid <> pure v2 <> values right - --- | Compute the union of two maps, using the specified function --- | to combine values for duplicate keys. -unionWith :: forall k v. Ord k => (v -> v -> v) -> Map k v -> Map k v -> Map k v -unionWith f m1 m2 = foldl go m2 (toUnfoldable m1 :: List (Tuple k v)) - where - go m (Tuple k v) = alter (Just <<< maybe v (f v)) k m - --- | Compute the union of two maps, preferring values from the first map in the case --- | of duplicate keys -union :: forall k v. Ord k => Map k v -> Map k v -> Map k v -union = unionWith const - --- | Compute the union of a collection of maps -unions :: forall k v f. Ord k => Foldable f => f (Map k v) -> Map k v -unions = foldl union empty - --- | Test whether one map contains all of the keys and values contained in another map -isSubmap :: forall k v. Ord k => Eq v => Map k v -> Map k v -> Boolean -isSubmap m1 m2 = LL.all f $ (toUnfoldable m1 :: LL.List (Tuple k v)) - where f (Tuple k v) = lookup k m2 == Just v - --- | Calculate the number of key/value pairs in a map -size :: forall k v. Map k v -> Int -size Leaf = 0 -size (Two m1 _ _ m2) = 1 + size m1 + size m2 -size (Three m1 _ _ m2 _ _ m3) = 2 + size m1 + size m2 + size m3 - --- | Apply a function of two arguments to each key/value pair, producing a new map -mapWithKey :: forall k v v'. (k -> v -> v') -> Map k v -> Map k v' -mapWithKey _ Leaf = Leaf -mapWithKey f (Two left k v right) = Two (mapWithKey f left) k (f k v) (mapWithKey f right) -mapWithKey f (Three left k1 v1 mid k2 v2 right) = Three (mapWithKey f left) k1 (f k1 v1) (mapWithKey f mid) k2 (f k2 v2) (mapWithKey f right) - --- | Filter out those key/value pairs of a map for which a predicate --- | fails to hold. -filterWithKey :: forall k v. Ord k => (k -> v -> Boolean) -> Map k v -> Map k v -filterWithKey predicate = - fromFoldable <<< LL.filter (uncurry predicate) <<< toUnfoldable - --- | Filter out those key/value pairs of a map for which a predicate --- | on the key fails to hold. -filterKeys :: forall k. Ord k => (k -> Boolean) -> Map k ~> Map k -filterKeys predicate = filterWithKey $ const <<< predicate - --- | Filter out those key/value pairs of a map for which a predicate --- | on the value fails to hold. -filter :: forall k v. Ord k => (v -> Boolean) -> Map k v -> Map k v -filter predicate = filterWithKey $ const predicate diff --git a/src/Data/Map/Gen.purs b/src/Data/Map/Gen.purs deleted file mode 100644 index 6398a2db..00000000 --- a/src/Data/Map/Gen.purs +++ /dev/null @@ -1,24 +0,0 @@ -module Data.Map.Gen where - -import Prelude - -import Control.Monad.Gen (class MonadGen, chooseInt, resize, sized, unfoldable) -import Control.Monad.Rec.Class (class MonadRec) -import Data.Map (Map, fromFoldable) -import Data.Tuple (Tuple(..)) -import Data.List (List) - --- | Generates a `Map` using the specified key and value generators. -genMap - :: forall m a b - . MonadRec m - => MonadGen m - => Ord a - => m a - -> m b - -> m (Map a b) -genMap genKey genValue = sized \size -> do - newSize <- chooseInt 0 size - resize (const newSize) $ - (fromFoldable :: List (Tuple a b) -> Map a b) - <$> unfoldable (Tuple <$> genKey <*> genValue) diff --git a/src/Data/StrMap.js b/src/Data/StrMap.js deleted file mode 100644 index ab82f990..00000000 --- a/src/Data/StrMap.js +++ /dev/null @@ -1,125 +0,0 @@ -"use strict"; - -exports._copyEff = function (m) { - return function () { - var r = {}; - for (var k in m) { - if (hasOwnProperty.call(m, k)) { - r[k] = m[k]; - } - } - return r; - }; -}; - -exports.empty = {}; - -exports.runST = function (f) { - return f; -}; - -exports._fmapStrMap = function (m0, f) { - var m = {}; - for (var k in m0) { - if (hasOwnProperty.call(m0, k)) { - m[k] = f(m0[k]); - } - } - return m; -}; - -exports._mapWithKey = function (m0, f) { - var m = {}; - for (var k in m0) { - if (hasOwnProperty.call(m0, k)) { - m[k] = f(k)(m0[k]); - } - } - return m; -}; - -exports._foldM = function (bind) { - return function (f) { - return function (mz) { - return function (m) { - var acc = mz; - function g(k) { - return function (z) { - return f(z)(k)(m[k]); - }; - } - for (var k in m) { - if (hasOwnProperty.call(m, k)) { - acc = bind(acc)(g(k)); - } - } - return acc; - }; - }; - }; -}; - -exports._foldSCStrMap = function (m, z, f, fromMaybe) { - var acc = z; - for (var k in m) { - if (hasOwnProperty.call(m, k)) { - var maybeR = f(acc)(k)(m[k]); - var r = fromMaybe(null)(maybeR); - if (r === null) return acc; - else acc = r; - } - } - return acc; -}; - -exports.all = function (f) { - return function (m) { - for (var k in m) { - if (hasOwnProperty.call(m, k) && !f(k)(m[k])) return false; - } - return true; - }; -}; - -exports.size = function (m) { - var s = 0; - for (var k in m) { - if (hasOwnProperty.call(m, k)) { - ++s; - } - } - return s; -}; - -exports._lookup = function (no, yes, k, m) { - return k in m ? yes(m[k]) : no; -}; - -exports._unsafeDeleteStrMap = function (m, k) { - delete m[k]; - return m; -}; - -exports._lookupST = function (no, yes, k, m) { - return function () { - return k in m ? yes(m[k]) : no; - }; -}; - -function toArrayWithKey(f) { - return function (m) { - var r = []; - for (var k in m) { - if (hasOwnProperty.call(m, k)) { - r.push(f(k)(m[k])); - } - } - return r; - }; -} - -exports.toArrayWithKey = toArrayWithKey; - -exports.keys = Object.keys || toArrayWithKey(function (k) { - return function () { return k; }; -}); diff --git a/src/Data/StrMap.purs b/src/Data/StrMap.purs deleted file mode 100644 index 3b57a4a7..00000000 --- a/src/Data/StrMap.purs +++ /dev/null @@ -1,302 +0,0 @@ --- | This module defines a type of native Javascript maps which --- | require the keys to be strings. --- | --- | To maximize performance, Javascript objects are not wrapped, --- | and some native code is used even when it's not necessary. - -module Data.StrMap - ( StrMap - , empty - , isEmpty - , size - , singleton - , insert - , lookup - , toUnfoldable - , toAscUnfoldable - , fromFoldable - , fromFoldableWith - , delete - , pop - , member - , alter - , update - , mapWithKey - , filterWithKey - , filterKeys - , filter - , keys - , values - , union - , unions - , isSubmap - , fold - , foldMap - , foldM - , foldMaybe - , all - , thawST - , freezeST - , runST - , pureST - , toArrayWithKey - ) where - -import Prelude - -import Control.Monad.Eff (Eff, runPure, foreachE) -import Control.Monad.ST as ST - -import Data.Array as A -import Data.Eq (class Eq1) -import Data.Foldable (class Foldable, foldl, foldr, for_) -import Data.FoldableWithIndex (class FoldableWithIndex) -import Data.Function.Uncurried (Fn2, runFn2, Fn4, runFn4) -import Data.FunctorWithIndex (class FunctorWithIndex) -import Data.Maybe (Maybe(..), maybe, fromMaybe) -import Data.Monoid (class Monoid, mempty) -import Data.StrMap.ST as SM -import Data.Traversable (class Traversable, traverse) -import Data.TraversableWithIndex (class TraversableWithIndex, traverseWithIndex) -import Data.Tuple (Tuple(..), fst, uncurry) -import Data.Unfoldable (class Unfoldable) - --- | `StrMap a` represents a map from `String`s to values of type `a`. -foreign import data StrMap :: Type -> Type - -foreign import _copyEff :: forall a b h r. a -> Eff (st :: ST.ST h | r) b - --- | Convert an immutable map into a mutable map -thawST :: forall a h r. StrMap a -> Eff (st :: ST.ST h | r) (SM.STStrMap h a) -thawST = _copyEff - --- | Convert a mutable map into an immutable map -freezeST :: forall a h r. SM.STStrMap h a -> Eff (st :: ST.ST h | r) (StrMap a) -freezeST = _copyEff - --- | Freeze a mutable map, creating an immutable map. Use this function as you would use --- | `Prelude.runST` to freeze a mutable reference. --- | --- | The rank-2 type prevents the map from escaping the scope of `runST`. -foreign import runST :: forall a r. (forall h. Eff (st :: ST.ST h | r) (SM.STStrMap h a)) -> Eff r (StrMap a) - -pureST :: forall a. (forall h e. Eff (st :: ST.ST h | e) (SM.STStrMap h a)) -> StrMap a -pureST f = runPure (runST f) - -mutate :: forall a b. (forall h e. SM.STStrMap h a -> Eff (st :: ST.ST h | e) b) -> StrMap a -> StrMap a -mutate f m = pureST do - s <- thawST m - _ <- f s - pure s - -foreign import _fmapStrMap :: forall a b. Fn2 (StrMap a) (a -> b) (StrMap b) - -instance functorStrMap :: Functor StrMap where - map f m = runFn2 _fmapStrMap m f - -instance functorWithIndexStrMap :: FunctorWithIndex String StrMap where - mapWithIndex = mapWithKey - -foreign import _foldM :: forall a m z. (m -> (z -> m) -> m) -> (z -> String -> a -> m) -> m -> StrMap a -> m - --- | Fold the keys and values of a map -fold :: forall a z. (z -> String -> a -> z) -> z -> StrMap a -> z -fold = _foldM ((#)) - --- | Fold the keys and values of a map, accumulating values using --- | some `Monoid`. -foldMap :: forall a m. Monoid m => (String -> a -> m) -> StrMap a -> m -foldMap f = fold (\acc k v -> acc <> f k v) mempty - --- | Fold the keys and values of a map, accumulating values and effects in --- | some `Monad`. -foldM :: forall a m z. Monad m => (z -> String -> a -> m z) -> z -> StrMap a -> m z -foldM f z = _foldM bind f (pure z) - -instance foldableStrMap :: Foldable StrMap where - foldl f = fold (\z _ -> f z) - foldr f z m = foldr f z (values m) - foldMap f = foldMap (const f) - -instance foldableWithIndexStrMap :: FoldableWithIndex String StrMap where - foldlWithIndex f = fold (flip f) - foldrWithIndex f z m = foldr (uncurry f) z (toArrayWithKey Tuple m) - foldMapWithIndex = foldMap - -instance traversableStrMap :: Traversable StrMap where - traverse = traverseWithIndex <<< const - sequence = traverse id - -instance traversableWithIndexStrMap :: TraversableWithIndex String StrMap where - traverseWithIndex f ms = - fold (\acc k v -> flip (insert k) <$> acc <*> f k v) (pure empty) ms - --- Unfortunately the above are not short-circuitable (consider using purescript-machines) --- so we need special cases: - -foreign import _foldSCStrMap :: forall a z. Fn4 (StrMap a) z (z -> String -> a -> Maybe z) (forall b. b -> Maybe b -> b) z - --- | Fold the keys and values of a map. --- | --- | This function allows the folding function to terminate the fold early, --- | using `Maybe`. -foldMaybe :: forall a z. (z -> String -> a -> Maybe z) -> z -> StrMap a -> z -foldMaybe f z m = runFn4 _foldSCStrMap m z f fromMaybe - --- | Test whether all key/value pairs in a `StrMap` satisfy a predicate. -foreign import all :: forall a. (String -> a -> Boolean) -> StrMap a -> Boolean - -instance eqStrMap :: Eq a => Eq (StrMap a) where - eq m1 m2 = (isSubmap m1 m2) && (isSubmap m2 m1) - -instance eq1StrMap :: Eq1 StrMap where - eq1 = eq - --- Internal use -toAscArray :: forall v. StrMap v -> Array (Tuple String v) -toAscArray = toAscUnfoldable - -instance ordStrMap :: Ord a => Ord (StrMap a) where - compare m1 m2 = compare (toAscArray m1) (toAscArray m2) - -instance showStrMap :: Show a => Show (StrMap a) where - show m = "(fromFoldable " <> show (toArray m) <> ")" - --- | An empty map -foreign import empty :: forall a. StrMap a - --- | Test whether one map contains all of the keys and values contained in another map -isSubmap :: forall a. Eq a => StrMap a -> StrMap a -> Boolean -isSubmap m1 m2 = all f m1 where - f k v = runFn4 _lookup false ((==) v) k m2 - --- | Test whether a map is empty -isEmpty :: forall a. StrMap a -> Boolean -isEmpty = all (\_ _ -> false) - --- | Calculate the number of key/value pairs in a map -foreign import size :: forall a. StrMap a -> Int - --- | Create a map with one key/value pair -singleton :: forall a. String -> a -> StrMap a -singleton k v = pureST do - s <- SM.new - SM.poke s k v - -foreign import _lookup :: forall a z. Fn4 z (a -> z) String (StrMap a) z - --- | Lookup the value for a key in a map -lookup :: forall a. String -> StrMap a -> Maybe a -lookup = runFn4 _lookup Nothing Just - --- | Test whether a `String` appears as a key in a map -member :: forall a. String -> StrMap a -> Boolean -member = runFn4 _lookup false (const true) - --- | Insert or replace a key/value pair in a map -insert :: forall a. String -> a -> StrMap a -> StrMap a -insert k v = mutate (\s -> void $ SM.poke s k v) - -foreign import _unsafeDeleteStrMap :: forall a. Fn2 (StrMap a) String (StrMap a) - --- | Delete a key and value from a map -delete :: forall a. String -> StrMap a -> StrMap a -delete k = mutate (\s -> void $ SM.delete s k) - --- | Delete a key and value from a map, returning the value --- | as well as the subsequent map -pop :: forall a. String -> StrMap a -> Maybe (Tuple a (StrMap a)) -pop k m = lookup k m <#> \a -> Tuple a (delete k m) - --- | Insert, remove or update a value for a key in a map -alter :: forall a. (Maybe a -> Maybe a) -> String -> StrMap a -> StrMap a -alter f k m = case f (k `lookup` m) of - Nothing -> delete k m - Just v -> insert k v m - --- | Remove or update a value for a key in a map -update :: forall a. (a -> Maybe a) -> String -> StrMap a -> StrMap a -update f k m = alter (maybe Nothing f) k m - --- | Create a map from a foldable collection of key/value pairs -fromFoldable :: forall f a. Foldable f => f (Tuple String a) -> StrMap a -fromFoldable l = pureST do - s <- SM.new - foreachE (A.fromFoldable l) \(Tuple k v) -> void (SM.poke s k v) - pure s - -foreign import _lookupST :: forall a h r z. Fn4 z (a -> z) String (SM.STStrMap h a) (Eff (st :: ST.ST h | r) z) - --- | Create a map from a foldable collection of key/value pairs, using the --- | specified function to combine values for duplicate keys. -fromFoldableWith :: forall f a. Foldable f => (a -> a -> a) -> f (Tuple String a) -> StrMap a -fromFoldableWith f l = pureST (do - s <- SM.new - for_ l (\(Tuple k v) -> runFn4 _lookupST v (f v) k s >>= SM.poke s k) - pure s) - -foreign import toArrayWithKey :: forall a b . (String -> a -> b) -> StrMap a -> Array b - --- | Unfolds a map into a list of key/value pairs -toUnfoldable :: forall f a. Unfoldable f => StrMap a -> f (Tuple String a) -toUnfoldable = A.toUnfoldable <<< toArrayWithKey Tuple - --- | Unfolds a map into a list of key/value pairs which is guaranteed to be --- | sorted by key -toAscUnfoldable :: forall f a. Unfoldable f => StrMap a -> f (Tuple String a) -toAscUnfoldable = A.toUnfoldable <<< A.sortWith fst <<< toArrayWithKey Tuple - --- Internal -toArray :: forall a. StrMap a -> Array (Tuple String a) -toArray = toArrayWithKey Tuple - --- | Get an array of the keys in a map -foreign import keys :: forall a. StrMap a -> Array String - --- | Get a list of the values in a map -values :: forall a. StrMap a -> Array a -values = toArrayWithKey (\_ v -> v) - --- | Compute the union of two maps, preferring the first map in the case of --- | duplicate keys. -union :: forall a. StrMap a -> StrMap a -> StrMap a -union m = mutate (\s -> void $ foldM SM.poke s m) - --- | Compute the union of a collection of maps -unions :: forall f a. Foldable f => f (StrMap a) -> StrMap a -unions = foldl union empty - -foreign import _mapWithKey :: forall a b. Fn2 (StrMap a) (String -> a -> b) (StrMap b) - --- | Apply a function of two arguments to each key/value pair, producing a new map -mapWithKey :: forall a b. (String -> a -> b) -> StrMap a -> StrMap b -mapWithKey f m = runFn2 _mapWithKey m f - -instance semigroupStrMap :: (Semigroup a) => Semigroup (StrMap a) where - append m1 m2 = mutate (\s1 -> void $ foldM (\s2 k v2 -> SM.poke s2 k (runFn4 _lookup v2 (\v1 -> v1 <> v2) k m2)) s1 m1) m2 - -instance monoidStrMap :: (Semigroup a) => Monoid (StrMap a) where - mempty = empty - --- | Filter out those key/value pairs of a map for which a predicate --- | fails to hold. -filterWithKey :: forall a. (String -> a -> Boolean) -> StrMap a -> StrMap a -filterWithKey predicate m = pureST go - where - go :: forall h e. Eff (st :: ST.ST h | e) (SM.STStrMap h a) - go = do - m' <- SM.new - foldM step m' m - - where - step acc k v = if predicate k v then SM.poke acc k v else pure acc - --- | Filter out those key/value pairs of a map for which a predicate --- | on the key fails to hold. -filterKeys :: (String -> Boolean) -> StrMap ~> StrMap -filterKeys predicate = filterWithKey $ const <<< predicate - --- | Filter out those key/value pairs of a map for which a predicate --- | on the value fails to hold. -filter :: forall a. (a -> Boolean) -> StrMap a -> StrMap a -filter predicate = filterWithKey $ const predicate diff --git a/src/Data/StrMap/Gen.purs b/src/Data/StrMap/Gen.purs deleted file mode 100644 index f44385d2..00000000 --- a/src/Data/StrMap/Gen.purs +++ /dev/null @@ -1,23 +0,0 @@ -module Data.StrMap.Gen where - -import Prelude - -import Control.Monad.Gen (class MonadGen, chooseInt, resize, sized, unfoldable) -import Control.Monad.Rec.Class (class MonadRec) -import Data.StrMap (StrMap, fromFoldable) -import Data.Tuple (Tuple(..)) -import Data.List (List) - --- | Generates a `StrMap` using the specified key and value generators. -genStrMap - :: forall m a - . MonadRec m - => MonadGen m - => m String - -> m a - -> m (StrMap a) -genStrMap genKey genValue = sized \size -> do - newSize <- chooseInt 0 size - resize (const newSize) $ - (fromFoldable :: List (Tuple String a) -> StrMap a) - <$> unfoldable (Tuple <$> genKey <*> genValue) diff --git a/src/Data/StrMap/ST.js b/src/Data/StrMap/ST.js deleted file mode 100644 index 7baf2f84..00000000 --- a/src/Data/StrMap/ST.js +++ /dev/null @@ -1,37 +0,0 @@ -"use strict"; - -exports["new"] = function () { - return {}; -}; - -exports.peekImpl = function (just) { - return function (nothing) { - return function (m) { - return function (k) { - return function () { - return {}.hasOwnProperty.call(m, k) ? just(m[k]) : nothing; - }; - }; - }; - }; -}; - -exports.poke = function (m) { - return function (k) { - return function (v) { - return function () { - m[k] = v; - return m; - }; - }; - }; -}; - -exports["delete"] = function (m) { - return function (k) { - return function () { - delete m[k]; - return m; - }; - }; -}; diff --git a/src/Data/StrMap/ST.purs b/src/Data/StrMap/ST.purs deleted file mode 100644 index ccf56105..00000000 --- a/src/Data/StrMap/ST.purs +++ /dev/null @@ -1,37 +0,0 @@ --- | Helper functions for working with mutable maps using the `ST` effect. --- | --- | This module can be used when performance is important and mutation is a local effect. - -module Data.StrMap.ST - ( STStrMap - , new - , peek - , poke - , delete - ) where - -import Control.Monad.Eff (Eff) -import Control.Monad.ST (ST) -import Data.Maybe (Maybe(..)) - --- | A reference to a mutable map --- | --- | The first type parameter represents the memory region which the map belongs to. The second type parameter defines the type of elements of the mutable array. --- | --- | The runtime representation of a value of type `STStrMap h a` is the same as that of `StrMap a`, except that mutation is allowed. -foreign import data STStrMap :: Type -> Type -> Type - --- | Create a new, empty mutable map -foreign import new :: forall a h r. Eff (st :: ST h | r) (STStrMap h a) - --- | Get the value for a key in a mutable map -peek :: forall a h r. STStrMap h a -> String -> Eff (st :: ST h | r) (Maybe a) -peek = peekImpl Just Nothing - -foreign import peekImpl :: forall a b h r. (a -> b) -> b -> STStrMap h a -> String -> Eff (st :: ST h | r) b - --- | Update the value for a key in a mutable map -foreign import poke :: forall a h r. STStrMap h a -> String -> a -> Eff (st :: ST h | r) (STStrMap h a) - --- | Remove a key and the corresponding value from a mutable map -foreign import delete :: forall a h r. STStrMap h a -> String -> Eff (st :: ST h | r) (STStrMap h a) diff --git a/src/Data/StrMap/ST/Unsafe.js b/src/Data/StrMap/ST/Unsafe.js deleted file mode 100644 index 83807658..00000000 --- a/src/Data/StrMap/ST/Unsafe.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -exports.unsafeFreeze = function (m) { - return function () { - return m; - }; -}; diff --git a/src/Data/StrMap/ST/Unsafe.purs b/src/Data/StrMap/ST/Unsafe.purs deleted file mode 100644 index 19c36d39..00000000 --- a/src/Data/StrMap/ST/Unsafe.purs +++ /dev/null @@ -1,11 +0,0 @@ -module Data.StrMap.ST.Unsafe where - -import Control.Monad.Eff (Eff) -import Control.Monad.ST (ST) -import Data.StrMap (StrMap) -import Data.StrMap.ST (STStrMap) - --- | Unsafely get the map out of ST without copying it --- | --- | If you later change the ST version of the map the pure value will also change. -foreign import unsafeFreeze :: forall a h r. STStrMap h a -> Eff (st :: ST h | r) (StrMap a) diff --git a/src/Data/StrMap/Unsafe.js b/src/Data/StrMap/Unsafe.js deleted file mode 100644 index 1cec670e..00000000 --- a/src/Data/StrMap/Unsafe.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -exports.unsafeIndex = function (m) { - return function (k) { - return m[k]; - }; -}; diff --git a/src/Data/StrMap/Unsafe.purs b/src/Data/StrMap/Unsafe.purs deleted file mode 100644 index 002df475..00000000 --- a/src/Data/StrMap/Unsafe.purs +++ /dev/null @@ -1,10 +0,0 @@ -module Data.StrMap.Unsafe - ( unsafeIndex - ) where - -import Data.StrMap (StrMap) - --- | Unsafely get the value for a key in a map. --- | --- | This function does not check whether the key exists in the map. -foreign import unsafeIndex :: forall a. StrMap a -> String -> a diff --git a/test/Test/Data/Map.purs b/test/Test/Data/Map.purs deleted file mode 100644 index bc38e615..00000000 --- a/test/Test/Data/Map.purs +++ /dev/null @@ -1,335 +0,0 @@ -module Test.Data.Map where - -import Prelude -import Control.Alt ((<|>)) -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (log, CONSOLE) -import Control.Monad.Eff.Exception (EXCEPTION) -import Control.Monad.Eff.Random (RANDOM) -import Data.Array as A -import Data.Foldable (foldl, for_, all) -import Data.Function (on) -import Data.List (List(Cons), groupBy, length, nubBy, singleton, sort, sortBy) -import Data.List.NonEmpty as NEL -import Data.Map as M -import Data.Map.Gen (genMap) -import Data.Maybe (Maybe(..), fromMaybe, maybe) -import Data.NonEmpty ((:|)) -import Data.Tuple (Tuple(..), fst, uncurry) -import Partial.Unsafe (unsafePartial) -import Test.QuickCheck ((), (===), quickCheck, quickCheck') -import Test.QuickCheck.Arbitrary (class Arbitrary, arbitrary) -import Test.QuickCheck.Gen (elements, oneOf) - -newtype TestMap k v = TestMap (M.Map k v) - -instance arbTestMap :: (Eq k, Ord k, Arbitrary k, Arbitrary v) => Arbitrary (TestMap k v) where - arbitrary = TestMap <$> genMap arbitrary arbitrary - -data SmallKey = A | B | C | D | E | F | G | H | I | J -derive instance eqSmallKey :: Eq SmallKey -derive instance ordSmallKey :: Ord SmallKey - -instance showSmallKey :: Show SmallKey where - show A = "A" - show B = "B" - show C = "C" - show D = "D" - show E = "E" - show F = "F" - show G = "G" - show H = "H" - show I = "I" - show J = "J" - -instance arbSmallKey :: Arbitrary SmallKey where - arbitrary = elements $ A :| [B, C, D, E, F, G, H, I, J] - -data Instruction k v = Insert k v | Delete k - -instance showInstruction :: (Show k, Show v) => Show (Instruction k v) where - show (Insert k v) = "Insert (" <> show k <> ") (" <> show v <> ")" - show (Delete k) = "Delete (" <> show k <> ")" - -instance arbInstruction :: (Arbitrary k, Arbitrary v) => Arbitrary (Instruction k v) where - arbitrary = oneOf $ (Insert <$> arbitrary <*> arbitrary) :| [Delete <$> arbitrary] - -runInstructions :: forall k v. Ord k => List (Instruction k v) -> M.Map k v -> M.Map k v -runInstructions instrs t0 = foldl step t0 instrs - where - step tree (Insert k v) = M.insert k v tree - step tree (Delete k) = M.delete k tree - -smallKey :: SmallKey -> SmallKey -smallKey k = k - -number :: Int -> Int -number n = n - -smallKeyToNumberMap :: M.Map SmallKey Int -> M.Map SmallKey Int -smallKeyToNumberMap m = m - -mapTests :: forall eff. Eff (console :: CONSOLE, random :: RANDOM, exception :: EXCEPTION | eff) Unit -mapTests = do - - -- Data.Map - - log "Test inserting into empty tree" - quickCheck $ \k v -> M.lookup (smallKey k) (M.insert k v M.empty) == Just (number v) - ("k: " <> show k <> ", v: " <> show v) - - log "Test inserting two values with same key" - quickCheck $ \k v1 v2 -> - M.lookup (smallKey k) (M.insert k v2 (M.insert k v1 M.empty)) == Just (number v2) - - log "Test delete after inserting" - quickCheck $ \k v -> M.isEmpty (M.delete (smallKey k) (M.insert k (number v) M.empty)) - ("k: " <> show k <> ", v: " <> show v) - - log "Test pop after inserting" - quickCheck $ \k v -> M.pop (smallKey k) (M.insert k (number v) M.empty) == Just (Tuple v M.empty) - ("k: " <> show k <> ", v: " <> show v) - - log "Pop non-existent key" - quickCheck $ \k1 k2 v -> k1 == k2 || M.pop (smallKey k2) (M.insert k1 (number v) M.empty) == Nothing - ("k1: " <> show k1 <> ", k2: " <> show k2 <> ", v: " <> show v) - - log "Insert two, lookup first" - quickCheck $ \k1 v1 k2 v2 -> k1 == k2 || M.lookup k1 (M.insert (smallKey k2) (number v2) (M.insert (smallKey k1) (number v1) M.empty)) == Just v1 - ("k1: " <> show k1 <> ", v1: " <> show v1 <> ", k2: " <> show k2 <> ", v2: " <> show v2) - - log "Insert two, lookup second" - quickCheck $ \k1 v1 k2 v2 -> M.lookup k2 (M.insert (smallKey k2) (number v2) (M.insert (smallKey k1) (number v1) M.empty)) == Just v2 - ("k1: " <> show k1 <> ", v1: " <> show v1 <> ", k2: " <> show k2 <> ", v2: " <> show v2) - - log "Insert two, delete one" - quickCheck $ \k1 v1 k2 v2 -> k1 == k2 || M.lookup k2 (M.delete k1 (M.insert (smallKey k2) (number v2) (M.insert (smallKey k1) (number v1) M.empty))) == Just v2 - ("k1: " <> show k1 <> ", v1: " <> show v1 <> ", k2: " <> show k2 <> ", v2: " <> show v2) - - log "Check balance property" - quickCheck' 1000 $ \instrs -> - let - tree :: M.Map SmallKey Int - tree = runInstructions instrs M.empty - in M.checkValid tree ("Map not balanced:\n " <> show tree <> "\nGenerated by:\n " <> show instrs) - - log "Lookup from empty" - quickCheck $ \k -> M.lookup k (M.empty :: M.Map SmallKey Int) == Nothing - - log "Lookup from singleton" - quickCheck $ \k v -> M.lookup (k :: SmallKey) (M.singleton k (v :: Int)) == Just v - - log "Random lookup" - quickCheck' 1000 $ \instrs k v -> - let - tree :: M.Map SmallKey Int - tree = M.insert k v (runInstructions instrs M.empty) - in M.lookup k tree == Just v ("instrs:\n " <> show instrs <> "\nk:\n " <> show k <> "\nv:\n " <> show v) - - log "Singleton to list" - quickCheck $ \k v -> M.toUnfoldable (M.singleton k v :: M.Map SmallKey Int) == singleton (Tuple k v) - - log "fromFoldable [] = empty" - quickCheck (M.fromFoldable [] == (M.empty :: M.Map Unit Unit) - "was not empty") - - log "fromFoldable & key collision" - do - let nums = M.fromFoldable [Tuple 0 "zero", Tuple 1 "what", Tuple 1 "one"] - quickCheck (M.lookup 0 nums == Just "zero" "invalid lookup - 0") - quickCheck (M.lookup 1 nums == Just "one" "invalid lookup - 1") - quickCheck (M.lookup 2 nums == Nothing "invalid lookup - 2") - - log "fromFoldableWith const [] = empty" - quickCheck (M.fromFoldableWith const [] == (M.empty :: M.Map Unit Unit) - "was not empty") - - log "fromFoldableWith (+) & key collision" - do - let nums = M.fromFoldableWith (+) [Tuple 0 1, Tuple 1 1, Tuple 1 1] - quickCheck (M.lookup 0 nums == Just 1 "invalid lookup - 0") - quickCheck (M.lookup 1 nums == Just 2 "invalid lookup - 1") - quickCheck (M.lookup 2 nums == Nothing "invalid lookup - 2") - - log "sort . toUnfoldable . fromFoldable = sort (on lists without key-duplicates)" - quickCheck $ \(list :: List (Tuple SmallKey Int)) -> - let nubbedList = nubBy ((==) `on` fst) list - f x = M.toUnfoldable (M.fromFoldable x) - in sort (f nubbedList) == sort nubbedList show nubbedList - - log "fromFoldable . toUnfoldable = id" - quickCheck $ \(TestMap (m :: M.Map SmallKey Int)) -> - let f m' = M.fromFoldable (M.toUnfoldable m' :: List (Tuple SmallKey Int)) - in f m == m show m - - log "fromFoldableWith const = fromFoldable" - quickCheck $ \arr -> - M.fromFoldableWith const arr == - M.fromFoldable (arr :: List (Tuple SmallKey Int)) show arr - - log "fromFoldableWith (<>) = fromFoldable . collapse with (<>) . group on fst" - quickCheck $ \arr -> - let combine (Tuple s a) (Tuple t b) = (Tuple s $ b <> a) - foldl1 g = unsafePartial \(Cons x xs) -> foldl g x xs - f = M.fromFoldable <<< map (foldl1 combine <<< NEL.toList) <<< - groupBy ((==) `on` fst) <<< sortBy (compare `on` fst) in - M.fromFoldableWith (<>) arr === f (arr :: List (Tuple String String)) - - log "toAscUnfoldable is sorted version of toUnfoldable" - quickCheck $ \(TestMap m) -> - let list = M.toUnfoldable (m :: M.Map SmallKey Int) - ascList = M.toAscUnfoldable m - in ascList === sortBy (compare `on` fst) list - - log "Lookup from union" - quickCheck $ \(TestMap m1) (TestMap m2) k -> - M.lookup (smallKey k) (M.union m1 m2) == (case M.lookup k m1 of - Nothing -> M.lookup k m2 - Just v -> Just (number v)) ("m1: " <> show m1 <> ", m2: " <> show m2 <> ", k: " <> show k <> ", v1: " <> show (M.lookup k m1) <> ", v2: " <> show (M.lookup k m2) <> ", union: " <> show (M.union m1 m2)) - - log "Union is idempotent" - quickCheck $ \(TestMap m1) (TestMap m2) -> (m1 `M.union` m2) == ((m1 `M.union` m2) `M.union` (m2 :: M.Map SmallKey Int)) - - log "Union prefers left" - quickCheck $ \(TestMap m1) (TestMap m2) k -> M.lookup k (M.union m1 (m2 :: M.Map SmallKey Int)) == (M.lookup k m1 <|> M.lookup k m2) - - log "unionWith" - for_ [Tuple (+) 0, Tuple (*) 1] $ \(Tuple op ident) -> - quickCheck $ \(TestMap m1) (TestMap m2) k -> - let u = M.unionWith op m1 m2 :: M.Map SmallKey Int - in case M.lookup k u of - Nothing -> not (M.member k m1 || M.member k m2) - Just v -> v == op (fromMaybe ident (M.lookup k m1)) (fromMaybe ident (M.lookup k m2)) - - log "unionWith argument order" - quickCheck $ \(TestMap m1) (TestMap m2) k -> - let u = M.unionWith (-) m1 m2 :: M.Map SmallKey Int - in1 = M.member k m1 - v1 = M.lookup k m1 - in2 = M.member k m2 - v2 = M.lookup k m2 - in case M.lookup k u of - Just v | in1 && in2 -> Just v == ((-) <$> v1 <*> v2) - Just v | in1 -> Just v == v1 - Just v -> Just v == v2 - Nothing -> not (in1 || in2) - - log "size" - quickCheck $ \xs -> - let xs' = nubBy ((==) `on` fst) xs - in M.size (M.fromFoldable xs') == length (xs' :: List (Tuple SmallKey Int)) - - log "lookupLE result is correct" - quickCheck $ \k (TestMap m) -> case M.lookupLE k (smallKeyToNumberMap m) of - Nothing -> all (_ > k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k1 < k2 && k2 < k - isLTwhenEQexists = k1 < k && M.member k m - in k1 <= k - && all (not <<< isCloserKey) (M.keys m) - && not isLTwhenEQexists - && M.lookup k1 m == Just v - - log "lookupGE result is correct" - quickCheck $ \k (TestMap m) -> case M.lookupGE k (smallKeyToNumberMap m) of - Nothing -> all (_ < k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k < k2 && k2 < k1 - isGTwhenEQexists = k < k1 && M.member k m - in k1 >= k - && all (not <<< isCloserKey) (M.keys m) - && not isGTwhenEQexists - && M.lookup k1 m == Just v - - log "lookupLT result is correct" - quickCheck $ \k (TestMap m) -> case M.lookupLT k (smallKeyToNumberMap m) of - Nothing -> all (_ >= k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k1 < k2 && k2 < k - in k1 < k - && all (not <<< isCloserKey) (M.keys m) - && M.lookup k1 m == Just v - - log "lookupGT result is correct" - quickCheck $ \k (TestMap m) -> case M.lookupGT k (smallKeyToNumberMap m) of - Nothing -> all (_ <= k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k < k2 && k2 < k1 - in k1 > k - && all (not <<< isCloserKey) (M.keys m) - && M.lookup k1 m == Just v - - log "findMin result is correct" - quickCheck $ \(TestMap m) -> case M.findMin (smallKeyToNumberMap m) of - Nothing -> M.isEmpty m - Just { key: k, value: v } -> M.lookup k m == Just v && all (_ >= k) (M.keys m) - - log "findMax result is correct" - quickCheck $ \(TestMap m) -> case M.findMax (smallKeyToNumberMap m) of - Nothing -> M.isEmpty m - Just { key: k, value: v } -> M.lookup k m == Just v && all (_ <= k) (M.keys m) - - log "mapWithKey is correct" - quickCheck $ \(TestMap m :: TestMap String Int) -> let - f k v = k <> show v - resultViaMapWithKey = m # M.mapWithKey f - toList = M.toUnfoldable :: forall k v. M.Map k v -> List (Tuple k v) - resultViaLists = m # toList # map (\(Tuple k v) → Tuple k (f k v)) # M.fromFoldable - in resultViaMapWithKey === resultViaLists - - log "filterWithKey gives submap" - quickCheck $ \(TestMap s :: TestMap String Int) p -> - M.isSubmap (M.filterWithKey p s) s - - log "filterWithKey keeps those keys for which predicate is true" - quickCheck $ \(TestMap s :: TestMap String Int) p -> - A.all (uncurry p) (M.toAscUnfoldable (M.filterWithKey p s) :: Array (Tuple String Int)) - - log "filterKeys gives submap" - quickCheck $ \(TestMap s :: TestMap String Int) p -> - M.isSubmap (M.filterKeys p s) s - - log "filterKeys keeps those keys for which predicate is true" - quickCheck $ \(TestMap s :: TestMap String Int) p -> - A.all p (M.keys (M.filterKeys p s)) - - log "filter gives submap" - quickCheck $ \(TestMap s :: TestMap String Int) p -> - M.isSubmap (M.filter p s) s - - log "filter keeps those values for which predicate is true" - quickCheck $ \(TestMap s :: TestMap String Int) p -> - A.all p (M.values (M.filter p s)) - - log "submap with no bounds = id" - quickCheck \(TestMap m :: TestMap SmallKey Int) -> - M.submap Nothing Nothing m === m - - log "submap with lower bound" - quickCheck' 1 $ - M.submap (Just B) Nothing (M.fromFoldable [Tuple A 0, Tuple B 0]) - == M.fromFoldable [Tuple B 0] - - log "submap with upper bound" - quickCheck' 1 $ - M.submap Nothing (Just A) (M.fromFoldable [Tuple A 0, Tuple B 0]) - == M.fromFoldable [Tuple A 0] - - log "submap with lower & upper bound" - quickCheck' 1 $ - M.submap (Just B) (Just B) (M.fromFoldable [Tuple A 0, Tuple B 0, Tuple C 0]) - == M.fromFoldable [Tuple B 0] - - log "submap" - quickCheck' 1000 \(TestMap m :: TestMap SmallKey Int) mmin mmax key -> - let - m' = M.submap mmin mmax m - in - (if (maybe true (\min -> min <= key) mmin && - maybe true (\max -> max >= key) mmax) - then M.lookup key m == M.lookup key m' - else (not (M.member key m'))) - "m: " <> show m - <> ", mmin: " <> show mmin - <> ", mmax: " <> show mmax - <> ", key: " <> show key diff --git a/test/Test/Data/StrMap.purs b/test/Test/Data/StrMap.purs deleted file mode 100644 index ae4ba00d..00000000 --- a/test/Test/Data/StrMap.purs +++ /dev/null @@ -1,252 +0,0 @@ -module Test.Data.StrMap where - -import Prelude - -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (log, CONSOLE) -import Control.Monad.Eff.Exception (EXCEPTION) -import Control.Monad.Eff.Random (RANDOM) -import Control.Monad.Writer (runWriter, tell) -import Data.Array as A -import Data.Foldable (foldl, foldr) -import Data.FoldableWithIndex (foldlWithIndex, foldrWithIndex, foldMapWithIndex) -import Data.Function (on) -import Data.List as L -import Data.List.NonEmpty as NEL -import Data.Maybe (Maybe(..)) -import Data.NonEmpty ((:|)) -import Data.StrMap as M -import Data.StrMap.Gen (genStrMap) -import Data.Traversable (sequence, traverse) -import Data.TraversableWithIndex (traverseWithIndex) -import Data.Tuple (Tuple(..), fst, snd, uncurry) -import Partial.Unsafe (unsafePartial) -import Test.QuickCheck ((), quickCheck, quickCheck', (===)) -import Test.QuickCheck.Arbitrary (class Arbitrary, arbitrary) -import Test.QuickCheck.Gen as Gen - -newtype TestStrMap v = TestStrMap (M.StrMap v) - -instance arbTestStrMap :: (Arbitrary v) => Arbitrary (TestStrMap v) where - arbitrary = TestStrMap <$> genStrMap arbitrary arbitrary - -newtype SmallArray v = SmallArray (Array v) - -instance arbSmallArray :: (Arbitrary v) => Arbitrary (SmallArray v) where - arbitrary = SmallArray <$> Gen.resize 3 arbitrary - -data Instruction k v = Insert k v | Delete k - -instance showInstruction :: (Show k, Show v) => Show (Instruction k v) where - show (Insert k v) = "Insert (" <> show k <> ") (" <> show v <> ")" - show (Delete k) = "Delete (" <> show k <> ")" - -instance arbInstruction :: (Arbitrary v) => Arbitrary (Instruction String v) where - arbitrary = do - b <- arbitrary - k <- Gen.frequency $ Tuple 10.0 (pure "hasOwnProperty") :| pure (Tuple 50.0 arbitrary) - case b of - true -> do - v <- arbitrary - pure (Insert k v) - false -> do - pure (Delete k) - -runInstructions :: forall v. L.List (Instruction String v) -> M.StrMap v -> M.StrMap v -runInstructions instrs t0 = foldl step t0 instrs - where - step tree (Insert k v) = M.insert k v tree - step tree (Delete k) = M.delete k tree - -number :: Int -> Int -number n = n - -toAscArray :: forall a. M.StrMap a -> Array (Tuple String a) -toAscArray = M.toAscUnfoldable - -strMapTests :: forall eff. Eff (console :: CONSOLE, random :: RANDOM, exception :: EXCEPTION | eff) Unit -strMapTests = do - log "Test inserting into empty tree" - quickCheck $ \k v -> M.lookup k (M.insert k v M.empty) == Just (number v) - ("k: " <> show k <> ", v: " <> show v) - - log "Test inserting two values with same key" - quickCheck $ \k v1 v2 -> - M.lookup k (M.insert k v2 (M.insert k v1 M.empty)) == Just (number v2) - - log "Test delete after inserting" - quickCheck $ \k v -> M.isEmpty (M.delete k (M.insert k (number v) M.empty)) - ("k: " <> show k <> ", v: " <> show v) - - log "Test pop after inserting" - quickCheck $ \k v -> M.pop k (M.insert k (number v) M.empty) == Just (Tuple v M.empty) - ("k: " <> show k <> ", v: " <> show v) - - log "Pop non-existent key" - quickCheck $ \k1 k2 v -> k1 == k2 || M.pop k2 (M.insert k1 (number v) M.empty) == Nothing - ("k1: " <> show k1 <> ", k2: " <> show k2 <> ", v: " <> show v) - - log "Insert two, lookup first" - quickCheck $ \k1 v1 k2 v2 -> k1 == k2 || M.lookup k1 (M.insert k2 (number v2) (M.insert k1 (number v1) M.empty)) == Just v1 - ("k1: " <> show k1 <> ", v1: " <> show v1 <> ", k2: " <> show k2 <> ", v2: " <> show v2) - - log "Insert two, lookup second" - quickCheck $ \k1 v1 k2 v2 -> M.lookup k2 (M.insert k2 (number v2) (M.insert k1 (number v1) M.empty)) == Just v2 - ("k1: " <> show k1 <> ", v1: " <> show v1 <> ", k2: " <> show k2 <> ", v2: " <> show v2) - - log "Insert two, delete one" - quickCheck $ \k1 v1 k2 v2 -> k1 == k2 || M.lookup k2 (M.delete k1 (M.insert k2 (number v2) (M.insert k1 (number v1) M.empty))) == Just v2 - ("k1: " <> show k1 <> ", v1: " <> show v1 <> ", k2: " <> show k2 <> ", v2: " <> show v2) - - log "Lookup from empty" - quickCheck $ \k -> M.lookup k (M.empty :: M.StrMap Int) == Nothing - - log "Lookup from singleton" - quickCheck $ \k v -> M.lookup k (M.singleton k (v :: Int)) == Just v - - log "Random lookup" - quickCheck' 1000 $ \instrs k v -> - let - tree :: M.StrMap Int - tree = M.insert k v (runInstructions instrs M.empty) - in M.lookup k tree == Just v ("instrs:\n " <> show instrs <> "\nk:\n " <> show k <> "\nv:\n " <> show v) - - log "Singleton to list" - quickCheck $ \k v -> M.toUnfoldable (M.singleton k v :: M.StrMap Int) == L.singleton (Tuple k v) - - log "filterWithKey gives submap" - quickCheck $ \(TestStrMap (s :: M.StrMap Int)) p -> - M.isSubmap (M.filterWithKey p s) s - - log "filterWithKey keeps those keys for which predicate is true" - quickCheck $ \(TestStrMap (s :: M.StrMap Int)) p -> - A.all (uncurry p) (M.toAscUnfoldable (M.filterWithKey p s) :: Array (Tuple String Int)) - - log "filterKeys gives submap" - quickCheck $ \(TestStrMap (s :: M.StrMap Int)) p -> - M.isSubmap (M.filterKeys p s) s - - log "filterKeys keeps those keys for which predicate is true" - quickCheck $ \(TestStrMap (s :: M.StrMap Int)) p -> - A.all p (M.keys (M.filterKeys p s)) - - log "filter gives submap" - quickCheck $ \(TestStrMap (s :: M.StrMap Int)) p -> - M.isSubmap (M.filter p s) s - - log "filter keeps those values for which predicate is true" - quickCheck $ \(TestStrMap (s :: M.StrMap Int)) p -> - A.all p (M.values (M.filter p s)) - - log "fromFoldable [] = empty" - quickCheck (M.fromFoldable [] == (M.empty :: M.StrMap Unit) - "was not empty") - - log "fromFoldable & key collision" - do - let nums = M.fromFoldable [Tuple "0" "zero", Tuple "1" "what", Tuple "1" "one"] - quickCheck (M.lookup "0" nums == Just "zero" "invalid lookup - 0") - quickCheck (M.lookup "1" nums == Just "one" "invalid lookup - 1") - quickCheck (M.lookup "2" nums == Nothing "invalid lookup - 2") - - log "fromFoldableWith const [] = empty" - quickCheck (M.fromFoldableWith const [] == (M.empty :: M.StrMap Unit) - "was not empty") - - log "fromFoldableWith (+) & key collision" - do - let nums = M.fromFoldableWith (+) [Tuple "0" 1, Tuple "1" 1, Tuple "1" 1] - quickCheck (M.lookup "0" nums == Just 1 "invalid lookup - 0") - quickCheck (M.lookup "1" nums == Just 2 "invalid lookup - 1") - quickCheck (M.lookup "2" nums == Nothing "invalid lookup - 2") - - log "toUnfoldable . fromFoldable = id" - quickCheck $ \arr -> let f x = M.toUnfoldable (M.fromFoldable x) - in f (f arr) == f (arr :: L.List (Tuple String Int)) show arr - - log "fromFoldable . toUnfoldable = id" - quickCheck $ \(TestStrMap m) -> - let f m1 = M.fromFoldable ((M.toUnfoldable m1) :: L.List (Tuple String Int)) in - M.toUnfoldable (f m) == (M.toUnfoldable m :: L.List (Tuple String Int)) show m - - log "fromFoldableWith const = fromFoldable" - quickCheck $ \arr -> M.fromFoldableWith const arr == - M.fromFoldable (arr :: L.List (Tuple String Int)) show arr - - log "fromFoldableWith (<>) = fromFoldable . collapse with (<>) . group on fst" - quickCheck $ \arr -> - let combine (Tuple s a) (Tuple t b) = (Tuple s $ b <> a) - foldl1 g = unsafePartial \(L.Cons x xs) -> foldl g x xs - f = M.fromFoldable <<< map (foldl1 combine <<< NEL.toList) <<< - L.groupBy ((==) `on` fst) <<< L.sortBy (compare `on` fst) in - M.fromFoldableWith (<>) arr == f (arr :: L.List (Tuple String String)) show arr - - log "Lookup from union" - quickCheck $ \(TestStrMap m1) (TestStrMap m2) k -> - M.lookup k (M.union m1 m2) == (case M.lookup k m1 of - Nothing -> M.lookup k m2 - Just v -> Just (number v)) ("m1: " <> show m1 <> ", m2: " <> show m2 <> ", k: " <> show k <> ", v1: " <> show (M.lookup k m1) <> ", v2: " <> show (M.lookup k m2) <> ", union: " <> show (M.union m1 m2)) - - log "Union is idempotent" - quickCheck $ \(TestStrMap m1) (TestStrMap m2) -> - (m1 `M.union` m2) == ((m1 `M.union` m2) `M.union` (m2 :: M.StrMap Int)) (show (M.size (m1 `M.union` m2)) <> " != " <> show (M.size ((m1 `M.union` m2) `M.union` m2))) - - log "fromFoldable = zip keys values" - quickCheck $ \(TestStrMap m) -> M.toUnfoldable m == A.zipWith Tuple (M.keys m) (M.values m :: Array Int) - - log "mapWithKey is correct" - quickCheck $ \(TestStrMap m :: TestStrMap Int) -> let - f k v = k <> show v - resultViaMapWithKey = m # M.mapWithKey f - resultViaLists = m # M.toUnfoldable # map (\(Tuple k v) → Tuple k (f k v)) # (M.fromFoldable :: forall a. L.List (Tuple String a) -> M.StrMap a) - in resultViaMapWithKey === resultViaLists - - log "foldl = foldlWithIndex <<< const" - quickCheck \(TestStrMap m :: TestStrMap String) -> - let f z v = z <> "," <> v - in foldl f "" m === foldlWithIndex (const f) "" m - - log "foldr = foldrWithIndex <<< const" - quickCheck \(TestStrMap m :: TestStrMap String) -> - let f v z = v <> "," <> z - in foldr f "" m === foldrWithIndex (const f) "" m - - log "foldlWithIndex = foldrWithIndex with flipped operation" - quickCheck \(TestStrMap m :: TestStrMap String) -> - let f k z v = z <> "," <> k <> ":" <> v - g k v z = k <> ":" <> v <> "," <> z - in foldlWithIndex f "" m <> "," === "," <> foldrWithIndex g "" m - - log "foldMapWithIndex f ~ traverseWithIndex (\\k v -> tell (f k v))" - quickCheck \(TestStrMap m :: TestStrMap Int) -> - let f k v = "(" <> "k" <> "," <> show v <> ")" - resultA = foldMapWithIndex f m - resultB = snd (runWriter (traverseWithIndex (\k v -> tell (f k v)) m)) - in resultA === resultB - - log "traverse = traverseWithIndex <<< const (for m = Writer)" - quickCheck \(TestStrMap m :: TestStrMap String) -> - runWriter (traverse tell m) === - runWriter (traverseWithIndex (const tell) m) - - log "sequence works (for m = Array)" - quickCheck \(TestStrMap mOfSmallArrays :: TestStrMap (SmallArray Int)) -> - let m = (\(SmallArray a) -> a) <$> mOfSmallArrays - Tuple keys values = A.unzip (toAscArray m) - resultViaArrays = (M.fromFoldable <<< A.zip keys) <$> sequence values - in A.sort (sequence m) === A.sort (resultViaArrays) - - log "sequence works (for m = Maybe)" - quickCheck \(TestStrMap m :: TestStrMap (Maybe Int)) -> - let Tuple keys values = A.unzip (toAscArray m) - resultViaArrays = (M.fromFoldable <<< A.zip keys) <$> sequence values - in sequence m === resultViaArrays - - log "Bug #63: accidental observable mutation in foldMap" - quickCheck \(TestStrMap m) -> - let lhs = go m - rhs = go m - in lhs == rhs ("lhs: " <> show lhs <> ", rhs: " <> show rhs) - where - go :: M.StrMap (Array Ordering) -> Array Ordering - go = M.foldMap \_ v -> v diff --git a/test/Test/Main.purs b/test/Test/Main.purs deleted file mode 100644 index 232714d2..00000000 --- a/test/Test/Main.purs +++ /dev/null @@ -1,19 +0,0 @@ -module Test.Main where - -import Prelude - -import Control.Monad.Eff (Eff) -import Control.Monad.Eff.Console (log, CONSOLE) -import Control.Monad.Eff.Exception (EXCEPTION) -import Control.Monad.Eff.Random (RANDOM) - -import Test.Data.Map (mapTests) -import Test.Data.StrMap (strMapTests) - -main :: Eff (console :: CONSOLE, random :: RANDOM, exception :: EXCEPTION) Unit -main = do - log "Running Map tests" - mapTests - - log "Running StrMap tests" - strMapTests