1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

bignum module wrapper for kokke/tiny-bignum-c

This commit is contained in:
Delyan Angelov 2019-11-07 21:04:18 +02:00 committed by Alexander Medvednikov
parent 7a29d959ce
commit a44ba0b8a2
10 changed files with 1170 additions and 33 deletions

2
thirdparty/bignum/README.md vendored Normal file
View File

@ -0,0 +1,2 @@
This folder contains bn.h and bn.c files
from https://github.com/kokke/tiny-bignum-c

664
thirdparty/bignum/bn.c vendored Normal file
View File

@ -0,0 +1,664 @@
/*
Big number library - arithmetic on multiple-precision unsigned integers.
This library is an implementation of arithmetic on arbitrarily large integers.
The difference between this and other implementations, is that the data structure
has optimal memory utilization (i.e. a 1024 bit integer takes up 128 bytes RAM),
and all memory is allocated statically: no dynamic allocation for better or worse.
Primary goals are correctness, clarity of code and clean, portable implementation.
Secondary goal is a memory footprint small enough to make it suitable for use in
embedded applications.
The current state is correct functionality and adequate performance.
There may well be room for performance-optimizations and improvements.
*/
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include "bn.h"
/* Functions for shifting number in-place. */
static void _lshift_one_bit(struct bn* a);
static void _rshift_one_bit(struct bn* a);
static void _lshift_word(struct bn* a, int nwords);
static void _rshift_word(struct bn* a, int nwords);
/* Public / Exported functions. */
void bignum_init(struct bn* n)
{
require(n, "n is null");
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
n->array[i] = 0;
}
}
void bignum_from_int(struct bn* n, DTYPE_TMP i)
{
require(n, "n is null");
bignum_init(n);
/* Endianness issue if machine is not little-endian? */
#ifdef WORD_SIZE
#if (WORD_SIZE == 1)
n->array[0] = (i & 0x000000ff);
n->array[1] = (i & 0x0000ff00) >> 8;
n->array[2] = (i & 0x00ff0000) >> 16;
n->array[3] = (i & 0xff000000) >> 24;
#elif (WORD_SIZE == 2)
n->array[0] = (i & 0x0000ffff);
n->array[1] = (i & 0xffff0000) >> 16;
#elif (WORD_SIZE == 4)
n->array[0] = i;
DTYPE_TMP num_32 = 32;
DTYPE_TMP tmp = i >> num_32; /* bit-shift with U64 operands to force 64-bit results */
n->array[1] = tmp;
#endif
#endif
}
int bignum_to_int(struct bn* n)
{
require(n, "n is null");
int ret = 0;
/* Endianness issue if machine is not little-endian? */
#if (WORD_SIZE == 1)
ret += n->array[0];
ret += n->array[1] << 8;
ret += n->array[2] << 16;
ret += n->array[3] << 24;
#elif (WORD_SIZE == 2)
ret += n->array[0];
ret += n->array[1] << 16;
#elif (WORD_SIZE == 4)
ret += n->array[0];
#endif
return ret;
}
void bignum_from_string(struct bn* n, char* str, int nbytes)
{
require(n, "n is null");
require(str, "str is null");
require(nbytes > 0, "nbytes must be positive");
require((nbytes & 1) == 0, "string format must be in hex -> equal number of bytes");
bignum_init(n);
DTYPE tmp; /* DTYPE is defined in bn.h - uint{8,16,32,64}_t */
int i = nbytes - (2 * WORD_SIZE); /* index into string */
int j = 0; /* index into array */
/* reading last hex-byte "MSB" from string first -> big endian */
/* MSB ~= most significant byte / block ? :) */
while (i >= 0)
{
tmp = 0;
sscanf(&str[i], SSCANF_FORMAT_STR, &tmp);
n->array[j] = tmp;
i -= (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) back in the string. */
j += 1; /* step one element forward in the array. */
}
}
void bignum_to_string(struct bn* n, char* str, int nbytes)
{
require(n, "n is null");
require(str, "str is null");
require(nbytes > 0, "nbytes must be positive");
require((nbytes & 1) == 0, "string format must be in hex -> equal number of bytes");
int j = BN_ARRAY_SIZE - 1; /* index into array - reading "MSB" first -> big-endian */
int i = 0; /* index into string representation. */
/* reading last array-element "MSB" first -> big endian */
while ((j >= 0) && (nbytes > (i + 1)))
{
sprintf(&str[i], SPRINTF_FORMAT_STR, n->array[j]);
i += (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) forward in the string. */
j -= 1; /* step one element back in the array. */
}
/* Count leading zeros: */
j = 0;
while (str[j] == '0')
{
j += 1;
}
/* Move string j places ahead, effectively skipping leading zeros */
for (i = 0; i < (nbytes - j); ++i)
{
str[i] = str[i + j];
}
/* Zero-terminate string */
str[i] = 0;
}
void bignum_dec(struct bn* n)
{
require(n, "n is null");
DTYPE tmp; /* copy of n */
DTYPE res;
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
tmp = n->array[i];
res = tmp - 1;
n->array[i] = res;
if (!(res > tmp))
{
break;
}
}
}
void bignum_inc(struct bn* n)
{
require(n, "n is null");
DTYPE res;
DTYPE_TMP tmp; /* copy of n */
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
tmp = n->array[i];
res = tmp + 1;
n->array[i] = res;
if (res > tmp)
{
break;
}
}
}
void bignum_add(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
DTYPE_TMP tmp;
int carry = 0;
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
tmp = (DTYPE_TMP)a->array[i] + b->array[i] + carry;
carry = (tmp > MAX_VAL);
c->array[i] = (tmp & MAX_VAL);
}
}
void bignum_sub(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
DTYPE_TMP res;
DTYPE_TMP tmp1;
DTYPE_TMP tmp2;
int borrow = 0;
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
tmp1 = (DTYPE_TMP)a->array[i] + (MAX_VAL + 1); /* + number_base */
tmp2 = (DTYPE_TMP)b->array[i] + borrow;;
res = (tmp1 - tmp2);
c->array[i] = (DTYPE)(res & MAX_VAL); /* "modulo number_base" == "% (number_base - 1)" if number_base is 2^N */
borrow = (res <= MAX_VAL);
}
}
void bignum_mul(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
struct bn row;
struct bn tmp;
int i, j;
bignum_init(c);
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
bignum_init(&row);
for (j = 0; j < BN_ARRAY_SIZE; ++j)
{
if (i + j < BN_ARRAY_SIZE)
{
bignum_init(&tmp);
DTYPE_TMP intermediate = ((DTYPE_TMP)a->array[i] * (DTYPE_TMP)b->array[j]);
bignum_from_int(&tmp, intermediate);
_lshift_word(&tmp, i + j);
bignum_add(&tmp, &row, &row);
}
}
bignum_add(c, &row, c);
}
}
void bignum_div(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
struct bn current;
struct bn denom;
struct bn tmp;
bignum_from_int(&current, 1); // int current = 1;
bignum_assign(&denom, b); // denom = b
bignum_assign(&tmp, a); // tmp = a
const DTYPE_TMP half_max = 1 + (DTYPE_TMP)(MAX_VAL / 2);
bool overflow = false;
while (bignum_cmp(&denom, a) != LARGER) // while (denom <= a) {
{
if (denom.array[BN_ARRAY_SIZE - 1] >= half_max)
{
overflow = true;
break;
}
_lshift_one_bit(&current); // current <<= 1;
_lshift_one_bit(&denom); // denom <<= 1;
}
if (!overflow)
{
_rshift_one_bit(&denom); // denom >>= 1;
_rshift_one_bit(&current); // current >>= 1;
}
bignum_init(c); // int answer = 0;
while (!bignum_is_zero(&current)) // while (current != 0)
{
if (bignum_cmp(&tmp, &denom) != SMALLER) // if (dividend >= denom)
{
bignum_sub(&tmp, &denom, &tmp); // dividend -= denom;
bignum_or(c, &current, c); // answer |= current;
}
_rshift_one_bit(&current); // current >>= 1;
_rshift_one_bit(&denom); // denom >>= 1;
} // return answer;
}
void bignum_lshift(struct bn* a, struct bn* b, int nbits)
{
require(a, "a is null");
require(b, "b is null");
require(nbits >= 0, "no negative shifts");
bignum_assign(b, a);
/* Handle shift in multiples of word-size */
const int nbits_pr_word = (WORD_SIZE * 8);
int nwords = nbits / nbits_pr_word;
if (nwords != 0)
{
_lshift_word(b, nwords);
nbits -= (nwords * nbits_pr_word);
}
if (nbits != 0)
{
int i;
for (i = (BN_ARRAY_SIZE - 1); i > 0; --i)
{
b->array[i] = (b->array[i] << nbits) | (b->array[i - 1] >> ((8 * WORD_SIZE) - nbits));
}
b->array[i] <<= nbits;
}
}
void bignum_rshift(struct bn* a, struct bn* b, int nbits)
{
require(a, "a is null");
require(b, "b is null");
require(nbits >= 0, "no negative shifts");
bignum_assign(b, a);
/* Handle shift in multiples of word-size */
const int nbits_pr_word = (WORD_SIZE * 8);
int nwords = nbits / nbits_pr_word;
if (nwords != 0)
{
_rshift_word(b, nwords);
nbits -= (nwords * nbits_pr_word);
}
if (nbits != 0)
{
int i;
for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i)
{
b->array[i] = (b->array[i] >> nbits) | (b->array[i + 1] << ((8 * WORD_SIZE) - nbits));
}
b->array[i] >>= nbits;
}
}
void bignum_mod(struct bn* a, struct bn* b, struct bn* c)
{
/*
Take divmod and throw away div part
*/
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
struct bn tmp;
bignum_divmod(a,b,&tmp,c);
}
void bignum_divmod(struct bn* a, struct bn* b, struct bn* c, struct bn* d)
{
/*
Puts a%b in d
and a/b in c
mod(a,b) = a - ((a / b) * b)
example:
mod(8, 3) = 8 - ((8 / 3) * 3) = 2
*/
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
struct bn tmp;
/* c = (a / b) */
bignum_div(a, b, c);
/* tmp = (c * b) */
bignum_mul(c, b, &tmp);
/* c = a - tmp */
bignum_sub(a, &tmp, d);
}
void bignum_and(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
c->array[i] = (a->array[i] & b->array[i]);
}
}
void bignum_or(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
c->array[i] = (a->array[i] | b->array[i]);
}
}
void bignum_xor(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
c->array[i] = (a->array[i] ^ b->array[i]);
}
}
int bignum_cmp(struct bn* a, struct bn* b)
{
require(a, "a is null");
require(b, "b is null");
int i = BN_ARRAY_SIZE;
do
{
i -= 1; /* Decrement first, to start with last array element */
if (a->array[i] > b->array[i])
{
return LARGER;
}
else if (a->array[i] < b->array[i])
{
return SMALLER;
}
}
while (i != 0);
return EQUAL;
}
int bignum_is_zero(struct bn* n)
{
require(n, "n is null");
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
if (n->array[i])
{
return 0;
}
}
return 1;
}
void bignum_pow(struct bn* a, struct bn* b, struct bn* c)
{
require(a, "a is null");
require(b, "b is null");
require(c, "c is null");
struct bn tmp;
bignum_init(c);
if (bignum_cmp(b, c) == EQUAL)
{
/* Return 1 when exponent is 0 -- n^0 = 1 */
bignum_inc(c);
}
else
{
struct bn bcopy;
bignum_assign(&bcopy, b);
/* Copy a -> tmp */
bignum_assign(&tmp, a);
bignum_dec(&bcopy);
/* Begin summing products: */
while (!bignum_is_zero(&bcopy))
{
/* c = tmp * tmp */
bignum_mul(&tmp, a, c);
/* Decrement b by one */
bignum_dec(&bcopy);
bignum_assign(&tmp, c);
}
/* c = tmp */
bignum_assign(c, &tmp);
}
}
void bignum_isqrt(struct bn *a, struct bn* b)
{
require(a, "a is null");
require(b, "b is null");
struct bn low, high, mid, tmp;
bignum_init(&low);
bignum_assign(&high, a);
bignum_rshift(&high, &mid, 1);
bignum_inc(&mid);
while (bignum_cmp(&high, &low) > 0)
{
bignum_mul(&mid, &mid, &tmp);
if (bignum_cmp(&tmp, a) > 0)
{
bignum_assign(&high, &mid);
bignum_dec(&high);
}
else
{
bignum_assign(&low, &mid);
}
bignum_sub(&high,&low,&mid);
_rshift_one_bit(&mid);
bignum_add(&low,&mid,&mid);
bignum_inc(&mid);
}
bignum_assign(b,&low);
}
void bignum_assign(struct bn* dst, struct bn* src)
{
require(dst, "dst is null");
require(src, "src is null");
int i;
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
dst->array[i] = src->array[i];
}
}
/* Private / Static functions. */
static void _rshift_word(struct bn* a, int nwords)
{
/* Naive method: */
require(a, "a is null");
require(nwords >= 0, "no negative shifts");
int i;
if (nwords >= BN_ARRAY_SIZE)
{
for (i = 0; i < BN_ARRAY_SIZE; ++i)
{
a->array[i] = 0;
}
return;
}
for (i = 0; i < BN_ARRAY_SIZE - nwords; ++i)
{
a->array[i] = a->array[i + nwords];
}
for (; i < BN_ARRAY_SIZE; ++i)
{
a->array[i] = 0;
}
}
static void _lshift_word(struct bn* a, int nwords)
{
require(a, "a is null");
require(nwords >= 0, "no negative shifts");
int i;
/* Shift whole words */
for (i = (BN_ARRAY_SIZE - 1); i >= nwords; --i)
{
a->array[i] = a->array[i - nwords];
}
/* Zero pad shifted words. */
for (; i >= 0; --i)
{
a->array[i] = 0;
}
}
static void _lshift_one_bit(struct bn* a)
{
require(a, "a is null");
int i;
for (i = (BN_ARRAY_SIZE - 1); i > 0; --i)
{
a->array[i] = (a->array[i] << 1) | (a->array[i - 1] >> ((8 * WORD_SIZE) - 1));
}
a->array[0] <<= 1;
}
static void _rshift_one_bit(struct bn* a)
{
require(a, "a is null");
int i;
for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i)
{
a->array[i] = (a->array[i] >> 1) | (a->array[i + 1] << ((8 * WORD_SIZE) - 1));
}
a->array[BN_ARRAY_SIZE - 1] >>= 1;
}

