math.big: reduce memory usage of Integer.str() (free intermediary Integers), optimise some operations using `[direct_array_access]` (#14353)
parent
870ed86afc
commit
1e750041ee
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue