From ed12a5c84c8565a52ea44f68756e198658e3757e Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 13 May 2022 08:21:34 +0300 Subject: [PATCH] math.big: reduce memory usage of Integer.str() (free intermediary Integers), optimise some operations using `[direct_array_access]` (#14353) --- vlib/math/big/array_ops.v | 69 ++++++--------- vlib/math/big/big.v | 3 + vlib/math/big/division_array_ops.v | 19 ++--- vlib/math/big/integer.v | 84 +++++++++++++------ ..._number_power_and_string_conversion_test.v | 65 ++++++++++++++ vlib/math/big/special_array_ops.v | 44 +++++----- 6 files changed, 186 insertions(+), 98 deletions(-) create mode 100644 vlib/math/big/large_number_power_and_string_conversion_test.v diff --git a/vlib/math/big/array_ops.v b/vlib/math/big/array_ops.v index 704b5d0bde..536c43ea80 100644 --- a/vlib/math/big/array_ops.v +++ b/vlib/math/big/array_ops.v @@ -5,6 +5,7 @@ import math // 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 // 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 { a_len := operand_a.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. // 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 +[direct_array_access] fn add_digit_array(operand_a []u32, operand_b []u32, mut sum []u32) { // Zero length cases 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. // It assumes operand_a contains the larger "integer" and that storage is // 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) { // Zero length cases 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) } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } 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 // 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. +[direct_array_access] fn simple_multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { for b_index in 0 .. operand_b.len { 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) } } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } // 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 +[direct_array_access] fn multiply_array_by_digit(operand_a []u32, value u32, mut storage []u32) { if value == 0 { - for storage.len > 0 { - storage.delete_last() - } + storage.clear() return } if value == 1 { for index in 0 .. operand_a.len { storage[index] = operand_a[index] } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) return } mut carry := u32(0) @@ -175,15 +172,14 @@ fn multiply_array_by_digit(operand_a []u32, value u32, mut storage []u32) { storage << carry } } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } // 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 // because it assumes that quotient and remainder are empty zero length arrays. They can be // 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) { cmp_result := compare_digit_array(operand_a, operand_b) // 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 { quotient.delete_last() } - for remainder.len > 0 { - remainder.delete_last() - } + remainder.clear() return } // a < b => q, r = 0, a if cmp_result < 0 { - for quotient.len > 0 { - quotient.delete_last() - } + quotient.clear() for index in 0 .. operand_a.len { 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 // 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) { if operand_a.len == 1 { // 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 } // Remove leading zeros from quotient - for quotient.len > 0 && quotient.last() == 0 { - quotient.delete_last() - } + shrink_tail_zeros(mut quotient) remainder << u32(rem) - for remainder.len > 0 && remainder.last() == 0 { - remainder.delete_last() - } + shrink_tail_zeros(mut remainder) } 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. // This function assumes that the amount is less than 32. The storage is expected to // allocated with zeroes. +[direct_array_access] fn shift_digits_left(original []u32, amount u32, mut storage []u32) { mut leftover := u32(0) 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. // This function assumes that the amount is less than 32. The storage is expected to // be allocated with zeroes. +[direct_array_access] fn shift_digits_right(original []u32, amount u32, mut storage []u32) { mut moveover := u32(0) mask := (u32(1) << amount) - 1 @@ -293,11 +284,10 @@ fn shift_digits_right(original []u32, amount u32, mut storage []u32) { moveover = original[index] & mask storage[index] = value } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } +[direct_array_access] fn bitwise_or_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { lower, upper, bigger := if operand_a.len < operand_b.len { 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 { storage[index] = bigger[index] } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } +[direct_array_access] fn bitwise_and_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { lower := math.min(operand_a.len, operand_b.len) for index in 0 .. lower { storage[index] = operand_a[index] & operand_b[index] } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } +[direct_array_access] fn bitwise_xor_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { lower, upper, bigger := if operand_a.len < operand_b.len { 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 { storage[index] = bigger[index] } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } +[direct_array_access] fn bitwise_not_digit_array(original []u32, mut storage []u32) { for index in 0 .. original.len { storage[index] = ~original[index] } - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } diff --git a/vlib/math/big/big.v b/vlib/math/big/big.v index ab8d9df00a..a883472216 100644 --- a/vlib/math/big/big.v +++ b/vlib/math/big/big.v @@ -4,13 +4,16 @@ pub const ( zero_int = Integer{ digits: []u32{len: 0} signum: 0 + is_const: true } one_int = Integer{ digits: [u32(1)] signum: 1 + is_const: true } two_int = Integer{ digits: [u32(2)] signum: 1 + is_const: true } ) diff --git a/vlib/math/big/division_array_ops.v b/vlib/math/big/division_array_ops.v index cc71fd18df..fa73e7837e 100644 --- a/vlib/math/big/division_array_ops.v +++ b/vlib/math/big/division_array_ops.v @@ -4,6 +4,7 @@ import math.bits // suppose operand_a bigger than operand_b and both not null. // 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) { for index in 0 .. operand_a.len { 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 remainder, lead_zer_remainder - lead_zer_divisor) } - for remainder.len > 0 && remainder.last() == 0 { - remainder.delete_last() - } - for quotient.len > 0 && quotient.last() == 0 { - quotient.delete_last() - } + shrink_tail_zeros(mut remainder) + shrink_tail_zeros(mut quotient) } // help routines for cleaner code but inline for performance // quicker than BitField.set_bit -[inline] +[direct_array_access; inline] fn bit_set(mut a []u32, n int) { byte_offset := n >> 5 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 // returns true if a >= b (completed with zeroes) -[inline] +[direct_array_access; inline] fn greater_equal_from_end(a []u32, b []u32) bool { $if debug { 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 // 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) { mut 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 // there is no overflow. We know that the last bits are zero // and that n <= 32 -[inline] +[direct_array_access; inline] fn lshift_in_place(mut a []u32, n u32) { mut 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 // shift left before -[inline] +[direct_array_access; inline] fn rshift_in_place(mut a []u32, n u32) { mut carry := u32(0) mut prec_carry := u32(0) diff --git a/vlib/math/big/integer.v b/vlib/math/big/integer.v index acae9bec1f..8ae09b432f 100644 --- a/vlib/math/big/integer.v +++ b/vlib/math/big/integer.v @@ -22,7 +22,24 @@ const ( pub struct Integer { digits []u32 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 { @@ -106,15 +123,18 @@ pub struct IntegerConfig { 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)` +[direct_array_access] pub fn integer_from_bytes(input []u8, config IntegerConfig) Integer { // Thank you to Miccah (@mcastorina) for this implementation and relevant unit tests. if input.len == 0 { return integer_from_int(0) } // 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 mut digits := []u32{len: padded_input.len / 4} // 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) ? { 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 { sign_present := characters[0] == `+` || characters[0] == `-` @@ -209,9 +231,7 @@ fn integer_from_special_string(characters string, chunk_size int) Integer { big_digits << current } - for big_digits.len > 0 && big_digits.last() == 0 { - big_digits.delete_last() - } + shrink_tail_zeros(mut big_digits) return Integer{ 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 { sign_present := characters[0] == `+` || characters[0] == `-` @@ -242,7 +263,7 @@ fn integer_from_regular_string(characters string, radix u32) Integer { } return Integer{ - ...result + digits: result.digits.clone() signum: result.signum * signum } } @@ -253,7 +274,7 @@ pub fn (integer Integer) abs() Integer { zero_int } else { Integer{ - ...integer + digits: integer.digits.clone() signum: 1 } } @@ -265,7 +286,7 @@ pub fn (integer Integer) neg() Integer { zero_int } else { Integer{ - ...integer + digits: integer.digits.clone() signum: -integer.signum } } @@ -274,10 +295,10 @@ pub fn (integer Integer) neg() Integer { pub fn (integer Integer) + (addend Integer) Integer { // Quick exits if integer.signum == 0 { - return addend + return addend.clone() } if addend.signum == 0 { - return integer + return integer.clone() } // Non-zero cases return if integer.signum == addend.signum { @@ -293,7 +314,7 @@ pub fn (integer Integer) - (subtrahend Integer) Integer { return subtrahend.neg() } if subtrahend.signum == 0 { - return integer + return integer.clone() } // Non-zero cases 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} add_digit_array(a, b, mut storage) return Integer{ - ...integer + signum: integer.signum digits: storage } } @@ -334,10 +355,10 @@ pub fn (integer Integer) * (multiplicand Integer) Integer { return zero_int } if integer == one_int { - return multiplicand + return multiplicand.clone() } if multiplicand == one_int { - return integer + return integer.clone() } // The final sign is the product of the signs 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 } if divisor == one_int { - return integer, zero_int + return integer.clone(), zero_int } if divisor.signum == -1 { q, r := integer.div_mod(divisor.neg()) @@ -403,7 +424,7 @@ pub fn (a Integer) pow(exponent u32) Integer { return one_int } if exponent == 1 { - return a + return a.clone() } mut n := exponent 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`. +[direct_array_access] pub fn (a Integer) big_mod_pow(exponent Integer, divisor Integer) Integer { if exponent.signum < 0 { 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. +[direct_array_access] pub fn (a Integer) get_bit(i u32) bool { check_sign(a) 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 { check_sign(a) 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) return Integer{ digits: result @@ -574,7 +597,7 @@ pub fn (a Integer) bitwise_or(b Integer) Integer { pub fn (a Integer) bitwise_and(b Integer) Integer { check_sign(a) 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) return Integer{ 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`. pub fn (a Integer) bitwise_not() Integer { 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) return Integer{ digits: result @@ -597,7 +620,7 @@ pub fn (a Integer) bitwise_not() Integer { pub fn (a Integer) bitwise_xor(b Integer) Integer { check_sign(a) 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) return Integer{ 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. +[direct_array_access] pub fn (a Integer) lshift(amount u32) Integer { if a.signum == 0 { return a @@ -615,7 +639,7 @@ pub fn (a Integer) lshift(amount u32) Integer { } normalised_amount := amount & 31 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 { 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. +[direct_array_access] pub fn (a Integer) rshift(amount u32) Integer { if a.signum == 0 { return a @@ -641,7 +666,7 @@ pub fn (a Integer) rshift(amount u32) Integer { if digit_offset >= a.digits.len { 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 { 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`. +[direct_array_access] pub fn (integer Integer) binary_str() string { // We have the zero integer 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`. +[direct_array_access] pub fn (integer Integer) hex() string { // We have the zero integer 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 { divisor := integer_from_u32(radix) - mut rune_array := []rune{} mut current := integer.abs() + mut new_current := zero_int mut digit := zero_int + mut rune_array := []rune{cap: current.digits.len * 4} for current.signum > 0 { - current, digit = current.div_mod(divisor) + new_current, digit = current.div_mod(divisor) rune_array << big.digit_array[digit.int()] + unsafe { digit.free() } + unsafe { current.free() } + current = new_current } if integer.signum == -1 { 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. // NOTE: The byte array returned is in big endian order. +[direct_array_access] pub fn (a Integer) bytes() ([]u8, int) { if a.signum == 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`. -[direct_array_access; inline] +[inline] pub fn (x Integer) bit_len() int { if x.signum == 0 { return 0 diff --git a/vlib/math/big/large_number_power_and_string_conversion_test.v b/vlib/math/big/large_number_power_and_string_conversion_test.v new file mode 100644 index 0000000000..de0b456975 --- /dev/null +++ b/vlib/math/big/large_number_power_and_string_conversion_test.v @@ -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 +} diff --git a/vlib/math/big/special_array_ops.v b/vlib/math/big/special_array_ops.v index e7f4f180a0..3c7c2d4156 100644 --- a/vlib/math/big/special_array_ops.v +++ b/vlib/math/big/special_array_ops.v @@ -4,6 +4,17 @@ import math import math.bits 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. // 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) { @@ -47,9 +58,7 @@ fn newton_divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient [ quotient = q.digits remainder = r.digits - for remainder.len > 0 && remainder.last() == 0 { - remainder.delete_last() - } + shrink_tail_zeros(mut remainder) } [inline] @@ -57,7 +66,7 @@ fn bit_length(a Integer) int { return a.digits.len * 32 - bits.leading_zeros_32(a.digits.last()) } -[inline] +[direct_array_access; inline] fn debug_u32_str(a []u32) string { mut sb := strings.new_builder(30) sb.write_string('[') @@ -76,12 +85,11 @@ fn debug_u32_str(a []u32) string { // karatsuba algorithm for multiplication // possible optimisations: // - 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) { // base case necessary to end recursion if operand_a.len == 0 || operand_b.len == 0 { - for storage.len > 0 { - storage.delete_last() - } + storage.clear() 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 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) - mut tmp_1 := []u32{len: math.max(a_h.len, a_l.len) + 1, init: 0} - mut tmp_2 := []u32{len: math.max(b_h.len, b_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} add_digit_array(a_h, a_l, mut tmp_1) 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) subtract_in_place(mut p_2, storage) // p_1 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_3) - for storage.len > 0 && storage.last() == 0 { - storage.delete_last() - } + shrink_tail_zeros(mut storage) } [inline] 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) return Integer{ 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 +[direct_array_access] fn lshift_byte_in_place(mut a []u32, byte_nb int) { a_len := a.len // 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 // the capacity of both array is supposed to be sufficient -[inline] +[direct_array_access; inline] fn add_in_place(mut a []u32, b []u32) { len_a := a.len len_b := b.len @@ -194,6 +201,7 @@ fn add_in_place(mut a []u32, b []u32) { } // a := a - b supposed a >= b +[direct_array_access] fn subtract_in_place(mut a []u32, b []u32) { len_a := a.len len_b := b.len @@ -221,8 +229,6 @@ fn subtract_in_place(mut a []u32, b []u32) { carry = new_carry } } else { // if len.b > len.a return zero - for a.len > 0 { - a.delete_last() - } + a.clear() } }