diff options
Diffstat (limited to 'v_windows/v/thirdparty/bignum/bn.c')
-rw-r--r-- | v_windows/v/thirdparty/bignum/bn.c | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/v_windows/v/thirdparty/bignum/bn.c b/v_windows/v/thirdparty/bignum/bn.c new file mode 100644 index 0000000..7ec0895 --- /dev/null +++ b/v_windows/v/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 <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; +} + + |