///|
pub(all) struct BigInt {
negative : Bool
digits : Bytes
} derive(Eq, Debug)
///|
pub fn bigint_zero() -> BigInt {
{ negative: false, digits: Bytes::from_array([]), }
}
///|
pub fn bigint_from_int(value : Int) -> BigInt {
bigint_from_int64(value.to_int64())
}
///|
pub fn bigint_from_int64(value : Int64) -> BigInt {
if value == 0L {
bigint_zero()
} else {
let negative = value < 0L
let mut mag = if negative { 0L - value } else { value }
let tmp : Array[Byte] = []
while mag > 0L {
tmp.push((mag & 255L).to_int().to_byte())
mag = mag >> 8
}
let digits : Array[Byte] = []
let mut i = tmp.length() - 1
while i >= 0 {
digits.push(tmp[i])
i -= 1
}
{ negative, digits: array_to_bytes(digits), }
}
}
///|
pub fn bigint_from_magnitude(negative : Bool, mag : Bytes) -> BigInt {
let arr = bytes_to_array(mag)
let mut start = 0
while start < arr.length() && arr[start].to_int() == 0 {
start += 1
}
if start >= arr.length() {
bigint_zero()
} else {
{ negative, digits: slice_bytes(arr, start, arr.length()), }
}
}
///|
pub fn BigInt::is_zero(self : BigInt) -> Bool {
self.digits.length() == 0
}
///|
pub fn BigInt::sign(self : BigInt) -> Int {
if self.digits.length() == 0 {
0
} else if self.negative {
-1
} else {
1
}
}
///|
pub fn BigInt::to_int64(self : BigInt) -> Int64? {
if self.digits.length() == 0 {
Some(0L)
} else if self.digits.length() > 8 {
None
} else {
let arr = bytes_to_array(self.digits)
let mut n = 0L
for b in arr {
n = (n << 8) | b.to_int().to_int64()
}
if self.negative {
if n < 0L {
None
} else {
Some(0L - n)
}
} else if n < 0L {
None
} else {
Some(n)
}
}
}
///|
fn divmod10_digits(digits : Array[Byte]) -> (Array[Byte], Int) {
let q : Array[Byte] = []
let mut rem = 0
for b in digits {
let cur = rem * 256 + b.to_int()
let d = cur / 10
rem = cur % 10
if q.length() > 0 || d != 0 {
q.push(d.to_byte())
}
}
(q, rem)
}
///|
pub fn BigInt::to_decimal_string(self : BigInt) -> String {
if self.digits.length() == 0 {
"0"
} else {
let mut digits = bytes_to_array(self.digits)
let acc : Array[Int] = []
while digits.length() > 0 {
let pair = divmod10_digits(digits)
digits = pair.0
acc.push(pair.1)
}
let out = StringBuilder()
if self.negative {
out.write_char('-')
}
let mut i = acc.length() - 1
while i >= 0 {
out.write_char((48 + acc[i]).unsafe_to_char())
i -= 1
}
out.to_string()
}
}
///|
pub fn bigint_from_decimal_string(text : String) -> Result[BigInt, IonError] {
if text.length() == 0 {
return Err(InvalidNumber(text))
}
let mut i = 0
let mut negative = false
if text[0].to_int() == 45 {
negative = true
i = 1
} else if text[0].to_int() == 43 {
i = 1
}
if i >= text.length() {
return Err(InvalidNumber(text))
}
let mag : Array[Byte] = []
let mut seen = false
while i < text.length() {
let c = text[i].to_int()
if c == 95 {
i += 1
continue
}
if !is_digit(c) {
return Err(InvalidNumber(text))
}
seen = true
mul_add_radix(mag, 10, c - 48)
i += 1
}
if !seen {
return Err(InvalidNumber(text))
}
Ok(mag_le_to_bigint(negative, mag))
}
///|
pub fn bigint_from_radix_string(
text : String,
radix : Int,
) -> Result[BigInt, IonError] {
if text.length() == 0 {
return Err(InvalidNumber(text))
}
let mut i = 0
let mut negative = false
if text[0].to_int() == 45 {
negative = true
i = 1
} else if text[0].to_int() == 43 {
i = 1
}
if i >= text.length() {
return Err(InvalidNumber(text))
}
let mag : Array[Byte] = []
let mut seen = false
while i < text.length() {
let c = text[i].to_int()
if c == 95 {
i += 1
continue
}
let digit = if is_digit(c) {
c - 48
} else if c >= 65 && c <= 90 {
c - 55
} else if c >= 97 && c <= 122 {
c - 87
} else {
return Err(InvalidNumber(text))
}
if digit >= radix {
return Err(InvalidNumber(text))
}
seen = true
mul_add_radix(mag, radix, digit)
i += 1
}
if !seen {
return Err(InvalidNumber(text))
}
Ok(mag_le_to_bigint(negative, mag))
}
///|
pub fn BigInt::compare(self : BigInt, other : BigInt) -> Int {
if self.sign() != other.sign() {
if self.sign() < other.sign() {
-1
} else {
1
}
} else if self.digits.length() != other.digits.length() {
let cmp = if self.digits.length() > other.digits.length() { 1 } else { -1 }
if self.negative {
0 - cmp
} else {
cmp
}
} else {
let a = bytes_to_array(self.digits)
let b = bytes_to_array(other.digits)
let mut i = 0
let mut cmp = 0
while i < a.length() {
if a[i].to_int() != b[i].to_int() {
cmp = if a[i].to_int() > b[i].to_int() { 1 } else { -1 }
i = a.length()
} else {
i += 1
}
}
if self.negative {
0 - cmp
} else {
cmp
}
}
}
///|
pub fn BigInt::eq_value(self : BigInt, other : BigInt) -> Bool {
self.compare(other) == 0
}
///|
fn mul_add_radix(mag : Array[Byte], radix : Int, digit : Int) -> Unit {
let mut carry = digit
let mut k = 0
while k < mag.length() || carry > 0 {
let cur = if k < mag.length() {
mag[k].to_int() * radix + carry
} else {
carry
}
if k < mag.length() {
mag[k] = (cur & 255).to_byte()
} else {
mag.push((cur & 255).to_byte())
}
carry = cur >> 8
k += 1
}
}
///|
fn mag_le_to_bigint(negative : Bool, mag : Array[Byte]) -> BigInt {
if mag.length() == 0 {
bigint_zero()
} else {
let be : Array[Byte] = []
let mut i = mag.length() - 1
while i >= 0 {
be.push(mag[i])
i -= 1
}
bigint_from_magnitude(negative, array_to_bytes(be))
}
}