mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
bigint: division (#11386)
This commit is contained in:
parent
56ad5d72ef
commit
43fae5de74
@ -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
|
// 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) {
|
fn divide_array_by_digit(operand_a []u32, divisor u32, mut quotient []u32, mut remainder []u32) {
|
||||||
if operand_a.len == 1 {
|
if operand_a.len == 1 {
|
||||||
// 1 digit for both dividend and divisor
|
// 1 digit for both dividend and divisor
|
||||||
@ -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.
|
// Shifts the contents of the original array by the given amount of bits to the left.
|
||||||
// This function assumes that the amount is less than 32. The storage is expected to
|
// This function assumes that the amount is less than 32. The storage is expected to
|
||||||
// allocated with zeroes.
|
// allocated with zeroes.
|
||||||
|
139
vlib/math/big/division_array_ops.v
Normal file
139
vlib/math/big/division_array_ops.v
Normal file
@ -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)
|
||||||
|
}
|
162
vlib/math/big/division_array_ops_test.v
Normal file
162
vlib/math/big/division_array_ops_test.v
Normal file
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user