v / vlib / math / big / array_ops.v
324 lines · 295 sloc · 9.4 KB · 1820a5584b9bc606d0b5d12f6a60dfc62e98fe96
Raw
1module big
2
3import math.bits
4
5// Compares the magnitude of the two unsigned integers represented the given
6// digit arrays. Returns -1 if a < b, 0 if a == b and +1 if a > b. Here
7// a is operand_a and b is operand_b (for brevity).
8@[direct_array_access]
9fn compare_digit_array(operand_a []u64, operand_b []u64) int {
10 a_len := operand_a.len
11 b_len := operand_b.len
12 if a_len != b_len {
13 return if a_len < b_len { -1 } else { 1 }
14 }
15 // They have the same number of digits now
16 // Go from the most significant digit to the least significant one
17 for index := a_len - 1; index >= 0; index-- {
18 a_digit := operand_a[index]
19 b_digit := operand_b[index]
20 if a_digit != b_digit {
21 return if a_digit < b_digit { -1 } else { 1 }
22 }
23 }
24 return 0
25}
26
27// Add the digits in operand_a and operand_b and stores the result in sum.
28// This function does not perform any allocation and assumes that the storage is
29// large enough. It may affect the last element, based on the presence of a carry
30@[direct_array_access]
31fn add_digit_array(operand_a []u64, operand_b []u64, mut sum []u64) {
32 // Zero length cases
33 if operand_a.len == 0 {
34 for index in 0 .. operand_b.len {
35 sum[index] = operand_b[index]
36 }
37 shrink_tail_zeros(mut sum)
38 return
39 }
40 if operand_b.len == 0 {
41 for index in 0 .. operand_a.len {
42 sum[index] = operand_a[index]
43 }
44 shrink_tail_zeros(mut sum)
45 return
46 }
47
48 mut a, mut b := if operand_a.len >= operand_b.len {
49 operand_a, operand_b
50 } else {
51 operand_b, operand_a
52 }
53 mut carry := u64(0)
54 for index in 0 .. b.len {
55 partial := carry + a[index] + b[index]
56 sum[index] = partial & max_digit
57 carry = partial >> digit_bits
58 }
59
60 for index in b.len .. a.len {
61 partial := carry + a[index]
62 sum[index] = partial & max_digit
63 carry = partial >> digit_bits
64 }
65
66 sum[a.len] = carry
67 shrink_tail_zeros(mut sum)
68}
69
70// Subtracts operand_b from operand_a and stores the difference in storage.
71// It assumes operand_a contains the larger "integer" and that storage is
72// the same size as operand_a and is 0
73@[direct_array_access]
74fn subtract_digit_array(operand_a []u64, operand_b []u64, mut storage []u64) {
75 // Zero length cases
76 if operand_a.len == 0 {
77 // nothing to subtract from
78 return
79 }
80 if operand_b.len == 0 {
81 // nothing to subtract
82 for index in 0 .. operand_a.len {
83 storage[index] = operand_a[index]
84 }
85 return
86 }
87
88 mut borrow := u64(0)
89 for index in 0 .. operand_b.len {
90 a := operand_a[index]
91 b := operand_b[index] + borrow
92 diff := a - b
93 borrow = (diff >> digit_bits) & 1
94 storage[index] = diff + (borrow << digit_bits)
95 }
96 for index in operand_b.len .. operand_a.len {
97 diff := operand_a[index] - borrow
98 borrow = (diff >> digit_bits) & 1
99 storage[index] = diff + (borrow << digit_bits)
100 }
101
102 shrink_tail_zeros(mut storage)
103}
104
105const karatsuba_multiplication_limit = 70
106
107const toom3_multiplication_limit = 360
108
109@[inline]
110fn multiply_digit_array(operand_a []u64, operand_b []u64, mut storage []u64) {
111 max_len := if operand_a.len >= operand_b.len {
112 operand_a.len
113 } else {
114 operand_b.len
115 }
116 if max_len >= toom3_multiplication_limit {
117 toom3_multiply_digit_array(operand_a, operand_b, mut storage)
118 } else if max_len >= karatsuba_multiplication_limit {
119 karatsuba_multiply_digit_array(operand_a, operand_b, mut storage)
120 } else {
121 simple_multiply_digit_array(operand_a, operand_b, mut storage)
122 }
123}
124
125// Multiplies the unsigned (non-negative) integers represented in a and b and the product is
126// stored in storage. It assumes that storage has length equal to the sum of lengths
127// of a and b. Length refers to length of array, that is, digit count.
128@[direct_array_access]
129fn simple_multiply_digit_array(operand_a []u64, operand_b []u64, mut storage []u64) {
130 for b_index in 0 .. operand_b.len {
131 mut hi := u64(0)
132 mut lo := u64(0)
133 for a_index in 0 .. operand_a.len {
134 hi, lo = bits.mul_add_64(operand_a[a_index], operand_b[b_index], storage[a_index +
135 b_index] + hi)
136 storage[a_index + b_index] = lo & max_digit
137 hi = (hi << (64 - digit_bits)) | (lo >> digit_bits)
138 }
139 if hi != 0 {
140 storage[b_index + operand_a.len] = hi
141 }
142 }
143 shrink_tail_zeros(mut storage)
144}
145
146// Stores the product of the unsigned (non-negative) integer represented in a and the digit in value
147// in the storage array. It assumes storage is pre-initialised and populated with 0's
148@[direct_array_access]
149fn multiply_array_by_digit(operand_a []u64, value u64, mut storage []u64) {
150 if value == 0 {
151 storage.clear()
152 return
153 }
154 if value == 1 {
155 for index in 0 .. operand_a.len {
156 storage[index] = operand_a[index]
157 }
158 shrink_tail_zeros(mut storage)
159 return
160 }
161 mut hi := u64(0)
162 mut lo := u64(0)
163 for index in 0 .. operand_a.len {
164 hi, lo = bits.mul_add_64(operand_a[index], value, hi)
165 storage[index] = lo & max_digit
166 hi = hi << (64 - digit_bits) + (lo >> digit_bits)
167 }
168
169 if hi > 0 {
170 storage[operand_a.len] = hi
171 }
172 shrink_tail_zeros(mut storage)
173}
174
175// Divides the non-negative integer in a by non-negative integer b and store the two results
176// in quotient and remainder respectively. It is different from the rest of the functions
177// because it assumes that quotient and remainder are empty zero length arrays. They can be
178// made to have appropriate capacity though
179@[direct_array_access]
180fn divide_digit_array(operand_a []u64, operand_b []u64, mut quotient []u64, mut remainder []u64) {
181 cmp_result := compare_digit_array(operand_a, operand_b)
182 // a == b => q, r = 1, 0
183 if cmp_result == 0 {
184 quotient[0] = 1
185 remainder.clear()
186 return
187 }
188
189 // a < b => q, r = 0, a
190 if cmp_result < 0 {
191 quotient.clear()
192 for i in 0 .. operand_a.len {
193 remainder[i] = operand_a[i]
194 }
195 return
196 }
197 if operand_b.len == 1 {
198 divide_array_by_digit(operand_a, operand_b[0], mut quotient, mut remainder)
199 } else {
200 divide_array_by_array(operand_a, operand_b, mut quotient, mut remainder)
201 }
202}
203
204// Performs division on the non-negative dividend in a by the single digit divisor b. It assumes
205// quotient and remainder are empty zero length arrays without previous allocation
206@[direct_array_access]
207fn divide_array_by_digit(operand_a []u64, divisor u64, mut quotient []u64, mut remainder []u64) {
208 if operand_a.len == 1 {
209 // 1 digit for both dividend and divisor
210 dividend := operand_a[0]
211 q := dividend / divisor
212 quotient[0] = q
213
214 rem := dividend % divisor
215 remainder[0] = rem
216
217 shrink_tail_zeros(mut quotient)
218 shrink_tail_zeros(mut remainder)
219 return
220 }
221 // Dividend has more digits
222 mut rem := u64(0)
223 mut quo := u64(0)
224
225 // Perform division step by step
226 for index := operand_a.len - 1; index >= 0; index-- {
227 hi := rem >> (64 - digit_bits)
228 lo := rem << digit_bits | operand_a[index]
229 quo, rem = bits.div_64(hi, lo, divisor)
230 quotient[index] = quo & max_digit
231 }
232
233 // Remove leading zeros from quotient
234 shrink_tail_zeros(mut quotient)
235 remainder[0] = rem
236 shrink_tail_zeros(mut remainder)
237}
238
239@[inline]
240fn divide_array_by_array(operand_a []u64, operand_b []u64, mut quotient []u64, mut remainder []u64) {
241 knuth_divide_array_by_array(operand_a, operand_b, mut quotient, mut remainder)
242}
243
244// Shifts the contents of the original array by the given amount of bits to the left.
245// This function assumes that the amount is less than `digit_bits`. The storage is expected to
246// allocated with zeroes.
247@[direct_array_access]
248fn shift_digits_left(original []u64, amount u32, mut storage []u64) {
249 mut leftover := u64(0)
250 offset := digit_bits - amount
251 for index in 0 .. original.len {
252 value := (leftover | (original[index] << amount)) & max_digit
253 leftover = (original[index] & (u64(-1) << offset)) >> offset
254 storage[index] = value
255 }
256 if leftover != 0 {
257 storage << leftover
258 }
259}
260
261// Shifts the contents of the original array by the given amount of bits to the right.
262// This function assumes that the amount is less than `digit_bits`. The storage is expected to
263// be allocated with zeroes.
264@[direct_array_access]
265fn shift_digits_right(original []u64, amount u32, mut storage []u64) {
266 mut moveover := u64(0)
267 mask := (u64(1) << amount) - 1
268 offset := digit_bits - amount
269 for index := original.len - 1; index >= 0; index-- {
270 value := (moveover << offset) | (original[index] >> amount)
271 moveover = original[index] & mask
272 storage[index] = value
273 }
274 shrink_tail_zeros(mut storage)
275}
276
277@[direct_array_access]
278fn bitwise_or_digit_array(operand_a []u64, operand_b []u64, mut storage []u64) {
279 lower, upper, bigger := if operand_a.len < operand_b.len {
280 operand_a.len, operand_b.len, operand_b
281 } else {
282 operand_b.len, operand_a.len, operand_a
283 }
284 for index in 0 .. lower {
285 storage[index] = operand_a[index] | operand_b[index]
286 }
287 for index in lower .. upper {
288 storage[index] = bigger[index]
289 }
290 shrink_tail_zeros(mut storage)
291}
292
293@[direct_array_access]
294fn bitwise_and_digit_array(operand_a []u64, operand_b []u64, mut storage []u64) {
295 lower := imin(operand_a.len, operand_b.len)
296 for index in 0 .. lower {
297 storage[index] = operand_a[index] & operand_b[index]
298 }
299 shrink_tail_zeros(mut storage)
300}
301
302@[direct_array_access]
303fn bitwise_xor_digit_array(operand_a []u64, operand_b []u64, mut storage []u64) {
304 lower, upper, bigger := if operand_a.len < operand_b.len {
305 operand_a.len, operand_b.len, operand_b
306 } else {
307 operand_b.len, operand_a.len, operand_a
308 }
309 for index in 0 .. lower {
310 storage[index] = operand_a[index] ^ operand_b[index]
311 }
312 for index in lower .. upper {
313 storage[index] = bigger[index]
314 }
315 shrink_tail_zeros(mut storage)
316}
317
318@[direct_array_access]
319fn bitwise_not_digit_array(original []u64, mut storage []u64) {
320 for index in 0 .. original.len {
321 storage[index] = (~original[index]) & max_digit
322 }
323 shrink_tail_zeros(mut storage)
324}
325