11526b6e9d
This removes the odd width and precision specifiers found and replacing them with the more consistent api described in #1358. Take the following example: {1:5.9} This refers to the first argument (0-indexed) in the argument list. It will be printed with a minimum width of 5 and will have a precision of 9 (if applicable). Not all types correctly use these parameters just yet. There are still some missing gaps to fill in. Fill characters and alignment have yet to be implemented.
2315 lines
69 KiB
Zig
2315 lines
69 KiB
Zig
const std = @import("../../std.zig");
|
|
const builtin = @import("builtin");
|
|
const debug = std.debug;
|
|
const testing = std.testing;
|
|
const math = std.math;
|
|
const mem = std.mem;
|
|
const Allocator = mem.Allocator;
|
|
const ArrayList = std.ArrayList;
|
|
const maxInt = std.math.maxInt;
|
|
const minInt = std.math.minInt;
|
|
|
|
const TypeId = builtin.TypeId;
|
|
|
|
pub const Limb = usize;
|
|
pub const DoubleLimb = @IntType(false, 2 * Limb.bit_count);
|
|
pub const Log2Limb = math.Log2Int(Limb);
|
|
|
|
comptime {
|
|
debug.assert(math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
|
|
debug.assert(Limb.bit_count <= 64); // u128 set is unsupported
|
|
debug.assert(Limb.is_signed == false);
|
|
}
|
|
|
|
/// An arbitrary-precision big integer.
|
|
///
|
|
/// Memory is allocated by an Int as needed to ensure operations never overflow. The range of an
|
|
/// Int is bounded only by available memory.
|
|
pub const Int = struct {
|
|
const sign_bit: usize = 1 << (usize.bit_count - 1);
|
|
|
|
/// Default number of limbs to allocate on creation of an Int.
|
|
pub const default_capacity = 4;
|
|
|
|
/// Allocator used by the Int when requesting memory.
|
|
allocator: ?*Allocator,
|
|
|
|
/// Raw digits. These are:
|
|
///
|
|
/// * Little-endian ordered
|
|
/// * limbs.len >= 1
|
|
/// * Zero is represent as Int.len() == 1 with limbs[0] == 0.
|
|
///
|
|
/// Accessing limbs directly should be avoided.
|
|
limbs: []Limb,
|
|
|
|
/// High bit is the sign bit. If set, Int is negative, else Int is positive.
|
|
/// The remaining bits represent the number of limbs used by Int.
|
|
metadata: usize,
|
|
|
|
/// Creates a new Int. default_capacity limbs will be allocated immediately.
|
|
/// Int will be zeroed.
|
|
pub fn init(allocator: *Allocator) !Int {
|
|
return try Int.initCapacity(allocator, default_capacity);
|
|
}
|
|
|
|
/// Creates a new Int. Int will be set to `value`.
|
|
///
|
|
/// This is identical to an `init`, followed by a `set`.
|
|
pub fn initSet(allocator: *Allocator, value: var) !Int {
|
|
var s = try Int.init(allocator);
|
|
try s.set(value);
|
|
return s;
|
|
}
|
|
|
|
/// Creates a new Int with a specific capacity. If capacity < default_capacity then the
|
|
/// default capacity will be used instead.
|
|
pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
|
|
return Int{
|
|
.allocator = allocator,
|
|
.metadata = 1,
|
|
.limbs = block: {
|
|
var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
|
|
limbs[0] = 0;
|
|
break :block limbs;
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Returns the number of limbs currently in use.
|
|
pub fn len(self: Int) usize {
|
|
return self.metadata & ~sign_bit;
|
|
}
|
|
|
|
/// Returns whether an Int is positive.
|
|
pub fn isPositive(self: Int) bool {
|
|
return self.metadata & sign_bit == 0;
|
|
}
|
|
|
|
/// Sets the sign of an Int.
|
|
pub fn setSign(self: *Int, positive: bool) void {
|
|
if (positive) {
|
|
self.metadata &= ~sign_bit;
|
|
} else {
|
|
self.metadata |= sign_bit;
|
|
}
|
|
}
|
|
|
|
/// Sets the length of an Int.
|
|
///
|
|
/// If setLen is used, then the Int must be normalized to suit.
|
|
pub fn setLen(self: *Int, new_len: usize) void {
|
|
self.metadata &= sign_bit;
|
|
self.metadata |= new_len;
|
|
}
|
|
|
|
/// Returns an Int backed by a fixed set of limb values.
|
|
/// This is read-only and cannot be used as a result argument. If the Int tries to allocate
|
|
/// memory a runtime panic will occur.
|
|
pub fn initFixed(limbs: []const Limb) Int {
|
|
var self = Int{
|
|
.allocator = null,
|
|
.metadata = limbs.len,
|
|
// Cast away the const, invalid use to pass as a pointer argument.
|
|
.limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len],
|
|
};
|
|
|
|
self.normalize(limbs.len);
|
|
return self;
|
|
}
|
|
|
|
/// Ensures an Int has enough space allocated for capacity limbs. If the Int does not have
|
|
/// sufficient capacity, the exact amount will be allocated. This occurs even if the requested
|
|
/// capacity is only greater than the current capacity by one limb.
|
|
pub fn ensureCapacity(self: *Int, capacity: usize) !void {
|
|
self.assertWritable();
|
|
if (capacity <= self.limbs.len) {
|
|
return;
|
|
}
|
|
|
|
self.limbs = try self.allocator.?.realloc(self.limbs, capacity);
|
|
}
|
|
|
|
fn assertWritable(self: Int) void {
|
|
if (self.allocator == null) {
|
|
@panic("provided Int value is read-only but must be writable");
|
|
}
|
|
}
|
|
|
|
/// Frees all memory associated with an Int.
|
|
pub fn deinit(self: *Int) void {
|
|
self.assertWritable();
|
|
self.allocator.?.free(self.limbs);
|
|
self.* = undefined;
|
|
}
|
|
|
|
/// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and
|
|
/// can be modified separately from the original.
|
|
pub fn clone(other: Int) !Int {
|
|
other.assertWritable();
|
|
return Int{
|
|
.allocator = other.allocator,
|
|
.metadata = other.metadata,
|
|
.limbs = block: {
|
|
var limbs = try other.allocator.?.alloc(Limb, other.len());
|
|
mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);
|
|
break :block limbs;
|
|
},
|
|
};
|
|
}
|
|
|
|
/// Copies the value of an Int to an existing Int so that they both have the same value.
|
|
/// Extra memory will be allocated if the receiver does not have enough capacity.
|
|
pub fn copy(self: *Int, other: Int) !void {
|
|
self.assertWritable();
|
|
if (self.limbs.ptr == other.limbs.ptr) {
|
|
return;
|
|
}
|
|
|
|
try self.ensureCapacity(other.len());
|
|
mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len()]);
|
|
self.metadata = other.metadata;
|
|
}
|
|
|
|
/// Efficiently swap an Int with another. This swaps the limb pointers and a full copy is not
|
|
/// performed. The address of the limbs field will not be the same after this function.
|
|
pub fn swap(self: *Int, other: *Int) void {
|
|
self.assertWritable();
|
|
mem.swap(Int, self, other);
|
|
}
|
|
|
|
pub fn dump(self: Int) void {
|
|
for (self.limbs) |limb| {
|
|
debug.warn("{x} ", limb);
|
|
}
|
|
debug.warn("\n");
|
|
}
|
|
|
|
/// Negate the sign of an Int.
|
|
pub fn negate(self: *Int) void {
|
|
self.metadata ^= sign_bit;
|
|
}
|
|
|
|
/// Make an Int positive.
|
|
pub fn abs(self: *Int) void {
|
|
self.metadata &= ~sign_bit;
|
|
}
|
|
|
|
/// Returns true if an Int is odd.
|
|
pub fn isOdd(self: Int) bool {
|
|
return self.limbs[0] & 1 != 0;
|
|
}
|
|
|
|
/// Returns true if an Int is even.
|
|
pub fn isEven(self: Int) bool {
|
|
return !self.isOdd();
|
|
}
|
|
|
|
/// Returns the number of bits required to represent the absolute value an Int.
|
|
fn bitCountAbs(self: Int) usize {
|
|
return (self.len() - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.len() - 1]));
|
|
}
|
|
|
|
/// Returns the number of bits required to represent the integer in twos-complement form.
|
|
///
|
|
/// If the integer is negative the value returned is the number of bits needed by a signed
|
|
/// integer to represent the value. If positive the value is the number of bits for an
|
|
/// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
|
|
/// one greater than the returned value.
|
|
///
|
|
/// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
|
|
fn bitCountTwosComp(self: Int) usize {
|
|
var bits = self.bitCountAbs();
|
|
|
|
// If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
|
|
// complement requires one less bit.
|
|
if (!self.isPositive()) block: {
|
|
bits += 1;
|
|
|
|
if (@popCount(Limb, self.limbs[self.len() - 1]) == 1) {
|
|
for (self.limbs[0 .. self.len() - 1]) |limb| {
|
|
if (@popCount(Limb, limb) != 0) {
|
|
break :block;
|
|
}
|
|
}
|
|
|
|
bits -= 1;
|
|
}
|
|
}
|
|
|
|
return bits;
|
|
}
|
|
|
|
fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
|
|
if (self.eqZero()) {
|
|
return true;
|
|
}
|
|
if (!is_signed and !self.isPositive()) {
|
|
return false;
|
|
}
|
|
|
|
const req_bits = self.bitCountTwosComp() + @boolToInt(self.isPositive() and is_signed);
|
|
return bit_count >= req_bits;
|
|
}
|
|
|
|
/// Returns whether self can fit into an integer of the requested type.
|
|
pub fn fits(self: Int, comptime T: type) bool {
|
|
return self.fitsInTwosComp(T.is_signed, T.bit_count);
|
|
}
|
|
|
|
/// Returns the approximate size of the integer in the given base. Negative values accommodate for
|
|
/// the minus sign. This is used for determining the number of characters needed to print the
|
|
/// value. It is inexact and may exceed the given value by ~1-2 bytes.
|
|
pub fn sizeInBase(self: Int, base: usize) usize {
|
|
const bit_count = usize(@boolToInt(!self.isPositive())) + self.bitCountAbs();
|
|
return (bit_count / math.log2(base)) + 1;
|
|
}
|
|
|
|
/// Sets an Int to value. Value must be an primitive integer type.
|
|
pub fn set(self: *Int, value: var) Allocator.Error!void {
|
|
self.assertWritable();
|
|
const T = @typeOf(value);
|
|
|
|
switch (@typeInfo(T)) {
|
|
TypeId.Int => |info| {
|
|
const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
|
|
|
|
try self.ensureCapacity(@sizeOf(UT) / @sizeOf(Limb));
|
|
self.metadata = 0;
|
|
self.setSign(value >= 0);
|
|
|
|
var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
|
|
|
|
if (info.bits <= Limb.bit_count) {
|
|
self.limbs[0] = Limb(w_value);
|
|
self.metadata += 1;
|
|
} else {
|
|
var i: usize = 0;
|
|
while (w_value != 0) : (i += 1) {
|
|
self.limbs[i] = @truncate(Limb, w_value);
|
|
self.metadata += 1;
|
|
|
|
// TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
|
|
w_value >>= Limb.bit_count / 2;
|
|
w_value >>= Limb.bit_count / 2;
|
|
}
|
|
}
|
|
},
|
|
TypeId.ComptimeInt => {
|
|
comptime var w_value = if (value < 0) -value else value;
|
|
|
|
const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
|
|
try self.ensureCapacity(req_limbs);
|
|
|
|
self.metadata = req_limbs;
|
|
self.setSign(value >= 0);
|
|
|
|
if (w_value <= maxInt(Limb)) {
|
|
self.limbs[0] = w_value;
|
|
} else {
|
|
const mask = (1 << Limb.bit_count) - 1;
|
|
|
|
comptime var i = 0;
|
|
inline while (w_value != 0) : (i += 1) {
|
|
self.limbs[i] = w_value & mask;
|
|
|
|
w_value >>= Limb.bit_count / 2;
|
|
w_value >>= Limb.bit_count / 2;
|
|
}
|
|
}
|
|
},
|
|
else => {
|
|
@compileError("cannot set Int using type " ++ @typeName(T));
|
|
},
|
|
}
|
|
}
|
|
|
|
pub const ConvertError = error{
|
|
NegativeIntoUnsigned,
|
|
TargetTooSmall,
|
|
};
|
|
|
|
/// Convert self to type T.
|
|
///
|
|
/// Returns an error if self cannot be narrowed into the requested type without truncation.
|
|
pub fn to(self: Int, comptime T: type) ConvertError!T {
|
|
switch (@typeId(T)) {
|
|
TypeId.Int => {
|
|
const UT = @IntType(false, T.bit_count);
|
|
|
|
if (self.bitCountTwosComp() > T.bit_count) {
|
|
return error.TargetTooSmall;
|
|
}
|
|
|
|
var r: UT = 0;
|
|
|
|
if (@sizeOf(UT) <= @sizeOf(Limb)) {
|
|
r = @intCast(UT, self.limbs[0]);
|
|
} else {
|
|
for (self.limbs[0..self.len()]) |_, ri| {
|
|
const limb = self.limbs[self.len() - ri - 1];
|
|
r <<= Limb.bit_count;
|
|
r |= limb;
|
|
}
|
|
}
|
|
|
|
if (!T.is_signed) {
|
|
return if (self.isPositive()) @intCast(T, r) else error.NegativeIntoUnsigned;
|
|
} else {
|
|
if (self.isPositive()) {
|
|
return @intCast(T, r);
|
|
} else {
|
|
if (math.cast(T, r)) |ok| {
|
|
return -ok;
|
|
} else |_| {
|
|
return minInt(T);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
else => {
|
|
@compileError("cannot convert Int to type " ++ @typeName(T));
|
|
},
|
|
}
|
|
}
|
|
|
|
fn charToDigit(ch: u8, base: u8) !u8 {
|
|
const d = switch (ch) {
|
|
'0'...'9' => ch - '0',
|
|
'a'...'f' => (ch - 'a') + 0xa,
|
|
else => return error.InvalidCharForDigit,
|
|
};
|
|
|
|
return if (d < base) d else return error.DigitTooLargeForBase;
|
|
}
|
|
|
|
fn digitToChar(d: u8, base: u8) !u8 {
|
|
if (d >= base) {
|
|
return error.DigitTooLargeForBase;
|
|
}
|
|
|
|
return switch (d) {
|
|
0...9 => '0' + d,
|
|
0xa...0xf => ('a' - 0xa) + d,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
/// Set self from the string representation `value`.
|
|
///
|
|
/// value must contain only digits <= `base`. Base prefixes are not allowed (e.g. 0x43 should
|
|
/// simply be 43).
|
|
///
|
|
/// Returns an error if memory could not be allocated or `value` has invalid digits for the
|
|
/// requested base.
|
|
pub fn setString(self: *Int, base: u8, value: []const u8) !void {
|
|
self.assertWritable();
|
|
if (base < 2 or base > 16) {
|
|
return error.InvalidBase;
|
|
}
|
|
|
|
var i: usize = 0;
|
|
var positive = true;
|
|
if (value.len > 0 and value[0] == '-') {
|
|
positive = false;
|
|
i += 1;
|
|
}
|
|
|
|
const ap_base = Int.initFixed(([_]Limb{base})[0..]);
|
|
try self.set(0);
|
|
|
|
for (value[i..]) |ch| {
|
|
const d = try charToDigit(ch, base);
|
|
|
|
const ap_d = Int.initFixed(([_]Limb{d})[0..]);
|
|
|
|
try self.mul(self.*, ap_base);
|
|
try self.add(self.*, ap_d);
|
|
}
|
|
self.setSign(positive);
|
|
}
|
|
|
|
/// Converts self to a string in the requested base. Memory is allocated from the provided
|
|
/// allocator and not the one present in self.
|
|
/// TODO make this call format instead of the other way around
|
|
pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
|
|
if (base < 2 or base > 16) {
|
|
return error.InvalidBase;
|
|
}
|
|
|
|
var digits = ArrayList(u8).init(allocator);
|
|
try digits.ensureCapacity(self.sizeInBase(base) + 1);
|
|
defer digits.deinit();
|
|
|
|
if (self.eqZero()) {
|
|
try digits.append('0');
|
|
return digits.toOwnedSlice();
|
|
}
|
|
|
|
// Power of two: can do a single pass and use masks to extract digits.
|
|
if (math.isPowerOfTwo(base)) {
|
|
const base_shift = math.log2_int(Limb, base);
|
|
|
|
for (self.limbs[0..self.len()]) |limb| {
|
|
var shift: usize = 0;
|
|
while (shift < Limb.bit_count) : (shift += base_shift) {
|
|
const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & Limb(base - 1));
|
|
const ch = try digitToChar(r, base);
|
|
try digits.append(ch);
|
|
}
|
|
}
|
|
|
|
while (true) {
|
|
// always will have a non-zero digit somewhere
|
|
const c = digits.pop();
|
|
if (c != '0') {
|
|
digits.append(c) catch unreachable;
|
|
break;
|
|
}
|
|
}
|
|
} // Non power-of-two: batch divisions per word size.
|
|
else {
|
|
const digits_per_limb = math.log(Limb, base, maxInt(Limb));
|
|
var limb_base: Limb = 1;
|
|
var j: usize = 0;
|
|
while (j < digits_per_limb) : (j += 1) {
|
|
limb_base *= base;
|
|
}
|
|
|
|
var q = try self.clone();
|
|
q.abs();
|
|
var r = try Int.init(allocator);
|
|
var b = try Int.initSet(allocator, limb_base);
|
|
|
|
while (q.len() >= 2) {
|
|
try Int.divTrunc(&q, &r, q, b);
|
|
|
|
var r_word = r.limbs[0];
|
|
var i: usize = 0;
|
|
while (i < digits_per_limb) : (i += 1) {
|
|
const ch = try digitToChar(@intCast(u8, r_word % base), base);
|
|
r_word /= base;
|
|
try digits.append(ch);
|
|
}
|
|
}
|
|
|
|
{
|
|
debug.assert(q.len() == 1);
|
|
|
|
var r_word = q.limbs[0];
|
|
while (r_word != 0) {
|
|
const ch = try digitToChar(@intCast(u8, r_word % base), base);
|
|
r_word /= base;
|
|
try digits.append(ch);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!self.isPositive()) {
|
|
try digits.append('-');
|
|
}
|
|
|
|
var s = digits.toOwnedSlice();
|
|
mem.reverse(u8, s);
|
|
return s;
|
|
}
|
|
|
|
/// To allow `std.fmt.printf` to work with Int.
|
|
/// TODO make this non-allocating
|
|
pub fn format(
|
|
self: Int,
|
|
comptime fmt: []const u8,
|
|
comptime options: std.fmt.FormatOptions,
|
|
context: var,
|
|
comptime FmtError: type,
|
|
output: fn (@typeOf(context), []const u8) FmtError!void,
|
|
) FmtError!void {
|
|
self.assertWritable();
|
|
// TODO look at fmt and support other bases
|
|
// TODO support read-only fixed integers
|
|
const str = self.toString(self.allocator.?, 10) catch @panic("TODO make this non allocating");
|
|
defer self.allocator.?.free(str);
|
|
return output(context, str);
|
|
}
|
|
|
|
/// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
|
|
pub fn cmpAbs(a: Int, b: Int) i8 {
|
|
if (a.len() < b.len()) {
|
|
return -1;
|
|
}
|
|
if (a.len() > b.len()) {
|
|
return 1;
|
|
}
|
|
|
|
var i: usize = a.len() - 1;
|
|
while (i != 0) : (i -= 1) {
|
|
if (a.limbs[i] != b.limbs[i]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (a.limbs[i] < b.limbs[i]) {
|
|
return -1;
|
|
} else if (a.limbs[i] > b.limbs[i]) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/// Returns -1, 0, 1 if a < b, a == b or a > b respectively.
|
|
pub fn cmp(a: Int, b: Int) i8 {
|
|
if (a.isPositive() != b.isPositive()) {
|
|
return if (a.isPositive()) i8(1) else -1;
|
|
} else {
|
|
const r = cmpAbs(a, b);
|
|
return if (a.isPositive()) r else -r;
|
|
}
|
|
}
|
|
|
|
/// Returns true if a == 0.
|
|
pub fn eqZero(a: Int) bool {
|
|
return a.len() == 1 and a.limbs[0] == 0;
|
|
}
|
|
|
|
/// Returns true if |a| == |b|.
|
|
pub fn eqAbs(a: Int, b: Int) bool {
|
|
return cmpAbs(a, b) == 0;
|
|
}
|
|
|
|
/// Returns true if a == b.
|
|
pub fn eq(a: Int, b: Int) bool {
|
|
return cmp(a, b) == 0;
|
|
}
|
|
|
|
// Normalize a possible sequence of leading zeros.
|
|
//
|
|
// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
|
|
// [1, 2, 0, 0, 0] -> [1, 2]
|
|
// [0, 0, 0, 0, 0] -> [0]
|
|
fn normalize(r: *Int, length: usize) void {
|
|
debug.assert(length > 0);
|
|
debug.assert(length <= r.limbs.len);
|
|
|
|
var j = length;
|
|
while (j > 0) : (j -= 1) {
|
|
if (r.limbs[j - 1] != 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Handle zero
|
|
r.setLen(if (j != 0) j else 1);
|
|
}
|
|
|
|
// Cannot be used as a result argument to any function.
|
|
fn readOnlyPositive(a: Int) Int {
|
|
return Int{
|
|
.allocator = null,
|
|
.metadata = a.len(),
|
|
.limbs = a.limbs,
|
|
};
|
|
}
|
|
|
|
/// r = a + b
|
|
///
|
|
/// r, a and b may be aliases.
|
|
///
|
|
/// Returns an error if memory could not be allocated.
|
|
pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {
|
|
r.assertWritable();
|
|
if (a.eqZero()) {
|
|
try r.copy(b);
|
|
return;
|
|
} else if (b.eqZero()) {
|
|
try r.copy(a);
|
|
return;
|
|
}
|
|
|
|
if (a.isPositive() != b.isPositive()) {
|
|
if (a.isPositive()) {
|
|
// (a) + (-b) => a - b
|
|
try r.sub(a, readOnlyPositive(b));
|
|
} else {
|
|
// (-a) + (b) => b - a
|
|
try r.sub(b, readOnlyPositive(a));
|
|
}
|
|
} else {
|
|
if (a.len() >= b.len()) {
|
|
try r.ensureCapacity(a.len() + 1);
|
|
lladd(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
|
|
r.normalize(a.len() + 1);
|
|
} else {
|
|
try r.ensureCapacity(b.len() + 1);
|
|
lladd(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
|
|
r.normalize(b.len() + 1);
|
|
}
|
|
|
|
r.setSign(a.isPositive());
|
|
}
|
|
}
|
|
|
|
// Knuth 4.3.1, Algorithm A.
|
|
fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(a.len != 0 and b.len != 0);
|
|
debug.assert(a.len >= b.len);
|
|
debug.assert(r.len >= a.len + 1);
|
|
|
|
var i: usize = 0;
|
|
var carry: Limb = 0;
|
|
|
|
while (i < b.len) : (i += 1) {
|
|
var c: Limb = 0;
|
|
c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i]));
|
|
c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i]));
|
|
carry = c;
|
|
}
|
|
|
|
while (i < a.len) : (i += 1) {
|
|
carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i]));
|
|
}
|
|
|
|
r[i] = carry;
|
|
}
|
|
|
|
/// r = a - b
|
|
///
|
|
/// r, a and b may be aliases.
|
|
///
|
|
/// Returns an error if memory could not be allocated.
|
|
pub fn sub(r: *Int, a: Int, b: Int) !void {
|
|
r.assertWritable();
|
|
if (a.isPositive() != b.isPositive()) {
|
|
if (a.isPositive()) {
|
|
// (a) - (-b) => a + b
|
|
try r.add(a, readOnlyPositive(b));
|
|
} else {
|
|
// (-a) - (b) => -(a + b)
|
|
try r.add(readOnlyPositive(a), b);
|
|
r.setSign(false);
|
|
}
|
|
} else {
|
|
if (a.isPositive()) {
|
|
// (a) - (b) => a - b
|
|
if (a.cmp(b) >= 0) {
|
|
try r.ensureCapacity(a.len() + 1);
|
|
llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
|
|
r.normalize(a.len());
|
|
r.setSign(true);
|
|
} else {
|
|
try r.ensureCapacity(b.len() + 1);
|
|
llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
|
|
r.normalize(b.len());
|
|
r.setSign(false);
|
|
}
|
|
} else {
|
|
// (-a) - (-b) => -(a - b)
|
|
if (a.cmp(b) < 0) {
|
|
try r.ensureCapacity(a.len() + 1);
|
|
llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
|
|
r.normalize(a.len());
|
|
r.setSign(false);
|
|
} else {
|
|
try r.ensureCapacity(b.len() + 1);
|
|
llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
|
|
r.normalize(b.len());
|
|
r.setSign(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Knuth 4.3.1, Algorithm S.
|
|
fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(a.len != 0 and b.len != 0);
|
|
debug.assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
|
|
debug.assert(r.len >= a.len);
|
|
|
|
var i: usize = 0;
|
|
var borrow: Limb = 0;
|
|
|
|
while (i < b.len) : (i += 1) {
|
|
var c: Limb = 0;
|
|
c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i]));
|
|
c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i]));
|
|
borrow = c;
|
|
}
|
|
|
|
while (i < a.len) : (i += 1) {
|
|
borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i]));
|
|
}
|
|
|
|
debug.assert(borrow == 0);
|
|
}
|
|
|
|
/// rma = a * b
|
|
///
|
|
/// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
|
|
///
|
|
/// Returns an error if memory could not be allocated.
|
|
pub fn mul(rma: *Int, a: Int, b: Int) !void {
|
|
rma.assertWritable();
|
|
|
|
var r = rma;
|
|
var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;
|
|
|
|
var sr: Int = undefined;
|
|
if (aliased) {
|
|
sr = try Int.initCapacity(rma.allocator.?, a.len() + b.len());
|
|
r = &sr;
|
|
aliased = true;
|
|
}
|
|
defer if (aliased) {
|
|
rma.swap(r);
|
|
r.deinit();
|
|
};
|
|
|
|
try r.ensureCapacity(a.len() + b.len());
|
|
|
|
if (a.len() >= b.len()) {
|
|
llmul(r.limbs, a.limbs[0..a.len()], b.limbs[0..b.len()]);
|
|
} else {
|
|
llmul(r.limbs, b.limbs[0..b.len()], a.limbs[0..a.len()]);
|
|
}
|
|
|
|
r.normalize(a.len() + b.len());
|
|
r.setSign(a.isPositive() == b.isPositive());
|
|
}
|
|
|
|
// a + b * c + *carry, sets carry to the overflow bits
|
|
pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
|
|
var r1: Limb = undefined;
|
|
|
|
// r1 = a + *carry
|
|
const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1));
|
|
|
|
// r2 = b * c
|
|
const bc = DoubleLimb(math.mulWide(Limb, b, c));
|
|
const r2 = @truncate(Limb, bc);
|
|
const c2 = @truncate(Limb, bc >> Limb.bit_count);
|
|
|
|
// r1 = r1 + r2
|
|
const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1));
|
|
|
|
// This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
|
|
// c2 is at least <= maxInt(Limb) - 2.
|
|
carry.* = c1 + c2 + c3;
|
|
|
|
return r1;
|
|
}
|
|
|
|
// Knuth 4.3.1, Algorithm M.
|
|
//
|
|
// r MUST NOT alias any of a or b.
|
|
fn llmul(r: []Limb, a: []const Limb, b: []const Limb) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(a.len >= b.len);
|
|
debug.assert(r.len >= a.len + b.len);
|
|
|
|
mem.set(Limb, r[0 .. a.len + b.len], 0);
|
|
|
|
var i: usize = 0;
|
|
while (i < a.len) : (i += 1) {
|
|
var carry: Limb = 0;
|
|
var j: usize = 0;
|
|
while (j < b.len) : (j += 1) {
|
|
r[i + j] = @inlineCall(addMulLimbWithCarry, r[i + j], a[i], b[j], &carry);
|
|
}
|
|
r[i + j] = carry;
|
|
}
|
|
}
|
|
|
|
/// q = a / b (rem r)
|
|
///
|
|
/// a / b are floored (rounded towards 0).
|
|
pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void {
|
|
try div(q, r, a, b);
|
|
|
|
// Trunc -> Floor.
|
|
if (!q.isPositive()) {
|
|
const one = Int.initFixed(([_]Limb{1})[0..]);
|
|
try q.sub(q.*, one);
|
|
try r.add(q.*, one);
|
|
}
|
|
r.setSign(b.isPositive());
|
|
}
|
|
|
|
/// q = a / b (rem r)
|
|
///
|
|
/// a / b are truncated (rounded towards -inf).
|
|
pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void {
|
|
try div(q, r, a, b);
|
|
r.setSign(a.isPositive());
|
|
}
|
|
|
|
// Truncates by default.
|
|
fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void {
|
|
quo.assertWritable();
|
|
rem.assertWritable();
|
|
|
|
if (b.eqZero()) {
|
|
@panic("division by zero");
|
|
}
|
|
if (quo == rem) {
|
|
@panic("quo and rem cannot be same variable");
|
|
}
|
|
|
|
if (a.cmpAbs(b) < 0) {
|
|
// quo may alias a so handle rem first
|
|
try rem.copy(a);
|
|
rem.setSign(a.isPositive() == b.isPositive());
|
|
|
|
quo.metadata = 1;
|
|
quo.limbs[0] = 0;
|
|
return;
|
|
}
|
|
|
|
// Handle trailing zero-words of divisor/dividend. These are not handled in the following
|
|
// algorithms.
|
|
const a_zero_limb_count = blk: {
|
|
var i: usize = 0;
|
|
while (i < a.len()) : (i += 1) {
|
|
if (a.limbs[i] != 0) break;
|
|
}
|
|
break :blk i;
|
|
};
|
|
const b_zero_limb_count = blk: {
|
|
var i: usize = 0;
|
|
while (i < b.len()) : (i += 1) {
|
|
if (b.limbs[i] != 0) break;
|
|
}
|
|
break :blk i;
|
|
};
|
|
|
|
const ab_zero_limb_count = std.math.min(a_zero_limb_count, b_zero_limb_count);
|
|
|
|
if (b.len() - ab_zero_limb_count == 1) {
|
|
try quo.ensureCapacity(a.len());
|
|
|
|
lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.len()], b.limbs[b.len() - 1]);
|
|
quo.normalize(a.len() - ab_zero_limb_count);
|
|
quo.setSign(a.isPositive() == b.isPositive());
|
|
|
|
rem.metadata = 1;
|
|
} else {
|
|
// x and y are modified during division
|
|
var x = try Int.initCapacity(quo.allocator.?, a.len());
|
|
defer x.deinit();
|
|
try x.copy(a);
|
|
|
|
var y = try Int.initCapacity(quo.allocator.?, b.len());
|
|
defer y.deinit();
|
|
try y.copy(b);
|
|
|
|
// x may grow one limb during normalization
|
|
try quo.ensureCapacity(a.len() + y.len());
|
|
|
|
// Shrink x, y such that the trailing zero limbs shared between are removed.
|
|
if (ab_zero_limb_count != 0) {
|
|
std.mem.copy(Limb, x.limbs[0..], x.limbs[ab_zero_limb_count..]);
|
|
std.mem.copy(Limb, y.limbs[0..], y.limbs[ab_zero_limb_count..]);
|
|
x.metadata -= ab_zero_limb_count;
|
|
y.metadata -= ab_zero_limb_count;
|
|
}
|
|
|
|
try divN(quo.allocator.?, quo, rem, &x, &y);
|
|
quo.setSign(a.isPositive() == b.isPositive());
|
|
}
|
|
|
|
if (ab_zero_limb_count != 0) {
|
|
try rem.shiftLeft(rem.*, ab_zero_limb_count * Limb.bit_count);
|
|
}
|
|
}
|
|
|
|
// Knuth 4.3.1, Exercise 16.
|
|
fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(a.len > 1 or a[0] >= b);
|
|
debug.assert(quo.len >= a.len);
|
|
|
|
rem.* = 0;
|
|
for (a) |_, ri| {
|
|
const i = a.len - ri - 1;
|
|
const pdiv = ((DoubleLimb(rem.*) << Limb.bit_count) | a[i]);
|
|
|
|
if (pdiv == 0) {
|
|
quo[i] = 0;
|
|
rem.* = 0;
|
|
} else if (pdiv < b) {
|
|
quo[i] = 0;
|
|
rem.* = @truncate(Limb, pdiv);
|
|
} else if (pdiv == b) {
|
|
quo[i] = 1;
|
|
rem.* = 0;
|
|
} else {
|
|
quo[i] = @truncate(Limb, @divTrunc(pdiv, b));
|
|
rem.* = @truncate(Limb, pdiv - (quo[i] *% b));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handbook of Applied Cryptography, 14.20
|
|
//
|
|
// x = qy + r where 0 <= r < y
|
|
fn divN(allocator: *Allocator, q: *Int, r: *Int, x: *Int, y: *Int) !void {
|
|
debug.assert(y.len() >= 2);
|
|
debug.assert(x.len() >= y.len());
|
|
debug.assert(q.limbs.len >= x.len() + y.len() - 1);
|
|
debug.assert(default_capacity >= 3); // see 3.2
|
|
|
|
var tmp = try Int.init(allocator);
|
|
defer tmp.deinit();
|
|
|
|
// Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
|
|
var norm_shift = @clz(Limb, y.limbs[y.len() - 1]);
|
|
if (norm_shift == 0 and y.isOdd()) {
|
|
norm_shift = Limb.bit_count;
|
|
}
|
|
try x.shiftLeft(x.*, norm_shift);
|
|
try y.shiftLeft(y.*, norm_shift);
|
|
|
|
const n = x.len() - 1;
|
|
const t = y.len() - 1;
|
|
|
|
// 1.
|
|
q.metadata = n - t + 1;
|
|
mem.set(Limb, q.limbs[0..q.len()], 0);
|
|
|
|
// 2.
|
|
try tmp.shiftLeft(y.*, Limb.bit_count * (n - t));
|
|
while (x.cmp(tmp) >= 0) {
|
|
q.limbs[n - t] += 1;
|
|
try x.sub(x.*, tmp);
|
|
}
|
|
|
|
// 3.
|
|
var i = n;
|
|
while (i > t) : (i -= 1) {
|
|
// 3.1
|
|
if (x.limbs[i] == y.limbs[t]) {
|
|
q.limbs[i - t - 1] = maxInt(Limb);
|
|
} else {
|
|
const num = (DoubleLimb(x.limbs[i]) << Limb.bit_count) | DoubleLimb(x.limbs[i - 1]);
|
|
const z = @intCast(Limb, num / DoubleLimb(y.limbs[t]));
|
|
q.limbs[i - t - 1] = if (z > maxInt(Limb)) maxInt(Limb) else Limb(z);
|
|
}
|
|
|
|
// 3.2
|
|
tmp.limbs[0] = if (i >= 2) x.limbs[i - 2] else 0;
|
|
tmp.limbs[1] = if (i >= 1) x.limbs[i - 1] else 0;
|
|
tmp.limbs[2] = x.limbs[i];
|
|
tmp.normalize(3);
|
|
|
|
while (true) {
|
|
// 2x1 limb multiplication unrolled against single-limb q[i-t-1]
|
|
var carry: Limb = 0;
|
|
r.limbs[0] = addMulLimbWithCarry(0, if (t >= 1) y.limbs[t - 1] else 0, q.limbs[i - t - 1], &carry);
|
|
r.limbs[1] = addMulLimbWithCarry(0, y.limbs[t], q.limbs[i - t - 1], &carry);
|
|
r.limbs[2] = carry;
|
|
r.normalize(3);
|
|
|
|
if (r.cmpAbs(tmp) <= 0) {
|
|
break;
|
|
}
|
|
|
|
q.limbs[i - t - 1] -= 1;
|
|
}
|
|
|
|
// 3.3
|
|
try tmp.set(q.limbs[i - t - 1]);
|
|
try tmp.mul(tmp, y.*);
|
|
try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1));
|
|
try x.sub(x.*, tmp);
|
|
|
|
if (!x.isPositive()) {
|
|
try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1));
|
|
try x.add(x.*, tmp);
|
|
q.limbs[i - t - 1] -= 1;
|
|
}
|
|
}
|
|
|
|
// Denormalize
|
|
q.normalize(q.len());
|
|
|
|
try r.shiftRight(x.*, norm_shift);
|
|
r.normalize(r.len());
|
|
}
|
|
|
|
/// r = a << shift, in other words, r = a * 2^shift
|
|
pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {
|
|
r.assertWritable();
|
|
|
|
try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);
|
|
llshl(r.limbs[0..], a.limbs[0..a.len()], shift);
|
|
r.normalize(a.len() + (shift / Limb.bit_count) + 1);
|
|
r.setSign(a.isPositive());
|
|
}
|
|
|
|
fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(a.len >= 1);
|
|
debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
|
|
|
|
const limb_shift = shift / Limb.bit_count + 1;
|
|
const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
|
|
|
|
var carry: Limb = 0;
|
|
var i: usize = 0;
|
|
while (i < a.len) : (i += 1) {
|
|
const src_i = a.len - i - 1;
|
|
const dst_i = src_i + limb_shift;
|
|
|
|
const src_digit = a[src_i];
|
|
r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift));
|
|
carry = (src_digit << interior_limb_shift);
|
|
}
|
|
|
|
r[limb_shift - 1] = carry;
|
|
mem.set(Limb, r[0 .. limb_shift - 1], 0);
|
|
}
|
|
|
|
/// r = a >> shift
|
|
pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {
|
|
r.assertWritable();
|
|
|
|
if (a.len() <= shift / Limb.bit_count) {
|
|
r.metadata = 1;
|
|
r.limbs[0] = 0;
|
|
return;
|
|
}
|
|
|
|
try r.ensureCapacity(a.len() - (shift / Limb.bit_count));
|
|
const r_len = llshr(r.limbs[0..], a.limbs[0..a.len()], shift);
|
|
r.metadata = a.len() - (shift / Limb.bit_count);
|
|
r.setSign(a.isPositive());
|
|
}
|
|
|
|
fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(a.len >= 1);
|
|
debug.assert(r.len >= a.len - (shift / Limb.bit_count));
|
|
|
|
const limb_shift = shift / Limb.bit_count;
|
|
const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count);
|
|
|
|
var carry: Limb = 0;
|
|
var i: usize = 0;
|
|
while (i < a.len - limb_shift) : (i += 1) {
|
|
const src_i = a.len - i - 1;
|
|
const dst_i = src_i - limb_shift;
|
|
|
|
const src_digit = a[src_i];
|
|
r[dst_i] = carry | (src_digit >> interior_limb_shift);
|
|
carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - @intCast(Limb, interior_limb_shift));
|
|
}
|
|
}
|
|
|
|
/// r = a | b
|
|
///
|
|
/// a and b are zero-extended to the longer of a or b.
|
|
pub fn bitOr(r: *Int, a: Int, b: Int) !void {
|
|
r.assertWritable();
|
|
|
|
if (a.len() > b.len()) {
|
|
try r.ensureCapacity(a.len());
|
|
llor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
|
|
r.setLen(a.len());
|
|
} else {
|
|
try r.ensureCapacity(b.len());
|
|
llor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
|
|
r.setLen(b.len());
|
|
}
|
|
}
|
|
|
|
fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(r.len >= a.len);
|
|
debug.assert(a.len >= b.len);
|
|
|
|
var i: usize = 0;
|
|
while (i < b.len) : (i += 1) {
|
|
r[i] = a[i] | b[i];
|
|
}
|
|
while (i < a.len) : (i += 1) {
|
|
r[i] = a[i];
|
|
}
|
|
}
|
|
|
|
/// r = a & b
|
|
pub fn bitAnd(r: *Int, a: Int, b: Int) !void {
|
|
r.assertWritable();
|
|
|
|
if (a.len() > b.len()) {
|
|
try r.ensureCapacity(b.len());
|
|
lland(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
|
|
r.normalize(b.len());
|
|
} else {
|
|
try r.ensureCapacity(a.len());
|
|
lland(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
|
|
r.normalize(a.len());
|
|
}
|
|
}
|
|
|
|
fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(r.len >= b.len);
|
|
debug.assert(a.len >= b.len);
|
|
|
|
var i: usize = 0;
|
|
while (i < b.len) : (i += 1) {
|
|
r[i] = a[i] & b[i];
|
|
}
|
|
}
|
|
|
|
/// r = a ^ b
|
|
pub fn bitXor(r: *Int, a: Int, b: Int) !void {
|
|
r.assertWritable();
|
|
|
|
if (a.len() > b.len()) {
|
|
try r.ensureCapacity(a.len());
|
|
llxor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
|
|
r.normalize(a.len());
|
|
} else {
|
|
try r.ensureCapacity(b.len());
|
|
llxor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
|
|
r.normalize(b.len());
|
|
}
|
|
}
|
|
|
|
fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
|
|
@setRuntimeSafety(false);
|
|
debug.assert(r.len >= a.len);
|
|
debug.assert(a.len >= b.len);
|
|
|
|
var i: usize = 0;
|
|
while (i < b.len) : (i += 1) {
|
|
r[i] = a[i] ^ b[i];
|
|
}
|
|
while (i < a.len) : (i += 1) {
|
|
r[i] = a[i];
|
|
}
|
|
}
|
|
};
|
|
|
|
// NOTE: All the following tests assume the max machine-word will be 64-bit.
|
|
//
|
|
// They will still run on larger than this and should pass, but the multi-limb code-paths
|
|
// may be untested in some cases.
|
|
|
|
var buffer: [64 * 8192]u8 = undefined;
|
|
var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
|
|
const al = &fixed.allocator;
|
|
|
|
test "big.int comptime_int set" {
|
|
comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
|
|
var a = try Int.initSet(al, s);
|
|
|
|
const s_limb_count = 128 / Limb.bit_count;
|
|
|
|
comptime var i: usize = 0;
|
|
inline while (i < s_limb_count) : (i += 1) {
|
|
const result = Limb(s & maxInt(Limb));
|
|
s >>= Limb.bit_count / 2;
|
|
s >>= Limb.bit_count / 2;
|
|
testing.expect(a.limbs[i] == result);
|
|
}
|
|
}
|
|
|
|
test "big.int comptime_int set negative" {
|
|
var a = try Int.initSet(al, -10);
|
|
|
|
testing.expect(a.limbs[0] == 10);
|
|
testing.expect(a.isPositive() == false);
|
|
}
|
|
|
|
test "big.int int set unaligned small" {
|
|
var a = try Int.initSet(al, u7(45));
|
|
|
|
testing.expect(a.limbs[0] == 45);
|
|
testing.expect(a.isPositive() == true);
|
|
}
|
|
|
|
test "big.int comptime_int to" {
|
|
const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab);
|
|
|
|
testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
|
|
}
|
|
|
|
test "big.int sub-limb to" {
|
|
const a = try Int.initSet(al, 10);
|
|
|
|
testing.expect((try a.to(u8)) == 10);
|
|
}
|
|
|
|
test "big.int to target too small error" {
|
|
const a = try Int.initSet(al, 0xffffffff);
|
|
|
|
testing.expectError(error.TargetTooSmall, a.to(u8));
|
|
}
|
|
|
|
test "big.int normalize" {
|
|
var a = try Int.init(al);
|
|
try a.ensureCapacity(8);
|
|
|
|
a.limbs[0] = 1;
|
|
a.limbs[1] = 2;
|
|
a.limbs[2] = 3;
|
|
a.limbs[3] = 0;
|
|
a.normalize(4);
|
|
testing.expect(a.len() == 3);
|
|
|
|
a.limbs[0] = 1;
|
|
a.limbs[1] = 2;
|
|
a.limbs[2] = 3;
|
|
a.normalize(3);
|
|
testing.expect(a.len() == 3);
|
|
|
|
a.limbs[0] = 0;
|
|
a.limbs[1] = 0;
|
|
a.normalize(2);
|
|
testing.expect(a.len() == 1);
|
|
|
|
a.limbs[0] = 0;
|
|
a.normalize(1);
|
|
testing.expect(a.len() == 1);
|
|
}
|
|
|
|
test "big.int normalize multi" {
|
|
var a = try Int.init(al);
|
|
try a.ensureCapacity(8);
|
|
|
|
a.limbs[0] = 1;
|
|
a.limbs[1] = 2;
|
|
a.limbs[2] = 0;
|
|
a.limbs[3] = 0;
|
|
a.normalize(4);
|
|
testing.expect(a.len() == 2);
|
|
|
|
a.limbs[0] = 1;
|
|
a.limbs[1] = 2;
|
|
a.limbs[2] = 3;
|
|
a.normalize(3);
|
|
testing.expect(a.len() == 3);
|
|
|
|
a.limbs[0] = 0;
|
|
a.limbs[1] = 0;
|
|
a.limbs[2] = 0;
|
|
a.limbs[3] = 0;
|
|
a.normalize(4);
|
|
testing.expect(a.len() == 1);
|
|
|
|
a.limbs[0] = 0;
|
|
a.normalize(1);
|
|
testing.expect(a.len() == 1);
|
|
}
|
|
|
|
test "big.int parity" {
|
|
var a = try Int.init(al);
|
|
try a.set(0);
|
|
testing.expect(a.isEven());
|
|
testing.expect(!a.isOdd());
|
|
|
|
try a.set(7);
|
|
testing.expect(!a.isEven());
|
|
testing.expect(a.isOdd());
|
|
}
|
|
|
|
test "big.int bitcount + sizeInBase" {
|
|
var a = try Int.init(al);
|
|
|
|
try a.set(0b100);
|
|
testing.expect(a.bitCountAbs() == 3);
|
|
testing.expect(a.sizeInBase(2) >= 3);
|
|
testing.expect(a.sizeInBase(10) >= 1);
|
|
|
|
a.negate();
|
|
testing.expect(a.bitCountAbs() == 3);
|
|
testing.expect(a.sizeInBase(2) >= 4);
|
|
testing.expect(a.sizeInBase(10) >= 2);
|
|
|
|
try a.set(0xffffffff);
|
|
testing.expect(a.bitCountAbs() == 32);
|
|
testing.expect(a.sizeInBase(2) >= 32);
|
|
testing.expect(a.sizeInBase(10) >= 10);
|
|
|
|
try a.shiftLeft(a, 5000);
|
|
testing.expect(a.bitCountAbs() == 5032);
|
|
testing.expect(a.sizeInBase(2) >= 5032);
|
|
a.setSign(false);
|
|
|
|
testing.expect(a.bitCountAbs() == 5032);
|
|
testing.expect(a.sizeInBase(2) >= 5033);
|
|
}
|
|
|
|
test "big.int bitcount/to" {
|
|
var a = try Int.init(al);
|
|
|
|
try a.set(0);
|
|
testing.expect(a.bitCountTwosComp() == 0);
|
|
|
|
testing.expect((try a.to(u0)) == 0);
|
|
testing.expect((try a.to(i0)) == 0);
|
|
|
|
try a.set(-1);
|
|
testing.expect(a.bitCountTwosComp() == 1);
|
|
testing.expect((try a.to(i1)) == -1);
|
|
|
|
try a.set(-8);
|
|
testing.expect(a.bitCountTwosComp() == 4);
|
|
testing.expect((try a.to(i4)) == -8);
|
|
|
|
try a.set(127);
|
|
testing.expect(a.bitCountTwosComp() == 7);
|
|
testing.expect((try a.to(u7)) == 127);
|
|
|
|
try a.set(-128);
|
|
testing.expect(a.bitCountTwosComp() == 8);
|
|
testing.expect((try a.to(i8)) == -128);
|
|
|
|
try a.set(-129);
|
|
testing.expect(a.bitCountTwosComp() == 9);
|
|
testing.expect((try a.to(i9)) == -129);
|
|
}
|
|
|
|
test "big.int fits" {
|
|
var a = try Int.init(al);
|
|
|
|
try a.set(0);
|
|
testing.expect(a.fits(u0));
|
|
testing.expect(a.fits(i0));
|
|
|
|
try a.set(255);
|
|
testing.expect(!a.fits(u0));
|
|
testing.expect(!a.fits(u1));
|
|
testing.expect(!a.fits(i8));
|
|
testing.expect(a.fits(u8));
|
|
testing.expect(a.fits(u9));
|
|
testing.expect(a.fits(i9));
|
|
|
|
try a.set(-128);
|
|
testing.expect(!a.fits(i7));
|
|
testing.expect(a.fits(i8));
|
|
testing.expect(a.fits(i9));
|
|
testing.expect(!a.fits(u9));
|
|
|
|
try a.set(0x1ffffffffeeeeeeee);
|
|
testing.expect(!a.fits(u32));
|
|
testing.expect(!a.fits(u64));
|
|
testing.expect(a.fits(u65));
|
|
}
|
|
|
|
test "big.int string set" {
|
|
var a = try Int.init(al);
|
|
try a.setString(10, "120317241209124781241290847124");
|
|
|
|
testing.expect((try a.to(u128)) == 120317241209124781241290847124);
|
|
}
|
|
|
|
test "big.int string negative" {
|
|
var a = try Int.init(al);
|
|
try a.setString(10, "-1023");
|
|
testing.expect((try a.to(i32)) == -1023);
|
|
}
|
|
|
|
test "big.int string set bad char error" {
|
|
var a = try Int.init(al);
|
|
testing.expectError(error.InvalidCharForDigit, a.setString(10, "x"));
|
|
}
|
|
|
|
test "big.int string set bad base error" {
|
|
var a = try Int.init(al);
|
|
testing.expectError(error.InvalidBase, a.setString(45, "10"));
|
|
}
|
|
|
|
test "big.int string to" {
|
|
const a = try Int.initSet(al, 120317241209124781241290847124);
|
|
|
|
const as = try a.toString(al, 10);
|
|
const es = "120317241209124781241290847124";
|
|
|
|
testing.expect(mem.eql(u8, as, es));
|
|
}
|
|
|
|
test "big.int string to base base error" {
|
|
const a = try Int.initSet(al, 0xffffffff);
|
|
|
|
testing.expectError(error.InvalidBase, a.toString(al, 45));
|
|
}
|
|
|
|
test "big.int string to base 2" {
|
|
const a = try Int.initSet(al, -0b1011);
|
|
|
|
const as = try a.toString(al, 2);
|
|
const es = "-1011";
|
|
|
|
testing.expect(mem.eql(u8, as, es));
|
|
}
|
|
|
|
test "big.int string to base 16" {
|
|
const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab);
|
|
|
|
const as = try a.toString(al, 16);
|
|
const es = "efffffff00000001eeeeeeefaaaaaaab";
|
|
|
|
testing.expect(mem.eql(u8, as, es));
|
|
}
|
|
|
|
test "big.int neg string to" {
|
|
const a = try Int.initSet(al, -123907434);
|
|
|
|
const as = try a.toString(al, 10);
|
|
const es = "-123907434";
|
|
|
|
testing.expect(mem.eql(u8, as, es));
|
|
}
|
|
|
|
test "big.int zero string to" {
|
|
const a = try Int.initSet(al, 0);
|
|
|
|
const as = try a.toString(al, 10);
|
|
const es = "0";
|
|
|
|
testing.expect(mem.eql(u8, as, es));
|
|
}
|
|
|
|
test "big.int clone" {
|
|
var a = try Int.initSet(al, 1234);
|
|
const b = try a.clone();
|
|
|
|
testing.expect((try a.to(u32)) == 1234);
|
|
testing.expect((try b.to(u32)) == 1234);
|
|
|
|
try a.set(77);
|
|
testing.expect((try a.to(u32)) == 77);
|
|
testing.expect((try b.to(u32)) == 1234);
|
|
}
|
|
|
|
test "big.int swap" {
|
|
var a = try Int.initSet(al, 1234);
|
|
var b = try Int.initSet(al, 5678);
|
|
|
|
testing.expect((try a.to(u32)) == 1234);
|
|
testing.expect((try b.to(u32)) == 5678);
|
|
|
|
a.swap(&b);
|
|
|
|
testing.expect((try a.to(u32)) == 5678);
|
|
testing.expect((try b.to(u32)) == 1234);
|
|
}
|
|
|
|
test "big.int to negative" {
|
|
var a = try Int.initSet(al, -10);
|
|
|
|
testing.expect((try a.to(i32)) == -10);
|
|
}
|
|
|
|
test "big.int compare" {
|
|
var a = try Int.initSet(al, -11);
|
|
var b = try Int.initSet(al, 10);
|
|
|
|
testing.expect(a.cmpAbs(b) == 1);
|
|
testing.expect(a.cmp(b) == -1);
|
|
}
|
|
|
|
test "big.int compare similar" {
|
|
var a = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeee);
|
|
var b = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeef);
|
|
|
|
testing.expect(a.cmpAbs(b) == -1);
|
|
testing.expect(b.cmpAbs(a) == 1);
|
|
}
|
|
|
|
test "big.int compare different limb size" {
|
|
var a = try Int.initSet(al, maxInt(Limb) + 1);
|
|
var b = try Int.initSet(al, 1);
|
|
|
|
testing.expect(a.cmpAbs(b) == 1);
|
|
testing.expect(b.cmpAbs(a) == -1);
|
|
}
|
|
|
|
test "big.int compare multi-limb" {
|
|
var a = try Int.initSet(al, -0x7777777799999999ffffeeeeffffeeeeffffeeeef);
|
|
var b = try Int.initSet(al, 0x7777777799999999ffffeeeeffffeeeeffffeeeee);
|
|
|
|
testing.expect(a.cmpAbs(b) == 1);
|
|
testing.expect(a.cmp(b) == -1);
|
|
}
|
|
|
|
test "big.int equality" {
|
|
var a = try Int.initSet(al, 0xffffffff1);
|
|
var b = try Int.initSet(al, -0xffffffff1);
|
|
|
|
testing.expect(a.eqAbs(b));
|
|
testing.expect(!a.eq(b));
|
|
}
|
|
|
|
test "big.int abs" {
|
|
var a = try Int.initSet(al, -5);
|
|
|
|
a.abs();
|
|
testing.expect((try a.to(u32)) == 5);
|
|
|
|
a.abs();
|
|
testing.expect((try a.to(u32)) == 5);
|
|
}
|
|
|
|
test "big.int negate" {
|
|
var a = try Int.initSet(al, 5);
|
|
|
|
a.negate();
|
|
testing.expect((try a.to(i32)) == -5);
|
|
|
|
a.negate();
|
|
testing.expect((try a.to(i32)) == 5);
|
|
}
|
|
|
|
test "big.int add single-single" {
|
|
var a = try Int.initSet(al, 50);
|
|
var b = try Int.initSet(al, 5);
|
|
|
|
var c = try Int.init(al);
|
|
try c.add(a, b);
|
|
|
|
testing.expect((try c.to(u32)) == 55);
|
|
}
|
|
|
|
test "big.int add multi-single" {
|
|
var a = try Int.initSet(al, maxInt(Limb) + 1);
|
|
var b = try Int.initSet(al, 1);
|
|
|
|
var c = try Int.init(al);
|
|
|
|
try c.add(a, b);
|
|
testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
|
|
|
|
try c.add(b, a);
|
|
testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
|
|
}
|
|
|
|
test "big.int add multi-multi" {
|
|
const op1 = 0xefefefef7f7f7f7f;
|
|
const op2 = 0xfefefefe9f9f9f9f;
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, op2);
|
|
|
|
var c = try Int.init(al);
|
|
try c.add(a, b);
|
|
|
|
testing.expect((try c.to(u128)) == op1 + op2);
|
|
}
|
|
|
|
test "big.int add zero-zero" {
|
|
var a = try Int.initSet(al, 0);
|
|
var b = try Int.initSet(al, 0);
|
|
|
|
var c = try Int.init(al);
|
|
try c.add(a, b);
|
|
|
|
testing.expect((try c.to(u32)) == 0);
|
|
}
|
|
|
|
test "big.int add alias multi-limb nonzero-zero" {
|
|
const op1 = 0xffffffff777777771;
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, 0);
|
|
|
|
try a.add(a, b);
|
|
|
|
testing.expect((try a.to(u128)) == op1);
|
|
}
|
|
|
|
test "big.int add sign" {
|
|
var a = try Int.init(al);
|
|
|
|
const one = try Int.initSet(al, 1);
|
|
const two = try Int.initSet(al, 2);
|
|
const neg_one = try Int.initSet(al, -1);
|
|
const neg_two = try Int.initSet(al, -2);
|
|
|
|
try a.add(one, two);
|
|
testing.expect((try a.to(i32)) == 3);
|
|
|
|
try a.add(neg_one, two);
|
|
testing.expect((try a.to(i32)) == 1);
|
|
|
|
try a.add(one, neg_two);
|
|
testing.expect((try a.to(i32)) == -1);
|
|
|
|
try a.add(neg_one, neg_two);
|
|
testing.expect((try a.to(i32)) == -3);
|
|
}
|
|
|
|
test "big.int sub single-single" {
|
|
var a = try Int.initSet(al, 50);
|
|
var b = try Int.initSet(al, 5);
|
|
|
|
var c = try Int.init(al);
|
|
try c.sub(a, b);
|
|
|
|
testing.expect((try c.to(u32)) == 45);
|
|
}
|
|
|
|
test "big.int sub multi-single" {
|
|
var a = try Int.initSet(al, maxInt(Limb) + 1);
|
|
var b = try Int.initSet(al, 1);
|
|
|
|
var c = try Int.init(al);
|
|
try c.sub(a, b);
|
|
|
|
testing.expect((try c.to(Limb)) == maxInt(Limb));
|
|
}
|
|
|
|
test "big.int sub multi-multi" {
|
|
const op1 = 0xefefefefefefefefefefefef;
|
|
const op2 = 0xabababababababababababab;
|
|
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, op2);
|
|
|
|
var c = try Int.init(al);
|
|
try c.sub(a, b);
|
|
|
|
testing.expect((try c.to(u128)) == op1 - op2);
|
|
}
|
|
|
|
test "big.int sub equal" {
|
|
var a = try Int.initSet(al, 0x11efefefefefefefefefefefef);
|
|
var b = try Int.initSet(al, 0x11efefefefefefefefefefefef);
|
|
|
|
var c = try Int.init(al);
|
|
try c.sub(a, b);
|
|
|
|
testing.expect((try c.to(u32)) == 0);
|
|
}
|
|
|
|
test "big.int sub sign" {
|
|
var a = try Int.init(al);
|
|
|
|
const one = try Int.initSet(al, 1);
|
|
const two = try Int.initSet(al, 2);
|
|
const neg_one = try Int.initSet(al, -1);
|
|
const neg_two = try Int.initSet(al, -2);
|
|
|
|
try a.sub(one, two);
|
|
testing.expect((try a.to(i32)) == -1);
|
|
|
|
try a.sub(neg_one, two);
|
|
testing.expect((try a.to(i32)) == -3);
|
|
|
|
try a.sub(one, neg_two);
|
|
testing.expect((try a.to(i32)) == 3);
|
|
|
|
try a.sub(neg_one, neg_two);
|
|
testing.expect((try a.to(i32)) == 1);
|
|
|
|
try a.sub(neg_two, neg_one);
|
|
testing.expect((try a.to(i32)) == -1);
|
|
}
|
|
|
|
test "big.int mul single-single" {
|
|
var a = try Int.initSet(al, 50);
|
|
var b = try Int.initSet(al, 5);
|
|
|
|
var c = try Int.init(al);
|
|
try c.mul(a, b);
|
|
|
|
testing.expect((try c.to(u64)) == 250);
|
|
}
|
|
|
|
test "big.int mul multi-single" {
|
|
var a = try Int.initSet(al, maxInt(Limb));
|
|
var b = try Int.initSet(al, 2);
|
|
|
|
var c = try Int.init(al);
|
|
try c.mul(a, b);
|
|
|
|
testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb));
|
|
}
|
|
|
|
test "big.int mul multi-multi" {
|
|
const op1 = 0x998888efefefefefefefef;
|
|
const op2 = 0x333000abababababababab;
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, op2);
|
|
|
|
var c = try Int.init(al);
|
|
try c.mul(a, b);
|
|
|
|
testing.expect((try c.to(u256)) == op1 * op2);
|
|
}
|
|
|
|
test "big.int mul alias r with a" {
|
|
var a = try Int.initSet(al, maxInt(Limb));
|
|
var b = try Int.initSet(al, 2);
|
|
|
|
try a.mul(a, b);
|
|
|
|
testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
|
|
}
|
|
|
|
test "big.int mul alias r with b" {
|
|
var a = try Int.initSet(al, maxInt(Limb));
|
|
var b = try Int.initSet(al, 2);
|
|
|
|
try a.mul(b, a);
|
|
|
|
testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
|
|
}
|
|
|
|
test "big.int mul alias r with a and b" {
|
|
var a = try Int.initSet(al, maxInt(Limb));
|
|
|
|
try a.mul(a, a);
|
|
|
|
testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb));
|
|
}
|
|
|
|
test "big.int mul a*0" {
|
|
var a = try Int.initSet(al, 0xefefefefefefefef);
|
|
var b = try Int.initSet(al, 0);
|
|
|
|
var c = try Int.init(al);
|
|
try c.mul(a, b);
|
|
|
|
testing.expect((try c.to(u32)) == 0);
|
|
}
|
|
|
|
test "big.int mul 0*0" {
|
|
var a = try Int.initSet(al, 0);
|
|
var b = try Int.initSet(al, 0);
|
|
|
|
var c = try Int.init(al);
|
|
try c.mul(a, b);
|
|
|
|
testing.expect((try c.to(u32)) == 0);
|
|
}
|
|
|
|
test "big.int div single-single no rem" {
|
|
var a = try Int.initSet(al, 50);
|
|
var b = try Int.initSet(al, 5);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u32)) == 10);
|
|
testing.expect((try r.to(u32)) == 0);
|
|
}
|
|
|
|
test "big.int div single-single with rem" {
|
|
var a = try Int.initSet(al, 49);
|
|
var b = try Int.initSet(al, 5);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u32)) == 9);
|
|
testing.expect((try r.to(u32)) == 4);
|
|
}
|
|
|
|
test "big.int div multi-single no rem" {
|
|
const op1 = 0xffffeeeeddddcccc;
|
|
const op2 = 34;
|
|
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, op2);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u64)) == op1 / op2);
|
|
testing.expect((try r.to(u64)) == 0);
|
|
}
|
|
|
|
test "big.int div multi-single with rem" {
|
|
const op1 = 0xffffeeeeddddcccf;
|
|
const op2 = 34;
|
|
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, op2);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u64)) == op1 / op2);
|
|
testing.expect((try r.to(u64)) == 3);
|
|
}
|
|
|
|
test "big.int div multi>2-single" {
|
|
const op1 = 0xfefefefefefefefefefefefefefefefe;
|
|
const op2 = 0xefab8;
|
|
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, op2);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == op1 / op2);
|
|
testing.expect((try r.to(u32)) == 0x3e4e);
|
|
}
|
|
|
|
test "big.int div single-single q < r" {
|
|
var a = try Int.initSet(al, 0x0078f432);
|
|
var b = try Int.initSet(al, 0x01000000);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u64)) == 0);
|
|
testing.expect((try r.to(u64)) == 0x0078f432);
|
|
}
|
|
|
|
test "big.int div single-single q == r" {
|
|
var a = try Int.initSet(al, 10);
|
|
var b = try Int.initSet(al, 10);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u64)) == 1);
|
|
testing.expect((try r.to(u64)) == 0);
|
|
}
|
|
|
|
test "big.int div q=0 alias" {
|
|
var a = try Int.initSet(al, 3);
|
|
var b = try Int.initSet(al, 10);
|
|
|
|
try Int.divTrunc(&a, &b, a, b);
|
|
|
|
testing.expect((try a.to(u64)) == 0);
|
|
testing.expect((try b.to(u64)) == 3);
|
|
}
|
|
|
|
test "big.int div multi-multi q < r" {
|
|
const op1 = 0x1ffffffff0078f432;
|
|
const op2 = 0x1ffffffff01000000;
|
|
var a = try Int.initSet(al, op1);
|
|
var b = try Int.initSet(al, op2);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == 0);
|
|
testing.expect((try r.to(u128)) == op1);
|
|
}
|
|
|
|
test "big.int div trunc single-single +/+" {
|
|
const u: i32 = 5;
|
|
const v: i32 = 3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// 5 = 1 * 3 + 2
|
|
const eq = @divTrunc(u, v);
|
|
const er = @mod(u, v);
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div trunc single-single -/+" {
|
|
const u: i32 = -5;
|
|
const v: i32 = 3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// -5 = 1 * -3 - 2
|
|
const eq = -1;
|
|
const er = -2;
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div trunc single-single +/-" {
|
|
const u: i32 = 5;
|
|
const v: i32 = -3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// 5 = -1 * -3 + 2
|
|
const eq = -1;
|
|
const er = 2;
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div trunc single-single -/-" {
|
|
const u: i32 = -5;
|
|
const v: i32 = -3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// -5 = 1 * -3 - 2
|
|
const eq = 1;
|
|
const er = -2;
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div floor single-single +/+" {
|
|
const u: i32 = 5;
|
|
const v: i32 = 3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divFloor(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// 5 = 1 * 3 + 2
|
|
const eq = 1;
|
|
const er = 2;
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div floor single-single -/+" {
|
|
const u: i32 = -5;
|
|
const v: i32 = 3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divFloor(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// -5 = -2 * 3 + 1
|
|
const eq = -2;
|
|
const er = 1;
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div floor single-single +/-" {
|
|
const u: i32 = 5;
|
|
const v: i32 = -3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divFloor(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// 5 = -2 * -3 - 1
|
|
const eq = -2;
|
|
const er = -1;
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div floor single-single -/-" {
|
|
const u: i32 = -5;
|
|
const v: i32 = -3;
|
|
|
|
var a = try Int.initSet(al, u);
|
|
var b = try Int.initSet(al, v);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divFloor(&q, &r, a, b);
|
|
|
|
// n = q * d + r
|
|
// -5 = 2 * -3 + 1
|
|
const eq = 1;
|
|
const er = -2;
|
|
|
|
testing.expect((try q.to(i32)) == eq);
|
|
testing.expect((try r.to(i32)) == er);
|
|
}
|
|
|
|
test "big.int div multi-multi with rem" {
|
|
var a = try Int.initSet(al, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
|
|
var b = try Int.initSet(al, 0x99990000111122223333);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
|
|
testing.expect((try r.to(u128)) == 0x28de0acacd806823638);
|
|
}
|
|
|
|
test "big.int div multi-multi no rem" {
|
|
var a = try Int.initSet(al, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);
|
|
var b = try Int.initSet(al, 0x99990000111122223333);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
|
|
testing.expect((try r.to(u128)) == 0);
|
|
}
|
|
|
|
test "big.int div multi-multi (2 branch)" {
|
|
var a = try Int.initSet(al, 0x866666665555555588888887777777761111111111111111);
|
|
var b = try Int.initSet(al, 0x86666666555555554444444433333333);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == 0x10000000000000000);
|
|
testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);
|
|
}
|
|
|
|
test "big.int div multi-multi (3.1/3.3 branch)" {
|
|
var a = try Int.initSet(al, 0x11111111111111111111111111111111111111111111111111111111111111);
|
|
var b = try Int.initSet(al, 0x1111111111111111111111111111111111111111171);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == 0xfffffffffffffffffff);
|
|
testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
|
|
}
|
|
|
|
test "big.int div multi-single zero-limb trailing" {
|
|
var a = try Int.initSet(al, 0x60000000000000000000000000000000000000000000000000000000000000000);
|
|
var b = try Int.initSet(al, 0x10000000000000000);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
var expected = try Int.initSet(al, 0x6000000000000000000000000000000000000000000000000);
|
|
testing.expect(q.eq(expected));
|
|
testing.expect(r.eqZero());
|
|
}
|
|
|
|
test "big.int div multi-multi zero-limb trailing (with rem)" {
|
|
var a = try Int.initSet(al, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
|
|
var b = try Int.initSet(al, 0x8666666655555555444444443333333300000000000000000000000000000000);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == 0x10000000000000000);
|
|
|
|
const rs = try r.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000"));
|
|
}
|
|
|
|
test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {
|
|
var a = try Int.initSet(al, 0x8666666655555555888888877777777611111111111111110000000000000000);
|
|
var b = try Int.initSet(al, 0x8666666655555555444444443333333300000000000000000000000000000000);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
testing.expect((try q.to(u128)) == 0x1);
|
|
|
|
const rs = try r.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000"));
|
|
}
|
|
|
|
test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {
|
|
var a = try Int.initSet(al, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
|
|
var b = try Int.initSet(al, 0x866666665555555544444444333333330000000000000000);
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
const qs = try q.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f"));
|
|
|
|
const rs = try r.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000"));
|
|
}
|
|
|
|
test "big.int div multi-multi fuzz case #1" {
|
|
var a = try Int.init(al);
|
|
var b = try Int.init(al);
|
|
|
|
try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
|
try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff");
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
const qs = try q.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1"));
|
|
|
|
const rs = try r.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1"));
|
|
}
|
|
|
|
test "big.int div multi-multi fuzz case #2" {
|
|
var a = try Int.init(al);
|
|
var b = try Int.init(al);
|
|
|
|
try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000");
|
|
try b.setString(16, "ffc0000000000000000000000000000000000000000000000000");
|
|
|
|
var q = try Int.init(al);
|
|
var r = try Int.init(al);
|
|
try Int.divTrunc(&q, &r, a, b);
|
|
|
|
const qs = try q.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4"));
|
|
|
|
const rs = try r.toString(al, 16);
|
|
testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
|
|
}
|
|
|
|
test "big.int shift-right single" {
|
|
var a = try Int.initSet(al, 0xffff0000);
|
|
try a.shiftRight(a, 16);
|
|
|
|
testing.expect((try a.to(u32)) == 0xffff);
|
|
}
|
|
|
|
test "big.int shift-right multi" {
|
|
var a = try Int.initSet(al, 0xffff0000eeee1111dddd2222cccc3333);
|
|
try a.shiftRight(a, 67);
|
|
|
|
testing.expect((try a.to(u64)) == 0x1fffe0001dddc222);
|
|
}
|
|
|
|
test "big.int shift-left single" {
|
|
var a = try Int.initSet(al, 0xffff);
|
|
try a.shiftLeft(a, 16);
|
|
|
|
testing.expect((try a.to(u64)) == 0xffff0000);
|
|
}
|
|
|
|
test "big.int shift-left multi" {
|
|
var a = try Int.initSet(al, 0x1fffe0001dddc222);
|
|
try a.shiftLeft(a, 67);
|
|
|
|
testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000);
|
|
}
|
|
|
|
test "big.int shift-right negative" {
|
|
var a = try Int.init(al);
|
|
|
|
try a.shiftRight(try Int.initSet(al, -20), 2);
|
|
testing.expect((try a.to(i32)) == -20 >> 2);
|
|
|
|
try a.shiftRight(try Int.initSet(al, -5), 10);
|
|
testing.expect((try a.to(i32)) == -5 >> 10);
|
|
}
|
|
|
|
test "big.int shift-left negative" {
|
|
var a = try Int.init(al);
|
|
|
|
try a.shiftRight(try Int.initSet(al, -10), 1232);
|
|
testing.expect((try a.to(i32)) == -10 >> 1232);
|
|
}
|
|
|
|
test "big.int bitwise and simple" {
|
|
var a = try Int.initSet(al, 0xffffffff11111111);
|
|
var b = try Int.initSet(al, 0xeeeeeeee22222222);
|
|
|
|
try a.bitAnd(a, b);
|
|
|
|
testing.expect((try a.to(u64)) == 0xeeeeeeee00000000);
|
|
}
|
|
|
|
test "big.int bitwise and multi-limb" {
|
|
var a = try Int.initSet(al, maxInt(Limb) + 1);
|
|
var b = try Int.initSet(al, maxInt(Limb));
|
|
|
|
try a.bitAnd(a, b);
|
|
|
|
testing.expect((try a.to(u128)) == 0);
|
|
}
|
|
|
|
test "big.int bitwise xor simple" {
|
|
var a = try Int.initSet(al, 0xffffffff11111111);
|
|
var b = try Int.initSet(al, 0xeeeeeeee22222222);
|
|
|
|
try a.bitXor(a, b);
|
|
|
|
testing.expect((try a.to(u64)) == 0x1111111133333333);
|
|
}
|
|
|
|
test "big.int bitwise xor multi-limb" {
|
|
var a = try Int.initSet(al, maxInt(Limb) + 1);
|
|
var b = try Int.initSet(al, maxInt(Limb));
|
|
|
|
try a.bitXor(a, b);
|
|
|
|
testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb));
|
|
}
|
|
|
|
test "big.int bitwise or simple" {
|
|
var a = try Int.initSet(al, 0xffffffff11111111);
|
|
var b = try Int.initSet(al, 0xeeeeeeee22222222);
|
|
|
|
try a.bitOr(a, b);
|
|
|
|
testing.expect((try a.to(u64)) == 0xffffffff33333333);
|
|
}
|
|
|
|
test "big.int bitwise or multi-limb" {
|
|
var a = try Int.initSet(al, maxInt(Limb) + 1);
|
|
var b = try Int.initSet(al, maxInt(Limb));
|
|
|
|
try a.bitOr(a, b);
|
|
|
|
// TODO: big.int.cpp or is wrong on multi-limb.
|
|
testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
|
|
}
|
|
|
|
test "big.int var args" {
|
|
var a = try Int.initSet(al, 5);
|
|
|
|
try a.add(a, try Int.initSet(al, 6));
|
|
testing.expect((try a.to(u64)) == 11);
|
|
|
|
testing.expect(a.cmp(try Int.initSet(al, 11)) == 0);
|
|
testing.expect(a.cmp(try Int.initSet(al, 14)) <= 0);
|
|
}
|