math.big: reduce memory usage of Integer.str() (free intermediary Integers), optimise some operations using `[direct_array_access]` (#14353)

Delyan Angelov 2022-05-13 08:21:34 +03:00 committed by Jef Roosens
parent 870ed86afc
commit 1e750041ee
Signed by: Jef Roosens
GPG Key ID: B75D4F293C7052DB
6 changed files with 186 additions and 98 deletions

View File

@ -5,6 +5,7 @@ import math
// Compares the magnitude of the two unsigned integers represented the given // Compares the magnitude of the two unsigned integers represented the given
// digit arrays. Returns -1 if a < b, 0 if a == b and +1 if a > b. Here // digit arrays. Returns -1 if a < b, 0 if a == b and +1 if a > b. Here
// a is operand_a and b is operand_b (for brevity). // a is operand_a and b is operand_b (for brevity).
[direct_array_access]
fn compare_digit_array(operand_a []u32, operand_b []u32) int { fn compare_digit_array(operand_a []u32, operand_b []u32) int {
a_len := operand_a.len a_len := operand_a.len
b_len := operand_b.len b_len := operand_b.len
@ -26,6 +27,7 @@ fn compare_digit_array(operand_a []u32, operand_b []u32) int {
// Add the digits in operand_a and operand_b and stores the result in sum. // Add the digits in operand_a and operand_b and stores the result in sum.
// This function does not perform any allocation and assumes that the storage is // This function does not perform any allocation and assumes that the storage is
// large enough. It may affect the last element, based on the presence of a carry // large enough. It may affect the last element, based on the presence of a carry
[direct_array_access]
fn add_digit_array(operand_a []u32, operand_b []u32, mut sum []u32) { fn add_digit_array(operand_a []u32, operand_b []u32, mut sum []u32) {
// Zero length cases // Zero length cases
if operand_a.len == 0 { if operand_a.len == 0 {
@ -70,6 +72,7 @@ fn add_digit_array(operand_a []u32, operand_b []u32, mut sum []u32) {
// Subtracts operand_b from operand_a and stores the difference in storage. // Subtracts operand_b from operand_a and stores the difference in storage.
// It assumes operand_a contains the larger "integer" and that storage is // It assumes operand_a contains the larger "integer" and that storage is
// the same size as operand_a and is 0 // the same size as operand_a and is 0
[direct_array_access]
fn subtract_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { fn subtract_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
// Zero length cases // Zero length cases
if operand_a.len == 0 { if operand_a.len == 0 {
@ -104,9 +107,7 @@ fn subtract_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
storage[index] = u32(a_digit - b_digit) storage[index] = u32(a_digit - b_digit)
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
const karatsuba_multiplication_limit = 1_000_000 const karatsuba_multiplication_limit = 1_000_000
@ -126,6 +127,7 @@ fn multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
// Multiplies the unsigned (non-negative) integers represented in a and b and the product is // Multiplies the unsigned (non-negative) integers represented in a and b and the product is
// stored in storage. It assumes that storage has length equal to the sum of lengths // stored in storage. It assumes that storage has length equal to the sum of lengths
// of a and b. Length refers to length of array, that is, digit count. // of a and b. Length refers to length of array, that is, digit count.
[direct_array_access]
fn simple_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { fn simple_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
for b_index in 0 .. operand_b.len { for b_index in 0 .. operand_b.len {
mut carry := u64(0) mut carry := u64(0)
@ -139,27 +141,22 @@ fn simple_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u
storage[b_index + operand_a.len] = u32(carry) storage[b_index + operand_a.len] = u32(carry)
} }
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
// Stores the product of the unsigned (non-negative) integer represented in a and the digit in value // Stores the product of the unsigned (non-negative) integer represented in a and the digit in value
// in the storage array. It assumes storage is pre-initialised and populated with 0's // in the storage array. It assumes storage is pre-initialised and populated with 0's
[direct_array_access]
fn multiply_array_by_digit(operand_a []u32, value u32, mut storage []u32) { fn multiply_array_by_digit(operand_a []u32, value u32, mut storage []u32) {
if value == 0 { if value == 0 {
for storage.len > 0 { storage.clear()
storage.delete_last()
}
return return
} }
if value == 1 { if value == 1 {
for index in 0 .. operand_a.len { for index in 0 .. operand_a.len {
storage[index] = operand_a[index] storage[index] = operand_a[index]
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
return return
} }
mut carry := u32(0) mut carry := u32(0)
@ -175,15 +172,14 @@ fn multiply_array_by_digit(operand_a []u32, value u32, mut storage []u32) {
storage << carry storage << carry
} }
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
// Divides the non-negative integer in a by non-negative integer b and store the two results // Divides the non-negative integer in a by non-negative integer b and store the two results
// in quotient and remainder respectively. It is different from the rest of the functions // in quotient and remainder respectively. It is different from the rest of the functions
// because it assumes that quotient and remainder are empty zero length arrays. They can be // because it assumes that quotient and remainder are empty zero length arrays. They can be
// made to have appropriate capacity though // made to have appropriate capacity though
[direct_array_access]
fn divide_digit_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) { fn divide_digit_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) {
cmp_result := compare_digit_array(operand_a, operand_b) cmp_result := compare_digit_array(operand_a, operand_b)
// a == b => q, r = 1, 0 // a == b => q, r = 1, 0
@ -192,17 +188,13 @@ fn divide_digit_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut
for quotient.len > 1 { for quotient.len > 1 {
quotient.delete_last() quotient.delete_last()
} }
for remainder.len > 0 { remainder.clear()
remainder.delete_last()
}
return return
} }
// a < b => q, r = 0, a // a < b => q, r = 0, a
if cmp_result < 0 { if cmp_result < 0 {
for quotient.len > 0 { quotient.clear()
quotient.delete_last()
}
for index in 0 .. operand_a.len { for index in 0 .. operand_a.len {
remainder << operand_a[index] remainder << operand_a[index]
} }
@ -217,6 +209,7 @@ fn divide_digit_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut
// Performs division on the non-negative dividend in a by the single digit divisor b. It assumes // Performs division on the non-negative dividend in a by the single digit divisor b. It assumes
// quotient and remainder are empty zero length arrays without previous allocation // quotient and remainder are empty zero length arrays without previous allocation
[direct_array_access]
fn divide_array_by_digit(operand_a []u32, divisor u32, mut quotient []u32, mut remainder []u32) { fn divide_array_by_digit(operand_a []u32, divisor u32, mut quotient []u32, mut remainder []u32) {
if operand_a.len == 1 { if operand_a.len == 1 {
// 1 digit for both dividend and divisor // 1 digit for both dividend and divisor
@ -245,13 +238,9 @@ fn divide_array_by_digit(operand_a []u32, divisor u32, mut quotient []u32, mut r
rem = dividend % divisor64 rem = dividend % divisor64
} }
// Remove leading zeros from quotient // Remove leading zeros from quotient
for quotient.len > 0 && quotient.last() == 0 { shrink_tail_zeros(mut quotient)
quotient.delete_last()
}
remainder << u32(rem) remainder << u32(rem)
for remainder.len > 0 && remainder.last() == 0 { shrink_tail_zeros(mut remainder)
remainder.delete_last()
}
} }
const newton_division_limit = 10_000 const newton_division_limit = 10_000
@ -268,6 +257,7 @@ fn divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient []u32, m
// Shifts the contents of the original array by the given amount of bits to the left. // Shifts the contents of the original array by the given amount of bits to the left.
// This function assumes that the amount is less than 32. The storage is expected to // This function assumes that the amount is less than 32. The storage is expected to
// allocated with zeroes. // allocated with zeroes.
[direct_array_access]
fn shift_digits_left(original []u32, amount u32, mut storage []u32) { fn shift_digits_left(original []u32, amount u32, mut storage []u32) {
mut leftover := u32(0) mut leftover := u32(0)
offset := 32 - amount offset := 32 - amount
@ -284,6 +274,7 @@ fn shift_digits_left(original []u32, amount u32, mut storage []u32) {
// Shifts the contents of the original array by the given amount of bits to the right. // Shifts the contents of the original array by the given amount of bits to the right.
// This function assumes that the amount is less than 32. The storage is expected to // This function assumes that the amount is less than 32. The storage is expected to
// be allocated with zeroes. // be allocated with zeroes.
[direct_array_access]
fn shift_digits_right(original []u32, amount u32, mut storage []u32) { fn shift_digits_right(original []u32, amount u32, mut storage []u32) {
mut moveover := u32(0) mut moveover := u32(0)
mask := (u32(1) << amount) - 1 mask := (u32(1) << amount) - 1
@ -293,11 +284,10 @@ fn shift_digits_right(original []u32, amount u32, mut storage []u32) {
moveover = original[index] & mask moveover = original[index] & mask
storage[index] = value storage[index] = value
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
[direct_array_access]
fn bitwise_or_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { fn bitwise_or_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
lower, upper, bigger := if operand_a.len < operand_b.len { lower, upper, bigger := if operand_a.len < operand_b.len {
operand_a.len, operand_b.len, operand_b operand_a.len, operand_b.len, operand_b
@ -310,21 +300,19 @@ fn bitwise_or_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
for index in lower .. upper { for index in lower .. upper {
storage[index] = bigger[index] storage[index] = bigger[index]
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
[direct_array_access]
fn bitwise_and_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { fn bitwise_and_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
lower := math.min(operand_a.len, operand_b.len) lower := math.min(operand_a.len, operand_b.len)
for index in 0 .. lower { for index in 0 .. lower {
storage[index] = operand_a[index] & operand_b[index] storage[index] = operand_a[index] & operand_b[index]
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
[direct_array_access]
fn bitwise_xor_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { fn bitwise_xor_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
lower, upper, bigger := if operand_a.len < operand_b.len { lower, upper, bigger := if operand_a.len < operand_b.len {
operand_a.len, operand_b.len, operand_b operand_a.len, operand_b.len, operand_b
@ -337,16 +325,13 @@ fn bitwise_xor_digit_array(operand_a []u32, operand_b []u32, mut storage []u32)
for index in lower .. upper { for index in lower .. upper {
storage[index] = bigger[index] storage[index] = bigger[index]
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
[direct_array_access]
fn bitwise_not_digit_array(original []u32, mut storage []u32) { fn bitwise_not_digit_array(original []u32, mut storage []u32) {
for index in 0 .. original.len { for index in 0 .. original.len {
storage[index] = ~original[index] storage[index] = ~original[index]
} }
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }

View File

@ -4,13 +4,16 @@ pub const (
zero_int = Integer{ zero_int = Integer{
digits: []u32{len: 0} digits: []u32{len: 0}
signum: 0 signum: 0
is_const: true
} }
one_int = Integer{ one_int = Integer{
digits: [u32(1)] digits: [u32(1)]
signum: 1 signum: 1
is_const: true
} }
two_int = Integer{ two_int = Integer{
digits: [u32(2)] digits: [u32(2)]
signum: 1 signum: 1
is_const: true
} }
) )

View File

@ -4,6 +4,7 @@ import math.bits
// suppose operand_a bigger than operand_b and both not null. // suppose operand_a bigger than operand_b and both not null.
// Both quotient and remaider are allocated but of length 0 // Both quotient and remaider are allocated but of length 0
[direct_array_access]
fn binary_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) { fn binary_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) {
for index in 0 .. operand_a.len { for index in 0 .. operand_a.len {
remainder << operand_a[index] remainder << operand_a[index]
@ -53,17 +54,13 @@ fn binary_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient [
// rshift_in_place(mut quotient, lead_zer_remainder - lead_zer_divisor) // rshift_in_place(mut quotient, lead_zer_remainder - lead_zer_divisor)
rshift_in_place(mut remainder, lead_zer_remainder - lead_zer_divisor) rshift_in_place(mut remainder, lead_zer_remainder - lead_zer_divisor)
} }
for remainder.len > 0 && remainder.last() == 0 { shrink_tail_zeros(mut remainder)
remainder.delete_last() shrink_tail_zeros(mut quotient)
}
for quotient.len > 0 && quotient.last() == 0 {
quotient.delete_last()
}
} }
// help routines for cleaner code but inline for performance // help routines for cleaner code but inline for performance
// quicker than BitField.set_bit // quicker than BitField.set_bit
[inline] [direct_array_access; inline]
fn bit_set(mut a []u32, n int) { fn bit_set(mut a []u32, n int) {
byte_offset := n >> 5 byte_offset := n >> 5
mask := u32(1) << u32(n % 32) mask := u32(1) << u32(n % 32)
@ -75,7 +72,7 @@ fn bit_set(mut a []u32, n int) {
// a.len is greater or equal to b.len // a.len is greater or equal to b.len
// returns true if a >= b (completed with zeroes) // returns true if a >= b (completed with zeroes)
[inline] [direct_array_access; inline]
fn greater_equal_from_end(a []u32, b []u32) bool { fn greater_equal_from_end(a []u32, b []u32) bool {
$if debug { $if debug {
assert a.len >= b.len assert a.len >= b.len
@ -93,7 +90,7 @@ fn greater_equal_from_end(a []u32, b []u32) bool {
// a := a - b supposed a >= b // a := a - b supposed a >= b
// attention the b operand is align with the a operand before the subtraction // attention the b operand is align with the a operand before the subtraction
[inline] [direct_array_access; inline]
fn subtract_align_last_byte_in_place(mut a []u32, b []u32) { fn subtract_align_last_byte_in_place(mut a []u32, b []u32) {
mut carry := u32(0) mut carry := u32(0)
mut new_carry := u32(0) mut new_carry := u32(0)
@ -115,7 +112,7 @@ fn subtract_align_last_byte_in_place(mut a []u32, b []u32) {
// logical left shift // logical left shift
// there is no overflow. We know that the last bits are zero // there is no overflow. We know that the last bits are zero
// and that n <= 32 // and that n <= 32
[inline] [direct_array_access; inline]
fn lshift_in_place(mut a []u32, n u32) { fn lshift_in_place(mut a []u32, n u32) {
mut carry := u32(0) mut carry := u32(0)
mut prec_carry := u32(0) mut prec_carry := u32(0)
@ -130,7 +127,7 @@ fn lshift_in_place(mut a []u32, n u32) {
// logical right shift without control because these digits have already been // logical right shift without control because these digits have already been
// shift left before // shift left before
[inline] [direct_array_access; inline]
fn rshift_in_place(mut a []u32, n u32) { fn rshift_in_place(mut a []u32, n u32) {
mut carry := u32(0) mut carry := u32(0)
mut prec_carry := u32(0) mut prec_carry := u32(0)

View File

@ -22,7 +22,24 @@ const (
pub struct Integer { pub struct Integer {
digits []u32 digits []u32
pub: pub:
signum int signum int
is_const bool
}
[unsafe]
fn (mut x Integer) free() {
if x.is_const {
return
}
unsafe { x.digits.free() }
}
fn (x Integer) clone() Integer {
return Integer{
digits: x.digits.clone()
signum: x.signum
is_const: false
}
} }
fn int_signum(value int) int { fn int_signum(value int) int {
@ -106,15 +123,18 @@ pub struct IntegerConfig {
signum int = 1 signum int = 1
} }
// integer_from_bytes creates a new `big.Integer` from the given byte array. By default, positive integers are assumed. If you want a negative integer, use in the following manner: // integer_from_bytes creates a new `big.Integer` from the given byte array.
// By default, positive integers are assumed.
// If you want a negative integer, use in the following manner:
// `value := big.integer_from_bytes(bytes, signum: -1)` // `value := big.integer_from_bytes(bytes, signum: -1)`
[direct_array_access]
pub fn integer_from_bytes(input []u8, config IntegerConfig) Integer { pub fn integer_from_bytes(input []u8, config IntegerConfig) Integer {
// Thank you to Miccah (@mcastorina) for this implementation and relevant unit tests. // Thank you to Miccah (@mcastorina) for this implementation and relevant unit tests.
if input.len == 0 { if input.len == 0 {
return integer_from_int(0) return integer_from_int(0)
} }
// pad input // pad input
mut padded_input := []u8{len: ((input.len + 3) & ~0x3) - input.len, cap: (input.len + 3) & ~0x3, init: 0x0} mut padded_input := []u8{len: ((input.len + 3) & ~0x3) - input.len, cap: (input.len + 3) & ~0x3}
padded_input << input padded_input << input
mut digits := []u32{len: padded_input.len / 4} mut digits := []u32{len: padded_input.len / 4}
// combine every 4 bytes into a u32 and insert into n.digits // combine every 4 bytes into a u32 and insert into n.digits
@ -158,6 +178,7 @@ pub fn integer_from_radix(all_characters string, radix u32) ?Integer {
} }
} }
[direct_array_access]
fn validate_string(characters string, radix u32) ? { fn validate_string(characters string, radix u32) ? {
sign_present := characters[0] == `+` || characters[0] == `-` sign_present := characters[0] == `+` || characters[0] == `-`
@ -176,6 +197,7 @@ fn validate_string(characters string, radix u32) ? {
} }
} }
[direct_array_access]
fn integer_from_special_string(characters string, chunk_size int) Integer { fn integer_from_special_string(characters string, chunk_size int) Integer {
sign_present := characters[0] == `+` || characters[0] == `-` sign_present := characters[0] == `+` || characters[0] == `-`
@ -209,9 +231,7 @@ fn integer_from_special_string(characters string, chunk_size int) Integer {
big_digits << current big_digits << current
} }
for big_digits.len > 0 && big_digits.last() == 0 { shrink_tail_zeros(mut big_digits)
big_digits.delete_last()
}
return Integer{ return Integer{
digits: big_digits digits: big_digits
@ -219,6 +239,7 @@ fn integer_from_special_string(characters string, chunk_size int) Integer {
} }
} }
[direct_array_access]
fn integer_from_regular_string(characters string, radix u32) Integer { fn integer_from_regular_string(characters string, radix u32) Integer {
sign_present := characters[0] == `+` || characters[0] == `-` sign_present := characters[0] == `+` || characters[0] == `-`
@ -242,7 +263,7 @@ fn integer_from_regular_string(characters string, radix u32) Integer {
} }
return Integer{ return Integer{
...result digits: result.digits.clone()
signum: result.signum * signum signum: result.signum * signum
} }
} }
@ -253,7 +274,7 @@ pub fn (integer Integer) abs() Integer {
zero_int zero_int
} else { } else {
Integer{ Integer{
...integer digits: integer.digits.clone()
signum: 1 signum: 1
} }
} }
@ -265,7 +286,7 @@ pub fn (integer Integer) neg() Integer {
zero_int zero_int
} else { } else {
Integer{ Integer{
...integer digits: integer.digits.clone()
signum: -integer.signum signum: -integer.signum
} }
} }
@ -274,10 +295,10 @@ pub fn (integer Integer) neg() Integer {
pub fn (integer Integer) + (addend Integer) Integer { pub fn (integer Integer) + (addend Integer) Integer {
// Quick exits // Quick exits
if integer.signum == 0 { if integer.signum == 0 {
return addend return addend.clone()
} }
if addend.signum == 0 { if addend.signum == 0 {
return integer return integer.clone()
} }
// Non-zero cases // Non-zero cases
return if integer.signum == addend.signum { return if integer.signum == addend.signum {
@ -293,7 +314,7 @@ pub fn (integer Integer) - (subtrahend Integer) Integer {
return subtrahend.neg() return subtrahend.neg()
} }
if subtrahend.signum == 0 { if subtrahend.signum == 0 {
return integer return integer.clone()
} }
// Non-zero cases // Non-zero cases
return if integer.signum == subtrahend.signum { return if integer.signum == subtrahend.signum {
@ -309,7 +330,7 @@ fn (integer Integer) add(addend Integer) Integer {
mut storage := []u32{len: math.max(a.len, b.len) + 1} mut storage := []u32{len: math.max(a.len, b.len) + 1}
add_digit_array(a, b, mut storage) add_digit_array(a, b, mut storage)
return Integer{ return Integer{
...integer signum: integer.signum
digits: storage digits: storage
} }
} }
@ -334,10 +355,10 @@ pub fn (integer Integer) * (multiplicand Integer) Integer {
return zero_int return zero_int
} }
if integer == one_int { if integer == one_int {
return multiplicand return multiplicand.clone()
} }
if multiplicand == one_int { if multiplicand == one_int {
return integer return integer.clone()
} }
// The final sign is the product of the signs // The final sign is the product of the signs
mut storage := []u32{len: integer.digits.len + multiplicand.digits.len} mut storage := []u32{len: integer.digits.len + multiplicand.digits.len}
@ -358,7 +379,7 @@ pub fn (integer Integer) div_mod(divisor Integer) (Integer, Integer) {
return zero_int, zero_int return zero_int, zero_int
} }
if divisor == one_int { if divisor == one_int {
return integer, zero_int return integer.clone(), zero_int
} }
if divisor.signum == -1 { if divisor.signum == -1 {
q, r := integer.div_mod(divisor.neg()) q, r := integer.div_mod(divisor.neg())
@ -403,7 +424,7 @@ pub fn (a Integer) pow(exponent u32) Integer {
return one_int return one_int
} }
if exponent == 1 { if exponent == 1 {
return a return a.clone()
} }
mut n := exponent mut n := exponent
mut x := a mut x := a
@ -440,6 +461,7 @@ pub fn (a Integer) mod_pow(exponent u32, divisor Integer) Integer {
} }
// big_mod_power returns the integer `a` raised to the power of the integer `exponent` modulo the integer `divisor`. // big_mod_power returns the integer `a` raised to the power of the integer `exponent` modulo the integer `divisor`.
[direct_array_access]
pub fn (a Integer) big_mod_pow(exponent Integer, divisor Integer) Integer { pub fn (a Integer) big_mod_pow(exponent Integer, divisor Integer) Integer {
if exponent.signum < 0 { if exponent.signum < 0 {
panic('Exponent needs to be non-negative.') panic('Exponent needs to be non-negative.')
@ -521,6 +543,7 @@ fn check_sign(a Integer) {
} }
// get_bit checks whether the bit at the given index is set. // get_bit checks whether the bit at the given index is set.
[direct_array_access]
pub fn (a Integer) get_bit(i u32) bool { pub fn (a Integer) get_bit(i u32) bool {
check_sign(a) check_sign(a)
target_index := i / 32 target_index := i / 32
@ -562,7 +585,7 @@ pub fn (mut a Integer) set_bit(i u32, value bool) {
pub fn (a Integer) bitwise_or(b Integer) Integer { pub fn (a Integer) bitwise_or(b Integer) Integer {
check_sign(a) check_sign(a)
check_sign(b) check_sign(b)
mut result := []u32{len: math.max(a.digits.len, b.digits.len), init: 0} mut result := []u32{len: math.max(a.digits.len, b.digits.len)}
bitwise_or_digit_array(a.digits, b.digits, mut result) bitwise_or_digit_array(a.digits, b.digits, mut result)
return Integer{ return Integer{
digits: result digits: result
@ -574,7 +597,7 @@ pub fn (a Integer) bitwise_or(b Integer) Integer {
pub fn (a Integer) bitwise_and(b Integer) Integer { pub fn (a Integer) bitwise_and(b Integer) Integer {
check_sign(a) check_sign(a)
check_sign(b) check_sign(b)
mut result := []u32{len: math.max(a.digits.len, b.digits.len), init: 0} mut result := []u32{len: math.max(a.digits.len, b.digits.len)}
bitwise_and_digit_array(a.digits, b.digits, mut result) bitwise_and_digit_array(a.digits, b.digits, mut result)
return Integer{ return Integer{
digits: result digits: result
@ -585,7 +608,7 @@ pub fn (a Integer) bitwise_and(b Integer) Integer {
// bitwise_not returns the "bitwise not" of the integer `a`. // bitwise_not returns the "bitwise not" of the integer `a`.
pub fn (a Integer) bitwise_not() Integer { pub fn (a Integer) bitwise_not() Integer {
check_sign(a) check_sign(a)
mut result := []u32{len: a.digits.len, init: 0} mut result := []u32{len: a.digits.len}
bitwise_not_digit_array(a.digits, mut result) bitwise_not_digit_array(a.digits, mut result)
return Integer{ return Integer{
digits: result digits: result
@ -597,7 +620,7 @@ pub fn (a Integer) bitwise_not() Integer {
pub fn (a Integer) bitwise_xor(b Integer) Integer { pub fn (a Integer) bitwise_xor(b Integer) Integer {
check_sign(a) check_sign(a)
check_sign(b) check_sign(b)
mut result := []u32{len: math.max(a.digits.len, b.digits.len), init: 0} mut result := []u32{len: math.max(a.digits.len, b.digits.len)}
bitwise_xor_digit_array(a.digits, b.digits, mut result) bitwise_xor_digit_array(a.digits, b.digits, mut result)
return Integer{ return Integer{
digits: result digits: result
@ -606,6 +629,7 @@ pub fn (a Integer) bitwise_xor(b Integer) Integer {
} }
// lshift returns the integer `a` shifted left by `amount` bits. // lshift returns the integer `a` shifted left by `amount` bits.
[direct_array_access]
pub fn (a Integer) lshift(amount u32) Integer { pub fn (a Integer) lshift(amount u32) Integer {
if a.signum == 0 { if a.signum == 0 {
return a return a
@ -615,7 +639,7 @@ pub fn (a Integer) lshift(amount u32) Integer {
} }
normalised_amount := amount & 31 normalised_amount := amount & 31
digit_offset := int(amount >> 5) digit_offset := int(amount >> 5)
mut new_array := []u32{len: a.digits.len + digit_offset, init: 0} mut new_array := []u32{len: a.digits.len + digit_offset}
for index in 0 .. a.digits.len { for index in 0 .. a.digits.len {
new_array[index + digit_offset] = a.digits[index] new_array[index + digit_offset] = a.digits[index]
} }
@ -629,6 +653,7 @@ pub fn (a Integer) lshift(amount u32) Integer {
} }
// rshift returns the integer `a` shifted right by `amount` bits. // rshift returns the integer `a` shifted right by `amount` bits.
[direct_array_access]
pub fn (a Integer) rshift(amount u32) Integer { pub fn (a Integer) rshift(amount u32) Integer {
if a.signum == 0 { if a.signum == 0 {
return a return a
@ -641,7 +666,7 @@ pub fn (a Integer) rshift(amount u32) Integer {
if digit_offset >= a.digits.len { if digit_offset >= a.digits.len {
return zero_int return zero_int
} }
mut new_array := []u32{len: a.digits.len - digit_offset, init: 0} mut new_array := []u32{len: a.digits.len - digit_offset}
for index in 0 .. new_array.len { for index in 0 .. new_array.len {
new_array[index] = a.digits[index + digit_offset] new_array[index] = a.digits[index + digit_offset]
} }
@ -655,6 +680,7 @@ pub fn (a Integer) rshift(amount u32) Integer {
} }
// binary_str returns the binary string representation of the integer `a`. // binary_str returns the binary string representation of the integer `a`.
[direct_array_access]
pub fn (integer Integer) binary_str() string { pub fn (integer Integer) binary_str() string {
// We have the zero integer // We have the zero integer
if integer.signum == 0 { if integer.signum == 0 {
@ -676,6 +702,7 @@ pub fn (integer Integer) binary_str() string {
} }
// hex returns the hexadecimal string representation of the integer `a`. // hex returns the hexadecimal string representation of the integer `a`.
[direct_array_access]
pub fn (integer Integer) hex() string { pub fn (integer Integer) hex() string {
// We have the zero integer // We have the zero integer
if integer.signum == 0 { if integer.signum == 0 {
@ -717,13 +744,17 @@ pub fn (integer Integer) radix_str(radix u32) string {
fn (integer Integer) general_radix_str(radix u32) string { fn (integer Integer) general_radix_str(radix u32) string {
divisor := integer_from_u32(radix) divisor := integer_from_u32(radix)
mut rune_array := []rune{}
mut current := integer.abs() mut current := integer.abs()
mut new_current := zero_int
mut digit := zero_int mut digit := zero_int
mut rune_array := []rune{cap: current.digits.len * 4}
for current.signum > 0 { for current.signum > 0 {
current, digit = current.div_mod(divisor) new_current, digit = current.div_mod(divisor)
rune_array << big.digit_array[digit.int()] rune_array << big.digit_array[digit.int()]
unsafe { digit.free() }
unsafe { current.free() }
current = new_current
} }
if integer.signum == -1 { if integer.signum == -1 {
rune_array << `-` rune_array << `-`
@ -778,6 +809,7 @@ pub fn (a Integer) int() int {
// bytes returns the a byte representation of the integer a, along with the signum int. // bytes returns the a byte representation of the integer a, along with the signum int.
// NOTE: The byte array returned is in big endian order. // NOTE: The byte array returned is in big endian order.
[direct_array_access]
pub fn (a Integer) bytes() ([]u8, int) { pub fn (a Integer) bytes() ([]u8, int) {
if a.signum == 0 { if a.signum == 0 {
return []u8{len: 0}, 0 return []u8{len: 0}, 0
@ -923,7 +955,7 @@ fn gcd_binary(x Integer, y Integer) Integer {
} }
// bit_len returns the number of bits required to represent the integer `a`. // bit_len returns the number of bits required to represent the integer `a`.
[direct_array_access; inline] [inline]
pub fn (x Integer) bit_len() int { pub fn (x Integer) bit_len() int {
if x.signum == 0 { if x.signum == 0 {
return 0 return 0

View File

@ -0,0 +1,65 @@
import time
import math.big
const t = time.new_stopwatch()
// a := big.integer_from_i64(2).pow(123123123)
// println(a)
fn timed_println(msg string) {
timed_println_extended(t, msg)
}
fn timed_println_extended(t time.StopWatch, msg string) {
println('${t.elapsed().microseconds():12} | $msg')
}
fn f(x big.Integer, y int) big.Integer {
timed_println('f x y, y=${y:12}')
if y & 1 == 0 {
return f(x * x, y / 2)
}
if y == 1 {
return x
}
return g(x * x, y / 2, x)
}
fn g(x big.Integer, y int, z big.Integer) big.Integer {
timed_println('g x y z, y=${y:12}')
if y & 1 == 0 {
return g(x * x, y / 2, z)
}
if y == 1 {
return x * z
}
return g(x * x, y / 2, x * z)
}
fn calculate_and_measure(calc_label string, cb fn () big.Integer) string {
sw := time.new_stopwatch()
timed_println_extended(sw, 'start')
a := cb()
timed_println_extended(sw, 'done $calc_label')
timed_println_extended(sw, 'a.bit_len(): ${a.bit_len():12}')
timed_println_extended(sw, 'before a.str()')
// s := a.hex() // hex is very fast since it does not need to do divisions at all
s := a.str()
timed_println_extended(sw, 'after a.str()')
dump(s.len)
dump(s#[0..10])
dump(s#[-10..])
timed_println_extended(sw, 'finish')
return s
}
fn test_exponentiation() {
res1 := calculate_and_measure('f(x, y)', fn () big.Integer {
return f(big.integer_from_int(2), 66777)
})
res2 := calculate_and_measure('big.int(x).pow(y)', fn () big.Integer {
return big.integer_from_i64(2).pow(66777)
})
assert res1 == res2
}

View File

@ -4,6 +4,17 @@ import math
import math.bits import math.bits
import strings import strings
[direct_array_access; inline]
fn shrink_tail_zeros(mut a []u32) {
mut alen := a.len
for alen > 0 && a[alen - 1] == 0 {
alen--
}
unsafe {
a.len = alen
}
}
// suppose operand_a bigger than operand_b and both not null. // suppose operand_a bigger than operand_b and both not null.
// Both quotient and remaider are already allocated but of length 0 // Both quotient and remaider are already allocated but of length 0
fn newton_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) { fn newton_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) {
@ -47,9 +58,7 @@ fn newton_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient [
quotient = q.digits quotient = q.digits
remainder = r.digits remainder = r.digits
for remainder.len > 0 && remainder.last() == 0 { shrink_tail_zeros(mut remainder)
remainder.delete_last()
}
} }
[inline] [inline]
@ -57,7 +66,7 @@ fn bit_length(a Integer) int {
return a.digits.len * 32 - bits.leading_zeros_32(a.digits.last()) return a.digits.len * 32 - bits.leading_zeros_32(a.digits.last())
} }
[inline] [direct_array_access; inline]
fn debug_u32_str(a []u32) string { fn debug_u32_str(a []u32) string {
mut sb := strings.new_builder(30) mut sb := strings.new_builder(30)
sb.write_string('[') sb.write_string('[')
@ -76,12 +85,11 @@ fn debug_u32_str(a []u32) string {
// karatsuba algorithm for multiplication // karatsuba algorithm for multiplication
// possible optimisations: // possible optimisations:
// - transform one or all the recurrences in loops // - transform one or all the recurrences in loops
[direct_array_access]
fn karatsuba_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { fn karatsuba_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) {
// base case necessary to end recursion // base case necessary to end recursion
if operand_a.len == 0 || operand_b.len == 0 { if operand_a.len == 0 || operand_b.len == 0 {
for storage.len > 0 { storage.clear()
storage.delete_last()
}
return return
} }
@ -115,15 +123,15 @@ fn karatsuba_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage
// use storage for p_1 to avoid allocation and copy later // use storage for p_1 to avoid allocation and copy later
multiply_digit_array(a_h, b_h, mut storage) multiply_digit_array(a_h, b_h, mut storage)
mut p_3 := []u32{len: a_l.len + b_l.len + 1, init: 0} mut p_3 := []u32{len: a_l.len + b_l.len + 1}
multiply_digit_array(a_l, b_l, mut p_3) multiply_digit_array(a_l, b_l, mut p_3)
mut tmp_1 := []u32{len: math.max(a_h.len, a_l.len) + 1, init: 0} mut tmp_1 := []u32{len: math.max(a_h.len, a_l.len) + 1}
mut tmp_2 := []u32{len: math.max(b_h.len, b_l.len) + 1, init: 0} mut tmp_2 := []u32{len: math.max(b_h.len, b_l.len) + 1}
add_digit_array(a_h, a_l, mut tmp_1) add_digit_array(a_h, a_l, mut tmp_1)
add_digit_array(b_h, b_l, mut tmp_2) add_digit_array(b_h, b_l, mut tmp_2)
mut p_2 := []u32{len: operand_a.len + operand_b.len + 1, init: 0} mut p_2 := []u32{len: operand_a.len + operand_b.len + 1}
multiply_digit_array(tmp_1, tmp_2, mut p_2) multiply_digit_array(tmp_1, tmp_2, mut p_2)
subtract_in_place(mut p_2, storage) // p_1 subtract_in_place(mut p_2, storage) // p_1
subtract_in_place(mut p_2, p_3) subtract_in_place(mut p_2, p_3)
@ -134,14 +142,12 @@ fn karatsuba_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage
add_in_place(mut storage, p_2) add_in_place(mut storage, p_2)
add_in_place(mut storage, p_3) add_in_place(mut storage, p_3)
for storage.len > 0 && storage.last() == 0 { shrink_tail_zeros(mut storage)
storage.delete_last()
}
} }
[inline] [inline]
fn pow2(k int) Integer { fn pow2(k int) Integer {
mut ret := []u32{len: (k >> 5) + 1, init: 0} mut ret := []u32{len: (k >> 5) + 1}
bit_set(mut ret, k) bit_set(mut ret, k)
return Integer{ return Integer{
signum: 1 signum: 1
@ -150,6 +156,7 @@ fn pow2(k int) Integer {
} }
// optimized left shift of full u8(s) in place. byte_nb must be positive // optimized left shift of full u8(s) in place. byte_nb must be positive
[direct_array_access]
fn lshift_byte_in_place(mut a []u32, byte_nb int) { fn lshift_byte_in_place(mut a []u32, byte_nb int) {
a_len := a.len a_len := a.len
// control or allocate capacity // control or allocate capacity
@ -166,7 +173,7 @@ fn lshift_byte_in_place(mut a []u32, byte_nb int) {
// operand b can be greater than operand a // operand b can be greater than operand a
// the capacity of both array is supposed to be sufficient // the capacity of both array is supposed to be sufficient
[inline] [direct_array_access; inline]
fn add_in_place(mut a []u32, b []u32) { fn add_in_place(mut a []u32, b []u32) {
len_a := a.len len_a := a.len
len_b := b.len len_b := b.len
@ -194,6 +201,7 @@ fn add_in_place(mut a []u32, b []u32) {
} }
// a := a - b supposed a >= b // a := a - b supposed a >= b
[direct_array_access]
fn subtract_in_place(mut a []u32, b []u32) { fn subtract_in_place(mut a []u32, b []u32) {
len_a := a.len len_a := a.len
len_b := b.len len_b := b.len
@ -221,8 +229,6 @@ fn subtract_in_place(mut a []u32, b []u32) {
carry = new_carry carry = new_carry
} }
} else { // if len.b > len.a return zero } else { // if len.b > len.a return zero
for a.len > 0 { a.clear()
a.delete_last()
}
} }
} }