Phase 5: HAMT Persistent Map + Persistent Set, 86 tests pass
- 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
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
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"
|
||||
Reference in New Issue
Block a user