Skip to content
This repository was archived by the owner on Oct 4, 2020. It is now read-only.

Commit f831f48

Browse files
authored
Merge pull request #56 from mtolly/master
Add lookup{LT,LE,GT,GE}, find{Min,Max}
2 parents 35a8464 + 7c2354c commit f831f48

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

src/Data/Map.purs

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ module Data.Map
1010
, checkValid
1111
, insert
1212
, lookup
13+
, lookupLE
14+
, lookupLT
15+
, lookupGE
16+
, lookupGT
17+
, findMin
18+
, findMax
1319
, fromFoldable
1420
, fromFoldableWith
1521
, toList
@@ -32,7 +38,7 @@ import Prelude
3238

3339
import Data.Foldable (foldl, foldMap, foldr, class Foldable)
3440
import Data.List (List(..), length, nub)
35-
import Data.Maybe (Maybe(..), maybe, isJust)
41+
import Data.Maybe (Maybe(..), maybe, isJust, fromMaybe)
3642
import Data.Monoid (class Monoid)
3743
import Data.Traversable (traverse, class Traversable)
3844
import Data.Tuple (Tuple(..), uncurry, snd)
@@ -139,6 +145,67 @@ lookup = unsafePartial \k tree ->
139145
_ , GT -> lookup k right
140146
_ , _ -> lookup k mid
141147

148+
149+
-- | Lookup a value for the specified key, or the greatest one less than it
150+
lookupLE :: forall k v. (Ord k) => k -> Map k v -> Maybe { key :: k, value :: v }
151+
lookupLE _ Leaf = Nothing
152+
lookupLE k (Two left k1 v1 right) = case compare k k1 of
153+
EQ -> Just { key: k1, value: v1 }
154+
GT -> Just $ fromMaybe { key: k1, value: v1 } $ lookupLE k right
155+
LT -> lookupLE k left
156+
lookupLE k (Three left k1 v1 mid k2 v2 right) = case compare k k2 of
157+
EQ -> Just { key: k2, value: v2 }
158+
GT -> Just $ fromMaybe { key: k2, value: v2 } $ lookupLE k right
159+
LT -> lookupLE k $ Two left k1 v1 mid
160+
161+
-- | Lookup a value for the greatest key less than the specified key
162+
lookupLT :: forall k v. (Ord k) => k -> Map k v -> Maybe { key :: k, value :: v }
163+
lookupLT _ Leaf = Nothing
164+
lookupLT k (Two left k1 v1 right) = case compare k k1 of
165+
EQ -> findMax left
166+
GT -> Just $ fromMaybe { key: k1, value: v1 } $ lookupLT k right
167+
LT -> lookupLT k left
168+
lookupLT k (Three left k1 v1 mid k2 v2 right) = case compare k k2 of
169+
EQ -> findMax $ Two left k1 v1 mid
170+
GT -> Just $ fromMaybe { key: k2, value: v2 } $ lookupLT k right
171+
LT -> lookupLT k $ Two left k1 v1 mid
172+
173+
-- | Lookup a value for the specified key, or the least one greater than it
174+
lookupGE :: forall k v. (Ord k) => k -> Map k v -> Maybe { key :: k, value :: v }
175+
lookupGE _ Leaf = Nothing
176+
lookupGE k (Two left k1 v1 right) = case compare k k1 of
177+
EQ -> Just { key: k1, value: v1 }
178+
LT -> Just $ fromMaybe { key: k1, value: v1 } $ lookupGE k left
179+
GT -> lookupGE k right
180+
lookupGE k (Three left k1 v1 mid k2 v2 right) = case compare k k1 of
181+
EQ -> Just { key: k1, value: v1 }
182+
LT -> Just $ fromMaybe { key: k1, value: v1 } $ lookupGE k left
183+
GT -> lookupGE k $ Two mid k2 v2 right
184+
185+
-- | Lookup a value for the least key greater than the specified key
186+
lookupGT :: forall k v. (Ord k) => k -> Map k v -> Maybe { key :: k, value :: v }
187+
lookupGT _ Leaf = Nothing
188+
lookupGT k (Two left k1 v1 right) = case compare k k1 of
189+
EQ -> findMin right
190+
LT -> Just $ fromMaybe { key: k1, value: v1 } $ lookupGT k left
191+
GT -> lookupGT k right
192+
lookupGT k (Three left k1 v1 mid k2 v2 right) = case compare k k1 of
193+
EQ -> findMin $ Two mid k2 v2 right
194+
LT -> Just $ fromMaybe { key: k1, value: v1 } $ lookupGT k left
195+
GT -> lookupGT k $ Two mid k2 v2 right
196+
197+
-- | Returns the pair with the greatest key
198+
findMax :: forall k v. Map k v -> Maybe { key :: k, value :: v }
199+
findMax Leaf = Nothing
200+
findMax (Two _ k1 v1 right) = Just $ fromMaybe { key: k1, value: v1 } $ findMax right
201+
findMax (Three _ _ _ _ k2 v2 right) = Just $ fromMaybe { key: k2, value: v2 } $ findMax right
202+
203+
-- | Returns the pair with the least key
204+
findMin :: forall k v. Map k v -> Maybe { key :: k, value :: v }
205+
findMin Leaf = Nothing
206+
findMin (Two left k1 v1 _) = Just $ fromMaybe { key: k1, value: v1 } $ findMin left
207+
findMin (Three left k1 v1 _ _ _ _) = Just $ fromMaybe { key: k1, value: v1 } $ findMin left
208+
142209
-- | Test if a key is a member of a map
143210
member :: forall k v. Ord k => k -> Map k v -> Boolean
144211
member k m = isJust (k `lookup` m)

