mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
665 lines
14 KiB
C
665 lines
14 KiB
C
/*
|
|
|
|
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;
|
|
}
|
|
|
|
|