Skip to content

Commit

Permalink
Add persistent Vec (#1949)
Browse files Browse the repository at this point in the history
  • Loading branch information
Theodus authored and jemc committed Jun 7, 2017
1 parent 4638e7a commit 9ddb721
Show file tree
Hide file tree
Showing 3 changed files with 476 additions and 17 deletions.
119 changes: 102 additions & 17 deletions packages/collections/persistent/_test.pony
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ actor Main is TestList
test(_TestMap)
test(_TestMapVsMap)
test(_TestSet)
test(_TestVec)
test(_TestVecIterators)

class iso _TestListPrepend is UnitTest
fun name(): String => "collections/persistent/List/prepend()"
fun name(): String => "collections/persistent/List (prepend)"

fun apply(h: TestHelper) ? =>
let a = Lists[U32].empty()
Expand All @@ -55,15 +57,15 @@ class iso _TestListPrepend is UnitTest
h.assert_eq[USize](e.tail().size(), 0)

class iso _TestListFrom is UnitTest
fun name(): String => "collections/persistent/Lists/from()"
fun name(): String => "collections/persistent/Lists (from)"

fun apply(h: TestHelper) ? =>
let l1 = Lists[U32]([1; 2; 3])
h.assert_eq[USize](l1.size(), 3)
h.assert_eq[U32](l1.head(), 1)

class iso _TestListApply is UnitTest
fun name(): String => "collections/persistent/List/apply()"
fun name(): String => "collections/persistent/List (apply)"

fun apply(h: TestHelper) ? =>
let l1 = Lists[U32]([1; 2; 3])
Expand All @@ -77,7 +79,7 @@ class iso _TestListApply is UnitTest
h.assert_error({()? => l2(0) })

class iso _TestListValues is UnitTest
fun name(): String => "collections/persistent/List/values()"
fun name(): String => "collections/persistent/List (values)"

fun apply(h: TestHelper) ? =>
let iter = Lists[U32]([1; 2; 3]).values()
Expand All @@ -93,7 +95,7 @@ class iso _TestListValues is UnitTest
h.assert_false(try iter.next(); true else false end)

class iso _TestListConcat is UnitTest
fun name(): String => "collections/persistent/List/concat()"
fun name(): String => "collections/persistent/List (concat)"

fun apply(h: TestHelper) ? =>
let l1 = Lists[U32]([1; 2; 3])
Expand All @@ -115,30 +117,30 @@ class iso _TestListConcat is UnitTest
h.assert_true(Lists[U32].eq(l9, Lists[U32]([1])))

class iso _TestListMap is UnitTest
fun name(): String => "collections/persistent/Lists/map()"
fun name(): String => "collections/persistent/Lists (map)"

fun apply(h: TestHelper) ? =>
let l5 = Lists[U32]([1; 2; 3]).map[U32]({(x: U32): U32 => x * 2 })
h.assert_true(Lists[U32].eq(l5, Lists[U32]([2; 4; 6])))

class iso _TestListFlatMap is UnitTest
fun name(): String => "collections/persistent/Lists/flat_map()"
fun name(): String => "collections/persistent/Lists (flat_map)"

fun apply(h: TestHelper) ? =>
let f = {(x: U32): List[U32] => Lists[U32]([x - 1; x; x + 1]) }
let l6 = Lists[U32]([2; 5; 8]).flat_map[U32](f)
h.assert_true(Lists[U32].eq(l6, Lists[U32]([1; 2; 3; 4; 5; 6; 7; 8; 9])))

class iso _TestListFilter is UnitTest
fun name(): String => "collections/persistent/Lists/filter()"
fun name(): String => "collections/persistent/Lists (filter)"

fun apply(h: TestHelper) ? =>
let is_even = {(x: U32): Bool => (x % 2) == 0 }
let l7 = Lists[U32]([1; 2; 3; 4; 5; 6; 7; 8]).filter(is_even)
h.assert_true(Lists[U32].eq(l7, Lists[U32]([2; 4; 6; 8])))

class iso _TestListFold is UnitTest
fun name(): String => "collections/persistent/Lists/fold()"
fun name(): String => "collections/persistent/Lists (fold)"

fun apply(h: TestHelper) ? =>
let add = {(acc: U32, x: U32): U32 => acc + x }
Expand All @@ -153,7 +155,7 @@ class iso _TestListFold is UnitTest
h.assert_true(Lists[U32].eq(l8, Lists[U32]([6; 4; 2])))

class iso _TestListEveryExists is UnitTest
fun name(): String => "collections/persistent/Lists/every()exists()"
fun name(): String => "collections/persistent/Lists (every, exists)"

fun apply(h: TestHelper) =>
let is_even = {(x: U32): Bool => (x % 2) == 0 }
Expand All @@ -174,7 +176,7 @@ class iso _TestListEveryExists is UnitTest
h.assert_eq[Bool](l13.exists(is_even), false)

class iso _TestListPartition is UnitTest
fun name(): String => "collections/persistent/Lists/partition()"
fun name(): String => "collections/persistent/Lists (partition)"

fun apply(h: TestHelper) ? =>
let is_even = {(x: U32): Bool => (x % 2) == 0 }
Expand All @@ -184,7 +186,7 @@ class iso _TestListPartition is UnitTest
h.assert_true(Lists[U32].eq(misses, Lists[U32]([1; 3; 5])))

class iso _TestListDrop is UnitTest
fun name(): String => "collections/persistent/List/drop()"
fun name(): String => "collections/persistent/List (drop)"

fun apply(h: TestHelper) ? =>
let l = Lists[String](["a"; "b"; "c"; "d"; "e"])
Expand All @@ -195,7 +197,7 @@ class iso _TestListDrop is UnitTest
h.assert_true(Lists[String].eq(empty.drop(3), Lists[String].empty()))

class iso _TestListDropWhile is UnitTest
fun name(): String => "collections/persistent/List/drop_while()"
fun name(): String => "collections/persistent/List (drop_while)"

fun apply(h: TestHelper) ? =>
let is_even = {(x: U32): Bool => (x % 2) == 0 }
Expand All @@ -206,7 +208,7 @@ class iso _TestListDropWhile is UnitTest
h.assert_true(Lists[U32].eq(empty.drop_while(is_even), Lists[U32].empty()))

class iso _TestListTake is UnitTest
fun name(): String => "collections/persistent/List/take()"
fun name(): String => "collections/persistent/List (take)"

fun apply(h: TestHelper) ? =>
let l = Lists[String](["a"; "b"; "c"; "d"; "e"])
Expand All @@ -217,7 +219,7 @@ class iso _TestListTake is UnitTest
h.assert_true(Lists[String].eq(empty.take(3), Lists[String].empty()))

class iso _TestListTakeWhile is UnitTest
fun name(): String => "collections/persistent/List/take_while()"
fun name(): String => "collections/persistent/List (take_while)"

fun apply(h: TestHelper) ? =>
let is_even = {(x: U32): Bool => (x % 2) == 0 }
Expand All @@ -227,7 +229,7 @@ class iso _TestListTakeWhile is UnitTest
h.assert_true(Lists[U32].eq(empty.take_while(is_even), Lists[U32].empty()))

class iso _TestMap is UnitTest
fun name(): String => "collections/persistent/Map"
fun name(): String => "collections/persistent/Map (update, remove, concat)"

fun apply(h: TestHelper) ? =>
let m1 = Map[String,U32]
Expand Down Expand Up @@ -268,7 +270,7 @@ class iso _TestMap is UnitTest
h.assert_error({()? => m10("d") })

class iso _TestMapVsMap is UnitTest
fun name(): String => "collections/persistent/mapvsmap"
fun name(): String => "collections/persistent/Map (persistent vs mutable)"

fun apply(h: TestHelper) ? =>
let keys: USize = 300
Expand Down Expand Up @@ -325,3 +327,86 @@ class iso _TestSet is UnitTest
h.assert_true((b xor a) == (Set[USize] + 1 + 4))
h.assert_true(a.without(b) == (Set[USize] + 1))
h.assert_true(b.without(a) == (Set[USize] + 4))

class iso _TestVec is UnitTest
fun name(): String => "collections/persistent/Vec"

fun apply(h: TestHelper) ? =>
var v = Vec[USize]
let n: USize = 33_000 // resize up to 4 levels in depth

// push
for i in mut.Range(0, n) do
v = v.push(i)
h.assert_eq[USize](v(i), i)
end

// update
for i in mut.Range(0, n) do
v = v.update(i, -i)
h.assert_eq[USize](v(i), -i)
end

var idx: USize = 0
for num in v.values() do
h.assert_eq[USize](num, -idx)
idx = idx + 1
end
h.assert_eq[USize](v.size(), idx)

// pop
for i in mut.Range(0, n) do
v = v.pop()
h.assert_error({() ? => v(n - i) })
h.assert_eq[USize](v.size(), n - i - 1)
end

// concat
v = Vec[USize].concat(mut.Range(0, n))
for i in mut.Range(0, n) do
h.assert_eq[USize](v(i), i)
end

// insert
let insert_idx: USize = 14
v = v.insert(insert_idx, 9999)
h.assert_eq[USize](v(insert_idx - 1), insert_idx - 1)
h.assert_eq[USize](v(insert_idx), 9999)
h.assert_eq[USize](v(insert_idx + 1), insert_idx)
h.assert_eq[USize](v.size(), n + 1)
h.assert_error({() ? => v.insert(v.size(), 0) })
h.assert_error({() ? => v.insert(-1, 0) })

// delete
v = v.delete(insert_idx)
h.assert_eq[USize](v(insert_idx - 1), insert_idx - 1)
h.assert_eq[USize](v(insert_idx), insert_idx)
h.assert_eq[USize](v.size(), n)
h.assert_error({() ? => v.delete(v.size()) })
h.assert_error({() ? => v.delete(-1) })

// remove
v = v.remove(0, 1)
h.assert_eq[USize](v(0), 1)
h.assert_eq[USize](v(1), 2)
h.assert_eq[USize](v.size(), n - 1)

v = v.remove(10, 10)
h.assert_eq[USize](v(9), 10)
h.assert_eq[USize](v(10), 21)
h.assert_eq[USize](v(n - 12), n - 1)
h.assert_eq[USize](v.size(), n - 11)

class iso _TestVecIterators is UnitTest
fun name(): String => "collections/persistent/Vec (iterators)"

fun apply(h: TestHelper) ? =>
let n: USize = 33_000 // resize up to 4 levels in depth
var vec = Vec[USize]
for i in mut.Range(0, n) do vec = vec.push(i) end
var c = vec.size()
for (i, v) in vec.pairs() do
c = c - 1
h.assert_eq[USize](v, vec(i))
end
h.assert_eq[USize](c, 0)
82 changes: 82 additions & 0 deletions packages/collections/persistent/_vec_node.pony
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
type _VecLeafNodes[A: Any #share] is Array[Array[A] val] val
type _VecNodes[A: Any #share] is Array[_VecNode[A]] val

type _VecEntries[A: Any #share] is (_VecNodes[A] | _VecLeafNodes[A])

class val _VecNode[A: Any #share]
let _entries: _VecEntries[A]
let _level: U8

new val empty(level: U8) =>
_entries =
if level == 1 then recover Array[Array[A] val](32) end
else recover Array[_VecNode[A]](32) end
end
_level = level

new val create(entries: _VecEntries[A], level: U8) =>
(_entries, _level) = (consume entries, level)

fun val new_root(): _VecNode[A] =>
let entries = recover val Array[_VecNode[A]](32) .> push(this) end
create(entries, _level + 1)

fun apply(i: USize): A ? =>
let idx = _Bits.mask(i.u32_unsafe(), _level).usize_unsafe()
match _entries
| let sns: _VecNodes[A] => sns(idx)(i)
| let lns: _VecLeafNodes[A] =>
lns(idx)(_Bits.mask(i.u32_unsafe(), _level - 1).usize_unsafe())
end

fun val update(i: USize, value: A): _VecNode[A] ? =>
let idx = _Bits.mask(i.u32_unsafe(), _level).usize_unsafe()
match _entries
| let sns: _VecNodes[A] =>
let sn = sns(idx).update(i, value)
let entries = recover val sns.clone() .> update(idx, sn) end
create(entries, _level)
| let lns: _VecLeafNodes[A] =>
let li = _Bits.mask(i.u32_unsafe(), _level - 1).usize_unsafe()
let ln = recover val lns(idx).clone() .> update(li, value) end
let entries = recover val lns.clone() .> update(idx, ln) end
create(entries, _level)
end

fun val push(i: USize, tail: Array[A] val): _VecNode[A] ? =>
// this method must never be called before a node resize (if required)
match _entries
| let sns: _VecNodes[A] =>
let idx = _Bits.mask(i.u32_unsafe(), _level).usize_unsafe()
if _entries.size() > idx then
let sn = (_entries as _VecNodes[A])(idx).push(i, tail)
let entries = recover val sns.clone() .> update(idx, sn) end
create(entries, _level)
else
let sn = empty(_level - 1).push(i, tail)
let entries = recover val sns.clone() .> push(sn) end
create(entries, _level)
end
| let lns: _VecLeafNodes[A] =>
let entries = recover val lns.clone() .> push(tail) end
create(entries, _level)
end

fun val pop(i: USize): (_VecNode[A], Array[A] val) ? =>
let idx = _Bits.mask(i.u32_unsafe(), _level).usize_unsafe()
match _entries
| let sns: _VecNodes[A] =>
(let sn', let tail) = sns(idx).pop(i)
let entries = recover val sns.clone() .> update(idx, sn') end
(create(entries, _level), tail)
| let lns: _VecLeafNodes[A] => (this, lns(idx))
end

fun val leaf_nodes(lns: Array[Array[A] val]): Array[Array[A] val]^ =>
match _entries
| let sns: _VecNodes[A] =>
for sn in sns.values() do sn.leaf_nodes(lns) end
| let lns': _VecLeafNodes[A] =>
lns.append(lns')
end
lns
Loading

0 comments on commit 9ddb721

Please sign in to comment.