diff --git a/packages/collections/persistent/_test.pony b/packages/collections/persistent/_test.pony index 535fc903c1..6abbd64dc7 100644 --- a/packages/collections/persistent/_test.pony +++ b/packages/collections/persistent/_test.pony @@ -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() @@ -55,7 +57,7 @@ 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]) @@ -63,7 +65,7 @@ class iso _TestListFrom is UnitTest 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]) @@ -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() @@ -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]) @@ -115,14 +117,14 @@ 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]) } @@ -130,7 +132,7 @@ class iso _TestListFlatMap is UnitTest 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 } @@ -138,7 +140,7 @@ class iso _TestListFilter is UnitTest 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 } @@ -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 } @@ -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 } @@ -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"]) @@ -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 } @@ -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"]) @@ -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 } @@ -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] @@ -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 @@ -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) diff --git a/packages/collections/persistent/_vec_node.pony b/packages/collections/persistent/_vec_node.pony new file mode 100644 index 0000000000..11fefe6f1b --- /dev/null +++ b/packages/collections/persistent/_vec_node.pony @@ -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 diff --git a/packages/collections/persistent/vec.pony b/packages/collections/persistent/vec.pony new file mode 100644 index 0000000000..460c895f4f --- /dev/null +++ b/packages/collections/persistent/vec.pony @@ -0,0 +1,292 @@ +use mut = "collections" + +class val Vec[A: Any #share] + """ + A persistent vector based on the Hash Array Mapped Trie from 'Ideal Hash + Trees' by Phil Bagwell. + """ + + let _root: (_VecNode[A] | None) + let _tail: Array[A] val + let _size: USize + let _depth: U8 + let _tail_offset: USize + + new val create() => + _root = None + _tail = recover Array[A](32) end + _size = 0 + _depth = 1 + _tail_offset = 0 + + new val _create( + root': (_VecNode[A] | None), + tail': Array[A] val, + size': USize, + depth': U8, + tail_offset': USize) + => + _root = root' + _tail = tail' + _size = size' + _depth = depth' + _tail_offset = tail_offset' + + fun size(): USize => + """ + Return the amount of values in the vector. + """ + _size + + fun apply(i: USize): val->A ? => + """ + Get the i-th element, raising an error if the index is out of bounds. + """ + if i < _tail_offset then + (_root as _VecNode[A])(i) + else + _tail(i - _tail_offset) + end + + fun val update(i: USize, value: val->A): Vec[A] ? => + """ + Return a vector with the i-th element changed, raising an error if the + index is out of bounds. + """ + if i < _tail_offset then + let root = (_root as _VecNode[A]).update(i, value) + _create(root, _tail, _size, _depth, _tail_offset) + else + let tail = + recover val _tail.clone() .> update(i - _tail_offset, value) end + _create(_root, tail, _size, _depth, _tail_offset) + end + + fun val insert(i: USize, value: val->A): Vec[A] ? => + """ + Return a vector with an element inserted. Elements after this are moved + up by one index, extending the vector. An out of bounds index raises an + error. + """ + if i >= _size then error end + var vec = this + var prev = value + for idx in mut.Range(i, _size) do + vec = vec.update(idx, prev = this(idx)) + end + vec.push(this(_size - 1)) + + fun val delete(i: USize): Vec[A] ? => + """ + Return a vector with an element deleted. Elements after this are moved + down by one index, compacting the vector. An out of bounds index raises an + error. + """ + if i >= _size then error end + var vec = pop() + for idx in mut.Range(i + 1, _size) do + vec = vec.update(idx - 1, this(idx)) + end + vec + + fun val remove(i: USize, n: USize): Vec[A] ? => + """ + Return a vector with n elements removed, beginning at index i. + """ + if i >= _size then error end + var vec = this + for _ in mut.Range(0, n) do vec = vec.pop() end + for idx in mut.Range(i, _size - n) do + vec = vec.update(idx, this(idx + n)) + end + vec + + fun val push(value: val->A): Vec[A] => + """ + Return a vector with the value added to the end. + """ + if _tail.size() < 32 then + // push value into tail + let tail = recover val _tail.clone() .> push(value) end + _create(_root, tail, _size + 1, _depth, _tail_offset) + elseif _tail_offset == _pow32(_depth.usize_unsafe()) then + // create new root + // push tail into root + // push value into new tail + let root = + try (_root as _VecNode[A]).new_root().push(_tail_offset, _tail) + else _root + end + let tail = recover val Array[A](32) .> push(value) end + _create(root, tail, _size + 1, _depth + 1, _tail_offset + 32) + else + // push tail into root + // push value into new tail + let root = + match _root + | let r: _VecNode[A] => try r.push(_tail_offset, _tail) else r end + | None => + let r = _VecNode[A].empty(1) + try r.push(0, _tail) else r end + end + let tail = recover val Array[A](32) .> push(value) end + _create(root, tail, _size + 1, _depth, _tail_offset + 32) + end + + fun val pop(): Vec[A] ? => + """ + Return a vector with the value at the end removed. + """ + if (_tail.size() > 1) or (_size == 1) then + let tail = recover val _tail.clone() .> pop() end + _create(_root, tail, _size - 1, _depth, _tail_offset) + else + let tail_offset = _tail_offset - 32 + (let root, let tail) = (_root as _VecNode[A]).pop(tail_offset) + _create(root, tail, _size - 1, _depth, tail_offset) + end + + fun val concat(iter: Iterator[val->A]): Vec[A] => + """ + Return a vector with the values of the given iterator added to the end. + """ + var v = this + for a in iter do + v = v.push(a) + end + v + + fun val find( + value: val->A, + offset: USize = 0, + nth: USize = 0, + predicate: {(A, A): Bool} val = {(l: A, r: A): Bool => l is r }) + : USize ? + => + """ + Find the `nth` appearance of `value` from the beginning of the vector, + starting at `offset` and examining higher indices, and using the + supplied `predicate` for comparisons. Returns the index of the value, or + raise an error if the value isn't present. + + By default, the search starts at the first element of the vector, + returns the first instance of `value` found, and uses object identity + for comparison. + """ + var n: USize = 0 + for i in mut.Range(offset, _size) do + if predicate(this(i), value) then + if n == nth then return i end + n = n + 1 + end + end + error + + fun val contains( + value: val->A, + predicate: {(A, A): Bool} val = {(l: A, r: A): Bool => l is r }) + : Bool + => + """ + Returns true if the vector contains `value`, false otherwise. + """ + for v in values() do + if predicate(v, value) then return true end + end + false + + fun val slice(from: USize = 0, to: USize = -1, step: USize = 1): Vec[A] => + """ + Return a vector that is a clone of a portion of this vector. The range is + exclusive and saturated. + """ + var vec = Vec[A] + for i in mut.Range(0, if _size < to then _size else to end, step) do + try vec.push(this(i)) end + end + vec + + fun val reverse(): Vec[A] => + """ + Return a vector with the elements in reverse order. + """ + var vec = Vec[A] + for i in mut.Reverse(_size - 1, 0) do + try vec = vec.push(this(i)) end + end + vec + + fun val keys(): VecKeys[A]^ => + """ + Return an iterator over the indices in the vector. + """ + VecKeys[A](this) + + fun val values(): VecValues[A]^ => + """ + Return an iterator over the values in the vector. + """ + VecValues[A](this) + + fun val pairs(): VecPairs[A]^ => + """ + Return an iterator over the (index, value) pairs in the vector. + """ + VecPairs[A](this) + + fun _pow32(n: USize): USize => + """ + Raise 32 to the power of n. + """ + if n == 0 then + 1 + else + 32 << ((n - 1) * 5) + end + + fun _leaf_nodes(): Array[Array[A] val]^ => + let lns = Array[Array[A] val](_size / 32) + match _root + | let vn: _VecNode[A] => vn.leaf_nodes(lns) + end + if _tail.size() > 0 then lns.push(_tail) end + lns + +class VecKeys[A: Any #share] + embed _pairs: VecPairs[A] + + new create(v: Vec[A]) => _pairs = VecPairs[A](v) + + fun has_next(): Bool => _pairs.has_next() + + fun ref next(): USize ? => _pairs.next()._1 + +class VecValues[A: Any #share] + embed _pairs: VecPairs[A] + + new create(v: Vec[A]) => _pairs = VecPairs[A](v) + + fun has_next(): Bool => _pairs.has_next() + + fun ref next(): val->A ? => _pairs.next()._2 + +class VecPairs[A: Any #share] + let _leaf_nodes: Array[Array[A] val] + var _idx: USize = 0 + var _i: USize = 0 + + new create(v: Vec[A]) => + _leaf_nodes = v._leaf_nodes() + + fun has_next(): Bool => + _leaf_nodes.size() > 0 + + fun ref next(): (USize, A) ? => + var leaves = _leaf_nodes(0) + let v = leaves(_idx = _idx + 1) + + if _idx == leaves.size() then + _leaf_nodes.shift() + _idx = 0 + end + (_i = _i + 1, v)