fix Map and Set behaviour for negative hash values

fixes #335

- add new Int.positive-mod function
- use it for Map and Set operations
- add tests
This commit is contained in:
Joel Kaasinen 2018-12-01 11:05:11 +02:00
parent b42e506fc3
commit cfb61847cb
4 changed files with 44 additions and 8 deletions

View File

@ -53,6 +53,13 @@
(set! y (/ y 2))
(set! x (* x x))))
r))
(doc positive-mod "Like mod but always returns a positive answer.")
(defn positive-mod [k n]
(let [r (Int.mod k n)]
(if (> 0 r)
(+ r n)
r)))
)
(defmodule IntRef

View File

@ -101,14 +101,14 @@
(doc put "Put a a value v into map m, using the key k.")
(defn put [m k v]
(let [idx (Int.mod (hash k) @(n-buckets &m))]
(let [idx (Int.positive-mod (hash k) @(n-buckets &m))]
(update-buckets m &(fn [b]
(let [n (Array.nth &b idx)]
(Array.aset b idx (Bucket.grow n (Pair.init @k @v))))))))
(doc get "Get the value for the key k from map m. If it isnt found, a zero element for the value type is returned.")
(defn get [m k]
(let [idx (Int.mod (hash k) @(n-buckets m))]
(let [idx (Int.positive-mod (hash k) @(n-buckets m))]
(Bucket.get (Array.nth (buckets m) idx) k)))
(doc length "Get the length of the map m.")
@ -124,12 +124,12 @@
(doc contains? "Check whether the map m contains the key k.")
(defn contains? [m k]
(let [idx (Int.mod (hash k) @(n-buckets m))]
(let [idx (Int.positive-mod (hash k) @(n-buckets m))]
(Bucket.contains? (Array.nth (buckets m) idx) k)))
(doc remove "Remove the value under the key k from the map m.")
(defn remove [m k]
(let [idx (Int.mod (hash k) @(n-buckets &m))]
(let [idx (Int.positive-mod (hash k) @(n-buckets &m))]
(update-buckets m &(fn [b]
(let [n (Array.nth &b idx)]
(Array.aset b idx (Bucket.shrink n k)))))))
@ -216,7 +216,7 @@
(doc put "Put a a key k into the set s.")
(defn put [s k]
(let [idx (Int.mod (hash k) @(n-buckets &s))]
(let [idx (Int.positive-mod (hash k) @(n-buckets &s))]
(update-buckets s &(fn [b]
(let [n (Array.nth &b idx)]
(Array.aset b idx (SetBucket.grow n @k)))))))
@ -234,12 +234,12 @@
(doc contains? "Check whether the set s contains the key k.")
(defn contains? [s k]
(let [idx (Int.mod (hash k) @(n-buckets s))]
(let [idx (Int.positive-mod (hash k) @(n-buckets s))]
(SetBucket.contains? (Array.nth (buckets s) idx) k)))
(doc remove "Remove the key k from the set s.")
(defn remove [s k]
(let [idx (Int.mod (hash k) @(n-buckets &s))]
(let [idx (Int.positive-mod (hash k) @(n-buckets &s))]
(update-buckets s &(fn [b]
(let [n (Array.nth &b idx)]
(Array.aset b idx (SetBucket.shrink n k)))))))

View File

@ -58,4 +58,13 @@
(assert-equal test
1
(/ 3 2)
"integer division truncates as expected"))
"integer division truncates as expected")
(assert-equal test
3
(positive-mod -7 5)
"positive-mod works on negative inputs")
(assert-equal test
0
(positive-mod 0 7)
"positive-mod works on zero")
)

View File

@ -9,6 +9,11 @@
&(Map.get &(Map.put (Map.create) "1" "2") "1")
"basic put and get works"
)
(assert-equal test
"2"
&(Map.get &(Map.put (Map.create) &-7 "2") &-7)
"basic put and get works with negative keys"
)
(assert-equal test
1
(Map.length &(Map.put (Map.create) "1" "2"))
@ -29,6 +34,16 @@
(Map.contains? &(Map.put (Map.create) "1" "2") "1")
"contains? works"
)
(assert-equal test
true
(Map.contains? &(Map.put (Map.create) &-7 "2") &-7)
"contains? works with negative keys"
)
(assert-equal test
false
(Map.contains? &(Map.put (Map.create) &1 "2") &-7)
"contains? works with negative keys"
)
(assert-equal test
true
(Map.empty? &(the (Map Int Int) (Map.create)))
@ -80,6 +95,11 @@
(Set.contains? &(Set.put (Set.create) "1") "1")
"contains? works"
)
(assert-equal test
true
(Set.contains? &(Set.put (Set.create) &-7) &-7)
"contains? works with negative keys"
)
(assert-equal test
true
(Set.empty? &(the (Set Int) (Set.create)))