diff --git a/vlib/math/big/array_ops.v b/vlib/math/big/array_ops.v index 0b678ede1f..3124d23093 100644 --- a/vlib/math/big/array_ops.v +++ b/vlib/math/big/array_ops.v @@ -202,7 +202,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 with sufficient capacity +// quotient and remainder are empty zero length arrays without previous allocation 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 @@ -240,58 +240,6 @@ fn divide_array_by_digit(operand_a []u32, divisor u32, mut quotient []u32, mut r } } -// Performs division on the non-negative dividend in a by the multi digit divisor b. It assumes -// quotient and remainder are empty zero length arrays with sufficient capacity -// This is different from divide_digit_array because it depends on this very function -// after making sure that the divisor is indeed multi-digit. -fn 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] - } - for _ in 0 .. operand_b.len { - quotient << 0 - } - offset := operand_a.len - operand_b.len - divisor_last_index := operand_b.len - 1 - for index := offset; index >= 0; index-- { - dividend_last_index := divisor_last_index + index - value_upper := if remainder.len > dividend_last_index + 1 { - u64(remainder[dividend_last_index + 1]) - } else { - u64(0) - } - value_lower := if remainder.len > dividend_last_index { - u64(remainder[dividend_last_index]) - } else { - u64(0) - } - partial := value_lower + (value_upper << 32) - mut q := u32(partial / operand_b[divisor_last_index]) - if q > 0 { - mut modified_divisor := []u32{len: operand_b.len + index, init: 0} - for i in 0 .. operand_b.len { - modified_divisor[index + i] = operand_b[i] - } - - mut product := []u32{len: operand_b.len + 1, init: 0} - multiply_array_by_digit(modified_divisor, q, mut product) - for q > 0 && compare_digit_array(product, remainder) > 0 { - q-- - subtract_digit_array(product, modified_divisor, mut product) - } - subtract_digit_array(remainder, product, mut remainder) - } - quotient[index] = q - } - // Remove leading zeros from quotient and remainder - for quotient.len > 0 && quotient.last() == 0 { - quotient.delete_last() - } - for remainder.len > 0 && remainder.last() == 0 { - remainder.delete_last() - } -} - // 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. diff --git a/vlib/math/big/division_array_ops.v b/vlib/math/big/division_array_ops.v new file mode 100644 index 0000000000..b00cda2afa --- /dev/null +++ b/vlib/math/big/division_array_ops.v @@ -0,0 +1,139 @@ +module big + +import math.bits + +// suppose operand_a bigger than operand_b and both not null. +// Both quotient and remaider are allocated but of length 0 +fn 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] + } + + len_diff := operand_a.len - operand_b.len + assert len_diff >= 0 + + // we must do in place shift and operations. + mut divisor := []u32{cap: operand_b.len} + for _ in 0 .. len_diff { + divisor << u32(0) + } + for index in 0 .. operand_b.len { + divisor << operand_b[index] + } + for _ in 0 .. len_diff + 1 { + quotient << u32(0) + } + + lead_zer_remainder := u32(bits.leading_zeros_32(remainder.last())) + lead_zer_divisor := u32(bits.leading_zeros_32(divisor.last())) + bit_offset := (u32(32) * u32(len_diff)) + (lead_zer_divisor - lead_zer_remainder) + + // align + if lead_zer_remainder < lead_zer_divisor { + lshift_in_place(mut divisor, lead_zer_divisor - lead_zer_remainder) + } else if lead_zer_remainder > lead_zer_divisor { + lshift_in_place(mut remainder, lead_zer_remainder - lead_zer_divisor) + } + + assert left_align_p(divisor[divisor.len - 1], remainder[remainder.len - 1]) + for bit_idx := int(bit_offset); bit_idx >= 0; bit_idx-- { + if greater_equal_from_end(remainder, divisor) { + bit_set(mut quotient, bit_idx) + subtract_in_place(mut remainder, divisor) + } + rshift_in_place(mut divisor, 1) + } + + // ajust + if 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) + } + for remainder.len > 0 && remainder.last() == 0 { + remainder.delete_last() + } + for quotient.len > 0 && quotient.last() == 0 { + quotient.delete_last() + } +} + +// help routines for cleaner code but inline for performance +// quicker than BitField.set_bit +[inline] +fn bit_set(mut a []u32, n int) { + byte_offset := n / 32 + mask := u32(1) << u32(n % 32) + assert a.len >= byte_offset + a[byte_offset] |= mask +} + +// a.len is greater or equal to b.len +// returns true if a >= b (completed with zeroes) +[inline] +fn greater_equal_from_end(a []u32, b []u32) bool { + assert a.len >= b.len + offset := a.len - b.len + for index := a.len - 1; index >= offset; index-- { + if a[index] > b[index - offset] { + return true + } else if a[index] < b[index - offset] { + return false + } + } + return true +} + +// logical left shift +// there is no overflow. We know that the last bits are zero +// and that n <= 32 +[inline] +fn lshift_in_place(mut a []u32, n u32) { + mut carry := u32(0) + mut prec_carry := u32(0) + mask := ((u32(1) << n) - 1) << (32 - n) + for index in 0 .. a.len { + prec_carry = carry >> (32 - n) + carry = a[index] & mask + a[index] <<= n + a[index] |= prec_carry + } +} + +// logical right shift without control because these digits have already been +// shift left before +[inline] +fn rshift_in_place(mut a []u32, n u32) { + mut carry := u32(0) + mut prec_carry := u32(0) + mask := u32((1 << n) - 1) + for index := a.len - 1; index >= 0; index-- { + carry = a[index] & mask + a[index] >>= n + a[index] |= prec_carry << (32 - n) + prec_carry = carry + } +} + +// a := a - b supposed a >= b +[inline] +fn subtract_in_place(mut a []u32, b []u32) { + mut carry := u32(0) + mut new_carry := u32(0) + offset := a.len - b.len + for index := a.len - b.len; index < a.len; index++ { + if a[index] < (b[index - offset] + carry) { + new_carry = 1 + } else { + new_carry = 0 + } + a[index] -= (b[index - offset] + carry) + carry = new_carry + } + assert carry == 0 +} + +// for assert +[inline] +fn left_align_p(a u32, b u32) bool { + return bits.leading_zeros_32(a) == bits.leading_zeros_32(b) +} diff --git a/vlib/math/big/division_array_ops_test.v b/vlib/math/big/division_array_ops_test.v new file mode 100644 index 0000000000..91c158dd9e --- /dev/null +++ b/vlib/math/big/division_array_ops_test.v @@ -0,0 +1,162 @@ +module big + +import rand + +fn test_lshift_in_place() { + mut a := [u32(1), 1, 1, 1, 1] + lshift_in_place(mut a, 1) + assert a == [u32(2), 2, 2, 2, 2] + lshift_in_place(mut a, 7) + assert a == [u32(256), 256, 256, 256, 256] + mut b := [u32(0x80000001), 0xc0000000, 0x80000000, 0x7fffffff] + lshift_in_place(mut b, 1) + assert b == [u32(2), 0x80000001, 1, 0xffffffff] + mut c := [u32(0x00ffffff), 0xf0f0f0f0, 1, 0x3fffffff, 1] + lshift_in_place(mut c, 2) + assert c == [u32(0x3fffffc), 0xc3c3c3c0, 7, 0xfffffffc, 4] +} + +fn test_rshift_in_place() { + mut a := [u32(2), 2, 2, 2, 2] + rshift_in_place(mut a, 1) + assert a == [u32(1), 1, 1, 1, 1] + a = [u32(256), 256, 256, 256, 256] + rshift_in_place(mut a, 7) + assert a == [u32(2), 2, 2, 2, 2] + a = [u32(0), 0, 1] + rshift_in_place(mut a, 1) + assert a == [u32(0), 0x80000000, 0] + mut b := [u32(3), 0x80000001, 1, 0xffffffff] + rshift_in_place(mut b, 1) + assert b == [u32(0x80000001), 0xc0000000, 0x80000000, 0x7fffffff] + mut c := [u32(0x03ffffff), 0xc3c3c3c0, 7, 0xfffffffc, 4] + rshift_in_place(mut c, 2) + assert c == [u32(0x00ffffff), 0xf0f0f0f0, 1, 0x3fffffff, 1] +} + +fn test_subtract_in_place() { + mut a := [u32(2), 2, 2, 2, 2] + mut b := [u32(1), 1, 2, 1, 1] + subtract_in_place(mut a, b) + assert a == [u32(1), 1, 0, 1, 1] + + a = [u32(0), 0, 0, 0, 1] + b = [u32(0), 0, 1] + subtract_in_place(mut a, b) + assert a == [u32(0), 0, 0, 0, 0] + + a = [u32(0), 0, 0, 0, 1, 13] + b = [u32(1), 0, 1] + mut c := []u32{len: a.len} + mut d := [u32(0), 0, 0] + d << b // to have same length + subtract_digit_array(a, d, mut c) + subtract_in_place(mut a, b) + assert a == [u32(0), 0, 0, u32(-1), 0, 12] + assert c == a +} + +fn test_greater_equal_from_end() { + mut a := [u32(1), 2, 3, 4, 5, 6] + mut b := [u32(3), 4, 5, 6] + assert greater_equal_from_end(a, b) == true + + a = [u32(1), 2, 3, 4, 5, 6] + b = [u32(1), 2, 3, 4, 5, 6] + assert greater_equal_from_end(a, b) == true + + a = [u32(1), 2, 3, 4, 5, 6] + b = [u32(2), 2, 3, 4, 5, 6] + assert greater_equal_from_end(a, b) == false + + a = [u32(0), 0, 0, 4, 5, 6] + b = [u32(4), 5, 6] + assert greater_equal_from_end(a, b) == true + + a = [u32(0), 0, 0, 4, 5, 6] + b = [u32(4), 6, 6] + assert greater_equal_from_end(a, b) == false + + a = [u32(0), 0, 0, 4, 5, 5] + b = [u32(4), 5, 6] + assert greater_equal_from_end(a, b) == false +} + +fn test_divide_digit_array_03() { + a := [u32(0), 4] + b := [u32(0), 1] + mut q := []u32{cap: a.len - b.len + 1} + mut r := []u32{cap: a.len} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(4)] + assert r == []u32{len: 0} +} + +fn test_divide_digit_array_04() { + a := [u32(2), 4] + b := [u32(0), 1] + mut q := []u32{cap: a.len - b.len + 1} + mut r := []u32{cap: a.len} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(4)] + assert r == [u32(2)] +} + +fn test_divide_digit_array_05() { + a := [u32(2), 4, 5] + b := [u32(0), 1] + mut q := []u32{cap: a.len - b.len + 1} + mut r := []u32{cap: a.len} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(4), 5] + assert r == [u32(2)] +} + +fn test_divide_digit_array_06() { + a := [u32(2), 4, 5, 3] + b := [u32(0), 0x8000] + mut q := []u32{cap: a.len - b.len + 1} + mut r := []u32{cap: a.len} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(0xa0000), 0x60000] + assert r == [u32(2), 4] +} + +// For debugging +fn integer_from_u32_array(a []u32) Integer { + mut res := integer_from_i64(0) + mut multiplicand := integer_from_u32(1) + for i in 0 .. a.len { + res += integer_from_u32(a[i]) * multiplicand + multiplicand = multiplicand.lshift(32) + } + return res +} + +fn test_many_divisions() { + for _ in 0 .. 100 { + a := random_number(30) + b := random_number(30) + c := a * b + assert c / a == b + assert c / b == a + q, r := a.div_mod(b) + assert (q * b) + r == a + } +} + +fn random_number(length int) Integer { + numbers := '0123456789' + mut stri := '' + for _ in 0 .. length { + i := rand.intn(10) + nr := numbers[i] + stri = stri + nr.ascii_str() + } + res := integer_from_string(stri) or { panic('error in random_number') } + return res +}