Skip to content

Commit e852880

Browse files
committed
Add alter
1 parent bf0c275 commit e852880

File tree

6 files changed

+118
-4
lines changed

6 files changed

+118
-4
lines changed

docs/Data/IntMap.md

+13
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,19 @@ updateWithKey :: forall a. (Int -> a -> Maybe a) -> Int -> IntMap a -> IntMap a
143143
at @k@ (if it is in the map). If (@f k x@) is 'Nothing', the element is
144144
deleted. If it is (@'Just' y@), the key @k@ is bound to the new value @y@.
145145

146+
#### `alter`
147+
148+
``` purescript
149+
alter :: forall a. (Maybe a -> Maybe a) -> Int -> IntMap a -> IntMap a
150+
```
151+
152+
/O(min(n,W))/. The expresion (@'alter' f k m@) alters the value @x@
153+
at key @k@, or absence thereof.
154+
'alter' can be used to insert, delete, or update the value under given
155+
key in the 'IntMap'.
156+
The following property holds:
157+
@'lookup' k ('alter' f k m) = f ('lookup' k m)@.
158+
146159
#### `unionWith`
147160

148161
``` purescript

docs/Data/IntMap/Internal.md

+12
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,18 @@ branchingBit' :: Int -> Mask -> Int -> Mask -> Mask
7979
branchingBit :: Int -> Int -> Mask
8080
```
8181

82+
#### `branchMask`
83+
84+
``` purescript
85+
branchMask :: Int -> Int -> Mask
86+
```
87+
88+
#### `highestBitMask`
89+
90+
``` purescript
91+
highestBitMask :: Int -> Int
92+
```
93+
8294
#### `dec2bin`
8395

8496
``` purescript

src/Data/IntMap.purs

+43-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ module Data.IntMap (
3535
, adjustWithKey
3636
, update
3737
, updateWithKey
38+
, alter
3839

3940
, unionWith
4041
, unionLeft
@@ -58,7 +59,7 @@ module Data.IntMap (
5859
) where
5960

6061
import Data.Foldable (class Foldable, foldMap, foldl)
61-
import Data.IntMap.Internal (Prefix, Mask(Mask), mask, branchLeft, branchingBit', prefixAsKey, matchPrefix, maskLonger)
62+
import Data.IntMap.Internal (Prefix(Prefix), Mask(Mask), mask, branchLeft, branchingBit', prefixAsKey, matchPrefix, maskLonger, branchMask)
6263
import Data.Maybe (Maybe(Nothing, Just))
6364
import Data.Monoid (class Monoid, mempty)
6465
import Data.Traversable (class Traversable)
@@ -226,8 +227,38 @@ updateWithKey f k t = go t where
226227
| branchLeft m k -> br p m (go l) r
227228
| otherwise -> br p m l (go r)
228229

229-
-- | Unions two `IntMap`s together using a splatting function. If
230-
-- | a key is present in both constituent lists then the resulting
230+
-- | /O(min(n,W))/. The expresion (@'alter' f k m@) alters the value @x@
231+
-- | at key @k@, or absence thereof.
232+
-- | 'alter' can be used to insert, delete, or update the value under given
233+
-- | key in the 'IntMap'.
234+
-- | The following property holds:
235+
-- | @'lookup' k ('alter' f k m) = f ('lookup' k m)@.
236+
alter :: forall a. (Maybe a -> Maybe a) -> Int -> IntMap a -> IntMap a
237+
alter f k t =
238+
case t of
239+
Br p@(Prefix p') m l r
240+
| not (matchPrefix p m k) ->
241+
case f Nothing of
242+
Nothing -> t
243+
Just a -> link k (Lf k a) p' t
244+
| branchLeft m k -> br p m (alter f k l) r
245+
| otherwise -> br p m l (alter f k r)
246+
Lf ky y
247+
| k == ky ->
248+
case f (Just y) of
249+
Just x -> Lf ky x
250+
Nothing -> Empty
251+
| otherwise ->
252+
case f Nothing of
253+
Just x -> link k (Lf k x) ky t
254+
Nothing -> Lf ky y
255+
Empty ->
256+
case f Nothing of
257+
Just a -> Lf k a
258+
Nothing -> Empty
259+
260+
-- | Unions two `IntMap`s together using a splatting function. If
261+
-- | a key is present in both constituent lists then the resulting
231262
-- | list will be the splat of the values from each constituent. If the key
232263
-- | was available in only one constituent then it is available unmodified
233264
-- | in the result.
@@ -378,3 +409,12 @@ join k1 m1 t1 k2 m2 t2 =
378409
in if branchLeft m k1
379410
then Br (mask m k1) m t1 t2
380411
else Br (mask m k1) m t2 t1
412+
413+
link :: forall a. Int -> IntMap a -> Int -> IntMap a -> IntMap a
414+
link k1 t1 k2 t2 =
415+
if branchLeft m k1
416+
then Br p m t1 t2
417+
else Br p m t2 t1
418+
where
419+
m = branchMask k1 k2
420+
p = mask m k1

src/Data/IntMap/Internal.purs

+14-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
module Data.IntMap.Internal where
33

44
import Data.Function (Fn2 (), runFn2)
5-
import Data.Int.Bits ((.^.), complement, (.&.), (.|.))
5+
import Data.Int.Bits ((.^.), complement, (.&.), (.|.), zshr)
66
import Prelude (class Ord, class Eq, (*), (-), otherwise, (==), (<), compare, eq)
77

88
-- Newtypes
@@ -57,6 +57,19 @@ branchingBit' k1 (Mask m1) k2 (Mask m2) =
5757
branchingBit :: Int -> Int -> Mask
5858
branchingBit k1 k2 = branchingBit' k1 (Mask 0) k2 (Mask 0)
5959

60+
branchMask :: Int -> Int -> Mask
61+
branchMask x1 x2 =
62+
Mask (highestBitMask (x1 .^. x2))
63+
64+
highestBitMask :: Int -> Int
65+
highestBitMask x1 =
66+
let x2 = x1 .|. x1 `zshr` 1
67+
x3 = x2 .|. x2 `zshr` 2
68+
x4 = x3 .|. x3 `zshr` 4
69+
x5 = x4 .|. x4 `zshr` 8
70+
x6 = x5 .|. x5 `zshr` 16
71+
in x6 .^. (x6 `zshr` 1)
72+
6073
foreign import dec2bin :: Int -> String
6174
foreign import bin2dec :: String -> Int
6275
foreign import pow :: Fn2 Int Int Int

test/Data/IntMap.purs

+25
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Prelude
99
import qualified Test.Data.IntMap.Internal as Internal
1010
import Test.Unit (Test (), test)
1111
import Test.Unit.Assert as Assert
12+
import Data.Tuple (Tuple(Tuple))
1213

1314
(>|) :: forall a b . a -> (a -> b) -> b
1415
(>|) a f = f a
@@ -49,4 +50,28 @@ tests = do
4950
Assert.equal empty (filter (const false) ex2)
5051
test "filter by key" $
5152
Assert.equal (delete 20 ex2) (filterWithKey (\i _ -> i /= 20) ex2)
53+
testAlter
5254
Internal.tests
55+
56+
testAlter :: Test ()
57+
testAlter = do
58+
test "alter" do
59+
test "adding" do
60+
Assert.equal (singleton 1 10) (alterIns 10 1 empty)
61+
Assert.equal (singleton 1 10) (alterIns 10 1 (singleton 1 10))
62+
Assert.equal (fromAssocArray [Tuple 1 10, Tuple 2 20, Tuple 3 30])
63+
(alterIns 20 2 (fromAssocArray [Tuple 1 10, Tuple 3 30]))
64+
test "deleting" do
65+
Assert.equal empty (alterDel 1 empty)
66+
Assert.equal empty (alterDel 1 (singleton 1 10))
67+
Assert.equal (singleton 2 20) (alterDel 1 (singleton 2 20))
68+
Assert.equal (singleton 2 20) (alterDel 1 (fromAssocArray [Tuple 1 10, Tuple 2 20]))
69+
test "updating" do
70+
Assert.equal empty (alterUpd 1 empty)
71+
Assert.equal (singleton 2 20) (alterUpd 1 (singleton 2 20))
72+
Assert.equal (fromAssocArray [Tuple 1 10, Tuple 2 21, Tuple 3 30])
73+
(alterUpd 2 (fromAssocArray [Tuple 1 10, Tuple 2 20, Tuple 3 30]))
74+
where
75+
alterIns a = alter (maybe (Just a) Just)
76+
alterDel = alter (const Nothing)
77+
alterUpd = alter (map (_ + 1))

test/Data/IntMap/Internal.purs

+11
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ tests = do
2626
Assert.equal "10000" (dec2bin (highestBit (bin2dec "10100") 1))
2727
Assert.equal "10000" (binBranchingBit "01010101" "01000001")
2828
Assert.equal "1000000" (dec2bin $ highestBit (bin2dec "1010101") (bin2dec "00000000001"))
29+
testHighestBitMask
2930

3031
testInversionTrick :: Test ()
3132
testInversionTrick =
@@ -41,3 +42,13 @@ binBranchingBit :: String -> String -> String
4142
binBranchingBit s1 s2 =
4243
case branchingBit (bin2dec s1) (bin2dec s2) of
4344
Mask b -> dec2bin b
45+
46+
testHighestBitMask :: Test ()
47+
testHighestBitMask =
48+
test "highest bit mask" do
49+
eq "000000" "000000"
50+
eq "000001" "000001"
51+
eq "000010" "000011"
52+
eq "010000" "010110"
53+
eq "100000" "100101"
54+
where eq m k = Assert.equal (bin2dec m) (highestBitMask $ bin2dec k)

0 commit comments

Comments
 (0)