bignum module wrapper for kokke/tiny-bignum-c
parent
7a29d959ce
commit
a44ba0b8a2
|
@ -0,0 +1,2 @@
|
||||||
|
This folder contains bn.h and bn.c files
|
||||||
|
from https://github.com/kokke/tiny-bignum-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 <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(¤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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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__ */
|
||||||
|
|
||||||
|
|
|
@ -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) )
|
||||||
|
}
|
|
@ -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'
|
||||||
|
}
|
|
@ -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')
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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')
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue