math.big: implement decimal .str() for big numbers (#7314)

pull/7285/head
LilEnvy 2020-12-14 01:53:18 -08:00 committed by GitHub
parent f57c7032b4
commit 069d77d1c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 76 deletions

View File

@ -1,50 +1,85 @@
module big
// Wrapper for https://github.com/kokke/tiny-bignum-c
#flag -I @VROOT/thirdparty/bignum
#flag @VROOT/thirdparty/bignum/bn.o
#include "bn.h"
[typedef]
struct C.bn {
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 byteptr, nbytes int)
fn C.bignum_to_string(n &Number, s byteptr, maxsize int)
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
fn C.bignum_divmod( a &Number, b &Number, c &Number, d &Number) // c = a/b d=a%b
// c = a + b
fn C.bignum_add(a &Number, b &Number, c &Number)
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 | b
fn C.bignum_xor( a &Number, b &Number, c &Number) // c = a xor b
fn C.bignum_lshift( a &Number, b &Number, nbits int) // b = a << nbits
fn C.bignum_rshift( a &Number, b &Number, nbits int) // b = a >> nbits
// 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)
fn C.bignum_pow( a &Number, b &Number, c &Number) // c = a ^ b
fn C.bignum_isqrt( a &Number, b &Number) // b = integer_square_root_of(a)
fn C.bignum_assign( dst &Number, src &Number) // copy src number to dst 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)
// //////////////////////////////////////////////////////////
// conversion actions to/from big numbers:
pub fn new() Number {
return Number{}
}
pub fn from_int(i int) Number {
n := Number{}
C.bignum_from_int(&n, i)
@ -56,6 +91,8 @@ pub fn from_u64(u u64) Number {
C.bignum_from_int(&n, u)
return n
}
// Converts a hex string to big.Number.
pub fn from_string(s string) Number {
n := Number{}
C.bignum_from_string(&n, s.str, s.len)
@ -67,12 +104,24 @@ pub fn (n Number) int() int {
return r
}
const (
ten = from_int(10)
)
// Decimal representation for the big unsigned integer number n.
pub fn (n Number) str() string {
// TODO: return a decimal representation of the bignumber n.
// A decimal representation will be easier to use in the repl
// but will be slower to calculate. Also, it is not implemented
// in the bn library.
return 'Number (in hex): ' + n.hexstr()
if n.is_zero() {
return '0'
}
mut digits := []byte{}
mut x := n.clone()
div := Number{}
for !x.is_zero() {
mod := divmod(&x, &ten, &div)
digits << byte(mod.int()) + `0`
x = div
}
return digits.reverse().bytestr()
}
pub fn (n Number) hexstr() string {
@ -80,9 +129,12 @@ pub fn (n Number) hexstr() string {
C.bignum_to_string(&n, buf, 8192)
// NB: bignum_to_string , returns the HEXADECIMAL representation of the bignum n
s := tos_clone(buf)
if s.len == 0 { return '0' }
if s.len == 0 {
return '0'
}
return s
}
// //////////////////////////////////////////////////////////
// overloaded ops for the numbers:
pub fn (a Number) +(b Number) Number {
@ -120,60 +172,73 @@ pub fn divmod( a &Number, b &Number, c &Number) Number {
C.bignum_divmod(a, b, c, &d)
return 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()

View File

@ -77,10 +77,22 @@ fn test_mod(){
assert (big.from_u64(7) % big.from_u64(5)).int() == 2
}
fn test_str() {
assert big.from_u64(255).str() == '255'
assert big.from_u64(127).str() == '127'
assert big.from_u64(1024).str() == '1024'
assert big.from_u64(4294967295).str() == '4294967295'
assert big.from_u64(4398046511104).str() == '4398046511104'
assert big.from_int(4294967295).str() == '18446744073709551615'
assert big.from_int(-1).str() == '18446744073709551615'
assert big.from_string('e'.repeat(80)).str() ==
'1993587900192849410235353592424915306962524220866209251950572167300738410728597846688097947807470'
}
fn test_factorial() {
f5 := big.factorial(big.from_u64(5))
assert f5.hexstr() == '78'
f100 := big.factorial(big.from_u64(100))
assert f100.hexstr() == '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000'
assert f100.hexstr() ==
'1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000'
}