From e6caf9ebaa73242136fac74691d83207ecce795f Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Mon, 20 May 2024 15:25:57 +0800 Subject: [PATCH 1/4] rename array and vector --- array/array.mbt | 915 +------------------- array/array.mbti | 68 +- array/array_test.mbt | 459 +++++++++- array/moon.pkg.json | 6 +- array/sort.mbt | 304 +------ array/sort_by.mbt | 84 +- {vec => array}/sort_by_test.mbt | 8 +- {vec => array}/sort_test.mbt | 26 +- array/view.mbt | 21 + {vec => array}/view_test.mbt | 2 +- builtin/array.mbt | 997 +++++++++++++++++++++- vec/view.mbt => builtin/arrayview.mbt | 31 +- builtin/debug.mbt | 11 + vec/types.mbt => builtin/fixedarray.mbt | 22 +- builtin/intrinsics.mbt | 14 +- devec/types.mbt | 2 +- {array => fixedarray}/blit.mbt | 18 +- fixedarray/fixedarray.mbt | 916 ++++++++++++++++++++ fixedarray/fixedarray.mbti | 48 ++ fixedarray/fixedarray_test.mbt | 41 + {vec => fixedarray}/moon.pkg.json | 6 +- {array => fixedarray}/slice.mbt | 16 +- fixedarray/sort.mbt | 506 ++++++++++++ {vec => fixedarray}/sort_by.mbt | 88 +- {vec => fixedarray}/utils.mbt | 0 hashmap/hashmap.mbt | 6 +- hashset/hashset.mbt | 6 +- immutable_set/immutable_set.mbt | 10 +- immutable_set/immutable_set_test.mbt | 7 - immutable_set/moon.pkg.json | 2 +- immutable_vec/immutable_vec.mbt | 28 +- immutable_vec/moon.pkg.json | 6 +- json/json.mbt | 52 +- json/moon.pkg.json | 4 +- json/types.mbt | 2 +- linked_hashmap/impl.mbt | 6 +- map/map.mbt | 12 +- map/map_test.mbt | 8 - map/moon.pkg.json | 2 +- mutable_set/moon.pkg.json | 2 +- mutable_set/mutable_set.mbt | 31 +- vec/sort.mbt | 232 ------ vec/vec.mbt | 1010 ----------------------- vec/vec.mbti | 88 -- vec/vec_test.mbt | 467 ----------- 45 files changed, 3302 insertions(+), 3288 deletions(-) rename {vec => array}/sort_by_test.mbt (87%) rename {vec => array}/sort_test.mbt (71%) create mode 100644 array/view.mbt rename {vec => array}/view_test.mbt (96%) rename vec/view.mbt => builtin/arrayview.mbt (72%) rename vec/types.mbt => builtin/fixedarray.mbt (66%) rename {array => fixedarray}/blit.mbt (83%) create mode 100644 fixedarray/fixedarray.mbt create mode 100644 fixedarray/fixedarray.mbti create mode 100644 fixedarray/fixedarray_test.mbt rename {vec => fixedarray}/moon.pkg.json (60%) rename {array => fixedarray}/slice.mbt (69%) create mode 100644 fixedarray/sort.mbt rename {vec => fixedarray}/sort_by.mbt (75%) rename {vec => fixedarray}/utils.mbt (100%) delete mode 100644 vec/sort.mbt delete mode 100644 vec/vec.mbt delete mode 100644 vec/vec.mbti delete mode 100644 vec/vec_test.mbt diff --git a/array/array.mbt b/array/array.mbt index cc56b2de..5dbed7cb 100644 --- a/array/array.mbt +++ b/array/array.mbt @@ -12,288 +12,62 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Iterates over each element. -/// -/// # Arguments -/// -/// - `self`: The array to iterate over. -/// - `f`: The function to apply to each element. -/// -/// # Example -/// -/// ``` -/// [1, 2, 3, 4, 5].iter(fn(x){ print("\(x) ") }) //output: 1 2 3 4 5 -/// ``` -/// @intrinsic %array.iter -pub fn iter[T](self : Array[T], f : (T) -> Unit) -> Unit { - for i = 0; i < self.length(); i = i + 1 { - f(self[i]) - } +pub fn as_iter[T](self : Array[T]) -> @iter.Iter[T] { + @iter.Iter::_unstable_internal_make( + fn(yield) { + for i = 0, len = self.length(); i < len; i = i + 1 { + if yield(self[i]).not() { + break false + } + } else { + true + } + }, + ) } -test "iter" { - let mut i = 0 - let mut failed = false - let f = fn(elem) { - if elem != i + 1 { - failed = true +fn unsafe_blit[A]( + dst : Array[A], + dst_offset : Int, + src : Array[A], + src_offset : Int, + len : Int +) -> Unit { + if physical_equal(dst, src) && dst_offset < src_offset { + for i = 0; i < len; i = i + 1 { + dst[dst_offset + i] = src[src_offset + i] } - i = i + 1 - } - { - i = 0 - ([] : Array[_]).iter(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 0)? - } - { - i = 0 - ([1] : Array[_]).iter(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? - } - i = 0 - ([1, 2, 3, 4, 5] : Array[_]).iter(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 5)? -} - -/// Iterates over the array with index. -/// -/// # Arguments -/// -/// - `self`: The array to iterate over. -/// - `f`: A function that takes an `Int` representing the index and a `T` representing the element of the array, and returns `Unit`. -/// -/// # Example -/// -/// ``` -/// [1, 2, 3, 4, 5].iteri(fn(index, elem){ -/// print("(\(index),\(elem)) ") -/// }) //output: (0,1) (1,2) (2,3) (3,4) (4,5) -/// ``` -pub fn iteri[T](self : Array[T], f : (Int, T) -> Unit) -> Unit { - for i = 0; i < self.length(); i = i + 1 { - f(i, self[i]) - } -} - -test "iteri" { - let mut i = 0 - let mut failed = false - let f = fn(index, elem) { - if index != i || elem != i + 1 { - failed = true + } else { + for i = len - 1; i >= 0; i = i - 1 { + dst[dst_offset + i] = src[src_offset + i] } - i = i + 1 - } - { - i = 0 - ([] : Array[_]).iteri(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 0)? - } - { - i = 0 - ([1] : Array[_]).iteri(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? } - i = 0 - ([1, 2, 3, 4, 5] : Array[_]).iteri(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 5)? } -/// Iterates over each element in reversed turn. -/// -/// # Arguments -/// -/// - `self`: The array to iterate over. -/// - `f`: The function to apply to each element. -/// -/// # Example -/// -/// ``` -/// [1, 2, 3, 4, 5].iter(fn(x){ print("\(x) ") }) //output: 5 4 3 2 1 -/// ``` -pub fn iter_rev[T](self : Array[T], f : (T) -> Unit) -> Unit { - for i = self.length() - 1; i >= 0; i = i - 1 { - f(self[i]) +pub fn blit_to[A]( + self : Array[A], + dst : Array[A], + ~len : Int, + ~src_offset : Int = 0, + ~dst_offset : Int = 0 +) -> Unit { + if dst_offset + len > dst.length() || src_offset + len > self.length() { + abort("invalid argument in blit") } + unsafe_blit(dst, dst_offset, self, src_offset, len) } -test "iter_rev" { - let mut i = 6 - let mut failed = false - let f = fn(elem) { - if elem != i - 1 { - failed = true - } - i = i - 1 - } - { - i = 1 - ([] : Array[_]).iter_rev(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? - } - { - i = 2 - ([1] : Array[_]).iter_rev(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? - } - i = 6 - ([1, 2, 3, 4, 5] : Array[_]).iter_rev(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? -} - -/// Iterates over the array with index in reversed turn. -/// -/// # Arguments -/// -/// - `self`: The array to iterate over. -/// - `f`: A function that takes an `Int` representing the index and a `T` representing the element of the array, and returns `Unit`. -/// -/// # Example -/// -/// ``` -/// [1, 2, 3, 4, 5].iter_revi(fn(index, elem){ -/// print("(\(index),\(elem)) ") -/// }) //output: (4,5) (3,4) (2,3) (1,2) (0,1) -/// ``` -pub fn iter_revi[T](self : Array[T], f : (Int, T) -> Unit) -> Unit { +pub fn copy[T](self : Array[T]) -> Array[T] { let len = self.length() - for i = 0; i < len; i = i + 1 { - f(i, self[len - i - 1]) - } -} - -test "iter_revi" { - let mut i = 6 - let mut j = 0 - let mut failed = false - let f = fn(index, elem) { - if index != j || elem != i - 1 { - failed = true - } - i = i - 1 - j = j + 1 - } - { - i = 1 - j = 0 - ([] : Array[_]).iter_revi(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? - @assertion.assert_eq(j, 0)? - } - { - i = 2 - j = 0 - ([1] : Array[_]).iter_revi(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? - @assertion.assert_eq(j, 1)? - } - i = 6 - j = 0 - ([1, 2, 3, 4, 5] : Array[_]).iter_revi(f) - @assertion.assert_false(failed)? - @assertion.assert_eq(i, 1)? - @assertion.assert_eq(j, 5)? -} - -/// Applies a function to each element of the array and returns a new array with the results. -/// -/// # Example -/// -/// ``` -/// let arr = [1, 2, 3, 4, 5] -/// let doubled = arr.map(fn(x){ x * 2 }) -/// debug(doubled) //output: [2, 4, 6, 8, 10] -/// ``` -pub fn map[T, U](self : Array[T], f : (T) -> U) -> Array[U] { - if self.length() == 0 { - return [] - } - let res = Array::make(self.length(), f(self[0])) - for i = 1; i < self.length(); i = i + 1 { - res[i] = f(self[i]) - } - res -} - -test "map" { - let empty : Array[Unit] = Array::default().map(fn(x) { x }) - @assertion.assert_eq(empty, [])? - let simple_arr : Array[_] = [6] - let simple_doubled = simple_arr.map(fn(x) { x * 2 }) - @assertion.assert_eq(simple_doubled, [12])? - let arr : Array[_] = [1, 2, 3, 4, 5] - let doubled = arr.map(fn(x) { x * 2 }) - @assertion.assert_eq(doubled, [2, 4, 6, 8, 10])? -} - -/// Maps a function over the elements of the arr with index. -/// -/// # Example -/// ``` -/// let arr = [3, 4, 5] -/// let added = arr.mapi(fn (i, x) { x + i }) -/// debug(added) //output: [3, 5, 7] -/// ``` -pub fn mapi[T, U](self : Array[T], f : (Int, T) -> U) -> Array[U] { - if self.length() == 0 { - return [] - } - let res = Array::make(self.length(), f(0, self[0])) - for i = 1; i < self.length(); i = i + 1 { - res[i] = f(i, self[i]) - } - res -} - -test "mapi" { - let empty : Array[Int] = Array::default().mapi(fn(i, x) { x + i }) - @assertion.assert_eq(empty, [])? - let simple_arr : Array[_] = [6] - let simple_doubled = simple_arr.mapi(fn(i, x) { x * 2 + i }) - @assertion.assert_eq(simple_doubled, [12])? - let arr : Array[_] = [1, 2, 3, 4, 5] - let doubled = arr.mapi(fn(i, x) { x * 2 + i }) - @assertion.assert_eq(doubled, [2, 5, 8, 11, 14])? -} - -/// Create a new array. Values are lazily built. -pub fn new[T](length : Int, value : () -> T) -> Array[T] { - if length <= 0 { + if len == 0 { [] } else { - let array = Array::make(length, value()) - for i = 1; i < length; i = i + 1 { - array[i] = value() - } - array + let arr = Array::make(len, self[0]) + unsafe_blit(arr, 0, self, 0, len) + arr } } -test "new" { - let empty = new(0, fn() { { val: 3 } }) - @assertion.assert_eq(empty.length(), 0)? - let simple_arr = new(1, fn() { { val: 2 } }) - @assertion.assert_eq(simple_arr.length(), 1)? - @assertion.assert_eq(simple_arr[0].val, 2)? - let arr = new(2, fn() { { val: 1 } }) - @assertion.assert_eq(arr.length(), 2)? - @assertion.assert_is_not(arr[0], arr[1])? - @assertion.assert_eq(arr[0].val, 1)? - @assertion.assert_eq(arr[1].val, 1)? -} - -/// Create a new array. Values are built from indexes. pub fn new_with_index[T](length : Int, value : (Int) -> T) -> Array[T] { if length <= 0 { [] @@ -305,612 +79,3 @@ pub fn new_with_index[T](length : Int, value : (Int) -> T) -> Array[T] { array } } - -test "new_with_index" { - let empty = new_with_index(0, fn { i => i }) - @assertion.assert_eq(empty.length(), 0)? - let simple_arr = new_with_index(1, fn { i => i }) - @assertion.assert_eq(simple_arr.length(), 1)? - @assertion.assert_eq(simple_arr[0], 0)? - let arr = new_with_index(2, fn { i => i }) - @assertion.assert_eq(arr.length(), 2)? - @assertion.assert_eq(arr[0], 0)? - @assertion.assert_eq(arr[1], 1)? -} - -/// Create a new array with given values. -pub fn Array::from_array[T](array : Array[T]) -> Array[T] { - array -} - -test "from_array" { - let array = Array::[1, 2, 3, 4, 5] - @assertion.assert_eq(array, [1, 2, 3, 4, 5])? -} - -/// Fold out values from an array according to certain rules. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_left(init=0, fn { sum, elem => sum + elem }) -/// sum // 15 -/// ``` -pub fn fold_left[T, U](self : Array[T], f : (U, T) -> U, ~init : U) -> U { - for i = 0, acc = init; i < self.length(); { - continue i + 1, f(acc, self[i]) - } else { - acc - } -} - -test "fold_left" { - let sum = ([] : Array[_]).fold_left(init=1, fn { sum, elem => sum + elem }) - @assertion.assert_eq(sum, 1)? - let sum = ([1] : Array[_]).fold_left(init=2, fn { sum, elem => sum + elem }) - @assertion.assert_eq(sum, 3)? - let sum = ([1, 2, 3, 4, 5] : Array[_]).fold_left( - init=0, - fn { sum, elem => sum + elem }, - ) - @assertion.assert_eq(sum, 15)? -} - -/// Fold out values from an array according to certain rules in reversed turn. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_right(init=0, fn { sum, elem => sum + elem }) -/// sum // 15 -/// ``` -pub fn fold_right[T, U](self : Array[T], f : (U, T) -> U, ~init : U) -> U { - for i = self.length() - 1, acc = init; i >= 0; { - continue i - 1, f(acc, self[i]) - } else { - acc - } -} - -test "fold_right" { - let sum = ([] : Array[_]).fold_right(init=1, fn { sum, elem => sum + elem }) - @assertion.assert_eq(sum, 1)? - let sum = ([1] : Array[_]).fold_right(init=2, fn { sum, elem => sum + elem }) - @assertion.assert_eq(sum, 3)? - let sum = ([1, 2, 3, 4, 5] : Array[_]).fold_right( - init=0, - fn { sum, elem => sum + elem }, - ) - @assertion.assert_eq(sum, 15)? -} - -/// Fold out values from an array according to certain rules with index. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_lefti(init=0, fn { index, sum, elem => sum + index }) -/// sum // 10 -/// ``` -pub fn fold_lefti[T, U](self : Array[T], f : (Int, U, T) -> U, ~init : U) -> U { - for i = 0, acc = init; i < self.length(); { - continue i + 1, f(i, acc, self[i]) - } else { - acc - } -} - -test "fold_lefti" { - let f = fn { index, sum, elem => index + sum + elem } - { - let sum = ([] : Array[_]).fold_lefti(init=1, f) - @assertion.assert_eq(sum, 1)? - } - { - let sum = ([1] : Array[_]).fold_lefti(init=2, f) - @assertion.assert_eq(sum, 3)? - } - let sum = ([1, 2, 3, 4, 5] : Array[_]).fold_lefti(init=0, f) - @assertion.assert_eq(sum, 25)? -} - -/// Fold out values from an array according to certain rules in reversed turn with index. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_righti(init=0, fn { index, sum, elem => sum + index }) -/// sum // 10 -/// ``` -pub fn fold_righti[T, U](self : Array[T], f : (Int, U, T) -> U, ~init : U) -> U { - let len = self.length() - for i = len - 1, acc = init; i >= 0; { - continue i - 1, f(len - i - 1, acc, self[i]) - } else { - acc - } -} - -test "fold_righti" { - let f = fn { index, sum, elem => index + sum + elem } - { - let sum = ([] : Array[_]).fold_lefti(init=1, f) - @assertion.assert_eq(sum, 1)? - } - { - let sum = ([1] : Array[_]).fold_lefti(init=2, f) - @assertion.assert_eq(sum, 3)? - } - let sum = ([1, 2, 3, 4, 5] : Array[_]).fold_righti(init=0, f) - @assertion.assert_eq(sum, 25)? -} - -/// Reverses the order of elements in the slice, in place. -/// -/// # Example -/// ``` -/// let arr = [1, 2, 3, 4, 5] -/// reverse(arr) // [5, 4, 3, 2, 1] -/// ``` -pub fn reverse[T](self : Array[T]) -> Unit { - let mid_len = self.length() / 2 - for i = 0; i < mid_len; i = i + 1 { - let j = self.length() - i - 1 - let temp = self[i] - self[i] = self[j] - self[j] = temp - } -} - -test "reverse" { - { - let arr : Array[Int] = [] - arr.reverse() - @assertion.assert_eq(arr, [])? - } - { - let arr : Array[_] = [1] - arr.reverse() - @assertion.assert_eq(arr, [1])? - } - { - let arr : Array[_] = [1, 2] - arr.reverse() - @assertion.assert_eq(arr, [2, 1])? - } - { - let arr : Array[_] = [1, 2, 3, 4, 5] - arr.reverse() - @assertion.assert_eq(arr, [5, 4, 3, 2, 1])? - } - let arr : Array[_] = [1, 2, 3, 4, 5, 6] - arr.reverse() - @assertion.assert_eq(arr, [6, 5, 4, 3, 2, 1])? -} - -/// Swap two elements in the array. -/// -/// # Example -/// -/// ``` -/// let arr = [1, 2, 3, 4, 5] -/// arr.swap(0, 1) -/// debug(arr) //output: [2, 1, 3, 4, 5] -/// ``` -fn swap[T](self : Array[T], i : Int, j : Int) -> Unit { - let temp = self[i] - self[i] = self[j] - self[j] = temp -} - -test "swap" { - { - let arr : Array[Int] = [1] - arr.swap(0, 0) - @assertion.assert_eq(arr, [1])? - } - { - let arr : Array[_] = [1, 2] - arr.swap(0, 0) - @assertion.assert_eq(arr, [1, 2])? - arr.swap(0, 1) - @assertion.assert_eq(arr, [2, 1])? - } - let arr : Array[_] = [1, 2, 3, 4, 5] - arr.swap(3, 3) - @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? - arr.swap(1, 3) - @assertion.assert_eq(arr, [1, 4, 3, 2, 5])? -} - -/// Check if all the elements in the array match the condition. -/// -/// # Example -/// -/// ``` -/// let arr = [1, 2, 3, 4, 5] -/// arr.all(fn(ele) { ele < 6 }) // true -/// arr.all(fn(ele) { ele < 5 }) // false -/// ``` -pub fn all[T](self : Array[T], f : (T) -> Bool) -> Bool { - for i = 0; i < self.length(); i = i + 1 { - if f(self[i]).not() { - return false - } - } - true -} - -test "all" { - { - let arr : Array[Int] = [] - @assertion.assert_true(arr.all(fn(ele) { ele < 6 }))? - @assertion.assert_true(arr.all(fn(ele) { ele < 5 }))? - } - { - let arr : Array[_] = [5] - @assertion.assert_true(arr.all(fn(ele) { ele < 6 }))? - @assertion.assert_false(arr.all(fn(ele) { ele < 5 }))? - } - let arr : Array[_] = [1, 2, 3, 4, 5] - @assertion.assert_true(arr.all(fn(ele) { ele < 6 }))? - @assertion.assert_false(arr.all(fn(ele) { ele < 5 }))? -} - -/// Check if any of the elements in the array match the condition. -/// -/// # Example -/// -/// ``` -/// let arr = [1, 2, 3, 4, 5] -/// arr.any(fn(ele) { ele < 6 }) // true -/// arr.any(fn(ele) { ele < 5 }) // true -/// ``` -pub fn any[T](self : Array[T], f : (T) -> Bool) -> Bool { - for i = 0; i < self.length(); i = i + 1 { - if f(self[i]) { - return true - } - } - false -} - -test "any" { - { - let arr : Array[Int] = [] - @assertion.assert_false(arr.any(fn(ele) { ele < 6 }))? - @assertion.assert_false(arr.any(fn(ele) { ele < 5 }))? - } - { - let arr : Array[_] = [5] - @assertion.assert_true(arr.any(fn(ele) { ele < 6 }))? - @assertion.assert_false(arr.any(fn(ele) { ele < 5 }))? - } - let arr : Array[_] = [1, 2, 3, 4, 5] - @assertion.assert_true(arr.any(fn(ele) { ele < 6 }))? - @assertion.assert_true(arr.any(fn(ele) { ele < 5 }))? -} - -/// Fill the array with a given value. -/// -/// # Example -/// ``` -/// [0, 0, 0, 0, 0].fill(3) // [3, 3, 3, 3, 3] -/// ``` -pub fn fill[T](self : Array[T], value : T) -> Unit { - for i = 0; i < self.length(); i = i + 1 { - self[i] = value - } -} - -test "fill" { - { - let arr : Array[Int] = [] - arr.fill(3) - @assertion.assert_eq(arr, [])? - } - { - let arr : Array[_] = [6] - arr.fill(5) - @assertion.assert_eq(arr, [5])? - } - let arr : Array[_] = [0, 0, 0, 0, 0] - arr.fill(3) - @assertion.assert_eq(arr, [3, 3, 3, 3, 3])? -} - -/// Search the array index for a given element. -/// -/// # Example -/// ``` -/// let arr = [3, 4, 5] -/// arr.search(3) // 0 -/// ``` -pub fn search[T : Eq](self : Array[T], value : T) -> Option[Int] { - for i = 0; i < self.length(); i = i + 1 { - if self[i] == value { - return Some(i) - } - } - None -} - -test "search" { - { - let arr : Array[Int] = [] - @assertion.assert_eq(arr.search(3), None)? - @assertion.assert_eq(arr.search(-1), None)? - } - { - let arr : Array[_] = [3] - @assertion.assert_eq(arr.search(3), Some(0))? - @assertion.assert_eq(arr.search(-1), None)? - } - let arr : Array[_] = [1, 2, 3, 4, 5] - @assertion.assert_eq(arr.search(1), Some(0))? - @assertion.assert_eq(arr.search(5), Some(4))? - @assertion.assert_eq(arr.search(3), Some(2))? - @assertion.assert_eq(arr.search(-1), None)? -} - -/// Checks if the array contains an element. -/// -/// # Example -/// ``` -/// let arr = [3, 4, 5] -/// arr.contains(3) -/// ``` -pub fn contains[T : Eq](self : Array[T], value : T) -> Bool { - for i = 0; i < self.length(); i = i + 1 { - if self[i] == value { - return true - } - } - false -} - -test "contains" { - { - let arr : Array[Int] = [] - @assertion.assert_false(arr.contains(3))? - @assertion.assert_false(arr.contains(-1))? - } - { - let arr : Array[_] = [3] - @assertion.assert_true(arr.contains(3))? - @assertion.assert_false(arr.contains(-1))? - } - let arr : Array[_] = [3, 4, 5] - @assertion.assert_true(arr.contains(3))? - @assertion.assert_true(arr.contains(4))? - @assertion.assert_true(arr.contains(5))? - @assertion.assert_false(arr.contains(6))? -} - -/// Check if the array starts with a given prefix. -/// -/// # Example -/// ``` -/// let arr = [3, 4, 5] -/// arr.starts_with([3, 4]) // true -/// ``` -pub fn starts_with[T : Eq](self : Array[T], prefix : Array[T]) -> Bool { - if prefix.length() > self.length() { - return false - } - for i = 0; i < prefix.length(); i = i + 1 { - if self[i] != prefix[i] { - return false - } - } - true -} - -test "starts_with" { - { - let arr : Array[Int] = [] - @assertion.assert_true(arr.starts_with([]))? - @assertion.assert_false(arr.starts_with([1]))? - } - { - let arr : Array[_] = [3] - @assertion.assert_true(arr.starts_with([]))? - @assertion.assert_true(arr.starts_with([3]))? - @assertion.assert_false(arr.starts_with([2]))? - @assertion.assert_false(arr.starts_with([3, 1]))? - } - let arr : Array[_] = [3, 4, 5] - @assertion.assert_true(arr.starts_with([]))? - @assertion.assert_true(arr.starts_with([3]))? - @assertion.assert_false(arr.starts_with([2]))? - @assertion.assert_true(arr.starts_with([3, 4]))? - @assertion.assert_false(arr.starts_with([3, 2]))? - @assertion.assert_true(arr.starts_with([3, 4, 5]))? - @assertion.assert_false(arr.starts_with([3, 4, 2]))? - @assertion.assert_false(arr.starts_with([3, 4, 5, 6]))? -} - -/// Check if the array ends with a given suffix. -/// -/// # Example -/// ``` -/// let v = [3, 4, 5] -/// v.ends_with([5]) // true -/// ``` -pub fn ends_with[T : Eq](self : Array[T], suffix : Array[T]) -> Bool { - let self_len = self.length() - let suf_len = suffix.length() - if suf_len > self_len { - return false - } - for i = 0; i < suf_len; i = i + 1 { - if self[self_len - suf_len + i] != suffix[i] { - return false - } - } - true -} - -test "ends_with" { - { - let arr : Array[Int] = [] - @assertion.assert_true(arr.ends_with([]))? - @assertion.assert_false(arr.ends_with([1]))? - } - { - let arr : Array[_] = [3] - @assertion.assert_true(arr.ends_with([]))? - @assertion.assert_true(arr.ends_with([3]))? - @assertion.assert_false(arr.ends_with([2]))? - @assertion.assert_false(arr.ends_with([3, 1]))? - } - let arr : Array[_] = [3, 4, 5] - @assertion.assert_true(arr.ends_with([]))? - @assertion.assert_true(arr.ends_with([5]))? - @assertion.assert_false(arr.ends_with([2]))? - @assertion.assert_true(arr.ends_with([4, 5]))? - @assertion.assert_false(arr.ends_with([4, 2]))? - @assertion.assert_false(arr.ends_with([2, 5]))? - @assertion.assert_true(arr.ends_with([3, 4, 5]))? - @assertion.assert_false(arr.ends_with([3, 4, 2]))? - @assertion.assert_false(arr.ends_with([3, 2, 5]))? - @assertion.assert_false(arr.ends_with([2, 4, 5]))? - @assertion.assert_false(arr.ends_with([3, 4, 5, 6]))? - @assertion.assert_false(arr.ends_with([2, 3, 4, 5]))? -} - -pub fn op_equal[T : Eq](self : Array[T], that : Array[T]) -> Bool { - if self.length() != that.length() { - return false - } - for i = 0; i < self.length(); i = i + 1 { - if self[i] != that[i] { - return false - } - } - true -} - -test "op_equal" { - { - inspect(([] : Array[Int]) == [], content="true")? - inspect(([] : Array[_]) == [1], content="false")? - inspect(([1, 2] : Array[_]) == [], content="false")? - } - { - inspect(([1] : Array[_]) == [1], content="true")? - inspect(([1] : Array[_]) == [2], content="false")? - inspect(([1, 2] : Array[_]) == [1], content="false")? - inspect(([1] : Array[_]) == [1, 2], content="false")? - } - inspect(([1, 2, 3, 4, 5] : Array[_]) == [1, 2, 3, 4, 5], content="true")? - inspect(([1, 2, 3, 4, 5] : Array[_]) == [1, 2, 3, 4], content="false")? - inspect(([1, 2, 3, 4] : Array[_]) == [1, 2, 3, 4, 5], content="false")? - inspect(([1, 2, 3, 4, 5] : Array[_]) == [6, 2, 3, 4, 5], content="false")? - inspect(([1, 2, 3, 4, 5] : Array[_]) == [1, 2, 6, 4, 5], content="false")? - inspect(([1, 2, 3, 4, 5] : Array[_]) == [1, 2, 3, 4, 6], content="false")? -} - -fn deep_clone[T](arr : Array[T]) -> Array[T] { - let len = arr.length() - if len == 0 { - [] - } else { - let result = Array::make(len, arr[0]) - for i = 0; i < len; i = i + 1 { - result[i] = arr[i] - } - result - } -} - -test "deep_clone" { - { - let pre : Array[Int] = [] - let arr = deep_clone(pre) - @assertion.assert_is_not(arr, pre)? - @assertion.assert_eq(arr, [])? - } - { - let pre : Array[_] = [3] - let arr = deep_clone(pre) - @assertion.assert_is_not(arr, pre)? - @assertion.assert_eq(arr, [3])? - } - let pre : Array[_] = [1, 2, 3, 4, 5] - let arr = deep_clone(pre) - @assertion.assert_is_not(arr, pre)? - @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? -} - -pub fn op_add[T](self : Array[T], other : Array[T]) -> Array[T] { - let slen = self.length() - let nlen = other.length() - if slen == 0 { - deep_clone(other) - } else if nlen == 0 { - deep_clone(self) - } else { - let result = Array::make(slen + nlen, self[0]) - for i = 1; i < slen; i = i + 1 { - result[i] = self[i] - } - for i = 0; i < nlen; i = i + 1 { - result[i + slen] = other[i] - } - result - } -} - -test "op_add" { - { - inspect(([] : Array[Int]) + [], content="[]")? - inspect(([] : Array[_]) + [1, 2, 3, 4, 5], content="[1, 2, 3, 4, 5]")? - inspect(([1, 2, 3, 4, 5] : Array[_]) + [], content="[1, 2, 3, 4, 5]")? - } - { - inspect(([1] : Array[_]) + [2], content="[1, 2]")? - inspect(([1] : Array[_]) + [1, 2, 3, 4, 5], content="[1, 1, 2, 3, 4, 5]")? - inspect(([1, 2, 3, 4, 5] : Array[_]) + [1], content="[1, 2, 3, 4, 5, 1]")? - } - inspect( - ([1, 2, 3, 4, 5] : Array[_]) + [6, 7, 8, 9, 10], - content="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", - )? -} - -/// @intrinsic %iter.from_array -pub fn as_iter[T](self : Array[T]) -> @iter.Iter[T] { - @iter.Iter::_unstable_internal_make( - fn(yield) { - for i = 0, len = self.length(); i < len; i = i + 1 { - if yield(self[i]).not() { - break false - } - } else { - true - } - }, - ) -} - -test "as_iter" { - let arr : Array[_] = [1, 2, 3, 4, 5] - let iter = arr.as_iter() - let exb = Buffer::make(0) - let mut i = 0 - iter.iter( - fn(x) { - exb.write_string(x.to_string()) - exb.write_char('\n') - i = i + 1 - }, - ) - @assertion.assert_eq(i, arr.length())? - exb.expect( - content= - #|1 - #|2 - #|3 - #|4 - #|5 - #| - , - )? -} diff --git a/array/array.mbti b/array/array.mbti index 60f7bf99..2ec9efde 100644 --- a/array/array.mbti +++ b/array/array.mbti @@ -1,45 +1,85 @@ -package moonbitlang/core/array +package moonbitlang/core/vec alias @moonbitlang/core/iter as @iter // Values -fn is_sorted[T : Compare + Eq](Array[T]) -> Bool - -fn new[T](Int, () -> T) -> Array[T] - -fn new_with_index[T](Int, (Int) -> T) -> Array[T] // Types and methods -type TimSortRun -impl Array { - all[T](Self[T], (T) -> Bool) -> Bool - any[T](Self[T], (T) -> Bool) -> Bool +type Vec +impl Vec { + append[T](Self[T], Self[T]) -> Unit as_iter[T](Self[T]) -> @iter.Iter[T] - blit_to[A](Self[A], Self[A], ~len : Int, ~src_offset : Int = .., ~dst_offset : Int = ..) -> Unit + capacity[T](Self[T]) -> Int + chunk_by[T](Self[T], (T, T) -> Bool) -> Self[Self[T]] + chunks[T](Self[T], Int) -> Self[Self[T]] + clear[T](Self[T]) -> Unit contains[T : Eq](Self[T], T) -> Bool - copy[T](Self[T]) -> Self[T] + debug_write[T : Show](Self[T], Buffer) -> Unit + dedup[T : Eq](Self[T]) -> Unit + drain[T](Self[T], Int, Int) -> Self[T] ends_with[T : Eq](Self[T], Self[T]) -> Bool + extract_if[T](Self[T], (T) -> Bool) -> Self[T] fill[T](Self[T], T) -> Unit + fill_with[T](Self[T], () -> T) -> Unit + flatten[T](Self[Self[T]]) -> Self[T] fold_left[T, U](Self[T], (U, T) -> U, ~init : U) -> U fold_lefti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U fold_right[T, U](Self[T], (U, T) -> U, ~init : U) -> U fold_righti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U - from_array[T](Self[T]) -> Self[T] + from_array[T](Array[T]) -> Self[T] + get[T](Self[T], Int) -> Option[T] + insert[T](Self[T], Int, T) -> Unit + is_empty[T](Self[T]) -> Bool + is_sorted[T : Compare + Eq](Self[T]) -> Bool iter[T](Self[T], (T) -> Unit) -> Unit iter_rev[T](Self[T], (T) -> Unit) -> Unit iter_revi[T](Self[T], (Int, T) -> Unit) -> Unit iteri[T](Self[T], (Int, T) -> Unit) -> Unit + join[T](Self[Self[T]], T) -> Self[T] + length[T](Self[T]) -> Int map[T, U](Self[T], (T) -> U) -> Self[U] + map_inplace[T](Self[T], (T) -> T) -> Unit mapi[T, U](Self[T], (Int, T) -> U) -> Self[U] + mapi_inplace[T](Self[T], (Int, T) -> T) -> Unit + new[T]() -> Self[T] op_add[T](Self[T], Self[T]) -> Self[T] + op_as_view[T](Self[T], ~start : Int, ~end : Int) -> VecView[T] op_equal[T : Eq](Self[T], Self[T]) -> Bool + op_get[T](Self[T], Int) -> T + op_set[T](Self[T], Int, T) -> Unit + pop[T](Self[T]) -> Option[T] + pop_exn[T](Self[T]) -> T + push[T](Self[T], T) -> Unit + remove[T](Self[T], Int) -> T + repeat[T](Self[T], Int) -> Self[T] + reserve_capacity[T](Self[T], Int) -> Unit + resize[T](Self[T], Int, T) -> Unit + retain[T](Self[T], (T) -> Bool) -> Unit reverse[T](Self[T]) -> Unit search[T : Eq](Self[T], T) -> Option[Int] + shrink_to_fit[T](Self[T]) -> Unit sort[T : Compare + Eq](Self[T]) -> Unit sort_by[T](Self[T], (T, T) -> Int) -> Unit sort_by_key[T, K : Compare + Eq](Self[T], (T) -> K) -> Unit - stable_sort[T : Compare + Eq](Self[T]) -> Unit + split[T](Self[T], (T) -> Bool) -> Self[Self[T]] + split_at[T](Self[T], Int) -> Tuple[Self[T], Self[T]] starts_with[T : Eq](Self[T], Self[T]) -> Bool + strip_prefix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] + strip_suffix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] + swap[T](Self[T], Int, Int) -> Unit + to_list[T](Self[T]) -> List[T] + to_string[T : Show](Self[T]) -> String + with_capacity[T](Int) -> Self[T] +} + +type VecView +impl VecView { + length[T](Self[T]) -> Int + op_as_view[T](Self[T], ~start : Int, ~end : Int) -> Self[T] + op_get[T](Self[T], Int) -> T + op_set[T](Self[T], Int, T) -> Unit + reverse[T](Self[T]) -> Unit + swap[T](Self[T], Int, Int) -> Unit } // Traits diff --git a/array/array_test.mbt b/array/array_test.mbt index 9b21f7b2..e82675c3 100644 --- a/array/array_test.mbt +++ b/array/array_test.mbt @@ -12,30 +12,445 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Convert array to list. -/// -/// # Example -/// -/// ``` -/// [1, 2, 3, 4, 5].to_list() -/// ``` -pub fn to_list[T](self : Array[T]) -> List[T] { - for i = self.length() - 1, list = List::Nil; i >= 0; { - continue i - 1, List::Cons(self[i], list) - } else { - list - } +test "to_string" { + let empty: Array[Int] = [] + inspect(empty, content="[]")? + let a0 = [0] + inspect(a0, content="[0]")? + a0.push(1) + inspect(a0, content="[0, 1]")? + a0.push(2) + inspect(a0, content="[0, 1, 2]")? +} + +test "get" { + let v = [3] + inspect(v.get(-1), content="None")? + inspect(v.get(0), content="Some(3)")? + inspect(v.get(1), content="None")? +} + +test "op_add" { + inspect(([] : Array[Int]) + [], content="[]")? + inspect( + ([] : Array[Int]) + [1, 2, 3, 4, 5], + content="[1, 2, 3, 4, 5]", + )? + inspect([1, 2, 3, 4, 5] + [], content="[1, 2, 3, 4, 5]")? + inspect( + [1, 2, 3, 4, 5] + [6, 7, 8, 9, 10], + content="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", + )? +} + +test "op_equal" { + let v1 = [3, 4, 5] + let v2 = [3, 4, 5] + let v3 = [3, 4, 6] + @assertion.assert_true(v1 == v2)? + @assertion.assert_false(v1 == v3)? +} + +test "pop" { + let v = Array::new() + v.push(3) + v.push(4) + @assertion.assert_eq(v.length(), 2)? + @assertion.assert_eq(v.pop(), Some(4))? + @assertion.assert_eq(v.pop(), Some(3))? + @assertion.assert_eq(v.length(), 0)? + @assertion.assert_eq(v.pop(), None)? +} + +test "pop_exn" { + let v = Array::new() + v.push(3) + v.push(4) + @assertion.assert_eq(v.length(), 2)? + @assertion.assert_eq(v.pop_exn(), 4)? + @assertion.assert_eq(v.pop_exn(), 3)? + @assertion.assert_eq(v.length(), 0)? +} + +test "push" { + let v = [3, 4, 5] + @assertion.assert_eq(v, [3, 4, 5])? + v[0] = 6 + @assertion.assert_eq(v, [6, 4, 5])? +} + +test "drain" { + let v = [3, 4, 5] + let v2 = [1, 2, 3, 4, 5] + let vv = v.drain(1, 2) + let v2v = v2.drain(2, 5) + @assertion.assert_eq(v, [3, 5])? + @assertion.assert_eq(vv, [4])? + @assertion.assert_eq(v2, [1, 2])? + @assertion.assert_eq(v2v, [3, 4, 5])? +} + +test "append" { + let v1 = [3, 4, 5] + let v2 = [6, 7, 8] + v1.append(v2) + @assertion.assert_eq(v1, [3, 4, 5, 6, 7, 8])? + @assertion.assert_eq(v1.length(), 6)? +} + +test "iter" { + let v = [3, 4, 5] + let mut sum = 0 + v.iter(fn(x) { sum = sum + x }) + @assertion.assert_eq(sum, 12)? +} + +test "iter_rev" { + let v = with_capacity(3) + v.push(3) + v.push(4) + v.push(5) + let mut sum = 0 + v.iter_rev(fn(x) { sum = sum + x }) + @assertion.assert_eq(sum, 12)? +} + +test "iteri" { + let v = [3, 4, 5] + let mut sum = 0 + v.iteri(fn(i, x) { sum = sum + x + i }) + @assertion.assert_eq(sum, 15)? +} + +test "iter_revi" { + let v = [3, 4, 5] + let mut sum = 0 + v.iter_revi(fn(i, x) { sum = sum + x + i }) + @assertion.assert_eq(sum, 15)? +} + +test "clear" { + let v = [3, 4, 5] + v.clear() + @assertion.assert_eq(v.length(), 0)? + @assertion.assert_eq(v.capacity(), 3)? +} + +test "map" { + let v = [3, 4, 5] + let e : Array[Int] = [] + inspect(v.map(fn(x) { x + 1 }), content="[4, 5, 6]")? + inspect(v.map(fn(x) { x.to_string() }), content="[3, 4, 5]")? + inspect(e.map(fn(x) { x + 1 }), content="[]")? + // Is it correct? + inspect(["a", "b", "c"], content="[a, b, c]")? +} + +test "map_inplace" { + let v = [3, 4, 5] + let e : Array[Int] = [] + v.map_inplace(fn(x) { x + 1 }) + e.map_inplace(fn(x) { x + 1 }) + inspect(v, content="[4, 5, 6]")? + inspect(e, content="[]")? +} + +test "mapi" { + let v = [3, 4, 5] + let e : Array[Int] = [] + inspect(v.mapi(fn(i, x) { x + i }), content="[3, 5, 7]")? + inspect(v.mapi(fn(i, x) { (x + i).to_string() }), content="[3, 5, 7]")? + inspect(e.mapi(fn(_i, x) { x + 1 }), content="[]")? +} + +test "mapi_inplace" { + let v = [3, 4, 5] + let e : Array[Int] = [] + v.mapi_inplace(fn(i, x) { x + i }) + e.mapi_inplace(fn(_i, x) { x + 1 }) + inspect(v, content="[3, 5, 7]")? + inspect(e, content="[]")? +} + +test "is_empty" { + let v = Array::new() + @assertion.assert_true(v.is_empty())? + v.push(3) + @assertion.assert_false(v.is_empty())? +} + +test "is_sorted" { + let v: Array[Int] = [] + @assertion.assert_true(v.is_sorted())? + let v = [3, 4, 5] + @assertion.assert_true(v.is_sorted())? + let v2 = [3, 5, 4] + @assertion.assert_false(v2.is_sorted())? +} + +test "reverse" { + let v = [3, 4, 5] + v.reverse() + @assertion.assert_eq(v, [5, 4, 3])? + v.reverse() + @assertion.assert_eq(v, [3, 4, 5])? + let v = [3, 4] + v.reverse() + @assertion.assert_eq(v, [4, 3])? + v.reverse() + @assertion.assert_eq(v, [3, 4])? + let empty: Array[Int] = [] + @assertion.assert_eq(empty, empty)? +} + +test "split_at" { + let v = [3, 4, 5] + let (v1, v2) = v.split_at(1) + @assertion.assert_eq(v1, [3])? + @assertion.assert_eq(v2, [4, 5])? +} + +test "contains" { + let v = [3, 4, 5] + @assertion.assert_true(v.contains(3))? + @assertion.assert_true(v.contains(4))? + @assertion.assert_true(v.contains(5))? + @assertion.assert_false(v.contains(6))? +} + +test "starts_with" { + let v = [3, 4, 5] + @assertion.assert_true(v.starts_with([3, 4]))? + @assertion.assert_true(v.starts_with([3, 4, 5]))? + @assertion.assert_false(v.starts_with([3, 4, 6]))? + @assertion.assert_false(v.starts_with([3, 4, 5, 6]))? +} + +test "ends_with" { + let v = [3, 4, 5] + @assertion.assert_true(v.ends_with([4, 5]))? + @assertion.assert_true(v.ends_with([3, 4, 5]))? + @assertion.assert_false(v.ends_with([3, 4, 6]))? + @assertion.assert_false(v.ends_with([2, 3, 4, 5]))? +} + +test "strip_prefix" { + let v = [3, 4, 5] + let sv = v.strip_prefix([3]) + @assertion.assert_eq(v, [3, 4, 5])? + @assertion.assert_eq(sv, Some([4, 5]))? + @assertion.assert_eq(v.strip_prefix([4]), None)? +} + +test "strip_suffix" { + let v = [3, 4, 5] + let sv = v.strip_suffix([5]) + @assertion.assert_eq(v, [3, 4, 5])? + @assertion.assert_eq(sv, Some([3, 4]))? + @assertion.assert_eq(v.strip_suffix([4]), None)? +} + +test "search" { + let v = [3, 4, 5] + @assertion.assert_eq(v.search(3), Some(0))? + @assertion.assert_eq(v.search(4), Some(1))? + @assertion.assert_eq(v.search(5), Some(2))? + @assertion.assert_eq(v.search(6), None)? +} + +test "swap" { + let v = [3, 4, 5] + v.swap(0, 2) + @assertion.assert_eq(v, [5, 4, 3])? + v.swap(0, 2) + @assertion.assert_eq(v, [3, 4, 5])? +} + +test "remove" { + let v = [3, 4, 5] + @assertion.assert_eq(v.remove(1), 4)? + @assertion.assert_eq(v, [3, 5])? +} + +test "retain" { + let v = [3, 4, 5] + v.retain(fn(x) { x > 3 }) + @assertion.assert_eq(v, [4, 5])? +} + +test "resize" { + let v = [3, 4, 5] + v.resize(5, 0) + @assertion.assert_eq(v, [3, 4, 5, 0, 0])? + @assertion.assert_eq(v.length(), 5)? + let vv = [3, 4, 5] + vv.resize(2, 0) + @assertion.assert_eq(vv, [3, 4])? + @assertion.assert_eq(vv.length(), 2)? +} + +test "insert" { + let v = [3, 4, 5] + v.insert(1, 6) + @assertion.assert_eq(v, [3, 6, 4, 5])? + @assertion.assert_eq(v.length(), 4)? + @assertion.assert_eq(v.capacity(), 6)? +} + +test "fill" { + let v = with_capacity(3) + v.fill(3) + @assertion.assert_eq(v, [3, 3, 3])? + @assertion.assert_eq(v.length(), 3)? +} + +test "fill_with" { + let v = with_capacity(3) + v.fill_with(fn() { 3 }) + @assertion.assert_eq(v, [3, 3, 3])? + @assertion.assert_eq(v.length(), 3)? +} + +test "flatten" { + let v = [[3, 4], [5, 6]] + let vv = v.flatten() + @assertion.assert_eq(vv, [3, 4, 5, 6])? + @assertion.assert_eq(vv.length(), 4)? +} + +test "repeat" { + let v = [3, 4] + let vv = v.repeat(2) + @assertion.assert_eq(vv, [3, 4, 3, 4])? + @assertion.assert_eq(vv.length(), 4)? +} + +test "join" { + let v = [[3, 4], [5, 6]] + let vv = v.join(0) + @assertion.assert_eq(vv, [3, 4, 0, 5, 6])? + @assertion.assert_eq(vv.length(), 5)? +} + +test "fold_left" { + let sum = [1, 2, 3, 4, 5].fold_left( + init=0, + fn { sum, elem => sum + elem }, + ) + @assertion.assert_eq(sum, 15)? +} + +test "fold_right" { + let sum = [1, 2, 3, 4, 5].fold_right( + init=0, + fn { sum, elem => sum + elem }, + ) + @assertion.assert_eq(sum, 15)? +} + +test "fold_lefti" { + let sum = [1, 2, 3, 4, 5].fold_lefti( + init=0, + fn { index, sum, _elem => sum + index }, + ) + @assertion.assert_eq(sum, 10)? +} + +test "fold_righti" { + let sum = [1, 2, 3, 4, 5].fold_righti( + init=0, + fn { index, sum, _elem => sum + index }, + ) + @assertion.assert_eq(sum, 10)? +} + +test "dedup" { + let v = [3, 4, 4, 5, 5, 5] + v.dedup() + @assertion.assert_eq(v, [3, 4, 5])? + @assertion.assert_eq(v.length(), 3)? +} + +test "extract_if" { + let v = [3, 4, 5] + let vv = v.extract_if(fn(x) { x > 3 }) + @assertion.assert_eq(v, [3])? + @assertion.assert_eq(v.length(), 1)? + @assertion.assert_eq(vv, [4, 5])? +} + +test "chunks" { + let v = [1, 2, 3, 4, 5, 6, 7, 8, 9] + let chunks = v.chunks(3) + let chunks2 = v.chunks(2) + @assertion.assert_eq( + chunks, + [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + )? + @assertion.assert_eq( + chunks2, + [[1, 2], [3, 4], [5, 6], [7, 8], [9]], + )? +} + +test "chunk_by" { + let v = [1, 1, 2, 3, 2, 3, 2, 3, 4] + let chunks = v.chunk_by(fn(x, y) { x <= y }) + @assertion.assert_eq(chunks[0], [1, 1, 2, 3])? + @assertion.assert_eq(chunks[1], [2, 3])? + @assertion.assert_eq(chunks[2], [2, 3, 4])? +} + +test "split" { + let v = [1, 0, 2, 0, 3, 0, 4] + let chunks = v.split(fn(x) { x == 0 }) + @assertion.assert_eq(chunks, [[1], [2], [3], [4]])? + @assertion.assert_eq(chunks.length(), 4)? } test "to_list" { - { - let ls : List[Int] = ([] : Array[_]).to_list() - @assertion.assert_eq(ls, Nil)? - } - { - let ls = ([3] : Array[_]).to_list() - @assertion.assert_eq(ls, Cons(3, Nil))? - } - let ls = ([1, 2, 3, 4, 5] : Array[_]).to_list() + let ls = [1, 2, 3, 4, 5].to_list() @assertion.assert_eq(ls, Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil))))))? } + +test "reserve_capacity" { + let v = [1] + v.reserve_capacity(10) + @assertion.assert_true(v.capacity() == 10)? +} + +test "reserve_capacity_2" { + let v = [1, 2, 3, 4, 5] + v.reserve_capacity(3) + @assertion.assert_true(v.capacity() == 5)? +} + +test "shrink_to_fit" { + let v = with_capacity(10) + v.push(1) + v.push(2) + v.push(3) + @assertion.assert_true(v.capacity() == 10)? + v.shrink_to_fit() + @assertion.assert_true(v.capacity() == 3)? +} + +test "shrink_to_fit_2" { + let v = [1, 2, 3] + @assertion.assert_true(v.capacity() == 3)? + v.shrink_to_fit() + @assertion.assert_true(v.capacity() == 3)? +} + +test "as_iter" { + let buf = Buffer::make(5) + [1, 2, 3, 4, 5].as_iter().iter( + fn { x => buf.write_string(x.to_string()) }, + ) + inspect(buf, content="12345")? + buf.reset() + [1, 2, 3, 4, 5].as_iter().take(2).iter( + fn { x => buf.write_string(x.to_string()) }, + ) + inspect(buf, content="12")? +} diff --git a/array/moon.pkg.json b/array/moon.pkg.json index 1eb607e2..1cece7ea 100644 --- a/array/moon.pkg.json +++ b/array/moon.pkg.json @@ -1,10 +1,8 @@ { "import": [ "moonbitlang/core/builtin", - "moonbitlang/core/assertion", - "moonbitlang/core/coverage", - "moonbitlang/core/vec", "moonbitlang/core/iter" - ] + ], + "test_import": ["moonbitlang/core/assertion"] } diff --git a/array/sort.mbt b/array/sort.mbt index 03276d5e..9de0a7a5 100644 --- a/array/sort.mbt +++ b/array/sort.mbt @@ -12,201 +12,27 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Sorts the array -/// -/// It's an stable sort(it will not reorder equal elements). The time complexity is *O*(*n* \* log(*n*)) in the worst case. -/// -/// # Example -/// -/// ``` -/// let arr = [5, 4, 3, 2, 1] -/// arr.stable_sort() -/// debug(arr) //output: [1, 2, 3, 4, 5] -/// ``` -pub fn stable_sort[T : Compare](self : Array[T]) -> Unit { - timsort({ array: self, start: 0, end: self.length() }) -} - -struct TimSortRun { - len : Int - start : Int -} - -/// The algorithm identifies strictly descending and non-descending subsequences, which are called -/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed -/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are -/// satisfied: -/// -/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` -/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` -/// -/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case. -fn timsort[T : Compare](arr : ArraySlice[T]) -> Unit { - // Slices of up to this length get sorted using insertion sort. - let max_insertion = 20 - - // Short arrays get sorted in-place via insertion sort to avoid allocations. - let len = arr.length() - if len <= max_insertion { - insertion_sort(arr) - } - let mut end = 0 - let mut start = 0 - let runs : @vec.Vec[TimSortRun] = @vec.Vec::new() - while end < len { - let (streak_end, was_reversed) = find_streak(arr.slice(start, arr.end)) - end += streak_end - if was_reversed { - arr.slice(start, end).reverse() - } - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - end = provide_sorted_batch(arr, start, end) - runs.push({ start, len: end - start }) - start = end - while true { - match collapse(runs, len) { - Some(r) => { - let left = runs[r] - let right = runs[r + 1] - merge(arr.slice(left.start, right.start + right.len), left.len) - runs[r + 1] = { start: left.start, len: left.len + right.len } - runs.remove(r) |> ignore - } - None => break - } - } - } -} - -fn insertion_sort[T : Compare](arr : ArraySlice[T]) -> Unit { - for i = 1, len = arr.length(); i < len; i = i + 1 { - for j = i; j > 0 && arr[j] < arr[j - 1]; j = j - 1 { - arr.swap(j, j - 1) - } - } -} - -/// Merges non-decreasing runs `arr[..mid]` and `arr[mid..]`. Copy `arr[mid..]` to buf and merge -/// `buf` and `arr[..mid]` to `arr[..]` -fn merge[T : Compare](arr : ArraySlice[T], mid : Int) -> Unit { - let buf_len = arr.length() - mid - let buf : Array[T] = Array::make(buf_len, arr[mid]) - for i = 0; i < buf.length(); i = i + 1 { - buf[i] = arr[mid + i] - } - let buf = { array: buf, start: 0, end: buf_len } - let buf_remaining = for p1 = mid - 1, p2 = buf_len - 1, p = mid + buf_len - 1; p1 >= - 0 && p2 >= 0; { - if arr[p1] > buf[p2] { - arr[p] = arr[p1] - continue p1 - 1, p2, p - 1 - } else { - arr[p] = buf[p2] - continue p1, p2 - 1, p - 1 - } - } else { - p2 - } - for i = buf_remaining; i >= 0; i = i - 1 { - arr[i] = buf[i] - } -} - -/// Finds a streak of presorted elements starting at the beginning of the slice. Returns the first -/// value that is not part of said streak, and a bool denoting whether the streak was reversed. -/// Streaks can be increasing or decreasing. -fn find_streak[T : Compare](arr : ArraySlice[T]) -> (Int, Bool) { - let len = arr.length() - if len < 2 { - return (len, false) - } - let assume_reverse = arr[1] < arr[0] - if assume_reverse { - for end = 2 { - if end < len && arr[end] < arr[end - 1] { - continue end + 1 - } else { - break (end, true) - } - } - } else { - for end = 2 { - if end < len && arr[end] >= arr[end - 1] { - continue end + 1 - } else { - break (end, false) - } - } - } -} - -fn provide_sorted_batch[T : Compare]( - arr : ArraySlice[T], - start : Int, - end : Int -) -> Int { - let len = arr.length() - // This value is a balance between least comparisons and best performance, as - // influenced by for example cache locality. - let min_insertion_run = 10 - // Insert some more elements into the run if it's too short. Insertion sort is faster than - // merge sort on short sequences, so this significantly improves performance. - let start_end_diff = end - start - if start_end_diff < min_insertion_run && end < len { - // v[start_found..end] are elements that are already sorted in the input. We want to extend - // the sorted region to the left, so we push up MIN_INSERTION_RUN - 1 to the right. Which is - // more efficient that trying to push those already sorted elements to the left. - let sort_end = minimum(len, start + min_insertion_run) - insertion_sort(arr.slice(start, sort_end)) - sort_end - } else { - end - } -} - -// TimSort is infamous for its buggy implementations, as described here: -// http://envisage-project.eu/timsort-specification-and-verification/ -// -// This function correctly checks invariants for the top four runs. Additionally, if the top -// run starts at index 0, it will always demand a merge operation until the stack is fully -// collapsed, in order to complete the sort. -fn collapse(runs : @vec.Vec[TimSortRun], stop : Int) -> Option[Int] { - let n : Int = runs.length() - if n >= 2 && (runs[n - 1].start + runs[n - 1].len == stop || runs[n - 2].len <= - runs[n - 1].len || n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len || - n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len) { - if n >= 3 && runs[n - 3].len < runs[n - 1].len { - Some(n - 3) - } else { - Some(n - 2) - } - } else { - None - } -} - -/// Sorts the array +/// Sorts the vector in place. /// /// It's an in-place, unstable sort(it will reorder equal elements). The time complexity is O(n log n) in the worst case. /// /// # Example /// /// ``` -/// let arr = [5, 4, 3, 2, 1] +/// let arr = Array::[5, 4, 3, 2, 1] /// arr.sort() -/// debug(arr) //output: [1, 2, 3, 4, 5] +/// debug(arr) //output: Array::[1, 2, 3, 4, 5] /// ``` pub fn sort[T : Compare](self : Array[T]) -> Unit { quick_sort( - { array: self, start: 0, end: self.length() }, + self.op_as_view(start=0, end=self.length()), None, get_limit(self.length()), ) } fn quick_sort[T : Compare]( - arr : ArraySlice[T], + arr : ArrayView[T], pred : Option[T], limit : Int ) -> Unit { @@ -252,13 +78,13 @@ fn quick_sort[T : Compare]( while i < len && pred == arr[i] { i = i + 1 } - arr = arr.slice(i, len) + arr = arr.op_as_view(start=i, end=len) continue } _ => () } - let left = arr.slice(0, pivot) - let right = arr.slice(pivot + 1, len) + let left = arr.op_as_view(start=0, end=pivot) + let right = arr.op_as_view(start=pivot + 1, end=len) // Reduce the stack depth by only call quick_sort on the smaller partition. if left.length() < right.length() { quick_sort(left, pred, limit) @@ -286,10 +112,7 @@ fn get_limit(len : Int) -> Int { /// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). /// /// Returns whether the array is sorted. -fn try_bubble_sort[T : Compare]( - arr : ArraySlice[T], - ~max_tries : Int = 8 -) -> Bool { +fn try_bubble_sort[T : Compare](arr : ArrayView[T], ~max_tries : Int = 8) -> Bool { let mut tries = 0 for i = 1; i < arr.length(); i = i + 1 { let mut sorted = true @@ -312,7 +135,7 @@ fn try_bubble_sort[T : Compare]( /// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). /// /// Returns whether the array is sorted. -fn bubble_sort[T : Compare](arr : ArraySlice[T]) -> Unit { +fn bubble_sort[T : Compare](arr : ArrayView[T]) -> Unit { for i = 1; i < arr.length(); i = i + 1 { for j = i; j > 0 && arr[j - 1] > arr[j]; j = j - 1 { arr.swap(j, j - 1) @@ -320,17 +143,7 @@ fn bubble_sort[T : Compare](arr : ArraySlice[T]) -> Unit { } } -test "try_bubble_sort" { - let arr : Array[_] = [8, 7, 6, 5, 4, 3, 2, 1] - let sorted = try_bubble_sort({ array: arr, start: 0, end: 8 }) - @assertion.assert_eq(sorted, true)? - @assertion.assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8])? -} - -fn partition[T : Compare]( - arr : ArraySlice[T], - pivot_index : Int -) -> (Int, Bool) { +fn partition[T : Compare](arr : ArrayView[T], pivot_index : Int) -> (Int, Bool) { arr.swap(pivot_index, arr.length() - 1) let pivot = arr[arr.length() - 1] let mut i = 0 @@ -353,7 +166,7 @@ fn partition[T : Compare]( /// It avoids worst case performance by choosing a pivot that is likely to be close to the median. /// /// Returns the pivot index and whether the array is likely sorted. -fn choose_pivot[T : Compare](arr : ArraySlice[T]) -> (Int, Bool) { +fn choose_pivot[T : Compare](arr : ArrayView[T]) -> (Int, Bool) { let len = arr.length() let use_median_of_medians = 50 let max_swaps = 4 * 3 @@ -390,18 +203,18 @@ fn choose_pivot[T : Compare](arr : ArraySlice[T]) -> (Int, Bool) { } } -fn heap_sort[T : Compare](arr : ArraySlice[T]) -> Unit { +fn heap_sort[T : Compare](arr : ArrayView[T]) -> Unit { let len = arr.length() for i = len / 2 - 1; i >= 0; i = i - 1 { sift_down(arr, i) } for i = len - 1; i > 0; i = i - 1 { arr.swap(0, i) - sift_down(arr.slice(0, i), 0) + sift_down(arr.op_as_view(start=0, end=i), 0) } } -fn sift_down[T : Compare](arr : ArraySlice[T], index : Int) -> Unit { +fn sift_down[T : Compare](arr : ArrayView[T], index : Int) -> Unit { let mut index = index let len = arr.length() let mut child = index * 2 + 1 @@ -417,90 +230,3 @@ fn sift_down[T : Compare](arr : ArraySlice[T], index : Int) -> Unit { child = index * 2 + 1 } } - -fn test_sort(f : (Array[Int]) -> Unit) -> Result[Unit, String] { - let arr : Array[_] = [5, 4, 3, 2, 1] - f(arr) - @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? - let arr : Array[_] = [5, 5, 5, 5, 1] - f(arr) - @assertion.assert_eq(arr, [1, 5, 5, 5, 5])? - let arr : Array[_] = [1, 2, 3, 4, 5] - f(arr) - @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? - { - let arr = Array::make(1000, 0) - for i = 0; i < 1000; i = i + 1 { - arr[i] = 1000 - i - 1 - } - for i = 10; i < 1000; i = i + 10 { - arr.swap(i, i - 1) - } - f(arr) - let expected = Array::make(1000, 0) - for i = 0; i < 1000; i = i + 1 { - expected[i] = i - } - @assertion.assert_eq(arr, expected)? - } - Ok(()) -} - -test "heap_sort" { - test_sort(fn(arr) { heap_sort({ array: arr, start: 0, end: arr.length() }) })? -} - -test "bubble_sort" { - test_sort( - fn(arr) { bubble_sort({ array: arr, start: 0, end: arr.length() }) }, - )? -} - -test "sort" { - test_sort(fn(arr) { arr.sort() })? -} - -test "stable_sort" { - let arr : Array[_] = [5, 1, 3, 4, 2] - arr.stable_sort() - @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? - let arr = Array::make(1000, 0) - for i = 0; i < 1000; i = i + 1 { - arr[i] = 1000 - i - 1 - } - for i = 10; i < 1000; i = i + 10 { - arr.swap(i, i - 1) - } - arr.stable_sort() - let expected = Array::make(1000, 0) - for i = 0; i < 1000; i = i + 1 { - expected[i] = i - } - @assertion.assert_eq(arr, expected)? -} - -pub fn is_sorted[T : Compare](arr : Array[T]) -> Bool { - for i = 1; i < arr.length(); i = i + 1 { - if arr[i] < arr[i - 1] { - break false - } - } else { - true - } -} - -test "stable_sort_complex" { - let run_lens = [86, 64, 21, 20, 22] - let total_len = run_lens.fold_left(init=0, fn { acc, x => acc + x }) - let arr = Array::make(total_len, 0) - let mut index = 0 - for i = 0, len = run_lens.length(); i < len; i = i + 1 { - for j = 0; j < run_lens[i]; j = j + 1 { - arr[index] = j - index += 1 - } - } - @assertion.assert_false(is_sorted(arr))? - arr.stable_sort() - @assertion.assert_true(is_sorted(arr))? -} diff --git a/array/sort_by.mbt b/array/sort_by.mbt index a7a1f020..4192584f 100644 --- a/array/sort_by.mbt +++ b/array/sort_by.mbt @@ -12,40 +12,40 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Sorts the array with a key extraction function. +/// Sorts the vector with a key extraction function. /// /// It's an in-place, unstable sort(it will reorder equal elements). The time complexity is O(n log n) in the worst case. /// /// # Example /// /// ``` -/// let arr = [5, 3, 2, 4, 1] +/// let arr = Array::[5, 3, 2, 4, 1] /// arr.sort_by_key(fn (x) {-x}) -/// debug(arr) //output: [5, 4, 3, 2, 1] +/// debug(arr) //output: Array::[5, 4, 3, 2, 1] /// ``` pub fn sort_by_key[T, K : Compare](self : Array[T], map : (T) -> K) -> Unit { quick_sort_by( - { array: self, start: 0, end: self.length() }, + self.op_as_view(start=0, end=self.length()), fn(a, b) { map(a).compare(map(b)) }, None, get_limit(self.length()), ) } -/// Sorts the array with a custom comparison function. +/// Sorts the vector with a custom comparison function. /// /// It's an in-place, unstable sort(it will reorder equal elements). The time complexity is O(n log n) in the worst case. /// /// # Example /// /// ``` -/// let arr = [5, 3, 2, 4, 1] +/// let arr = Array::[5, 3, 2, 4, 1] /// arr.sort_by(fn (a, b) { a - b }) -/// debug(arr) //output: [1, 2, 3, 4, 5] +/// debug(arr) //output: Array::[1, 2, 3, 4, 5] /// ``` pub fn sort_by[T](self : Array[T], cmp : (T, T) -> Int) -> Unit { quick_sort_by( - { array: self, start: 0, end: self.length() }, + self.op_as_view(start=0, end=self.length()), cmp, None, get_limit(self.length()), @@ -53,7 +53,7 @@ pub fn sort_by[T](self : Array[T], cmp : (T, T) -> Int) -> Unit { } fn quick_sort_by[T]( - arr : ArraySlice[T], + arr : ArrayView[T], cmp : (T, T) -> Int, pred : Option[T], limit : Int @@ -100,13 +100,13 @@ fn quick_sort_by[T]( while i < len && cmp(pred, arr[i]) == 0 { i = i + 1 } - arr = arr.slice(i, len) + arr = arr.op_as_view(start=i, end=len) continue } _ => () } - let left = arr.slice(0, pivot) - let right = arr.slice(pivot + 1, len) + let left = arr.op_as_view(start=0, end=pivot) + let right = arr.op_as_view(start=pivot + 1, end=len) // Reduce the stack depth by only call quick_sort on the smaller partition. if left.length() < right.length() { quick_sort_by(left, cmp, pred, limit) @@ -125,7 +125,7 @@ fn quick_sort_by[T]( /// /// Returns whether the array is sorted. fn try_bubble_sort_by[T]( - arr : ArraySlice[T], + arr : ArrayView[T], cmp : (T, T) -> Int, ~max_tries : Int = 8 ) -> Bool { @@ -151,7 +151,7 @@ fn try_bubble_sort_by[T]( /// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). /// /// Returns whether the array is sorted. -fn bubble_sort_by[T](arr : ArraySlice[T], cmp : (T, T) -> Int) -> Unit { +fn bubble_sort_by[T](arr : ArrayView[T], cmp : (T, T) -> Int) -> Unit { for i = 1; i < arr.length(); i = i + 1 { for j = i; j > 0 && cmp(arr[j - 1], arr[j]) > 0; j = j - 1 { arr.swap(j, j - 1) @@ -159,18 +159,8 @@ fn bubble_sort_by[T](arr : ArraySlice[T], cmp : (T, T) -> Int) -> Unit { } } -test "try_bubble_sort" { - let arr : Array[_] = [8, 7, 6, 5, 4, 3, 2, 1] - let sorted = try_bubble_sort_by( - { array: arr, start: 0, end: 8 }, - fn(a, b) { a - b }, - ) - @assertion.assert_eq(sorted, true)? - @assertion.assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8])? -} - fn partition_by[T]( - arr : ArraySlice[T], + arr : ArrayView[T], cmp : (T, T) -> Int, pivot_index : Int ) -> (Int, Bool) { @@ -196,7 +186,7 @@ fn partition_by[T]( /// It avoids worst case performance by choosing a pivot that is likely to be close to the median. /// /// Returns the pivot index and whether the array is likely sorted. -fn choose_pivot_by[T](arr : ArraySlice[T], cmp : (T, T) -> Int) -> (Int, Bool) { +fn choose_pivot_by[T](arr : ArrayView[T], cmp : (T, T) -> Int) -> (Int, Bool) { let len = arr.length() let use_median_of_medians = 50 let max_swaps = 4 * 3 @@ -233,22 +223,18 @@ fn choose_pivot_by[T](arr : ArraySlice[T], cmp : (T, T) -> Int) -> (Int, Bool) { } } -fn heap_sort_by[T](arr : ArraySlice[T], cmp : (T, T) -> Int) -> Unit { +fn heap_sort_by[T](arr : ArrayView[T], cmp : (T, T) -> Int) -> Unit { let len = arr.length() for i = len / 2 - 1; i >= 0; i = i - 1 { sift_down_by(arr, i, cmp) } for i = len - 1; i > 0; i = i - 1 { arr.swap(0, i) - sift_down_by(arr.slice(0, i), 0, cmp) + sift_down_by(arr.op_as_view(start=0, end=i), 0, cmp) } } -fn sift_down_by[T]( - arr : ArraySlice[T], - index : Int, - cmp : (T, T) -> Int -) -> Unit { +fn sift_down_by[T](arr : ArrayView[T], index : Int, cmp : (T, T) -> Int) -> Unit { let mut index = index let len = arr.length() let mut child = index * 2 + 1 @@ -264,35 +250,3 @@ fn sift_down_by[T]( child = index * 2 + 1 } } - -test "heap_sort" { - test_sort( - fn(arr) { - heap_sort_by( - { array: arr, start: 0, end: arr.length() }, - fn(a, b) { a - b }, - ) - }, - )? -} - -test "bubble_sort" { - test_sort( - fn(arr) { - bubble_sort_by( - { array: arr, start: 0, end: arr.length() }, - fn(a, b) { a - b }, - ) - }, - )? -} - -test "sort" { - test_sort(fn(arr) { arr.sort() })? -} - -test "sort_by" { - let arr = [5, 1, 3, 4, 2] - arr.sort_by_key(fn(x) { -x }) - @assertion.assert_eq(arr, [5, 4, 3, 2, 1])? -} diff --git a/vec/sort_by_test.mbt b/array/sort_by_test.mbt similarity index 87% rename from vec/sort_by_test.mbt rename to array/sort_by_test.mbt index 8fe43457..c6b941ac 100644 --- a/vec/sort_by_test.mbt +++ b/array/sort_by_test.mbt @@ -13,13 +13,13 @@ // limitations under the License. test "try_bubble_sort" { - let arr = Vec::[8, 7, 6, 5, 4, 3, 2, 1] + let arr = [8, 7, 6, 5, 4, 3, 2, 1] let sorted = try_bubble_sort_by( arr.op_as_view(start=0, end=8), fn(a, b) { a - b }, ) @assertion.assert_eq(sorted, true)? - @assertion.assert_eq(arr, Vec::[1, 2, 3, 4, 5, 6, 7, 8])? + @assertion.assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8])? } test "heap_sort" { @@ -45,9 +45,9 @@ test "bubble_sort" { } test "sort_by_key" { - let arr = Vec::[5, 1, 3, 4, 2] + let arr = [5, 1, 3, 4, 2] arr.sort_by_key(fn(x) { -x }) - @assertion.assert_eq(arr, Vec::[5, 4, 3, 2, 1])? + @assertion.assert_eq(arr, [5, 4, 3, 2, 1])? } test "sort_by" { diff --git a/vec/sort_test.mbt b/array/sort_test.mbt similarity index 71% rename from vec/sort_test.mbt rename to array/sort_test.mbt index fba772b4..e858a2a7 100644 --- a/vec/sort_test.mbt +++ b/array/sort_test.mbt @@ -12,18 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -fn test_sort(f : (Vec[Int]) -> Unit) -> Result[Unit, String] { - let arr = Vec::[5, 4, 3, 2, 1] +fn test_sort(f : (Array[Int]) -> Unit) -> Result[Unit, String] { + let arr = [5, 4, 3, 2, 1] f(arr) - @assertion.assert_eq(arr, Vec::[1, 2, 3, 4, 5])? - let arr = Vec::[5, 5, 5, 5, 1] + @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? + let arr = [5, 5, 5, 5, 1] f(arr) - @assertion.assert_eq(arr, Vec::[1, 5, 5, 5, 5])? - let arr = Vec::[1, 2, 3, 4, 5] + @assertion.assert_eq(arr, [1, 5, 5, 5, 5])? + let arr = [1, 2, 3, 4, 5] f(arr) - @assertion.assert_eq(arr, Vec::[1, 2, 3, 4, 5])? + @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? { - let arr = Vec::with_capacity(1000) + let arr = with_capacity(1000) for i = 0; i < 1000; i = i + 1 { arr.push(1000 - i - 1) } @@ -31,7 +31,7 @@ fn test_sort(f : (Vec[Int]) -> Unit) -> Result[Unit, String] { arr.swap(i, i - 1) } f(arr) - let expected = Vec::with_capacity(1000) + let expected = with_capacity(1000) for i = 0; i < 1000; i = i + 1 { expected.push(i) } @@ -41,18 +41,18 @@ fn test_sort(f : (Vec[Int]) -> Unit) -> Result[Unit, String] { } test "try_bubble_sort" { - let arr = Vec::[8, 7, 6, 5, 4, 3, 2, 1] + let arr = [8, 7, 6, 5, 4, 3, 2, 1] let sorted = try_bubble_sort(arr.op_as_view(start=0, end=8)) @assertion.assert_eq(sorted, true)? - @assertion.assert_eq(arr, Vec::[1, 2, 3, 4, 5, 6, 7, 8])? + @assertion.assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8])? } test "heap_sort" { - test_sort(fn(arr) { heap_sort(arr.op_as_view(start=0, end=arr.len)) })? + test_sort(fn(arr) { heap_sort(arr.op_as_view(start=0, end=arr.length())) })? } test "bubble_sort" { - test_sort(fn(arr) { bubble_sort(arr.op_as_view(start=0, end=arr.len)) })? + test_sort(fn(arr) { bubble_sort(arr.op_as_view(start=0, end=arr.length())) })? } test "sort" { diff --git a/array/view.mbt b/array/view.mbt new file mode 100644 index 00000000..2dee681c --- /dev/null +++ b/array/view.mbt @@ -0,0 +1,21 @@ +// Copyright 2024 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub fn reverse[T](self : ArrayView[T]) -> Unit { + let mid_len = self.length() / 2 + for i = 0; i < mid_len; i = i + 1 { + let j = self.length() - i - 1 + self.swap(i, j) + } +} diff --git a/vec/view_test.mbt b/array/view_test.mbt similarity index 96% rename from vec/view_test.mbt rename to array/view_test.mbt index 6081f026..e7e2e439 100644 --- a/vec/view_test.mbt +++ b/array/view_test.mbt @@ -13,7 +13,7 @@ // limitations under the License. test "slice" { - let v = Vec::[1, 2, 3, 4, 5] + let v = [1, 2, 3, 4, 5] let s = v[1..4] @assertion.assert_eq(s.length(), 3)? @assertion.assert_eq(s[0], 2)? diff --git a/builtin/array.mbt b/builtin/array.mbt index 60fe98bd..caa2fbeb 100644 --- a/builtin/array.mbt +++ b/builtin/array.mbt @@ -12,17 +12,998 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub fn to_string[X : Show](self : Array[X]) -> String { - let mut acc = "[" +priv type UninitializedArray[T] FixedArray[UnsafeMaybeUninit[T]] + +/// An `Array` is a collection of values that supports random access and can +/// grow in size. +struct Array[T] { + mut buf : UninitializedArray[T] + mut len : Int +} + +fn UninitializedArray::make[T](size : Int) -> UninitializedArray[T] = "%make_array_maybe_uninit" + +fn op_get[T](self : UninitializedArray[T], index : Int) -> T = "%array_get" + +fn op_set[T](self : UninitializedArray[T], index : Int, value : T) = "%array_set" + +/// Converts the vector to a string. +pub fn to_string[T : Show](self : Array[T]) -> String { + if self.len == 0 { + return "[]" + } + let first = self.buf[0] + // CR: format issues + for i = 1, init = "[\(first)" { + if i >= self.len { + break "\(init)]" + } + let cur = self.buf[i] + continue i + 1, "\(init), \(cur)" + } +} + +/// Creates a new, empty vector. +pub fn Array::new[T]() -> Array[T] { + { buf: UninitializedArray::make(0), len: 0 } +} + +/// Creates a new, empty vector with a specified initial capacity. +pub fn Array::with_capacity[T](cap : Int) -> Array[T] { + { buf: UninitializedArray::make(cap), len: 0 } +} + +/// Creates a new vector from an array. +pub fn Array::from_fixed_array[T](arr : FixedArray[T]) -> Array[T] { + let len = arr.length() + let buf = UninitializedArray::make(len) + for i = 0; i < len; i = i + 1 { + buf[i] = arr[i] + } + { buf, len } +} + +pub fn Array::make[T](len: Int, elem: T) -> Array[T] { + let buf = UninitializedArray::make(len) + for i = 0; i < len; i = i + 1 { + buf[i] = elem + } + { buf, len } +} + +/// Returns the number of elements in the vector. +pub fn length[T](self : Array[T]) -> Int { + self.len +} + +/// Returns the total number of elements the vector can hold without reallocating. +pub fn capacity[T](self : Array[T]) -> Int { + self.buf.0.length() +} + +/// Reallocate the vector with a new capacity. +fn realloc[T](self : Array[T]) -> Unit { + let old_cap = self.len + let new_cap = if old_cap == 0 { 8 } else { old_cap * 2 } + let new_buf = UninitializedArray::make(new_cap) + for i = 0; i < old_cap; i = i + 1 { + new_buf[i] = self.buf[i] + } + self.buf = new_buf +} + +/// Retrieves the element at the specified index from the vector. +/// +/// # Example +/// ``` +/// let v = Array::new() +/// v.push(3) +/// println(v[0]) // 3 +/// ``` +/// @alert unsafe "Panic if index is out of bounds" +pub fn op_get[T](self : Array[T], index : Int) -> T { + if index < 0 || index >= self.len { + let len = self.len + abort( + "index out of bounds: the len is from 0 to \(len) but the index is \(index)", + ) + } + self.buf[index] +} + +/// Retrieves the element at the specified index from the vector, or `None` if index is out of bounds +/// +/// # Example +/// ``` +/// let v = Array::new() +/// v.push(3) +/// println(v.get(0)) // Some(3) +/// ``` +pub fn get[T](self : Array[T], index : Int) -> Option[T] { + if index < 0 || index >= self.len { + return None + } + Some(self.buf[index]) +} + +/// Sets the value of the element at the specified index. +/// +/// # Example +/// ``` +/// let v = Array::new() +/// v.push(3) +/// println(v[0]) // 3 +/// ``` +/// @alert unsafe "Panic if index is out of bounds." +pub fn op_set[T](self : Array[T], index : Int, value : T) -> Unit { + if index < 0 || index >= self.len { + let len = self.len + abort( + "index out of bounds: the len is from 0 to \(len) but the index is \(index)", + ) + } + self.buf[index] = value +} + +/// Compares two vectors for equality. +pub fn op_equal[T : Eq](self : Array[T], other : Array[T]) -> Bool { + if self.len != other.len { + return false + } + for i = 0 { + // CR: format issue + if i >= self.len { + break true + } + if self[i] != other[i] { + break false + } + continue i + 1 + } +} + +fn deep_clone[T](vec : Array[T]) -> Array[T] { + let result = Array::{ buf: UninitializedArray::make(vec.len), len: vec.len } + for i = 0; i < vec.len; i = i + 1 { + result[i] = vec[i] + } + result +} + +pub fn op_add[T](self : Array[T], other : Array[T]) -> Array[T] { + if self.len == 0 { + deep_clone(other) + } else if other.len == 0 { + deep_clone(self) + } else { + let result = Array::{ + buf: UninitializedArray::make(self.len + other.len), + len: self.len + other.len, + } + for i = 0; i < self.len; i = i + 1 { + result[i] = self[i] + } + for i = 0; i < other.len; i = i + 1 { + result[i + self.len] = other[i] + } + result + } +} + +/// Removes the last element from a vector and returns it, or `None` if it is empty. +/// +/// # Example +/// ``` +/// let v = Array::[1, 2, 3] +/// v.pop() +/// ``` +pub fn pop[T](self : Array[T]) -> Option[T] { + if self.len == 0 { + None + } else { + self.len -= 1 + // TODO: should fill the element with a dummy slot to avoid memory leak? + Some(self.buf[self.len]) + } +} + +/// Removes the last element from a vector and returns it. +/// +/// # Example +/// ``` +/// let v = Array::[1, 2, 3] +/// v.pop_exn() // 3 +/// ``` +/// @alert unsafe "Panic if the vector is empty." +pub fn pop_exn[T](self : Array[T]) -> T { + if self.len == 0 { + abort("pop from an empty Array") + } + self.len -= 1 + self.buf[self.len] +} + +/// Adds an element to the end of the vector. +/// +/// If the vector is at capacity, it will be reallocated. +/// +/// # Example +/// ``` +/// let v = Array::new() +/// v.push(3) +/// ``` +pub fn push[T](self : Array[T], value : T) -> Unit { + if self.len == self.buf.0.length() { + self.realloc() + } + self.buf[self.len] = value + self.len += 1 +} + +/// Removes the specified range from the vector and returns it. +/// +/// This functions returns a vector range from `begin` to `end` `[begin, end)` +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// let vv = v.drain(1, 2) // vv = Array::[4], v = Array::[3, 5] +/// ``` +/// @alert unsafe "Panic if index is out of bounds." +pub fn drain[T](self : Array[T], begin : Int, end : Int) -> Array[T] { + if begin < 0 || begin >= self.len || end < 0 || end > self.len || begin > end { + let len = self.len + abort( + "index out of bounds: the len is \(len) but the index is (\(begin), \(end))", + ) + } + let num = end - begin + let v = Array::with_capacity(num) + for i = begin; i < end; i = i + 1 { + v.buf[i - begin] = self[i] + } + v.len = num + for i = end; i < self.len; i = i + 1 { + self.buf[i - num] = self.buf[i] + } + self.len -= num + v +} + +/// Appends all the elements of other vector into self +/// +/// # Example +/// ``` +/// let v1 = Array::[3, 4, 5] +/// let v2 = Array::[6, 7, 8] +/// v1.append(v2) +/// ``` +/// TODO: could be made more efficient +pub fn append[T](self : Array[T], other : Array[T]) -> Unit { + for i = 0; i < other.len; i = i + 1 { + self.push(other[i]) + } +} + +/// Iterates over the elements of the vector. +/// +/// # Example +/// ``` +/// let v = Array::with_capacity(3) +/// v.push(3) +/// v.push(4) +/// v.push(5) +/// let mut sum = 0 +/// v.iter(fn (x) {sum = sum + x}) +/// ``` +pub fn iter[T](self : Array[T], f : (T) -> Unit) -> Unit { + for i = 0; i < self.len; i = i + 1 { + f(self[i]) + } +} + +pub fn iter_rev[T](self : Array[T], f : (T) -> Unit) -> Unit { + for i = self.length() - 1; i >= 0; i = i - 1 { + f(self[i]) + } +} + +/// Iterates over the elements of the vector with index. +/// +/// # Example +/// ``` +/// let v = Array::with_capacity(3) +/// v.push(3) +/// v.push(4) +/// v.push(5) +/// let mut sum = 0 +/// v.iteri(fn (i, x) {sum = sum + x + i}) +/// ``` +pub fn iteri[T](self : Array[T], f : (Int, T) -> Unit) -> Unit { + for i = 0; i < self.len; i = i + 1 { + f(i, self[i]) + } +} + +pub fn iter_revi[T](self : Array[T], f : (Int, T) -> Unit) -> Unit { + let len = self.length() + for i = 0; i < len; i = i + 1 { + f(i, self[len - i - 1]) + } +} + +/// Clears the vector, removing all values. +/// +/// This method has no effect on the allocated capacity of the vector, only setting the length to 0. +/// +/// # Example +/// ``` +/// let v = Array::from_array([3, 4, 5]) +/// v.clear() +/// ``` +pub fn clear[T](self : Array[T]) -> Unit { + self.len = 0 +} + +/// Maps a function over the elements of the vector. +/// +/// # Example +/// ``` +/// let v = Array::from_array([3, 4, 5]) +/// v.map(fn (x) {x + 1}) +/// ``` +pub fn map[T, U](self : Array[T], f : (T) -> U) -> Array[U] { + if self.length() == 0 { + return Array::{ buf: UninitializedArray::make(0), len: 0 } + } + let buf : UninitializedArray[U] = UninitializedArray::make(self.length()) + for i = 0; i < self.length(); i = i + 1 { + buf[i] = f(self[i]) + } + Array::{ buf, len: self.length() } +} + +/// Maps a function over the elements of the vector in place. +/// +/// # Example +/// ``` +/// let v = Array::from_array([3, 4, 5]) +/// v.map_inplace(fn (x) {x + 1}) +/// ``` +pub fn map_inplace[T](self : Array[T], f : (T) -> T) -> Unit { + for i = 0; i < self.length(); i = i + 1 { + self[i] = f(self[i]) + } +} + +/// Maps a function over the elements of the vector with index. +/// +/// # Example +/// ``` +/// let v = Array::from_array([3, 4, 5]) +/// v.mapi(fn (i, x) {x + i}) +/// ``` +pub fn mapi[T, U](self : Array[T], f : (Int, T) -> U) -> Array[U] { + if self.length() == 0 { + return Array::{ buf: UninitializedArray::make(0), len: 0 } + } + let buf : UninitializedArray[U] = UninitializedArray::make(self.length()) for i = 0; i < self.length(); i = i + 1 { - if i > 0 { - acc = acc + ", " + buf[i] = f(i, self[i]) + } + Array::{ buf, len: self.length() } +} + +/// Maps a function over the elements of the vector with index in place. +/// +/// # Example +/// ``` +/// let v = Array::from_array([3, 4, 5]) +/// v.mapi_inplace(fn (i, x) {x + i}) +/// ``` +pub fn mapi_inplace[T](self : Array[T], f : (Int, T) -> T) -> Unit { + for i = 0; i < self.length(); i = i + 1 { + self[i] = f(i, self[i]) + } +} + +/// Test if the vector is empty. +/// +/// # Example +/// ``` +/// let v = Array::new() +/// v.is_empty() +/// ``` +pub fn is_empty[T](self : Array[T]) -> Bool { + self.len == 0 +} + +/// Test if the vector is sorted. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.is_sorted() // true +/// ``` +pub fn is_sorted[T : Compare](self : Array[T]) -> Bool { + for i = 1 { + if i >= self.len { + break true + } + if self[i - 1] > self[i] { + break false + } + continue i + 1 + } +} + +/// Reverses the order of elements in the slice, in place. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.reverse() +/// ``` +pub fn reverse[T](self : Array[T]) -> Unit { + for i = 0; i < self.len / 2; i = i + 1 { + let temp = self.buf[i] + self.buf[i] = self.buf[self.len - i - 1] + self.buf[self.len - i - 1] = temp + } +} + +/// Split the vector into two at the given index. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// let (v1, v2) = v.split_at(1) +/// ``` +/// TODO: perf could be optimized +/// @alert unsafe "Panic if index is out of bounds." +pub fn split_at[T](self : Array[T], index : Int) -> (Array[T], Array[T]) { + if index < 0 || index >= self.len { + let len = self.len + abort( + "index out of bounds: the len is from 0 to \(len) but the index is \(index)", + ) + } + let v1 = Array::with_capacity(index) + let v2 = Array::with_capacity(self.len - index) + for i = 0; i < index; i = i + 1 { + v1.push(self.buf[i]) + } + for i = index; i < self.len; i = i + 1 { + v2.push(self.buf[i]) + } + (v1, v2) +} + +/// Checks if the vector contains an element. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.contains(3) +/// ``` +pub fn contains[T : Eq](self : Array[T], value : T) -> Bool { + for i = 0; i < self.len; i = i + 1 { + if self.buf[i] == value { + break true + } + } else { + false + } +} + +/// Check if the vector starts with a given prefix. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.starts_with(Array::[3, 4]) +/// ``` +pub fn starts_with[T : Eq](self : Array[T], prefix : Array[T]) -> Bool { + if prefix.len > self.len { + return false + } + for i = 0; i < prefix.len; i = i + 1 { + if self.buf[i] != prefix.buf[i] { + break false + } + } else { + true + } +} + +/// Check if the vector ends with a given suffix. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.ends_with(Array::[5]) +/// ``` +pub fn ends_with[T : Eq](self : Array[T], suffix : Array[T]) -> Bool { + if suffix.len > self.len { + return false + } + for i = 0; i < suffix.len; i = i + 1 { + if self.buf[self.len - suffix.len + i] != suffix.buf[i] { + break false + } + } else { + true + } +} + +/// Strip a prefix from the vector. +/// +/// If the vector starts with the prefix, return the vector after the prefix, otherwise return None. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.strip_prefix(Array::[3]) // Some(Array::[4, 5]) +/// ``` +pub fn strip_prefix[T : Eq](self : Array[T], prefix : Array[T]) -> Option[Array[T]] { + if self.starts_with(prefix) { + let v = Array::with_capacity(self.len - prefix.len) + for i = prefix.len; i < self.len; i = i + 1 { + v.buf[i - prefix.len] = self.buf[i] + } + v.len = self.len - prefix.len + Some(v) + } else { + None + } +} + +/// Strip a suffix from the vector. +/// +/// If the vector ends with the suffix, return the vector before the suffix, otherwise return None. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.strip_suffix(Array::[5]) // Some(Array::[3, 4]) +/// ``` +pub fn strip_suffix[T : Eq](self : Array[T], suffix : Array[T]) -> Option[Array[T]] { + if self.ends_with(suffix) { + let v = Array::with_capacity(self.len - suffix.len) + for i = 0; i < self.len - suffix.len; i = i + 1 { + v.buf[i] = self.buf[i] + } + v.len = self.len - suffix.len + Some(v) + } else { + None + } +} + +/// Search the vector index for a given element. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.search(3) +/// ``` +pub fn search[T : Eq](self : Array[T], value : T) -> Option[Int] { + for i = 0; i < self.len; i = i + 1 { + if self.buf[i] == value { + break Some(i) } - acc = acc + self[i].to_string() + } else { + None + } +} + +/// Swap two elements in the vector. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.swap(1, 2) +/// ``` +/// @alert unsafe "Panic if index is out of bounds." +pub fn swap[T](self : Array[T], i : Int, j : Int) -> Unit { + if i >= self.len || j >= self.len || i < 0 || j < 0 { + let len = self.len + abort( + "index out of bounds: the len is from 0 to \(len) but the index is (\(i), \(j))", + ) + } + let temp = self.buf[i] + self.buf[i] = self.buf[j] + self.buf[j] = temp +} + +/// Remove an element from the vector at a given index. +/// +/// Removes and returns the element at position index within the vector, shifting all elements after it to the left. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.remove(1) +/// ``` +/// @alert unsafe "Panic if index is out of bounds." +pub fn remove[T](self : Array[T], index : Int) -> T { + if index < 0 || index >= self.len { + let len = self.len + abort( + "index out of bounds: the len is from 0 to \(len) but the index is \(index)", + ) + } + let value = self.buf[index] + for i = index; i < self.len - 1; i = i + 1 { + self.buf[i] = self.buf[i + 1] + } + self.len = self.len - 1 + value +} + +/// Retains only the elements specified by the predicate. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// v.retain(fn(x) { x > 3 }) +/// ``` +/// TODO: perf could be improved +pub fn retain[T](self : Array[T], f : (T) -> Bool) -> Unit { + let mut i = 0 + while i < self.len { + if f(self.buf[i]).not() { + self.remove(i) |> ignore + } else { + i = i + 1 + } + } +} + +/// Resize the vector to a new length with a default value. +/// +/// # Example +/// ``` +/// Array::[3, 4, 5].resize(1) +/// ``` +/// @alert unsafe "Panic if new length is negative." +pub fn resize[T](self : Array[T], new_len : Int, f : T) -> Unit { + if new_len < 0 { + abort("negative new length") + } + if new_len < self.len { + self.len = new_len + } else { + for i = self.len; i < new_len; i = i + 1 { + self.push(f) + } + } +} + +/// Inserts an element at a given index within the vector. +/// +/// # Example +/// ``` +/// Array::[3, 4, 5].insert(1, 6) +/// ``` +/// @alert unsafe "Panic if index is out of bounds." +pub fn insert[T](self : Array[T], index : Int, value : T) -> Unit { + if index < 0 || index > self.len { + let len = self.len + abort( + "index out of bounds: the len is from 0 to \(len) but the index is \(index)", + ) + } + if self.len == self.buf.0.length() { + self.realloc() + } + for i = self.len; i > index; i = i - 1 { + self.buf[i] = self.buf[i - 1] + } + self.buf[index] = value + self.len = self.len + 1 +} + +/// Fill the vector (by capacity) with a given value. +/// +/// # Example +/// ``` +/// Array::with_capacity(3).fill(3) +/// ``` +pub fn fill[T](self : Array[T], value : T) -> Unit { + for i = 0; i < self.capacity(); i = i + 1 { + self.buf[i] = value + } + self.len = self.capacity() +} + +/// Fill the vector by calling a function (By capacity). +/// +/// # Example +/// ``` +/// Array::with_capacity(3).fill_with(fn() { 3 }) +/// ``` +pub fn fill_with[T](self : Array[T], f : () -> T) -> Unit { + for i = 0; i < self.capacity(); i = i + 1 { + self.buf[i] = f() } - acc + "]" + self.len = self.capacity() } -pub fn Array::default[X]() -> Array[X] { - [] +/// Flattens a vector of vectors into a vector. +/// +/// # Example +/// ``` +/// Array::[Array::[3, 4], Array::[5, 6]].flatten() +/// ``` +pub fn flatten[T](self : Array[Array[T]]) -> Array[T] { + let v = Array::new() + for i = 0; i < self.len; i = i + 1 { + v.append(self[i]) + } + v +} + +/// Create a vector by repeat a given vector for a given times. +/// +/// # Example +/// ``` +/// Array::[3, 4].repeat(2) +/// ``` +pub fn repeat[T](self : Array[T], times : Int) -> Array[T] { + let v = Array::with_capacity(self.len * times) + for i = 0; i < times; i = i + 1 { + v.append(self) + } + v +} + +/// Flattens a vector of vectors with a given separator. +/// +/// # Example +/// ``` +/// Array::[Array::[3, 4], Array::[5, 6]].join(0) +/// ``` +pub fn join[T](self : Array[Array[T]], sep : T) -> Array[T] { + let v = Array::new() + for i = 0; i < self.len; i = i + 1 { + v.append(self[i]) + if i < self.len - 1 { + v.push(sep) + } + } + v +} + +/// Fold out values from an array according to certain rules. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_left(~init=0, fn { sum, elem => sum + elem }) +/// sum // 15 +/// ``` +pub fn fold_left[T, U](self : Array[T], f : (U, T) -> U, ~init : U) -> U { + for i = 0, acc = init; i < self.length(); { + continue i + 1, f(acc, self[i]) + } else { + acc + } +} + +/// Fold out values from an array according to certain rules in reversed turn. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_right(~init=0, fn { sum, elem => sum + elem }) +/// sum // 15 +/// ``` +pub fn fold_right[T, U](self : Array[T], f : (U, T) -> U, ~init : U) -> U { + for i = self.length() - 1, acc = init; i >= 0; { + continue i - 1, f(acc, self[i]) + } else { + acc + } +} + +/// Fold out values from an array according to certain rules with index. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_lefti(~init=0, fn { index, sum, elem => sum + index }) +/// sum // 10 +/// ``` +pub fn fold_lefti[T, U](self : Array[T], f : (Int, U, T) -> U, ~init : U) -> U { + for i = 0, acc = init; i < self.length(); { + continue i + 1, f(i, acc, self[i]) + } else { + acc + } +} + +/// Fold out values from an array according to certain rules in reversed turn with index. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_righti(~init=0, fn { index, sum, elem => sum + index }) +/// sum // 10 +/// ``` +pub fn fold_righti[T, U](self : Array[T], f : (Int, U, T) -> U, ~init : U) -> U { + for i = self.len - 1, acc = init; i >= 0; { + continue i - 1, f(self.len - i - 1, acc, self[i]) + } else { + acc + } +} + +/// Removes consecutive repeated elements in the vector according to the Eq trait. +/// +/// # Example +/// ``` +/// let v = Array::[3, 4, 4, 5, 5, 5] +/// v.dedup() // v = Array::[3, 4, 5] +/// ``` +pub fn dedup[T : Eq](self : Array[T]) -> Unit { + for i = 0; i < self.len; i = i + 1 { + let mut j = i + 1 + while j < self.len { + if self[i] == self[j] { + self.remove(j) |> ignore + } else { + j = j + 1 + } + } + } +} + +/// Extract elements from the vector according to the given function. +/// +/// This function will remove the elements from the original vector and return a new vector. +/// # Example +/// ``` +/// let v = Array::[3, 4, 5] +/// let vv = v.extract_if(fn(x) { x > 3 }) // vv = Array::[4, 5], v = Array::[3] +/// ``` +pub fn extract_if[T](self : Array[T], f : (T) -> Bool) -> Array[T] { + let v = Array::new() + let indices = Array::new() + for i = 0; i < self.len; i = i + 1 { + if f(self[i]) { + v.push(self[i]) + indices.push(i) + } + } + for i = 0; i < indices.len; i = i + 1 { + self.remove(indices[i] - i) |> ignore + } + v +} + +/// Group the elements of the vector into sized chunks. +/// +/// If the elements of the vector cannot be divided into equal-sized chunks, the last chunk will be smaller. +/// +/// # Example +/// ``` +/// let v = Array::[1, 2, 3, 4, 5, 6, 7, 8, 9] +/// let chunks = v.chunks(3) // chunks = Array::[Array::[1, 2, 3], Array::[4, 5, 6], Array::[7, 8, 9]] +/// ``` +pub fn chunks[T](self : Array[T], size : Int) -> Array[Array[T]] { + let chunks = Array::new() + let mut i = 0 + while i < self.len { + let chunk = Array::with_capacity(size) + for j = 0; j < size && i < self.len; j = j + 1 { + chunk.push(self[i]) + i = i + 1 + } + chunks.push(chunk) + } + chunks +} + +/// Group the elements of the vector into chunks based on a predicate. +/// +/// # Example +/// ``` +/// let v = Array::[1, 1, 2, 3, 2, 3, 2, 3, 4] +/// let chunks = v.chunk_by(fn(x, y) { x <= y }) +/// // chunks = Array::[Array::[1, 1, 2, 3], Array::[2, 3], Array::[2, 3], Array::[4]] +/// ``` +pub fn chunk_by[T](self : Array[T], pred : (T, T) -> Bool) -> Array[Array[T]] { + let chunks = Array::new() + let mut i = 0 + while i < self.len { + let chunk = Array::new() + chunk.push(self[i]) + i = i + 1 + while i < self.len && pred(self[i - 1], self[i]) { + chunk.push(self[i]) + i = i + 1 + } + chunks.push(chunk) + } + chunks +} + +/// Split the vector into chunks based on a predicate. +/// +/// # Example +/// ``` +/// let v = Array::[1, 0, 2, 0, 3, 0, 4] +/// let chunks = v.split(fn(x) { x == 0 }) +/// ``` +pub fn split[T](self : Array[T], pred : (T) -> Bool) -> Array[Array[T]] { + let chunks = Array::new() + let mut i = 0 + while i < self.len { + let chunk = Array::new() + while i < self.len && pred(self[i]).not() { + chunk.push(self[i]) + i = i + 1 + } + chunks.push(chunk) + i = i + 1 + } + chunks +} + +/// Convert vec to list. +/// +/// # Example +/// +/// ``` +/// Array::[1, 2, 3, 4, 5].to_list() +/// ``` +pub fn to_list[T](self : Array[T]) -> List[T] { + for i = self.len - 1, list = List::Nil; i >= 0; { + continue i - 1, List::Cons(self[i], list) + } else { + list + } +} + +/// Reserves capacity to ensure that it can hold at least the number of elements +/// specified by the `capacity` argument. +/// +/// # Example +/// +/// ``` +/// let v = Array::[1] +/// v.reserve_capacity(10) +/// println(v.capacity()) // 10 +/// ``` +pub fn reserve_capacity[T](self : Array[T], capacity : Int) -> Unit { + if self.capacity() >= capacity { + return + } + let new_buf : UninitializedArray[T] = UninitializedArray::make(capacity) + for i = 0; i < self.length(); i = i + 1 { + new_buf[i] = self.buf[i] + } + self.buf = new_buf +} + +/// Shrinks the capacity of the vector as much as possible. +/// +/// # Example +/// +/// ``` +/// let v = Array::with_capacity(10) +/// v.push(1) +/// v.push(2) +/// v.push(3) +/// println(v.capacity()) // >= 10 +/// v.shrink_to_fit() +/// println(v.capacity()) // >= 3 +/// ``` +pub fn shrink_to_fit[T](self : Array[T]) -> Unit { + if self.capacity() <= self.length() { + return + } + let new_buf : UninitializedArray[T] = UninitializedArray::make(self.length()) + for i = 0; i < self.length(); i = i + 1 { + new_buf[i] = self.buf[i] + } + self.buf = new_buf } diff --git a/vec/view.mbt b/builtin/arrayview.mbt similarity index 72% rename from vec/view.mbt rename to builtin/arrayview.mbt index bdf4bc29..9a29532d 100644 --- a/vec/view.mbt +++ b/builtin/arrayview.mbt @@ -12,11 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub fn length[T](self : VecView[T]) -> Int { +/// A `ArrayView` is a slice of a `Array`. +struct ArrayView[T] { + buf : UninitializedArray[T] + start : Int + len : Int +} + +pub fn length[T](self : ArrayView[T]) -> Int { self.len } -pub fn op_get[T](self : VecView[T], index : Int) -> T { +pub fn op_get[T](self : ArrayView[T], index : Int) -> T { if index < 0 || index >= self.len { let len = self.len abort( @@ -26,7 +33,7 @@ pub fn op_get[T](self : VecView[T], index : Int) -> T { self.buf[self.start + index] } -pub fn op_set[T](self : VecView[T], index : Int, value : T) -> Unit { +pub fn op_set[T](self : ArrayView[T], index : Int, value : T) -> Unit { if index < 0 || index >= self.len { let len = self.len abort( @@ -36,7 +43,7 @@ pub fn op_set[T](self : VecView[T], index : Int, value : T) -> Unit { self.buf[self.start + index] = value } -pub fn swap[T](self : VecView[T], i : Int, j : Int) -> Unit { +pub fn swap[T](self : ArrayView[T], i : Int, j : Int) -> Unit { if i >= self.len || j >= self.len || i < 0 || j < 0 { let len = self.len abort( @@ -48,15 +55,7 @@ pub fn swap[T](self : VecView[T], i : Int, j : Int) -> Unit { self.buf[self.start + j] = temp } -pub fn reverse[T](self : VecView[T]) -> Unit { - let mid_len = self.length() / 2 - for i = 0; i < mid_len; i = i + 1 { - let j = self.length() - i - 1 - self.swap(i, j) - } -} - -pub fn op_as_view[T](self : Vec[T], ~start : Int, ~end : Int) -> VecView[T] { +pub fn op_as_view[T](self : Array[T], ~start : Int, ~end : Int) -> ArrayView[T] { if start < 0 { abort("Slice start index out of bounds") } else if end > self.length() { @@ -64,10 +63,10 @@ pub fn op_as_view[T](self : Vec[T], ~start : Int, ~end : Int) -> VecView[T] { } else if start > end { abort("Slice start index greater than end index") } - VecView::{ buf: self.buf, start, len: end - start } + ArrayView::{ buf: self.buf, start, len: end - start } } -pub fn op_as_view[T](self : VecView[T], ~start : Int, ~end : Int) -> VecView[T] { +pub fn op_as_view[T](self : ArrayView[T], ~start : Int, ~end : Int) -> ArrayView[T] { if start < 0 { abort("Slice start index out of bounds") } else if end > self.length() { @@ -75,5 +74,5 @@ pub fn op_as_view[T](self : VecView[T], ~start : Int, ~end : Int) -> VecView[T] } else if start > end { abort("Slice start index greater than end index") } - VecView::{ buf: self.buf, start: self.start + start, len: end - start } + ArrayView::{ buf: self.buf, start: self.start + start, len: end - start } } diff --git a/builtin/debug.mbt b/builtin/debug.mbt index 1ea9a2bb..2fc80992 100644 --- a/builtin/debug.mbt +++ b/builtin/debug.mbt @@ -76,6 +76,17 @@ pub fn debug_write[X : Debug](self : Ref[X], buf : Buffer) -> Unit { buf.write_string("}") } +pub fn debug_write[X : Debug](self : FixedArray[X], buf : Buffer) -> Unit { + buf.write_char('[') + for i = 0; i < self.length(); i = i + 1 { + if i > 0 { + buf.write_string(", ") + } + self[i].debug_write(buf) + } + buf.write_char(']') +} + pub fn debug_write[X : Debug](self : Array[X], buf : Buffer) -> Unit { buf.write_char('[') for i = 0; i < self.length(); i = i + 1 { diff --git a/vec/types.mbt b/builtin/fixedarray.mbt similarity index 66% rename from vec/types.mbt rename to builtin/fixedarray.mbt index d46057b5..6f489c33 100644 --- a/vec/types.mbt +++ b/builtin/fixedarray.mbt @@ -12,17 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -priv type UninitializedArray[T] Array[UnsafeMaybeUninit[T]] - -/// A `Vec` is a generic vector (dynamic array) that can grow in size. -struct Vec[T] { - mut buf : UninitializedArray[T] - mut len : Int +pub fn to_string[X : Show](self : FixedArray[X]) -> String { + let mut acc = "[" + for i = 0; i < self.length(); i = i + 1 { + if i > 0 { + acc = acc + ", " + } + acc = acc + self[i].to_string() + } + acc + "]" } -/// A `VecView` is a slice of a `Vec`. -struct VecView[T] { - buf : UninitializedArray[T] - start : Int - len : Int +pub fn FixedArray::default[X]() -> FixedArray[X] { + [] } diff --git a/builtin/intrinsics.mbt b/builtin/intrinsics.mbt index 59cf5237..e882bcac 100644 --- a/builtin/intrinsics.mbt +++ b/builtin/intrinsics.mbt @@ -216,19 +216,19 @@ pub fn Int64::to_byte(self : Int64) -> Byte { identity(self.to_int().land(0xFF)) } -// Array primitive ops +// FixedArray primitive ops -pub fn Array::op_get[T](self : Array[T], idx : Int) -> T = "%array_get" +pub fn FixedArray::op_get[T](self : FixedArray[T], idx : Int) -> T = "%array_get" -pub fn Array::get[T](self : Array[T], idx : Int) -> T = "%array_get" +pub fn FixedArray::get[T](self : FixedArray[T], idx : Int) -> T = "%array_get" -pub fn Array::op_set[T](self : Array[T], idx : Int, val : T) -> Unit = "%array_set" +pub fn FixedArray::op_set[T](self : FixedArray[T], idx : Int, val : T) -> Unit = "%array_set" -pub fn Array::set[T](self : Array[T], idx : Int, val : T) -> Unit = "%array_set" +pub fn FixedArray::set[T](self : FixedArray[T], idx : Int, val : T) -> Unit = "%array_set" -pub fn Array::length[T](self : Array[T]) -> Int = "%array_length" +pub fn FixedArray::length[T](self : FixedArray[T]) -> Int = "%array_length" -pub fn Array::make[T](len : Int, init : T) -> Array[T] = "%array_make" +pub fn FixedArray::make[T](len : Int, init : T) -> FixedArray[T] = "%array_make" // String primitive ops diff --git a/devec/types.mbt b/devec/types.mbt index 0506adb3..14d042e3 100644 --- a/devec/types.mbt +++ b/devec/types.mbt @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -priv type UninitializedArray[T] Array[UnsafeMaybeUninit[T]] +priv type UninitializedArray[T] FixedArray[UnsafeMaybeUninit[T]] // head and tail point to non-empty slots. When the buffer is empty, head and tail points to the same slot. struct Devec[T] { diff --git a/array/blit.mbt b/fixedarray/blit.mbt similarity index 83% rename from array/blit.mbt rename to fixedarray/blit.mbt index b9d865d4..8a766be5 100644 --- a/array/blit.mbt +++ b/fixedarray/blit.mbt @@ -14,9 +14,9 @@ /// @intrinsic %array.copy fn unsafe_blit[A]( - dst : Array[A], + dst : FixedArray[A], dst_offset : Int, - src : Array[A], + src : FixedArray[A], src_offset : Int, len : Int ) -> Unit { @@ -32,8 +32,8 @@ fn unsafe_blit[A]( } pub fn blit_to[A]( - self : Array[A], - dst : Array[A], + self : FixedArray[A], + dst : FixedArray[A], ~len : Int, ~src_offset : Int = 0, ~dst_offset : Int = 0 @@ -44,26 +44,26 @@ pub fn blit_to[A]( unsafe_blit(dst, dst_offset, self, src_offset, len) } -pub fn copy[T](self : Array[T]) -> Array[T] { +pub fn copy[T](self : FixedArray[T]) -> FixedArray[T] { let len = self.length() if len == 0 { [] } else { - let arr = Array::make(len, self[0]) + let arr = FixedArray::make(len, self[0]) unsafe_blit(arr, 0, self, 0, len) arr } } test "copy" { - let a : Array[_] = [1, 2, 3, 4] + let a : FixedArray[_] = [1, 2, 3, 4] let b = a.copy() inspect(b, content="[1, 2, 3, 4]")? inspect(physical_equal(b, a), content="false")? - let c = Array::make(8, 0) + let c = FixedArray::make(8, 0) a.blit_to(c, len=4, dst_offset=3) inspect(c, content="[0, 0, 0, 1, 2, 3, 4, 0]")? - inspect(([] : Array[Int]).copy(), content="[]")? + inspect(([] : FixedArray[Int]).copy(), content="[]")? a.blit_to(a, len=2, src_offset=1) inspect(a, content="[2, 3, 3, 4]")? } diff --git a/fixedarray/fixedarray.mbt b/fixedarray/fixedarray.mbt new file mode 100644 index 00000000..f1960bd3 --- /dev/null +++ b/fixedarray/fixedarray.mbt @@ -0,0 +1,916 @@ +// Copyright 2024 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Iterates over each element. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: The function to apply to each element. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iter(fn(x){ print("\(x) ") }) //output: 1 2 3 4 5 +/// ``` +/// @intrinsic %array.iter +pub fn iter[T](self : FixedArray[T], f : (T) -> Unit) -> Unit { + for i = 0; i < self.length(); i = i + 1 { + f(self[i]) + } +} + +test "iter" { + let mut i = 0 + let mut failed = false + let f = fn(elem) { + if elem != i + 1 { + failed = true + } + i = i + 1 + } + { + i = 0 + ([] : FixedArray[_]).iter(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 0)? + } + { + i = 0 + ([1] : FixedArray[_]).iter(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? + } + i = 0 + ([1, 2, 3, 4, 5] : FixedArray[_]).iter(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 5)? +} + +/// Iterates over the array with index. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: A function that takes an `Int` representing the index and a `T` representing the element of the array, and returns `Unit`. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iteri(fn(index, elem){ +/// print("(\(index),\(elem)) ") +/// }) //output: (0,1) (1,2) (2,3) (3,4) (4,5) +/// ``` +pub fn iteri[T](self : FixedArray[T], f : (Int, T) -> Unit) -> Unit { + for i = 0; i < self.length(); i = i + 1 { + f(i, self[i]) + } +} + +test "iteri" { + let mut i = 0 + let mut failed = false + let f = fn(index, elem) { + if index != i || elem != i + 1 { + failed = true + } + i = i + 1 + } + { + i = 0 + ([] : FixedArray[_]).iteri(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 0)? + } + { + i = 0 + ([1] : FixedArray[_]).iteri(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? + } + i = 0 + ([1, 2, 3, 4, 5] : FixedArray[_]).iteri(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 5)? +} + +/// Iterates over each element in reversed turn. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: The function to apply to each element. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iter(fn(x){ print("\(x) ") }) //output: 5 4 3 2 1 +/// ``` +pub fn iter_rev[T](self : FixedArray[T], f : (T) -> Unit) -> Unit { + for i = self.length() - 1; i >= 0; i = i - 1 { + f(self[i]) + } +} + +test "iter_rev" { + let mut i = 6 + let mut failed = false + let f = fn(elem) { + if elem != i - 1 { + failed = true + } + i = i - 1 + } + { + i = 1 + ([] : FixedArray[_]).iter_rev(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? + } + { + i = 2 + ([1] : FixedArray[_]).iter_rev(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? + } + i = 6 + ([1, 2, 3, 4, 5] : FixedArray[_]).iter_rev(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? +} + +/// Iterates over the array with index in reversed turn. +/// +/// # Arguments +/// +/// - `self`: The array to iterate over. +/// - `f`: A function that takes an `Int` representing the index and a `T` representing the element of the array, and returns `Unit`. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].iter_revi(fn(index, elem){ +/// print("(\(index),\(elem)) ") +/// }) //output: (4,5) (3,4) (2,3) (1,2) (0,1) +/// ``` +pub fn iter_revi[T](self : FixedArray[T], f : (Int, T) -> Unit) -> Unit { + let len = self.length() + for i = 0; i < len; i = i + 1 { + f(i, self[len - i - 1]) + } +} + +test "iter_revi" { + let mut i = 6 + let mut j = 0 + let mut failed = false + let f = fn(index, elem) { + if index != j || elem != i - 1 { + failed = true + } + i = i - 1 + j = j + 1 + } + { + i = 1 + j = 0 + ([] : FixedArray[_]).iter_revi(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? + @assertion.assert_eq(j, 0)? + } + { + i = 2 + j = 0 + ([1] : FixedArray[_]).iter_revi(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? + @assertion.assert_eq(j, 1)? + } + i = 6 + j = 0 + ([1, 2, 3, 4, 5] : FixedArray[_]).iter_revi(f) + @assertion.assert_false(failed)? + @assertion.assert_eq(i, 1)? + @assertion.assert_eq(j, 5)? +} + +/// Applies a function to each element of the array and returns a new array with the results. +/// +/// # Example +/// +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// let doubled = arr.map(fn(x){ x * 2 }) +/// debug(doubled) //output: [2, 4, 6, 8, 10] +/// ``` +pub fn map[T, U](self : FixedArray[T], f : (T) -> U) -> FixedArray[U] { + if self.length() == 0 { + return [] + } + let res = FixedArray::make(self.length(), f(self[0])) + for i = 1; i < self.length(); i = i + 1 { + res[i] = f(self[i]) + } + res +} + +test "map" { + let empty : FixedArray[Unit] = FixedArray::default().map(fn(x) { x }) + @assertion.assert_eq(empty, [])? + let simple_arr : FixedArray[_] = [6] + let simple_doubled = simple_arr.map(fn(x) { x * 2 }) + @assertion.assert_eq(simple_doubled, [12])? + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + let doubled = arr.map(fn(x) { x * 2 }) + @assertion.assert_eq(doubled, [2, 4, 6, 8, 10])? +} + +/// Maps a function over the elements of the arr with index. +/// +/// # Example +/// ``` +/// let arr = [3, 4, 5] +/// let added = arr.mapi(fn (i, x) { x + i }) +/// debug(added) //output: [3, 5, 7] +/// ``` +pub fn mapi[T, U](self : FixedArray[T], f : (Int, T) -> U) -> FixedArray[U] { + if self.length() == 0 { + return [] + } + let res = FixedArray::make(self.length(), f(0, self[0])) + for i = 1; i < self.length(); i = i + 1 { + res[i] = f(i, self[i]) + } + res +} + +test "mapi" { + let empty : FixedArray[Int] = FixedArray::default().mapi(fn(i, x) { x + i }) + @assertion.assert_eq(empty, [])? + let simple_arr : FixedArray[_] = [6] + let simple_doubled = simple_arr.mapi(fn(i, x) { x * 2 + i }) + @assertion.assert_eq(simple_doubled, [12])? + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + let doubled = arr.mapi(fn(i, x) { x * 2 + i }) + @assertion.assert_eq(doubled, [2, 5, 8, 11, 14])? +} + +/// Create a new array. Values are lazily built. +pub fn new[T](length : Int, value : () -> T) -> FixedArray[T] { + if length <= 0 { + [] + } else { + let array = FixedArray::make(length, value()) + for i = 1; i < length; i = i + 1 { + array[i] = value() + } + array + } +} + +test "new" { + let empty = new(0, fn() { { val: 3 } }) + @assertion.assert_eq(empty.length(), 0)? + let simple_arr = new(1, fn() { { val: 2 } }) + @assertion.assert_eq(simple_arr.length(), 1)? + @assertion.assert_eq(simple_arr[0].val, 2)? + let arr = new(2, fn() { { val: 1 } }) + @assertion.assert_eq(arr.length(), 2)? + @assertion.assert_is_not(arr[0], arr[1])? + @assertion.assert_eq(arr[0].val, 1)? + @assertion.assert_eq(arr[1].val, 1)? +} + +/// Create a new array. Values are built from indexes. +pub fn new_with_index[T](length : Int, value : (Int) -> T) -> FixedArray[T] { + if length <= 0 { + [] + } else { + let array = FixedArray::make(length, value(0)) + for i = 1; i < length; i = i + 1 { + array[i] = value(i) + } + array + } +} + +test "new_with_index" { + let empty = new_with_index(0, fn { i => i }) + @assertion.assert_eq(empty.length(), 0)? + let simple_arr = new_with_index(1, fn { i => i }) + @assertion.assert_eq(simple_arr.length(), 1)? + @assertion.assert_eq(simple_arr[0], 0)? + let arr = new_with_index(2, fn { i => i }) + @assertion.assert_eq(arr.length(), 2)? + @assertion.assert_eq(arr[0], 0)? + @assertion.assert_eq(arr[1], 1)? +} + +/// Create a new array with given values. +pub fn FixedArray::from_array[T](array : FixedArray[T]) -> FixedArray[T] { + array +} + +test "from_array" { + let array = FixedArray::[1, 2, 3, 4, 5] + @assertion.assert_eq(array, [1, 2, 3, 4, 5])? +} + +/// Fold out values from an array according to certain rules. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_left(init=0, fn { sum, elem => sum + elem }) +/// sum // 15 +/// ``` +pub fn fold_left[T, U](self : FixedArray[T], f : (U, T) -> U, ~init : U) -> U { + for i = 0, acc = init; i < self.length(); { + continue i + 1, f(acc, self[i]) + } else { + acc + } +} + +test "fold_left" { + let sum = ([] : FixedArray[_]).fold_left(init=1, fn { sum, elem => sum + elem }) + @assertion.assert_eq(sum, 1)? + let sum = ([1] : FixedArray[_]).fold_left(init=2, fn { sum, elem => sum + elem }) + @assertion.assert_eq(sum, 3)? + let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).fold_left( + init=0, + fn { sum, elem => sum + elem }, + ) + @assertion.assert_eq(sum, 15)? +} + +/// Fold out values from an array according to certain rules in reversed turn. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_right(init=0, fn { sum, elem => sum + elem }) +/// sum // 15 +/// ``` +pub fn fold_right[T, U](self : FixedArray[T], f : (U, T) -> U, ~init : U) -> U { + for i = self.length() - 1, acc = init; i >= 0; { + continue i - 1, f(acc, self[i]) + } else { + acc + } +} + +test "fold_right" { + let sum = ([] : FixedArray[_]).fold_right(init=1, fn { sum, elem => sum + elem }) + @assertion.assert_eq(sum, 1)? + let sum = ([1] : FixedArray[_]).fold_right(init=2, fn { sum, elem => sum + elem }) + @assertion.assert_eq(sum, 3)? + let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).fold_right( + init=0, + fn { sum, elem => sum + elem }, + ) + @assertion.assert_eq(sum, 15)? +} + +/// Fold out values from an array according to certain rules with index. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_lefti(init=0, fn { index, sum, elem => sum + index }) +/// sum // 10 +/// ``` +pub fn fold_lefti[T, U](self : FixedArray[T], f : (Int, U, T) -> U, ~init : U) -> U { + for i = 0, acc = init; i < self.length(); { + continue i + 1, f(i, acc, self[i]) + } else { + acc + } +} + +test "fold_lefti" { + let f = fn { index, sum, elem => index + sum + elem } + { + let sum = ([] : FixedArray[_]).fold_lefti(init=1, f) + @assertion.assert_eq(sum, 1)? + } + { + let sum = ([1] : FixedArray[_]).fold_lefti(init=2, f) + @assertion.assert_eq(sum, 3)? + } + let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).fold_lefti(init=0, f) + @assertion.assert_eq(sum, 25)? +} + +/// Fold out values from an array according to certain rules in reversed turn with index. +/// +/// # Example +/// ``` +/// let sum = [1, 2, 3, 4, 5].fold_righti(init=0, fn { index, sum, elem => sum + index }) +/// sum // 10 +/// ``` +pub fn fold_righti[T, U](self : FixedArray[T], f : (Int, U, T) -> U, ~init : U) -> U { + let len = self.length() + for i = len - 1, acc = init; i >= 0; { + continue i - 1, f(len - i - 1, acc, self[i]) + } else { + acc + } +} + +test "fold_righti" { + let f = fn { index, sum, elem => index + sum + elem } + { + let sum = ([] : FixedArray[_]).fold_lefti(init=1, f) + @assertion.assert_eq(sum, 1)? + } + { + let sum = ([1] : FixedArray[_]).fold_lefti(init=2, f) + @assertion.assert_eq(sum, 3)? + } + let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).fold_righti(init=0, f) + @assertion.assert_eq(sum, 25)? +} + +/// Reverses the order of elements in the slice, in place. +/// +/// # Example +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// reverse(arr) // [5, 4, 3, 2, 1] +/// ``` +pub fn reverse[T](self : FixedArray[T]) -> Unit { + let mid_len = self.length() / 2 + for i = 0; i < mid_len; i = i + 1 { + let j = self.length() - i - 1 + let temp = self[i] + self[i] = self[j] + self[j] = temp + } +} + +test "reverse" { + { + let arr : FixedArray[Int] = [] + arr.reverse() + @assertion.assert_eq(arr, [])? + } + { + let arr : FixedArray[_] = [1] + arr.reverse() + @assertion.assert_eq(arr, [1])? + } + { + let arr : FixedArray[_] = [1, 2] + arr.reverse() + @assertion.assert_eq(arr, [2, 1])? + } + { + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + arr.reverse() + @assertion.assert_eq(arr, [5, 4, 3, 2, 1])? + } + let arr : FixedArray[_] = [1, 2, 3, 4, 5, 6] + arr.reverse() + @assertion.assert_eq(arr, [6, 5, 4, 3, 2, 1])? +} + +/// Swap two elements in the array. +/// +/// # Example +/// +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// arr.swap(0, 1) +/// debug(arr) //output: [2, 1, 3, 4, 5] +/// ``` +fn swap[T](self : FixedArray[T], i : Int, j : Int) -> Unit { + let temp = self[i] + self[i] = self[j] + self[j] = temp +} + +test "swap" { + { + let arr : FixedArray[Int] = [1] + arr.swap(0, 0) + @assertion.assert_eq(arr, [1])? + } + { + let arr : FixedArray[_] = [1, 2] + arr.swap(0, 0) + @assertion.assert_eq(arr, [1, 2])? + arr.swap(0, 1) + @assertion.assert_eq(arr, [2, 1])? + } + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + arr.swap(3, 3) + @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? + arr.swap(1, 3) + @assertion.assert_eq(arr, [1, 4, 3, 2, 5])? +} + +/// Check if all the elements in the array match the condition. +/// +/// # Example +/// +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// arr.all(fn(ele) { ele < 6 }) // true +/// arr.all(fn(ele) { ele < 5 }) // false +/// ``` +pub fn all[T](self : FixedArray[T], f : (T) -> Bool) -> Bool { + for i = 0; i < self.length(); i = i + 1 { + if f(self[i]).not() { + return false + } + } + true +} + +test "all" { + { + let arr : FixedArray[Int] = [] + @assertion.assert_true(arr.all(fn(ele) { ele < 6 }))? + @assertion.assert_true(arr.all(fn(ele) { ele < 5 }))? + } + { + let arr : FixedArray[_] = [5] + @assertion.assert_true(arr.all(fn(ele) { ele < 6 }))? + @assertion.assert_false(arr.all(fn(ele) { ele < 5 }))? + } + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + @assertion.assert_true(arr.all(fn(ele) { ele < 6 }))? + @assertion.assert_false(arr.all(fn(ele) { ele < 5 }))? +} + +/// Check if any of the elements in the array match the condition. +/// +/// # Example +/// +/// ``` +/// let arr = [1, 2, 3, 4, 5] +/// arr.any(fn(ele) { ele < 6 }) // true +/// arr.any(fn(ele) { ele < 5 }) // true +/// ``` +pub fn any[T](self : FixedArray[T], f : (T) -> Bool) -> Bool { + for i = 0; i < self.length(); i = i + 1 { + if f(self[i]) { + return true + } + } + false +} + +test "any" { + { + let arr : FixedArray[Int] = [] + @assertion.assert_false(arr.any(fn(ele) { ele < 6 }))? + @assertion.assert_false(arr.any(fn(ele) { ele < 5 }))? + } + { + let arr : FixedArray[_] = [5] + @assertion.assert_true(arr.any(fn(ele) { ele < 6 }))? + @assertion.assert_false(arr.any(fn(ele) { ele < 5 }))? + } + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + @assertion.assert_true(arr.any(fn(ele) { ele < 6 }))? + @assertion.assert_true(arr.any(fn(ele) { ele < 5 }))? +} + +/// Fill the array with a given value. +/// +/// # Example +/// ``` +/// [0, 0, 0, 0, 0].fill(3) // [3, 3, 3, 3, 3] +/// ``` +pub fn fill[T](self : FixedArray[T], value : T) -> Unit { + for i = 0; i < self.length(); i = i + 1 { + self[i] = value + } +} + +test "fill" { + { + let arr : FixedArray[Int] = [] + arr.fill(3) + @assertion.assert_eq(arr, [])? + } + { + let arr : FixedArray[_] = [6] + arr.fill(5) + @assertion.assert_eq(arr, [5])? + } + let arr : FixedArray[_] = [0, 0, 0, 0, 0] + arr.fill(3) + @assertion.assert_eq(arr, [3, 3, 3, 3, 3])? +} + +/// Search the array index for a given element. +/// +/// # Example +/// ``` +/// let arr = [3, 4, 5] +/// arr.search(3) // 0 +/// ``` +pub fn search[T : Eq](self : FixedArray[T], value : T) -> Option[Int] { + for i = 0; i < self.length(); i = i + 1 { + if self[i] == value { + return Some(i) + } + } + None +} + +test "search" { + { + let arr : FixedArray[Int] = [] + @assertion.assert_eq(arr.search(3), None)? + @assertion.assert_eq(arr.search(-1), None)? + } + { + let arr : FixedArray[_] = [3] + @assertion.assert_eq(arr.search(3), Some(0))? + @assertion.assert_eq(arr.search(-1), None)? + } + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + @assertion.assert_eq(arr.search(1), Some(0))? + @assertion.assert_eq(arr.search(5), Some(4))? + @assertion.assert_eq(arr.search(3), Some(2))? + @assertion.assert_eq(arr.search(-1), None)? +} + +/// Checks if the array contains an element. +/// +/// # Example +/// ``` +/// let arr = [3, 4, 5] +/// arr.contains(3) +/// ``` +pub fn contains[T : Eq](self : FixedArray[T], value : T) -> Bool { + for i = 0; i < self.length(); i = i + 1 { + if self[i] == value { + return true + } + } + false +} + +test "contains" { + { + let arr : FixedArray[Int] = [] + @assertion.assert_false(arr.contains(3))? + @assertion.assert_false(arr.contains(-1))? + } + { + let arr : FixedArray[_] = [3] + @assertion.assert_true(arr.contains(3))? + @assertion.assert_false(arr.contains(-1))? + } + let arr : FixedArray[_] = [3, 4, 5] + @assertion.assert_true(arr.contains(3))? + @assertion.assert_true(arr.contains(4))? + @assertion.assert_true(arr.contains(5))? + @assertion.assert_false(arr.contains(6))? +} + +/// Check if the array starts with a given prefix. +/// +/// # Example +/// ``` +/// let arr = [3, 4, 5] +/// arr.starts_with([3, 4]) // true +/// ``` +pub fn starts_with[T : Eq](self : FixedArray[T], prefix : FixedArray[T]) -> Bool { + if prefix.length() > self.length() { + return false + } + for i = 0; i < prefix.length(); i = i + 1 { + if self[i] != prefix[i] { + return false + } + } + true +} + +test "starts_with" { + { + let arr : FixedArray[Int] = [] + @assertion.assert_true(arr.starts_with([]))? + @assertion.assert_false(arr.starts_with([1]))? + } + { + let arr : FixedArray[_] = [3] + @assertion.assert_true(arr.starts_with([]))? + @assertion.assert_true(arr.starts_with([3]))? + @assertion.assert_false(arr.starts_with([2]))? + @assertion.assert_false(arr.starts_with([3, 1]))? + } + let arr : FixedArray[_] = [3, 4, 5] + @assertion.assert_true(arr.starts_with([]))? + @assertion.assert_true(arr.starts_with([3]))? + @assertion.assert_false(arr.starts_with([2]))? + @assertion.assert_true(arr.starts_with([3, 4]))? + @assertion.assert_false(arr.starts_with([3, 2]))? + @assertion.assert_true(arr.starts_with([3, 4, 5]))? + @assertion.assert_false(arr.starts_with([3, 4, 2]))? + @assertion.assert_false(arr.starts_with([3, 4, 5, 6]))? +} + +/// Check if the array ends with a given suffix. +/// +/// # Example +/// ``` +/// let v = [3, 4, 5] +/// v.ends_with([5]) // true +/// ``` +pub fn ends_with[T : Eq](self : FixedArray[T], suffix : FixedArray[T]) -> Bool { + let self_len = self.length() + let suf_len = suffix.length() + if suf_len > self_len { + return false + } + for i = 0; i < suf_len; i = i + 1 { + if self[self_len - suf_len + i] != suffix[i] { + return false + } + } + true +} + +test "ends_with" { + { + let arr : FixedArray[Int] = [] + @assertion.assert_true(arr.ends_with([]))? + @assertion.assert_false(arr.ends_with([1]))? + } + { + let arr : FixedArray[_] = [3] + @assertion.assert_true(arr.ends_with([]))? + @assertion.assert_true(arr.ends_with([3]))? + @assertion.assert_false(arr.ends_with([2]))? + @assertion.assert_false(arr.ends_with([3, 1]))? + } + let arr : FixedArray[_] = [3, 4, 5] + @assertion.assert_true(arr.ends_with([]))? + @assertion.assert_true(arr.ends_with([5]))? + @assertion.assert_false(arr.ends_with([2]))? + @assertion.assert_true(arr.ends_with([4, 5]))? + @assertion.assert_false(arr.ends_with([4, 2]))? + @assertion.assert_false(arr.ends_with([2, 5]))? + @assertion.assert_true(arr.ends_with([3, 4, 5]))? + @assertion.assert_false(arr.ends_with([3, 4, 2]))? + @assertion.assert_false(arr.ends_with([3, 2, 5]))? + @assertion.assert_false(arr.ends_with([2, 4, 5]))? + @assertion.assert_false(arr.ends_with([3, 4, 5, 6]))? + @assertion.assert_false(arr.ends_with([2, 3, 4, 5]))? +} + +pub fn op_equal[T : Eq](self : FixedArray[T], that : FixedArray[T]) -> Bool { + if self.length() != that.length() { + return false + } + for i = 0; i < self.length(); i = i + 1 { + if self[i] != that[i] { + return false + } + } + true +} + +test "op_equal" { + { + inspect(([] : FixedArray[Int]) == [], content="true")? + inspect(([] : FixedArray[_]) == [1], content="false")? + inspect(([1, 2] : FixedArray[_]) == [], content="false")? + } + { + inspect(([1] : FixedArray[_]) == [1], content="true")? + inspect(([1] : FixedArray[_]) == [2], content="false")? + inspect(([1, 2] : FixedArray[_]) == [1], content="false")? + inspect(([1] : FixedArray[_]) == [1, 2], content="false")? + } + inspect(([1, 2, 3, 4, 5] : FixedArray[_]) == [1, 2, 3, 4, 5], content="true")? + inspect(([1, 2, 3, 4, 5] : FixedArray[_]) == [1, 2, 3, 4], content="false")? + inspect(([1, 2, 3, 4] : FixedArray[_]) == [1, 2, 3, 4, 5], content="false")? + inspect(([1, 2, 3, 4, 5] : FixedArray[_]) == [6, 2, 3, 4, 5], content="false")? + inspect(([1, 2, 3, 4, 5] : FixedArray[_]) == [1, 2, 6, 4, 5], content="false")? + inspect(([1, 2, 3, 4, 5] : FixedArray[_]) == [1, 2, 3, 4, 6], content="false")? +} + +fn deep_clone[T](arr : FixedArray[T]) -> FixedArray[T] { + let len = arr.length() + if len == 0 { + [] + } else { + let result = FixedArray::make(len, arr[0]) + for i = 0; i < len; i = i + 1 { + result[i] = arr[i] + } + result + } +} + +test "deep_clone" { + { + let pre : FixedArray[Int] = [] + let arr = deep_clone(pre) + @assertion.assert_is_not(arr, pre)? + @assertion.assert_eq(arr, [])? + } + { + let pre : FixedArray[_] = [3] + let arr = deep_clone(pre) + @assertion.assert_is_not(arr, pre)? + @assertion.assert_eq(arr, [3])? + } + let pre : FixedArray[_] = [1, 2, 3, 4, 5] + let arr = deep_clone(pre) + @assertion.assert_is_not(arr, pre)? + @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? +} + +pub fn op_add[T](self : FixedArray[T], other : FixedArray[T]) -> FixedArray[T] { + let slen = self.length() + let nlen = other.length() + if slen == 0 { + deep_clone(other) + } else if nlen == 0 { + deep_clone(self) + } else { + let result = FixedArray::make(slen + nlen, self[0]) + for i = 1; i < slen; i = i + 1 { + result[i] = self[i] + } + for i = 0; i < nlen; i = i + 1 { + result[i + slen] = other[i] + } + result + } +} + +test "op_add" { + { + inspect(([] : FixedArray[Int]) + [], content="[]")? + inspect(([] : FixedArray[_]) + [1, 2, 3, 4, 5], content="[1, 2, 3, 4, 5]")? + inspect(([1, 2, 3, 4, 5] : FixedArray[_]) + [], content="[1, 2, 3, 4, 5]")? + } + { + inspect(([1] : FixedArray[_]) + [2], content="[1, 2]")? + inspect(([1] : FixedArray[_]) + [1, 2, 3, 4, 5], content="[1, 1, 2, 3, 4, 5]")? + inspect(([1, 2, 3, 4, 5] : FixedArray[_]) + [1], content="[1, 2, 3, 4, 5, 1]")? + } + inspect( + ([1, 2, 3, 4, 5] : FixedArray[_]) + [6, 7, 8, 9, 10], + content="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", + )? +} + +/// @intrinsic %iter.from_array +pub fn as_iter[T](self : FixedArray[T]) -> @iter.Iter[T] { + @iter.Iter::_unstable_internal_make( + fn(yield) { + for i = 0, len = self.length(); i < len; i = i + 1 { + if yield(self[i]).not() { + break false + } + } else { + true + } + }, + ) +} + +test "as_iter" { + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + let iter = arr.as_iter() + let exb = Buffer::make(0) + let mut i = 0 + iter.iter( + fn(x) { + exb.write_string(x.to_string()) + exb.write_char('\n') + i = i + 1 + }, + ) + @assertion.assert_eq(i, arr.length())? + exb.expect( + content= + #|1 + #|2 + #|3 + #|4 + #|5 + #| + , + )? +} diff --git a/fixedarray/fixedarray.mbti b/fixedarray/fixedarray.mbti new file mode 100644 index 00000000..60f7bf99 --- /dev/null +++ b/fixedarray/fixedarray.mbti @@ -0,0 +1,48 @@ +package moonbitlang/core/array + +alias @moonbitlang/core/iter as @iter + +// Values +fn is_sorted[T : Compare + Eq](Array[T]) -> Bool + +fn new[T](Int, () -> T) -> Array[T] + +fn new_with_index[T](Int, (Int) -> T) -> Array[T] + +// Types and methods +type TimSortRun +impl Array { + all[T](Self[T], (T) -> Bool) -> Bool + any[T](Self[T], (T) -> Bool) -> Bool + as_iter[T](Self[T]) -> @iter.Iter[T] + blit_to[A](Self[A], Self[A], ~len : Int, ~src_offset : Int = .., ~dst_offset : Int = ..) -> Unit + contains[T : Eq](Self[T], T) -> Bool + copy[T](Self[T]) -> Self[T] + ends_with[T : Eq](Self[T], Self[T]) -> Bool + fill[T](Self[T], T) -> Unit + fold_left[T, U](Self[T], (U, T) -> U, ~init : U) -> U + fold_lefti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U + fold_right[T, U](Self[T], (U, T) -> U, ~init : U) -> U + fold_righti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U + from_array[T](Self[T]) -> Self[T] + iter[T](Self[T], (T) -> Unit) -> Unit + iter_rev[T](Self[T], (T) -> Unit) -> Unit + iter_revi[T](Self[T], (Int, T) -> Unit) -> Unit + iteri[T](Self[T], (Int, T) -> Unit) -> Unit + map[T, U](Self[T], (T) -> U) -> Self[U] + mapi[T, U](Self[T], (Int, T) -> U) -> Self[U] + op_add[T](Self[T], Self[T]) -> Self[T] + op_equal[T : Eq](Self[T], Self[T]) -> Bool + reverse[T](Self[T]) -> Unit + search[T : Eq](Self[T], T) -> Option[Int] + sort[T : Compare + Eq](Self[T]) -> Unit + sort_by[T](Self[T], (T, T) -> Int) -> Unit + sort_by_key[T, K : Compare + Eq](Self[T], (T) -> K) -> Unit + stable_sort[T : Compare + Eq](Self[T]) -> Unit + starts_with[T : Eq](Self[T], Self[T]) -> Bool +} + +// Traits + +// Extension Methods + diff --git a/fixedarray/fixedarray_test.mbt b/fixedarray/fixedarray_test.mbt new file mode 100644 index 00000000..a556e963 --- /dev/null +++ b/fixedarray/fixedarray_test.mbt @@ -0,0 +1,41 @@ +// Copyright 2024 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Convert array to list. +/// +/// # Example +/// +/// ``` +/// [1, 2, 3, 4, 5].to_list() +/// ``` +pub fn to_list[T](self : FixedArray[T]) -> List[T] { + for i = self.length() - 1, list = List::Nil; i >= 0; { + continue i - 1, List::Cons(self[i], list) + } else { + list + } +} + +test "to_list" { + { + let ls : List[Int] = ([] : FixedArray[_]).to_list() + @assertion.assert_eq(ls, Nil)? + } + { + let ls = ([3] : FixedArray[_]).to_list() + @assertion.assert_eq(ls, Cons(3, Nil))? + } + let ls = ([1, 2, 3, 4, 5] : FixedArray[_]).to_list() + @assertion.assert_eq(ls, Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil))))))? +} diff --git a/vec/moon.pkg.json b/fixedarray/moon.pkg.json similarity index 60% rename from vec/moon.pkg.json rename to fixedarray/moon.pkg.json index 1cece7ea..a27e09fc 100644 --- a/vec/moon.pkg.json +++ b/fixedarray/moon.pkg.json @@ -1,8 +1,10 @@ { "import": [ "moonbitlang/core/builtin", + "moonbitlang/core/assertion", + "moonbitlang/core/coverage", + "moonbitlang/core/array", "moonbitlang/core/iter" - ], - "test_import": ["moonbitlang/core/assertion"] + ] } diff --git a/array/slice.mbt b/fixedarray/slice.mbt similarity index 69% rename from array/slice.mbt rename to fixedarray/slice.mbt index 4f970d9a..0066fe7d 100644 --- a/array/slice.mbt +++ b/fixedarray/slice.mbt @@ -12,29 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -priv struct ArraySlice[T] { - array : Array[T] +priv struct FixedArraySlice[T] { + array : FixedArray[T] start : Int end : Int } -fn length[T](self : ArraySlice[T]) -> Int { +fn length[T](self : FixedArraySlice[T]) -> Int { self.end - self.start } -fn op_get[T](self : ArraySlice[T], index : Int) -> T { +fn op_get[T](self : FixedArraySlice[T], index : Int) -> T { self.array[self.start + index] } -fn op_set[T](self : ArraySlice[T], index : Int, value : T) -> Unit { +fn op_set[T](self : FixedArraySlice[T], index : Int, value : T) -> Unit { self.array[self.start + index] = value } -fn swap[T](self : ArraySlice[T], a : Int, b : Int) -> Unit { +fn swap[T](self : FixedArraySlice[T], a : Int, b : Int) -> Unit { self.array.swap(self.start + a, self.start + b) } -fn reverse[T](self : ArraySlice[T]) -> Unit { +fn reverse[T](self : FixedArraySlice[T]) -> Unit { let mid_len = self.length() / 2 for i = 0; i < mid_len; i = i + 1 { let j = self.length() - i - 1 @@ -42,6 +42,6 @@ fn reverse[T](self : ArraySlice[T]) -> Unit { } } -fn slice[T](self : ArraySlice[T], start : Int, end : Int) -> ArraySlice[T] { +fn slice[T](self : FixedArraySlice[T], start : Int, end : Int) -> FixedArraySlice[T] { { array: self.array, start: self.start + start, end: self.start + end } } diff --git a/fixedarray/sort.mbt b/fixedarray/sort.mbt new file mode 100644 index 00000000..337935ba --- /dev/null +++ b/fixedarray/sort.mbt @@ -0,0 +1,506 @@ +// Copyright 2024 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Sorts the array +/// +/// It's an stable sort(it will not reorder equal elements). The time complexity is *O*(*n* \* log(*n*)) in the worst case. +/// +/// # Example +/// +/// ``` +/// let arr = [5, 4, 3, 2, 1] +/// arr.stable_sort() +/// debug(arr) //output: [1, 2, 3, 4, 5] +/// ``` +pub fn stable_sort[T : Compare](self : FixedArray[T]) -> Unit { + timsort({ array: self, start: 0, end: self.length() }) +} + +struct TimSortRun { + len : Int + start : Int +} + +/// The algorithm identifies strictly descending and non-descending subsequences, which are called +/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed +/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are +/// satisfied: +/// +/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len` +/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len` +/// +/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case. +fn timsort[T : Compare](arr : FixedArraySlice[T]) -> Unit { + // Slices of up to this length get sorted using insertion sort. + let max_insertion = 20 + + // Short arrays get sorted in-place via insertion sort to avoid allocations. + let len = arr.length() + if len <= max_insertion { + insertion_sort(arr) + } + let mut end = 0 + let mut start = 0 + let runs : Array[TimSortRun] = Array::new() + while end < len { + let (streak_end, was_reversed) = find_streak(arr.slice(start, arr.end)) + end += streak_end + if was_reversed { + arr.slice(start, end).reverse() + } + // Insert some more elements into the run if it's too short. Insertion sort is faster than + // merge sort on short sequences, so this significantly improves performance. + end = provide_sorted_batch(arr, start, end) + runs.push({ start, len: end - start }) + start = end + while true { + match collapse(runs, len) { + Some(r) => { + let left = runs[r] + let right = runs[r + 1] + merge(arr.slice(left.start, right.start + right.len), left.len) + runs[r + 1] = { start: left.start, len: left.len + right.len } + runs.remove(r) |> ignore + } + None => break + } + } + } +} + +fn insertion_sort[T : Compare](arr : FixedArraySlice[T]) -> Unit { + for i = 1, len = arr.length(); i < len; i = i + 1 { + for j = i; j > 0 && arr[j] < arr[j - 1]; j = j - 1 { + arr.swap(j, j - 1) + } + } +} + +/// Merges non-decreasing runs `arr[..mid]` and `arr[mid..]`. Copy `arr[mid..]` to buf and merge +/// `buf` and `arr[..mid]` to `arr[..]` +fn merge[T : Compare](arr : FixedArraySlice[T], mid : Int) -> Unit { + let buf_len = arr.length() - mid + let buf : FixedArray[T] = FixedArray::make(buf_len, arr[mid]) + for i = 0; i < buf.length(); i = i + 1 { + buf[i] = arr[mid + i] + } + let buf = { array: buf, start: 0, end: buf_len } + let buf_remaining = for p1 = mid - 1, p2 = buf_len - 1, p = mid + buf_len - 1; p1 >= + 0 && p2 >= 0; { + if arr[p1] > buf[p2] { + arr[p] = arr[p1] + continue p1 - 1, p2, p - 1 + } else { + arr[p] = buf[p2] + continue p1, p2 - 1, p - 1 + } + } else { + p2 + } + for i = buf_remaining; i >= 0; i = i - 1 { + arr[i] = buf[i] + } +} + +/// Finds a streak of presorted elements starting at the beginning of the slice. Returns the first +/// value that is not part of said streak, and a bool denoting whether the streak was reversed. +/// Streaks can be increasing or decreasing. +fn find_streak[T : Compare](arr : FixedArraySlice[T]) -> (Int, Bool) { + let len = arr.length() + if len < 2 { + return (len, false) + } + let assume_reverse = arr[1] < arr[0] + if assume_reverse { + for end = 2 { + if end < len && arr[end] < arr[end - 1] { + continue end + 1 + } else { + break (end, true) + } + } + } else { + for end = 2 { + if end < len && arr[end] >= arr[end - 1] { + continue end + 1 + } else { + break (end, false) + } + } + } +} + +fn provide_sorted_batch[T : Compare]( + arr : FixedArraySlice[T], + start : Int, + end : Int +) -> Int { + let len = arr.length() + // This value is a balance between least comparisons and best performance, as + // influenced by for example cache locality. + let min_insertion_run = 10 + // Insert some more elements into the run if it's too short. Insertion sort is faster than + // merge sort on short sequences, so this significantly improves performance. + let start_end_diff = end - start + if start_end_diff < min_insertion_run && end < len { + // v[start_found..end] are elements that are already sorted in the input. We want to extend + // the sorted region to the left, so we push up MIN_INSERTION_RUN - 1 to the right. Which is + // more efficient that trying to push those already sorted elements to the left. + let sort_end = minimum(len, start + min_insertion_run) + insertion_sort(arr.slice(start, sort_end)) + sort_end + } else { + end + } +} + +// TimSort is infamous for its buggy implementations, as described here: +// http://envisage-project.eu/timsort-specification-and-verification/ +// +// This function correctly checks invariants for the top four runs. Additionally, if the top +// run starts at index 0, it will always demand a merge operation until the stack is fully +// collapsed, in order to complete the sort. +fn collapse(runs : Array[TimSortRun], stop : Int) -> Option[Int] { + let n : Int = runs.length() + if n >= 2 && (runs[n - 1].start + runs[n - 1].len == stop || runs[n - 2].len <= + runs[n - 1].len || n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len || + n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len) { + if n >= 3 && runs[n - 3].len < runs[n - 1].len { + Some(n - 3) + } else { + Some(n - 2) + } + } else { + None + } +} + +/// Sorts the array +/// +/// It's an in-place, unstable sort(it will reorder equal elements). The time complexity is O(n log n) in the worst case. +/// +/// # Example +/// +/// ``` +/// let arr = [5, 4, 3, 2, 1] +/// arr.sort() +/// debug(arr) //output: [1, 2, 3, 4, 5] +/// ``` +pub fn sort[T : Compare](self : FixedArray[T]) -> Unit { + quick_sort( + { array: self, start: 0, end: self.length() }, + None, + get_limit(self.length()), + ) +} + +fn quick_sort[T : Compare]( + arr : FixedArraySlice[T], + pred : Option[T], + limit : Int +) -> Unit { + let mut limit = limit + let mut arr = arr + let mut pred = pred + let mut was_partitioned = true + let mut balanced = true + let bubble_sort_len = 16 + while true { + let len = arr.length() + if len <= bubble_sort_len { + if len >= 2 { + bubble_sort(arr) + } + return + } + // Too many imbalanced partitions may lead to O(n^2) performance in quick sort. + // If the limit is reached, use heap sort to ensure O(n log n) performance. + if limit == 0 { + heap_sort(arr) + return + } + let (pivot_index, likely_sorted) = choose_pivot(arr) + // Try bubble sort if the array is likely already sorted. + if was_partitioned && balanced && likely_sorted { + if try_bubble_sort(arr) { + return + } + } + let (pivot, partitioned) = partition(arr, pivot_index) + was_partitioned = partitioned + balanced = minimum(pivot, len - pivot) >= len / 8 + if not(balanced) { + limit -= 1 + } + match pred { + Some(pred) => + // pred is less than all elements in arr + // If pivot euqals to pred, then we can skip all elements that are equal to pred. + if pred == arr[pivot] { + let mut i = pivot + while i < len && pred == arr[i] { + i = i + 1 + } + arr = arr.slice(i, len) + continue + } + _ => () + } + let left = arr.slice(0, pivot) + let right = arr.slice(pivot + 1, len) + // Reduce the stack depth by only call quick_sort on the smaller partition. + if left.length() < right.length() { + quick_sort(left, pred, limit) + arr = right + pred = Some(arr[pivot]) + } else { + quick_sort(right, Some(arr[pivot]), limit) + arr = left + } + } +} + +fn get_limit(len : Int) -> Int { + let mut len = len + let mut limit = 0 + while len > 0 { + len = len / 2 + limit += 1 + } + limit +} + +/// Try to sort the array with bubble sort. +/// +/// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). +/// +/// Returns whether the array is sorted. +fn try_bubble_sort[T : Compare]( + arr : FixedArraySlice[T], + ~max_tries : Int = 8 +) -> Bool { + let mut tries = 0 + for i = 1; i < arr.length(); i = i + 1 { + let mut sorted = true + for j = i; j > 0 && arr[j - 1] > arr[j]; j = j - 1 { + sorted = false + arr.swap(j, j - 1) + } + if not(sorted) { + tries += 1 + if tries > max_tries { + return false + } + } + } + true +} + +/// Try to sort the array with bubble sort. +/// +/// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). +/// +/// Returns whether the array is sorted. +fn bubble_sort[T : Compare](arr : FixedArraySlice[T]) -> Unit { + for i = 1; i < arr.length(); i = i + 1 { + for j = i; j > 0 && arr[j - 1] > arr[j]; j = j - 1 { + arr.swap(j, j - 1) + } + } +} + +test "try_bubble_sort" { + let arr : FixedArray[_] = [8, 7, 6, 5, 4, 3, 2, 1] + let sorted = try_bubble_sort({ array: arr, start: 0, end: 8 }) + @assertion.assert_eq(sorted, true)? + @assertion.assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8])? +} + +fn partition[T : Compare]( + arr : FixedArraySlice[T], + pivot_index : Int +) -> (Int, Bool) { + arr.swap(pivot_index, arr.length() - 1) + let pivot = arr[arr.length() - 1] + let mut i = 0 + let mut partitioned = true + for j = 0; j < arr.length() - 1; j = j + 1 { + if arr[j] < pivot { + if i != j { + arr.swap(i, j) + partitioned = false + } + i = i + 1 + } + } + arr.swap(i, arr.length() - 1) + (i, partitioned) +} + +/// Choose a pivot index for quick sort. +/// +/// It avoids worst case performance by choosing a pivot that is likely to be close to the median. +/// +/// Returns the pivot index and whether the array is likely sorted. +fn choose_pivot[T : Compare](arr : FixedArraySlice[T]) -> (Int, Bool) { + let len = arr.length() + let use_median_of_medians = 50 + let max_swaps = 4 * 3 + let mut swaps = 0 + let b = len / 4 * 2 + if len >= 8 { + let a = len / 4 * 1 + let c = len / 4 * 3 + fn sort_2(a : Int, b : Int) { + if arr[a] > arr[b] { + arr.swap(a, b) + swaps += 1 + } + } + + fn sort_3(a : Int, b : Int, c : Int) { + sort_2(a, b) + sort_2(b, c) + sort_2(a, b) + } + + if len > use_median_of_medians { + sort_3(a - 1, a, a + 1) + sort_3(b - 1, b, b + 1) + sort_3(c - 1, c, c + 1) + } + sort_3(a, b, c) + } + if swaps == max_swaps { + arr.reverse() + (len - b - 1, true) + } else { + (b, swaps == 0) + } +} + +fn heap_sort[T : Compare](arr : FixedArraySlice[T]) -> Unit { + let len = arr.length() + for i = len / 2 - 1; i >= 0; i = i - 1 { + sift_down(arr, i) + } + for i = len - 1; i > 0; i = i - 1 { + arr.swap(0, i) + sift_down(arr.slice(0, i), 0) + } +} + +fn sift_down[T : Compare](arr : FixedArraySlice[T], index : Int) -> Unit { + let mut index = index + let len = arr.length() + let mut child = index * 2 + 1 + while child < len { + if child + 1 < len && arr[child] < arr[child + 1] { + child = child + 1 + } + if arr[index] >= arr[child] { + return + } + arr.swap(index, child) + index = child + child = index * 2 + 1 + } +} + +fn test_sort(f : (FixedArray[Int]) -> Unit) -> Result[Unit, String] { + let arr : FixedArray[_] = [5, 4, 3, 2, 1] + f(arr) + @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? + let arr : FixedArray[_] = [5, 5, 5, 5, 1] + f(arr) + @assertion.assert_eq(arr, [1, 5, 5, 5, 5])? + let arr : FixedArray[_] = [1, 2, 3, 4, 5] + f(arr) + @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? + { + let arr = FixedArray::make(1000, 0) + for i = 0; i < 1000; i = i + 1 { + arr[i] = 1000 - i - 1 + } + for i = 10; i < 1000; i = i + 10 { + arr.swap(i, i - 1) + } + f(arr) + let expected = FixedArray::make(1000, 0) + for i = 0; i < 1000; i = i + 1 { + expected[i] = i + } + @assertion.assert_eq(arr, expected)? + } + Ok(()) +} + +test "heap_sort" { + test_sort(fn(arr) { heap_sort({ array: arr, start: 0, end: arr.length() }) })? +} + +test "bubble_sort" { + test_sort( + fn(arr) { bubble_sort({ array: arr, start: 0, end: arr.length() }) }, + )? +} + +test "sort" { + test_sort(fn(arr) { arr.sort() })? +} + +test "stable_sort" { + let arr : FixedArray[_] = [5, 1, 3, 4, 2] + arr.stable_sort() + @assertion.assert_eq(arr, [1, 2, 3, 4, 5])? + let arr = FixedArray::make(1000, 0) + for i = 0; i < 1000; i = i + 1 { + arr[i] = 1000 - i - 1 + } + for i = 10; i < 1000; i = i + 10 { + arr.swap(i, i - 1) + } + arr.stable_sort() + let expected = FixedArray::make(1000, 0) + for i = 0; i < 1000; i = i + 1 { + expected[i] = i + } + @assertion.assert_eq(arr, expected)? +} + +pub fn is_sorted[T : Compare](arr : FixedArray[T]) -> Bool { + for i = 1; i < arr.length(); i = i + 1 { + if arr[i] < arr[i - 1] { + break false + } + } else { + true + } +} + +test "stable_sort_complex" { + let run_lens = [86, 64, 21, 20, 22] + let total_len = run_lens.fold_left(init=0, fn { acc, x => acc + x }) + let arr = FixedArray::make(total_len, 0) + let mut index = 0 + for i = 0, len = run_lens.length(); i < len; i = i + 1 { + for j = 0; j < run_lens[i]; j = j + 1 { + arr[index] = j + index += 1 + } + } + @assertion.assert_false(is_sorted(arr))? + arr.stable_sort() + @assertion.assert_true(is_sorted(arr))? +} diff --git a/vec/sort_by.mbt b/fixedarray/sort_by.mbt similarity index 75% rename from vec/sort_by.mbt rename to fixedarray/sort_by.mbt index 178cf9e0..14801a27 100644 --- a/vec/sort_by.mbt +++ b/fixedarray/sort_by.mbt @@ -12,40 +12,40 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Sorts the vector with a key extraction function. +/// Sorts the array with a key extraction function. /// /// It's an in-place, unstable sort(it will reorder equal elements). The time complexity is O(n log n) in the worst case. /// /// # Example /// /// ``` -/// let arr = Vec::[5, 3, 2, 4, 1] +/// let arr = [5, 3, 2, 4, 1] /// arr.sort_by_key(fn (x) {-x}) -/// debug(arr) //output: Vec::[5, 4, 3, 2, 1] +/// debug(arr) //output: [5, 4, 3, 2, 1] /// ``` -pub fn sort_by_key[T, K : Compare](self : Vec[T], map : (T) -> K) -> Unit { +pub fn sort_by_key[T, K : Compare](self : FixedArray[T], map : (T) -> K) -> Unit { quick_sort_by( - self.op_as_view(start=0, end=self.len), + { array: self, start: 0, end: self.length() }, fn(a, b) { map(a).compare(map(b)) }, None, get_limit(self.length()), ) } -/// Sorts the vector with a custom comparison function. +/// Sorts the array with a custom comparison function. /// /// It's an in-place, unstable sort(it will reorder equal elements). The time complexity is O(n log n) in the worst case. /// /// # Example /// /// ``` -/// let arr = Vec::[5, 3, 2, 4, 1] +/// let arr = [5, 3, 2, 4, 1] /// arr.sort_by(fn (a, b) { a - b }) -/// debug(arr) //output: Vec::[1, 2, 3, 4, 5] +/// debug(arr) //output: [1, 2, 3, 4, 5] /// ``` -pub fn sort_by[T](self : Vec[T], cmp : (T, T) -> Int) -> Unit { +pub fn sort_by[T](self : FixedArray[T], cmp : (T, T) -> Int) -> Unit { quick_sort_by( - self.op_as_view(start=0, end=self.len), + { array: self, start: 0, end: self.length() }, cmp, None, get_limit(self.length()), @@ -53,7 +53,7 @@ pub fn sort_by[T](self : Vec[T], cmp : (T, T) -> Int) -> Unit { } fn quick_sort_by[T]( - arr : VecView[T], + arr : FixedArraySlice[T], cmp : (T, T) -> Int, pred : Option[T], limit : Int @@ -100,13 +100,13 @@ fn quick_sort_by[T]( while i < len && cmp(pred, arr[i]) == 0 { i = i + 1 } - arr = arr.op_as_view(start=i, end=len) + arr = arr.slice(i, len) continue } _ => () } - let left = arr.op_as_view(start=0, end=pivot) - let right = arr.op_as_view(start=pivot + 1, end=len) + let left = arr.slice(0, pivot) + let right = arr.slice(pivot + 1, len) // Reduce the stack depth by only call quick_sort on the smaller partition. if left.length() < right.length() { quick_sort_by(left, cmp, pred, limit) @@ -125,7 +125,7 @@ fn quick_sort_by[T]( /// /// Returns whether the array is sorted. fn try_bubble_sort_by[T]( - arr : VecView[T], + arr : FixedArraySlice[T], cmp : (T, T) -> Int, ~max_tries : Int = 8 ) -> Bool { @@ -151,7 +151,7 @@ fn try_bubble_sort_by[T]( /// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). /// /// Returns whether the array is sorted. -fn bubble_sort_by[T](arr : VecView[T], cmp : (T, T) -> Int) -> Unit { +fn bubble_sort_by[T](arr : FixedArraySlice[T], cmp : (T, T) -> Int) -> Unit { for i = 1; i < arr.length(); i = i + 1 { for j = i; j > 0 && cmp(arr[j - 1], arr[j]) > 0; j = j - 1 { arr.swap(j, j - 1) @@ -159,8 +159,18 @@ fn bubble_sort_by[T](arr : VecView[T], cmp : (T, T) -> Int) -> Unit { } } +test "try_bubble_sort" { + let arr : FixedArray[_] = [8, 7, 6, 5, 4, 3, 2, 1] + let sorted = try_bubble_sort_by( + { array: arr, start: 0, end: 8 }, + fn(a, b) { a - b }, + ) + @assertion.assert_eq(sorted, true)? + @assertion.assert_eq(arr, [1, 2, 3, 4, 5, 6, 7, 8])? +} + fn partition_by[T]( - arr : VecView[T], + arr : FixedArraySlice[T], cmp : (T, T) -> Int, pivot_index : Int ) -> (Int, Bool) { @@ -186,7 +196,7 @@ fn partition_by[T]( /// It avoids worst case performance by choosing a pivot that is likely to be close to the median. /// /// Returns the pivot index and whether the array is likely sorted. -fn choose_pivot_by[T](arr : VecView[T], cmp : (T, T) -> Int) -> (Int, Bool) { +fn choose_pivot_by[T](arr : FixedArraySlice[T], cmp : (T, T) -> Int) -> (Int, Bool) { let len = arr.length() let use_median_of_medians = 50 let max_swaps = 4 * 3 @@ -223,18 +233,22 @@ fn choose_pivot_by[T](arr : VecView[T], cmp : (T, T) -> Int) -> (Int, Bool) { } } -fn heap_sort_by[T](arr : VecView[T], cmp : (T, T) -> Int) -> Unit { +fn heap_sort_by[T](arr : FixedArraySlice[T], cmp : (T, T) -> Int) -> Unit { let len = arr.length() for i = len / 2 - 1; i >= 0; i = i - 1 { sift_down_by(arr, i, cmp) } for i = len - 1; i > 0; i = i - 1 { arr.swap(0, i) - sift_down_by(arr.op_as_view(start=0, end=i), 0, cmp) + sift_down_by(arr.slice(0, i), 0, cmp) } } -fn sift_down_by[T](arr : VecView[T], index : Int, cmp : (T, T) -> Int) -> Unit { +fn sift_down_by[T]( + arr : FixedArraySlice[T], + index : Int, + cmp : (T, T) -> Int +) -> Unit { let mut index = index let len = arr.length() let mut child = index * 2 + 1 @@ -250,3 +264,35 @@ fn sift_down_by[T](arr : VecView[T], index : Int, cmp : (T, T) -> Int) -> Unit { child = index * 2 + 1 } } + +test "heap_sort" { + test_sort( + fn(arr) { + heap_sort_by( + { array: arr, start: 0, end: arr.length() }, + fn(a, b) { a - b }, + ) + }, + )? +} + +test "bubble_sort" { + test_sort( + fn(arr) { + bubble_sort_by( + { array: arr, start: 0, end: arr.length() }, + fn(a, b) { a - b }, + ) + }, + )? +} + +test "sort" { + test_sort(fn(arr) { arr.sort() })? +} + +test "sort_by" { + let arr = [5, 1, 3, 4, 2] + arr.sort_by_key(fn(x) { -x }) + @assertion.assert_eq(arr, [5, 4, 3, 2, 1])? +} diff --git a/vec/utils.mbt b/fixedarray/utils.mbt similarity index 100% rename from vec/utils.mbt rename to fixedarray/utils.mbt diff --git a/hashmap/hashmap.mbt b/hashmap/hashmap.mbt index a709b618..a62424fc 100644 --- a/hashmap/hashmap.mbt +++ b/hashmap/hashmap.mbt @@ -21,7 +21,7 @@ pub fn HashMap::new[K, V]() -> HashMap[K, V] { size: 0, capacity: default_init_capacity, growAt: calc_grow_threshold(default_init_capacity), - entries: @array.new(default_init_capacity, fn() { Empty }), + entries: Array::make(default_init_capacity, Empty), } } @@ -169,11 +169,11 @@ fn grow[K : Hash + Eq, V](self : HashMap[K, V]) -> Unit { self.capacity = default_init_capacity self.growAt = calc_grow_threshold(self.capacity) self.size = 0 - self.entries = @array.new(self.capacity, fn() { Empty }) + self.entries = Array::make(self.capacity, Empty) return } let old_entries = self.entries - self.entries = @array.new(self.capacity * 2, fn() { Empty }) + self.entries = Array::make(self.capacity * 2, Empty) self.capacity = self.capacity * 2 self.growAt = calc_grow_threshold(self.capacity) self.size = 0 diff --git a/hashset/hashset.mbt b/hashset/hashset.mbt index a52e00d1..b99ed3e4 100644 --- a/hashset/hashset.mbt +++ b/hashset/hashset.mbt @@ -21,7 +21,7 @@ pub fn HashSet::new[K]() -> HashSet[K] { size: 0, capacity: default_init_capacity, growAt: calc_grow_threshold(default_init_capacity), - entries: @array.new(default_init_capacity, fn() { Empty }), + entries: Array::make(default_init_capacity, Empty), } } @@ -233,11 +233,11 @@ fn grow[K : Hash + Eq](self : HashSet[K]) -> Unit { self.capacity = default_init_capacity self.growAt = calc_grow_threshold(self.capacity) self.size = 0 - self.entries = @array.new(self.capacity, fn() { Empty }) + self.entries = Array::make(self.capacity, Empty) return } let old_entries = self.entries - self.entries = @array.new(self.capacity * 2, fn() { Empty }) + self.entries = Array::make(self.capacity * 2, Empty) self.capacity = self.capacity * 2 self.growAt = calc_grow_threshold(self.capacity) self.size = 0 diff --git a/immutable_set/immutable_set.mbt b/immutable_set/immutable_set.mbt index c6ba2f10..554b2afa 100644 --- a/immutable_set/immutable_set.mbt +++ b/immutable_set/immutable_set.mbt @@ -68,19 +68,19 @@ pub fn to_list[T : Compare](self : ImmutableSet[T]) -> List[T] { } /// Convert ImmutableSet[T] to Array[T], the result must be ordered. -pub fn to_array[T : Compare](self : ImmutableSet[T]) -> Array[T] { +pub fn to_fixed_array[T : Compare](self : ImmutableSet[T]) -> FixedArray[T] { match self { Empty => [] Node(~left, ~value, ~right, ..) => - to_array(left) + [value] + to_array(right) + to_fixed_array(left) + [value] + to_fixed_array(right) } } -/// Convert ImmutableSet[T] to Vec[T], the result must be ordered. -pub fn to_vec[T : Compare](self : ImmutableSet[T]) -> @vec.Vec[T] { +/// Convert ImmutableSet[T] to Array[T], the result must be ordered. +pub fn to_array[T : Compare](self : ImmutableSet[T]) -> Array[T] { // This approach is better than using vec splicing implementation // due to the existence of scaling costs for Vec - @vec.from_array(self.to_array()) + Array::from_fixed_array(self.to_fixed_array()) } /// Remove the smallest value, diff --git a/immutable_set/immutable_set_test.mbt b/immutable_set/immutable_set_test.mbt index 146408a0..ed3683a6 100644 --- a/immutable_set/immutable_set_test.mbt +++ b/immutable_set/immutable_set_test.mbt @@ -193,13 +193,6 @@ test "to_array" { )? } -test "to_vec" { - inspect( - ImmutableSet::[7, 2, 9, 4, 5, 6, 3, 8, 1].to_vec(), - content="Vec::[1, 2, 3, 4, 5, 6, 7, 8, 9]", - )? -} - test "from_array" { inspect( ImmutableSet::[7, 2, 9, 4, 5, 6, 3, 8, 1], diff --git a/immutable_set/moon.pkg.json b/immutable_set/moon.pkg.json index 603752be..d34be33a 100644 --- a/immutable_set/moon.pkg.json +++ b/immutable_set/moon.pkg.json @@ -2,8 +2,8 @@ "import": [ "moonbitlang/core/builtin", "moonbitlang/core/list", - "moonbitlang/core/vec", "moonbitlang/core/array", + "moonbitlang/core/fixedarray", "moonbitlang/core/coverage", "moonbitlang/core/iter" ] diff --git a/immutable_vec/immutable_vec.mbt b/immutable_vec/immutable_vec.mbt index 8f85ee8a..f61d1d38 100644 --- a/immutable_vec/immutable_vec.mbt +++ b/immutable_vec/immutable_vec.mbt @@ -198,28 +198,6 @@ test "from_array" { )? } -/// Create a persistent vector from a vector. -/// -/// # Example -/// ``` -/// let v = ImmutableVec::from_vector(@vec.from_array([1, 2, 3, 4, 5])) -/// ``` -pub fn ImmutableVec::from_vector[T](vec : @vec.Vec[T]) -> ImmutableVec[T] { - new_by_leaves( - vec.length(), - fn(s, l) { @array.new_with_index(l, fn(i) { vec[s + i] }) }, - ) -} - -test "from_vector" { - let v : ImmutableVec[Int] = ImmutableVec::from_vector( - @vec.from_array([1, 2, 3, 4, 5]), - ) - let v1 : ImmutableVec[Int] = ImmutableVec::from_vector(@vec.from_array([])) - inspect(v, content="ImmutableVec::[1, 2, 3, 4, 5]")? - inspect(v1, content="ImmutableVec::[]")? -} - /// Iterate over the vector. /// /// # Example @@ -284,9 +262,9 @@ test "iteri" { let mut s = 0 v.iteri(fn(i, e) { s = s + i * e }) inspect(s, content="40")? - let vec = @vec.new() + let vec = Array::new() v.iteri(fn(i, _e) { vec.push(i) }) - inspect(vec, content="Vec::[0, 1, 2, 3, 4]")? + inspect(vec, content="[0, 1, 2, 3, 4]")? } pub fn op_equal[T : Eq]( @@ -521,7 +499,7 @@ test "mix" { for i = 0; i < large_const; i = i + 1 { v = v.push(i) } - let vec = @vec.new() + let vec = Array::new() v.iteri(fn(i, _e) { vec.push(i) }) for i = 0; i < large_const; i = i + 1 { @assertion.assert_eq(vec[i], i)? diff --git a/immutable_vec/moon.pkg.json b/immutable_vec/moon.pkg.json index 951e7479..471c665e 100644 --- a/immutable_vec/moon.pkg.json +++ b/immutable_vec/moon.pkg.json @@ -1,9 +1,9 @@ { "import": [ "moonbitlang/core/builtin", - "moonbitlang/core/array", + "moonbitlang/core/fixedarray", "moonbitlang/core/coverage", - "moonbitlang/core/vec", + "moonbitlang/core/array", "moonbitlang/core/assertion" ] -} \ No newline at end of file +} diff --git a/json/json.mbt b/json/json.mbt index aae13b3f..5fef1b32 100644 --- a/json/json.mbt +++ b/json/json.mbt @@ -26,7 +26,7 @@ test "get as null" { inspect(JsonValue::Number(1.0) |> as_null, content="None")? inspect(JsonValue::String("Hello World") |> as_null, content="None")? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]) |> as_null, + JsonValue::Array([JsonValue::String("Hello World")]) |> as_null, content="None", )? inspect( @@ -55,7 +55,7 @@ test "get as bool" { inspect(JsonValue::Number(1.0) |> as_bool, content="None")? inspect(JsonValue::String("Hello World") |> as_bool, content="None")? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]) |> as_bool, + JsonValue::Array([JsonValue::String("Hello World")]) |> as_bool, content="None", )? inspect( @@ -84,7 +84,7 @@ test "get as number" { inspect(JsonValue::Number(1.0) |> as_number, content="Some(1.0)")? inspect(JsonValue::String("Hello World") |> as_number, content="None")? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]) |> as_number, + JsonValue::Array([JsonValue::String("Hello World")]) |> as_number, content="None", )? inspect( @@ -116,7 +116,7 @@ test "get as string" { content="Some(Hello World)", )? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]) |> as_string, + JsonValue::Array([JsonValue::String("Hello World")]) |> as_string, content="None", )? inspect( @@ -132,7 +132,7 @@ test "get as string" { } /// Try to get this element as an Array -pub fn as_array(self : JsonValue) -> Option[@vec.Vec[JsonValue]] { +pub fn as_array(self : JsonValue) -> Option[Array[JsonValue]] { match self { Array(arr) => Some(arr) _ => None @@ -150,23 +150,23 @@ test "get as array" { inspect(JsonValue::Number(1.0) |> as_array, content="None")? inspect(JsonValue::String("Hello World") |> as_array, content="None")? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]) |> as_array, - content="Some(Vec::[String(\"Hello World\")])", + JsonValue::Array([JsonValue::String("Hello World")]) |> as_array, + content="Some([String(\"Hello World\")])", )? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]).item(0).bind( + JsonValue::Array([JsonValue::String("Hello World")]).item(0).bind( as_string, ), content="Some(Hello World)", )? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]).item(1).bind( + JsonValue::Array([JsonValue::String("Hello World")]).item(1).bind( as_string, ), content="None", )? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]).item(0).bind( + JsonValue::Array([JsonValue::String("Hello World")]).item(0).bind( as_number, ), content="None", @@ -198,27 +198,27 @@ pub fn value(self : JsonValue, key : String) -> Option[JsonValue] { test "get as object" { inspect( - JsonValue::Null |> as_object |> Option::map(@map.Map::to_vec), + JsonValue::Null |> as_object |> Option::map(@map.Map::to_array), content="None", )? inspect( - JsonValue::Boolean(false) |> as_object |> Option::map(@map.Map::to_vec), + JsonValue::Boolean(false) |> as_object |> Option::map(@map.Map::to_array), content="None", )? inspect( - JsonValue::Number(1.0) |> as_object |> Option::map(@map.Map::to_vec), + JsonValue::Number(1.0) |> as_object |> Option::map(@map.Map::to_array), content="None", )? inspect( JsonValue::String("Hello World") |> as_object - |> Option::map(@map.Map::to_vec), + |> Option::map(@map.Map::to_array), content="None", )? inspect( - JsonValue::Array(@vec.Vec::[JsonValue::String("Hello World")]) + JsonValue::Array([JsonValue::String("Hello World")]) |> as_object - |> Option::map(@map.Map::to_vec), + |> Option::map(@map.Map::to_array), content="None", )? inspect( @@ -229,8 +229,8 @@ test "get as object" { ], ) |> as_object - |> Option::map(@map.Map::to_vec), - content="Some(Vec::[(key, String(\"key\")), (value, Number(100.0))])", + |> Option::map(@map.Map::to_array), + content="Some([(key, String(\"key\")), (value, Number(100.0))])", )? inspect( JsonValue::Object( @@ -276,11 +276,11 @@ test "deep access" { ( "key", JsonValue::Array( - @vec.Vec::[ + [ JsonValue::Number(1.0), JsonValue::Boolean(true), JsonValue::Null, - JsonValue::Array(@vec.Vec::[]), + JsonValue::Array([]), JsonValue::Object( @map.Map::[ ("key", JsonValue::String("value")), @@ -307,21 +307,21 @@ test "deep access" { )? inspect( json.value("key").bind(as_array), - content="Some(Vec::[Number(1.0), Boolean(true), Null, Array(Vec::[]), Object(Map::[(\"key\", String(\"value\")), (\"value\", Number(100.0))])])", + content="Some([Number(1.0), Boolean(true), Null, Array([]), Object(Map::[(\"key\", String(\"value\")), (\"value\", Number(100.0))])])", )? inspect( json.value("key").bind(fn { array => array.item(3) }).bind(as_array), - content="Some(Vec::[])", + content="Some([])", )? inspect( json.value("key").bind(fn { array => array.item(4) }).bind(as_object).map( - @map.Map::to_vec, + @map.Map::to_array, ), - content="Some(Vec::[(key, String(\"value\")), (value, Number(100.0))])", + content="Some([(key, String(\"value\")), (value, Number(100.0))])", )? inspect( - json.value("obj").bind(as_object).map(@map.Map::to_vec), - content="Some(Vec::[])", + json.value("obj").bind(as_object).map(@map.Map::to_array), + content="Some([])", )? inspect( (fn() { json.value("key")?.item(4)?.value("value")?.as_number() })(), diff --git a/json/moon.pkg.json b/json/moon.pkg.json index 4ed5a2da..46372c58 100644 --- a/json/moon.pkg.json +++ b/json/moon.pkg.json @@ -5,7 +5,7 @@ "moonbitlang/core/string", "moonbitlang/core/bool", "moonbitlang/core/tuple", - "moonbitlang/core/vec", + "moonbitlang/core/array", "moonbitlang/core/map", "moonbitlang/core/coverage", "moonbitlang/core/assertion", @@ -13,4 +13,4 @@ "moonbitlang/core/unit", "moonbitlang/core/option" ] -} \ No newline at end of file +} diff --git a/json/types.mbt b/json/types.mbt index d861be45..40888042 100644 --- a/json/types.mbt +++ b/json/types.mbt @@ -17,6 +17,6 @@ pub enum JsonValue { Boolean(Bool) Number(Double) String(String) - Array(@vec.Vec[JsonValue]) + Array(Array[JsonValue]) Object(@map.Map[String, JsonValue]) } derive(Eq, Debug, Show) diff --git a/linked_hashmap/impl.mbt b/linked_hashmap/impl.mbt index d0006b09..be979504 100644 --- a/linked_hashmap/impl.mbt +++ b/linked_hashmap/impl.mbt @@ -21,7 +21,7 @@ pub fn LinkedHashMap::new[K, V]() -> LinkedHashMap[K, V] { size: 0, capacity: default_init_capacity, growAt: calc_grow_threshold(default_init_capacity), - entries: @array.new(default_init_capacity, fn() { Empty }), + entries: Array::make(default_init_capacity, Empty), head: None, tail: None, } @@ -251,11 +251,11 @@ fn grow[K : Hash + Eq, V](self : LinkedHashMap[K, V]) -> Unit { self.capacity = default_init_capacity self.growAt = calc_grow_threshold(self.capacity) self.size = 0 - self.entries = @array.new(self.capacity, fn() { Empty }) + self.entries = Array::make(self.capacity, Empty) return } let old_head = self.head - self.entries = @array.new(self.capacity * 2, fn() { Empty }) + self.entries = Array::make(self.capacity * 2, Empty) self.capacity = self.capacity * 2 self.growAt = calc_grow_threshold(self.capacity) self.size = 0 diff --git a/map/map.mbt b/map/map.mbt index ed175995..5393bcf2 100644 --- a/map/map.mbt +++ b/map/map.mbt @@ -109,15 +109,15 @@ pub fn filter_with_key[K : Compare, V]( } } -/// Convert to a vector of key-value pairs. -pub fn to_vec[K, V](self : Map[K, V]) -> @vec.Vec[(K, V)] { - let vec = @vec.new() - self.iter(fn(k, v) { vec.push((k, v)) }) - vec +/// Convert to an array of key-value pairs. +pub fn to_array[K, V](self : Map[K, V]) -> Array[(K, V)] { + let arr = Array::new() + self.iter(fn(k, v) { arr.push((k, v)) }) + arr } pub fn op_equal[K : Eq, V : Eq](self : Map[K, V], other : Map[K, V]) -> Bool { - self.to_vec() == other.to_vec() + self.to_array() == other.to_array() } /// The ratio between the sizes of the left and right subtrees. diff --git a/map/map_test.mbt b/map/map_test.mbt index 4b5aa5fc..06dec6e1 100644 --- a/map/map_test.mbt +++ b/map/map_test.mbt @@ -195,14 +195,6 @@ test "to_desc_list" { )? } -test "to_vec" { - let m = Map::[(1, "one"), (2, "two"), (3, "three")] - let v = m.to_vec() - @assertion.assert_eq(v[0], (1, "one"))? - @assertion.assert_eq(v[1], (2, "two"))? - @assertion.assert_eq(v[2], (3, "three"))? -} - test "keys" { let m = Map::[(1, "one"), (2, "two"), (3, "three")] @assertion.assert_eq(m.keys_list(), List::[1, 2, 3])? diff --git a/map/moon.pkg.json b/map/moon.pkg.json index c6db2925..232ce872 100644 --- a/map/moon.pkg.json +++ b/map/moon.pkg.json @@ -5,7 +5,7 @@ "moonbitlang/core/tuple", "moonbitlang/core/string", - "moonbitlang/core/vec", + "moonbitlang/core/array", "moonbitlang/core/coverage" ], "test_import": ["moonbitlang/core/immutable_set","moonbitlang/core/list"] diff --git a/mutable_set/moon.pkg.json b/mutable_set/moon.pkg.json index ef249381..f660050a 100644 --- a/mutable_set/moon.pkg.json +++ b/mutable_set/moon.pkg.json @@ -3,8 +3,8 @@ "moonbitlang/core/builtin", "moonbitlang/core/assertion", "moonbitlang/core/list", - "moonbitlang/core/vec", "moonbitlang/core/array", + "moonbitlang/core/fixedarray", "moonbitlang/core/option", "moonbitlang/core/coverage" ] diff --git a/mutable_set/mutable_set.mbt b/mutable_set/mutable_set.mbt index a01606c5..4f426879 100644 --- a/mutable_set/mutable_set.mbt +++ b/mutable_set/mutable_set.mbt @@ -769,13 +769,6 @@ pub fn to_array[T : Compare](self : MutableSet[T]) -> Array[T] { aux(self.root) } -/// Convert MutableSet[T] to Vec[T], the result must be ordered. -pub fn to_vec[T : Compare](self : MutableSet[T]) -> @vec.Vec[T] { - // This approach is better than vec splicing implementation - // due to the scaling cost of Vec - @vec.from_array(self.to_array()) -} - /// Convert MutableSet[T] to String, the result must be ordered. pub fn to_string[T : Compare + Show](self : MutableSet[T]) -> String { match self.root { @@ -816,8 +809,8 @@ fn to_string[T : Show](self : Node[T]) -> String { // # Convert from // Convert from other data structures to MutableSet. -/// Initialize an MutableSet[T] from a Array[T] -pub fn MutableSet::from_array[T : Compare](array : Array[T]) -> MutableSet[T] { +/// Initialize an MutableSet[T] from a FixedArray[T] +pub fn MutableSet::from_fixed_array[T : Compare](array : FixedArray[T]) -> MutableSet[T] { let set = MutableSet::new() for i = 0; i < array.length(); i = i + 1 { set.add(array[i]) @@ -832,10 +825,10 @@ pub fn MutableSet::from_list[T : Compare](list : List[T]) -> MutableSet[T] { set } -/// Initialize an MutableSet[T] from a Vec[T] -pub fn MutableSet::from_vec[T : Compare](vec : @vec.Vec[T]) -> MutableSet[T] { +/// Initialize an MutableSet[T] from a Array[T] +pub fn MutableSet::from_array[T : Compare](array : Array[T]) -> MutableSet[T] { let set = MutableSet::new() - vec.iter(fn(x) { set.add(x) }) + array.iter(fn(x) { set.add(x) }) set } @@ -1233,13 +1226,6 @@ test "to_array" { inspect((MutableSet::[] : MutableSet[Int]).to_array(), content="[]")? } -test "to_vec" { - inspect( - MutableSet::[7, 2, 9, 4, 5, 6, 3, 8, 1].to_vec(), - content="Vec::[1, 2, 3, 4, 5, 6, 7, 8, 9]", - )? -} - test "to_string" { inspect(MutableSet::[1, 2, 3, 4, 5], content="MutableSet::[1, 2, 3, 4, 5]")? inspect((MutableSet::[] : MutableSet[Int]), content="MutableSet::[]")? @@ -1269,13 +1255,6 @@ test "from_array" { )? } -test "from_vec" { - inspect( - MutableSet::from_vec(@vec.from_array([7, 2, 9, 4, 5, 6, 3, 8, 1])), - content="MutableSet::[1, 2, 3, 4, 5, 6, 7, 8, 9]", - )? -} - test "is_empty" { inspect((MutableSet::[] : MutableSet[Int]).is_empty(), content="true")? inspect(MutableSet::[1].is_empty(), content="false")? diff --git a/vec/sort.mbt b/vec/sort.mbt deleted file mode 100644 index 5bd32c8a..00000000 --- a/vec/sort.mbt +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2024 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Sorts the vector in place. -/// -/// It's an in-place, unstable sort(it will reorder equal elements). The time complexity is O(n log n) in the worst case. -/// -/// # Example -/// -/// ``` -/// let arr = Vec::[5, 4, 3, 2, 1] -/// arr.sort() -/// debug(arr) //output: Vec::[1, 2, 3, 4, 5] -/// ``` -pub fn sort[T : Compare](self : Vec[T]) -> Unit { - quick_sort( - self.op_as_view(start=0, end=self.len), - None, - get_limit(self.length()), - ) -} - -fn quick_sort[T : Compare]( - arr : VecView[T], - pred : Option[T], - limit : Int -) -> Unit { - let mut limit = limit - let mut arr = arr - let mut pred = pred - let mut was_partitioned = true - let mut balanced = true - let bubble_sort_len = 16 - while true { - let len = arr.length() - if len <= bubble_sort_len { - if len >= 2 { - bubble_sort(arr) - } - return - } - // Too many imbalanced partitions may lead to O(n^2) performance in quick sort. - // If the limit is reached, use heap sort to ensure O(n log n) performance. - if limit == 0 { - heap_sort(arr) - return - } - let (pivot_index, likely_sorted) = choose_pivot(arr) - // Try bubble sort if the array is likely already sorted. - if was_partitioned && balanced && likely_sorted { - if try_bubble_sort(arr) { - return - } - } - let (pivot, partitioned) = partition(arr, pivot_index) - was_partitioned = partitioned - balanced = minimum(pivot, len - pivot) >= len / 8 - if not(balanced) { - limit -= 1 - } - match pred { - Some(pred) => - // pred is less than all elements in arr - // If pivot euqals to pred, then we can skip all elements that are equal to pred. - if pred == arr[pivot] { - let mut i = pivot - while i < len && pred == arr[i] { - i = i + 1 - } - arr = arr.op_as_view(start=i, end=len) - continue - } - _ => () - } - let left = arr.op_as_view(start=0, end=pivot) - let right = arr.op_as_view(start=pivot + 1, end=len) - // Reduce the stack depth by only call quick_sort on the smaller partition. - if left.length() < right.length() { - quick_sort(left, pred, limit) - arr = right - pred = Some(arr[pivot]) - } else { - quick_sort(right, Some(arr[pivot]), limit) - arr = left - } - } -} - -fn get_limit(len : Int) -> Int { - let mut len = len - let mut limit = 0 - while len > 0 { - len = len / 2 - limit += 1 - } - limit -} - -/// Try to sort the array with bubble sort. -/// -/// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). -/// -/// Returns whether the array is sorted. -fn try_bubble_sort[T : Compare](arr : VecView[T], ~max_tries : Int = 8) -> Bool { - let mut tries = 0 - for i = 1; i < arr.length(); i = i + 1 { - let mut sorted = true - for j = i; j > 0 && arr[j - 1] > arr[j]; j = j - 1 { - sorted = false - arr.swap(j, j - 1) - } - if not(sorted) { - tries += 1 - if tries > max_tries { - return false - } - } - } - true -} - -/// Try to sort the array with bubble sort. -/// -/// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). -/// -/// Returns whether the array is sorted. -fn bubble_sort[T : Compare](arr : VecView[T]) -> Unit { - for i = 1; i < arr.length(); i = i + 1 { - for j = i; j > 0 && arr[j - 1] > arr[j]; j = j - 1 { - arr.swap(j, j - 1) - } - } -} - -fn partition[T : Compare](arr : VecView[T], pivot_index : Int) -> (Int, Bool) { - arr.swap(pivot_index, arr.length() - 1) - let pivot = arr[arr.length() - 1] - let mut i = 0 - let mut partitioned = true - for j = 0; j < arr.length() - 1; j = j + 1 { - if arr[j] < pivot { - if i != j { - arr.swap(i, j) - partitioned = false - } - i = i + 1 - } - } - arr.swap(i, arr.length() - 1) - (i, partitioned) -} - -/// Choose a pivot index for quick sort. -/// -/// It avoids worst case performance by choosing a pivot that is likely to be close to the median. -/// -/// Returns the pivot index and whether the array is likely sorted. -fn choose_pivot[T : Compare](arr : VecView[T]) -> (Int, Bool) { - let len = arr.length() - let use_median_of_medians = 50 - let max_swaps = 4 * 3 - let mut swaps = 0 - let b = len / 4 * 2 - if len >= 8 { - let a = len / 4 * 1 - let c = len / 4 * 3 - fn sort_2(a : Int, b : Int) { - if arr[a] > arr[b] { - arr.swap(a, b) - swaps += 1 - } - } - - fn sort_3(a : Int, b : Int, c : Int) { - sort_2(a, b) - sort_2(b, c) - sort_2(a, b) - } - - if len > use_median_of_medians { - sort_3(a - 1, a, a + 1) - sort_3(b - 1, b, b + 1) - sort_3(c - 1, c, c + 1) - } - sort_3(a, b, c) - } - if swaps == max_swaps { - arr.reverse() - (len - b - 1, true) - } else { - (b, swaps == 0) - } -} - -fn heap_sort[T : Compare](arr : VecView[T]) -> Unit { - let len = arr.length() - for i = len / 2 - 1; i >= 0; i = i - 1 { - sift_down(arr, i) - } - for i = len - 1; i > 0; i = i - 1 { - arr.swap(0, i) - sift_down(arr.op_as_view(start=0, end=i), 0) - } -} - -fn sift_down[T : Compare](arr : VecView[T], index : Int) -> Unit { - let mut index = index - let len = arr.length() - let mut child = index * 2 + 1 - while child < len { - if child + 1 < len && arr[child] < arr[child + 1] { - child = child + 1 - } - if arr[index] >= arr[child] { - return - } - arr.swap(index, child) - index = child - child = index * 2 + 1 - } -} diff --git a/vec/vec.mbt b/vec/vec.mbt deleted file mode 100644 index 808c692b..00000000 --- a/vec/vec.mbt +++ /dev/null @@ -1,1010 +0,0 @@ -// Copyright 2024 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -fn UninitializedArray::make[T](size : Int) -> UninitializedArray[T] = "%make_array_maybe_uninit" - -fn op_get[T](self : UninitializedArray[T], index : Int) -> T = "%array_get" - -fn op_set[T](self : UninitializedArray[T], index : Int, value : T) = "%array_set" - -/// Converts the vector to a string. -pub fn to_string[T : Show](self : Vec[T]) -> String { - if self.len == 0 { - return "Vec::[]" - } - let first = self.buf[0] - // CR: format issues - for i = 1, init = "Vec::[\(first)" { - if i >= self.len { - break "\(init)]" - } - let cur = self.buf[i] - continue i + 1, "\(init), \(cur)" - } -} - -pub fn debug_write[T : Show](self : Vec[T], buf : Buffer) -> Unit { - buf.write_string(self.to_string()) -} - -/// Creates a new, empty vector. -pub fn Vec::new[T]() -> Vec[T] { - { buf: UninitializedArray::make(0), len: 0 } -} - -/// Creates a new, empty vector with a specified initial capacity. -pub fn Vec::with_capacity[T](cap : Int) -> Vec[T] { - { buf: UninitializedArray::make(cap), len: 0 } -} - -/// Creates a new vector from an array. -pub fn Vec::from_array[T](arr : Array[T]) -> Vec[T] { - let len = arr.length() - let buf = UninitializedArray::make(len) - for i = 0; i < len; i = i + 1 { - buf[i] = arr[i] - } - { buf, len } -} - -/// Returns the number of elements in the vector. -pub fn length[T](self : Vec[T]) -> Int { - self.len -} - -/// Returns the total number of elements the vector can hold without reallocating. -pub fn capacity[T](self : Vec[T]) -> Int { - self.buf.0.length() -} - -/// Reallocate the vector with a new capacity. -fn realloc[T](self : Vec[T]) -> Unit { - let old_cap = self.len - let new_cap = if old_cap == 0 { 8 } else { old_cap * 2 } - let new_buf = UninitializedArray::make(new_cap) - for i = 0; i < old_cap; i = i + 1 { - new_buf[i] = self.buf[i] - } - self.buf = new_buf -} - -/// Retrieves the element at the specified index from the vector. -/// -/// # Example -/// ``` -/// let v = Vec::new() -/// v.push(3) -/// println(v[0]) // 3 -/// ``` -/// @alert unsafe "Panic if index is out of bounds" -pub fn op_get[T](self : Vec[T], index : Int) -> T { - if index < 0 || index >= self.len { - let len = self.len - abort( - "index out of bounds: the len is from 0 to \(len) but the index is \(index)", - ) - } - self.buf[index] -} - -/// Retrieves the element at the specified index from the vector, or `None` if index is out of bounds -/// -/// # Example -/// ``` -/// let v = Vec::new() -/// v.push(3) -/// println(v.get(0)) // Some(3) -/// ``` -pub fn get[T](self : Vec[T], index : Int) -> Option[T] { - if index < 0 || index >= self.len { - return None - } - Some(self.buf[index]) -} - -/// Sets the value of the element at the specified index. -/// -/// # Example -/// ``` -/// let v = Vec::new() -/// v.push(3) -/// println(v[0]) // 3 -/// ``` -/// @alert unsafe "Panic if index is out of bounds." -pub fn op_set[T](self : Vec[T], index : Int, value : T) -> Unit { - if index < 0 || index >= self.len { - let len = self.len - abort( - "index out of bounds: the len is from 0 to \(len) but the index is \(index)", - ) - } - self.buf[index] = value -} - -/// Compares two vectors for equality. -pub fn op_equal[T : Eq](self : Vec[T], other : Vec[T]) -> Bool { - if self.len != other.len { - return false - } - for i = 0 { - // CR: format issue - if i >= self.len { - break true - } - if self[i] != other[i] { - break false - } - continue i + 1 - } -} - -fn deep_clone[T](vec : Vec[T]) -> Vec[T] { - let result = Vec::{ buf: UninitializedArray::make(vec.len), len: vec.len } - for i = 0; i < vec.len; i = i + 1 { - result[i] = vec[i] - } - result -} - -pub fn op_add[T](self : Vec[T], other : Vec[T]) -> Vec[T] { - if self.len == 0 { - deep_clone(other) - } else if other.len == 0 { - deep_clone(self) - } else { - let result = Vec::{ - buf: UninitializedArray::make(self.len + other.len), - len: self.len + other.len, - } - for i = 0; i < self.len; i = i + 1 { - result[i] = self[i] - } - for i = 0; i < other.len; i = i + 1 { - result[i + self.len] = other[i] - } - result - } -} - -/// Removes the last element from a vector and returns it, or `None` if it is empty. -/// -/// # Example -/// ``` -/// let v = Vec::[1, 2, 3] -/// v.pop() -/// ``` -pub fn pop[T](self : Vec[T]) -> Option[T] { - if self.len == 0 { - None - } else { - self.len -= 1 - // TODO: should fill the element with a dummy slot to avoid memory leak? - Some(self.buf[self.len]) - } -} - -/// Removes the last element from a vector and returns it. -/// -/// # Example -/// ``` -/// let v = Vec::[1, 2, 3] -/// v.pop_exn() // 3 -/// ``` -/// @alert unsafe "Panic if the vector is empty." -pub fn pop_exn[T](self : Vec[T]) -> T { - if self.len == 0 { - abort("pop from an empty Vec") - } - self.len -= 1 - self.buf[self.len] -} - -/// Adds an element to the end of the vector. -/// -/// If the vector is at capacity, it will be reallocated. -/// -/// # Example -/// ``` -/// let v = Vec::new() -/// v.push(3) -/// ``` -pub fn push[T](self : Vec[T], value : T) -> Unit { - if self.len == self.buf.0.length() { - self.realloc() - } - self.buf[self.len] = value - self.len += 1 -} - -/// Removes the specified range from the vector and returns it. -/// -/// This functions returns a vector range from `begin` to `end` `[begin, end)` -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// let vv = v.drain(1, 2) // vv = Vec::[4], v = Vec::[3, 5] -/// ``` -/// @alert unsafe "Panic if index is out of bounds." -pub fn drain[T](self : Vec[T], begin : Int, end : Int) -> Vec[T] { - if begin < 0 || begin >= self.len || end < 0 || end > self.len || begin > end { - let len = self.len - abort( - "index out of bounds: the len is \(len) but the index is (\(begin), \(end))", - ) - } - let num = end - begin - let v = Vec::with_capacity(num) - for i = begin; i < end; i = i + 1 { - v.buf[i - begin] = self[i] - } - v.len = num - for i = end; i < self.len; i = i + 1 { - self.buf[i - num] = self.buf[i] - } - self.len -= num - v -} - -/// Appends all the elements of other vector into self -/// -/// # Example -/// ``` -/// let v1 = Vec::[3, 4, 5] -/// let v2 = Vec::[6, 7, 8] -/// v1.append(v2) -/// ``` -/// TODO: could be made more efficient -pub fn append[T](self : Vec[T], other : Vec[T]) -> Unit { - for i = 0; i < other.len; i = i + 1 { - self.push(other[i]) - } -} - -/// Iterates over the elements of the vector. -/// -/// # Example -/// ``` -/// let v = Vec::with_capacity(3) -/// v.push(3) -/// v.push(4) -/// v.push(5) -/// let mut sum = 0 -/// v.iter(fn (x) {sum = sum + x}) -/// ``` -pub fn iter[T](self : Vec[T], f : (T) -> Unit) -> Unit { - for i = 0; i < self.len; i = i + 1 { - f(self[i]) - } -} - -pub fn iter_rev[T](self : Vec[T], f : (T) -> Unit) -> Unit { - for i = self.length() - 1; i >= 0; i = i - 1 { - f(self[i]) - } -} - -/// Iterates over the elements of the vector with index. -/// -/// # Example -/// ``` -/// let v = Vec::with_capacity(3) -/// v.push(3) -/// v.push(4) -/// v.push(5) -/// let mut sum = 0 -/// v.iteri(fn (i, x) {sum = sum + x + i}) -/// ``` -pub fn iteri[T](self : Vec[T], f : (Int, T) -> Unit) -> Unit { - for i = 0; i < self.len; i = i + 1 { - f(i, self[i]) - } -} - -pub fn iter_revi[T](self : Vec[T], f : (Int, T) -> Unit) -> Unit { - let len = self.length() - for i = 0; i < len; i = i + 1 { - f(i, self[len - i - 1]) - } -} - -/// Clears the vector, removing all values. -/// -/// This method has no effect on the allocated capacity of the vector, only setting the length to 0. -/// -/// # Example -/// ``` -/// let v = Vec::from_array([3, 4, 5]) -/// v.clear() -/// ``` -pub fn clear[T](self : Vec[T]) -> Unit { - self.len = 0 -} - -/// Maps a function over the elements of the vector. -/// -/// # Example -/// ``` -/// let v = Vec::from_array([3, 4, 5]) -/// v.map(fn (x) {x + 1}) -/// ``` -pub fn map[T, U](self : Vec[T], f : (T) -> U) -> Vec[U] { - if self.length() == 0 { - return Vec::{ buf: UninitializedArray::make(0), len: 0 } - } - let buf : UninitializedArray[U] = UninitializedArray::make(self.length()) - for i = 0; i < self.length(); i = i + 1 { - buf[i] = f(self[i]) - } - Vec::{ buf, len: self.length() } -} - -/// Maps a function over the elements of the vector in place. -/// -/// # Example -/// ``` -/// let v = Vec::from_array([3, 4, 5]) -/// v.map_inplace(fn (x) {x + 1}) -/// ``` -pub fn map_inplace[T](self : Vec[T], f : (T) -> T) -> Unit { - for i = 0; i < self.length(); i = i + 1 { - self[i] = f(self[i]) - } -} - -/// Maps a function over the elements of the vector with index. -/// -/// # Example -/// ``` -/// let v = Vec::from_array([3, 4, 5]) -/// v.mapi(fn (i, x) {x + i}) -/// ``` -pub fn mapi[T, U](self : Vec[T], f : (Int, T) -> U) -> Vec[U] { - if self.length() == 0 { - return Vec::{ buf: UninitializedArray::make(0), len: 0 } - } - let buf : UninitializedArray[U] = UninitializedArray::make(self.length()) - for i = 0; i < self.length(); i = i + 1 { - buf[i] = f(i, self[i]) - } - Vec::{ buf, len: self.length() } -} - -/// Maps a function over the elements of the vector with index in place. -/// -/// # Example -/// ``` -/// let v = Vec::from_array([3, 4, 5]) -/// v.mapi_inplace(fn (i, x) {x + i}) -/// ``` -pub fn mapi_inplace[T](self : Vec[T], f : (Int, T) -> T) -> Unit { - for i = 0; i < self.length(); i = i + 1 { - self[i] = f(i, self[i]) - } -} - -/// Test if the vector is empty. -/// -/// # Example -/// ``` -/// let v = Vec::new() -/// v.is_empty() -/// ``` -pub fn is_empty[T](self : Vec[T]) -> Bool { - self.len == 0 -} - -/// Test if the vector is sorted. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.is_sorted() // true -/// ``` -pub fn is_sorted[T : Compare](self : Vec[T]) -> Bool { - for i = 1 { - if i >= self.len { - break true - } - if self[i - 1] > self[i] { - break false - } - continue i + 1 - } -} - -/// Reverses the order of elements in the slice, in place. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.reverse() -/// ``` -pub fn reverse[T](self : Vec[T]) -> Unit { - for i = 0; i < self.len / 2; i = i + 1 { - let temp = self.buf[i] - self.buf[i] = self.buf[self.len - i - 1] - self.buf[self.len - i - 1] = temp - } -} - -/// Split the vector into two at the given index. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// let (v1, v2) = v.split_at(1) -/// ``` -/// TODO: perf could be optimized -/// @alert unsafe "Panic if index is out of bounds." -pub fn split_at[T](self : Vec[T], index : Int) -> (Vec[T], Vec[T]) { - if index < 0 || index >= self.len { - let len = self.len - abort( - "index out of bounds: the len is from 0 to \(len) but the index is \(index)", - ) - } - let v1 = Vec::with_capacity(index) - let v2 = Vec::with_capacity(self.len - index) - for i = 0; i < index; i = i + 1 { - v1.push(self.buf[i]) - } - for i = index; i < self.len; i = i + 1 { - v2.push(self.buf[i]) - } - (v1, v2) -} - -/// Checks if the vector contains an element. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.contains(3) -/// ``` -pub fn contains[T : Eq](self : Vec[T], value : T) -> Bool { - for i = 0; i < self.len; i = i + 1 { - if self.buf[i] == value { - break true - } - } else { - false - } -} - -/// Check if the vector starts with a given prefix. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.starts_with(Vec::[3, 4]) -/// ``` -pub fn starts_with[T : Eq](self : Vec[T], prefix : Vec[T]) -> Bool { - if prefix.len > self.len { - return false - } - for i = 0; i < prefix.len; i = i + 1 { - if self.buf[i] != prefix.buf[i] { - break false - } - } else { - true - } -} - -/// Check if the vector ends with a given suffix. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.ends_with(Vec::[5]) -/// ``` -pub fn ends_with[T : Eq](self : Vec[T], suffix : Vec[T]) -> Bool { - if suffix.len > self.len { - return false - } - for i = 0; i < suffix.len; i = i + 1 { - if self.buf[self.len - suffix.len + i] != suffix.buf[i] { - break false - } - } else { - true - } -} - -/// Strip a prefix from the vector. -/// -/// If the vector starts with the prefix, return the vector after the prefix, otherwise return None. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.strip_prefix(Vec::[3]) // Some(Vec::[4, 5]) -/// ``` -pub fn strip_prefix[T : Eq](self : Vec[T], prefix : Vec[T]) -> Option[Vec[T]] { - if self.starts_with(prefix) { - let v = Vec::with_capacity(self.len - prefix.len) - for i = prefix.len; i < self.len; i = i + 1 { - v.buf[i - prefix.len] = self.buf[i] - } - v.len = self.len - prefix.len - Some(v) - } else { - None - } -} - -/// Strip a suffix from the vector. -/// -/// If the vector ends with the suffix, return the vector before the suffix, otherwise return None. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.strip_suffix(Vec::[5]) // Some(Vec::[3, 4]) -/// ``` -pub fn strip_suffix[T : Eq](self : Vec[T], suffix : Vec[T]) -> Option[Vec[T]] { - if self.ends_with(suffix) { - let v = Vec::with_capacity(self.len - suffix.len) - for i = 0; i < self.len - suffix.len; i = i + 1 { - v.buf[i] = self.buf[i] - } - v.len = self.len - suffix.len - Some(v) - } else { - None - } -} - -/// Search the vector index for a given element. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.search(3) -/// ``` -pub fn search[T : Eq](self : Vec[T], value : T) -> Option[Int] { - for i = 0; i < self.len; i = i + 1 { - if self.buf[i] == value { - break Some(i) - } - } else { - None - } -} - -/// Swap two elements in the vector. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.swap(1, 2) -/// ``` -/// @alert unsafe "Panic if index is out of bounds." -pub fn swap[T](self : Vec[T], i : Int, j : Int) -> Unit { - if i >= self.len || j >= self.len || i < 0 || j < 0 { - let len = self.len - abort( - "index out of bounds: the len is from 0 to \(len) but the index is (\(i), \(j))", - ) - } - let temp = self.buf[i] - self.buf[i] = self.buf[j] - self.buf[j] = temp -} - -/// Remove an element from the vector at a given index. -/// -/// Removes and returns the element at position index within the vector, shifting all elements after it to the left. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.remove(1) -/// ``` -/// @alert unsafe "Panic if index is out of bounds." -pub fn remove[T](self : Vec[T], index : Int) -> T { - if index < 0 || index >= self.len { - let len = self.len - abort( - "index out of bounds: the len is from 0 to \(len) but the index is \(index)", - ) - } - let value = self.buf[index] - for i = index; i < self.len - 1; i = i + 1 { - self.buf[i] = self.buf[i + 1] - } - self.len = self.len - 1 - value -} - -/// Retains only the elements specified by the predicate. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// v.retain(fn(x) { x > 3 }) -/// ``` -/// TODO: perf could be improved -pub fn retain[T](self : Vec[T], f : (T) -> Bool) -> Unit { - let mut i = 0 - while i < self.len { - if f(self.buf[i]).not() { - self.remove(i) |> ignore - } else { - i = i + 1 - } - } -} - -/// Resize the vector to a new length with a default value. -/// -/// # Example -/// ``` -/// Vec::[3, 4, 5].resize(1) -/// ``` -/// @alert unsafe "Panic if new length is negative." -pub fn resize[T](self : Vec[T], new_len : Int, f : T) -> Unit { - if new_len < 0 { - abort("negative new length") - } - if new_len < self.len { - self.len = new_len - } else { - for i = self.len; i < new_len; i = i + 1 { - self.push(f) - } - } -} - -/// Inserts an element at a given index within the vector. -/// -/// # Example -/// ``` -/// Vec::[3, 4, 5].insert(1, 6) -/// ``` -/// @alert unsafe "Panic if index is out of bounds." -pub fn insert[T](self : Vec[T], index : Int, value : T) -> Unit { - if index < 0 || index > self.len { - let len = self.len - abort( - "index out of bounds: the len is from 0 to \(len) but the index is \(index)", - ) - } - if self.len == self.buf.0.length() { - self.realloc() - } - for i = self.len; i > index; i = i - 1 { - self.buf[i] = self.buf[i - 1] - } - self.buf[index] = value - self.len = self.len + 1 -} - -/// Fill the vector (by capacity) with a given value. -/// -/// # Example -/// ``` -/// Vec::with_capacity(3).fill(3) -/// ``` -pub fn fill[T](self : Vec[T], value : T) -> Unit { - for i = 0; i < self.capacity(); i = i + 1 { - self.buf[i] = value - } - self.len = self.capacity() -} - -/// Fill the vector by calling a function (By capacity). -/// -/// # Example -/// ``` -/// Vec::with_capacity(3).fill_with(fn() { 3 }) -/// ``` -pub fn fill_with[T](self : Vec[T], f : () -> T) -> Unit { - for i = 0; i < self.capacity(); i = i + 1 { - self.buf[i] = f() - } - self.len = self.capacity() -} - -/// Flattens a vector of vectors into a vector. -/// -/// # Example -/// ``` -/// Vec::[Vec::[3, 4], Vec::[5, 6]].flatten() -/// ``` -pub fn flatten[T](self : Vec[Vec[T]]) -> Vec[T] { - let v = Vec::new() - for i = 0; i < self.len; i = i + 1 { - v.append(self[i]) - } - v -} - -/// Create a vector by repeat a given vector for a given times. -/// -/// # Example -/// ``` -/// Vec::[3, 4].repeat(2) -/// ``` -pub fn repeat[T](self : Vec[T], times : Int) -> Vec[T] { - let v = Vec::with_capacity(self.len * times) - for i = 0; i < times; i = i + 1 { - v.append(self) - } - v -} - -/// Flattens a vector of vectors with a given separator. -/// -/// # Example -/// ``` -/// Vec::[Vec::[3, 4], Vec::[5, 6]].join(0) -/// ``` -pub fn join[T](self : Vec[Vec[T]], sep : T) -> Vec[T] { - let v = Vec::new() - for i = 0; i < self.len; i = i + 1 { - v.append(self[i]) - if i < self.len - 1 { - v.push(sep) - } - } - v -} - -/// Fold out values from an array according to certain rules. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_left(~init=0, fn { sum, elem => sum + elem }) -/// sum // 15 -/// ``` -pub fn fold_left[T, U](self : Vec[T], f : (U, T) -> U, ~init : U) -> U { - for i = 0, acc = init; i < self.length(); { - continue i + 1, f(acc, self[i]) - } else { - acc - } -} - -/// Fold out values from an array according to certain rules in reversed turn. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_right(~init=0, fn { sum, elem => sum + elem }) -/// sum // 15 -/// ``` -pub fn fold_right[T, U](self : Vec[T], f : (U, T) -> U, ~init : U) -> U { - for i = self.length() - 1, acc = init; i >= 0; { - continue i - 1, f(acc, self[i]) - } else { - acc - } -} - -/// Fold out values from an array according to certain rules with index. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_lefti(~init=0, fn { index, sum, elem => sum + index }) -/// sum // 10 -/// ``` -pub fn fold_lefti[T, U](self : Vec[T], f : (Int, U, T) -> U, ~init : U) -> U { - for i = 0, acc = init; i < self.length(); { - continue i + 1, f(i, acc, self[i]) - } else { - acc - } -} - -/// Fold out values from an array according to certain rules in reversed turn with index. -/// -/// # Example -/// ``` -/// let sum = [1, 2, 3, 4, 5].fold_righti(~init=0, fn { index, sum, elem => sum + index }) -/// sum // 10 -/// ``` -pub fn fold_righti[T, U](self : Vec[T], f : (Int, U, T) -> U, ~init : U) -> U { - for i = self.len - 1, acc = init; i >= 0; { - continue i - 1, f(self.len - i - 1, acc, self[i]) - } else { - acc - } -} - -/// Removes consecutive repeated elements in the vector according to the Eq trait. -/// -/// # Example -/// ``` -/// let v = Vec::[3, 4, 4, 5, 5, 5] -/// v.dedup() // v = Vec::[3, 4, 5] -/// ``` -pub fn dedup[T : Eq](self : Vec[T]) -> Unit { - for i = 0; i < self.len; i = i + 1 { - let mut j = i + 1 - while j < self.len { - if self[i] == self[j] { - self.remove(j) |> ignore - } else { - j = j + 1 - } - } - } -} - -/// Extract elements from the vector according to the given function. -/// -/// This function will remove the elements from the original vector and return a new vector. -/// # Example -/// ``` -/// let v = Vec::[3, 4, 5] -/// let vv = v.extract_if(fn(x) { x > 3 }) // vv = Vec::[4, 5], v = Vec::[3] -/// ``` -pub fn extract_if[T](self : Vec[T], f : (T) -> Bool) -> Vec[T] { - let v = Vec::new() - let indices = Vec::new() - for i = 0; i < self.len; i = i + 1 { - if f(self[i]) { - v.push(self[i]) - indices.push(i) - } - } - for i = 0; i < indices.len; i = i + 1 { - self.remove(indices[i] - i) |> ignore - } - v -} - -/// Group the elements of the vector into sized chunks. -/// -/// If the elements of the vector cannot be divided into equal-sized chunks, the last chunk will be smaller. -/// -/// # Example -/// ``` -/// let v = Vec::[1, 2, 3, 4, 5, 6, 7, 8, 9] -/// let chunks = v.chunks(3) // chunks = Vec::[Vec::[1, 2, 3], Vec::[4, 5, 6], Vec::[7, 8, 9]] -/// ``` -pub fn chunks[T](self : Vec[T], size : Int) -> Vec[Vec[T]] { - let chunks = Vec::new() - let mut i = 0 - while i < self.len { - let chunk = Vec::with_capacity(size) - for j = 0; j < size && i < self.len; j = j + 1 { - chunk.push(self[i]) - i = i + 1 - } - chunks.push(chunk) - } - chunks -} - -/// Group the elements of the vector into chunks based on a predicate. -/// -/// # Example -/// ``` -/// let v = Vec::[1, 1, 2, 3, 2, 3, 2, 3, 4] -/// let chunks = v.chunk_by(fn(x, y) { x <= y }) -/// // chunks = Vec::[Vec::[1, 1, 2, 3], Vec::[2, 3], Vec::[2, 3], Vec::[4]] -/// ``` -pub fn chunk_by[T](self : Vec[T], pred : (T, T) -> Bool) -> Vec[Vec[T]] { - let chunks = Vec::new() - let mut i = 0 - while i < self.len { - let chunk = Vec::new() - chunk.push(self[i]) - i = i + 1 - while i < self.len && pred(self[i - 1], self[i]) { - chunk.push(self[i]) - i = i + 1 - } - chunks.push(chunk) - } - chunks -} - -/// Split the vector into chunks based on a predicate. -/// -/// # Example -/// ``` -/// let v = Vec::[1, 0, 2, 0, 3, 0, 4] -/// let chunks = v.split(fn(x) { x == 0 }) -/// ``` -pub fn split[T](self : Vec[T], pred : (T) -> Bool) -> Vec[Vec[T]] { - let chunks = Vec::new() - let mut i = 0 - while i < self.len { - let chunk = Vec::new() - while i < self.len && pred(self[i]).not() { - chunk.push(self[i]) - i = i + 1 - } - chunks.push(chunk) - i = i + 1 - } - chunks -} - -/// Convert vec to list. -/// -/// # Example -/// -/// ``` -/// Vec::[1, 2, 3, 4, 5].to_list() -/// ``` -pub fn to_list[T](self : Vec[T]) -> List[T] { - for i = self.len - 1, list = List::Nil; i >= 0; { - continue i - 1, List::Cons(self[i], list) - } else { - list - } -} - -/// Reserves capacity to ensure that it can hold at least the number of elements -/// specified by the `capacity` argument. -/// -/// # Example -/// -/// ``` -/// let v = Vec::[1] -/// v.reserve_capacity(10) -/// println(v.capacity()) // 10 -/// ``` -pub fn reserve_capacity[T](self : Vec[T], capacity : Int) -> Unit { - if self.capacity() >= capacity { - return - } - let new_buf : UninitializedArray[T] = UninitializedArray::make(capacity) - for i = 0; i < self.length(); i = i + 1 { - new_buf[i] = self.buf[i] - } - self.buf = new_buf -} - -/// Shrinks the capacity of the vector as much as possible. -/// -/// # Example -/// -/// ``` -/// let v = Vec::with_capacity(10) -/// v.push(1) -/// v.push(2) -/// v.push(3) -/// println(v.capacity()) // >= 10 -/// v.shrink_to_fit() -/// println(v.capacity()) // >= 3 -/// ``` -pub fn shrink_to_fit[T](self : Vec[T]) -> Unit { - if self.capacity() <= self.length() { - return - } - let new_buf : UninitializedArray[T] = UninitializedArray::make(self.length()) - for i = 0; i < self.length(); i = i + 1 { - new_buf[i] = self.buf[i] - } - self.buf = new_buf -} - -pub fn as_iter[T](self : Vec[T]) -> @iter.Iter[T] { - @iter.Iter::_unstable_internal_make( - fn(yield) { - for i = 0, len = self.length(); i < len; i = i + 1 { - if yield(self[i]).not() { - break false - } - } else { - true - } - }, - ) -} diff --git a/vec/vec.mbti b/vec/vec.mbti deleted file mode 100644 index 2ec9efde..00000000 --- a/vec/vec.mbti +++ /dev/null @@ -1,88 +0,0 @@ -package moonbitlang/core/vec - -alias @moonbitlang/core/iter as @iter - -// Values - -// Types and methods -type Vec -impl Vec { - append[T](Self[T], Self[T]) -> Unit - as_iter[T](Self[T]) -> @iter.Iter[T] - capacity[T](Self[T]) -> Int - chunk_by[T](Self[T], (T, T) -> Bool) -> Self[Self[T]] - chunks[T](Self[T], Int) -> Self[Self[T]] - clear[T](Self[T]) -> Unit - contains[T : Eq](Self[T], T) -> Bool - debug_write[T : Show](Self[T], Buffer) -> Unit - dedup[T : Eq](Self[T]) -> Unit - drain[T](Self[T], Int, Int) -> Self[T] - ends_with[T : Eq](Self[T], Self[T]) -> Bool - extract_if[T](Self[T], (T) -> Bool) -> Self[T] - fill[T](Self[T], T) -> Unit - fill_with[T](Self[T], () -> T) -> Unit - flatten[T](Self[Self[T]]) -> Self[T] - fold_left[T, U](Self[T], (U, T) -> U, ~init : U) -> U - fold_lefti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U - fold_right[T, U](Self[T], (U, T) -> U, ~init : U) -> U - fold_righti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U - from_array[T](Array[T]) -> Self[T] - get[T](Self[T], Int) -> Option[T] - insert[T](Self[T], Int, T) -> Unit - is_empty[T](Self[T]) -> Bool - is_sorted[T : Compare + Eq](Self[T]) -> Bool - iter[T](Self[T], (T) -> Unit) -> Unit - iter_rev[T](Self[T], (T) -> Unit) -> Unit - iter_revi[T](Self[T], (Int, T) -> Unit) -> Unit - iteri[T](Self[T], (Int, T) -> Unit) -> Unit - join[T](Self[Self[T]], T) -> Self[T] - length[T](Self[T]) -> Int - map[T, U](Self[T], (T) -> U) -> Self[U] - map_inplace[T](Self[T], (T) -> T) -> Unit - mapi[T, U](Self[T], (Int, T) -> U) -> Self[U] - mapi_inplace[T](Self[T], (Int, T) -> T) -> Unit - new[T]() -> Self[T] - op_add[T](Self[T], Self[T]) -> Self[T] - op_as_view[T](Self[T], ~start : Int, ~end : Int) -> VecView[T] - op_equal[T : Eq](Self[T], Self[T]) -> Bool - op_get[T](Self[T], Int) -> T - op_set[T](Self[T], Int, T) -> Unit - pop[T](Self[T]) -> Option[T] - pop_exn[T](Self[T]) -> T - push[T](Self[T], T) -> Unit - remove[T](Self[T], Int) -> T - repeat[T](Self[T], Int) -> Self[T] - reserve_capacity[T](Self[T], Int) -> Unit - resize[T](Self[T], Int, T) -> Unit - retain[T](Self[T], (T) -> Bool) -> Unit - reverse[T](Self[T]) -> Unit - search[T : Eq](Self[T], T) -> Option[Int] - shrink_to_fit[T](Self[T]) -> Unit - sort[T : Compare + Eq](Self[T]) -> Unit - sort_by[T](Self[T], (T, T) -> Int) -> Unit - sort_by_key[T, K : Compare + Eq](Self[T], (T) -> K) -> Unit - split[T](Self[T], (T) -> Bool) -> Self[Self[T]] - split_at[T](Self[T], Int) -> Tuple[Self[T], Self[T]] - starts_with[T : Eq](Self[T], Self[T]) -> Bool - strip_prefix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] - strip_suffix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] - swap[T](Self[T], Int, Int) -> Unit - to_list[T](Self[T]) -> List[T] - to_string[T : Show](Self[T]) -> String - with_capacity[T](Int) -> Self[T] -} - -type VecView -impl VecView { - length[T](Self[T]) -> Int - op_as_view[T](Self[T], ~start : Int, ~end : Int) -> Self[T] - op_get[T](Self[T], Int) -> T - op_set[T](Self[T], Int, T) -> Unit - reverse[T](Self[T]) -> Unit - swap[T](Self[T], Int, Int) -> Unit -} - -// Traits - -// Extension Methods - diff --git a/vec/vec_test.mbt b/vec/vec_test.mbt deleted file mode 100644 index 2622e265..00000000 --- a/vec/vec_test.mbt +++ /dev/null @@ -1,467 +0,0 @@ -// Copyright 2024 International Digital Economy Academy -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -test "to_string" { - let empty : Vec[Int] = Vec::[] - inspect(empty, content="Vec::[]")? - let a0 = Vec::[0] - inspect(a0, content="Vec::[0]")? - a0.push(1) - inspect(a0, content="Vec::[0, 1]")? - a0.push(2) - inspect(a0, content="Vec::[0, 1, 2]")? -} - -test "realloc" { - let v : Vec[Int] = Vec::new() - v.realloc() - v.realloc() - @assertion.assert_eq(v.capacity(), 8)? - for i = 0; i < 9; i = i + 1 { - v.push(i) - } - @assertion.assert_eq(v.capacity(), 16)? -} - -test "get" { - let v = Vec::[3] - inspect(v.get(-1), content="None")? - inspect(v.get(0), content="Some(3)")? - inspect(v.get(1), content="None")? -} - -test "op_add" { - inspect((Vec::[] : Vec[Int]) + Vec::[], content="Vec::[]")? - inspect( - (Vec::[] : Vec[Int]) + Vec::[1, 2, 3, 4, 5], - content="Vec::[1, 2, 3, 4, 5]", - )? - inspect(Vec::[1, 2, 3, 4, 5] + Vec::[], content="Vec::[1, 2, 3, 4, 5]")? - inspect( - Vec::[1, 2, 3, 4, 5] + Vec::[6, 7, 8, 9, 10], - content="Vec::[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", - )? -} - -test "op_equal" { - let v1 = Vec::[3, 4, 5] - let v2 = Vec::[3, 4, 5] - let v3 = Vec::[3, 4, 6] - @assertion.assert_true(v1 == v2)? - @assertion.assert_false(v1 == v3)? -} - -test "pop" { - let v = Vec::new() - v.push(3) - v.push(4) - @assertion.assert_eq(v.length(), 2)? - @assertion.assert_eq(v.pop(), Some(4))? - @assertion.assert_eq(v.pop(), Some(3))? - @assertion.assert_eq(v.length(), 0)? - @assertion.assert_eq(v.pop(), None)? -} - -test "pop_exn" { - let v = Vec::new() - v.push(3) - v.push(4) - @assertion.assert_eq(v.length(), 2)? - @assertion.assert_eq(v.pop_exn(), 4)? - @assertion.assert_eq(v.pop_exn(), 3)? - @assertion.assert_eq(v.length(), 0)? -} - -test "push" { - let v = Vec::[3, 4, 5] - @assertion.assert_eq(v, Vec::[3, 4, 5])? - v[0] = 6 - @assertion.assert_eq(v, Vec::[6, 4, 5])? -} - -test "drain" { - let v = Vec::[3, 4, 5] - let v2 = Vec::[1, 2, 3, 4, 5] - let vv = v.drain(1, 2) - let v2v = v2.drain(2, 5) - @assertion.assert_eq(v, Vec::[3, 5])? - @assertion.assert_eq(vv, Vec::[4])? - @assertion.assert_eq(v2, Vec::[1, 2])? - @assertion.assert_eq(v2v, Vec::[3, 4, 5])? -} - -test "append" { - let v1 = Vec::[3, 4, 5] - let v2 = Vec::[6, 7, 8] - v1.append(v2) - @assertion.assert_eq(v1, Vec::[3, 4, 5, 6, 7, 8])? - @assertion.assert_eq(v1.length(), 6)? -} - -test "iter" { - let v = Vec::[3, 4, 5] - let mut sum = 0 - v.iter(fn(x) { sum = sum + x }) - @assertion.assert_eq(sum, 12)? -} - -test "iter_rev" { - let v = Vec::with_capacity(3) - v.push(3) - v.push(4) - v.push(5) - let mut sum = 0 - v.iter_rev(fn(x) { sum = sum + x }) - @assertion.assert_eq(sum, 12)? -} - -test "iteri" { - let v = Vec::[3, 4, 5] - let mut sum = 0 - v.iteri(fn(i, x) { sum = sum + x + i }) - @assertion.assert_eq(sum, 15)? -} - -test "iter_revi" { - let v = Vec::[3, 4, 5] - let mut sum = 0 - v.iter_revi(fn(i, x) { sum = sum + x + i }) - @assertion.assert_eq(sum, 15)? -} - -test "clear" { - let v = Vec::[3, 4, 5] - v.clear() - @assertion.assert_eq(v.length(), 0)? - @assertion.assert_eq(v.capacity(), 3)? -} - -test "map" { - let v = Vec::[3, 4, 5] - let e : Vec[Int] = Vec::[] - inspect(v.map(fn(x) { x + 1 }), content="Vec::[4, 5, 6]")? - inspect(v.map(fn(x) { x.to_string() }), content="Vec::[3, 4, 5]")? - inspect(e.map(fn(x) { x + 1 }), content="Vec::[]")? - // Is it correct? - inspect(Vec::["a", "b", "c"], content="Vec::[a, b, c]")? -} - -test "map_inplace" { - let v = Vec::[3, 4, 5] - let e : Vec[Int] = Vec::[] - v.map_inplace(fn(x) { x + 1 }) - e.map_inplace(fn(x) { x + 1 }) - inspect(v, content="Vec::[4, 5, 6]")? - inspect(e, content="Vec::[]")? -} - -test "mapi" { - let v = Vec::[3, 4, 5] - let e : Vec[Int] = Vec::[] - inspect(v.mapi(fn(i, x) { x + i }), content="Vec::[3, 5, 7]")? - inspect(v.mapi(fn(i, x) { (x + i).to_string() }), content="Vec::[3, 5, 7]")? - inspect(e.mapi(fn(_i, x) { x + 1 }), content="Vec::[]")? -} - -test "mapi_inplace" { - let v = Vec::[3, 4, 5] - let e : Vec[Int] = Vec::[] - v.mapi_inplace(fn(i, x) { x + i }) - e.mapi_inplace(fn(_i, x) { x + 1 }) - inspect(v, content="Vec::[3, 5, 7]")? - inspect(e, content="Vec::[]")? -} - -test "is_empty" { - let v = Vec::new() - @assertion.assert_true(v.is_empty())? - v.push(3) - @assertion.assert_false(v.is_empty())? -} - -test "is_sorted" { - let v : Vec[Int] = Vec::[] - @assertion.assert_true(v.is_sorted())? - let v = Vec::[3, 4, 5] - @assertion.assert_true(v.is_sorted())? - let v2 = Vec::[3, 5, 4] - @assertion.assert_false(v2.is_sorted())? -} - -test "reverse" { - let v = Vec::[3, 4, 5] - v.reverse() - @assertion.assert_eq(v, Vec::[5, 4, 3])? - v.reverse() - @assertion.assert_eq(v, Vec::[3, 4, 5])? - let v = Vec::[3, 4] - v.reverse() - @assertion.assert_eq(v, Vec::[4, 3])? - v.reverse() - @assertion.assert_eq(v, Vec::[3, 4])? - let empty : Vec[Int] = Vec::[] - @assertion.assert_eq(empty, empty)? -} - -test "split_at" { - let v = Vec::[3, 4, 5] - let (v1, v2) = v.split_at(1) - @assertion.assert_eq(v1, Vec::[3])? - @assertion.assert_eq(v2, Vec::[4, 5])? -} - -test "contains" { - let v = Vec::[3, 4, 5] - @assertion.assert_true(v.contains(3))? - @assertion.assert_true(v.contains(4))? - @assertion.assert_true(v.contains(5))? - @assertion.assert_false(v.contains(6))? -} - -test "starts_with" { - let v = Vec::[3, 4, 5] - @assertion.assert_true(v.starts_with(Vec::[3, 4]))? - @assertion.assert_true(v.starts_with(Vec::[3, 4, 5]))? - @assertion.assert_false(v.starts_with(Vec::[3, 4, 6]))? - @assertion.assert_false(v.starts_with(Vec::[3, 4, 5, 6]))? -} - -test "ends_with" { - let v = Vec::[3, 4, 5] - @assertion.assert_true(v.ends_with(Vec::[4, 5]))? - @assertion.assert_true(v.ends_with(Vec::[3, 4, 5]))? - @assertion.assert_false(v.ends_with(Vec::[3, 4, 6]))? - @assertion.assert_false(v.ends_with(Vec::[2, 3, 4, 5]))? -} - -test "strip_prefix" { - let v = Vec::[3, 4, 5] - let sv = v.strip_prefix(Vec::[3]) - @assertion.assert_eq(v, Vec::[3, 4, 5])? - @assertion.assert_eq(sv, Some(Vec::[4, 5]))? - @assertion.assert_eq(v.strip_prefix(Vec::[4]), None)? -} - -test "strip_suffix" { - let v = Vec::[3, 4, 5] - let sv = v.strip_suffix(Vec::[5]) - @assertion.assert_eq(v, Vec::[3, 4, 5])? - @assertion.assert_eq(sv, Some(Vec::[3, 4]))? - @assertion.assert_eq(v.strip_suffix(Vec::[4]), None)? -} - -test "search" { - let v = Vec::[3, 4, 5] - @assertion.assert_eq(v.search(3), Some(0))? - @assertion.assert_eq(v.search(4), Some(1))? - @assertion.assert_eq(v.search(5), Some(2))? - @assertion.assert_eq(v.search(6), None)? -} - -test "swap" { - let v = Vec::[3, 4, 5] - v.swap(0, 2) - @assertion.assert_eq(v, Vec::[5, 4, 3])? - v.swap(0, 2) - @assertion.assert_eq(v, Vec::[3, 4, 5])? -} - -test "remove" { - let v = Vec::[3, 4, 5] - @assertion.assert_eq(v.remove(1), 4)? - @assertion.assert_eq(v, Vec::[3, 5])? -} - -test "retain" { - let v = Vec::[3, 4, 5] - v.retain(fn(x) { x > 3 }) - @assertion.assert_eq(v, Vec::[4, 5])? -} - -test "resize" { - let v = Vec::[3, 4, 5] - v.resize(5, 0) - @assertion.assert_eq(v, Vec::[3, 4, 5, 0, 0])? - @assertion.assert_eq(v.length(), 5)? - let vv = Vec::[3, 4, 5] - vv.resize(2, 0) - @assertion.assert_eq(vv, Vec::[3, 4])? - @assertion.assert_eq(vv.length(), 2)? -} - -test "insert" { - let v = Vec::[3, 4, 5] - v.insert(1, 6) - @assertion.assert_eq(v, Vec::[3, 6, 4, 5])? - @assertion.assert_eq(v.length(), 4)? - @assertion.assert_eq(v.capacity(), 6)? -} - -test "fill" { - let v = Vec::with_capacity(3) - v.fill(3) - @assertion.assert_eq(v, Vec::[3, 3, 3])? - @assertion.assert_eq(v.length(), 3)? -} - -test "fill_with" { - let v = Vec::with_capacity(3) - v.fill_with(fn() { 3 }) - @assertion.assert_eq(v, Vec::[3, 3, 3])? - @assertion.assert_eq(v.length(), 3)? -} - -test "flatten" { - let v = Vec::[Vec::[3, 4], Vec::[5, 6]] - let vv = v.flatten() - @assertion.assert_eq(vv, Vec::[3, 4, 5, 6])? - @assertion.assert_eq(vv.length(), 4)? -} - -test "repeat" { - let v = Vec::[3, 4] - let vv = v.repeat(2) - @assertion.assert_eq(vv, Vec::[3, 4, 3, 4])? - @assertion.assert_eq(vv.length(), 4)? -} - -test "join" { - let v = Vec::[Vec::[3, 4], Vec::[5, 6]] - let vv = v.join(0) - @assertion.assert_eq(vv, Vec::[3, 4, 0, 5, 6])? - @assertion.assert_eq(vv.length(), 5)? -} - -test "fold_left" { - let sum = Vec::[1, 2, 3, 4, 5].fold_left( - init=0, - fn { sum, elem => sum + elem }, - ) - @assertion.assert_eq(sum, 15)? -} - -test "fold_right" { - let sum = Vec::[1, 2, 3, 4, 5].fold_right( - init=0, - fn { sum, elem => sum + elem }, - ) - @assertion.assert_eq(sum, 15)? -} - -test "fold_lefti" { - let sum = Vec::[1, 2, 3, 4, 5].fold_lefti( - init=0, - fn { index, sum, _elem => sum + index }, - ) - @assertion.assert_eq(sum, 10)? -} - -test "fold_righti" { - let sum = Vec::[1, 2, 3, 4, 5].fold_righti( - init=0, - fn { index, sum, _elem => sum + index }, - ) - @assertion.assert_eq(sum, 10)? -} - -test "dedup" { - let v = Vec::[3, 4, 4, 5, 5, 5] - v.dedup() - @assertion.assert_eq(v, Vec::[3, 4, 5])? - @assertion.assert_eq(v.length(), 3)? -} - -test "extract_if" { - let v = Vec::[3, 4, 5] - let vv = v.extract_if(fn(x) { x > 3 }) - @assertion.assert_eq(v, Vec::[3])? - @assertion.assert_eq(v.length(), 1)? - @assertion.assert_eq(vv, Vec::[4, 5])? -} - -test "chunks" { - let v = Vec::[1, 2, 3, 4, 5, 6, 7, 8, 9] - let chunks = v.chunks(3) - let chunks2 = v.chunks(2) - @assertion.assert_eq( - chunks, - Vec::[Vec::[1, 2, 3], Vec::[4, 5, 6], Vec::[7, 8, 9]], - )? - @assertion.assert_eq( - chunks2, - Vec::[Vec::[1, 2], Vec::[3, 4], Vec::[5, 6], Vec::[7, 8], Vec::[9]], - )? -} - -test "chunk_by" { - let v = Vec::[1, 1, 2, 3, 2, 3, 2, 3, 4] - let chunks = v.chunk_by(fn(x, y) { x <= y }) - @assertion.assert_eq(chunks[0], Vec::[1, 1, 2, 3])? - @assertion.assert_eq(chunks[1], Vec::[2, 3])? - @assertion.assert_eq(chunks[2], Vec::[2, 3, 4])? -} - -test "split" { - let v = Vec::[1, 0, 2, 0, 3, 0, 4] - let chunks = v.split(fn(x) { x == 0 }) - @assertion.assert_eq(chunks, Vec::[Vec::[1], Vec::[2], Vec::[3], Vec::[4]])? - @assertion.assert_eq(chunks.length(), 4)? -} - -test "to_list" { - let ls = Vec::[1, 2, 3, 4, 5].to_list() - @assertion.assert_eq(ls, Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil))))))? -} - -test "reserve_capacity" { - let v = Vec::[1] - v.reserve_capacity(10) - @assertion.assert_true(v.capacity() == 10)? -} - -test "reserve_capacity_2" { - let v = Vec::[1, 2, 3, 4, 5] - v.reserve_capacity(3) - @assertion.assert_true(v.capacity() == 5)? -} - -test "shrink_to_fit" { - let v = Vec::with_capacity(10) - v.push(1) - v.push(2) - v.push(3) - @assertion.assert_true(v.capacity() == 10)? - v.shrink_to_fit() - @assertion.assert_true(v.capacity() == 3)? -} - -test "shrink_to_fit_2" { - let v = Vec::[1, 2, 3] - @assertion.assert_true(v.capacity() == 3)? - v.shrink_to_fit() - @assertion.assert_true(v.capacity() == 3)? -} - -test "as_iter" { - let buf = Buffer::make(5) - Vec::[1, 2, 3, 4, 5].as_iter().iter( - fn { x => buf.write_string(x.to_string()) }, - ) - inspect(buf, content="12345")? - buf.reset() - Vec::[1, 2, 3, 4, 5].as_iter().take(2).iter( - fn { x => buf.write_string(x.to_string()) }, - ) - inspect(buf, content="12")? -} From 5c31c646d7a30a7599b5ea6b71a44ab57a1ad360 Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Mon, 20 May 2024 15:27:21 +0800 Subject: [PATCH 2/4] update mbti --- array/array.mbti | 76 +++----------------------------- builtin/builtin.mbti | 73 ++++++++++++++++++++++++++++++ fixedarray/fixedarray.mbti | 2 +- immutable_set/immutable_set.mbti | 3 +- immutable_vec/immutable_vec.mbti | 3 -- json/json.mbti | 5 +-- map/map.mbti | 4 +- mutable_set/mutable_set.mbti | 5 +-- 8 files changed, 85 insertions(+), 86 deletions(-) diff --git a/array/array.mbti b/array/array.mbti index 2ec9efde..bf623eda 100644 --- a/array/array.mbti +++ b/array/array.mbti @@ -1,85 +1,21 @@ -package moonbitlang/core/vec +package moonbitlang/core/array alias @moonbitlang/core/iter as @iter // Values +fn new_with_index[T](Int, (Int) -> T) -> Array[T] // Types and methods -type Vec -impl Vec { - append[T](Self[T], Self[T]) -> Unit +impl Array { as_iter[T](Self[T]) -> @iter.Iter[T] - capacity[T](Self[T]) -> Int - chunk_by[T](Self[T], (T, T) -> Bool) -> Self[Self[T]] - chunks[T](Self[T], Int) -> Self[Self[T]] - clear[T](Self[T]) -> Unit - contains[T : Eq](Self[T], T) -> Bool - debug_write[T : Show](Self[T], Buffer) -> Unit - dedup[T : Eq](Self[T]) -> Unit - drain[T](Self[T], Int, Int) -> Self[T] - ends_with[T : Eq](Self[T], Self[T]) -> Bool - extract_if[T](Self[T], (T) -> Bool) -> Self[T] - fill[T](Self[T], T) -> Unit - fill_with[T](Self[T], () -> T) -> Unit - flatten[T](Self[Self[T]]) -> Self[T] - fold_left[T, U](Self[T], (U, T) -> U, ~init : U) -> U - fold_lefti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U - fold_right[T, U](Self[T], (U, T) -> U, ~init : U) -> U - fold_righti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U - from_array[T](Array[T]) -> Self[T] - get[T](Self[T], Int) -> Option[T] - insert[T](Self[T], Int, T) -> Unit - is_empty[T](Self[T]) -> Bool - is_sorted[T : Compare + Eq](Self[T]) -> Bool - iter[T](Self[T], (T) -> Unit) -> Unit - iter_rev[T](Self[T], (T) -> Unit) -> Unit - iter_revi[T](Self[T], (Int, T) -> Unit) -> Unit - iteri[T](Self[T], (Int, T) -> Unit) -> Unit - join[T](Self[Self[T]], T) -> Self[T] - length[T](Self[T]) -> Int - map[T, U](Self[T], (T) -> U) -> Self[U] - map_inplace[T](Self[T], (T) -> T) -> Unit - mapi[T, U](Self[T], (Int, T) -> U) -> Self[U] - mapi_inplace[T](Self[T], (Int, T) -> T) -> Unit - new[T]() -> Self[T] - op_add[T](Self[T], Self[T]) -> Self[T] - op_as_view[T](Self[T], ~start : Int, ~end : Int) -> VecView[T] - op_equal[T : Eq](Self[T], Self[T]) -> Bool - op_get[T](Self[T], Int) -> T - op_set[T](Self[T], Int, T) -> Unit - pop[T](Self[T]) -> Option[T] - pop_exn[T](Self[T]) -> T - push[T](Self[T], T) -> Unit - remove[T](Self[T], Int) -> T - repeat[T](Self[T], Int) -> Self[T] - reserve_capacity[T](Self[T], Int) -> Unit - resize[T](Self[T], Int, T) -> Unit - retain[T](Self[T], (T) -> Bool) -> Unit - reverse[T](Self[T]) -> Unit - search[T : Eq](Self[T], T) -> Option[Int] - shrink_to_fit[T](Self[T]) -> Unit + blit_to[A](Self[A], Self[A], ~len : Int, ~src_offset : Int = .., ~dst_offset : Int = ..) -> Unit + copy[T](Self[T]) -> Self[T] sort[T : Compare + Eq](Self[T]) -> Unit sort_by[T](Self[T], (T, T) -> Int) -> Unit sort_by_key[T, K : Compare + Eq](Self[T], (T) -> K) -> Unit - split[T](Self[T], (T) -> Bool) -> Self[Self[T]] - split_at[T](Self[T], Int) -> Tuple[Self[T], Self[T]] - starts_with[T : Eq](Self[T], Self[T]) -> Bool - strip_prefix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] - strip_suffix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] - swap[T](Self[T], Int, Int) -> Unit - to_list[T](Self[T]) -> List[T] - to_string[T : Show](Self[T]) -> String - with_capacity[T](Int) -> Self[T] } - -type VecView -impl VecView { - length[T](Self[T]) -> Int - op_as_view[T](Self[T], ~start : Int, ~end : Int) -> Self[T] - op_get[T](Self[T], Int) -> T - op_set[T](Self[T], Int, T) -> Unit +impl ArrayView { reverse[T](Self[T]) -> Unit - swap[T](Self[T], Int, Int) -> Unit } // Traits diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index 7a4514ce..084e24cf 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -34,6 +34,79 @@ impl ArgsLoc { to_string(Self) -> String } +type Array +impl Array { + append[T](Self[T], Self[T]) -> Unit + capacity[T](Self[T]) -> Int + chunk_by[T](Self[T], (T, T) -> Bool) -> Self[Self[T]] + chunks[T](Self[T], Int) -> Self[Self[T]] + clear[T](Self[T]) -> Unit + contains[T : Eq](Self[T], T) -> Bool + debug_write[X : Debug](Self[X], Buffer) -> Unit + dedup[T : Eq](Self[T]) -> Unit + drain[T](Self[T], Int, Int) -> Self[T] + ends_with[T : Eq](Self[T], Self[T]) -> Bool + extract_if[T](Self[T], (T) -> Bool) -> Self[T] + fill[T](Self[T], T) -> Unit + fill_with[T](Self[T], () -> T) -> Unit + flatten[T](Self[Self[T]]) -> Self[T] + fold_left[T, U](Self[T], (U, T) -> U, ~init : U) -> U + fold_lefti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U + fold_right[T, U](Self[T], (U, T) -> U, ~init : U) -> U + fold_righti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U + from_fixed_array[T](Array[T]) -> Self[T] + get[T](Self[T], Int) -> Option[T] + insert[T](Self[T], Int, T) -> Unit + is_empty[T](Self[T]) -> Bool + is_sorted[T : Compare + Eq](Self[T]) -> Bool + iter[T](Self[T], (T) -> Unit) -> Unit + iter_rev[T](Self[T], (T) -> Unit) -> Unit + iter_revi[T](Self[T], (Int, T) -> Unit) -> Unit + iteri[T](Self[T], (Int, T) -> Unit) -> Unit + join[T](Self[Self[T]], T) -> Self[T] + length[T](Self[T]) -> Int + make[T](Int, T) -> Self[T] + map[T, U](Self[T], (T) -> U) -> Self[U] + map_inplace[T](Self[T], (T) -> T) -> Unit + mapi[T, U](Self[T], (Int, T) -> U) -> Self[U] + mapi_inplace[T](Self[T], (Int, T) -> T) -> Unit + new[T]() -> Self[T] + op_add[T](Self[T], Self[T]) -> Self[T] + op_as_view[T](Self[T], ~start : Int, ~end : Int) -> ArrayView[T] + op_equal[T : Eq](Self[T], Self[T]) -> Bool + op_get[T](Self[T], Int) -> T + op_set[T](Self[T], Int, T) -> Unit + pop[T](Self[T]) -> Option[T] + pop_exn[T](Self[T]) -> T + push[T](Self[T], T) -> Unit + remove[T](Self[T], Int) -> T + repeat[T](Self[T], Int) -> Self[T] + reserve_capacity[T](Self[T], Int) -> Unit + resize[T](Self[T], Int, T) -> Unit + retain[T](Self[T], (T) -> Bool) -> Unit + reverse[T](Self[T]) -> Unit + search[T : Eq](Self[T], T) -> Option[Int] + shrink_to_fit[T](Self[T]) -> Unit + split[T](Self[T], (T) -> Bool) -> Self[Self[T]] + split_at[T](Self[T], Int) -> Tuple[Self[T], Self[T]] + starts_with[T : Eq](Self[T], Self[T]) -> Bool + strip_prefix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] + strip_suffix[T : Eq](Self[T], Self[T]) -> Option[Self[T]] + swap[T](Self[T], Int, Int) -> Unit + to_list[T](Self[T]) -> List[T] + to_string[T : Show](Self[T]) -> String + with_capacity[T](Int) -> Self[T] +} + +type ArrayView +impl ArrayView { + length[T](Self[T]) -> Int + op_as_view[T](Self[T], ~start : Int, ~end : Int) -> Self[T] + op_get[T](Self[T], Int) -> T + op_set[T](Self[T], Int, T) -> Unit + swap[T](Self[T], Int, Int) -> Unit +} + type Buffer impl Buffer { expect(Self, ~content : String = .., ~loc : SourceLoc = _, ~args_loc : ArgsLoc = _) -> Result[Unit, String] diff --git a/fixedarray/fixedarray.mbti b/fixedarray/fixedarray.mbti index 60f7bf99..1238e080 100644 --- a/fixedarray/fixedarray.mbti +++ b/fixedarray/fixedarray.mbti @@ -1,4 +1,4 @@ -package moonbitlang/core/array +package moonbitlang/core/fixedarray alias @moonbitlang/core/iter as @iter diff --git a/immutable_set/immutable_set.mbti b/immutable_set/immutable_set.mbti index cd619bcc..a12fde99 100644 --- a/immutable_set/immutable_set.mbti +++ b/immutable_set/immutable_set.mbti @@ -1,7 +1,6 @@ package moonbitlang/core/immutable_set alias @moonbitlang/core/iter as @iter -alias @moonbitlang/core/vec as @vec // Values @@ -37,9 +36,9 @@ impl ImmutableSet { split[T : Compare + Eq](Self[T], T) -> Tuple[Self[T], Bool, Self[T]] subset[T : Compare + Eq](Self[T], Self[T]) -> Bool to_array[T : Compare + Eq](Self[T]) -> Array[T] + to_fixed_array[T : Compare + Eq](Self[T]) -> Array[T] to_list[T : Compare + Eq](Self[T]) -> List[T] to_string[T : Show + Compare + Eq](Self[T]) -> String - to_vec[T : Compare + Eq](Self[T]) -> @vec.Vec[T] union[T : Compare + Eq](Self[T], Self[T]) -> Self[T] } diff --git a/immutable_vec/immutable_vec.mbti b/immutable_vec/immutable_vec.mbti index 5bd3d464..5b99c3d7 100644 --- a/immutable_vec/immutable_vec.mbti +++ b/immutable_vec/immutable_vec.mbti @@ -1,7 +1,5 @@ package moonbitlang/core/immutable_vec -alias @moonbitlang/core/vec as @vec - // Values fn immutable_push[T](Array[T], T) -> Array[T] @@ -20,7 +18,6 @@ impl ImmutableVec { fold_left[T](Self[T], (T, T) -> T, ~init : T) -> T fold_right[T](Self[T], (T, T) -> T, ~init : T) -> T from_array[T](Array[T]) -> Self[T] - from_vector[T](@vec.Vec[T]) -> Self[T] iter[T](Self[T], (T) -> Unit) -> Unit iteri[T](Self[T], (Int, T) -> Unit) -> Unit map[T, U](Self[T], (T) -> U) -> Self[U] diff --git a/json/json.mbti b/json/json.mbti index 0755859b..8bfd5dc3 100644 --- a/json/json.mbti +++ b/json/json.mbti @@ -1,7 +1,6 @@ package moonbitlang/core/json alias @moonbitlang/core/map as @map -alias @moonbitlang/core/vec as @vec // Values @@ -11,11 +10,11 @@ pub enum JsonValue { Boolean(Bool) Number(Double) String(String) - Array(@vec.Vec[JsonValue]) + Array(Array[JsonValue]) Object(@map.Map[String, JsonValue]) } impl JsonValue { - as_array(Self) -> Option[@vec.Vec[Self]] + as_array(Self) -> Option[Array[Self]] as_bool(Self) -> Option[Bool] as_null(Self) -> Option[Unit] as_number(Self) -> Option[Double] diff --git a/map/map.mbti b/map/map.mbti index 4b3fbbf0..958d5bb7 100644 --- a/map/map.mbti +++ b/map/map.mbti @@ -1,7 +1,5 @@ package moonbitlang/core/map -alias @moonbitlang/core/vec as @vec - // Values fn empty[K, V]() -> Map[K, V] @@ -29,7 +27,7 @@ impl Map { op_get[K : Compare + Eq, V](Self[K, V], K) -> Option[V] remove[K : Compare + Eq, V](Self[K, V], K) -> Self[K, V] size[K, V](Self[K, V]) -> Int - to_vec[K, V](Self[K, V]) -> @vec.Vec[Tuple[K, V]] + to_array[K, V](Self[K, V]) -> Array[Tuple[K, V]] } // Traits diff --git a/mutable_set/mutable_set.mbti b/mutable_set/mutable_set.mbti index f9c9078b..7a77cd1b 100644 --- a/mutable_set/mutable_set.mbti +++ b/mutable_set/mutable_set.mbti @@ -1,7 +1,5 @@ package moonbitlang/core/mutable_set -alias @moonbitlang/core/vec as @vec - // Values // Types and methods @@ -15,8 +13,8 @@ impl MutableSet { disjoint[T : Compare + Eq](Self[T], Self[T]) -> Bool filter[T : Compare + Eq](Self[T], (T) -> Bool) -> Self[T] from_array[T : Compare + Eq](Array[T]) -> Self[T] + from_fixed_array[T : Compare + Eq](Array[T]) -> Self[T] from_list[T : Compare + Eq](List[T]) -> Self[T] - from_vec[T : Compare + Eq](@vec.Vec[T]) -> Self[T] intersect[T : Compare + Eq](Self[T], Self[T]) -> Self[T] is_empty[T : Compare + Eq](Self[T]) -> Bool iter[T : Compare + Eq](Self[T], (T) -> Unit) -> Unit @@ -29,7 +27,6 @@ impl MutableSet { to_array[T : Compare + Eq](Self[T]) -> Array[T] to_list[T : Compare + Eq](Self[T]) -> List[T] to_string[T : Compare + Show + Eq](Self[T]) -> String - to_vec[T : Compare + Eq](Self[T]) -> @vec.Vec[T] union[T : Compare + Eq](Self[T], Self[T]) -> Self[T] } From aa7d79a818db9050cbf5791d8323b24eb7aeeced Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Mon, 20 May 2024 16:52:53 +0800 Subject: [PATCH 3/4] tweak --- builtin/builtin.mbti | 5 +++-- builtin/debug.mbt | 4 ++++ fixedarray/fixedarray.mbti | 8 ++++---- immutable_set/immutable_set.mbti | 2 +- mutable_set/mutable_set.mbti | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index 084e24cf..0a88b112 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -54,7 +54,7 @@ impl Array { fold_lefti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U fold_right[T, U](Self[T], (U, T) -> U, ~init : U) -> U fold_righti[T, U](Self[T], (Int, U, T) -> U, ~init : U) -> U - from_fixed_array[T](Array[T]) -> Self[T] + from_fixed_array[T](FixedArray[T]) -> Self[T] get[T](Self[T], Int) -> Option[T] insert[T](Self[T], Int, T) -> Unit is_empty[T](Self[T]) -> Bool @@ -270,7 +270,7 @@ impl List { op_equal[X : Eq](Self[X], Self[X]) -> Bool to_string[X : Debug](Self[X]) -> String } -impl Array { +impl FixedArray { debug_write[X : Debug](Self[X], Buffer) -> Unit default[X]() -> Self[X] get[T](Self[T], Int) -> T @@ -285,6 +285,7 @@ impl Bytes { blit(Bytes, Int, Bytes, Int, Int) -> Unit blit_from_string(Bytes, Int, String, Int, Int) -> Unit copy(Bytes) -> Bytes + debug_write(Bytes, Buffer) -> Unit length(Bytes) -> Int make(Int, ~init : Byte = ..) -> Bytes of_string(String) -> Bytes diff --git a/builtin/debug.mbt b/builtin/debug.mbt index 2fc80992..77e266ba 100644 --- a/builtin/debug.mbt +++ b/builtin/debug.mbt @@ -41,6 +41,10 @@ pub fn debug_write(self : Int64, buf : Buffer) -> Unit { buf.write_string(self.to_string()) } +pub fn debug_write(self : Bytes, buf : Buffer) -> Unit { + buf.write_string(self.to_string()) +} + pub fn debug_write[X : Debug](self : Option[X], buf : Buffer) -> Unit { match self { None => buf.write_string("None") diff --git a/fixedarray/fixedarray.mbti b/fixedarray/fixedarray.mbti index 1238e080..c57b474d 100644 --- a/fixedarray/fixedarray.mbti +++ b/fixedarray/fixedarray.mbti @@ -3,15 +3,15 @@ package moonbitlang/core/fixedarray alias @moonbitlang/core/iter as @iter // Values -fn is_sorted[T : Compare + Eq](Array[T]) -> Bool +fn is_sorted[T : Compare + Eq](FixedArray[T]) -> Bool -fn new[T](Int, () -> T) -> Array[T] +fn new[T](Int, () -> T) -> FixedArray[T] -fn new_with_index[T](Int, (Int) -> T) -> Array[T] +fn new_with_index[T](Int, (Int) -> T) -> FixedArray[T] // Types and methods type TimSortRun -impl Array { +impl FixedArray { all[T](Self[T], (T) -> Bool) -> Bool any[T](Self[T], (T) -> Bool) -> Bool as_iter[T](Self[T]) -> @iter.Iter[T] diff --git a/immutable_set/immutable_set.mbti b/immutable_set/immutable_set.mbti index a12fde99..39759dbb 100644 --- a/immutable_set/immutable_set.mbti +++ b/immutable_set/immutable_set.mbti @@ -36,7 +36,7 @@ impl ImmutableSet { split[T : Compare + Eq](Self[T], T) -> Tuple[Self[T], Bool, Self[T]] subset[T : Compare + Eq](Self[T], Self[T]) -> Bool to_array[T : Compare + Eq](Self[T]) -> Array[T] - to_fixed_array[T : Compare + Eq](Self[T]) -> Array[T] + to_fixed_array[T : Compare + Eq](Self[T]) -> FixedArray[T] to_list[T : Compare + Eq](Self[T]) -> List[T] to_string[T : Show + Compare + Eq](Self[T]) -> String union[T : Compare + Eq](Self[T], Self[T]) -> Self[T] diff --git a/mutable_set/mutable_set.mbti b/mutable_set/mutable_set.mbti index 7a77cd1b..009a23e9 100644 --- a/mutable_set/mutable_set.mbti +++ b/mutable_set/mutable_set.mbti @@ -13,7 +13,7 @@ impl MutableSet { disjoint[T : Compare + Eq](Self[T], Self[T]) -> Bool filter[T : Compare + Eq](Self[T], (T) -> Bool) -> Self[T] from_array[T : Compare + Eq](Array[T]) -> Self[T] - from_fixed_array[T : Compare + Eq](Array[T]) -> Self[T] + from_fixed_array[T : Compare + Eq](FixedArray[T]) -> Self[T] from_list[T : Compare + Eq](List[T]) -> Self[T] intersect[T : Compare + Eq](Self[T], Self[T]) -> Self[T] is_empty[T : Compare + Eq](Self[T]) -> Bool From 0321b4955e222b80fddd88297108cd2f4d5d0096 Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Mon, 20 May 2024 23:37:41 +0800 Subject: [PATCH 4/4] format --- array/array_test.mbt | 35 +++++++-------------------- array/sort.mbt | 5 +++- array/sort_by.mbt | 6 ++++- builtin/array.mbt | 12 +++++++--- builtin/arrayview.mbt | 6 ++++- fixedarray/fixedarray.mbt | 47 ++++++++++++++++++++++++++++++------- fixedarray/slice.mbt | 6 ++++- fixedarray/sort_by.mbt | 10 ++++++-- json/json.mbt | 12 +++------- mutable_set/mutable_set.mbt | 4 +++- 10 files changed, 89 insertions(+), 54 deletions(-) diff --git a/array/array_test.mbt b/array/array_test.mbt index e82675c3..6b16d573 100644 --- a/array/array_test.mbt +++ b/array/array_test.mbt @@ -13,7 +13,7 @@ // limitations under the License. test "to_string" { - let empty: Array[Int] = [] + let empty : Array[Int] = [] inspect(empty, content="[]")? let a0 = [0] inspect(a0, content="[0]")? @@ -32,10 +32,7 @@ test "get" { test "op_add" { inspect(([] : Array[Int]) + [], content="[]")? - inspect( - ([] : Array[Int]) + [1, 2, 3, 4, 5], - content="[1, 2, 3, 4, 5]", - )? + inspect(([] : Array[Int]) + [1, 2, 3, 4, 5], content="[1, 2, 3, 4, 5]")? inspect([1, 2, 3, 4, 5] + [], content="[1, 2, 3, 4, 5]")? inspect( [1, 2, 3, 4, 5] + [6, 7, 8, 9, 10], @@ -180,7 +177,7 @@ test "is_empty" { } test "is_sorted" { - let v: Array[Int] = [] + let v : Array[Int] = [] @assertion.assert_true(v.is_sorted())? let v = [3, 4, 5] @assertion.assert_true(v.is_sorted())? @@ -199,7 +196,7 @@ test "reverse" { @assertion.assert_eq(v, [4, 3])? v.reverse() @assertion.assert_eq(v, [3, 4])? - let empty: Array[Int] = [] + let empty : Array[Int] = [] @assertion.assert_eq(empty, empty)? } @@ -333,18 +330,12 @@ test "join" { } test "fold_left" { - let sum = [1, 2, 3, 4, 5].fold_left( - init=0, - fn { sum, elem => sum + elem }, - ) + let sum = [1, 2, 3, 4, 5].fold_left(init=0, fn { sum, elem => sum + elem }) @assertion.assert_eq(sum, 15)? } test "fold_right" { - let sum = [1, 2, 3, 4, 5].fold_right( - init=0, - fn { sum, elem => sum + elem }, - ) + let sum = [1, 2, 3, 4, 5].fold_right(init=0, fn { sum, elem => sum + elem }) @assertion.assert_eq(sum, 15)? } @@ -383,14 +374,8 @@ test "chunks" { let v = [1, 2, 3, 4, 5, 6, 7, 8, 9] let chunks = v.chunks(3) let chunks2 = v.chunks(2) - @assertion.assert_eq( - chunks, - [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - )? - @assertion.assert_eq( - chunks2, - [[1, 2], [3, 4], [5, 6], [7, 8], [9]], - )? + @assertion.assert_eq(chunks, [[1, 2, 3], [4, 5, 6], [7, 8, 9]])? + @assertion.assert_eq(chunks2, [[1, 2], [3, 4], [5, 6], [7, 8], [9]])? } test "chunk_by" { @@ -444,9 +429,7 @@ test "shrink_to_fit_2" { test "as_iter" { let buf = Buffer::make(5) - [1, 2, 3, 4, 5].as_iter().iter( - fn { x => buf.write_string(x.to_string()) }, - ) + [1, 2, 3, 4, 5].as_iter().iter(fn { x => buf.write_string(x.to_string()) }) inspect(buf, content="12345")? buf.reset() [1, 2, 3, 4, 5].as_iter().take(2).iter( diff --git a/array/sort.mbt b/array/sort.mbt index 9de0a7a5..d3b1ff1d 100644 --- a/array/sort.mbt +++ b/array/sort.mbt @@ -112,7 +112,10 @@ fn get_limit(len : Int) -> Int { /// It will only tolerate at most 8 unsorted elements. The time complexity is O(n). /// /// Returns whether the array is sorted. -fn try_bubble_sort[T : Compare](arr : ArrayView[T], ~max_tries : Int = 8) -> Bool { +fn try_bubble_sort[T : Compare]( + arr : ArrayView[T], + ~max_tries : Int = 8 +) -> Bool { let mut tries = 0 for i = 1; i < arr.length(); i = i + 1 { let mut sorted = true diff --git a/array/sort_by.mbt b/array/sort_by.mbt index 4192584f..b1fa682e 100644 --- a/array/sort_by.mbt +++ b/array/sort_by.mbt @@ -234,7 +234,11 @@ fn heap_sort_by[T](arr : ArrayView[T], cmp : (T, T) -> Int) -> Unit { } } -fn sift_down_by[T](arr : ArrayView[T], index : Int, cmp : (T, T) -> Int) -> Unit { +fn sift_down_by[T]( + arr : ArrayView[T], + index : Int, + cmp : (T, T) -> Int +) -> Unit { let mut index = index let len = arr.length() let mut child = index * 2 + 1 diff --git a/builtin/array.mbt b/builtin/array.mbt index caa2fbeb..f6046389 100644 --- a/builtin/array.mbt +++ b/builtin/array.mbt @@ -63,7 +63,7 @@ pub fn Array::from_fixed_array[T](arr : FixedArray[T]) -> Array[T] { { buf, len } } -pub fn Array::make[T](len: Int, elem: T) -> Array[T] { +pub fn Array::make[T](len : Int, elem : T) -> Array[T] { let buf = UninitializedArray::make(len) for i = 0; i < len; i = i + 1 { buf[i] = elem @@ -545,7 +545,10 @@ pub fn ends_with[T : Eq](self : Array[T], suffix : Array[T]) -> Bool { /// let v = Array::[3, 4, 5] /// v.strip_prefix(Array::[3]) // Some(Array::[4, 5]) /// ``` -pub fn strip_prefix[T : Eq](self : Array[T], prefix : Array[T]) -> Option[Array[T]] { +pub fn strip_prefix[T : Eq]( + self : Array[T], + prefix : Array[T] +) -> Option[Array[T]] { if self.starts_with(prefix) { let v = Array::with_capacity(self.len - prefix.len) for i = prefix.len; i < self.len; i = i + 1 { @@ -567,7 +570,10 @@ pub fn strip_prefix[T : Eq](self : Array[T], prefix : Array[T]) -> Option[Array[ /// let v = Array::[3, 4, 5] /// v.strip_suffix(Array::[5]) // Some(Array::[3, 4]) /// ``` -pub fn strip_suffix[T : Eq](self : Array[T], suffix : Array[T]) -> Option[Array[T]] { +pub fn strip_suffix[T : Eq]( + self : Array[T], + suffix : Array[T] +) -> Option[Array[T]] { if self.ends_with(suffix) { let v = Array::with_capacity(self.len - suffix.len) for i = 0; i < self.len - suffix.len; i = i + 1 { diff --git a/builtin/arrayview.mbt b/builtin/arrayview.mbt index 9a29532d..4281ab86 100644 --- a/builtin/arrayview.mbt +++ b/builtin/arrayview.mbt @@ -66,7 +66,11 @@ pub fn op_as_view[T](self : Array[T], ~start : Int, ~end : Int) -> ArrayView[T] ArrayView::{ buf: self.buf, start, len: end - start } } -pub fn op_as_view[T](self : ArrayView[T], ~start : Int, ~end : Int) -> ArrayView[T] { +pub fn op_as_view[T]( + self : ArrayView[T], + ~start : Int, + ~end : Int +) -> ArrayView[T] { if start < 0 { abort("Slice start index out of bounds") } else if end > self.length() { diff --git a/fixedarray/fixedarray.mbt b/fixedarray/fixedarray.mbt index f1960bd3..ef44484a 100644 --- a/fixedarray/fixedarray.mbt +++ b/fixedarray/fixedarray.mbt @@ -344,9 +344,15 @@ pub fn fold_left[T, U](self : FixedArray[T], f : (U, T) -> U, ~init : U) -> U { } test "fold_left" { - let sum = ([] : FixedArray[_]).fold_left(init=1, fn { sum, elem => sum + elem }) + let sum = ([] : FixedArray[_]).fold_left( + init=1, + fn { sum, elem => sum + elem }, + ) @assertion.assert_eq(sum, 1)? - let sum = ([1] : FixedArray[_]).fold_left(init=2, fn { sum, elem => sum + elem }) + let sum = ([1] : FixedArray[_]).fold_left( + init=2, + fn { sum, elem => sum + elem }, + ) @assertion.assert_eq(sum, 3)? let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).fold_left( init=0, @@ -371,9 +377,15 @@ pub fn fold_right[T, U](self : FixedArray[T], f : (U, T) -> U, ~init : U) -> U { } test "fold_right" { - let sum = ([] : FixedArray[_]).fold_right(init=1, fn { sum, elem => sum + elem }) + let sum = ([] : FixedArray[_]).fold_right( + init=1, + fn { sum, elem => sum + elem }, + ) @assertion.assert_eq(sum, 1)? - let sum = ([1] : FixedArray[_]).fold_right(init=2, fn { sum, elem => sum + elem }) + let sum = ([1] : FixedArray[_]).fold_right( + init=2, + fn { sum, elem => sum + elem }, + ) @assertion.assert_eq(sum, 3)? let sum = ([1, 2, 3, 4, 5] : FixedArray[_]).fold_right( init=0, @@ -389,7 +401,11 @@ test "fold_right" { /// let sum = [1, 2, 3, 4, 5].fold_lefti(init=0, fn { index, sum, elem => sum + index }) /// sum // 10 /// ``` -pub fn fold_lefti[T, U](self : FixedArray[T], f : (Int, U, T) -> U, ~init : U) -> U { +pub fn fold_lefti[T, U]( + self : FixedArray[T], + f : (Int, U, T) -> U, + ~init : U +) -> U { for i = 0, acc = init; i < self.length(); { continue i + 1, f(i, acc, self[i]) } else { @@ -418,7 +434,11 @@ test "fold_lefti" { /// let sum = [1, 2, 3, 4, 5].fold_righti(init=0, fn { index, sum, elem => sum + index }) /// sum // 10 /// ``` -pub fn fold_righti[T, U](self : FixedArray[T], f : (Int, U, T) -> U, ~init : U) -> U { +pub fn fold_righti[T, U]( + self : FixedArray[T], + f : (Int, U, T) -> U, + ~init : U +) -> U { let len = self.length() for i = len - 1, acc = init; i >= 0; { continue i - 1, f(len - i - 1, acc, self[i]) @@ -690,7 +710,10 @@ test "contains" { /// let arr = [3, 4, 5] /// arr.starts_with([3, 4]) // true /// ``` -pub fn starts_with[T : Eq](self : FixedArray[T], prefix : FixedArray[T]) -> Bool { +pub fn starts_with[T : Eq]( + self : FixedArray[T], + prefix : FixedArray[T] +) -> Bool { if prefix.length() > self.length() { return false } @@ -866,8 +889,14 @@ test "op_add" { } { inspect(([1] : FixedArray[_]) + [2], content="[1, 2]")? - inspect(([1] : FixedArray[_]) + [1, 2, 3, 4, 5], content="[1, 1, 2, 3, 4, 5]")? - inspect(([1, 2, 3, 4, 5] : FixedArray[_]) + [1], content="[1, 2, 3, 4, 5, 1]")? + inspect( + ([1] : FixedArray[_]) + [1, 2, 3, 4, 5], + content="[1, 1, 2, 3, 4, 5]", + )? + inspect( + ([1, 2, 3, 4, 5] : FixedArray[_]) + [1], + content="[1, 2, 3, 4, 5, 1]", + )? } inspect( ([1, 2, 3, 4, 5] : FixedArray[_]) + [6, 7, 8, 9, 10], diff --git a/fixedarray/slice.mbt b/fixedarray/slice.mbt index 0066fe7d..8b8df0ea 100644 --- a/fixedarray/slice.mbt +++ b/fixedarray/slice.mbt @@ -42,6 +42,10 @@ fn reverse[T](self : FixedArraySlice[T]) -> Unit { } } -fn slice[T](self : FixedArraySlice[T], start : Int, end : Int) -> FixedArraySlice[T] { +fn slice[T]( + self : FixedArraySlice[T], + start : Int, + end : Int +) -> FixedArraySlice[T] { { array: self.array, start: self.start + start, end: self.start + end } } diff --git a/fixedarray/sort_by.mbt b/fixedarray/sort_by.mbt index 14801a27..30bfe551 100644 --- a/fixedarray/sort_by.mbt +++ b/fixedarray/sort_by.mbt @@ -23,7 +23,10 @@ /// arr.sort_by_key(fn (x) {-x}) /// debug(arr) //output: [5, 4, 3, 2, 1] /// ``` -pub fn sort_by_key[T, K : Compare](self : FixedArray[T], map : (T) -> K) -> Unit { +pub fn sort_by_key[T, K : Compare]( + self : FixedArray[T], + map : (T) -> K +) -> Unit { quick_sort_by( { array: self, start: 0, end: self.length() }, fn(a, b) { map(a).compare(map(b)) }, @@ -196,7 +199,10 @@ fn partition_by[T]( /// It avoids worst case performance by choosing a pivot that is likely to be close to the median. /// /// Returns the pivot index and whether the array is likely sorted. -fn choose_pivot_by[T](arr : FixedArraySlice[T], cmp : (T, T) -> Int) -> (Int, Bool) { +fn choose_pivot_by[T]( + arr : FixedArraySlice[T], + cmp : (T, T) -> Int +) -> (Int, Bool) { let len = arr.length() let use_median_of_medians = 50 let max_swaps = 4 * 3 diff --git a/json/json.mbt b/json/json.mbt index 5fef1b32..89dc3a88 100644 --- a/json/json.mbt +++ b/json/json.mbt @@ -154,21 +154,15 @@ test "get as array" { content="Some([String(\"Hello World\")])", )? inspect( - JsonValue::Array([JsonValue::String("Hello World")]).item(0).bind( - as_string, - ), + JsonValue::Array([JsonValue::String("Hello World")]).item(0).bind(as_string), content="Some(Hello World)", )? inspect( - JsonValue::Array([JsonValue::String("Hello World")]).item(1).bind( - as_string, - ), + JsonValue::Array([JsonValue::String("Hello World")]).item(1).bind(as_string), content="None", )? inspect( - JsonValue::Array([JsonValue::String("Hello World")]).item(0).bind( - as_number, - ), + JsonValue::Array([JsonValue::String("Hello World")]).item(0).bind(as_number), content="None", )? inspect( diff --git a/mutable_set/mutable_set.mbt b/mutable_set/mutable_set.mbt index 4f426879..09ef314e 100644 --- a/mutable_set/mutable_set.mbt +++ b/mutable_set/mutable_set.mbt @@ -810,7 +810,9 @@ fn to_string[T : Show](self : Node[T]) -> String { // Convert from other data structures to MutableSet. /// Initialize an MutableSet[T] from a FixedArray[T] -pub fn MutableSet::from_fixed_array[T : Compare](array : FixedArray[T]) -> MutableSet[T] { +pub fn MutableSet::from_fixed_array[T : Compare]( + array : FixedArray[T] +) -> MutableSet[T] { let set = MutableSet::new() for i = 0; i < array.length(); i = i + 1 { set.add(array[i])