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

master
Delyan Angelov 2022-05-13 08:21:34 +03:00 committed by GitHub
parent 3c95504a35
commit ed12a5c84c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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
// 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)
}

View File

@ -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
}
)

View File

@ -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)

View File

@ -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

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 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()
}
}