diff --git a/builtin/bigint_deprecated.mbt b/builtin/bigint_deprecated.mbt new file mode 100644 index 000000000..a912d43bb --- /dev/null +++ b/builtin/bigint_deprecated.mbt @@ -0,0 +1,45 @@ +// 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. + +/// @alert deprecated "Use infix bitwise operator `>>` instead" +pub fn asr(self : BigInt, n : Int) -> BigInt { + self >> n +} + +/// Left shift a bigint +/// The sign of the result is the same as the sign of the input. +/// Only the absolute value is shifted. +/// +/// @alert deprecated "Use infix bitwise operator `<<` instead" +pub fn shl(self : BigInt, n : Int) -> BigInt { + self << n +} + +/// Left shift a bigint +/// The sign of the result is the same as the sign of the input. +/// Only the absolute value is shifted. +/// +/// @alert deprecated "Use infix bitwise operator `<<` instead" +pub fn lsl(self : BigInt, n : Int) -> BigInt { + self << n +} + +/// Right shift a bigint +/// The sign of the result is the same as the sign of the input. +/// Only the absolute value is shifted. +/// +/// @alert deprecated "Use infix bitwise operator `>>` instead" +pub fn shr(self : BigInt, n : Int) -> BigInt { + self >> n +} diff --git a/builtin/bigint_js.mbt b/builtin/bigint_js.mbt new file mode 100644 index 000000000..6fd905be1 --- /dev/null +++ b/builtin/bigint_js.mbt @@ -0,0 +1,166 @@ +// 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. + +type BigInt + +pub fn BigInt::from_string(str : String) -> BigInt { + if str.length() == 0 { + abort("empty string") + } + BigInt::js_from_string(str) +} + +extern "js" fn BigInt::js_from_string(str : String) -> BigInt = + #|(x) => BigInt(x) + +pub impl Show for BigInt with output(self, logger) { + logger.write_string(self.to_string()) +} + +pub extern "js" fn to_string(self : BigInt) -> String = + #|(x) => String(x) + +pub extern "js" fn BigInt::from_hex(str : String) -> BigInt = + #|(x) => x.startsWith('-') ? -BigInt(`0x${x.slice(1)}`) : BigInt(`0x${x}`) + +pub extern "js" fn to_hex(self : BigInt, ~uppercase : Bool = true) -> String = + #|(x, uppercase) => { + #| const r = x.toString(16); + #| return uppercase ? r.toUpperCase() : r; + #|} + +extern "js" fn hex2(b : Byte) -> String = + #|(x) => x.toString(16).padStart(2, '0') + +pub fn BigInt::from_octets(octets : Bytes, ~signum : Int = 1) -> BigInt { + if signum < 0 { + return -1N * BigInt::from_octets(octets, signum=1) + } + if signum == 0 { + return 0N + } + let str = StringBuilder::new() + str.write_string("0x") + for octet in octets { + str.write_string(hex2(octet)) + } + BigInt::from_string(str.to_string()) +} + +pub fn to_octets(self : BigInt, ~length? : Int) -> Bytes { + if self < 0 { + abort("negative BigInt") + } + + // TODO: Optimize this + let buf = [] + fn to_bytes() { + let len = buf.length() + let len = match length { + Some(len2) => + if len2 <= 0 { + abort("negative length") + } else if len2 > len { + len2 + } else { + len + } + None => len + } + let res = Bytes::new(len) + let mut j = res.length() - 1 + for octet in buf { + res[j] = octet + j -= 1 + } + res + } + + if self == 0 { + buf.push(0) + return to_bytes() + } + let mut x = self + while x > 0 { + buf.push(x.to_byte()) + x = x >> 8 + } + to_bytes() +} + +pub extern "js" fn compare(self : BigInt, other : BigInt) -> Int = + #|(x, y) => x < y ? -1 : x > y ? 1 : 0 + +pub extern "js" fn op_equal(self : BigInt, other : BigInt) -> Bool = + #|(x, y) => x === y + +pub extern "js" fn BigInt::from_int(x : Int) -> BigInt = + #|(x) => BigInt(x) + +pub extern "js" fn BigInt::from_int64(x : Int64) -> BigInt = + #|(x) => BigInt(x.hi) * 0x100000000n + BigInt(x.lo >>> 0) + +pub extern "js" fn BigInt::from_uint64(x : UInt64) -> BigInt = + #|(x) => BigInt(x.hi >>> 0) * 0x100000000n + BigInt(x.lo >>> 0) + +pub extern "js" fn is_zero(self : BigInt) -> Bool = + #|(x) => x === 0n + +pub extern "js" fn op_neg(self : BigInt) -> BigInt = + #|(x) => -x + +pub extern "js" fn op_add(self : BigInt, other : BigInt) -> BigInt = + #|(x, y) => x + y + +pub extern "js" fn op_sub(self : BigInt, other : BigInt) -> BigInt = + #|(x, y) => x - y + +pub extern "js" fn op_mul(self : BigInt, other : BigInt) -> BigInt = + #|(x, y) => x * y + +pub extern "js" fn op_div(self : BigInt, other : BigInt) -> BigInt = + #|(x, y) => x / y + +pub extern "js" fn op_mod(self : BigInt, other : BigInt) -> BigInt = + #|(x, y) => x % y + +pub extern "js" fn pow( + self : BigInt, + other : BigInt, + ~modulus : BigInt = 1N +) -> BigInt = + #|(x, y, z) => (x ** y) % z + +extern "js" fn to_byte(self : BigInt) -> Byte = + #|(x) => Number(BigInt.asUintN(8, x)) | 0 + +pub fn op_shl(self : BigInt, n : Int) -> BigInt { + if n < 0 { + abort("negative shift count") + } + self.js_shl(n) +} + +pub fn op_shr(self : BigInt, n : Int) -> BigInt { + if n < 0 { + abort("negative shift count") + } + self.js_shr(n) +} + +extern "js" fn js_shl(self : BigInt, other : Int) -> BigInt = + #|(x, y) => x << BigInt(y) + +extern "js" fn js_shr(self : BigInt, other : Int) -> BigInt = + #|(x, y) => x >> BigInt(y) diff --git a/builtin/bigint_js_wbtest.mbt b/builtin/bigint_js_wbtest.mbt new file mode 100644 index 000000000..4e55de2ef --- /dev/null +++ b/builtin/bigint_js_wbtest.mbt @@ -0,0 +1,19 @@ +// 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 check_len(_a : BigInt) -> Unit! { + assert_true!(true) +} + +let zero = 0N diff --git a/builtin/bigint.mbt b/builtin/bigint_nonjs.mbt similarity index 93% rename from builtin/bigint.mbt rename to builtin/bigint_nonjs.mbt index 396e2c37b..8f08b82b8 100644 --- a/builtin/bigint.mbt +++ b/builtin/bigint_nonjs.mbt @@ -424,42 +424,6 @@ fn grade_school_div(self : BigInt, other : BigInt) -> (BigInt, BigInt) { // Bitwise Operations -/// @alert deprecated "Use infix bitwise operator `<<` instead" -pub fn lsl(self : BigInt, n : Int) -> BigInt { - if n < 0 { - abort("negative shift count") - } - if not(self.is_zero()) { - let new_limbs = FixedArray::make( - self.len + (n + radix_bit_len - 1) / radix_bit_len, // ceiling(n / radix_bit_len) - 0U, - ) - let a = self.limbs - let r = n % radix_bit_len - let lz = n / radix_bit_len // number of leading zeros - let mut len = self.len + lz - if r != 0 { - let mut carry = 0UL - for i = 0; i < self.len; i = i + 1 { - carry = carry | (a[i].to_uint64() << r) - new_limbs[i + lz] = (carry % radix).to_uint() - carry = carry >> radix_bit_len - } - if carry != 0 { - new_limbs[self.len + lz] = carry.to_uint() - len += 1 - } - } else { - for i = 0; i < self.len; i = i + 1 { - new_limbs[i + lz] = a[i] - } - } - { limbs: new_limbs, sign: self.sign, len } - } else { - zero - } -} - /// Left shift a bigint /// The sign of the result is the same as the sign of the input. /// Only the absolute value is shifted. @@ -497,15 +461,6 @@ pub fn op_shl(self : BigInt, n : Int) -> BigInt { } } -/// Left shift a bigint -/// The sign of the result is the same as the sign of the input. -/// Only the absolute value is shifted. -/// -/// @alert deprecated "Use infix bitwise operator `<<` instead" -pub fn shl(self : BigInt, n : Int) -> BigInt { - self << n -} - /// Right shift a bigint /// The sign of the result is the same as the sign of the input. /// Only the absolute value is shifted. @@ -547,20 +502,6 @@ pub fn op_shr(self : BigInt, n : Int) -> BigInt { } } -/// Right shift a bigint -/// The sign of the result is the same as the sign of the input. -/// Only the absolute value is shifted. -/// -/// @alert deprecated "Use infix bitwise operator `>>` instead" -pub fn shr(self : BigInt, n : Int) -> BigInt { - self >> n -} - -/// @alert deprecated "Use infix bitwise operator `>>` instead" -pub fn asr(self : BigInt, n : Int) -> BigInt { - self >> n -} - // Comparison Operations /// Check if a bigint is zero diff --git a/builtin/bigint_nonjs_wbtest.mbt b/builtin/bigint_nonjs_wbtest.mbt new file mode 100644 index 000000000..9374fc5a7 --- /dev/null +++ b/builtin/bigint_nonjs_wbtest.mbt @@ -0,0 +1,88 @@ +// 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. + +type MyBigInt BigInt + +impl Show for MyBigInt with output(self, logger) { + logger.write_string( + "{limbs : \{self.limbs}, sign : \{self.sign}, len : \{self.len} }", + ) +} + +test "debug_string" { + let buf = StringBuilder::new() + let v : Array[MyBigInt] = [0, 1, 2, 3, 4, -0, -1, -2, -3] + (buf as Logger).write_iter(v.iter(), sep="\n", prefix="", suffix="") + // Logger::writer_iter() + // trait logger has no method write_iter + // precise: + // (dyn Logger)::write_iter(buf,..) + // Logger::trait_method() + inspect!( + buf, + content= + #|{limbs : [0, 0], sign : Positive, len : 1 } + #|{limbs : [1, 0], sign : Positive, len : 1 } + #|{limbs : [2, 0], sign : Positive, len : 1 } + #|{limbs : [3, 0], sign : Positive, len : 1 } + #|{limbs : [4, 0], sign : Positive, len : 1 } + #|{limbs : [0, 0], sign : Positive, len : 1 } + #|{limbs : [1, 0], sign : Negative, len : 1 } + #|{limbs : [2, 0], sign : Negative, len : 1 } + #|{limbs : [3, 0], sign : Negative, len : 1 } + , + ) +} + +fn check_len(a : BigInt) -> Unit! { + if a.is_zero() { + return () + } + assert_eq!(a.limbs[a.len - 1] != 0, true) + for i in a.len..> 1 + check_len!(b) + inspect!(b, content="617283945061728394") + let c = a >> 64 + check_len!(c) + inspect!(c, content="0") + let a = BigInt::from_int64((radix * radix / 2).reinterpret_as_int64()) + let b = a >> (radix_bit_len * 2) + check_len!(b) + inspect!(b, content="0") +} + +test "op_add coverage for max(self_len, other_len)" { + let a = BigInt::from_int(123456789) + let b = BigInt::from_int(987654321) + let result = a + b + inspect!(a.len, content="1") + inspect!(b.len, content="1") + inspect!(result.len, content="1") +} + +test "op_sub coverage for max(self_len, other_len)" { + let a = BigInt::from_int(987654321) + let b = BigInt::from_int(123456789) + let result = a - b + inspect!(a.len, content="1") + inspect!(b.len, content="1") + inspect!(result.len, content="1") +} diff --git a/builtin/bigint_wbtest.mbt b/builtin/bigint_wbtest.mbt index c7dc62d0f..3238da1fc 100644 --- a/builtin/bigint_wbtest.mbt +++ b/builtin/bigint_wbtest.mbt @@ -12,49 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -type MyBigInt BigInt - -impl Show for MyBigInt with output(self, logger) { - logger.write_string( - "{limbs : \{self.limbs}, sign : \{self.sign}, len : \{self.len} }", - ) -} - -test "debug_string" { - let buf = StringBuilder::new() - let v : Array[MyBigInt] = [0, 1, 2, 3, 4, -0, -1, -2, -3] - (buf as Logger).write_iter(v.iter(), sep="\n", prefix="", suffix="") - // Logger::writer_iter() - // trait logger has no method write_iter - // precise: - // (dyn Logger)::write_iter(buf,..) - // Logger::trait_method() - inspect!( - buf, - content= - #|{limbs : [0, 0], sign : Positive, len : 1 } - #|{limbs : [1, 0], sign : Positive, len : 1 } - #|{limbs : [2, 0], sign : Positive, len : 1 } - #|{limbs : [3, 0], sign : Positive, len : 1 } - #|{limbs : [4, 0], sign : Positive, len : 1 } - #|{limbs : [0, 0], sign : Positive, len : 1 } - #|{limbs : [1, 0], sign : Negative, len : 1 } - #|{limbs : [2, 0], sign : Negative, len : 1 } - #|{limbs : [3, 0], sign : Negative, len : 1 } - , - ) -} - -fn check_len(a : BigInt) -> Unit! { - if a.is_zero() { - return () - } - assert_eq!(a.limbs[a.len - 1] != 0, true) - for i in a.len..> 1 - check_len!(b) - inspect!(b, content="617283945061728394") - let c = a >> 64 - check_len!(c) - inspect!(c, content="0") - let a = BigInt::from_int64((radix * radix / 2).reinterpret_as_int64()) - let b = a >> (radix_bit_len * 2) - check_len!(b) - inspect!(b, content="0") -} - test "decimal_string" { let a = BigInt::from_string("0") check_len!(a) @@ -776,24 +719,6 @@ test "to_octets with padding" { ) } -test "op_add coverage for max(self_len, other_len)" { - let a = BigInt::from_int(123456789) - let b = BigInt::from_int(987654321) - let result = a + b - inspect!(a.len, content="1") - inspect!(b.len, content="1") - inspect!(result.len, content="1") -} - -test "op_sub coverage for max(self_len, other_len)" { - let a = BigInt::from_int(987654321) - let b = BigInt::from_int(123456789) - let result = a - b - inspect!(a.len, content="1") - inspect!(b.len, content="1") - inspect!(result.len, content="1") -} - test "pow" { inspect!(4N.pow(13N, modulus=497N), content="445") inspect!(65N.pow(17, modulus=3233), content="2790") diff --git a/builtin/moon.pkg.json b/builtin/moon.pkg.json index 665734633..691c812e9 100644 --- a/builtin/moon.pkg.json +++ b/builtin/moon.pkg.json @@ -17,6 +17,10 @@ "targets": { "int64_js.mbt": ["js"], "int64_nonjs.mbt": ["not", "js"], + "bigint_js.mbt": ["js"], + "bigint_nonjs.mbt": ["not", "js"], + "bigint_js_wbtest.mbt": ["js"], + "bigint_nonjs_wbtest.mbt": ["not", "js"], "arraycore_js.mbt": ["js"], "arraycore_nonjs.mbt": ["not", "js"], "panic_test.mbt": ["not", "native"],