From 069d77d1c513f47772679bd52f5afe05d239e654 Mon Sep 17 00:00:00 2001 From: LilEnvy <75917200+lilenvy@users.noreply.github.com> Date: Mon, 14 Dec 2020 01:53:18 -0800 Subject: [PATCH] math.big: implement decimal .str() for big numbers (#7314) --- vlib/math/big/big.v | 179 ++++++++++++++++++++++++++------------- vlib/math/big/big_test.v | 50 ++++++----- 2 files changed, 153 insertions(+), 76 deletions(-) diff --git a/vlib/math/big/big.v b/vlib/math/big/big.v index a8a01767a6..453e59131a 100644 --- a/vlib/math/big/big.v +++ b/vlib/math/big/big.v @@ -1,61 +1,98 @@ 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_init(n &Number) -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 +fn C.bignum_from_int(n &Number, i u64) -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 +fn C.bignum_to_int(n &Number) int -fn C.bignum_cmp( a &Number, b &Number) int -fn C.bignum_is_zero( a &Number) int +fn C.bignum_from_string(n &Number, s byteptr, nbytes int) + +fn C.bignum_to_string(n &Number, s byteptr, 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) -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 -//////////////////////////////////////////////////////////// +// 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) + +// ////////////////////////////////////////////////////////// // 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) + C.bignum_from_int(&n, i) return n } pub fn from_u64(u u64) Number { n := Number{} - C.bignum_from_int( &n, u) + 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,119 +104,147 @@ 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 { mut buf := [8192]byte{} - C.bignum_to_string( &n, buf, 8192) + 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' } + s := tos_clone(buf) + if s.len == 0 { + return '0' + } return s } -//////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////// // overloaded ops for the numbers: -pub fn (a Number) + (b Number) Number { +pub fn (a Number) +(b Number) Number { c := Number{} C.bignum_add(&a, &b, &c) return c } -pub fn (a Number) - (b Number) Number { +pub fn (a Number) -(b Number) Number { c := Number{} C.bignum_sub(&a, &b, &c) return c } -pub fn (a Number) * (b Number) Number { +pub fn (a Number) *(b Number) Number { c := Number{} C.bignum_mul(&a, &b, &c) return c } -pub fn (a Number) / (b Number) Number { +pub fn (a Number) /(b Number) Number { c := Number{} C.bignum_div(&a, &b, &c) return c } -pub fn (a Number) % (b Number) Number { +pub fn (a Number) %(b Number) Number { c := Number{} C.bignum_mod(&a, &b, &c) return c } -pub fn divmod( a &Number, b &Number, c &Number) Number { +pub fn divmod(a &Number, b &Number, c &Number) Number { d := Number{} - C.bignum_divmod( a, b, c, &d) + C.bignum_divmod(a, b, c, &d) return d } -//////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////// pub fn cmp(a Number, b Number) int { - return C.bignum_cmp(&a,&b) + 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) + C.bignum_pow(&a, &b, &c) return c } + pub fn (a Number) isqrt() Number { b := Number{} - C.bignum_isqrt(&a,&b) + C.bignum_isqrt(&a, &b) return b } -//////////////////////////////////////////////////////////// + +// ////////////////////////////////////////////////////////// pub fn b_and(a Number, b Number) Number { c := Number{} - C.bignum_and(&a,&b,&c) + 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) + 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) + C.bignum_xor(&a, &b, &c) return c } + pub fn (a Number) lshift(nbits int) Number { b := Number{} - C.bignum_lshift(&a,&b,nbits) + C.bignum_lshift(&a, &b, nbits) return b } + pub fn (a Number) rshift(nbits int) Number { b := Number{} - C.bignum_rshift(&a,&b,nbits) + C.bignum_rshift(&a, &b, nbits) return b } + pub fn (a Number) clone() Number { b := Number{} - C.bignum_assign(&b,&a) + 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 + mut i := 1 for !n.is_zero() { res := a * n n.dec() @@ -190,5 +255,5 @@ pub fn factorial(nn Number) Number { } pub fn fact(n int) Number { - return factorial( from_int(n) ) + return factorial(from_int(n)) } diff --git a/vlib/math/big/big_test.v b/vlib/math/big/big_test.v index 439219bf0c..418863093e 100644 --- a/vlib/math/big/big_test.v +++ b/vlib/math/big/big_test.v @@ -1,12 +1,12 @@ import big -fn test_new_big(){ +fn test_new_big() { n := big.new() - assert sizeof( big.Number ) == 128 + assert sizeof(big.Number) == 128 assert n.hexstr() == '0' } -fn test_from_int(){ +fn test_from_int() { assert big.from_int(255).hexstr() == 'ff' assert big.from_int(127).hexstr() == '7f' assert big.from_int(1024).hexstr() == '400' @@ -14,7 +14,7 @@ fn test_from_int(){ assert big.from_int(-1).hexstr() == 'ffffffffffffffff' } -fn test_from_u64(){ +fn test_from_u64() { assert big.from_u64(255).hexstr() == 'ff' assert big.from_u64(127).hexstr() == '7f' assert big.from_u64(1024).hexstr() == '400' @@ -23,7 +23,7 @@ fn test_from_u64(){ assert big.from_u64(-1).hexstr() == 'ffffffffffffffff' } -fn test_plus(){ +fn test_plus() { a := big.from_u64(2) b := big.from_u64(3) c := a + b @@ -31,7 +31,7 @@ fn test_plus(){ assert (big.from_u64(1024) + big.from_u64(1024)).hexstr() == '800' } -fn test_minus(){ +fn test_minus() { a := big.from_u64(2) b := big.from_u64(3) c := b - a @@ -41,20 +41,20 @@ fn test_minus(){ assert ee.hexstr() == '0' } -fn test_divide(){ +fn test_divide() { a := big.from_u64(2) b := big.from_u64(3) c := b / a assert c.hexstr() == '1' - assert (b % a ).hexstr() == '1' + assert (b % a).hexstr() == '1' e := big.from_u64(1024) // dec(1024) == hex(0x400) ee := e / e assert ee.hexstr() == '1' assert (e / a).hexstr() == '200' - assert (e / (a*a)).hexstr() == '100' + assert (e / (a * a)).hexstr() == '100' } -fn test_multiply(){ +fn test_multiply() { a := big.from_u64(2) b := big.from_u64(3) c := b * a @@ -64,23 +64,35 @@ fn test_multiply(){ e4 := e2 * e2 e8 := e2 * e2 * e2 * e2 e9 := e8 + big.from_u64(1) - d := ((e9 * e9) + b) * c + d := ((e9 * e9) + b) * c assert e4.hexstr() == '10000000000' assert e8.hexstr() == '100000000000000000000' assert e9.hexstr() == '100000000000000000001' assert d.hexstr() == '60000000000000000000c00000000000000000018' } -fn test_mod(){ - assert (big.from_u64(13) % big.from_u64(10) ).int() == 3 - assert (big.from_u64(13) % big.from_u64(9) ).int() == 4 - assert (big.from_u64(7) % big.from_u64(5) ).int() == 2 +fn test_mod() { + assert (big.from_u64(13) % big.from_u64(10)).int() == 3 + assert (big.from_u64(13) % big.from_u64(9)).int() == 4 + 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) ) +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' + f100 := big.factorial(big.from_u64(100)) + assert f100.hexstr() == + '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000' }