123
thirdparty/bignum/bn.h vendored Normal file
View File

@ -0,0 +1,123 @@
#ifndef __BIGNUM_H__
#define __BIGNUM_H__
/*
Big number library - arithmetic on multiple-precision unsigned integers.
This library is an implementation of arithmetic on arbitrarily large integers.
The difference between this and other implementations, is that the data structure
has optimal memory utilization (i.e. a 1024 bit integer takes up 128 bytes RAM),
and all memory is allocated statically: no dynamic allocation for better or worse.
Primary goals are correctness, clarity of code and clean, portable implementation.
Secondary goal is a memory footprint small enough to make it suitable for use in
embedded applications.
The current state is correct functionality and adequate performance.
There may well be room for performance-optimizations and improvements.
*/
#include <stdint.h>
#include <assert.h>
/* This macro defines the word size in bytes of the array that constitues the big-number data structure. */
#ifndef WORD_SIZE
#define WORD_SIZE 4
#endif
/* Size of big-numbers in bytes */
#define BN_ARRAY_SIZE (128 / WORD_SIZE)
/* Here comes the compile-time specialization for how large the underlying array size should be. */
/* The choices are 1, 2 and 4 bytes in size with uint32, uint64 for WORD_SIZE==4, as temporary. */
#ifndef WORD_SIZE
#error Must define WORD_SIZE to be 1, 2, 4
#elif (WORD_SIZE == 1)
/* Data type of array in structure */
#define DTYPE uint8_t
/* bitmask for getting MSB */
#define DTYPE_MSB ((DTYPE_TMP)(0x80))
/* Data-type larger than DTYPE, for holding intermediate results of calculations */
#define DTYPE_TMP uint32_t
/* sprintf format string */
#define SPRINTF_FORMAT_STR "%.02x"
#define SSCANF_FORMAT_STR "%2hhx"
/* Max value of integer type */
#define MAX_VAL ((DTYPE_TMP)0xFF)
#elif (WORD_SIZE == 2)
#define DTYPE uint16_t
#define DTYPE_TMP uint32_t
#define DTYPE_MSB ((DTYPE_TMP)(0x8000))
#define SPRINTF_FORMAT_STR "%.04x"
#define SSCANF_FORMAT_STR "%4hx"
#define MAX_VAL ((DTYPE_TMP)0xFFFF)
#elif (WORD_SIZE == 4)
#define DTYPE uint32_t
#define DTYPE_TMP uint64_t
#define DTYPE_MSB ((DTYPE_TMP)(0x80000000))
#define SPRINTF_FORMAT_STR "%.08x"
#define SSCANF_FORMAT_STR "%8x"
#define MAX_VAL ((DTYPE_TMP)0xFFFFFFFF)
#endif
#ifndef DTYPE
#error DTYPE must be defined to uint8_t, uint16_t uint32_t or whatever
#endif
/* Custom assert macro - easy to disable */
#define require(p, msg) assert(p && #msg)
/* Data-holding structure: array of DTYPEs */
struct bn
{
DTYPE array[BN_ARRAY_SIZE];
};
/* Tokens returned by bignum_cmp() for value comparison */
enum { SMALLER = -1, EQUAL = 0, LARGER = 1 };
/* Initialization functions: */
void bignum_init(struct bn* n);
void bignum_from_int(struct bn* n, DTYPE_TMP i);
int bignum_to_int(struct bn* n);
void bignum_from_string(struct bn* n, char* str, int nbytes);
void bignum_to_string(struct bn* n, char* str, int maxsize);
/* Basic arithmetic operations: */
void bignum_add(struct bn* a, struct bn* b, struct bn* c); /* c = a + b */
void bignum_sub(struct bn* a, struct bn* b, struct bn* c); /* c = a - b */
void bignum_mul(struct bn* a, struct bn* b, struct bn* c); /* c = a * b */
void bignum_div(struct bn* a, struct bn* b, struct bn* c); /* c = a / b */
void bignum_mod(struct bn* a, struct bn* b, struct bn* c); /* c = a % b */
void bignum_divmod(struct bn* a, struct bn* b, struct bn* c, struct bn* d); /* c = a/b, d = a%b */
/* Bitwise operations: */
void bignum_and(struct bn* a, struct bn* b, struct bn* c); /* c = a & b */
void bignum_or(struct bn* a, struct bn* b, struct bn* c); /* c = a | b */
void bignum_xor(struct bn* a, struct bn* b, struct bn* c); /* c = a ^ b */
void bignum_lshift(struct bn* a, struct bn* b, int nbits); /* b = a << nbits */
void bignum_rshift(struct bn* a, struct bn* b, int nbits); /* b = a >> nbits */
/* Special operators and comparison */
int bignum_cmp(struct bn* a, struct bn* b); /* Compare: returns LARGER, EQUAL or SMALLER */
int bignum_is_zero(struct bn* n); /* For comparison with zero */
void bignum_inc(struct bn* n); /* Increment: add one to n */
void bignum_dec(struct bn* n); /* Decrement: subtract one from n */
void bignum_pow(struct bn* a, struct bn* b, struct bn* c); /* Calculate a^b -- e.g. 2^10 => 1024 */
void bignum_isqrt(struct bn* a, struct bn* b); /* Integer square root -- e.g. isqrt(5) => 2*/
void bignum_assign(struct bn* dst, struct bn* src); /* Copy src into dst -- dst := src */
#endif /* #ifndef __BIGNUM_H__ */

