345 lines
7.7 KiB
V
345 lines
7.7 KiB
V
module big
|
|
|
|
// Wrapper for https://github.com/kokke/tiny-bignum-c
|
|
#flag -I @VEXEROOT/thirdparty/bignum
|
|
#flag @VEXEROOT/thirdparty/bignum/bn.o
|
|
#include "bn.h"
|
|
|
|
struct C.bn {
|
|
mut:
|
|
array [32]u32
|
|
}
|
|
|
|
// Big unsigned integer number.
|
|
type Number = C.bn
|
|
|
|
fn C.bignum_init(n &Number)
|
|
|
|
fn C.bignum_from_int(n &Number, i u64)
|
|
|
|
fn C.bignum_to_int(n &Number) int
|
|
|
|
fn C.bignum_from_string(n &Number, s &char, nbytes int)
|
|
|
|
fn C.bignum_to_string(n &Number, s &char, maxsize int)
|
|
|
|
// c = a + b
|
|
fn C.bignum_add(a &Number, b &Number, c &Number)
|
|
|
|
// c = a - b
|
|
fn C.bignum_sub(a &Number, b &Number, c &Number)
|
|
|
|
// c = a * b
|
|
fn C.bignum_mul(a &Number, b &Number, c &Number)
|
|
|
|
// c = a / b
|
|
fn C.bignum_div(a &Number, b &Number, c &Number)
|
|
|
|
// c = a % b
|
|
fn C.bignum_mod(a &Number, b &Number, c &Number)
|
|
|
|
// c = a/b d=a%b
|
|
fn C.bignum_divmod(a &Number, b &Number, c &Number, d &Number)
|
|
|
|
// c = a & b
|
|
fn C.bignum_and(a &Number, b &Number, c &Number)
|
|
|
|
// c = a | b
|
|
fn C.bignum_or(a &Number, b &Number, c &Number)
|
|
|
|
// c = a xor b
|
|
fn C.bignum_xor(a &Number, b &Number, c &Number)
|
|
|
|
// b = a << nbits
|
|
fn C.bignum_lshift(a &Number, b &Number, nbits int)
|
|
|
|
// b = a >> nbits
|
|
fn C.bignum_rshift(a &Number, b &Number, nbits int)
|
|
|
|
fn C.bignum_cmp(a &Number, b &Number) int
|
|
|
|
fn C.bignum_is_zero(a &Number) int
|
|
|
|
// n++
|
|
fn C.bignum_inc(n &Number)
|
|
|
|
// n--
|
|
fn C.bignum_dec(n &Number)
|
|
|
|
// c = a ^ b
|
|
fn C.bignum_pow(a &Number, b &Number, c &Number)
|
|
|
|
// b = integer_square_root_of(a)
|
|
fn C.bignum_isqrt(a &Number, b &Number)
|
|
|
|
// copy src number to dst number
|
|
fn C.bignum_assign(dst &Number, src &Number)
|
|
|
|
// new returns a bignum, initialized to 0
|
|
pub fn new() Number {
|
|
return Number{}
|
|
}
|
|
|
|
// conversion actions to/from big numbers:
|
|
// from_int converts an ordinary int number `i` to big.Number
|
|
pub fn from_int(i int) Number {
|
|
n := Number{}
|
|
C.bignum_from_int(&n, i)
|
|
return n
|
|
}
|
|
|
|
// from_u64 converts an ordinary u64 number `u` to big.Number
|
|
pub fn from_u64(u u64) Number {
|
|
n := Number{}
|
|
C.bignum_from_int(&n, u)
|
|
return n
|
|
}
|
|
|
|
// from_hex_string converts a hex string to big.Number
|
|
pub fn from_hex_string(input string) Number {
|
|
mut s := input.trim_prefix('0x')
|
|
if s.len == 0 {
|
|
s = '0'
|
|
}
|
|
padding := '0'.repeat((8 - s.len % 8) % 8)
|
|
s = padding + s
|
|
n := Number{}
|
|
C.bignum_from_string(&n, &char(s.str), s.len)
|
|
return n
|
|
}
|
|
|
|
// from_string converts a decimal string to big.Number
|
|
pub fn from_string(input string) Number {
|
|
mut n := from_int(0)
|
|
for _, c in input {
|
|
d := from_int(int(c - `0`))
|
|
n = (n * big.ten) + d
|
|
}
|
|
return n
|
|
}
|
|
|
|
// from_bytes converts an array of bytes (little-endian) to a big.Number.
|
|
// Higher precedence bytes are expected at lower indices in the array.
|
|
pub fn from_bytes(input []byte) ?Number {
|
|
if input.len > 128 {
|
|
return error('input array too large. big.Number can only hold up to 1024 bit numbers')
|
|
}
|
|
// pad input
|
|
mut padded_input := []byte{len: ((input.len + 3) & ~0x3) - input.len, cap: (input.len + 3) & ~0x3, init: 0x0}
|
|
padded_input << input
|
|
// combine every 4 bytes into a u32 and insert into n.array
|
|
mut n := Number{}
|
|
for i := 0; i < padded_input.len; i += 4 {
|
|
x3 := u32(padded_input[i])
|
|
x2 := u32(padded_input[i + 1])
|
|
x1 := u32(padded_input[i + 2])
|
|
x0 := u32(padded_input[i + 3])
|
|
val := (x3 << 24) | (x2 << 16) | (x1 << 8) | x0
|
|
n.array[(padded_input.len - i) / 4 - 1] = val
|
|
}
|
|
return n
|
|
}
|
|
|
|
// .int() converts (a small) big.Number `n` to an ordinary integer.
|
|
pub fn (n &Number) int() int {
|
|
r := C.bignum_to_int(n)
|
|
return r
|
|
}
|
|
|
|
const (
|
|
ten = from_int(10)
|
|
)
|
|
|
|
// .str returns a decimal representation of the big unsigned integer number n.
|
|
pub fn (n &Number) str() string {
|
|
if n.is_zero() {
|
|
return '0'
|
|
}
|
|
mut digits := []byte{}
|
|
mut x := n.clone()
|
|
|
|
for !x.is_zero() {
|
|
// changes to reflect new api
|
|
div, mod := divmod(&x, &big.ten)
|
|
digits << byte(mod.int()) + `0`
|
|
x = div
|
|
}
|
|
return digits.reverse().bytestr()
|
|
}
|
|
|
|
// .hexstr returns a hexadecimal representation of the bignum `n`
|
|
pub fn (n &Number) hexstr() string {
|
|
mut buf := [8192]byte{}
|
|
mut s := ''
|
|
unsafe {
|
|
bp := &buf[0]
|
|
// NB: C.bignum_to_string(), returns the HEXADECIMAL representation of the bignum n
|
|
C.bignum_to_string(n, &char(bp), 8192)
|
|
s = tos_clone(bp)
|
|
}
|
|
if s.len == 0 {
|
|
return '0'
|
|
}
|
|
return s
|
|
}
|
|
|
|
// //////////////////////////////////////////////////////////
|
|
// overloaded ops for the numbers:
|
|
pub fn (a &Number) + (b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_add(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn (a &Number) - (b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_sub(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn (a &Number) * (b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_mul(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn (a &Number) / (b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_div(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn (a &Number) % (b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_mod(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
// divmod returns a pair of quotient and remainder from div modulo operation
|
|
// between two bignums `a` and `b`
|
|
pub fn divmod(a &Number, b &Number) (Number, Number) {
|
|
c := Number{}
|
|
d := Number{}
|
|
C.bignum_divmod(a, b, &c, &d)
|
|
return c, d
|
|
}
|
|
|
|
// //////////////////////////////////////////////////////////
|
|
pub fn cmp(a &Number, b &Number) int {
|
|
return C.bignum_cmp(a, b)
|
|
}
|
|
|
|
pub fn (a &Number) is_zero() bool {
|
|
return C.bignum_is_zero(a) != 0
|
|
}
|
|
|
|
pub fn (mut a Number) inc() {
|
|
C.bignum_inc(&a)
|
|
}
|
|
|
|
pub fn (mut a Number) dec() {
|
|
C.bignum_dec(&a)
|
|
}
|
|
|
|
pub fn pow(a &Number, b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_pow(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn (a &Number) isqrt() Number {
|
|
b := Number{}
|
|
C.bignum_isqrt(a, &b)
|
|
return b
|
|
}
|
|
|
|
// //////////////////////////////////////////////////////////
|
|
pub fn b_and(a &Number, b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_and(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn b_or(a &Number, b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_or(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn b_xor(a &Number, b &Number) Number {
|
|
c := Number{}
|
|
C.bignum_xor(a, b, &c)
|
|
return c
|
|
}
|
|
|
|
pub fn (a &Number) lshift(nbits int) Number {
|
|
b := Number{}
|
|
C.bignum_lshift(a, &b, nbits)
|
|
return b
|
|
}
|
|
|
|
pub fn (a &Number) rshift(nbits int) Number {
|
|
b := Number{}
|
|
C.bignum_rshift(a, &b, nbits)
|
|
return b
|
|
}
|
|
|
|
pub fn (a &Number) clone() Number {
|
|
b := Number{}
|
|
C.bignum_assign(&b, a)
|
|
return b
|
|
}
|
|
|
|
// //////////////////////////////////////////////////////////
|
|
pub fn factorial(nn &Number) Number {
|
|
mut n := nn.clone()
|
|
mut a := nn.clone()
|
|
n.dec()
|
|
mut i := 1
|
|
for !n.is_zero() {
|
|
res := a * n
|
|
n.dec()
|
|
a = res
|
|
i++
|
|
}
|
|
return a
|
|
}
|
|
|
|
pub fn fact(n int) Number {
|
|
return factorial(from_int(n))
|
|
}
|
|
|
|
// bytes returns an array of the bytes for the number `n`,
|
|
// in little endian format, where .bytes()[0] is the least
|
|
// significant byte. The result is NOT trimmed, and will contain 0s, even
|
|
// after the significant bytes.
|
|
// This method is faster than .bytes_trimmed(), but may be less convenient.
|
|
// Example: assert big.from_int(1).bytes()[0] == byte(0x01)
|
|
// Example: assert big.from_int(1024).bytes()[1] == byte(0x04)
|
|
// Example: assert big.from_int(1048576).bytes()[2] == byte(0x10)
|
|
pub fn (n &Number) bytes() []byte {
|
|
mut res := []byte{len: 128, init: 0}
|
|
unsafe { C.memcpy(res.data, n, 128) }
|
|
return res
|
|
}
|
|
|
|
// bytes_trimmed returns an array of the bytes for the number `n`,
|
|
// in little endian format, where .bytes_trimmed()[0] is the least
|
|
// significant byte. The result is trimmed, so that *the last* byte
|
|
// of the result is also the the last meaningfull byte, != 0 .
|
|
// Example: assert big.from_int(1).bytes_trimmed() == [byte(0x01)]
|
|
// Example: assert big.from_int(1024).bytes_trimmed() == [byte(0x00), 0x04]
|
|
// Example: assert big.from_int(1048576).bytes_trimmed() == [byte(0x00), 0x00, 0x10]
|
|
pub fn (n &Number) bytes_trimmed() []byte {
|
|
mut res := []byte{len: 128, init: 0}
|
|
unsafe { C.memcpy(res.data, n, 128) }
|
|
mut non_zero_idx := 127
|
|
for ; non_zero_idx >= 0; non_zero_idx-- {
|
|
if res[non_zero_idx] != 0 {
|
|
break
|
|
}
|
|
}
|
|
res.trim(non_zero_idx + 1)
|
|
return res
|
|
}
|