6dfc2a9308
- T5.3: HAMT Persistent Map (lib/cljnim_pmap.nim)
O(log32 n) assoc/dissoc/get with structural sharing
16 unit tests, ckMap migrated from seq to HAMT
- T5.4: Persistent Set backed by HAMT map
ckSet with conj/disj/contains?/get, set literal #{}
- Fix: cljContains/cljCount return CljVal (not bool/int)
- Fix: equality in generics via explicit cljEq parameter
- Updated roadmap, task board, CI/Makefile
154 lines
4.9 KiB
Nim
154 lines
4.9 KiB
Nim
import unittest, hashes
|
|
import ../lib/cljnim_pmap
|
|
|
|
proc intEq(a, b: int): bool = a == b
|
|
proc strEq(a, b: string): bool = a == b
|
|
|
|
suite "Persistent Map - Basic":
|
|
test "empty map":
|
|
var m = newPersistentMap[int, int]()
|
|
check m.count == 0
|
|
check pmapGet(m, 42, -1, hash(42), intEq) == -1
|
|
|
|
test "single entry":
|
|
var m = newPersistentMap[int, string]()
|
|
m = pmapAssoc(m, 1, "one", hash(1), intEq)
|
|
check m.count == 1
|
|
check pmapGet(m, 1, "default", hash(1), intEq) == "one"
|
|
check pmapGet(m, 2, "default", hash(2), intEq) == "default"
|
|
|
|
test "few entries":
|
|
var m = newPersistentMap[int, int]()
|
|
for i in 0..<5:
|
|
m = pmapAssoc(m, i, i * 10, hash(i), intEq)
|
|
check m.count == 5
|
|
check pmapGet(m, 0, -1, hash(0), intEq) == 0
|
|
check pmapGet(m, 4, -1, hash(4), intEq) == 40
|
|
check pmapContains(m, 3, hash(3), intEq) == true
|
|
check pmapContains(m, 99, hash(99), intEq) == false
|
|
|
|
suite "Persistent Map - assoc":
|
|
test "assoc overwrites existing":
|
|
var m = newPersistentMap[int, string]()
|
|
m = pmapAssoc(m, 1, "one", hash(1), intEq)
|
|
m = pmapAssoc(m, 1, "uno", hash(1), intEq)
|
|
check pmapGet(m, 1, "", hash(1), intEq) == "uno"
|
|
check m.count == 1
|
|
|
|
test "assoc preserves old map":
|
|
var m1 = newPersistentMap[int, int]()
|
|
m1 = pmapAssoc(m1, 1, 10, hash(1), intEq)
|
|
var m2 = pmapAssoc(m1, 2, 20, hash(2), intEq)
|
|
check m1.count == 1
|
|
check m2.count == 2
|
|
check pmapGet(m1, 2, -1, hash(2), intEq) == -1
|
|
check pmapGet(m2, 1, -1, hash(1), intEq) == 10
|
|
|
|
test "assoc many entries (100)":
|
|
var m = newPersistentMap[int, int]()
|
|
for i in 0..<100:
|
|
m = pmapAssoc(m, i, i * i, hash(i), intEq)
|
|
check m.count == 100
|
|
check pmapGet(m, 0, -1, hash(0), intEq) == 0
|
|
check pmapGet(m, 50, -1, hash(50), intEq) == 2500
|
|
check pmapGet(m, 99, -1, hash(99), intEq) == 9801
|
|
for i in 0..<100:
|
|
check pmapGet(m, i, -1, hash(i), intEq) == i * i
|
|
|
|
test "assoc many entries (500)":
|
|
var m = newPersistentMap[int, int]()
|
|
for i in 0..<500:
|
|
m = pmapAssoc(m, i, i * 2, hash(i), intEq)
|
|
check m.count == 500
|
|
check pmapGet(m, 0, -1, hash(0), intEq) == 0
|
|
check pmapGet(m, 250, -1, hash(250), intEq) == 500
|
|
check pmapGet(m, 499, -1, hash(499), intEq) == 998
|
|
|
|
suite "Persistent Map - dissoc":
|
|
test "dissoc existing key":
|
|
var m = newPersistentMap[int, string]()
|
|
m = pmapAssoc(m, 1, "one", hash(1), intEq)
|
|
m = pmapAssoc(m, 2, "two", hash(2), intEq)
|
|
m = pmapAssoc(m, 3, "three", hash(3), intEq)
|
|
m = pmapDissoc(m, 2, hash(2), intEq)
|
|
check m.count == 2
|
|
check pmapContains(m, 1, hash(1), intEq) == true
|
|
check pmapContains(m, 2, hash(2), intEq) == false
|
|
check pmapContains(m, 3, hash(3), intEq) == true
|
|
|
|
test "dissoc non-existing key":
|
|
var m = newPersistentMap[int, int]()
|
|
m = pmapAssoc(m, 1, 10, hash(1), intEq)
|
|
m = pmapDissoc(m, 99, hash(99), intEq)
|
|
check m.count == 1
|
|
check pmapContains(m, 1, hash(1), intEq) == true
|
|
|
|
test "dissoc until empty":
|
|
var m = newPersistentMap[int, int]()
|
|
m = pmapAssoc(m, 1, 10, hash(1), intEq)
|
|
m = pmapDissoc(m, 1, hash(1), intEq)
|
|
check m.count == 0
|
|
check m.root.isNil
|
|
|
|
suite "Persistent Map - keys/values":
|
|
test "keys":
|
|
var m = newPersistentMap[int, string]()
|
|
m = pmapAssoc(m, 1, "one", hash(1), intEq)
|
|
m = pmapAssoc(m, 2, "two", hash(2), intEq)
|
|
let k = pmapKeys(m)
|
|
check k.len == 2
|
|
check 1 in k
|
|
check 2 in k
|
|
|
|
test "vals":
|
|
var m = newPersistentMap[int, string]()
|
|
m = pmapAssoc(m, 1, "one", hash(1), intEq)
|
|
m = pmapAssoc(m, 2, "two", hash(2), intEq)
|
|
let v = pmapVals(m)
|
|
check v.len == 2
|
|
check "one" in v
|
|
check "two" in v
|
|
|
|
test "entries":
|
|
var m = newPersistentMap[int, string]()
|
|
m = pmapAssoc(m, 1, "one", hash(1), intEq)
|
|
m = pmapAssoc(m, 2, "two", hash(2), intEq)
|
|
let e = pmapEntries(m)
|
|
check e.len == 2
|
|
|
|
suite "Persistent Map - items iterator":
|
|
test "iterate empty":
|
|
var m = newPersistentMap[int, int]()
|
|
var count = 0
|
|
for (k, v) in pmapItems(m):
|
|
count += 1
|
|
check count == 0
|
|
|
|
test "iterate non-empty":
|
|
var m = newPersistentMap[int, int]()
|
|
for i in 0..<10:
|
|
m = pmapAssoc(m, i, i * 10, hash(i), intEq)
|
|
var count = 0
|
|
var sum = 0
|
|
for (k, v) in pmapItems(m):
|
|
count += 1
|
|
sum += v
|
|
check count == 10
|
|
check sum == 450
|
|
|
|
suite "Persistent Map - string keys":
|
|
test "string keys":
|
|
var m = newPersistentMap[string, int]()
|
|
m = pmapAssoc(m, "hello", 1, hash("hello"), strEq)
|
|
m = pmapAssoc(m, "world", 2, hash("world"), strEq)
|
|
check pmapGet(m, "hello", -1, hash("hello"), strEq) == 1
|
|
check pmapGet(m, "world", -1, hash("world"), strEq) == 2
|
|
check pmapGet(m, "missing", -1, hash("missing"), strEq) == -1
|
|
|
|
suite "Persistent Map - build from pairs":
|
|
test "pmapBuild":
|
|
var pairs: seq[(int, string)] = @[(1, "one"), (2, "two"), (3, "three")]
|
|
var m = pmapBuild(pairs, intEq)
|
|
check m.count == 3
|
|
check pmapGet(m, 2, "", hash(2), intEq) == "two"
|