From a44ba0b8a2de6a93773baa403b1681a757a819b3 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 7 Nov 2019 21:04:18 +0200 Subject: [PATCH] bignum module wrapper for kokke/tiny-bignum-c --- thirdparty/bignum/README.md | 2 + thirdparty/bignum/bn.c | 664 ++++++++++++++++++++++++++++++++++++ thirdparty/bignum/bn.h | 123 +++++++ vlib/bignum/bignum.v | 195 +++++++++++ vlib/bignum/bignum_test.v | 86 +++++ vlib/compiler/expression.v | 48 ++- vlib/compiler/fn.v | 2 +- vlib/compiler/gen_c.v | 12 +- vlib/math/math.v | 31 +- vlib/os/os_test.v | 40 ++- 10 files changed, 1170 insertions(+), 33 deletions(-) create mode 100644 thirdparty/bignum/README.md create mode 100644 thirdparty/bignum/bn.c create mode 100644 thirdparty/bignum/bn.h create mode 100644 vlib/bignum/bignum.v create mode 100644 vlib/bignum/bignum_test.v diff --git a/thirdparty/bignum/README.md b/thirdparty/bignum/README.md new file mode 100644 index 0000000000..04eb5552ee --- /dev/null +++ b/thirdparty/bignum/README.md @@ -0,0 +1,2 @@ +This folder contains bn.h and bn.c files +from https://github.com/kokke/tiny-bignum-c diff --git a/thirdparty/bignum/bn.c b/thirdparty/bignum/bn.c new file mode 100644 index 0000000000..7ec0895f99 --- /dev/null +++ b/thirdparty/bignum/bn.c @@ -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 +#include +#include +#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(¤t, 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(¤t); // current <<= 1; + _lshift_one_bit(&denom); // denom <<= 1; + } + if (!overflow) + { + _rshift_one_bit(&denom); // denom >>= 1; + _rshift_one_bit(¤t); // current >>= 1; + } + bignum_init(c); // int answer = 0; + + while (!bignum_is_zero(¤t)) // while (current != 0) + { + if (bignum_cmp(&tmp, &denom) != SMALLER) // if (dividend >= denom) + { + bignum_sub(&tmp, &denom, &tmp); // dividend -= denom; + bignum_or(c, ¤t, c); // answer |= current; + } + _rshift_one_bit(¤t); // 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; +} + + diff --git a/thirdparty/bignum/bn.h b/thirdparty/bignum/bn.h new file mode 100644 index 0000000000..f82676f879 --- /dev/null +++ b/thirdparty/bignum/bn.h @@ -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 +#include + + +/* 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__ */ + + diff --git a/vlib/bignum/bignum.v b/vlib/bignum/bignum.v new file mode 100644 index 0000000000..09576d29aa --- /dev/null +++ b/vlib/bignum/bignum.v @@ -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) ) +} diff --git a/vlib/bignum/bignum_test.v b/vlib/bignum/bignum_test.v new file mode 100644 index 0000000000..8b668a3f11 --- /dev/null +++ b/vlib/bignum/bignum_test.v @@ -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' +} diff --git a/vlib/compiler/expression.v b/vlib/compiler/expression.v index fa765731db..56197b6f33 100644 --- a/vlib/compiler/expression.v +++ b/vlib/compiler/expression.v @@ -448,34 +448,29 @@ fn (p mut Parser) expression() string { // Make sure operators are used with correct types if !p.pref.translated && !is_str && !is_ustr && !is_num { T := p.table.find_type(typ) - if tok_op == .plus { - if T.has_method('+') { - 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`') - } - } + if tok_op == .plus { p.handle_operator('+', typ, 'op_plus', ph, T) } + else if tok_op == .minus { p.handle_operator('-', typ, 'op_minus', ph, T) } } } 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 { line_nr := p.scanner.line_nr //if p.fileis('fn_test') { //println('\nterm() $line_nr') //} + ph := p.cgen.add_placeholder() typ := p.unary() //if p.fileis('fn_test') { //println('2: $line_nr') @@ -491,11 +486,28 @@ fn (p mut Parser) term() string { // is_mul := tok == .mod p.next() p.gen(tok.str())// + ' /*op2*/ ') + oph := p.cgen.add_placeholder() p.fgen(' ' + tok.str() + ' ') if (is_div || is_mod) && p.tok == .number && p.lit == '0' { p.error('division or modulo by zero') } 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_integer_type(expr_type) && is_integer_type(typ)) { p.error('operator `mod` requires integer types') diff --git a/vlib/compiler/fn.v b/vlib/compiler/fn.v index 5555c73ec4..39736e09f0 100644 --- a/vlib/compiler/fn.v +++ b/vlib/compiler/fn.v @@ -265,7 +265,7 @@ fn (p mut Parser) fn_decl() { p.register_var(receiver) } // +-/* methods - if p.tok in [.plus, .minus, .mul] { + if p.tok in [.plus, .minus, .mul, .div, .mod] { f.name = p.tok.str() p.next() } diff --git a/vlib/compiler/gen_c.v b/vlib/compiler/gen_c.v index 6cd5bcc0ca..afc1c5cae9 100644 --- a/vlib/compiler/gen_c.v +++ b/vlib/compiler/gen_c.v @@ -228,9 +228,15 @@ fn (table mut Table) fn_gen_name(f &Fn) string { if f.is_method { name = '${f.receiver_typ}_$f.name' name = name.replace(' ', '') - name = name.replace('*', '') - name = name.replace('+', 'plus') - name = name.replace('-', 'minus') + if f.name.len == 1 { + match f.name[0] { + `+` { 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). // Generate v_abs(), v_print() diff --git a/vlib/math/math.v b/vlib/math/math.v index 6f45b6ee4a..31a0adc0d4 100644 --- a/vlib/math/math.v +++ b/vlib/math/math.v @@ -6,6 +6,35 @@ module math #include +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 // When adding a new function, please make sure it's in the right place. // All functions are sorted alphabetically. @@ -18,8 +47,6 @@ pub fn abs(a f64) f64 { return a } -fn C.acos(a f64) f64 - // acos calculates inverse cosine (arccosine). pub fn acos(a f64) f64 { return C.acos(a) diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index a451db0ed4..254dd7f226 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -1,5 +1,9 @@ import os +fn test_aaa_setup(){ + cleanup_leftovers() assert true +} + fn test_setenv() { os.setenv('foo', 'bar', true) assert os.getenv('foo') == 'bar' @@ -114,12 +118,11 @@ fn test_walk() { } fn test_cp() { - $if windows { - old_file_name := './example.txt' - new_file_name := './new_example.txt' - + old_file_name := 'cp_example.txt' + new_file_name := 'cp_new_example.txt' + 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) } 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(new_file_name) - } } fn test_cp_r() { //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.mkdir('ex') os.cp_r('ex1.txt', 'ex', false) or { panic(err) } @@ -148,7 +149,6 @@ fn test_cp_r() { assert old2 == new2 //recurring on dir -> local dir os.cp_r('ex', './', true) or { panic(err) } - */ } //fn test_fork() { @@ -173,3 +173,25 @@ fn test_cp_r() { // 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') +}