test/Test/Data/Map.purs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Control.Monad.Eff.Console (log, CONSOLE)
88
import Control.Monad.Eff.Exception (EXCEPTION)
99
import Control.Monad.Eff.Random (RANDOM)
1010

11-
import Data.Foldable (foldl, for_)
11+
import Data.Foldable (foldl, for_, all)
1212
import Data.Function (on)
1313
import Data.List (List(..), groupBy, length, nubBy, sortBy, singleton)
1414
import Data.Map as M
@@ -112,6 +112,9 @@ smallKey k = k
112112
number :: Int -> Int
113113
number n = n
114114

115+
smallKeyToNumberMap :: M.Map SmallKey Int -> M.Map SmallKey Int
116+
smallKeyToNumberMap m = m
117+
115118
mapTests :: forall eff. Eff (console :: CONSOLE, random :: RANDOM, err :: EXCEPTION | eff) Unit
116119
mapTests = do
117120

@@ -247,3 +250,53 @@ mapTests = do
247250
quickCheck $ \xs ->
248251
let xs' = nubBy ((==) `on` fst) xs
249252
in M.size (M.fromList xs') == length (xs' :: List (Tuple SmallKey Int))
253+
254+
log "lookupLE result is correct"
255+
quickCheck $ \k (TestMap m) -> case M.lookupLE k (smallKeyToNumberMap m) of
256+
Nothing -> all (_ > k) $ M.keys m
257+
Just { key: k1, value: v } -> let
258+
isCloserKey k2 = k1 < k2 && k2 < k
259+
isLTwhenEQexists = k1 < k && M.member k m
260+
in k1 <= k
261+
&& all (not <<< isCloserKey) (M.keys m)
262+
&& not isLTwhenEQexists
263+
&& M.lookup k1 m == Just v
264+
265+
log "lookupGE result is correct"
266+
quickCheck $ \k (TestMap m) -> case M.lookupGE k (smallKeyToNumberMap m) of
267+
Nothing -> all (_ < k) $ M.keys m
268+
Just { key: k1, value: v } -> let
269+
isCloserKey k2 = k < k2 && k2 < k1
270+
isGTwhenEQexists = k < k1 && M.member k m
271+
in k1 >= k
272+
&& all (not <<< isCloserKey) (M.keys m)
273+
&& not isGTwhenEQexists
274+
&& M.lookup k1 m == Just v
275+
276+
log "lookupLT result is correct"
277+
quickCheck $ \k (TestMap m) -> case M.lookupLT k (smallKeyToNumberMap m) of
278+
Nothing -> all (_ >= k) $ M.keys m
279+
Just { key: k1, value: v } -> let
280+
isCloserKey k2 = k1 < k2 && k2 < k
281+
in k1 < k
282+
&& all (not <<< isCloserKey) (M.keys m)
283+
&& M.lookup k1 m == Just v
284+
285+
log "lookupGT result is correct"
286+
quickCheck $ \k (TestMap m) -> case M.lookupGT k (smallKeyToNumberMap m) of
287+
Nothing -> all (_ <= k) $ M.keys m
288+
Just { key: k1, value: v } -> let
289+
isCloserKey k2 = k < k2 && k2 < k1
290+
in k1 > k
291+
&& all (not <<< isCloserKey) (M.keys m)
292+
&& M.lookup k1 m == Just v
293+
294+
log "findMin result is correct"
295+
quickCheck $ \(TestMap m) -> case M.findMin (smallKeyToNumberMap m) of
296+
Nothing -> M.isEmpty m
297+
Just { key: k, value: v } -> M.lookup k m == Just v && all (_ >= k) (M.keys m)
298+
299+
log "findMax result is correct"
300+
quickCheck $ \(TestMap m) -> case M.findMax (smallKeyToNumberMap m) of
301+
Nothing -> M.isEmpty m
302+
Just { key: k, value: v } -> M.lookup k m == Just v && all (_ <= k) (M.keys m)

0 commit comments

Comments
 (0)