195
vlib/bignum/bignum.v Normal file
View File

@ -0,0 +1,195 @@
module bignum
// Wrapper for https://github.com/kokke/tiny-bignum-c
#flag -I @VROOT/thirdparty/bignum
#flag @VROOT/thirdparty/bignum/bn.o
#include "bn.h"
const (
STRING_BUFFER_SIZE = 8192
)
pub struct Number {
array [32]u32
}
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
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_cmp( a &Number, b &Number) int
fn C.bignum_is_zero( a &Number) int
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
////////////////////////////////////////////////////////////
// conversion actions to/from big numbers:
pub fn new_bignum() Number {
return Number{}
}
pub fn from_int(i int) Number {
n := Number{}
C.bignum_from_int( &n, i)
return n
}
pub fn from_u64(u u64) Number {
n := Number{}
C.bignum_from_int( &n, u)
return n
}
pub fn from_string(s string) Number {
n := Number{}
C.bignum_from_string(&n, s.str, s.len)
return n
}
pub fn (n Number) int() int {
r := C.bignum_to_int(&n)
return r
}
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()
}
pub fn (n Number) hexstr() string {
mut buf := [STRING_BUFFER_SIZE]byte
C.bignum_to_string( &n, buf, STRING_BUFFER_SIZE)
// NB: bignum_to_string , returns the HEXADECIMAL representation of the bignum n
s := tos_clone( buf )
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
}
pub fn divmod( a &Number, b &Number, c &Number) Number {
d := 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 int(C.bignum_is_zero(&a)) != 0
}
pub fn (a mut Number) inc() {
C.bignum_inc(a)
}
pub fn (a mut 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 bignum.Number) bignum.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) bignum.Number {
return factorial( bignum.from_int(n) )
}

