diff --git a/array/array.mbt b/array/array.mbt index ebb9209de..d16ac0363 100644 --- a/array/array.mbt +++ b/array/array.mbt @@ -24,11 +24,9 @@ /// ``` /// [1, 2, 3, 4, 5].iter(fn(x){ print("\(x) ") }) //output: 1 2 3 4 5 /// ``` -pub fn iter[T](self : Array[T], f : (T) -> Unit) { - let mut i = 0 - while i < self.length() { +pub fn iter[T](self : Array[T], f : (T) -> Unit) -> Unit { + for i = 0; i < self.length(); i = i + 1 { f(self[i]) - i = i + 1 } } @@ -38,9 +36,7 @@ test "iter" { [1, 2, 3, 4, 5].iter( fn(elem) { if elem != i + 1 { failed = true }; i = i + 1 }, ) - if failed { - return Err("iter test failed") - } + @assertion.assert_false(failed)? } /// Iterates over the array with index. @@ -57,11 +53,9 @@ test "iter" { /// 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) { - let mut i = 0 - while i < self.length() { +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]) - i = i + 1 } } @@ -76,11 +70,44 @@ test "iteri" { i = i + 1 }, ) - if failed { - return Err("iteri test failed") + @assertion.assert_false(failed)? +} + +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]) + } +} + +test "iter rev" { + let mut i = 6 + let mut failed = false + [1, 2, 3, 4, 5].iter_rev( + fn(elem) { if elem != i - 1 { failed = true }; i = i - 1 }, + ) + @assertion.assert_false(failed)? +} + +pub fn iteri_rev[T](self : Array[T], f : (Int, T) -> Unit) -> Unit { + for i = self.length() - 1; i >= 0; i = i - 1 { + f(i, self[i]) } } +test "iteri_rev" { + let mut i = 6 + let mut failed = false + [1, 2, 3, 4, 5].iteri_rev( + fn(index, elem) { + if index != i - 2 || elem != i - 1 { + failed = true + } + i = i - 1 + }, + ) + @assertion.assert_false(failed)? +} + /// Applies a function to each element of the array and returns a new array with the results. /// /// # Example @@ -91,11 +118,12 @@ test "iteri" { /// 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 Array::default() + } let res = Array::make(self.length(), f(self[0])) - let mut i = 0 - while i < self.length() { + for i = 1; i < self.length(); i = i + 1 { res[i] = f(self[i]) - i = i + 1 } res } @@ -103,13 +131,18 @@ pub fn map[T, U](self : Array[T], f : (T) -> U) -> Array[U] { test "map" { let arr = [1, 2, 3, 4, 5] let doubled = arr.map(fn(x) { x * 2 }) + let empty : Array[Unit] = Array::default().map(fn(x) { x }) + @assertion.assert_eq(empty, [])? @assertion.assert_eq(doubled, [2, 4, 6, 8, 10])? } pub fn map_with_index[T, U](self : Array[T], f : (T, Int) -> U) -> Array[U] { + if self.length() == 0 { + return Array::default() + } let res = Array::make(self.length(), f(self[0], 0)) - loop 1 { - i => if i < self.length() { res[i] = f(self[i], i); continue i + 1 } + for i = 1; i < self.length(); i = i + 1 { + res[i] = f(self[i], i) } res } @@ -117,6 +150,8 @@ pub fn map_with_index[T, U](self : Array[T], f : (T, Int) -> U) -> Array[U] { test "map with index" { let arr = [1, 2, 3, 4, 5] let doubled = arr.map_with_index(fn(x, i) { x * 2 + i }) + let empty : Array[Int] = Array::default().map_with_index(fn(x, i) { x + i }) + @assertion.assert_eq(empty, [])? @assertion.assert_eq(doubled, [2, 5, 8, 11, 14])? } @@ -124,12 +159,10 @@ pub fn op_equal[T : Eq](self : Array[T], that : Array[T]) -> Bool { if self.length() != that.length() { return false } - let mut i = 0 - while i < self.length() { + for i = 0; i < self.length(); i = i + 1 { if self[i] != that[i] { return false } - i = i + 1 } true } @@ -140,8 +173,8 @@ pub fn new[T](length : Int, value : () -> T) -> Array[T] { Array::default() } else { let array = Array::make(length, value()) - loop 1 { - i => if i < length { array[i] = value(); continue i + 1 } + for i = 1; i < length; i = i + 1 { + array[i] = value() } array } @@ -158,8 +191,8 @@ pub fn new_with_index[T](length : Int, value : (Int) -> T) -> Array[T] { Array::default() } else { let array = Array::make(length, value(0)) - loop 1 { - i => if i < length { array[i] = value(i); continue i + 1 } + for i = 1; i < length; i = i + 1 { + array[i] = value(i) } array } @@ -182,14 +215,11 @@ test "from_array" { } pub fn fold[T, U](self : Array[T], f : (T, U) -> U, init : U) -> U { - loop 0, init { - i, acc => - if i < self.length() { - continue i + 1, - f(self[i], acc) - } else { - acc - } + for i = 0, acc = init; i < self.length(); { + continue i + 1, + f(self[i], acc) + } else { + acc } } diff --git a/assertion/assertion.mbt b/assertion/assertion.mbt index 07344496b..63a3790a3 100644 --- a/assertion/assertion.mbt +++ b/assertion/assertion.mbt @@ -71,11 +71,11 @@ test "assert_ne.eq" { } } -pub fn assert_false(x : Bool) -> Result[Unit, String] { +pub fn assert_false(x : Bool, ~loc : SourceLoc = _) -> Result[Unit, String] { if x == false { Ok(()) } else { - Err("assert_false failed") + Err("FAILED:\(loc)") } } @@ -87,11 +87,11 @@ test "assert_false.true" { assert_ne(assert_false(true), Ok(()))? } -pub fn assert_true(x : Bool) -> Result[Unit, String] { +pub fn assert_true(x : Bool, ~loc : SourceLoc = _) -> Result[Unit, String] { if x { Ok(()) } else { - Err("assert_true failed") + Err("FAILED:\(loc)") } } @@ -105,8 +105,8 @@ test "assert_true.false" { /// Assert referential equality of two values. /// -/// Returns Ok if the two arguments are the same object by reference, using the -/// `===` operator; raises an Error otherwise. Certain objects may be equal by +/// Returns Ok if the two arguments are the same object by reference, using +/// `physical_equal`; raises an Error otherwise. Certain objects may be equal by /// value, but they are different objects in the memory. This function checks /// the latter. /// @@ -118,13 +118,17 @@ test "assert_true.false" { /// assert_is(a, a)? // this is okay /// assert_is(a, b)? // yields an error /// ``` -pub fn assert_is[T : Debug](a : T, b : T) -> Result[Unit, String] { - if a === b { +pub fn assert_is[T : Debug]( + a : T, + b : T, + ~loc : SourceLoc = _ +) -> Result[Unit, String] { + if physical_equal(a, b) { Ok(()) } else { let a = debug_string(a) let b = debug_string(b) - Err("assertion failed for `\(a) === \(b)`") + Err("FAILED:\(loc) `\(a) is \(b)`") } } @@ -145,7 +149,7 @@ test "assert_is.is.not" { /// Assert referential inequality of two values. /// /// Returns Ok if the two arguments are NOT the same object by reference, using -/// the `===` operator; raises an Error otherwise. Certain objects may be equal +/// `physical_equal`; raises an Error otherwise. Certain objects may be equal /// by value, but they are different objects in the memory. This function /// checks the latter. /// @@ -157,13 +161,17 @@ test "assert_is.is.not" { /// assert_is_not(a, b)? // this is okay /// assert_is_not(a, a)? // yields an error /// ``` -pub fn assert_is_not[T : Debug](a : T, b : T) -> Result[Unit, String] { - if not(a === b) { +pub fn assert_is_not[T : Debug]( + a : T, + b : T, + ~loc : SourceLoc = _ +) -> Result[Unit, String] { + if not(physical_equal(a, b)) { Ok(()) } else { let a = debug_string(a) let b = debug_string(b) - Err("assertion failed for `not(\(a) === \(b))`") + Err("FAILED:\(loc) `not(\(a) is \(b))`") } } diff --git a/double/double.mbt b/double/double.mbt index f307e1338..f3a77d94a 100644 --- a/double/double.mbt +++ b/double/double.mbt @@ -11,3 +11,53 @@ // 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 Double::from_int(i : Int) -> Double { + i.to_double() +} + +pub fn abs(self : Double) -> Double { + if self < 0.0 { + -self + } else { + self + } +} + +/// Returns the sign of the double. +/// - If the double is positive, returns 1.0. +/// - If the double is negative, returns -1.0. +/// - Otherwise, returns the double itself (0.0, -0.0 and NaN). +pub fn signum(self : Double) -> Double { + if self < 0.0 { + -1.0 + } else if self > 0.0 { + 1.0 + } else { + self // handles 0.0, -0.0, NaN + } +} + +fn test_num[T : @num.Num + Debug + Default + Eq]( + x : T, + y : T, + x_plus_y : T, + x_mul_y : T, + x_minus_y : T, + x_div_y : T, + x_signum : T +) -> Result[Unit, String] { + @assertion.assert_eq(x + y, x_plus_y)? + @assertion.assert_eq(x * y, x_mul_y)? + @assertion.assert_eq(x - y, x_minus_y)? + @assertion.assert_eq(x / y, x_div_y)? + @assertion.assert_eq(x.abs(), T::default() - x)? + @assertion.assert_eq(x.signum(), x_signum)? + Ok(()) +} + +test "double.num" { + let x = -500.0 + let y = 792.0 + test_num(x, y, x + y, x * y, x - y, x / y, -1.0)? +} diff --git a/double/moon.pkg.json b/double/moon.pkg.json index 9e26dfeeb..373c84770 100644 --- a/double/moon.pkg.json +++ b/double/moon.pkg.json @@ -1 +1,6 @@ -{} \ No newline at end of file +{ + "import": [ + "moonbitlang/core/num", + "moonbitlang/core/assertion" + ] +} \ No newline at end of file diff --git a/int/int.mbt b/int/int.mbt index f307e1338..b88b41b78 100644 --- a/int/int.mbt +++ b/int/int.mbt @@ -11,3 +11,49 @@ // 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 Int::from_int(i : Int) -> Int { + i +} + +pub fn abs(self : Int) -> Int { + if self < 0 { + -self + } else { + self + } +} + +pub fn signum(self : Int) -> Int { + if self < 0 { + -1 + } else if self > 0 { + 1 + } else { + 0 + } +} + +fn test_num[T : @num.Num + Debug + Default + Eq]( + x : T, + y : T, + x_plus_y : T, + x_mul_y : T, + x_minus_y : T, + x_div_y : T, + x_signum : T +) -> Result[Unit, String] { + @assertion.assert_eq(x + y, x_plus_y)? + @assertion.assert_eq(x * y, x_mul_y)? + @assertion.assert_eq(x - y, x_minus_y)? + @assertion.assert_eq(x / y, x_div_y)? + @assertion.assert_eq(x.abs(), T::default() - x)? + @assertion.assert_eq(x.signum(), x_signum)? + Ok(()) +} + +test "int.num" { + let x = -5 + let y = 7 + test_num(x, y, x + y, x * y, x - y, x / y, -1)? +} diff --git a/int/moon.pkg.json b/int/moon.pkg.json index 9e26dfeeb..c8bf78ca0 100644 --- a/int/moon.pkg.json +++ b/int/moon.pkg.json @@ -1 +1,6 @@ -{} \ No newline at end of file +{ + "import": [ + "moonbitlang/core/num", + "moonbitlang/core/assertion" + ] +} \ No newline at end of file diff --git a/int64/int64.mbt b/int64/int64.mbt index f307e1338..e5995ce27 100644 --- a/int64/int64.mbt +++ b/int64/int64.mbt @@ -11,3 +11,49 @@ // 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 Int64::from_int(i : Int) -> Int64 { + i.to_int64() +} + +pub fn abs(self : Int64) -> Int64 { + if self < 0L { + -self + } else { + self + } +} + +pub fn signum(self : Int64) -> Int64 { + if self < 0L { + -1L + } else if self > 0L { + 1L + } else { + 0L + } +} + +fn test_num[T : @num.Num + Debug + Default + Eq]( + x : T, + y : T, + x_plus_y : T, + x_mul_y : T, + x_minus_y : T, + x_div_y : T, + x_signum : T +) -> Result[Unit, String] { + @assertion.assert_eq(x + y, x_plus_y)? + @assertion.assert_eq(x * y, x_mul_y)? + @assertion.assert_eq(x - y, x_minus_y)? + @assertion.assert_eq(x / y, x_div_y)? + @assertion.assert_eq(x.abs(), T::default() - x)? + @assertion.assert_eq(x.signum(), x_signum)? + Ok(()) +} + +test "int64.num" { + let x = -500L + let y = 792L + test_num(x, y, x + y, x * y, x - y, x / y, -1L)? +} diff --git a/int64/moon.pkg.json b/int64/moon.pkg.json index 9e26dfeeb..373c84770 100644 --- a/int64/moon.pkg.json +++ b/int64/moon.pkg.json @@ -1 +1,6 @@ -{} \ No newline at end of file +{ + "import": [ + "moonbitlang/core/num", + "moonbitlang/core/assertion" + ] +} \ No newline at end of file diff --git a/list/list.mbt b/list/list.mbt index 766da1f1c..46d44c68a 100644 --- a/list/list.mbt +++ b/list/list.mbt @@ -55,7 +55,7 @@ test "length" { /// ``` /// from_array([1, 2, 3, 4, 5]).iter(print) // output: 12345 /// ``` -pub fn iter[T](self : List[T], f : (T) -> Unit) { +pub fn iter[T](self : List[T], f : (T) -> Unit) -> Unit { match self { Nil => () Cons(head, tail) => { @@ -81,7 +81,7 @@ test "iter" { /// from_array([1, 2, 3, 4, 5]).iteri(fn(i, x) { print("(\(i),\(x)) ") }) /// // output: (0,1) (1,2) (2,3) (3,4) (4,5) /// ``` -pub fn iteri[T](self : List[T], f : (Int, T) -> Unit) { +pub fn iteri[T](self : List[T], f : (Int, T) -> Unit) -> Unit { loop self, 0 { Nil, _ => () Cons(x, xs), i => { @@ -486,22 +486,22 @@ test "repeat" { /// # Example /// /// ``` -/// let ls = from_array(["1", "2", "3", "4", "5"]).separate_by("|") +/// let ls = from_array(["1", "2", "3", "4", "5"]).intersperse("|") /// debug(ls) // output: from_array(["1", "|", "2", "|", "3", "|", "4", "|", "5"]) /// ``` -pub fn separate_by[T](self : List[T], separator : T) -> List[T] { +pub fn intersperse[T](self : List[T], separator : T) -> List[T] { match self { Nil => Nil Cons(head, Nil) => Cons(head, Nil) Cons(head, tail) => - Cons(head, Cons(separator, separate_by(tail, separator))) + Cons(head, Cons(separator, intersperse(tail, separator))) } } -test "separate_by" { +test "intersperse" { let ls = from_array(["1", "|", "2", "|", "3", "|", "4", "|", "5"]) @assertion.assert_eq( - from_array(["1", "2", "3", "4", "5"]).separate_by("|"), + from_array(["1", "2", "3", "4", "5"]).intersperse("|"), ls, )? } @@ -554,8 +554,8 @@ test "unzip" { /// let ls = flatten(from_array([from_array([1,2,3]), from_array([4,5,6]), from_array([7,8,9])])) /// debug(ls) // output: from_array([1, 2, 3, 4, 5, 6, 7, 8, 9]) /// ``` -pub fn flatten[T](list : List[List[T]]) -> List[T] { - match list { +pub fn flatten[T](self : List[List[T]]) -> List[T] { + match self { Nil => Nil Cons(head, tail) => concat(head, flatten(tail)) } @@ -955,6 +955,71 @@ test "findi_option" { )? } +/// Removes the element at the specified index in the list. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 2, 3, 4, 5].remove_at(2)) +/// // output: from_array([1, 2, 4, 5]) +/// ``` +pub fn remove_at[T](self : List[T], index : Int) -> List[T] { + match (index, self) { + (0, Cons(_, tail)) => tail + (_, Cons(head, tail)) => Cons(head, remove_at(tail, index - 1)) + (_, Nil) => Nil + // abort("remove_at: index out of bounds") + } +} + +test "remove_at" { + let ls = from_array([1, 2, 3, 4, 5]) + let rs = from_array([1, 2, 4, 5]) + @assertion.assert_eq(ls.remove_at(2), rs)? + @assertion.assert_eq(ls.remove_at(0), from_array([2, 3, 4, 5]))? + @assertion.assert_eq( + List::["a", "b", "c", "d", "e"].remove_at(2), + List::["a", "b", "d", "e"], + )? + @assertion.assert_eq( + List::["a", "b", "c", "d", "e"].remove_at(5), + List::["a", "b", "c", "d", "e"], + )? +} + +/// Removes the first occurrence of the specified element from the list, if it is present. +/// +/// # Example +/// +/// ``` +/// println(List::[1, 2, 3, 4, 5].remove(3)) +/// // output: from_array([1, 2, 4, 5]) +/// ``` +pub fn remove[T : Eq](self : List[T], elem : T) -> List[T] { + match self { + Nil => Nil + // abort("remove: element not found") + Cons(head, tail) => + if head == elem { + tail + } else { + Cons(head, remove(tail, elem)) + } + } +} + +test "remove" { + @assertion.assert_eq(List::[1, 2, 3, 4, 5].remove(3), List::[1, 2, 4, 5])? + @assertion.assert_eq( + List::["a", "b", "c", "d", "e"].remove("c"), + List::["a", "b", "d", "e"], + )? + @assertion.assert_eq( + List::["a", "b", "c", "d", "e"].remove("f"), + List::["a", "b", "c", "d", "e"], + )? +} + /// Returns true if list starts with prefix. /// /// # Example @@ -1016,3 +1081,27 @@ test "is_suffix" { @assertion.assert_true(List::[1, 2, 3, 4, 5].is_suffix(List::[3, 4, 5]))? @assertion.assert_false(List::[1, 2, 3, 4, 5].is_suffix(List::[3, 4, 6]))? } + +/// Similar to intersperse but with a list of values. +/// +/// # Example +/// ``` +/// let ls = List::[ +/// List::[1, 2, 3], +/// List::[4, 5, 6], +/// List::[7, 8, 9], +/// ] +/// ls.intercalate(List::[0]) +/// ``` +pub fn intercalate[T](self : List[List[T]], sep : List[T]) -> List[T] { + self.intersperse(sep).flatten() +} + +test "intercalate" { + let ls = List::[List::[1, 2, 3], List::[4, 5, 6], List::[7, 8, 9]] + @assertion.assert_eq( + ls.intercalate(List::[0]), + List::[1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9], + )? +} + diff --git a/map/map.mbt b/map/map.mbt new file mode 100644 index 000000000..aea364d8b --- /dev/null +++ b/map/map.mbt @@ -0,0 +1,398 @@ +// 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. + +// +// An efficient implementation of Map. +// Based on size balanced binary trees, described by: +// Stephen Adams, "Efficient sets: a balancing act" Journal of Functional Programming 3(4):553-562, October 1993 +// + +/// Immuatable map, consists of key-value pairs. +/// +/// # Example +/// +/// ``` +/// let map1 = Map::[(3, "three"), (8, "eight"), (1, "one")] +/// let map2 = map1.insert(2, "two").remove(3) +/// println(map3.lookup(3)) // output: None +/// println(map3.lookup(2)) // output: Some("two") +/// let map3 = map2.insert(2, "updated") +/// println(map3.lookup(2)) // output: Some("updated") +/// ``` +enum Map[K, V] { + Empty + Tree(K, V, Int, Map[K, V], Map[K, V]) +} + +/// Create an empty map. +pub fn empty[K, V]() -> Map[K, V] { + Empty +} + +/// Create a map with a single key-value pair. +pub fn singleton[K, V](key : K, value : V) -> Map[K, V] { + Tree(key, value, 1, Empty, Empty) +} + +/// Check if the map contains a key. +/// O(log n). +pub fn member[K : Compare + Show, V : Show](self : Map[K, V], key : K) -> Bool { + match self { + Empty => false + Tree(k, _, _, l, r) => + match key.compare(k) { + -1 => l.member(key) + 1 => r.member(key) + _ => true + } + } +} + +/// Get the number of key-value pairs in the map. +pub fn size[K, V](self : Map[K, V]) -> Int { + match self { + Empty => 0 + Tree(_, _, n, _, _) => n + } +} + +fn new[K, V](key : K, value : V, l : Map[K, V], r : Map[K, V]) -> Map[K, V] { + let size = size(l) + size(r) + 1 + Tree(key, value, size, l, r) +} + +/// Create a new map with a key-value pair inserted. +/// O(log n). +pub fn insert[K : Compare, V]( + self : Map[K, V], + key : K, + value : V +) -> Map[K, V] { + match self { + Empty => singleton(key, value) + Tree(k, v, _, l, r) => + match key.compare(k) { + -1 => balance(k, v, insert(l, key, value), r) + 1 => balance(k, v, l, insert(r, key, value)) + _ => new(k, value, l, r) + } + } +} + +fn split_max[K : Compare, V](self : Map[K, V]) -> (K, V, Map[K, V]) { + match self { + Tree(k, v, _, l, Empty) => (k, v, l) + Tree(k, v, _, l, r) => { + let (k1, v1, r) = split_max(r) + (k1, v1, balance(k, v, l, r)) + } + Empty => abort("Map::split_max error") + } +} + +fn split_min[K : Compare, V](self : Map[K, V]) -> (K, V, Map[K, V]) { + match self { + Tree(k, v, _, Empty, r) => (k, v, r) + Tree(k, v, _, l, r) => { + let (k1, v1, l) = split_min(l) + (k1, v1, balance(k, v, l, r)) + } + Empty => abort("Map::split_min error") + } +} + +fn glue[K : Compare, V](self : Map[K, V], other : Map[K, V]) -> Map[K, V] { + match (self, other) { + (Empty, r) => r + (l, Empty) => l + (l, r) => + if l.size() > r.size() { + let (k, v, l) = split_max(l) + balance(k, v, l, r) + } else { + let (k, v, r) = split_min(r) + balance(k, v, l, r) + } + } +} + +/// Create a new map with a key-value pair removed. O(log n). +/// If the key is not a member or map, the original map is returned. +pub fn remove[K : Compare, V](self : Map[K, V], key : K) -> Map[K, V] { + match self { + Empty => Empty + Tree(k, v, _, l, r) => + match key.compare(k) { + -1 => balance(k, v, remove(l, key), r) + 1 => balance(k, v, l, remove(r, key)) + _ => glue(l, r) + } + } +} + +/// Get the value associated with a key. +/// O(log n). +pub fn lookup[K : Compare, V](self : Map[K, V], key : K) -> Option[V] { + match self { + Empty => None + Tree(k, v, _, l, r) => + match key.compare(k) { + -1 => l.lookup(key) + 1 => r.lookup(key) + _ => Some(v) + } + } +} + +/// Iterate over the key-value pairs in the map. +pub fn iter[K : Compare, V](self : Map[K, V], f : (K, V) -> Unit) -> Unit { + match self { + Empty => () + Tree(k, v, _, l, r) => { + iter(l, f) + f(k, v) + iter(r, f) + } + } +} + +/// The ratio between the sizes of the left and right subtrees. +let ratio = 5 + +fn balance[K : Compare, V]( + key : K, + value : V, + l : Map[K, V], + r : Map[K, V] +) -> Map[K, V] { + // 1 2 + // / \ / \ + // x 2 ---> 1 z + // / \ / \ + // y z x y + fn single_l { + k1, v1, x, Map::Tree(k2, v2, _, y, z) => new(k2, v2, new(k1, v1, x, y), z) + _, _, _, _ => abort("single_l error") + } + + fn single_r { + k2, v2, Map::Tree(k1, v1, _, x, y), z => new(k1, v1, x, new(k2, v2, y, z)) + _, _, _, _ => abort("single_r error") + } + + // 1 2 + // / \ / \ + // x 3 1 3 + // / \ --> / \ / \ + // 2 z x y1 y2 z + // / \ + // y1 y2 + fn double_l { + k1, v1, x, Map::Tree(k3, v3, _, Tree(k2, v2, _, y1, y2), z) => + new(k2, v2, new(k1, v1, x, y1), new(k3, v3, y2, z)) + _, _, _, _ => abort("double_l error") + } + + // 3 2 + // / \ / \ + // 1 z 1 3 + // / \ --> / \ / \ + // x 2 x y1 y2 z + // / \ + // y1 y2 + fn double_r { + k3, v3, Map::Tree(k1, v1, _, x, Tree(k2, v2, _, y1, y2)), z => + new(k2, v2, new(k1, v1, x, y1), new(k3, v3, y2, z)) + _, _, _, _ => abort("double_r error") + } + + let ln = size(l) + let rn = size(r) + if ln + rn < 2 { + new(key, value, l, r) + } else if rn > ratio * ln { + // right is too big + let (rl, rr) = match r { + Tree(_, _, _, rl, rr) => (rl, rr) + Empty => abort("unreachable") + } + let rln = size(rl) + let rrn = size(rr) + if rln < rrn { + single_l(key, value, l, r) + } else { + double_l(key, value, l, r) + } + } else if ln > ratio * rn { + // left is too big + let (ll, lr) = match l { + Tree(_, _, _, ll, lr) => (ll, lr) + Empty => abort("unreachable") + } + let lln = size(ll) + let lrn = size(lr) + if lrn < lln { + single_r(key, value, l, r) + } else { + double_r(key, value, l, r) + } + } else { + new(key, value, l, r) + } +} + +fn debug_tree[K : Show, V : Show](self : Map[K, V]) -> String { + match self { + Empty => "_" + Tree(k, v, _, l, r) => { + let l = debug_tree(l) + let r = debug_tree(r) + "(\(k),\(v),\(l),\(r))" + } + } +} + +/// Build a map from an array of key-value pairs. +/// O(n*log n). +pub fn Map::from_array[K : Compare, V](array : Array[(K, V)]) -> Map[K, V] { + loop 0, Map::Empty { + i, mp => + if i < array.length() { + let (k, v) = array[i] + continue i + 1, + mp.insert(k, v) + } else { + mp + } + } +} + +test "from_array" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? +} + +test "insert" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one")] + @assertion.assert_eq(m.debug_tree(), "(3,three,(1,one,_,_),(8,eight,_,_))")? + let m = m.insert(5, "five").insert(2, "two").insert(0, "zero").insert( + 1, "one_updated", + ) + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one_updated,(0,zero,_,_),(2,two,_,_)),(8,eight,(5,five,_,_),_))", + )? +} + +test "remove" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let m = m.remove(1).remove(3) + @assertion.assert_eq(m.debug_tree(), "(2,two,(0,zero,_,_),(8,eight,_,_))")? +} + +test "lookup" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq(m.lookup(8), Some("eight"))? + @assertion.assert_eq(m.lookup(2), Some("two"))? + @assertion.assert_eq(m.lookup(4), None)? +} + +test "member" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + @assertion.assert_eq(m.member(8), true)? + @assertion.assert_eq(m.member(2), true)? + @assertion.assert_eq(m.member(4), false)? +} + +test "size" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq(m.size(), 5)? + let m = m.remove(1).remove(3) + @assertion.assert_eq(m.size(), 3)? +} + +test "iter" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + let mut s = "" + m.iter(fn(k, v) { s = s + "(\(k),\(v))" }) + @assertion.assert_eq(s, "(0,zero)(1,one)(2,two)(3,three)(8,eight)")? +} + +test "singleton" { + let m = singleton(3, "three") + @assertion.assert_eq(m.debug_tree(), "(3,three,_,_)")? +} + +test "empty" { + let m : Map[Int, Int] = empty() + @assertion.assert_eq(m.debug_tree(), "_")? +} + +test "split_max" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let (k, v, r) = m.split_max() + @assertion.assert_eq(k, 8)? + @assertion.assert_eq(v, "eight")? + @assertion.assert_eq( + r.debug_tree(), + "(2,two,(1,one,(0,zero,_,_),_),(3,three,_,_))", + )? +} + +test "split_min" { + let m = Map::[(3, "three"), (8, "eight"), (2, "two"), (1, "one"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let (k, v, r) = m.split_min() + @assertion.assert_eq(k, 0)? + @assertion.assert_eq(v, "zero")? + @assertion.assert_eq( + r.debug_tree(), + "(3,three,(1,one,_,(2,two,_,_)),(8,eight,_,_))", + )? +} + +test "glue" { + let m = Map::[(3, "three"), (8, "eight"), (1, "one"), (2, "two"), (0, "zero")] + @assertion.assert_eq( + m.debug_tree(), + "(3,three,(1,one,(0,zero,_,_),(2,two,_,_)),(8,eight,_,_))", + )? + let (l, r) = match m { + Tree(_, _, _, l, r) => (l, r) + _ => abort("unreachable") + } + let m = l.glue(r) + @assertion.assert_eq( + m.debug_tree(), + "(2,two,(1,one,(0,zero,_,_),_),(8,eight,_,_))", + )? +} diff --git a/map/moon.pkg.json b/map/moon.pkg.json new file mode 100644 index 000000000..4034f64a6 --- /dev/null +++ b/map/moon.pkg.json @@ -0,0 +1,6 @@ +{ + "import": [ + "moonbitlang/core/assertion", + "moonbitlang/core/list" + ] +} \ No newline at end of file diff --git a/num/moon.pkg.json b/num/moon.pkg.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/num/moon.pkg.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/num/num.mbt b/num/num.mbt new file mode 100644 index 000000000..c92e426e2 --- /dev/null +++ b/num/num.mbt @@ -0,0 +1,24 @@ +// 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 trait Num { + from_int(Int) -> Self + op_add(Self, Self) -> Self + op_sub(Self, Self) -> Self + op_mul(Self, Self) -> Self + op_div(Self, Self) -> Self + op_neg(Self) -> Self + abs(Self) -> Self + signum(Self) -> Self +} diff --git a/ref/moon.pkg.json b/ref/moon.pkg.json index 461766b4f..c92f2b9ea 100644 --- a/ref/moon.pkg.json +++ b/ref/moon.pkg.json @@ -1,5 +1,7 @@ { "import": [ - { "path": "moonbitlang/core/assertion", "alias": "assertion" } + "moonbitlang/core/assertion", + "moonbitlang/core/num", + "moonbitlang/core/int" ] } \ No newline at end of file diff --git a/ref/ref.mbt b/ref/ref.mbt index f279ebe95..489913a1c 100644 --- a/ref/ref.mbt +++ b/ref/ref.mbt @@ -84,7 +84,7 @@ test "protect" { /// debug(x) //output: ref(2) /// debug(y) //output: ref(1) /// ``` -pub fn swap[T](self : Ref[T], that : Ref[T]) { +pub fn swap[T](self : Ref[T], that : Ref[T]) -> Unit { let tmp = self.val self.val = that.val that.val = tmp @@ -98,6 +98,38 @@ test "swap" { @assertion.assert_eq(y.val, 1)? } -pub fn update[T](self : Ref[T], f : (T) -> T) { +pub fn update[T](self : Ref[T], f : (T) -> T) -> Unit { self.val = f(self.val) } + +/// Increments the value of a reference by 1. The default value is 1. +pub fn incr[T : @num.Num]( + self : Ref[T], + ~value : T = @num.Num::from_int(1) +) -> Unit { + self.val = self.val + value +} + +/// Decrements the value of a reference by value. The default value is 1. +pub fn decr[T : @num.Num]( + self : Ref[T], + ~value : T = @num.Num::from_int(1) +) -> Unit { + self.val = self.val - value +} + +test "decr" { + let a = ref(1) + a.decr() + @assertion.assert_eq(a.val, 0)? + a.decr(~value=5) + @assertion.assert_eq(a.val, -5)? +} + +test "incr" { + let a = ref(1) + a.incr() + @assertion.assert_eq(a.val, 2)? + a.incr(~value=5) + @assertion.assert_eq(a.val, 7)? +} diff --git a/stack/stack.mbt b/stack/stack.mbt index 0b3e462de..adf6bae2f 100644 --- a/stack/stack.mbt +++ b/stack/stack.mbt @@ -100,7 +100,7 @@ test "from_stack" { /// s.clear() /// println(s) //output: {elements: Nil, len: 0} /// ``` -pub fn clear[T](self : Stack[T]) { +pub fn clear[T](self : Stack[T]) -> Unit { self.elements = Nil self.len = 0 } diff --git a/vec/vec.mbt b/vec/vec.mbt index e375523ad..b47736b25 100644 --- a/vec/vec.mbt +++ b/vec/vec.mbt @@ -61,7 +61,7 @@ pub fn capacity[T](self : Vec[T]) -> Int { } /// Reallocate the vector with a new capacity. -fn realloc[T](self : Vec[T]) { +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) @@ -110,7 +110,7 @@ pub fn op_get[T](self : Vec[T], index : Int) -> T { /// v.push(3) /// println(v[0]) // 3 /// ``` -pub fn op_set[T](self : Vec[T], index : Int, value : T) { +pub fn op_set[T](self : Vec[T], index : Int, value : T) -> Unit { if index >= self.len { let len = self.len abort("index out of bounds: the len is \(len) but the index is \(index)") @@ -175,7 +175,7 @@ test "pop" { /// let v = Vec::new() /// v.push(3) /// ``` -pub fn push[T](self : Vec[T], value : T) { +pub fn push[T](self : Vec[T], value : T) -> Unit { if self.len == self.buf.length() { self.realloc() } @@ -200,7 +200,7 @@ test "push" { /// let v2 = Vec::[6, 7, 8] /// v1.append(v2) /// ``` -pub fn append[T](self : Vec[T], other : Vec[T]) { +pub fn append[T](self : Vec[T], other : Vec[T]) -> Unit { for i = 0; i < other.len; i = i + 1 { self.push(other[i]) } @@ -230,7 +230,7 @@ test "append" { /// let mut sum = 0 /// v.iter(fn (x) {sum = sum + x}) /// ``` -pub fn iter[T](self : Vec[T], f : (T) -> Unit) { +pub fn iter[T](self : Vec[T], f : (T) -> Unit) -> Unit { for i = 0; i < self.len; i = i + 1 { f(self[i]) } @@ -257,7 +257,7 @@ test "iter" { /// let mut sum = 0 /// v.iteri(fn (i, x) {sum = sum + x + i}) /// ``` -pub fn iteri[T](self : Vec[T], f : (Int, T) -> Unit) { +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]) } @@ -279,7 +279,7 @@ test "iteri" { /// let v = Vec::from_array([3, 4, 5]) /// v.clear() /// ``` -pub fn clear[T](self : Vec[T]) { +pub fn clear[T](self : Vec[T]) -> Unit { self.len = 0 } @@ -297,7 +297,7 @@ test "clear" { /// let v = Vec::from_array([3, 4, 5]) /// v.map(fn (x) {x + 1}) /// ``` -pub fn map[T](self : Vec[T], f : (T) -> T) { +pub fn map[T](self : Vec[T], f : (T) -> T) -> Unit { for i = 0; i < self.len; i = i + 1 { self.buf[i] = f(self.buf[i]) } @@ -318,7 +318,7 @@ test "map" { /// let v = Vec::from_array([3, 4, 5]) /// v.mapi(fn (i, x) {x + i}) /// ``` -pub fn mapi[T](self : Vec[T], f : (Int, T) -> T) { +pub fn mapi[T](self : Vec[T], f : (Int, T) -> T) -> Unit { for i = 0; i < self.len; i = i + 1 { self.buf[i] = f(i, self.buf[i]) } @@ -357,7 +357,7 @@ test "is_empty" { /// let v = Vec::[3, 4, 5] /// v.reverse() /// ``` -pub fn reverse[T](self : Vec[T]) { +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] @@ -518,7 +518,7 @@ test "search" { /// let v = Vec::[3, 4, 5] /// v.swap(1, 2) /// ``` -pub fn swap[T](self : Vec[T], i : Int, j : Int) { +pub fn swap[T](self : Vec[T], i : Int, j : Int) -> Unit { if i >= self.len || j >= self.len { abort("index out of bounds") } @@ -554,7 +554,7 @@ fn partition[T : Compare](self : Vec[T], left : Int, right : Int) -> Int { } /// Quicksort helper -fn quicksort[T : Compare](self : Vec[T], left : Int, right : Int) { +fn quicksort[T : Compare](self : Vec[T], left : Int, right : Int) -> Unit { if left <= right { let idx = self.partition(0, right) self.quicksort(left, idx - 1) @@ -571,7 +571,7 @@ fn quicksort[T : Compare](self : Vec[T], left : Int, right : Int) { /// let v = Vec::[3, 4, 5, 1, 2] /// v.sort() /// ``` -pub fn sort[T : Compare](self : Vec[T]) { +pub fn sort[T : Compare](self : Vec[T]) -> Unit { self.quicksort(0, self.len - 1) } @@ -621,7 +621,7 @@ test "remove" { /// let v = Vec::[3, 4, 5] /// v.retain(fn(x) { x > 3 }) /// ``` -pub fn retain[T](self : Vec[T], f : (T) -> Bool) { +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() { @@ -646,7 +646,7 @@ test "retain" { /// ``` /// Vec::[3, 4, 5].resize(1) /// ``` -pub fn resize[T](self : Vec[T], new_len : Int, f : T) { +pub fn resize[T](self : Vec[T], new_len : Int, f : T) -> Unit { if new_len < 0 { abort("negative new length") } @@ -683,7 +683,7 @@ test "resize" { /// ``` /// Vec::[3, 4, 5].insert(1, 6) /// ``` -pub fn insert[T](self : Vec[T], index : Int, value : T) { +pub fn insert[T](self : Vec[T], index : Int, value : T) -> Unit { if index > self.len { abort("index out of bounds") } @@ -714,7 +714,7 @@ test "insert" { /// ``` /// Vec::with_capacity(3).fill(3) /// ``` -pub fn fill[T](self : Vec[T], value : T) { +pub fn fill[T](self : Vec[T], value : T) -> Unit { for i = 0; i < self.capacity(); i = i + 1 { self.buf[i] = value }