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

math.big: implement big.integer in V (#11352)

This commit is contained in:
Subhomoy Haldar
2021-08-31 21:51:00 +05:30
committed by GitHub
parent f8aaf4bf67
commit dadfda9400
9 changed files with 1707 additions and 1297 deletions

View File

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

664
thirdparty/bignum/bn.c vendored
View File

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

123
thirdparty/bignum/bn.h vendored
View File

@ -1,123 +0,0 @@
#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__ */