86
vlib/bignum/bignum_test.v Normal file
View File

@ -0,0 +1,86 @@
import bignum
fn test_new_bignum(){
n := bignum.new_bignum()
assert sizeof( bignum.Number ) == 128
assert n.hexstr() == '0'
}
fn test_from_int(){
assert bignum.from_int(255).hexstr() == 'ff'
assert bignum.from_int(127).hexstr() == '7f'
assert bignum.from_int(1024).hexstr() == '400'
assert bignum.from_int(2147483647).hexstr() == '7fffffff'
assert bignum.from_int(-1).hexstr() == 'ffffffffffffffff'
}
fn test_from_u64(){
assert bignum.from_u64(255).hexstr() == 'ff'
assert bignum.from_u64(127).hexstr() == '7f'
assert bignum.from_u64(1024).hexstr() == '400'
assert bignum.from_u64(4294967295).hexstr() == 'ffffffff'
assert bignum.from_u64(4398046511104).hexstr() == '40000000000'
assert bignum.from_u64(-1).hexstr() == 'ffffffffffffffff'
}
fn test_plus(){
a := bignum.from_u64(2)
b := bignum.from_u64(3)
c := a + b
assert c.hexstr() == '5'
assert (bignum.from_u64(1024) + bignum.from_u64(1024)).hexstr() == '800'
}
fn test_minus(){
a := bignum.from_u64(2)
b := bignum.from_u64(3)
c := b - a
assert c.hexstr() == '1'
e := bignum.from_u64(1024)
ee := e - e
assert ee.hexstr() == '0'
}
fn test_divide(){
a := bignum.from_u64(2)
b := bignum.from_u64(3)
c := b / a
assert c.hexstr() == '1'
assert (b % a ).hexstr() == '1'
e := bignum.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'
}
fn test_multiply(){
a := bignum.from_u64(2)
b := bignum.from_u64(3)
c := b * a
assert c.hexstr() == '6'
e := bignum.from_u64(1024)
e2 := e * e
e4 := e2 * e2
e8 := e2 * e2 * e2 * e2
e9 := e8 + bignum.from_u64(1)
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 (bignum.from_u64(13) % bignum.from_u64(10) ).int() == 3
assert (bignum.from_u64(13) % bignum.from_u64(9) ).int() == 4
assert (bignum.from_u64(7) % bignum.from_u64(5) ).int() == 2
}
fn test_factorial(){
f5 := bignum.factorial( bignum.from_u64(5) )
assert f5.hexstr() == '78'
f100 := bignum.factorial( bignum.from_u64(100) )
assert f100.hexstr() == '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000'
}

