1644 lines
55 KiB
Plaintext
1644 lines
55 KiB
Plaintext
// Expect retcode = 0
|
||
|
||
// 多精度整数模块:BigInt 表示数学整数,Bits 表示任意正位宽的位串。
|
||
// limbs 为小端 u32;BigInt 的零规范为 len=0 且 negative=false。
|
||
// 普通赋值只借用同一存储;深复制使用 clone,所有权转移使用 take。
|
||
type .{ .Error, .Status } = @import("error.spl");
|
||
type .{ .checked_mul } = @import("memory.spl");
|
||
type .{ .digit_char, .digit_value } = @import("ascii.spl");
|
||
type .{ .ByteBuffer } = @import("byte_buffer.spl");
|
||
type .{ .alloc, .copy, .free } = @import("../sys/mem.spl");
|
||
|
||
type BigInt = @self;
|
||
|
||
limbs: *u32,
|
||
len: usize,
|
||
cap: usize,
|
||
negative: bool,
|
||
|
||
fn zero(): BigInt {
|
||
ret BigInt { .limbs = null, .len = 0, .cap = 0, .negative = false };
|
||
}
|
||
|
||
fn valid(self: BigInt): bool {
|
||
if self.len > self.cap { ret false; }
|
||
if self.cap == 0 {
|
||
ret self.limbs == null && self.len == 0 && !self.negative;
|
||
}
|
||
if self.limbs == null { ret false; }
|
||
if self.len == 0 { ret !self.negative; }
|
||
ret self.limbs[self.len - 1] != 0;
|
||
}
|
||
|
||
fn is_zero(self: BigInt): bool {
|
||
ret self.len == 0;
|
||
}
|
||
|
||
fn is_negative(self: BigInt): bool {
|
||
ret self.negative;
|
||
}
|
||
|
||
fn normalize(self: *BigInt) {
|
||
while self.len > 0 {
|
||
if self.limbs[self.len - 1] != 0 { break; }
|
||
self.len = self.len - 1;
|
||
}
|
||
if self.len == 0 { self.negative = false; }
|
||
}
|
||
|
||
fn reserve(self: *BigInt, need: usize): Status {
|
||
if !valid(self.*) { ret .Err(Error.make(.InvalidArgument)); }
|
||
if need <= self.cap { ret .Ok; }
|
||
|
||
var new_cap = self.cap;
|
||
if new_cap < 2 { new_cap = 2; }
|
||
while new_cap < need {
|
||
if new_cap > (~(0 as usize)) / 2 {
|
||
new_cap = need;
|
||
break;
|
||
}
|
||
new_cap = new_cap * 2;
|
||
}
|
||
|
||
var byte_count: usize = 0;
|
||
match checked_mul(new_cap, @sizeof(u32)) {
|
||
.Ok(value) => { byte_count = value; },
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
var fresh = alloc(byte_count) as *u32;
|
||
if fresh == null {
|
||
ret .Err(Error.make(.Overflow));
|
||
}
|
||
if self.len != 0 {
|
||
copy(fresh as *any, self.limbs as *any, self.len * @sizeof(u32));
|
||
}
|
||
free(self.limbs as *any);
|
||
self.limbs = fresh;
|
||
self.cap = new_cap;
|
||
ret .Ok;
|
||
}
|
||
|
||
fn drop(self: *BigInt) {
|
||
free(self.limbs as *any);
|
||
self.* = zero();
|
||
}
|
||
|
||
fn take(source: *BigInt): BigInt {
|
||
var result = source.*;
|
||
source.* = zero();
|
||
ret result;
|
||
}
|
||
|
||
fn replace(out: *BigInt, fresh: *BigInt) {
|
||
drop(out);
|
||
out.* = take(fresh);
|
||
}
|
||
|
||
fn clone(out: *BigInt, source: BigInt): Status {
|
||
if !valid(out.*) || !valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
var fresh = zero();
|
||
match reserve(&fresh, source.len) {
|
||
.Ok => {},
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
if source.len != 0 {
|
||
copy(fresh.limbs as *any, source.limbs as *any, source.len * @sizeof(u32));
|
||
}
|
||
fresh.len = source.len;
|
||
fresh.negative = source.negative;
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn from_u64(out: *BigInt, value: u64): Status {
|
||
if !valid(out.*) { ret .Err(Error.make(.InvalidArgument)); }
|
||
var fresh = zero();
|
||
if value != 0 {
|
||
match reserve(&fresh, 2) {
|
||
.Ok => {},
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
fresh.limbs[0] = value as u32;
|
||
fresh.limbs[1] = (value >> 32) as u32;
|
||
fresh.len = 1;
|
||
if fresh.limbs[1] != 0 { fresh.len = 2; }
|
||
}
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn from_i64(out: *BigInt, value: i64): Status {
|
||
var magnitude = value as u64;
|
||
var negative = value < 0;
|
||
if negative { magnitude = 0 - magnitude; }
|
||
match from_u64(out, magnitude) {
|
||
.Ok => {},
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
if out.len != 0 { out.negative = negative; }
|
||
ret .Ok;
|
||
}
|
||
|
||
fn cmp_abs(a: BigInt, b: BigInt): i32 {
|
||
if a.len < b.len { ret -1; }
|
||
if a.len > b.len { ret 1; }
|
||
var i = a.len;
|
||
while i > 0 {
|
||
i = i - 1;
|
||
if a.limbs[i] < b.limbs[i] { ret -1; }
|
||
if a.limbs[i] > b.limbs[i] { ret 1; }
|
||
}
|
||
ret 0;
|
||
}
|
||
|
||
fn cmp(a: BigInt, b: BigInt): i32 {
|
||
if a.negative != b.negative {
|
||
if a.negative { ret -1; }
|
||
ret 1;
|
||
}
|
||
var order = cmp_abs(a, b);
|
||
if a.negative { ret 0 - order; }
|
||
ret order;
|
||
}
|
||
|
||
fn eq(a: BigInt, b: BigInt): bool {
|
||
if a.negative != b.negative || a.len != b.len { ret false; }
|
||
var i: usize = 0;
|
||
while i < a.len {
|
||
if a.limbs[i] != b.limbs[i] { ret false; }
|
||
i = i + 1;
|
||
}
|
||
ret true;
|
||
}
|
||
|
||
fn add_abs_into(out: *BigInt, a: BigInt, b: BigInt): Status {
|
||
var count = a.len;
|
||
if b.len > count { count = b.len; }
|
||
if count == ~(0 as usize) { ret .Err(Error.make(.Overflow)); }
|
||
match reserve(out, count + 1) {
|
||
.Ok => {},
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
var carry: u64 = 0;
|
||
var i: usize = 0;
|
||
while i < count {
|
||
var av: u64 = 0;
|
||
var bv: u64 = 0;
|
||
if i < a.len { av = a.limbs[i] as u64; }
|
||
if i < b.len { bv = b.limbs[i] as u64; }
|
||
var sum = av + bv + carry;
|
||
out.limbs[i] = sum as u32;
|
||
carry = sum >> 32;
|
||
i = i + 1;
|
||
}
|
||
if carry != 0 {
|
||
out.limbs[count] = carry as u32;
|
||
count = count + 1;
|
||
}
|
||
out.len = count;
|
||
out.negative = false;
|
||
normalize(out);
|
||
ret .Ok;
|
||
}
|
||
|
||
// 要求 |a| >= |b|。
|
||
fn sub_abs_into(out: *BigInt, a: BigInt, b: BigInt): Status {
|
||
match reserve(out, a.len) {
|
||
.Ok => {},
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
var borrow: u64 = 0;
|
||
var base: u64 = 4294967296; // 2^32
|
||
var i: usize = 0;
|
||
while i < a.len {
|
||
var av = a.limbs[i] as u64;
|
||
var bv: u64 = 0;
|
||
if i < b.len { bv = b.limbs[i] as u64; }
|
||
var subtrahend = bv + borrow;
|
||
if av < subtrahend {
|
||
out.limbs[i] = (base + av - subtrahend) as u32;
|
||
borrow = 1;
|
||
} else {
|
||
out.limbs[i] = (av - subtrahend) as u32;
|
||
borrow = 0;
|
||
}
|
||
i = i + 1;
|
||
}
|
||
out.len = a.len;
|
||
out.negative = false;
|
||
normalize(out);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn add(out: *BigInt, a: BigInt, b: BigInt): Status {
|
||
if !valid(out.*) || !valid(a) || !valid(b) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
var fresh = zero();
|
||
if a.negative == b.negative {
|
||
match add_abs_into(&fresh, a, b) {
|
||
.Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); },
|
||
}
|
||
if fresh.len != 0 { fresh.negative = a.negative; }
|
||
} else {
|
||
var order = cmp_abs(a, b);
|
||
if order >= 0 {
|
||
match sub_abs_into(&fresh, a, b) {
|
||
.Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); },
|
||
}
|
||
if fresh.len != 0 { fresh.negative = a.negative; }
|
||
} else {
|
||
match sub_abs_into(&fresh, b, a) {
|
||
.Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); },
|
||
}
|
||
if fresh.len != 0 { fresh.negative = b.negative; }
|
||
}
|
||
}
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn sub(out: *BigInt, a: BigInt, b: BigInt): Status {
|
||
var neg_b = b;
|
||
if neg_b.len != 0 { neg_b.negative = !neg_b.negative; }
|
||
ret add(out, a, neg_b);
|
||
}
|
||
|
||
fn neg(out: *BigInt, source: BigInt): Status {
|
||
match clone(out, source) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
if out.len != 0 { out.negative = !out.negative; }
|
||
ret .Ok;
|
||
}
|
||
|
||
fn abs(out: *BigInt, source: BigInt): Status {
|
||
match clone(out, source) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
out.negative = false;
|
||
ret .Ok;
|
||
}
|
||
|
||
fn mul(out: *BigInt, a: BigInt, b: BigInt): Status {
|
||
if !valid(out.*) || !valid(a) || !valid(b) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
var fresh = zero();
|
||
if a.len == 0 || b.len == 0 {
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
if b.len > (~(0 as usize)) - a.len {
|
||
ret .Err(Error.make(.Overflow));
|
||
}
|
||
var count = a.len + b.len;
|
||
match reserve(&fresh, count) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
var z: usize = 0;
|
||
while z < count { fresh.limbs[z] = 0; z = z + 1; }
|
||
|
||
var i: usize = 0;
|
||
while i < a.len {
|
||
var carry: u64 = 0;
|
||
var j: usize = 0;
|
||
while j < b.len {
|
||
var at = i + j;
|
||
var product = (a.limbs[i] as u64) * (b.limbs[j] as u64) +
|
||
(fresh.limbs[at] as u64) + carry;
|
||
fresh.limbs[at] = product as u32;
|
||
carry = product >> 32;
|
||
j = j + 1;
|
||
}
|
||
fresh.limbs[i + b.len] = carry as u32;
|
||
i = i + 1;
|
||
}
|
||
fresh.len = count;
|
||
fresh.negative = a.negative != b.negative;
|
||
normalize(&fresh);
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn bit_length(out: *usize, self: BigInt): Status {
|
||
if out == null || !valid(self) { ret .Err(Error.make(.InvalidArgument)); }
|
||
if self.len == 0 { out.* = 0; ret .Ok; }
|
||
if self.len - 1 > (~(0 as usize)) / 32 { ret .Err(Error.make(.Overflow)); }
|
||
var top = self.limbs[self.len - 1];
|
||
var bits = (self.len - 1) * 32;
|
||
while top != 0 {
|
||
bits = bits + 1;
|
||
top = top >> 1;
|
||
}
|
||
out.* = bits;
|
||
ret .Ok;
|
||
}
|
||
|
||
fn mul_pow2(out: *BigInt, source: BigInt, amount: usize): Status {
|
||
if !valid(out.*) || !valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
if source.len == 0 { ret from_u64(out, 0); }
|
||
var words = amount / 32;
|
||
var shift = amount % 32;
|
||
if source.len == ~(0 as usize) || words > (~(0 as usize)) - source.len - 1 {
|
||
ret .Err(Error.make(.Overflow));
|
||
}
|
||
var fresh = zero();
|
||
var count = source.len + words + 1;
|
||
match reserve(&fresh, count) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
var i: usize = 0;
|
||
while i < words { fresh.limbs[i] = 0; i = i + 1; }
|
||
var carry: u64 = 0;
|
||
i = 0;
|
||
while i < source.len {
|
||
var value = ((source.limbs[i] as u64) << shift as u32) | carry;
|
||
fresh.limbs[i + words] = value as u32;
|
||
carry = value >> 32;
|
||
i = i + 1;
|
||
}
|
||
fresh.len = source.len + words;
|
||
if carry != 0 {
|
||
fresh.limbs[fresh.len] = carry as u32;
|
||
fresh.len = fresh.len + 1;
|
||
}
|
||
fresh.negative = source.negative;
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
// 按绝对值右移,等价于向零除以 2^amount。
|
||
fn div_pow2(out: *BigInt, source: BigInt, amount: usize): Status {
|
||
if !valid(out.*) || !valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
var words = amount / 32;
|
||
var shift = amount % 32;
|
||
if words >= source.len { ret from_u64(out, 0); }
|
||
var count = source.len - words;
|
||
var fresh = zero();
|
||
match reserve(&fresh, count) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
var i: usize = 0;
|
||
while i < count {
|
||
var src = i + words;
|
||
var value = (source.limbs[src] as u64) >> shift as u32;
|
||
if shift != 0 && src + 1 < source.len {
|
||
value = value | ((source.limbs[src + 1] << (32 - shift) as u32) as u64);
|
||
}
|
||
fresh.limbs[i] = value as u32;
|
||
i = i + 1;
|
||
}
|
||
fresh.len = count;
|
||
fresh.negative = source.negative;
|
||
normalize(&fresh);
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn shift_one(self: *BigInt) {
|
||
var carry: u64 = 0;
|
||
var i: usize = 0;
|
||
while i < self.len {
|
||
var value = ((self.limbs[i] as u64) << 1) | carry;
|
||
self.limbs[i] = value as u32;
|
||
carry = value >> 32;
|
||
i = i + 1;
|
||
}
|
||
if carry != 0 {
|
||
self.limbs[self.len] = carry as u32;
|
||
self.len = self.len + 1;
|
||
}
|
||
}
|
||
|
||
fn sub_abs_in_place(a: *BigInt, b: BigInt) {
|
||
var borrow: u64 = 0;
|
||
var base: u64 = 4294967296; // 2^32
|
||
var i: usize = 0;
|
||
while i < a.len {
|
||
var av = a.limbs[i] as u64;
|
||
var bv: u64 = 0;
|
||
if i < b.len { bv = b.limbs[i] as u64; }
|
||
var subtrahend = bv + borrow;
|
||
if av < subtrahend {
|
||
a.limbs[i] = (base + av - subtrahend) as u32;
|
||
borrow = 1;
|
||
} else {
|
||
a.limbs[i] = (av - subtrahend) as u32;
|
||
borrow = 0;
|
||
}
|
||
i = i + 1;
|
||
}
|
||
normalize(a);
|
||
}
|
||
|
||
fn div_rem(quotient: *BigInt, remainder: *BigInt, dividend: BigInt,
|
||
divisor: BigInt): Status {
|
||
if (quotient as usize) == (remainder as usize) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
if !valid(quotient.*) || !valid(remainder.*) || !valid(dividend) || !valid(divisor) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
if divisor.len == 0 { ret .Err(Error.make(.DivisionByZero)); }
|
||
|
||
var q = zero();
|
||
var r = zero();
|
||
match reserve(&q, dividend.len) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
if divisor.len == ~(0 as usize) { drop(&q); ret .Err(Error.make(.Overflow)); }
|
||
match reserve(&r, divisor.len + 1) {
|
||
.Ok => {}, .Err(error) => { drop(&q); ret .Err(error); },
|
||
}
|
||
var i: usize = 0;
|
||
while i < dividend.len { q.limbs[i] = 0; i = i + 1; }
|
||
q.len = dividend.len;
|
||
|
||
var bit: usize = 0;
|
||
match bit_length(&bit, dividend) {
|
||
.Ok => {},
|
||
.Err(error) => { drop(&q); drop(&r); ret .Err(error); },
|
||
}
|
||
while bit > 0 {
|
||
bit = bit - 1;
|
||
shift_one(&r);
|
||
var word = bit / 32;
|
||
var offset = bit % 32;
|
||
var incoming = (dividend.limbs[word] >> offset as u32) & 1;
|
||
if incoming != 0 {
|
||
if r.len == 0 { r.len = 1; r.limbs[0] = 0; }
|
||
r.limbs[0] = r.limbs[0] | 1;
|
||
}
|
||
if cmp_abs(r, divisor) >= 0 {
|
||
sub_abs_in_place(&r, divisor);
|
||
q.limbs[word] = q.limbs[word] | ((1 as u32) << offset as u32);
|
||
}
|
||
}
|
||
q.negative = q.len != 0 && dividend.negative != divisor.negative;
|
||
r.negative = r.len != 0 && dividend.negative;
|
||
normalize(&q);
|
||
normalize(&r);
|
||
replace(quotient, &q);
|
||
replace(remainder, &r);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn mul_small(self: *BigInt, value: u32): Status {
|
||
if self.len == 0 || value == 1 { ret .Ok; }
|
||
if value == 0 { self.len = 0; self.negative = false; ret .Ok; }
|
||
if self.len == ~(0 as usize) { ret .Err(Error.make(.Overflow)); }
|
||
match reserve(self, self.len + 1) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
var carry: u64 = 0;
|
||
var i: usize = 0;
|
||
while i < self.len {
|
||
var product = (self.limbs[i] as u64) * (value as u64) + carry;
|
||
self.limbs[i] = product as u32;
|
||
carry = product >> 32;
|
||
i = i + 1;
|
||
}
|
||
if carry != 0 {
|
||
self.limbs[self.len] = carry as u32;
|
||
self.len = self.len + 1;
|
||
}
|
||
ret .Ok;
|
||
}
|
||
|
||
fn add_small(self: *BigInt, value: u32): Status {
|
||
if value == 0 { ret .Ok; }
|
||
if self.len == ~(0 as usize) { ret .Err(Error.make(.Overflow)); }
|
||
match reserve(self, self.len + 1) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
var carry = value as u64;
|
||
var i: usize = 0;
|
||
while carry != 0 && i < self.len {
|
||
var sum = (self.limbs[i] as u64) + carry;
|
||
self.limbs[i] = sum as u32;
|
||
carry = sum >> 32;
|
||
i = i + 1;
|
||
}
|
||
if carry != 0 {
|
||
self.limbs[self.len] = carry as u32;
|
||
self.len = self.len + 1;
|
||
}
|
||
ret .Ok;
|
||
}
|
||
|
||
fn parse(out: *BigInt, bytes: []u8, radix: u8): Status {
|
||
if !valid(out.*) { ret .Err(Error.make(.InvalidArgument)); }
|
||
if radix < 2 || radix > 36 { ret .Err(Error.make(.InvalidArgument)); }
|
||
if bytes.len == 0 { ret .Err(Error.at(.UnexpectedEnd, 0)); }
|
||
if bytes.ptr == null { ret .Err(Error.make(.InvalidArgument)); }
|
||
|
||
var i: usize = 0;
|
||
var negative = false;
|
||
if bytes.ptr[0] == '-' || bytes.ptr[0] == '+' {
|
||
negative = bytes.ptr[0] == '-';
|
||
i = 1;
|
||
}
|
||
if i == bytes.len { ret .Err(Error.at(.UnexpectedEnd, i)); }
|
||
|
||
var fresh = zero();
|
||
while i < bytes.len {
|
||
var digit: u8 = 0;
|
||
match digit_value(bytes.ptr[i], radix) {
|
||
.Ok(value) => { digit = value; },
|
||
.Err(error) => { drop(&fresh); ret .Err(Error.at(.InvalidDigit, i)); },
|
||
}
|
||
match mul_small(&fresh, radix as u32) {
|
||
.Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); },
|
||
}
|
||
match add_small(&fresh, digit as u32) {
|
||
.Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); },
|
||
}
|
||
i = i + 1;
|
||
}
|
||
fresh.negative = fresh.len != 0 && negative;
|
||
replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn div_small(self: *BigInt, radix: u32): u32 {
|
||
var remainder: u64 = 0;
|
||
var i = self.len;
|
||
while i > 0 {
|
||
i = i - 1;
|
||
var current = (remainder << 32) | (self.limbs[i] as u64);
|
||
self.limbs[i] = (current / radix as u64) as u32;
|
||
remainder = current % radix as u64;
|
||
}
|
||
normalize(self);
|
||
ret remainder as u32;
|
||
}
|
||
|
||
fn format(out: *ByteBuffer, source: BigInt, radix: u8, uppercase: bool): Status {
|
||
if !valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
if radix < 2 || radix > 36 { ret .Err(Error.make(.InvalidArgument)); }
|
||
var reverse = ByteBuffer.new();
|
||
var fresh = ByteBuffer.new();
|
||
var magnitude = zero();
|
||
match clone(&magnitude, source) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
magnitude.negative = false;
|
||
if magnitude.len == 0 {
|
||
match ByteBuffer.append_byte(&reverse, '0') {
|
||
.Ok => {}, .Err(error) => { drop(&magnitude); ret .Err(error); },
|
||
}
|
||
}
|
||
while magnitude.len != 0 {
|
||
var remainder = div_small(&magnitude, radix as u32);
|
||
var byte: u8 = 0;
|
||
match digit_char(remainder as u8, uppercase) {
|
||
.Ok(value) => { byte = value; },
|
||
.Err(error) => { drop(&magnitude); ByteBuffer.drop(&reverse); ret .Err(error); },
|
||
}
|
||
match ByteBuffer.append_byte(&reverse, byte) {
|
||
.Ok => {},
|
||
.Err(error) => {
|
||
drop(&magnitude); ByteBuffer.drop(&reverse); ret .Err(error);
|
||
},
|
||
}
|
||
}
|
||
if source.negative {
|
||
match ByteBuffer.append_byte(&fresh, '-') {
|
||
.Ok => {},
|
||
.Err(error) => {
|
||
drop(&magnitude); ByteBuffer.drop(&reverse); ret .Err(error);
|
||
},
|
||
}
|
||
}
|
||
var i = ByteBuffer.size(reverse);
|
||
var digits = ByteBuffer.as_slice(reverse);
|
||
while i > 0 {
|
||
i = i - 1;
|
||
match ByteBuffer.append_byte(&fresh, digits.ptr[i]) {
|
||
.Ok => {},
|
||
.Err(error) => {
|
||
drop(&magnitude); ByteBuffer.drop(&reverse); ByteBuffer.drop(&fresh);
|
||
ret .Err(error);
|
||
},
|
||
}
|
||
}
|
||
drop(&magnitude);
|
||
ByteBuffer.drop(&reverse);
|
||
ByteBuffer.drop(out);
|
||
out.* = ByteBuffer.take(&fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn to_u64(out: *u64, source: BigInt): Status {
|
||
if out == null || !valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
if source.negative || source.len > 2 { ret .Err(Error.make(.Overflow)); }
|
||
var value: u64 = 0;
|
||
if source.len > 0 { value = source.limbs[0] as u64; }
|
||
if source.len > 1 { value = value | ((source.limbs[1] as u64) << 32); }
|
||
out.* = value;
|
||
ret .Ok;
|
||
}
|
||
|
||
fn to_i64(out: *i64, source: BigInt): Status {
|
||
if out == null || !valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
if source.len > 2 { ret .Err(Error.make(.Overflow)); }
|
||
var magnitude: u64 = 0;
|
||
if source.len > 0 { magnitude = source.limbs[0] as u64; }
|
||
if source.len > 1 { magnitude = magnitude | ((source.limbs[1] as u64) << 32); }
|
||
var max_positive: u64 = 9223372036854775807; // i64::MAX
|
||
var min_magnitude: u64 = 9223372036854775808; // |i64::MIN|
|
||
if !source.negative {
|
||
if magnitude > max_positive { ret .Err(Error.make(.Overflow)); }
|
||
out.* = magnitude as i64;
|
||
ret .Ok;
|
||
}
|
||
if magnitude > min_magnitude { ret .Err(Error.make(.Overflow)); }
|
||
if magnitude == min_magnitude {
|
||
out.* = magnitude as i64;
|
||
} else {
|
||
out.* = 0 - magnitude as i64;
|
||
}
|
||
ret .Ok;
|
||
}
|
||
|
||
fn to_usize(out: *usize, source: BigInt): Status {
|
||
if out == null { ret .Err(Error.make(.InvalidArgument)); }
|
||
var value: u64 = 0;
|
||
match to_u64(&value, source) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
if value > (~(0 as usize)) as u64 { ret .Err(Error.make(.Overflow)); }
|
||
out.* = value as usize;
|
||
ret .Ok;
|
||
}
|
||
|
||
// 任意正位宽的位串。value 始终按无符号值规范化,符号解释由具体操作显式指定。
|
||
type Bits = struct {
|
||
value: BigInt,
|
||
bit_width: usize,
|
||
|
||
type Result = enum {
|
||
Ok: Bits,
|
||
Err: Error,
|
||
}
|
||
|
||
type Endian = enum {
|
||
Little,
|
||
Big,
|
||
}
|
||
|
||
fn limb_count(width: usize): usize {
|
||
var count = width / 32;
|
||
if width % 32 != 0 { count = count + 1; }
|
||
ret count;
|
||
}
|
||
|
||
fn blank(): Bits {
|
||
ret Bits { .value = BigInt.zero(), .bit_width = 0 };
|
||
}
|
||
|
||
fn new(width: usize): Bits.Result {
|
||
if width == 0 { ret .Err(Error.make(.InvalidArgument)); }
|
||
ret .Ok(Bits { .value = BigInt.zero(), .bit_width = width });
|
||
}
|
||
|
||
fn valid(self: Bits): bool {
|
||
if self.bit_width == 0 || !BigInt.valid(self.value) || self.value.negative {
|
||
ret false;
|
||
}
|
||
var bits: usize = 0;
|
||
match BigInt.bit_length(&bits, self.value) {
|
||
.Ok => {},
|
||
.Err(error) => { ret false; },
|
||
}
|
||
ret bits <= self.bit_width;
|
||
}
|
||
|
||
fn drop(self: *Bits) {
|
||
BigInt.drop(&self.value);
|
||
self.bit_width = 0;
|
||
}
|
||
|
||
fn take(source: *Bits): Bits {
|
||
var result = source.*;
|
||
source.* = Bits.blank();
|
||
ret result;
|
||
}
|
||
|
||
fn replace(out: *Bits, fresh: *Bits) {
|
||
Bits.drop(out);
|
||
out.* = Bits.take(fresh);
|
||
}
|
||
|
||
fn clone(out: *Bits, source: Bits): Status {
|
||
if !Bits.valid(out.*) || !Bits.valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
var fresh_r = Bits.new(source.bit_width);
|
||
var fresh = Bits.blank();
|
||
match fresh_r {
|
||
.Ok(value) => { fresh = value; },
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
match BigInt.clone(&fresh.value, source.value) {
|
||
.Ok => {},
|
||
.Err(error) => { Bits.drop(&fresh); ret .Err(error); },
|
||
}
|
||
Bits.replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn same_width(a: Bits, b: Bits): bool {
|
||
ret Bits.valid(a) && Bits.valid(b) && a.bit_width == b.bit_width;
|
||
}
|
||
|
||
fn sign_bit(self: Bits): bool {
|
||
var bit = self.bit_width - 1;
|
||
var word = bit / 32;
|
||
var offset = bit % 32;
|
||
if word >= self.value.len { ret false; }
|
||
ret ((self.value.limbs[word] >> offset as u32) & 1) != 0;
|
||
}
|
||
|
||
fn mask_top(self: *Bits) {
|
||
var excess = self.bit_width % 32;
|
||
if excess != 0 && self.value.len != 0 {
|
||
var top = Bits.limb_count(self.bit_width) - 1;
|
||
if top < self.value.len {
|
||
var mask = ((1 as u32) << excess as u32) - 1;
|
||
self.value.limbs[top] = self.value.limbs[top] & mask;
|
||
}
|
||
}
|
||
if self.value.len > Bits.limb_count(self.bit_width) {
|
||
self.value.len = Bits.limb_count(self.bit_width);
|
||
}
|
||
BigInt.normalize(&self.value);
|
||
self.value.negative = false;
|
||
}
|
||
|
||
// 显式模 2^width 转换,接受任意正负 BigInt。
|
||
fn from_big_truncate(out: *Bits, source: BigInt, width: usize): Status {
|
||
if !Bits.valid(out.*) || !BigInt.valid(source) || width == 0 {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
var fresh_r = Bits.new(width);
|
||
var fresh = Bits.blank();
|
||
match fresh_r {
|
||
.Ok(value) => { fresh = value; },
|
||
.Err(error) => { ret .Err(error); },
|
||
}
|
||
var count = Bits.limb_count(width);
|
||
match BigInt.reserve(&fresh.value, count) {
|
||
.Ok => {},
|
||
.Err(error) => { Bits.drop(&fresh); ret .Err(error); },
|
||
}
|
||
var i: usize = 0;
|
||
while i < count {
|
||
var limb: u32 = 0;
|
||
if i < source.len { limb = source.limbs[i]; }
|
||
if source.negative { limb = ~limb; }
|
||
fresh.value.limbs[i] = limb;
|
||
i = i + 1;
|
||
}
|
||
fresh.value.len = count;
|
||
if source.negative {
|
||
var carry: u64 = 1;
|
||
i = 0;
|
||
while i < count {
|
||
var sum = (fresh.value.limbs[i] as u64) + carry;
|
||
fresh.value.limbs[i] = sum as u32;
|
||
carry = sum >> 32;
|
||
i = i + 1;
|
||
}
|
||
}
|
||
Bits.mask_top(&fresh);
|
||
Bits.replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn fits_big(source: BigInt, width: usize, signed: bool): bool {
|
||
if width == 0 || !BigInt.valid(source) { ret false; }
|
||
var bits: usize = 0;
|
||
match BigInt.bit_length(&bits, source) {
|
||
.Ok => {}, .Err(error) => { ret false; },
|
||
}
|
||
if !signed { ret !source.negative && bits <= width; }
|
||
if !source.negative { ret bits < width; }
|
||
if bits < width { ret true; }
|
||
if bits > width { ret false; }
|
||
var power = BigInt.zero();
|
||
var one = BigInt.zero();
|
||
defer { BigInt.drop(&power); }
|
||
defer { BigInt.drop(&one); }
|
||
match BigInt.from_u64(&one, 1) { .Ok => {}, .Err(error) => { ret false; }, }
|
||
match BigInt.mul_pow2(&power, one, width - 1) {
|
||
.Ok => {}, .Err(error) => { ret false; },
|
||
}
|
||
ret BigInt.cmp_abs(source, power) == 0;
|
||
}
|
||
|
||
fn from_big_checked(out: *Bits, source: BigInt, width: usize, signed: bool): Status {
|
||
if !Bits.valid(out.*) || !BigInt.valid(source) || width == 0 {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
if !Bits.fits_big(source, width, signed) { ret .Err(Error.make(.Overflow)); }
|
||
ret Bits.from_big_truncate(out, source, width);
|
||
}
|
||
|
||
fn to_big(out: *BigInt, source: Bits, signed: bool): Status {
|
||
if !BigInt.valid(out.*) || !Bits.valid(source) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
if !signed || !Bits.sign_bit(source) { ret BigInt.clone(out, source.value); }
|
||
|
||
var modulus = BigInt.zero();
|
||
var one = BigInt.zero();
|
||
defer { BigInt.drop(&modulus); }
|
||
defer { BigInt.drop(&one); }
|
||
match BigInt.from_u64(&one, 1) { .Ok => {}, .Err(error) => { ret .Err(error); }, }
|
||
match BigInt.mul_pow2(&modulus, one, source.bit_width) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
ret BigInt.sub(out, source.value, modulus);
|
||
}
|
||
|
||
fn cmp_unsigned(out: *i32, a: Bits, b: Bits): Status {
|
||
if out == null { ret .Err(Error.make(.InvalidArgument)); }
|
||
if !Bits.same_width(a, b) { ret .Err(Error.make(.WidthMismatch)); }
|
||
out.* = BigInt.cmp_abs(a.value, b.value);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn cmp_signed(out: *i32, a: Bits, b: Bits): Status {
|
||
if out == null { ret .Err(Error.make(.InvalidArgument)); }
|
||
if !Bits.same_width(a, b) { ret .Err(Error.make(.WidthMismatch)); }
|
||
var an = Bits.sign_bit(a);
|
||
var bn = Bits.sign_bit(b);
|
||
if an != bn {
|
||
if an { out.* = -1; } else { out.* = 1; }
|
||
} else {
|
||
out.* = BigInt.cmp_abs(a.value, b.value);
|
||
}
|
||
ret .Ok;
|
||
}
|
||
|
||
fn bitwise(out: *Bits, a: Bits, b: Bits, operation: u8): Status {
|
||
if !Bits.valid(out.*) || !Bits.same_width(a, b) || out.bit_width != a.bit_width {
|
||
ret .Err(Error.make(.WidthMismatch));
|
||
}
|
||
var fresh_r = Bits.new(a.bit_width);
|
||
var fresh = Bits.blank();
|
||
match fresh_r { .Ok(value) => { fresh = value; }, .Err(error) => { ret .Err(error); }, }
|
||
var count = Bits.limb_count(a.bit_width);
|
||
match BigInt.reserve(&fresh.value, count) {
|
||
.Ok => {}, .Err(error) => { Bits.drop(&fresh); ret .Err(error); },
|
||
}
|
||
var i: usize = 0;
|
||
while i < count {
|
||
var av: u32 = 0;
|
||
var bv: u32 = 0;
|
||
if i < a.value.len { av = a.value.limbs[i]; }
|
||
if i < b.value.len { bv = b.value.limbs[i]; }
|
||
if operation == 0 { fresh.value.limbs[i] = av & bv; }
|
||
else if operation == 1 { fresh.value.limbs[i] = av | bv; }
|
||
else { fresh.value.limbs[i] = av ^ bv; }
|
||
i = i + 1;
|
||
}
|
||
fresh.value.len = count;
|
||
Bits.mask_top(&fresh);
|
||
Bits.replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn bit_and(out: *Bits, a: Bits, b: Bits): Status { ret Bits.bitwise(out, a, b, 0); }
|
||
fn bit_or(out: *Bits, a: Bits, b: Bits): Status { ret Bits.bitwise(out, a, b, 1); }
|
||
fn bit_xor(out: *Bits, a: Bits, b: Bits): Status { ret Bits.bitwise(out, a, b, 2); }
|
||
|
||
fn bit_not(out: *Bits, source: Bits): Status {
|
||
if !Bits.valid(out.*) || !Bits.valid(source) || out.bit_width != source.bit_width {
|
||
ret .Err(Error.make(.WidthMismatch));
|
||
}
|
||
var fresh_r = Bits.new(source.bit_width);
|
||
var fresh = Bits.blank();
|
||
match fresh_r {
|
||
.Ok(value) => { fresh = value; }, .Err(error) => { ret .Err(error); },
|
||
}
|
||
var count = Bits.limb_count(source.bit_width);
|
||
match BigInt.reserve(&fresh.value, count) {
|
||
.Ok => {}, .Err(error) => { Bits.drop(&fresh); ret .Err(error); },
|
||
}
|
||
var i: usize = 0;
|
||
while i < count {
|
||
var limb: u32 = 0;
|
||
if i < source.value.len { limb = source.value.limbs[i]; }
|
||
fresh.value.limbs[i] = ~limb;
|
||
i = i + 1;
|
||
}
|
||
fresh.value.len = count;
|
||
Bits.mask_top(&fresh);
|
||
Bits.replace(out, &fresh);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn shift_left(out: *Bits, source: Bits, amount: usize): Status {
|
||
if !Bits.valid(out.*) || !Bits.valid(source) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
if out.bit_width != source.bit_width {
|
||
ret .Err(Error.make(.WidthMismatch));
|
||
}
|
||
if amount >= source.bit_width { ret .Err(Error.make(.OutOfRange)); }
|
||
var shifted = BigInt.zero();
|
||
defer { BigInt.drop(&shifted); }
|
||
match BigInt.mul_pow2(&shifted, source.value, amount) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
ret Bits.from_big_truncate(out, shifted, source.bit_width);
|
||
}
|
||
|
||
fn shift_right(out: *Bits, source: Bits, amount: usize): Status {
|
||
if !Bits.valid(out.*) || !Bits.valid(source) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
if out.bit_width != source.bit_width {
|
||
ret .Err(Error.make(.WidthMismatch));
|
||
}
|
||
if amount >= source.bit_width { ret .Err(Error.make(.OutOfRange)); }
|
||
var shifted = BigInt.zero();
|
||
defer { BigInt.drop(&shifted); }
|
||
match BigInt.div_pow2(&shifted, source.value, amount) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
ret Bits.from_big_truncate(out, shifted, source.bit_width);
|
||
}
|
||
|
||
fn shift_right_arithmetic(out: *Bits, source: Bits, amount: usize): Status {
|
||
if !Bits.valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
var negative = Bits.sign_bit(source);
|
||
match Bits.shift_right(out, source, amount) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
if !negative || amount == 0 { ret .Ok; }
|
||
var bit = source.bit_width - amount;
|
||
while bit < source.bit_width {
|
||
var word = bit / 32;
|
||
var offset = bit % 32;
|
||
match BigInt.reserve(&out.value, word + 1) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
while out.value.len <= word {
|
||
out.value.limbs[out.value.len] = 0;
|
||
out.value.len = out.value.len + 1;
|
||
}
|
||
out.value.limbs[word] = out.value.limbs[word] | ((1 as u32) << offset as u32);
|
||
bit = bit + 1;
|
||
}
|
||
Bits.mask_top(out);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn arithmetic(out: *Bits, a: Bits, b: Bits, signed: bool, operation: u8,
|
||
wrapping: bool): Status {
|
||
if !Bits.valid(out.*) || !Bits.same_width(a, b) || out.bit_width != a.bit_width {
|
||
ret .Err(Error.make(.WidthMismatch));
|
||
}
|
||
var av = BigInt.zero();
|
||
var bv = BigInt.zero();
|
||
var result = BigInt.zero();
|
||
defer { BigInt.drop(&result); }
|
||
defer { BigInt.drop(&bv); }
|
||
defer { BigInt.drop(&av); }
|
||
match Bits.to_big(&av, a, signed) { .Ok => {}, .Err(error) => { ret .Err(error); }, }
|
||
match Bits.to_big(&bv, b, signed) { .Ok => {}, .Err(error) => { ret .Err(error); }, }
|
||
if operation == 0 {
|
||
match BigInt.add(&result, av, bv) { .Ok => {}, .Err(error) => { ret .Err(error); }, }
|
||
} else if operation == 1 {
|
||
match BigInt.sub(&result, av, bv) { .Ok => {}, .Err(error) => { ret .Err(error); }, }
|
||
} else {
|
||
match BigInt.mul(&result, av, bv) { .Ok => {}, .Err(error) => { ret .Err(error); }, }
|
||
}
|
||
if wrapping { ret Bits.from_big_truncate(out, result, a.bit_width); }
|
||
ret Bits.from_big_checked(out, result, a.bit_width, signed);
|
||
}
|
||
|
||
fn add_checked(out: *Bits, a: Bits, b: Bits, signed: bool): Status {
|
||
ret Bits.arithmetic(out, a, b, signed, 0, false);
|
||
}
|
||
fn sub_checked(out: *Bits, a: Bits, b: Bits, signed: bool): Status {
|
||
ret Bits.arithmetic(out, a, b, signed, 1, false);
|
||
}
|
||
fn mul_checked(out: *Bits, a: Bits, b: Bits, signed: bool): Status {
|
||
ret Bits.arithmetic(out, a, b, signed, 2, false);
|
||
}
|
||
fn add_wrapping(out: *Bits, a: Bits, b: Bits): Status {
|
||
ret Bits.arithmetic(out, a, b, false, 0, true);
|
||
}
|
||
fn sub_wrapping(out: *Bits, a: Bits, b: Bits): Status {
|
||
ret Bits.arithmetic(out, a, b, false, 1, true);
|
||
}
|
||
fn mul_wrapping(out: *Bits, a: Bits, b: Bits): Status {
|
||
ret Bits.arithmetic(out, a, b, false, 2, true);
|
||
}
|
||
|
||
fn truncate(out: *Bits, source: Bits, width: usize): Status {
|
||
if width == 0 || width > source.bit_width { ret .Err(Error.make(.InvalidArgument)); }
|
||
if !Bits.valid(out.*) || !Bits.valid(source) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
ret Bits.from_big_truncate(out, source.value, width);
|
||
}
|
||
|
||
fn zero_extend(out: *Bits, source: Bits, width: usize): Status {
|
||
if width < source.bit_width { ret .Err(Error.make(.InvalidArgument)); }
|
||
if !Bits.valid(out.*) || !Bits.valid(source) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
ret Bits.from_big_checked(out, source.value, width, false);
|
||
}
|
||
|
||
fn sign_extend(out: *Bits, source: Bits, width: usize): Status {
|
||
if width < source.bit_width { ret .Err(Error.make(.InvalidArgument)); }
|
||
if !Bits.valid(out.*) || !Bits.valid(source) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
var signed_value = BigInt.zero();
|
||
defer { BigInt.drop(&signed_value); }
|
||
match Bits.to_big(&signed_value, source, true) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
ret Bits.from_big_truncate(out, signed_value, width);
|
||
}
|
||
|
||
fn div_rem(quotient: *Bits, remainder: *Bits, dividend: Bits, divisor: Bits,
|
||
signed: bool): Status {
|
||
if (quotient as usize) == (remainder as usize) {
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
if !Bits.valid(quotient.*) || !Bits.valid(remainder.*) ||
|
||
!Bits.same_width(dividend, divisor) ||
|
||
quotient.bit_width != dividend.bit_width ||
|
||
remainder.bit_width != dividend.bit_width {
|
||
ret .Err(Error.make(.WidthMismatch));
|
||
}
|
||
var a = BigInt.zero();
|
||
var b = BigInt.zero();
|
||
var q = BigInt.zero();
|
||
var r = BigInt.zero();
|
||
defer { BigInt.drop(&r); }
|
||
defer { BigInt.drop(&q); }
|
||
defer { BigInt.drop(&b); }
|
||
defer { BigInt.drop(&a); }
|
||
match Bits.to_big(&a, dividend, signed) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
match Bits.to_big(&b, divisor, signed) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
match BigInt.div_rem(&q, &r, a, b) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
var q_r = Bits.new(dividend.bit_width);
|
||
var r_r = Bits.new(dividend.bit_width);
|
||
var q_bits = Bits.blank();
|
||
var r_bits = Bits.blank();
|
||
match q_r { .Ok(value) => { q_bits = value; }, .Err(error) => { ret .Err(error); }, }
|
||
match r_r {
|
||
.Ok(value) => { r_bits = value; },
|
||
.Err(error) => { Bits.drop(&q_bits); ret .Err(error); },
|
||
}
|
||
match Bits.from_big_checked(&q_bits, q, dividend.bit_width, signed) {
|
||
.Ok => {},
|
||
.Err(error) => { Bits.drop(&r_bits); Bits.drop(&q_bits); ret .Err(error); },
|
||
}
|
||
match Bits.from_big_checked(&r_bits, r, dividend.bit_width, signed) {
|
||
.Ok => {},
|
||
.Err(error) => { Bits.drop(&r_bits); Bits.drop(&q_bits); ret .Err(error); },
|
||
}
|
||
Bits.replace(quotient, &q_bits);
|
||
Bits.replace(remainder, &r_bits);
|
||
ret .Ok;
|
||
}
|
||
|
||
fn byte_len(self: Bits): usize {
|
||
ret self.bit_width / 8 + (self.bit_width % 8 != 0) as usize;
|
||
}
|
||
|
||
fn little(endian: Bits.Endian): bool {
|
||
match endian { .Little => { ret true; }, .Big => { ret false; }, }
|
||
}
|
||
|
||
fn import_bytes(out: *Bits, bytes: []u8, width: usize, endian: Bits.Endian): Status {
|
||
if !Bits.valid(out.*) || width == 0 { ret .Err(Error.make(.InvalidArgument)); }
|
||
var expected = width / 8 + (width % 8 != 0) as usize;
|
||
if bytes.len != expected { ret .Err(Error.make(.OutOfRange)); }
|
||
if bytes.len != 0 && bytes.ptr == null { ret .Err(Error.make(.InvalidArgument)); }
|
||
|
||
var value = BigInt.zero();
|
||
defer { BigInt.drop(&value); }
|
||
var i: usize = 0;
|
||
while i < bytes.len {
|
||
var index = i;
|
||
if Bits.little(endian) { index = bytes.len - 1 - i; }
|
||
match BigInt.mul_small(&value, 256) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
match BigInt.add_small(&value, bytes.ptr[index] as u32) {
|
||
.Ok => {}, .Err(error) => { ret .Err(error); },
|
||
}
|
||
i = i + 1;
|
||
}
|
||
ret Bits.from_big_checked(out, value, width, false);
|
||
}
|
||
|
||
fn export_bytes(out: *ByteBuffer, source: Bits, endian: Bits.Endian): Status {
|
||
if !Bits.valid(source) { ret .Err(Error.make(.InvalidArgument)); }
|
||
var fresh = ByteBuffer.new();
|
||
var count = Bits.byte_len(source);
|
||
var i: usize = 0;
|
||
while i < count {
|
||
var logical = i;
|
||
if !Bits.little(endian) { logical = count - 1 - i; }
|
||
var word = logical / 4;
|
||
var offset = (logical % 4) * 8;
|
||
var byte: u8 = 0;
|
||
if word < source.value.len {
|
||
byte = (source.value.limbs[word] >> offset as u32) as u8;
|
||
}
|
||
match ByteBuffer.append_byte(&fresh, byte) {
|
||
.Ok => {}, .Err(error) => { ByteBuffer.drop(&fresh); ret .Err(error); },
|
||
}
|
||
i = i + 1;
|
||
}
|
||
ByteBuffer.drop(out);
|
||
out.* = ByteBuffer.take(&fresh);
|
||
ret .Ok;
|
||
}
|
||
}
|
||
|
||
fn expect_ok(result: Status) {
|
||
match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected BigInt error"); }, }
|
||
}
|
||
|
||
fn expect_text_radix(value: BigInt, radix: u8, uppercase: bool, expected: []u8) {
|
||
var buffer = ByteBuffer.new();
|
||
expect_ok(format(&buffer, value, radix, uppercase));
|
||
var actual = ByteBuffer.as_slice(buffer);
|
||
@assert(actual.len == expected.len, "formatted length");
|
||
var i: usize = 0;
|
||
while i < expected.len {
|
||
@assert(actual.ptr[i] == expected.ptr[i], "formatted byte");
|
||
i = i + 1;
|
||
}
|
||
ByteBuffer.drop(&buffer);
|
||
}
|
||
|
||
fn expect_text(value: BigInt, expected: []u8) {
|
||
expect_text_radix(value, 10, false, expected);
|
||
}
|
||
|
||
fn parsed(text: []u8): BigInt {
|
||
var value = zero();
|
||
expect_ok(parse(&value, text, 10));
|
||
ret value;
|
||
}
|
||
|
||
fn test_arithmetic() {
|
||
var a = parsed("4294967295");
|
||
defer { drop(&a); }
|
||
var one = zero();
|
||
defer { drop(&one); }
|
||
expect_ok(from_u64(&one, 1));
|
||
expect_ok(add(&a, a, one));
|
||
expect_text(a, "4294967296");
|
||
|
||
var all_ones = zero();
|
||
defer { drop(&all_ones); }
|
||
expect_ok(parse(&all_ones, "ffffffffffffffffffffffffffffffff", 16));
|
||
expect_ok(add(&all_ones, all_ones, one));
|
||
expect_text(all_ones, "340282366920938463463374607431768211456");
|
||
expect_ok(sub(&all_ones, all_ones, one));
|
||
expect_text_radix(all_ones, 16, false, "ffffffffffffffffffffffffffffffff");
|
||
|
||
var two_64 = parsed("18446744073709551616");
|
||
defer { drop(&two_64); }
|
||
var product = zero();
|
||
defer { drop(&product); }
|
||
expect_ok(mul(&product, two_64, two_64));
|
||
expect_text(product, "340282366920938463463374607431768211456");
|
||
|
||
var difference = zero();
|
||
defer { drop(&difference); }
|
||
expect_ok(sub(&difference, product, two_64));
|
||
expect_text(difference, "340282366920938463444927863358058659840");
|
||
|
||
var shifted = zero();
|
||
defer { drop(&shifted); }
|
||
expect_ok(mul_pow2(&shifted, one, 257));
|
||
var bits: usize = 0;
|
||
expect_ok(bit_length(&bits, shifted));
|
||
@assert(bits == 258);
|
||
expect_ok(div_pow2(&shifted, shifted, 257));
|
||
@assert(eq(shifted, one));
|
||
}
|
||
|
||
fn test_division_and_sign() {
|
||
var dividend = parsed("340282366920938463463374607431768211456");
|
||
defer { drop(÷nd); }
|
||
var divisor = parsed("18446744073709551616");
|
||
defer { drop(&divisor); }
|
||
var quotient = zero();
|
||
defer { drop("ient); }
|
||
var remainder = zero();
|
||
defer { drop(&remainder); }
|
||
expect_ok(div_rem("ient, &remainder, dividend, divisor));
|
||
@assert(eq(quotient, divisor));
|
||
@assert(is_zero(remainder));
|
||
|
||
var long_dividend = parsed("123456789012345678901234567890");
|
||
defer { drop(&long_dividend); }
|
||
var long_divisor = parsed("9876543210");
|
||
defer { drop(&long_divisor); }
|
||
expect_ok(div_rem("ient, &remainder, long_dividend, long_divisor));
|
||
expect_text(quotient, "12499999887343749990");
|
||
expect_text(remainder, "1562499990");
|
||
|
||
var negative = zero();
|
||
defer { drop(&negative); }
|
||
var seven = zero();
|
||
defer { drop(&seven); }
|
||
expect_ok(from_i64(&negative, -100));
|
||
expect_ok(from_u64(&seven, 7));
|
||
expect_ok(div_rem(&negative, &remainder, negative, seven));
|
||
expect_text(negative, "-14");
|
||
expect_text(remainder, "-2");
|
||
|
||
var reconstructed = zero();
|
||
defer { drop(&reconstructed); }
|
||
expect_ok(mul(&reconstructed, negative, seven));
|
||
expect_ok(add(&reconstructed, reconstructed, remainder));
|
||
expect_text(reconstructed, "-100");
|
||
|
||
var saved = parsed("123");
|
||
defer { drop(&saved); }
|
||
var zero_divisor = zero();
|
||
defer { drop(&zero_divisor); }
|
||
match div_rem(&saved, &remainder, dividend, zero_divisor) {
|
||
.Ok => { @assert(false, "division by zero"); },
|
||
.Err(error) => { match error.kind { .DivisionByZero => {}, _ => { @assert(false); }, } },
|
||
}
|
||
expect_text(saved, "123");
|
||
}
|
||
|
||
fn test_parse_format_and_conversion() {
|
||
var value = zero();
|
||
defer { drop(&value); }
|
||
expect_ok(parse(&value, "-deadBEEF", 16));
|
||
var output = ByteBuffer.new();
|
||
defer { ByteBuffer.drop(&output); }
|
||
expect_ok(format(&output, value, 16, false));
|
||
var text = ByteBuffer.as_slice(output);
|
||
@assert(text.len == 9 && text.ptr[0] == '-' && text.ptr[1] == 'd' && text.ptr[8] == 'f');
|
||
expect_text_radix(value, 16, true, "-DEADBEEF");
|
||
|
||
var base36 = zero();
|
||
defer { drop(&base36); }
|
||
expect_ok(parse(&base36, "zzzzzzzzzz", 36));
|
||
expect_text_radix(base36, 36, false, "zzzzzzzzzz");
|
||
|
||
var preserved = parsed("77");
|
||
defer { drop(&preserved); }
|
||
match parse(&preserved, "12x", 10) {
|
||
.Ok => { @assert(false, "invalid digit"); },
|
||
.Err(error) => {
|
||
@assert(error.has_position && error.position == 2);
|
||
match error.kind { .InvalidDigit => {}, _ => { @assert(false); }, }
|
||
},
|
||
}
|
||
expect_text(preserved, "77");
|
||
|
||
var max = parsed("18446744073709551615");
|
||
defer { drop(&max); }
|
||
var converted: u64 = 0;
|
||
expect_ok(to_u64(&converted, max));
|
||
@assert(converted == 18446744073709551615);
|
||
var over = parsed("18446744073709551616");
|
||
defer { drop(&over); }
|
||
converted = 17;
|
||
match to_u64(&converted, over) {
|
||
.Ok => { @assert(false); },
|
||
.Err(error) => { match error.kind { .Overflow => {}, _ => { @assert(false); }, } },
|
||
}
|
||
@assert(converted == 17, "failed conversion preserves output");
|
||
|
||
var minimum = parsed("-9223372036854775808");
|
||
defer { drop(&minimum); }
|
||
var signed_value: i64 = 0;
|
||
expect_ok(to_i64(&signed_value, minimum));
|
||
@assert(signed_value == (9223372036854775808 as u64) as i64);
|
||
var signed_over = parsed("9223372036854775808");
|
||
defer { drop(&signed_over); }
|
||
signed_value = 23;
|
||
match to_i64(&signed_value, signed_over) {
|
||
.Ok => { @assert(false); },
|
||
.Err(error) => { match error.kind { .Overflow => {}, _ => { @assert(false); }, } },
|
||
}
|
||
@assert(signed_value == 23, "failed signed conversion preserves output");
|
||
|
||
var copied = zero();
|
||
defer { drop(&copied); }
|
||
expect_ok(clone(&copied, max));
|
||
var one_more = parsed("1");
|
||
defer { drop(&one_more); }
|
||
expect_ok(add(&max, max, one_more));
|
||
expect_text(copied, "18446744073709551615");
|
||
var moved = take(&copied);
|
||
defer { drop(&moved); }
|
||
@assert(copied.limbs == null && copied.len == 0 && copied.cap == 0);
|
||
expect_text(moved, "18446744073709551615");
|
||
}
|
||
|
||
fn expect_i64_value(value: BigInt, expected: i64) {
|
||
var actual: i64 = 0;
|
||
expect_ok(to_i64(&actual, value));
|
||
@assert(actual == expected, "small integer reference result");
|
||
}
|
||
|
||
fn test_small_reference() {
|
||
var left = zero();
|
||
defer { drop(&left); }
|
||
var right = zero();
|
||
defer { drop(&right); }
|
||
var result = zero();
|
||
defer { drop(&result); }
|
||
var remainder = zero();
|
||
defer { drop(&remainder); }
|
||
var a: i64 = -32;
|
||
while a <= 32 {
|
||
var b: i64 = -32;
|
||
while b <= 32 {
|
||
expect_ok(from_i64(&left, a));
|
||
expect_ok(from_i64(&right, b));
|
||
expect_ok(add(&result, left, right));
|
||
expect_i64_value(result, a + b);
|
||
expect_ok(sub(&result, left, right));
|
||
expect_i64_value(result, a - b);
|
||
expect_ok(mul(&result, left, right));
|
||
expect_i64_value(result, a * b);
|
||
if b != 0 {
|
||
expect_ok(div_rem(&result, &remainder, left, right));
|
||
expect_i64_value(result, a / b);
|
||
expect_i64_value(remainder, a % b);
|
||
}
|
||
b = b + 1;
|
||
}
|
||
a = a + 1;
|
||
}
|
||
}
|
||
|
||
fn bits_new(width: usize): Bits {
|
||
match Bits.new(width) {
|
||
.Ok(value) => { ret value; },
|
||
.Err(error) => { @assert(false, "Bits construction"); },
|
||
}
|
||
ret Bits.blank();
|
||
}
|
||
|
||
fn bits_i64(value: i64, width: usize, signed: bool): Bits {
|
||
var integer = BigInt.zero();
|
||
defer { BigInt.drop(&integer); }
|
||
expect_ok(BigInt.from_i64(&integer, value));
|
||
var result = bits_new(width);
|
||
expect_ok(Bits.from_big_checked(&result, integer, width, signed));
|
||
ret result;
|
||
}
|
||
|
||
fn bits_u64(value: u64, width: usize): Bits {
|
||
var integer = BigInt.zero();
|
||
defer { BigInt.drop(&integer); }
|
||
expect_ok(BigInt.from_u64(&integer, value));
|
||
var result = bits_new(width);
|
||
expect_ok(Bits.from_big_checked(&result, integer, width, false));
|
||
ret result;
|
||
}
|
||
|
||
fn bits_as_i64(value: Bits, signed: bool): i64 {
|
||
var integer = BigInt.zero();
|
||
defer { BigInt.drop(&integer); }
|
||
expect_ok(Bits.to_big(&integer, value, signed));
|
||
var result: i64 = 0;
|
||
expect_ok(BigInt.to_i64(&result, integer));
|
||
ret result;
|
||
}
|
||
|
||
fn expect_overflow_status(result: Status) {
|
||
match result {
|
||
.Ok => { @assert(false, "expected overflow"); },
|
||
.Err(error) => { match error.kind { .Overflow => {}, _ => { @assert(false); }, } },
|
||
}
|
||
}
|
||
|
||
fn expect_range_status(result: Status) {
|
||
match result {
|
||
.Ok => { @assert(false, "expected range error"); },
|
||
.Err(error) => { match error.kind { .OutOfRange => {}, _ => { @assert(false); }, } },
|
||
}
|
||
}
|
||
|
||
fn expect_width_status(result: Status) {
|
||
match result {
|
||
.Ok => { @assert(false, "expected width mismatch"); },
|
||
.Err(error) => { match error.kind { .WidthMismatch => {}, _ => { @assert(false); }, } },
|
||
}
|
||
}
|
||
|
||
fn test_bits_widths_and_conversion() {
|
||
match Bits.new(0) {
|
||
.Ok(value) => { @assert(false, "zero width"); },
|
||
.Err(error) => { match error.kind { .InvalidArgument => {}, _ => { @assert(false); }, } },
|
||
}
|
||
var widths: [6]usize = .[1, 7, 24, 65, 128, 257];
|
||
var i: usize = 0;
|
||
while i < 6 {
|
||
var value = bits_new(widths[i]);
|
||
@assert(Bits.valid(value) && value.bit_width == widths[i]);
|
||
Bits.drop(&value);
|
||
i = i + 1;
|
||
}
|
||
|
||
var minus_one = bits_i64(-1, 7, true);
|
||
defer { Bits.drop(&minus_one); }
|
||
@assert(bits_as_i64(minus_one, true) == -1);
|
||
@assert(bits_as_i64(minus_one, false) == 127);
|
||
|
||
var one_bit = bits_u64(1, 1);
|
||
defer { Bits.drop(&one_bit); }
|
||
@assert(bits_as_i64(one_bit, true) == -1 && bits_as_i64(one_bit, false) == 1);
|
||
|
||
var all_128_int = parsed("340282366920938463463374607431768211455");
|
||
defer { BigInt.drop(&all_128_int); }
|
||
var all_128 = bits_new(128);
|
||
defer { Bits.drop(&all_128); }
|
||
expect_ok(Bits.from_big_checked(&all_128, all_128_int, 128, false));
|
||
@assert(all_128.value.len == 4 && all_128.value.limbs[3] == 0xffffffff);
|
||
|
||
var all_257 = bits_i64(-1, 257, true);
|
||
defer { Bits.drop(&all_257); }
|
||
@assert(all_257.value.len == 9 && all_257.value.limbs[8] == 1,
|
||
"257-bit top mask");
|
||
@assert(bits_as_i64(all_257, true) == -1);
|
||
|
||
var wide_text = parsed("36893488147419103231");
|
||
defer { BigInt.drop(&wide_text); }
|
||
var wide = bits_new(65);
|
||
defer { Bits.drop(&wide); }
|
||
expect_ok(Bits.from_big_checked(&wide, wide_text, 65, false));
|
||
var bit_count: usize = 0;
|
||
expect_ok(BigInt.bit_length(&bit_count, wide.value));
|
||
@assert(bit_count == 65 && wide.value.limbs[2] == 1, "65-bit top mask");
|
||
}
|
||
|
||
fn test_bits_arithmetic() {
|
||
var one = bits_u64(1, 7);
|
||
defer { Bits.drop(&one); }
|
||
var max = bits_u64(127, 7);
|
||
defer { Bits.drop(&max); }
|
||
var out = bits_u64(5, 7);
|
||
defer { Bits.drop(&out); }
|
||
|
||
expect_overflow_status(Bits.add_checked(&out, max, one, false));
|
||
@assert(bits_as_i64(out, false) == 5, "overflow preserves output");
|
||
expect_ok(Bits.add_wrapping(&out, max, one));
|
||
@assert(bits_as_i64(out, false) == 0, "u7 wrapping add");
|
||
|
||
var signed_max = bits_i64(63, 7, true);
|
||
defer { Bits.drop(&signed_max); }
|
||
expect_overflow_status(Bits.add_checked(&out, signed_max, one, true));
|
||
var signed_min = bits_i64(-64, 7, true);
|
||
defer { Bits.drop(&signed_min); }
|
||
expect_overflow_status(Bits.sub_checked(&out, signed_min, one, true));
|
||
|
||
var twenty = bits_u64(20, 7);
|
||
defer { Bits.drop(&twenty); }
|
||
var ten = bits_u64(10, 7);
|
||
defer { Bits.drop(&ten); }
|
||
expect_ok(Bits.mul_wrapping(&out, twenty, ten));
|
||
@assert(bits_as_i64(out, false) == 72, "u7 wrapping multiply");
|
||
}
|
||
|
||
fn test_bits_logic_and_shift() {
|
||
var a = bits_u64(85, 7);
|
||
defer { Bits.drop(&a); }
|
||
var b = bits_u64(15, 7);
|
||
defer { Bits.drop(&b); }
|
||
var out = bits_new(7);
|
||
defer { Bits.drop(&out); }
|
||
expect_ok(Bits.bit_and(&out, a, b));
|
||
@assert(bits_as_i64(out, false) == 5);
|
||
expect_ok(Bits.bit_or(&out, a, b));
|
||
@assert(bits_as_i64(out, false) == 95);
|
||
expect_ok(Bits.bit_xor(&out, a, b));
|
||
@assert(bits_as_i64(out, false) == 90);
|
||
expect_ok(Bits.bit_not(&out, a));
|
||
@assert(bits_as_i64(out, false) == 42);
|
||
|
||
var signed = bits_u64(65, 7);
|
||
defer { Bits.drop(&signed); }
|
||
expect_ok(Bits.shift_left(&out, signed, 1));
|
||
@assert(bits_as_i64(out, false) == 2);
|
||
expect_ok(Bits.shift_right(&out, signed, 1));
|
||
@assert(bits_as_i64(out, false) == 32);
|
||
expect_ok(Bits.shift_right_arithmetic(&out, signed, 1));
|
||
@assert(bits_as_i64(out, false) == 96);
|
||
expect_range_status(Bits.shift_left(&out, signed, 7));
|
||
@assert(bits_as_i64(out, false) == 96, "bad shift preserves output");
|
||
|
||
var order: i32 = 0;
|
||
expect_ok(Bits.cmp_unsigned(&order, signed, a));
|
||
@assert(order < 0);
|
||
expect_ok(Bits.cmp_signed(&order, signed, a));
|
||
@assert(order < 0);
|
||
|
||
var other_width = bits_u64(1, 24);
|
||
defer { Bits.drop(&other_width); }
|
||
order = 17;
|
||
expect_width_status(Bits.cmp_unsigned(&order, a, other_width));
|
||
@assert(order == 17, "width mismatch preserves comparison output");
|
||
expect_width_status(Bits.bit_and(&out, a, other_width));
|
||
expect_ok(Bits.shift_right_arithmetic(&signed, signed, 1));
|
||
@assert(bits_as_i64(signed, false) == 96, "aliased arithmetic shift");
|
||
}
|
||
|
||
fn test_bits_resize_division_and_bytes() {
|
||
var source = bits_u64(85, 7);
|
||
defer { Bits.drop(&source); }
|
||
var resized = bits_new(7);
|
||
defer { Bits.drop(&resized); }
|
||
expect_ok(Bits.truncate(&resized, source, 4));
|
||
@assert(bits_as_i64(resized, false) == 5);
|
||
expect_ok(Bits.zero_extend(&resized, source, 24));
|
||
@assert(bits_as_i64(resized, false) == 85 && resized.bit_width == 24);
|
||
|
||
var negative = bits_u64(65, 7);
|
||
defer { Bits.drop(&negative); }
|
||
expect_ok(Bits.sign_extend(&resized, negative, 24));
|
||
@assert(bits_as_i64(resized, true) == -63);
|
||
|
||
var hundred = bits_u64(100, 8);
|
||
defer { Bits.drop(&hundred); }
|
||
var seven = bits_u64(7, 8);
|
||
defer { Bits.drop(&seven); }
|
||
var quotient = bits_new(8);
|
||
defer { Bits.drop("ient); }
|
||
var remainder = bits_new(8);
|
||
defer { Bits.drop(&remainder); }
|
||
expect_ok(Bits.div_rem("ient, &remainder, hundred, seven, false));
|
||
@assert(bits_as_i64(quotient, false) == 14 && bits_as_i64(remainder, false) == 2);
|
||
|
||
var neg_hundred = bits_i64(-100, 8, true);
|
||
defer { Bits.drop(&neg_hundred); }
|
||
expect_ok(Bits.div_rem("ient, &remainder, neg_hundred, seven, true));
|
||
@assert(bits_as_i64(quotient, true) == -14 && bits_as_i64(remainder, true) == -2);
|
||
|
||
var min = bits_i64(-128, 8, true);
|
||
defer { Bits.drop(&min); }
|
||
var minus_one = bits_i64(-1, 8, true);
|
||
defer { Bits.drop(&minus_one); }
|
||
expect_overflow_status(Bits.div_rem("ient, &remainder, min, minus_one, true));
|
||
|
||
var bytes: [3]u8 = .[0x12, 0x34, 0x56];
|
||
expect_ok(Bits.import_bytes(&resized, bytes[0..3], 24, .Big));
|
||
@assert(bits_as_i64(resized, false) == 0x123456);
|
||
var output = ByteBuffer.new();
|
||
defer { ByteBuffer.drop(&output); }
|
||
expect_ok(Bits.export_bytes(&output, resized, .Little));
|
||
var exported = ByteBuffer.as_slice(output);
|
||
@assert(exported.len == 3 && exported.ptr[0] == 0x56 &&
|
||
exported.ptr[1] == 0x34 && exported.ptr[2] == 0x12);
|
||
|
||
var bad: [1]u8 = .[0xff];
|
||
var seven_bits = bits_u64(3, 7);
|
||
defer { Bits.drop(&seven_bits); }
|
||
expect_overflow_status(Bits.import_bytes(&seven_bits, bad[0..1], 7, .Big));
|
||
@assert(bits_as_i64(seven_bits, false) == 3, "bad import preserves output");
|
||
|
||
var copied = bits_new(7);
|
||
defer { Bits.drop(&copied); }
|
||
expect_ok(Bits.clone(&copied, seven_bits));
|
||
expect_ok(Bits.bit_not(&seven_bits, seven_bits));
|
||
@assert(bits_as_i64(copied, false) == 3, "Bits clone is deep");
|
||
var moved = Bits.take(&copied);
|
||
defer { Bits.drop(&moved); }
|
||
@assert(copied.bit_width == 0 && copied.value.limbs == null, "Bits take empties source");
|
||
@assert(bits_as_i64(moved, false) == 3);
|
||
}
|
||
|
||
fn main() {
|
||
test_arithmetic();
|
||
test_division_and_sign();
|
||
test_parse_format_and_conversion();
|
||
test_small_reference();
|
||
test_bits_widths_and_conversion();
|
||
test_bits_arithmetic();
|
||
test_bits_logic_and_shift();
|
||
test_bits_resize_division_and_bytes();
|
||
}
|