Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 58 additions & 5 deletions src/parazoa.nim
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ func toMap*[K, V](arr: openArray[(K, V)]): Map[K, V] =
m = m.add(k, v)
m

proc `[]`[K, V](m: Map[K, V]; key: K): V =
## get key
m.get(key)

func `$`*[K, V](m: Map[K, V]): string =
## Returns a string representing the `Map`
var x = newSeq[string]()
Expand Down Expand Up @@ -404,7 +408,8 @@ type
value: T
Vec*[T] = object
root: VecNode[T]
shift: int
shift: int16
start: int16
size: Natural

func initVec*[T](): Vec[T] =
Expand All @@ -415,14 +420,37 @@ func len*[T](v: Vec[T]): Natural =
## Returns the number of values in the `Vec`
v.size

func del[T](res: var Vec[T], node: VecNode[T], level: int, key: Natural) =
let
index = (key shr level) and mask
child = node.nodes[index]
if child == nil:
discard
else:
case child.kind:
of Branch:
let newChild = copyRef(child)
node.nodes[index] = newChild
del(res, newChild, level + parazoaBits, key)
of Leaf:
node.nodes[index ..< ^2] = node.nodes[index+1 ..< ^1]
node.nodes[^1] = nil
res.size -= 1

func del*[T](m: Vec[T], key: Natural): Vec[T] =
## delete node at index
result = m
result.root = copyRef(m.root)
del(result, result.root, 0, key)

