// Copyright 2026 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.
///|
/// Converts a `BigInt` value to its JSON representation as a string.
///
/// This implementation serializes the `BigInt` by first converting it to its
/// decimal string representation, then wrapping it in a JSON string value. This
/// approach preserves the exact numerical value while ensuring compatibility
/// with JSON parsers that may not natively support arbitrary-precision integers.
///
/// Parameters:
///
/// * `self` : The `BigInt` value to be converted to JSON.
///
/// Returns a `Json` value containing the string representation of the `BigInt`.
///
/// Example:
///
/// ```mbt check
/// test {
/// let big = 12345678901234567890N
/// let json = big.to_json()
/// @debug.debug_inspect(
/// json,
/// content=(
/// #|String("12345678901234567890")
/// ),
/// )
/// }
/// ```
///
pub impl ToJson for BigInt with fn to_json(self : BigInt) -> Json {
Json::string(self.to_string())
}
///|
pub impl Show for BigInt with fn to_string(self) {
BigInt::to_string(self)
}
///|
pub impl @json.FromJson for BigInt with fn from_json(json, path) {
guard json is String(s) else {
raise JsonDecodeError(
(path, "BigInt::from_json: expected number in string representation"),
)
}
parse_bigint(s.view()) catch {
_ =>
raise JsonDecodeError(
(path, "BigInt::from_json: invalid number in string representation"),
)
}
}
///|
/// Returns the default value `0` for `BigInt`
pub impl Default for BigInt with fn default() {
zero
}
///|
/// Tests equality between a `BigInt` and an `Int` value.
///
/// This function performs a safe comparison by first checking if the `BigInt`
/// can be converted to an `Int` without overflow, then comparing the converted
/// value with the `Int` parameter. If the `BigInt` is too large or too small to
/// fit in an `Int`, the function returns `false`.
///
/// Parameters:
///
/// * `self` : The `BigInt` value to compare.
/// * `other` : The `Int` value to compare against.
///
/// Returns `true` if the `BigInt` and `Int` represent the same numerical value,
/// `false` otherwise.
///
/// Example:
///
/// ```mbt check
/// test {
/// let big = 42N
/// inspect(big.equal_int(42), content="true")
/// inspect(big.equal_int(41), content="false")
/// let large = 9223372036854775808N // Beyond Int64 range
/// inspect(large.equal_int(42), content="false")
/// }
/// ```
///
pub fn BigInt::equal_int(self : BigInt, other : Int) -> Bool {
can_convert_to_int(self) && self.to_int() == other
}
///|
/// Tests whether a `BigInt` value is equal to an `Int64` value.
///
/// This function performs an efficient equality check by first verifying if the
/// `BigInt` can be represented as an `Int64` without overflow. If it can be
/// converted, it then compares the converted value with the given `Int64`.
///
/// Parameters:
///
/// * `self` : The `BigInt` value to compare.
/// * `other` : The `Int64` value to compare against.
///
/// Returns `true` if the `BigInt` and `Int64` represent the same numerical
/// value, `false` otherwise.
///
/// Example:
///
/// ```mbt check
/// test {
/// let big = @bigint.BigInt::from_int64(9223372036854775807L) // Int64 max value
/// inspect(big.equal_int64(9223372036854775807L), content="true")
/// inspect(big.equal_int64(42L), content="false")
/// let overflow = @bigint.BigInt::from_string("9223372036854775808") // Beyond Int64 range
/// inspect(overflow.equal_int64(9223372036854775807L), content="false")
/// }
/// ```
///
pub fn BigInt::equal_int64(self : BigInt, other : Int64) -> Bool {
can_convert_to_int64(self) && self.to_int64() == other
}
///|
/// Tests whether a `BigInt` value is equal to a `UInt` value.
///
/// This is a convenience helper mirroring `equal_int`/`equal_int64` and avoids
/// forcing callers to construct a temporary `BigInt` manually.
pub fn BigInt::equal_uint(self : BigInt, other : UInt) -> Bool {
if is_neg(self) || self.bit_length() > 32 {
false
} else {
self.to_uint() == other
}
}
///|
/// Tests whether a `BigInt` value is equal to a `UInt64` value.
///
/// This is a convenience helper mirroring `equal_int`/`equal_int64` and avoids
/// forcing callers to construct a temporary `BigInt` manually.
pub fn BigInt::equal_uint64(self : BigInt, other : UInt64) -> Bool {
if is_neg(self) || self.bit_length() > 64 {
false
} else {
self.to_uint64() == other
}
}
///|
/// Compares a `BigInt` with an `Int` and returns their relative order.
///
/// Parameters:
///
/// * `self` : The `BigInt` value to compare.
/// * `other` : The `Int` value to compare against.
///
/// Returns an integer indicating the relative order:
///
/// * A negative value if `self` is less than `other`
/// * Zero if `self` equals `other`
/// * A positive value if `self` is greater than `other`
///
/// Example:
///
/// ```mbt check
/// test {
/// let big = 42N
/// inspect(big.compare_int(24), content="1") // 42 > 24
/// inspect(big.compare_int(42), content="0") // 42 = 42
/// inspect(big.compare_int(100), content="-1") // 42 < 100
/// }
/// ```
///
pub fn BigInt::compare_int(self : BigInt, other : Int) -> Int {
guard can_convert_to_int(self) else {
return if is_neg(self) { -1 } else { 1 }
}
let self = self.to_int()
Int::compare(self, other)
}
///|
/// Compares a `BigInt` with an `Int64` and returns their relative order.
///
/// Parameters:
///
/// * `self` : The `BigInt` value to compare.
/// * `other` : The `Int64` value to compare against.
///
/// Returns an integer indicating the relative order:
///
/// * A negative value if `self` is less than `other`
/// * Zero if `self` equals `other`
/// * A positive value if `self` is greater than `other`
///
/// Example:
///
/// ```mbt check
/// test {
/// let big = 42N
/// inspect(big.compare_int64(24L), content="1") // 42 > 24
/// inspect(big.compare_int64(42L), content="0") // 42 = 42
/// inspect(big.compare_int64(100L), content="-1") // 42 < 100
/// }
/// ```
pub fn BigInt::compare_int64(self : BigInt, other : Int64) -> Int {
guard can_convert_to_int64(self) else {
return if is_neg(self) { -1 } else { 1 }
}
let self = self.to_int64()
Int64::compare(self, other)
}
///|
/// Compares a `BigInt` with a `UInt` and returns their relative order.
pub fn BigInt::compare_uint(self : BigInt, other : UInt) -> Int {
if is_neg(self) {
-1
} else if self.bit_length() > 32 {
1
} else {
UInt::compare(self.to_uint(), other)
}
}
///|
/// Compares a `BigInt` with a `UInt64` and returns their relative order.
pub fn BigInt::compare_uint64(self : BigInt, other : UInt64) -> Int {
if is_neg(self) {
-1
} else if self.bit_length() > 64 {
1
} else {
UInt64::compare(self.to_uint64(), other)
}
}
///|
test "can_convert_to_int" {
assert_true(can_convert_to_int(0N))
assert_true(can_convert_to_int(1N))
assert_true(can_convert_to_int(-1N))
assert_true(can_convert_to_int(2147483647N)) // Int.max_value
assert_true(can_convert_to_int(-2147483648N)) // Int.min_value
assert_false(can_convert_to_int(2147483648N)) // Int.max_value + 1
assert_false(can_convert_to_int(-2147483649N)) // Int.min_value - 1
assert_false(can_convert_to_int(4294967295N)) // 2^32 - 1
assert_false(can_convert_to_int(-4294967295N)) // -(2^32 - 1)
assert_false(can_convert_to_int(4294967296N)) // 2^32
assert_false(can_convert_to_int(-4294967296N)) // -2^32
}
///|
test "can_convert_to_int64" {
assert_true(can_convert_to_int64(0N))
assert_true(can_convert_to_int64(1N))
assert_true(can_convert_to_int64(-1N))
assert_true(can_convert_to_int64(2147483647N)) // Int.max_value
assert_true(can_convert_to_int64(-2147483648N)) // Int.min_value
assert_true(can_convert_to_int64(2147483648N)) // Int.max_value + 1
assert_true(can_convert_to_int64(-2147483649N)) // Int.min_value - 1
assert_true(can_convert_to_int64(4294967295N)) // 2^32 - 1
assert_true(can_convert_to_int64(-4294967295N)) // -(2^32 - 1)
assert_true(can_convert_to_int64(4294967296N)) // 2^32
assert_true(can_convert_to_int64(-4294967296N)) // -2^32
assert_true(can_convert_to_int64(9223372036854775807N)) // Int64.max_value
assert_true(can_convert_to_int64(-9223372036854775808N)) // Int64.min_value
assert_false(can_convert_to_int64(9223372036854775808N)) // Int64.max_value + 1
assert_false(can_convert_to_int64(-9223372036854775809N)) // Int64.min_value - 1
assert_false(can_convert_to_int64(18446744073709551615N)) // 2^64 - 1
assert_false(can_convert_to_int64(-18446744073709551615N)) // -(2^64 - 1)
assert_false(can_convert_to_int64(18446744073709551616N)) // 2^64
assert_false(can_convert_to_int64(-18446744073709551616N)) // -2^64
}
///|
/// Implements the `Hash` trait for `BigInt` type by providing a `hash_combine`
/// method that combines a BigInt value with a hasher using the computed hash.
///
/// Parameters:
///
/// * `self`: The BigInt value to be hashed.
/// * `hasher`: The hasher object that will be used to combine the BigInt hash
/// into its internal state.
///
/// Example:
///
/// ```mbt check
/// test {
/// let hasher = Hasher(seed=0)
/// let big = 12345N
/// hasher.combine(big)
/// inspect(hasher.finalize(), content="890579181")
/// }
/// ```
pub impl Hash for BigInt with fn hash_combine(self, hasher) {
hasher.combine(self.signum())
for limb in self.limbs() {
hasher.combine(limb)
}
}
///|
fn BigInt::signum(self : Self) -> Int {
if self.is_zero() {
0
} else if is_neg(self) {
-1
} else {
1
}
}
///|
/// Converts a `BigInt` value to an unsigned 16-bit integer (`UInt16`).
///
/// Parameters:
///
/// * `self` : The `BigInt` value to be converted.
///
/// Returns a `UInt16` value representing the lower 16 bits of the input
/// `BigInt`.
///
/// Example:
///
/// ```mbt check
/// test {
/// let n = 42N
/// inspect(n.to_uint16(), content="42")
/// let neg = -1N
/// inspect(neg.to_uint16(), content="65535") // 2^16 - 1
/// }
/// ```
///
pub fn BigInt::to_uint16(self : BigInt) -> UInt16 {
self.to_int().to_uint16()
}
///|
/// Converts a `BigInt` value to a signed 16-bit integer (`Int16`).
///
/// Parameters:
///
/// * `self` : The `BigInt` value to be converted.
///
/// Returns a 16-bit signed integer representing the lower 16 bits of the input
/// `BigInt`.
///
/// Example:
///
/// ```mbt check
/// test {
/// let n = 42N
/// inspect(n.to_int16(), content="42")
/// let neg = -1N
/// inspect(neg.to_int16(), content="-1")
/// let big = 32768N // 2^15
/// inspect(big.to_int16(), content="-32768") // Overflow to Int16.min_value
/// }
/// ```
///
pub fn BigInt::to_int16(self : BigInt) -> Int16 {
Int16::from_int(self.to_int())
}
///|
pub impl @debug.Debug for BigInt with fn to_repr(self) {
@debug.Repr::integer(self.to_string())
}