View File

@ -448,34 +448,29 @@ fn (p mut Parser) expression() string {
// Make sure operators are used with correct types // Make sure operators are used with correct types
if !p.pref.translated && !is_str && !is_ustr && !is_num { if !p.pref.translated && !is_str && !is_ustr && !is_num {
T := p.table.find_type(typ) T := p.table.find_type(typ)
if tok_op == .plus { if tok_op == .plus { p.handle_operator('+', typ, 'op_plus', ph, T) }
if T.has_method('+') { else if tok_op == .minus { p.handle_operator('-', typ, 'op_minus', ph, T) }
p.cgen.set_placeholder(ph, typ + '_plus(')
p.gen(')')
}
else {
p.error('operator + not defined on `$typ`')
}
}
else if tok_op == .minus {
if T.has_method('-') {
p.cgen.set_placeholder(ph, '${typ}_minus(')
p.gen(')')
}
else {
p.error('operator - not defined on `$typ`')
}
}
} }
} }
return typ return typ
} }
fn (p mut Parser) handle_operator(op string, typ string, cpostfix string, ph int, T &Type) {
if T.has_method( op ) {
p.cgen.set_placeholder(ph, '${typ}_${cpostfix}(')
p.gen(')')
}
else {
p.error('operator $op not defined on `$typ`')
}
}
fn (p mut Parser) term() string { fn (p mut Parser) term() string {
line_nr := p.scanner.line_nr line_nr := p.scanner.line_nr
//if p.fileis('fn_test') { //if p.fileis('fn_test') {
//println('\nterm() $line_nr') //println('\nterm() $line_nr')
//} //}
ph := p.cgen.add_placeholder()
typ := p.unary() typ := p.unary()
//if p.fileis('fn_test') { //if p.fileis('fn_test') {
//println('2: $line_nr') //println('2: $line_nr')
@ -491,11 +486,28 @@ fn (p mut Parser) term() string {
// is_mul := tok == .mod // is_mul := tok == .mod
p.next() p.next()
p.gen(tok.str())// + ' /*op2*/ ') p.gen(tok.str())// + ' /*op2*/ ')
oph := p.cgen.add_placeholder()
p.fgen(' ' + tok.str() + ' ') p.fgen(' ' + tok.str() + ' ')
if (is_div || is_mod) && p.tok == .number && p.lit == '0' { if (is_div || is_mod) && p.tok == .number && p.lit == '0' {
p.error('division or modulo by zero') p.error('division or modulo by zero')
} }
expr_type := p.unary() expr_type := p.unary()
if !is_primitive_type(expr_type) && expr_type == typ {
p.check_types(expr_type, typ)
T := p.table.find_type(typ)
// NB: oph is a char index just after the OP
before_oph := p.cgen.cur_line[..oph-1]
after_oph := p.cgen.cur_line[oph..]
p.cgen.cur_line = before_oph + ',' + after_oph
match tok {
.mul { p.handle_operator('*', typ, 'op_mul', ph, T) }
.div { p.handle_operator('/', typ, 'op_div', ph, T) }
.mod { p.handle_operator('%', typ, 'op_mod', ph, T) }
}
continue
}
if is_mod { if is_mod {
if !(is_integer_type(expr_type) && is_integer_type(typ)) { if !(is_integer_type(expr_type) && is_integer_type(typ)) {
p.error('operator `mod` requires integer types') p.error('operator `mod` requires integer types')

View File

@ -265,7 +265,7 @@ fn (p mut Parser) fn_decl() {
p.register_var(receiver) p.register_var(receiver)
} }
// +-/* methods // +-/* methods
if p.tok in [.plus, .minus, .mul] { if p.tok in [.plus, .minus, .mul, .div, .mod] {
f.name = p.tok.str() f.name = p.tok.str()
p.next() p.next()
} }

View File

@ -228,9 +228,15 @@ fn (table mut Table) fn_gen_name(f &Fn) string {
if f.is_method { if f.is_method {
name = '${f.receiver_typ}_$f.name' name = '${f.receiver_typ}_$f.name'
name = name.replace(' ', '') name = name.replace(' ', '')
name = name.replace('*', '') if f.name.len == 1 {
name = name.replace('+', 'plus') match f.name[0] {
name = name.replace('-', 'minus') `+` { name = name.replace('+', 'op_plus') }
`-` { name = name.replace('-', 'op_minus') }
`*` { name = name.replace('*', 'op_mul') }
`/` { name = name.replace('/', 'op_div') }
`%` { name = name.replace('%', 'op_mod') }
}
}
} }
// Avoid name conflicts (with things like abs(), print() etc). // Avoid name conflicts (with things like abs(), print() etc).
// Generate v_abs(), v_print() // Generate v_abs(), v_print()

View File

@ -6,6 +6,35 @@ module math
#include <math.h> #include <math.h>
fn C.acos(x f64) f64
fn C.asin(x f64) f64
fn C.atan(x f64) f64
fn C.atan2(y f64, x f64) f64
fn C.cbrt(x f64) f64
fn C.ceil(x f64) f64
fn C.cos(x f64) f64
fn C.cosh(x f64) f64
fn C.erf(x f64) f64
fn C.erfc(x f64) f64
fn C.exp(x f64) f64
fn C.exp2(x f64) f64
fn C.floor(x f64) f64
fn C.fmod(x f64, y f64) f64
fn C.hypot(x f64, y f64) f64
fn C.log(x f64) f64
fn C.log2(x f64) f64
fn C.log10(x f64) f64
fn C.lgamma(x f64) f64
fn C.pow(x f64, y f64) f64
fn C.round(x f64) f64
fn C.sin(x f64) f64
fn C.sqrt(x f64) f64
fn C.tgamma(x f64) f64
fn C.tan(x f64) f64
fn C.tanh(x f64) f64
fn C.trunc(x f64) f64
// NOTE // NOTE
// When adding a new function, please make sure it's in the right place. // When adding a new function, please make sure it's in the right place.
// All functions are sorted alphabetically. // All functions are sorted alphabetically.
@ -18,8 +47,6 @@ pub fn abs(a f64) f64 {
return a return a
} }
fn C.acos(a f64) f64
// acos calculates inverse cosine (arccosine). // acos calculates inverse cosine (arccosine).
pub fn acos(a f64) f64 { pub fn acos(a f64) f64 {
return C.acos(a) return C.acos(a)

View File

@ -1,5 +1,9 @@
import os import os
fn test_aaa_setup(){
cleanup_leftovers() assert true
}
fn test_setenv() { fn test_setenv() {
os.setenv('foo', 'bar', true) os.setenv('foo', 'bar', true)
assert os.getenv('foo') == 'bar' assert os.getenv('foo') == 'bar'
@ -114,12 +118,11 @@ fn test_walk() {
} }
fn test_cp() { fn test_cp() {
$if windows { old_file_name := 'cp_example.txt'
old_file_name := './example.txt' new_file_name := 'cp_new_example.txt'
new_file_name := './new_example.txt'
os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~') os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~')
result := os.cp(old_file_name, new_file_name) or { panic('$err: errcode: $errcode') } os.cp(old_file_name, new_file_name) or { panic('$err: errcode: $errcode') }
old_file := os.read_file(old_file_name) or { panic(err) } old_file := os.read_file(old_file_name) or { panic(err) }
new_file := os.read_file(new_file_name) or { panic(err) } new_file := os.read_file(new_file_name) or { panic(err) }
@ -127,13 +130,11 @@ fn test_cp() {
os.rm(old_file_name) os.rm(old_file_name)
os.rm(new_file_name) os.rm(new_file_name)
}
} }
fn test_cp_r() { fn test_cp_r() {
//fileX -> dir/fileX //fileX -> dir/fileX
// TODO clean up the files // NB: clean up of the files happens inside the cleanup_leftovers function
/*
os.write_file('ex1.txt', 'wow!') os.write_file('ex1.txt', 'wow!')
os.mkdir('ex') os.mkdir('ex')
os.cp_r('ex1.txt', 'ex', false) or { panic(err) } os.cp_r('ex1.txt', 'ex', false) or { panic(err) }
@ -148,7 +149,6 @@ fn test_cp_r() {
assert old2 == new2 assert old2 == new2
//recurring on dir -> local dir //recurring on dir -> local dir
os.cp_r('ex', './', true) or { panic(err) } os.cp_r('ex', './', true) or { panic(err) }
*/
} }
//fn test_fork() { //fn test_fork() {
@ -173,3 +173,25 @@ fn test_cp_r() {
// println(cpid) // println(cpid)
// } // }
//} //}
fn test_zzz_cleanup(){
cleanup_leftovers() assert true
}
// this function is called by both test_aaa_setup & test_zzz_cleanup
// it ensures that os tests do not polute the filesystem with leftover
// files so that they can be run several times in a row.
fn cleanup_leftovers(){
// possible leftovers from test_cp
os.rm('cp_example.txt')
os.rm('cp_new_example.txt')
// possible leftovers from test_cp_r
os.rm('ex/ex2/ex2.txt')
os.rm('ex/ex2')
os.rm('ex/ex1.txt')
os.rm('ex')
os.rm('ex2/ex2.txt')
os.rm('ex2')
}