func add[T](res: var Vec[T], node: VecNode[T], level: int, key: Natural, value: T) =
let
index = (key shr level) and mask
child = node.nodes[index]
if child == nil:
if level == 0:
node.nodes[index] = VecNode[T](kind: Leaf, value: value)
res.size += 1
res.size.inc()
else:
let newChild = VecNode[T](kind: Branch)
node.nodes[index] = newChild
Expand All @@ -436,27 +464,32 @@ func add[T](res: var Vec[T], node: VecNode[T], level: int, key: Natural, value:
of Leaf:
newChild.value = value
node.nodes[index] = newChild
res.size.inc()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct. At this point we are reaching a leaf that already exists and updating its value, so the size should not change.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, you inspired me to look into the size stuff and I realized I am actually incrementing too often. Specifically, if you use setLen to increase the size of a vec, and then set a value in one of the new slots, it is incrementing the size when it should not:

7fc8d3a

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setLen isn't used when adding a new item, at least for Vec[T]. In my test case when you added an item to the seq, the length wasn't changing like it should.

Specifically, if you use setLen to increase the size of a vec, and then set a value in one of the new slots, it is incrementing the size when it should not:

To handle both a setLen and adding new items, you'll might want a capacity and a length. That's how seq works I think.

Though, perhaps you could update add to only increment if index == size? Then I think my change would be correct for cases when you're adding at the end, and fix the issue you pointed out.

Or you could re-work add to always use setLen when index >= size.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setLen isn't used when adding a new item

I know; I didn't mean to imply that it was. It was a separate bug that I happened to notice yesterday.

Though, perhaps you could update add to only increment if index == size? Then I think my change would be correct for cases when you're adding at the end, and fix the issue you pointed out.

That is exactly what I did. I linked to the commit in my last comment.


func add*[T](v: Vec[T], key: Natural, value: T): Vec[T] =
## Updates the existing value at `key`
if key < 0 or key > v.len:
raise newException(IndexError, "Index is out of bounds")
let key = key + v.start
var res = v
if key == v.len and key == branchWidth ^ (v.shift + 1):
if key == v.len + v.start and key == branchWidth ^ (v.shift + 1):
res.root = VecNode[T](kind: Branch)
res.shift = v.shift + 1
res.size = v.len
let index = ((res.size-1) shr (res.shift * parazoaBits)) and mask
res.root.nodes[index] = v.root
else:
res.root = copyRef(v.root)
for i in 0 ..< res.start:
res.root.nodes[i] = nil
add(res, res.root, res.shift * parazoaBits, key, value)
res

func add*[T](v: Vec[T], value: T): Vec[T] =
## Adds a new value to the `Vec`
add(v, v.len, value)


func setLen*[T](v: Vec[T], newLen: Natural): Vec[T] =
## Updates the length of `Vec`
var res = v
Expand Down Expand Up @@ -516,7 +549,7 @@ func get*[T](v: Vec[T], key: Natural): T =
## Returns the value at `key`, or raises an exception if out of bounds
if key < 0 or key >= v.len:
raise newException(IndexError, "Index is out of bounds")
get(v.root, v.shift * parazoaBits, key)
get(v.root, v.shift * parazoaBits, key + v.start)

func getOrDefault*[T](v: Vec[T], key: Natural, defaultValue: T): T =
## Returns the value at `key`, or `defaultValue` if not found
Expand All @@ -528,7 +561,7 @@ func getOrDefault*[T](v: Vec[T], key: Natural, defaultValue: T): T =
iterator pairs*[T](v: Vec[T]): (Natural, T) =
## Iterates over the indexes and values in the `Vec`
if v.root != nil:
var stack: seq[tuple[parent: VecNode[T], index: int]] = @[(v.root, 0)]
var stack: seq[tuple[parent: VecNode[T], index: int]] = @[(v.root, v.start.int)]
var key: Natural = 0
while stack.len > 0:
let (parent, index) = stack[stack.len-1]
Expand All @@ -545,6 +578,8 @@ iterator pairs*[T](v: Vec[T]): (Natural, T) =
of Branch:
stack.add((node, 0))
of Leaf:
if key >= v.size:
break
yield (key, node.value)
stack[stack.len-1].index += 1
key += 1
Expand All @@ -554,6 +589,18 @@ iterator items*[T](v: Vec[T]): T =
for (i, v) in v.pairs:
yield v

proc `[]`[T](v: Vec[T]; key: Natural): T =
## get key
v.get(key)

proc `[]`[T; U, V: Ordinal](v: Vec[T]; x: HSlice[U, V]): Vec[T] =
## Returns the value at `key`, or raises an exception if out of bounds
if x.a < 0 or x.a > x.b or x.b > v.size:
raise newException(IndexError, "Index is out of bounds")
result = v
result.start = x.a.int16
result.size = x.b - x.a + 1

func `==`*[T](v1: Vec[T], v2: Vec[T]): bool =
## Returns whether the `Vec`s are equal
if v1.len != v2.len:
Expand All @@ -573,6 +620,12 @@ func toVec*[T](arr: openArray[T]): Vec[T] =
v = v.add(k)
v

func toSeq*[T](v: Vec[T]): seq[T] =
## Returns a `seq` containing the values in `Vec`
result = newSeqOfCap[T](v.len)
for k in v:
result.add(k)

func `$`*[T](v: Vec[T]): string =
## Returns a string representing the `Vec`
var x = newSeq[string]()
Expand Down
46 changes: 46 additions & 0 deletions tests/test1.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ test "maps":
expect(KeyError):
discard m1.get("hello")
check m2.get("hello") == "world"
check m2["hello"] == "world"
check m1.getOrDefault("hello", "") == ""
check m2.getOrDefault("hello", "") == "world"
check m2.contains("hello")
Expand Down Expand Up @@ -68,6 +69,7 @@ test "sets":
check not s1.contains("hello")
check s2.contains("hello")
check s3.contains("goodbye")
check "goodbye" in s3
let s4 = s3.incl("what's")
let s5 = s3.excl("what's").excl("asdf")
check s1.len == 0
Expand Down Expand Up @@ -153,6 +155,50 @@ test "vecs":
check v10.add("hello").get(0) == "hello"
check v10 == v1

test "vecs del":
var v1 = initVec[string]()
v1 = v1.add("one")
v1 = v1.add("two")
v1 = v1.add("three")
v1 = v1.add("four")
check v1.len() == 4

let v2 = v1.del(2)
check v2.toSeq == @["one", "two", "four"]

let v3 = v2.del(0)
check v3.toSeq == @["two", "four"]

check v1.toSeq == @["one", "two", "three", "four"]

test "vecs slice":
var v1 = initVec[string]()
v1 = v1.add("one")
v1 = v1.add("two")
v1 = v1.add("three")
v1 = v1.add("four")
check v1.len() == 4

let s1 = v1[1..2]
check s1.len() == 2
check s1[0] == "two"
check s1[1] == "three"
check s1.toSeq == @["two", "three"]

let s2 = v1[1..1]
check s2.len() == 1
check s2[0] == "two"
check s2.toSeq == @["two"]

let v2 = s1.add("five")
check v2.toSeq == @["two", "three", "five"]

let v3 = v2.add("six")
check v3.toSeq == @["two", "three", "five", "six"]

check v2.toSeq == @["two", "three", "five"]
check s1.toSeq == @["two", "three"]

import hashes

test "partial hash collisions":
Expand Down