Files
Odin/core/math/big/internal.odin
T
2021-08-11 20:59:53 +02:00

1628 lines
40 KiB
Odin

//+ignore
package big
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
A BigInt implementation in Odin.
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
========================== Low-level routines ==========================
IMPORTANT: `internal_*` procedures make certain assumptions about their input.
The public functions that call them are expected to satisfy their sanity check requirements.
This allows `internal_*` call `internal_*` without paying this overhead multiple times.
Where errors can occur, they are of course still checked and returned as appropriate.
When importing `math:core/big` to implement an involved algorithm of your own, you are welcome
to use these procedures instead of their public counterparts.
Most inputs and outputs are expected to be passed an initialized `Int`, for example.
Exceptions include `quotient` and `remainder`, which are allowed to be `nil` when the calling code doesn't need them.
Check the comments above each `internal_*` implementation to see what constraints it expects to have met.
TODO: Handle +/- Infinity and NaN.
*/
import "core:mem"
import "core:intrinsics"
/*
Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7.
Assumptions:
`dest`, `a` and `b` != `nil` and have been initalized.
*/
internal_int_add_unsigned :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
dest := dest; x := a; y := b;
old_used, min_used, max_used, i: int;
if x.used < y.used {
x, y = y, x;
assert(x.used >= y.used);
}
min_used = y.used;
max_used = x.used;
old_used = dest.used;
if err = grow(dest, max(max_used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; }
dest.used = max_used + 1;
/*
All parameters have been initialized.
*/
/* Zero the carry */
carry := DIGIT(0);
#no_bounds_check for i = 0; i < min_used; i += 1 {
/*
Compute the sum one _DIGIT at a time.
dest[i] = a[i] + b[i] + carry;
*/
dest.digit[i] = x.digit[i] + y.digit[i] + carry;
/*
Compute carry
*/
carry = dest.digit[i] >> _DIGIT_BITS;
/*
Mask away carry from result digit.
*/
dest.digit[i] &= _MASK;
}
if min_used != max_used {
/*
Now copy higher words, if any, in A+B.
If A or B has more digits, add those in.
*/
#no_bounds_check for ; i < max_used; i += 1 {
dest.digit[i] = x.digit[i] + carry;
/*
Compute carry
*/
carry = dest.digit[i] >> _DIGIT_BITS;
/*
Mask away carry from result digit.
*/
dest.digit[i] &= _MASK;
}
}
/*
Add remaining carry.
*/
dest.digit[i] = carry;
/*
Zero remainder.
*/
internal_zero_unused(dest, old_used);
/*
Adjust dest.used based on leading zeroes.
*/
return clamp(dest);
}
internal_add_unsigned :: proc { internal_int_add_unsigned, };
/*
Low-level addition, signed. Handbook of Applied Cryptography, algorithm 14.7.
Assumptions:
`dest`, `a` and `b` != `nil` and have been initalized.
*/
internal_int_add_signed :: proc(dest, a, b: ^Int, allocator := context.allocator) -> (err: Error) {
x := a; y := b;
/*
Handle both negative or both positive.
*/
if x.sign == y.sign {
dest.sign = x.sign;
return #force_inline internal_int_add_unsigned(dest, x, y, allocator);
}
/*
One positive, the other negative.
Subtract the one with the greater magnitude from the other.
The result gets the sign of the one with the greater magnitude.
*/
if c, _ := #force_inline cmp_mag(a, b); c == -1 {
x, y = y, x;
}
dest.sign = x.sign;
return #force_inline internal_int_sub_unsigned(dest, x, y, allocator);
}
internal_add_signed :: proc { internal_int_add_signed, };
/*
Low-level addition Int+DIGIT, signed. Handbook of Applied Cryptography, algorithm 14.7.
Assumptions:
`dest` and `a` != `nil` and have been initalized.
`dest` is large enough (a.used + 1) to fit result.
*/
internal_int_add_digit :: proc(dest, a: ^Int, digit: DIGIT) -> (err: Error) {
/*
Fast paths for destination and input Int being the same.
*/
if dest == a {
/*
Fast path for dest.digit[0] + digit fits in dest.digit[0] without overflow.
*/
if dest.sign == .Zero_or_Positive && (dest.digit[0] + digit < _DIGIT_MAX) {
dest.digit[0] += digit;
dest.used += 1;
return clamp(dest);
}
/*
Can be subtracted from dest.digit[0] without underflow.
*/
if a.sign == .Negative && (dest.digit[0] > digit) {
dest.digit[0] -= digit;
dest.used += 1;
return clamp(dest);
}
}
/*
If `a` is negative and `|a|` >= `digit`, call `dest = |a| - digit`
*/
if a.sign == .Negative && (a.used > 1 || a.digit[0] >= digit) {
/*
Temporarily fix `a`'s sign.
*/
a.sign = .Zero_or_Positive;
/*
dest = |a| - digit
*/
if err = #force_inline internal_int_add_digit(dest, a, digit); err != nil {
/*
Restore a's sign.
*/
a.sign = .Negative;
return err;
}
/*
Restore sign and set `dest` sign.
*/
a.sign = .Negative;
dest.sign = .Negative;
return clamp(dest);
}
/*
Remember the currently used number of digits in `dest`.
*/
old_used := dest.used;
/*
If `a` is positive
*/
if a.sign == .Zero_or_Positive {
/*
Add digits, use `carry`.
*/
i: int;
carry := digit;
#no_bounds_check for i = 0; i < a.used; i += 1 {
dest.digit[i] = a.digit[i] + carry;
carry = dest.digit[i] >> _DIGIT_BITS;
dest.digit[i] &= _MASK;
}
/*
Set final carry.
*/
dest.digit[i] = carry;
/*
Set `dest` size.
*/
dest.used = a.used + 1;
} else {
/*
`a` was negative and |a| < digit.
*/
dest.used = 1;
/*
The result is a single DIGIT.
*/
dest.digit[0] = digit - a.digit[0] if a.used == 1 else digit;
}
/*
Sign is always positive.
*/
dest.sign = .Zero_or_Positive;
/*
Zero remainder.
*/
internal_zero_unused(dest, old_used);
/*
Adjust dest.used based on leading zeroes.
*/
return clamp(dest);
}
internal_add :: proc { internal_int_add_signed, internal_int_add_digit, };
/*
Low-level subtraction, dest = number - decrease. Assumes |number| > |decrease|.
Handbook of Applied Cryptography, algorithm 14.9.
Assumptions:
`dest`, `number` and `decrease` != `nil` and have been initalized.
*/
internal_int_sub_unsigned :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) {
dest := dest; x := number; y := decrease;
old_used := dest.used;
min_used := y.used;
max_used := x.used;
i: int;
if err = grow(dest, max(max_used, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; }
dest.used = max_used;
/*
All parameters have been initialized.
*/
borrow := DIGIT(0);
#no_bounds_check for i = 0; i < min_used; i += 1 {
dest.digit[i] = (x.digit[i] - y.digit[i] - borrow);
/*
borrow = carry bit of dest[i]
Note this saves performing an AND operation since if a carry does occur,
it will propagate all the way to the MSB.
As a result a single shift is enough to get the carry.
*/
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
/*
Clear borrow from dest[i].
*/
dest.digit[i] &= _MASK;
}
/*
Now copy higher words if any, e.g. if A has more digits than B
*/
#no_bounds_check for ; i < max_used; i += 1 {
dest.digit[i] = x.digit[i] - borrow;
/*
borrow = carry bit of dest[i]
Note this saves performing an AND operation since if a carry does occur,
it will propagate all the way to the MSB.
As a result a single shift is enough to get the carry.
*/
borrow = dest.digit[i] >> ((size_of(DIGIT) * 8) - 1);
/*
Clear borrow from dest[i].
*/
dest.digit[i] &= _MASK;
}
/*
Zero remainder.
*/
internal_zero_unused(dest, old_used);
/*
Adjust dest.used based on leading zeroes.
*/
return clamp(dest);
}
internal_sub_unsigned :: proc { internal_int_sub_unsigned, };
/*
Low-level subtraction, signed. Handbook of Applied Cryptography, algorithm 14.9.
dest = number - decrease. Assumes |number| > |decrease|.
Assumptions:
`dest`, `number` and `decrease` != `nil` and have been initalized.
*/
internal_int_sub_signed :: proc(dest, number, decrease: ^Int, allocator := context.allocator) -> (err: Error) {
number := number; decrease := decrease;
if number.sign != decrease.sign {
/*
Subtract a negative from a positive, OR subtract a positive from a negative.
In either case, ADD their magnitudes and use the sign of the first number.
*/
dest.sign = number.sign;
return #force_inline internal_int_add_unsigned(dest, number, decrease, allocator);
}
/*
Subtract a positive from a positive, OR negative from a negative.
First, take the difference between their magnitudes, then...
*/
if c, _ := #force_inline cmp_mag(number, decrease); c == -1 {
/*
The second has a larger magnitude.
The result has the *opposite* sign from the first number.
*/
dest.sign = .Negative if number.sign == .Zero_or_Positive else .Zero_or_Positive;
number, decrease = decrease, number;
} else {
/*
The first has a larger or equal magnitude.
Copy the sign from the first.
*/
dest.sign = number.sign;
}
return #force_inline internal_int_sub_unsigned(dest, number, decrease, allocator);
}
/*
Low-level subtraction, signed. Handbook of Applied Cryptography, algorithm 14.9.
dest = number - decrease. Assumes |number| > |decrease|.
Assumptions:
`dest`, `number` != `nil` and have been initalized.
`dest` is large enough (number.used + 1) to fit result.
*/
internal_int_sub_digit :: proc(dest, number: ^Int, digit: DIGIT) -> (err: Error) {
dest := dest; digit := digit;
/*
All parameters have been initialized.
Fast paths for destination and input Int being the same.
*/
if dest == number {
/*
Fast path for `dest` is negative and unsigned addition doesn't overflow the lowest digit.
*/
if dest.sign == .Negative && (dest.digit[0] + digit < _DIGIT_MAX) {
dest.digit[0] += digit;
return nil;
}
/*
Can be subtracted from dest.digit[0] without underflow.
*/
if number.sign == .Zero_or_Positive && (dest.digit[0] > digit) {
dest.digit[0] -= digit;
return nil;
}
}
/*
If `a` is negative, just do an unsigned addition (with fudged signs).
*/
if number.sign == .Negative {
t := number;
t.sign = .Zero_or_Positive;
err = #force_inline internal_int_add_digit(dest, t, digit);
dest.sign = .Negative;
clamp(dest);
return err;
}
old_used := dest.used;
/*
if `a`<= digit, simply fix the single digit.
*/
if number.used == 1 && (number.digit[0] <= digit) || number.used == 0 {
dest.digit[0] = digit - number.digit[0] if number.used == 1 else digit;
dest.sign = .Negative;
dest.used = 1;
} else {
dest.sign = .Zero_or_Positive;
dest.used = number.used;
/*
Subtract with carry.
*/
carry := digit;
#no_bounds_check for i := 0; i < number.used; i += 1 {
dest.digit[i] = number.digit[i] - carry;
carry = dest.digit[i] >> (_DIGIT_TYPE_BITS - 1);
dest.digit[i] &= _MASK;
}
}
/*
Zero remainder.
*/
internal_zero_unused(dest, old_used);
/*
Adjust dest.used based on leading zeroes.
*/
return clamp(dest);
}
internal_sub :: proc { internal_int_sub_signed, internal_int_sub_digit, };
/*
dest = src / 2
dest = src >> 1
*/
internal_int_shr1 :: proc(dest, src: ^Int) -> (err: Error) {
old_used := dest.used; dest.used = src.used;
/*
Carry
*/
fwd_carry := DIGIT(0);
#no_bounds_check for x := dest.used - 1; x >= 0; x -= 1 {
/*
Get the carry for the next iteration.
*/
src_digit := src.digit[x];
carry := src_digit & 1;
/*
Shift the current digit, add in carry and store.
*/
dest.digit[x] = (src_digit >> 1) | (fwd_carry << (_DIGIT_BITS - 1));
/*
Forward carry to next iteration.
*/
fwd_carry = carry;
}
/*
Zero remainder.
*/
internal_zero_unused(dest, old_used);
/*
Adjust dest.used based on leading zeroes.
*/
dest.sign = src.sign;
return clamp(dest);
}
/*
dest = src * 2
dest = src << 1
*/
internal_int_shl1 :: proc(dest, src: ^Int) -> (err: Error) {
if err = copy(dest, src); err != nil { return err; }
/*
Grow `dest` to accommodate the additional bits.
*/
digits_needed := dest.used + 1;
if err = grow(dest, digits_needed); err != nil { return err; }
dest.used = digits_needed;
mask := (DIGIT(1) << uint(1)) - DIGIT(1);
shift := DIGIT(_DIGIT_BITS - 1);
carry := DIGIT(0);
#no_bounds_check for x:= 0; x < dest.used; x+= 1 {
fwd_carry := (dest.digit[x] >> shift) & mask;
dest.digit[x] = (dest.digit[x] << uint(1) | carry) & _MASK;
carry = fwd_carry;
}
/*
Use final carry.
*/
if carry != 0 {
dest.digit[dest.used] = carry;
dest.used += 1;
}
return clamp(dest);
}
/*
Multiply by a DIGIT.
*/
internal_int_mul_digit :: proc(dest, src: ^Int, multiplier: DIGIT, allocator := context.allocator) -> (err: Error) {
assert(dest != nil && src != nil);
if multiplier == 0 {
return zero(dest);
}
if multiplier == 1 {
return copy(dest, src);
}
/*
Power of two?
*/
if multiplier == 2 {
return #force_inline internal_int_shl1(dest, src);
}
if is_power_of_two(int(multiplier)) {
ix: int;
if ix, err = log(multiplier, 2); err != nil { return err; }
return shl(dest, src, ix);
}
/*
Ensure `dest` is big enough to hold `src` * `multiplier`.
*/
if err = grow(dest, max(src.used + 1, _DEFAULT_DIGIT_COUNT), false, allocator); err != nil { return err; }
/*
Save the original used count.
*/
old_used := dest.used;
/*
Set the sign.
*/
dest.sign = src.sign;
/*
Set up carry.
*/
carry := _WORD(0);
/*
Compute columns.
*/
ix := 0;
for ; ix < src.used; ix += 1 {
/*
Compute product and carry sum for this term
*/
product := carry + _WORD(src.digit[ix]) * _WORD(multiplier);
/*
Mask off higher bits to get a single DIGIT.
*/
dest.digit[ix] = DIGIT(product & _WORD(_MASK));
/*
Send carry into next iteration
*/
carry = product >> _DIGIT_BITS;
}
/*
Store final carry [if any] and increment used.
*/
dest.digit[ix] = DIGIT(carry);
dest.used = src.used + 1;
/*
Zero remainder.
*/
internal_zero_unused(dest, old_used);
return clamp(dest);
}
/*
High level multiplication (handles sign).
*/
internal_int_mul :: proc(dest, src, multiplier: ^Int, allocator := context.allocator) -> (err: Error) {
/*
Early out for `multiplier` is zero; Set `dest` to zero.
*/
if multiplier.used == 0 || src.used == 0 { return zero(dest); }
if src == multiplier {
/*
Do we need to square?
*/
if false && src.used >= SQR_TOOM_CUTOFF {
/* Use Toom-Cook? */
// err = s_mp_sqr_toom(a, c);
} else if false && src.used >= SQR_KARATSUBA_CUTOFF {
/* Karatsuba? */
// err = s_mp_sqr_karatsuba(a, c);
} else if false && ((src.used * 2) + 1) < _WARRAY &&
src.used < (_MAX_COMBA / 2) {
/* Fast comba? */
// err = s_mp_sqr_comba(a, c);
} else {
err = #force_inline _private_int_sqr(dest, src);
}
} else {
/*
Can we use the balance method? Check sizes.
* The smaller one needs to be larger than the Karatsuba cut-off.
* The bigger one needs to be at least about one `_MUL_KARATSUBA_CUTOFF` bigger
* to make some sense, but it depends on architecture, OS, position of the
* stars... so YMMV.
* Using it to cut the input into slices small enough for _mul_comba
* was actually slower on the author's machine, but YMMV.
*/
min_used := min(src.used, multiplier.used);
max_used := max(src.used, multiplier.used);
digits := src.used + multiplier.used + 1;
if false && min_used >= MUL_KARATSUBA_CUTOFF &&
max_used / 2 >= MUL_KARATSUBA_CUTOFF &&
/*
Not much effect was observed below a ratio of 1:2, but again: YMMV.
*/
max_used >= 2 * min_used {
// err = s_mp_mul_balance(a,b,c);
} else if false && min_used >= MUL_TOOM_CUTOFF {
// err = s_mp_mul_toom(a, b, c);
} else if false && min_used >= MUL_KARATSUBA_CUTOFF {
// err = s_mp_mul_karatsuba(a, b, c);
} else if digits < _WARRAY && min_used <= _MAX_COMBA {
/*
Can we use the fast multiplier?
* The fast multiplier can be used if the output will
* have less than MP_WARRAY digits and the number of
* digits won't affect carry propagation
*/
err = #force_inline _private_int_mul_comba(dest, src, multiplier, digits);
} else {
err = #force_inline _private_int_mul(dest, src, multiplier, digits);
}
}
neg := src.sign != multiplier.sign;
dest.sign = .Negative if dest.used > 0 && neg else .Zero_or_Positive;
return err;
}
internal_mul :: proc { internal_int_mul, internal_int_mul_digit, };
internal_sqr :: proc (dest, src: ^Int) -> (res: Error) {
/*
We call `internal_mul` and not e.g. `_private_int_sqr` because the former
will dispatch to the optimal implementation depending on the source.
*/
return #force_inline internal_mul(dest, src, src);
}
/*
divmod.
Both the quotient and remainder are optional and may be passed a nil.
*/
internal_int_divmod :: proc(quotient, remainder, numerator, denominator: ^Int, allocator := context.allocator) -> (err: Error) {
if denominator.used == 0 { return .Division_by_Zero; }
/*
If numerator < denominator then quotient = 0, remainder = numerator.
*/
c: int;
if c, err = #force_inline cmp_mag(numerator, denominator); c == -1 {
if remainder != nil {
if err = copy(remainder, numerator, false, allocator); err != nil { return err; }
}
if quotient != nil {
zero(quotient);
}
return nil;
}
if false && (denominator.used > 2 * MUL_KARATSUBA_CUTOFF) && (denominator.used <= (numerator.used/3) * 2) {
// err = _int_div_recursive(quotient, remainder, numerator, denominator);
} else {
when true {
err = #force_inline _private_int_div_school(quotient, remainder, numerator, denominator);
} else {
/*
NOTE(Jeroen): We no longer need or use `_private_int_div_small`.
We'll keep it around for a bit until we're reasonably certain div_school is bug free.
*/
err = _private_int_div_small(quotient, remainder, numerator, denominator);
}
}
return;
}
/*
Single digit division (based on routine from MPI).
The quotient is optional and may be passed a nil.
*/
internal_int_divmod_digit :: proc(quotient, numerator: ^Int, denominator: DIGIT) -> (remainder: DIGIT, err: Error) {
/*
Cannot divide by zero.
*/
if denominator == 0 { return 0, .Division_by_Zero; }
/*
Quick outs.
*/
if denominator == 1 || numerator.used == 0 {
if quotient != nil {
return 0, copy(quotient, numerator);
}
return 0, err;
}
/*
Power of two?
*/
if denominator == 2 {
if numerator.used > 0 && numerator.digit[0] & 1 != 0 {
// Remainder is 1 if numerator is odd.
remainder = 1;
}
if quotient == nil {
return remainder, nil;
}
return remainder, shr(quotient, numerator, 1);
}
ix: int;
if is_power_of_two(int(denominator)) {
ix = 1;
for ix < _DIGIT_BITS && denominator != (1 << uint(ix)) {
ix += 1;
}
remainder = numerator.digit[0] & ((1 << uint(ix)) - 1);
if quotient == nil {
return remainder, nil;
}
return remainder, shr(quotient, numerator, int(ix));
}
/*
Three?
*/
if denominator == 3 {
return _private_int_div_3(quotient, numerator);
}
/*
No easy answer [c'est la vie]. Just division.
*/
q := &Int{};
if err = grow(q, numerator.used); err != nil { return 0, err; }
q.used = numerator.used;
q.sign = numerator.sign;
w := _WORD(0);
for ix = numerator.used - 1; ix >= 0; ix -= 1 {
t := DIGIT(0);
w = (w << _WORD(_DIGIT_BITS) | _WORD(numerator.digit[ix]));
if w >= _WORD(denominator) {
t = DIGIT(w / _WORD(denominator));
w -= _WORD(t) * _WORD(denominator);
}
q.digit[ix] = t;
}
remainder = DIGIT(w);
if quotient != nil {
clamp(q);
swap(q, quotient);
}
destroy(q);
return remainder, nil;
}
internal_divmod :: proc { internal_int_divmod, internal_int_divmod_digit, };
/*
Asssumes quotient, numerator and denominator to have been initialized and not to be nil.
*/
internal_int_div :: proc(quotient, numerator, denominator: ^Int) -> (err: Error) {
return #force_inline internal_int_divmod(quotient, nil, numerator, denominator);
}
internal_div :: proc { internal_int_div, };
/*
remainder = numerator % denominator.
0 <= remainder < denominator if denominator > 0
denominator < remainder <= 0 if denominator < 0
Asssumes quotient, numerator and denominator to have been initialized and not to be nil.
*/
internal_int_mod :: proc(remainder, numerator, denominator: ^Int) -> (err: Error) {
if err = #force_inline internal_int_divmod(nil, remainder, numerator, denominator); err != nil { return err; }
if remainder.used == 0 || denominator.sign == remainder.sign { return nil; }
return #force_inline internal_add(remainder, remainder, numerator);
}
internal_mod :: proc{ internal_int_mod, };
/*
remainder = (number + addend) % modulus.
*/
internal_int_addmod :: proc(remainder, number, addend, modulus: ^Int) -> (err: Error) {
if err = #force_inline internal_add(remainder, number, addend); err != nil { return err; }
return #force_inline internal_mod(remainder, remainder, modulus);
}
internal_addmod :: proc { internal_int_addmod, };
/*
remainder = (number - decrease) % modulus.
*/
internal_int_submod :: proc(remainder, number, decrease, modulus: ^Int) -> (err: Error) {
if err = #force_inline internal_sub(remainder, number, decrease); err != nil { return err; }
return #force_inline internal_mod(remainder, remainder, modulus);
}
internal_submod :: proc { internal_int_submod, };
/*
remainder = (number * multiplicand) % modulus.
*/
internal_int_mulmod :: proc(remainder, number, multiplicand, modulus: ^Int) -> (err: Error) {
if err = #force_inline internal_mul(remainder, number, multiplicand); err != nil { return err; }
return #force_inline internal_mod(remainder, remainder, modulus);
}
internal_mulmod :: proc { internal_int_mulmod, };
/*
remainder = (number * number) % modulus.
*/
internal_int_sqrmod :: proc(remainder, number, modulus: ^Int) -> (err: Error) {
if err = #force_inline internal_sqr(remainder, number); err != nil { return err; }
return #force_inline internal_mod(remainder, remainder, modulus);
}
internal_sqrmod :: proc { internal_int_sqrmod, };
/*
TODO: Use Sterling's Approximation to estimate log2(N!) to size the result.
This way we'll have to reallocate less, possibly not at all.
*/
internal_int_factorial :: proc(res: ^Int, n: int) -> (err: Error) {
if n >= FACTORIAL_BINARY_SPLIT_CUTOFF {
return #force_inline _private_int_factorial_binary_split(res, n);
}
i := len(_factorial_table);
if n < i {
return #force_inline set(res, _factorial_table[n]);
}
if err = #force_inline set(res, _factorial_table[i - 1]); err != nil { return err; }
for {
if err = #force_inline internal_mul(res, res, DIGIT(i)); err != nil || i == n { return err; }
i += 1;
}
return nil;
}
/*
Returns GCD, LCM or both.
Assumes `a` and `b` to have been initialized.
`res_gcd` and `res_lcm` can be nil or ^Int depending on which results are desired.
*/
internal_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int) -> (err: Error) {
if res_gcd == nil && res_lcm == nil { return nil; }
return #force_inline _private_int_gcd_lcm(res_gcd, res_lcm, a, b);
}
/*
remainder = numerator % (1 << bits)
Assumes `remainder` and `numerator` both not to be `nil` and `bits` to be >= 0.
*/
internal_int_mod_bits :: proc(remainder, numerator: ^Int, bits: int) -> (err: Error) {
/*
Everything is divisible by 1 << 0 == 1, so this returns 0.
*/
if bits == 0 { return zero(remainder); }
/*
If the modulus is larger than the value, return the value.
*/
err = copy(remainder, numerator);
if bits >= (numerator.used * _DIGIT_BITS) || err != nil {
return;
}
/*
Zero digits above the last digit of the modulus.
*/
zero_count := (bits / _DIGIT_BITS);
zero_count += 0 if (bits % _DIGIT_BITS == 0) else 1;
/*
Zero remainder. Special case, can't use `zero_unused`.
*/
if zero_count > 0 {
mem.zero_slice(remainder.digit[zero_count:]);
}
/*
Clear the digit that is not completely outside/inside the modulus.
*/
remainder.digit[bits / _DIGIT_BITS] &= DIGIT(1 << DIGIT(bits % _DIGIT_BITS)) - DIGIT(1);
return clamp(remainder);
}
/*
============================= Low-level helpers =============================
`internal_*` helpers don't return an `Error` like their public counterparts do,
because they expect not to be passed `nil` or uninitialized inputs.
This makes them more suitable for `internal_*` functions and some of the
public ones that have already satisfied these constraints.
*/
/*
This procedure will return `true` if the `Int` is initialized, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_initialized :: #force_inline proc(a: ^Int) -> (initialized: bool) {
raw := transmute(mem.Raw_Dynamic_Array)a.digit;
return raw.cap >= _MIN_DIGIT_COUNT;
}
internal_is_initialized :: proc { internal_int_is_initialized, };
/*
This procedure will return `true` if the `Int` is zero, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_zero :: #force_inline proc(a: ^Int) -> (zero: bool) {
return a.used == 0;
}
internal_is_zero :: proc { internal_int_is_zero, };
/*
This procedure will return `true` if the `Int` is positive, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_positive :: #force_inline proc(a: ^Int) -> (positive: bool) {
return a.sign == .Zero_or_Positive;
}
internal_is_positive :: proc { internal_int_is_positive, };
/*
This procedure will return `true` if the `Int` is negative, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_negative :: #force_inline proc(a: ^Int) -> (negative: bool) {
return a.sign == .Negative;
}
internal_is_negative :: proc { internal_int_is_negative, };
/*
This procedure will return `true` if the `Int` is even, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_even :: #force_inline proc(a: ^Int) -> (even: bool) {
if internal_is_zero(a) { return true; }
/*
`a.used` > 0 here, because the above handled `is_zero`.
We don't need to explicitly test it.
*/
return a.digit[0] & 1 == 0;
}
internal_is_even :: proc { internal_int_is_even, };
/*
This procedure will return `true` if the `Int` is even, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_odd :: #force_inline proc(a: ^Int) -> (odd: bool) {
return !internal_int_is_even(a);
}
internal_is_odd :: proc { internal_int_is_odd, };
/*
This procedure will return `true` if the `Int` is a power of two, `false` if not.
Assumes `a` not to be `nil`.
*/
internal_int_is_power_of_two :: #force_inline proc(a: ^Int) -> (power_of_two: bool) {
/*
Early out for Int == 0.
*/
if #force_inline internal_is_zero(a) { return true; }
/*
For an `Int` to be a power of two, its bottom limb has to be a power of two.
*/
if ! #force_inline platform_int_is_power_of_two(int(a.digit[a.used - 1])) { return false; }
/*
We've established that the bottom limb is a power of two.
If it's the only limb, that makes the entire Int a power of two.
*/
if a.used == 1 { return true; }
/*
For an `Int` to be a power of two, all limbs except the top one have to be zero.
*/
for i := 1; i < a.used && a.digit[i - 1] != 0; i += 1 { return false; }
return true;
}
internal_is_power_of_two :: proc { internal_int_is_power_of_two, };
/*
Compare two `Int`s, signed.
Returns -1 if `a` < `b`, 0 if `a` == `b` and 1 if `b` > `a`.
Expects `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`.
*/
internal_int_compare :: #force_inline proc(a, b: ^Int) -> (comparison: int) {
a_is_negative := #force_inline internal_is_negative(a);
/*
Compare based on sign.
*/
if a.sign != b.sign { return -1 if a_is_negative else +1; }
/*
If `a` is negative, compare in the opposite direction */
if a_is_negative { return #force_inline internal_compare_magnitude(b, a); }
return #force_inline internal_compare_magnitude(a, b);
}
internal_compare :: proc { internal_int_compare, internal_int_compare_digit, };
internal_cmp :: internal_compare;
/*
Compare an `Int` to an unsigned number upto `DIGIT & _MASK`.
Returns -1 if `a` < `b`, 0 if `a` == `b` and 1 if `b` > `a`.
Expects: `a` and `b` both to be valid `Int`s, i.e. initialized and not `nil`.
*/
internal_int_compare_digit :: #force_inline proc(a: ^Int, b: DIGIT) -> (comparison: int) {
a_is_negative := #force_inline internal_is_negative(a);
switch {
/*
Compare based on sign first.
*/
case a_is_negative: return -1;
/*
Then compare on magnitude.
*/
case a.used > 1: return +1;
/*
We have only one digit. Compare it against `b`.
*/
case a.digit[0] < b: return -1;
case a.digit[0] == b: return 0;
case a.digit[0] > b: return +1;
/*
Unreachable.
Just here because Odin complains about a missing return value at the bottom of the proc otherwise.
*/
case: return;
}
}
internal_compare_digit :: proc { internal_int_compare_digit, };
internal_cmp_digit :: internal_compare_digit;
/*
Compare the magnitude of two `Int`s, unsigned.
*/
internal_int_compare_magnitude :: #force_inline proc(a, b: ^Int) -> (comparison: int) {
/*
Compare based on used digits.
*/
if a.used != b.used {
if a.used > b.used {
return +1;
}
return -1;
}
/*
Same number of used digits, compare based on their value.
*/
#no_bounds_check for n := a.used - 1; n >= 0; n -= 1 {
if a.digit[n] != b.digit[n] {
if a.digit[n] > b.digit[n] {
return +1;
}
return -1;
}
}
return 0;
}
internal_compare_magnitude :: proc { internal_int_compare_magnitude, };
internal_cmp_mag :: internal_compare_magnitude;
/*
========================= Logs, powers and roots ============================
*/
/*
Returns log_base(a).
Assumes `a` to not be `nil` and have been iniialized.
*/
internal_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) {
if base < 2 || DIGIT(base) > _DIGIT_MAX { return -1, .Invalid_Argument; }
if internal_is_negative(a) { return -1, .Math_Domain_Error; }
if internal_is_zero(a) { return -1, .Math_Domain_Error; }
/*
Fast path for bases that are a power of two.
*/
if platform_int_is_power_of_two(int(base)) { return private_log_power_of_two(a, base); }
/*
Fast path for `Int`s that fit within a single `DIGIT`.
*/
if a.used == 1 { return internal_log(a.digit[0], DIGIT(base)); }
return private_int_log(a, base);
}
/*
Returns log_base(a), where `a` is a DIGIT.
*/
internal_digit_log :: proc(a: DIGIT, base: DIGIT) -> (log: int, err: Error) {
/*
If the number is smaller than the base, it fits within a fraction.
Therefore, we return 0.
*/
if a < base { return 0, nil; }
/*
If a number equals the base, the log is 1.
*/
if a == base { return 1, nil; }
N := _WORD(a);
bracket_low := _WORD(1);
bracket_high := _WORD(base);
high := 1;
low := 0;
for bracket_high < N {
low = high;
bracket_low = bracket_high;
high <<= 1;
bracket_high *= bracket_high;
}
for high - low > 1 {
mid := (low + high) >> 1;
bracket_mid := bracket_low * #force_inline internal_small_pow(_WORD(base), _WORD(mid - low));
if N < bracket_mid {
high = mid;
bracket_high = bracket_mid;
}
if N > bracket_mid {
low = mid;
bracket_low = bracket_mid;
}
if N == bracket_mid {
return mid, nil;
}
}
if bracket_high == N {
return high, nil;
} else {
return low, nil;
}
}
internal_log :: proc { internal_int_log, internal_digit_log, };
/*
Calculate dest = base^power using a square-multiply algorithm.
Assumes `dest` and `base` not to be `nil` and to have been initialized.
*/
internal_int_pow :: proc(dest, base: ^Int, power: int) -> (err: Error) {
power := power;
/*
Early outs.
*/
if #force_inline internal_is_zero(base) {
/*
A zero base is a special case.
*/
if power < 0 {
if err = zero(dest); err != nil { return err; }
return .Math_Domain_Error;
}
if power == 0 { return set(dest, 1); }
if power > 0 { return zero(dest); }
}
if power < 0 {
/*
Fraction, so we'll return zero.
*/
return zero(dest);
}
switch(power) {
case 0:
/*
Any base to the power zero is one.
*/
return one(dest);
case 1:
/*
Any base to the power one is itself.
*/
return copy(dest, base);
case 2:
return #force_inline internal_sqr(dest, base);
}
g := &Int{};
if err = copy(g, base); err != nil { return err; }
/*
Set initial result.
*/
if err = set(dest, 1); err != nil { return err; }
loop: for power > 0 {
/*
If the bit is set, multiply.
*/
if power & 1 != 0 {
if err = mul(dest, g, dest); err != nil {
break loop;
}
}
/*
Square.
*/
if power > 1 {
if err = #force_inline internal_sqr(g, g); err != nil {
break loop;
}
}
/* shift to next bit */
power >>= 1;
}
destroy(g);
return err;
}
/*
Calculate `dest = base^power`.
Assumes `dest` not to be `nil` and to have been initialized.
*/
internal_int_pow_int :: proc(dest: ^Int, base, power: int) -> (err: Error) {
base_t := &Int{};
defer destroy(base_t);
if err = set(base_t, base); err != nil { return err; }
return #force_inline internal_int_pow(dest, base_t, power);
}
internal_pow :: proc { internal_int_pow, internal_int_pow_int, };
internal_exp :: pow;
/*
Returns the log2 of an `Int`.
Assumes `a` not to be `nil` and to have been initialized.
Also assumes `base` is a power of two.
*/
private_log_power_of_two :: proc(a: ^Int, base: DIGIT) -> (log: int, err: Error) {
base := base;
y: int;
for y = 0; base & 1 == 0; {
y += 1;
base >>= 1;
}
log, err = count_bits(a);
return (log - 1) / y, err;
}
/*
*/
internal_small_pow :: proc(base: _WORD, exponent: _WORD) -> (result: _WORD) {
exponent := exponent; base := base;
result = _WORD(1);
for exponent != 0 {
if exponent & 1 == 1 {
result *= base;
}
exponent >>= 1;
base *= base;
}
return result;
}
/*
This function is less generic than `root_n`, simpler and faster.
Assumes `dest` and `src` not to be `nil` and to have been initialized.
*/
internal_int_sqrt :: proc(dest, src: ^Int) -> (err: Error) {
/*
Must be positive.
*/
if #force_inline internal_is_negative(src) { return .Invalid_Argument; }
/*
Easy out. If src is zero, so is dest.
*/
if #force_inline internal_is_zero(src) { return zero(dest); }
/*
Set up temporaries.
*/
x, y, t1, t2 := &Int{}, &Int{}, &Int{}, &Int{};
defer destroy(x, y, t1, t2);
count: int;
if count, err = count_bits(src); err != nil { return err; }
a, b := count >> 1, count & 1;
if err = power_of_two(x, a+b); err != nil { return err; }
for {
/*
y = (x + n // x) // 2
*/
internal_div(t1, src, x);
internal_add(t2, t1, x);
shr(y, t2, 1);
if c := internal_cmp(y, x); c == 0 || c == 1 {
swap(dest, x);
return nil;
}
swap(x, y);
}
swap(dest, x);
return err;
}
internal_sqrt :: proc { internal_int_sqrt, };
/*
Find the nth root of an Integer.
Result found such that `(dest)**n <= src` and `(dest+1)**n > src`
This algorithm uses Newton's approximation `x[i+1] = x[i] - f(x[i])/f'(x[i])`,
which will find the root in `log(n)` time where each step involves a fair bit.
Assumes `dest` and `src` not to be `nil` and have been initialized.
*/
internal_int_root_n :: proc(dest, src: ^Int, n: int) -> (err: Error) {
/*
Fast path for n == 2
*/
if n == 2 { return #force_inline internal_sqrt(dest, src); }
if n < 0 || n > int(_DIGIT_MAX) { return .Invalid_Argument; }
if n & 1 == 0 && #force_inline internal_is_negative(src) { return .Invalid_Argument; }
/*
Set up temporaries.
*/
t1, t2, t3, a := &Int{}, &Int{}, &Int{}, &Int{};
defer destroy(t1, t2, t3);
/*
If `src` is negative fudge the sign but keep track.
*/
a.sign = .Zero_or_Positive;
a.used = src.used;
a.digit = src.digit;
/*
If "n" is larger than INT_MAX it is also larger than
log_2(src) because the bit-length of the "src" is measured
with an int and hence the root is always < 2 (two).
*/
if n > max(int) / 2 {
err = set(dest, 1);
dest.sign = a.sign;
return err;
}
/*
Compute seed: 2^(log_2(src)/n + 2)
*/
ilog2: int;
ilog2, err = count_bits(src);
/*
"src" is smaller than max(int), we can cast safely.
*/
if ilog2 < n {
err = set(dest, 1);
dest.sign = a.sign;
return err;
}
ilog2 /= n;
if ilog2 == 0 {
err = set(dest, 1);
dest.sign = a.sign;
return err;
}
/*
Start value must be larger than root.
*/
ilog2 += 2;
if err = power_of_two(t2, ilog2); err != nil { return err; }
c: int;
iterations := 0;
for {
/* t1 = t2 */
if err = copy(t1, t2); err != nil { return err; }
/* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */
/* t3 = t1**(b-1) */
if err = internal_pow(t3, t1, n-1); err != nil { return err; }
/* numerator */
/* t2 = t1**b */
if err = internal_mul(t2, t1, t3); err != nil { return err; }
/* t2 = t1**b - a */
if err = internal_sub(t2, t2, a); err != nil { return err; }
/* denominator */
/* t3 = t1**(b-1) * b */
if err = internal_mul(t3, t3, DIGIT(n)); err != nil { return err; }
/* t3 = (t1**b - a)/(b * t1**(b-1)) */
if err = internal_div(t3, t2, t3); err != nil { return err; }
if err = internal_sub(t2, t1, t3); err != nil { return err; }
/*
Number of rounds is at most log_2(root). If it is more it
got stuck, so break out of the loop and do the rest manually.
*/
if ilog2 -= 1; ilog2 == 0 {
break;
}
if c := internal_cmp(t1, t2); c == 0 { break; }
iterations += 1;
if iterations == MAX_ITERATIONS_ROOT_N {
return .Max_Iterations_Reached;
}
}
/* Result can be off by a few so check. */
/* Loop beneath can overshoot by one if found root is smaller than actual root. */
iterations = 0;
for {
if err = internal_pow(t2, t1, n); err != nil { return err; }
c := internal_cmp(t2, a);
if c == 0 {
swap(dest, t1);
return nil;
} else if c == -1 {
if err = internal_add(t1, t1, DIGIT(1)); err != nil { return err; }
} else {
break;
}
iterations += 1;
if iterations == MAX_ITERATIONS_ROOT_N {
return .Max_Iterations_Reached;
}
}
iterations = 0;
/* Correct overshoot from above or from recurrence. */
for {
if err = internal_pow(t2, t1, n); err != nil { return err; }
c := internal_cmp(t2, a);
if c == 1 {
if err = internal_sub(t1, t1, DIGIT(1)); err != nil { return err; }
} else {
break;
}
iterations += 1;
if iterations == MAX_ITERATIONS_ROOT_N {
return .Max_Iterations_Reached;
}
}
/* Set the result. */
swap(dest, t1);
/* set the sign of the result */
dest.sign = src.sign;
return err;
}
internal_root_n :: proc { internal_int_root_n, };
/*
Internal implementation of log.
Assumes `a` not to be `nil` and to have been initialized.
*/
private_int_log :: proc(a: ^Int, base: DIGIT) -> (res: int, err: Error) {
bracket_low, bracket_high, bracket_mid, t, bi_base := &Int{}, &Int{}, &Int{}, &Int{}, &Int{};
defer destroy(bracket_low, bracket_high, bracket_mid, t, bi_base);
ic := #force_inline internal_cmp(a, base);
if ic == -1 || ic == 0 {
return 1 if ic == 0 else 0, nil;
}
if err = set(bi_base, base); err != nil { return -1, err; }
if err = init_multi(bracket_mid, t); err != nil { return -1, err; }
if err = set(bracket_low, 1); err != nil { return -1, err; }
if err = set(bracket_high, base); err != nil { return -1, err; }
low := 0; high := 1;
/*
A kind of Giant-step/baby-step algorithm.
Idea shamelessly stolen from https://programmingpraxis.com/2010/05/07/integer-logarithms/2/
The effect is asymptotic, hence needs benchmarks to test if the Giant-step should be skipped
for small n.
*/
for {
/*
Iterate until `a` is bracketed between low + high.
*/
if bc := #force_inline internal_cmp(bracket_high, a); bc != -1 { break; }
low = high;
if err = copy(bracket_low, bracket_high); err != nil { return -1, err; }
high <<= 1;
if err = #force_inline internal_sqr(bracket_high, bracket_high); err != nil { return -1, err; }
}
for (high - low) > 1 {
mid := (high + low) >> 1;
if err = #force_inline internal_pow(t, bi_base, mid - low); err != nil { return -1, err; }
if err = #force_inline internal_mul(bracket_mid, bracket_low, t); err != nil { return -1, err; }
mc := #force_inline internal_cmp(a, bracket_mid);
if mc == -1 {
high = mid;
swap(bracket_mid, bracket_high);
}
if mc == 1 {
low = mid;
swap(bracket_mid, bracket_low);
}
if mc == 0 { return mid, nil; }
}
fc := #force_inline internal_cmp(bracket_high, a);
res = high if fc == 0 else low;
return;
}
/*
Other internal helpers
*/
internal_int_zero_unused :: #force_inline proc(dest: ^Int, old_used := -1) {
/*
If we don't pass the number of previously used DIGITs, we zero all remaining ones.
*/
zero_count: int;
if old_used == -1 {
zero_count = len(dest.digit) - dest.used;
} else {
zero_count = old_used - dest.used;
}
/*
Zero remainder.
*/
if zero_count > 0 && dest.used < len(dest.digit) {
mem.zero_slice(dest.digit[dest.used:][:zero_count]);
}
}
internal_zero_unused :: proc { internal_int_zero_unused, };
/*
========================== End of low-level routines ==========================
*/