From dadfda94007b86d38e62e6ad9cb8c5b4d239fe19 Mon Sep 17 00:00:00 2001 From: Subhomoy Haldar Date: Tue, 31 Aug 2021 21:51:00 +0530 Subject: [PATCH] math.big: implement big.integer in V (#11352) --- thirdparty/bignum/README.md | 2 - thirdparty/bignum/bn.c | 664 ---------------------------- thirdparty/bignum/bn.h | 123 ------ vlib/math/big/array_ops.v | 379 ++++++++++++++++ vlib/math/big/array_ops_test.v | 221 ++++++++++ vlib/math/big/big.c.v | 344 --------------- vlib/math/big/big.v | 16 + vlib/math/big/big_test.v | 493 ++++++++++++++------- vlib/math/big/integer.v | 762 +++++++++++++++++++++++++++++++++ 9 files changed, 1707 insertions(+), 1297 deletions(-) delete mode 100644 thirdparty/bignum/README.md delete mode 100644 thirdparty/bignum/bn.c delete mode 100644 thirdparty/bignum/bn.h create mode 100644 vlib/math/big/array_ops.v create mode 100644 vlib/math/big/array_ops_test.v delete mode 100644 vlib/math/big/big.c.v create mode 100644 vlib/math/big/big.v create mode 100644 vlib/math/big/integer.v diff --git a/thirdparty/bignum/README.md b/thirdparty/bignum/README.md deleted file mode 100644 index 04eb5552ee..0000000000 --- a/thirdparty/bignum/README.md +++ /dev/null @@ -1,2 +0,0 @@ -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 deleted file mode 100644 index 7ec0895f99..0000000000 --- a/thirdparty/bignum/bn.c +++ /dev/null @@ -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 -#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 deleted file mode 100644 index f82676f879..0000000000 --- a/thirdparty/bignum/bn.h +++ /dev/null @@ -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 -#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/math/big/array_ops.v b/vlib/math/big/array_ops.v new file mode 100644 index 0000000000..0b678ede1f --- /dev/null +++ b/vlib/math/big/array_ops.v @@ -0,0 +1,379 @@ +module big + +import math.util + +// Compares the magnitude of the two unsigned integers represented the given +// digit arrays. Returns -1 if a < b, 0 if a == b and +1 if a > b. Here +// a is operand_a and b is operand_b (for brevity). +fn compare_digit_array(operand_a []u32, operand_b []u32) int { + a_len := operand_a.len + b_len := operand_b.len + if a_len != b_len { + return if a_len < b_len { -1 } else { 1 } + } + // They have the same number of digits now + // Go from the most significant digit to the least significant one + for index := a_len - 1; index >= 0; index-- { + a_digit := operand_a[index] + b_digit := operand_b[index] + if a_digit != b_digit { + return if a_digit < b_digit { -1 } else { 1 } + } + } + return 0 +} + +// Add the digits in operand_a and operand_b and stores the result in sum. +// This function does not perform any allocation and assumes that the storage is +// large enough. It may affect the last element, based on the presence of a carry +fn add_digit_array(operand_a []u32, operand_b []u32, mut sum []u32) { + // Zero length cases + if operand_a.len == 0 { + for index in 0 .. operand_b.len { + sum[index] = operand_b[index] + } + } + if operand_b.len == 0 { + for index in 0 .. operand_a.len { + sum[index] = operand_a[index] + } + } + + // First pass intersects with both operands + smaller_limit := util.imin(operand_a.len, operand_b.len) + larger_limit := util.imax(operand_a.len, operand_b.len) + mut a, mut b := if operand_a.len >= operand_b.len { + operand_a, operand_b + } else { + operand_b, operand_a + } + mut carry := u64(0) + for index in 0 .. smaller_limit { + partial := carry + a[index] + b[index] + sum[index] = u32(partial) + carry = u32(partial >> 32) + } + + for index in smaller_limit .. larger_limit { + partial := carry + a[index] + sum[index] = u32(partial) + carry = u32(partial >> 32) + } + + if carry == 0 { + sum.delete_last() + } else { + sum[larger_limit] = u32(carry) + } +} + +// Subtracts operand_b from operand_a and stores the difference in storage. +// It assumes operand_a contains the larger "integer" and that storage is +// the same size as operand_a +fn subtract_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { + // Zero length cases + if operand_a.len == 0 { + // nothing to subtract from + return + } + if operand_b.len == 0 { + // nothing to subtract + for index in 0 .. operand_a.len { + storage[index] = operand_a[index] + } + } + + mut carry := false + for index in 0 .. operand_b.len { + mut a_digit := u64(operand_a[index]) + b_digit := operand_b[index] + if carry { u64(1) } else { u64(0) } + carry = a_digit < b_digit + if carry { + a_digit += 0x100000000 + } + storage[index] = u32(a_digit - b_digit) + } + + for index in operand_b.len .. operand_a.len { + mut a_digit := u64(operand_a[index]) + b_digit := if carry { u64(1) } else { u64(0) } + carry = a_digit < b_digit + if carry { + a_digit += 0x100000000 + } + storage[index] = u32(a_digit - b_digit) + } + + if storage.last() == 0 { + storage.delete_last() + } +} + +// Multiplies the unsigned (non-negative) integers represented in a and b and the product is +// stored in storage. It assumes that storage has length equal to the sum of lengths +// of a and b. Length refers to length of array, that is, digit count. +fn multiply_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { + for b_index in 0 .. operand_b.len { + mut carry := u64(0) + for a_index in 0 .. operand_a.len { + partial_product := u64(storage[a_index + b_index]) + carry + + u64(operand_a[a_index]) * u64(operand_b[b_index]) + storage[a_index + b_index] = u32(partial_product) + carry = partial_product >> 32 + } + if carry != 0 { + storage[b_index + operand_a.len] = u32(carry) + } + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } +} + +// Stores the product of the unsigned (non-negative) integer represented in a and the digit in value +// in the storage array. It assumes storage is pre-initialised and populated with 0's +fn multiply_array_by_digit(operand_a []u32, value u32, mut storage []u32) { + if value == 0 { + for storage.len > 0 { + storage.delete_last() + } + return + } + if value == 1 { + for index in 0 .. operand_a.len { + storage[index] = operand_a[index] + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } + return + } + mut carry := u32(0) + for index in 0 .. operand_a.len { + product := u64(operand_a[index]) * value + carry + storage[index] = u32(product) + carry = u32(product >> 32) + } + if carry > 0 { + if storage.last() == 0 { + storage[operand_a.len] = carry + } else { + storage << carry + } + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } +} + +// Divides the non-negative integer in a by non-negative integer b and store the two results +// in quotient and remainder respectively. It is different from the rest of the functions +// because it assumes that quotient and remainder are empty zero length arrays. They can be +// made to have appropriate capacity though +fn divide_digit_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) { + cmp_result := compare_digit_array(operand_a, operand_b) + // a == b => q, r = 1, 0 + if cmp_result == 0 { + quotient << 1 + for quotient.len > 1 { + quotient.delete_last() + } + for remainder.len > 0 { + remainder.delete_last() + } + return + } + + // a < b => q, r = 0, a + if cmp_result < 0 { + for quotient.len > 0 { + quotient.delete_last() + } + for index in 0 .. operand_a.len { + remainder << operand_a[index] + } + return + } + if operand_b.len == 1 { + divide_array_by_digit(operand_a, operand_b[0], mut quotient, mut remainder) + } else { + divide_array_by_array(operand_a, operand_b, mut quotient, mut remainder) + } +} + +// Performs division on the non-negative dividend in a by the single digit divisor b. It assumes +// quotient and remainder are empty zero length arrays with sufficient capacity +fn divide_array_by_digit(operand_a []u32, divisor u32, mut quotient []u32, mut remainder []u32) { + if operand_a.len == 1 { + // 1 digit for both dividend and divisor + dividend := operand_a[0] + q := dividend / divisor + if q != 0 { + quotient << q + } + rem := dividend % divisor + if rem != 0 { + remainder << rem + } + return + } + // Dividend has more digits + mut rem := u64(0) + divisor64 := u64(divisor) + // Pad quotient to contain sufficient space + for _ in 0 .. operand_a.len { + quotient << 0 + } + // Perform division step by step + for index := operand_a.len - 1; index >= 0; index-- { + dividend := (rem << 32) + operand_a[index] + quotient[index] = u32(dividend / divisor64) + rem = dividend % divisor64 + } + // Remove leading zeros from quotient + for quotient.len > 0 && quotient.last() == 0 { + quotient.delete_last() + } + remainder << u32(rem) + for remainder.len > 0 && remainder.last() == 0 { + remainder.delete_last() + } +} + +// Performs division on the non-negative dividend in a by the multi digit divisor b. It assumes +// quotient and remainder are empty zero length arrays with sufficient capacity +// This is different from divide_digit_array because it depends on this very function +// after making sure that the divisor is indeed multi-digit. +fn divide_array_by_array(operand_a []u32, operand_b []u32, mut quotient []u32, mut remainder []u32) { + for index in 0 .. operand_a.len { + remainder << operand_a[index] + } + for _ in 0 .. operand_b.len { + quotient << 0 + } + offset := operand_a.len - operand_b.len + divisor_last_index := operand_b.len - 1 + for index := offset; index >= 0; index-- { + dividend_last_index := divisor_last_index + index + value_upper := if remainder.len > dividend_last_index + 1 { + u64(remainder[dividend_last_index + 1]) + } else { + u64(0) + } + value_lower := if remainder.len > dividend_last_index { + u64(remainder[dividend_last_index]) + } else { + u64(0) + } + partial := value_lower + (value_upper << 32) + mut q := u32(partial / operand_b[divisor_last_index]) + if q > 0 { + mut modified_divisor := []u32{len: operand_b.len + index, init: 0} + for i in 0 .. operand_b.len { + modified_divisor[index + i] = operand_b[i] + } + + mut product := []u32{len: operand_b.len + 1, init: 0} + multiply_array_by_digit(modified_divisor, q, mut product) + for q > 0 && compare_digit_array(product, remainder) > 0 { + q-- + subtract_digit_array(product, modified_divisor, mut product) + } + subtract_digit_array(remainder, product, mut remainder) + } + quotient[index] = q + } + // Remove leading zeros from quotient and remainder + for quotient.len > 0 && quotient.last() == 0 { + quotient.delete_last() + } + for remainder.len > 0 && remainder.last() == 0 { + remainder.delete_last() + } +} + +// Shifts the contents of the original array by the given amount of bits to the left. +// This function assumes that the amount is less than 32. The storage is expected to +// allocated with zeroes. +fn shift_digits_left(original []u32, amount u32, mut storage []u32) { + mut leftover := u32(0) + offset := 32 - amount + for index in 0 .. original.len { + value := leftover | (original[index] << amount) + leftover = (original[index] & (u32(-1) << offset)) >> offset + storage[index] = value + } + if leftover != 0 { + storage << leftover + } +} + +// Shifts the contents of the original array by the given amount of bits to the right. +// This function assumes that the amount is less than 32. The storage is expected to +// be allocated with zeroes. +fn shift_digits_right(original []u32, amount u32, mut storage []u32) { + mut moveover := u32(0) + mask := (u32(1) << amount) - 1 + offset := 32 - amount + for index := original.len - 1; index >= 0; index-- { + value := (moveover << offset) | (original[index] >> amount) + moveover = original[index] & mask + storage[index] = value + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } +} + +fn bitwise_or_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { + lower, upper, bigger := if operand_a.len < operand_b.len { + operand_a.len, operand_b.len, operand_b + } else { + operand_b.len, operand_a.len, operand_a + } + for index in 0 .. lower { + storage[index] = operand_a[index] | operand_b[index] + } + for index in lower .. upper { + storage[index] = bigger[index] + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } +} + +fn bitwise_and_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { + lower := util.imin(operand_a.len, operand_b.len) + for index in 0 .. lower { + storage[index] = operand_a[index] & operand_b[index] + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } +} + +fn bitwise_xor_digit_array(operand_a []u32, operand_b []u32, mut storage []u32) { + lower, upper, bigger := if operand_a.len < operand_b.len { + operand_a.len, operand_b.len, operand_b + } else { + operand_b.len, operand_a.len, operand_a + } + for index in 0 .. lower { + storage[index] = operand_a[index] ^ operand_b[index] + } + for index in lower .. upper { + storage[index] = bigger[index] + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } +} + +fn bitwise_not_digit_array(original []u32, mut storage []u32) { + for index in 0 .. original.len { + storage[index] = ~original[index] + } + for storage.len > 0 && storage.last() == 0 { + storage.delete_last() + } +} diff --git a/vlib/math/big/array_ops_test.v b/vlib/math/big/array_ops_test.v new file mode 100644 index 0000000000..1841e1431d --- /dev/null +++ b/vlib/math/big/array_ops_test.v @@ -0,0 +1,221 @@ +module big + +fn test_add_digit_array_01() { + a := [u32(1), 1, 1] + b := [u32(1), 1, 1] + mut c := []u32{len: 4} + add_digit_array(a, b, mut c) + + assert c == [u32(2), 2, 2] +} + +fn test_add_digit_array_02() { + a := [u32(1), u32(1) << 31, 1] + b := [u32(1), u32(1) << 31, 1] + mut c := []u32{len: 4} + add_digit_array(a, b, mut c) + + assert c == [u32(2), 0, 3] +} + +fn test_add_digit_array_03() { + a := [u32(1), (u32(1) << 31) + u32(34), 1] + b := [u32(242), u32(1) << 31, 1] + mut c := []u32{len: 4} + add_digit_array(a, b, mut c) + + assert c == [u32(243), 34, 3] +} + +fn test_add_digit_array_04() { + a := [u32(0)] + b := [u32(1), 3, 4] + mut c := []u32{len: 4} + add_digit_array(a, b, mut c) + + assert c == [u32(1), 3, 4] +} + +fn test_add_digit_array_05() { + a := [u32(1), 3, 4] + b := [u32(0)] + mut c := []u32{len: 4} + add_digit_array(a, b, mut c) + + assert c == [u32(1), 3, 4] +} + +fn test_add_digit_array_06() { + a := [u32(46), 13, 462, 13] + b := [u32(1), 3, 4] + mut c := []u32{len: 5} + add_digit_array(a, b, mut c) + + assert c == [u32(47), 16, 466, 13] +} + +fn test_subtract_digit_array_01() { + a := [u32(2), 2, 2, 2, 2] + b := [u32(1), 1, 2, 1, 1] + mut c := []u32{len: a.len} + subtract_digit_array(a, b, mut c) + + assert c == [u32(1), 1, 0, 1, 1] +} + +fn test_subtract_digit_array_02() { + a := [u32(0), 0, 0, 0, 1] + b := [u32(0), 0, 1] + mut c := []u32{len: a.len} + subtract_digit_array(a, b, mut c) + + assert c == [u32(0), 0, u32(-1), u32(-1)] +} + +fn test_subtract_digit_array_03() { + a := [u32(0), 0, 0, 0, 1, 13] + b := [u32(0), 0, 1] + mut c := []u32{len: a.len} + subtract_digit_array(a, b, mut c) + + assert c == [u32(0), 0, u32(-1), u32(-1), 0, 13] +} + +fn test_multiply_digit_array_01() { + a := [u32(0), 0, 0, 1] + b := [u32(0), 0, 1] + mut c := []u32{len: a.len + b.len} + multiply_digit_array(a, b, mut c) + + assert c == [u32(0), 0, 0, 0, 0, 1] +} + +fn test_multiply_digit_array_02() { + a := []u32{len: 0} + b := [u32(0), 0, 1] + mut c := []u32{len: a.len + b.len} + multiply_digit_array(a, b, mut c) + + assert c == [] + + c = []u32{len: a.len + b.len} + multiply_digit_array(b, a, mut c) + + assert c == [] +} + +fn test_compare_digit_array_01() { + a := [u32(0), 0, 2] + b := [u32(0), 0, 4] + + assert compare_digit_array(a, b) < 0 + assert compare_digit_array(b, a) > 0 + assert compare_digit_array(a, a) == 0 + assert compare_digit_array(b, b) == 0 +} + +fn test_compare_digit_array_02() { + a := [u32(0), 0, 2324, 0, 124] + b := [u32(0), 0, 4, 0, 0, 1] + + assert compare_digit_array(a, b) < 0 + assert compare_digit_array(b, a) > 0 + assert compare_digit_array(a, a) == 0 + assert compare_digit_array(b, b) == 0 +} + +fn test_divide_digit_array_01() { + a := [u32(14)] + b := [u32(2)] + mut q := []u32{cap: 1} + mut r := []u32{cap: 1} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(7)] + assert r == []u32{len: 0} +} + +fn test_divide_digit_array_02() { + a := [u32(14)] + b := [u32(15)] + mut q := []u32{cap: 1} + mut r := []u32{cap: 1} + + divide_digit_array(a, b, mut q, mut r) + assert q == []u32{len: 0} + assert r == a +} + +fn test_divide_digit_array_03() { + a := [u32(0), 4] + b := [u32(0), 1] + mut q := []u32{cap: a.len - b.len + 1} + mut r := []u32{cap: a.len} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(4)] + assert r == []u32{len: 0} +} + +fn test_divide_digit_array_04() { + a := [u32(2), 4] + b := [u32(0), 1] + mut q := []u32{cap: a.len - b.len + 1} + mut r := []u32{cap: a.len} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(4)] + assert r == [u32(2)] +} + +fn test_divide_digit_array_05() { + a := [u32(3)] + b := [u32(2)] + mut q := []u32{cap: a.len - b.len + 1} + mut r := []u32{cap: a.len} + + divide_digit_array(a, b, mut q, mut r) + assert q == [u32(1)] + assert r == [u32(1)] +} + +fn test_left_and_right_shift() { + a := [u32(1), 1, 1] + mut r := [u32(2), 2, 2] + mut b := []u32{len: 3, init: 0} + shift_digits_left(a, 1, mut b) + assert r == b + shift_digits_right(r, 1, mut r) + assert r == a + shift_digits_left(r, 1, mut r) + assert r == b + + mut c := [u32(0xffffffff)] + shift_digits_left(c, 16, mut c) + assert c == [u32(0xfffff0000), 0xffff] + shift_digits_right(c, 8, mut c) + assert c == [u32(0xfffffff00), 0xff] + shift_digits_right(c, 16, mut c) + assert c == [u32(0x00ffffff)] + shift_digits_right(c, 16, mut c) + assert c == [u32(0xff)] + shift_digits_right(c, 16, mut c) + assert c == []u32{len: 0} +} + +fn test_or_digit_array() { + a := [u32(10), 10, 10] + b := [u32(5), 5, 5] + mut c := []u32{len: 3, init: 0} + bitwise_or_digit_array(a, b, mut c) + assert c == [u32(15), 15, 15] + + bitwise_or_digit_array(a, a, mut c) + assert c == a + + x := [u32(10), 10, 10, 42, 42] + y := [u32(2), 2, 5, 2] + mut d := []u32{len: 5, init: 0} + bitwise_or_digit_array(y, x, mut d) + assert d == [u32(10), 10, 15, 42, 42] +} diff --git a/vlib/math/big/big.c.v b/vlib/math/big/big.c.v deleted file mode 100644 index 7ef05349f3..0000000000 --- a/vlib/math/big/big.c.v +++ /dev/null @@ -1,344 +0,0 @@ -module big - -// Wrapper for https://github.com/kokke/tiny-bignum-c -#flag -I @VEXEROOT/thirdparty/bignum -#flag @VEXEROOT/thirdparty/bignum/bn.o -#include "bn.h" - -struct C.bn { -mut: - array [32]u32 -} - -// Big unsigned integer number. -type Number = C.bn - -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 &char, nbytes int) - -fn C.bignum_to_string(n &Number, s &char, maxsize int) - -// c = a + b -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 d=a%b -fn C.bignum_divmod(a &Number, b &Number, c &Number, d &Number) - -// c = 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 xor b -fn C.bignum_xor(a &Number, b &Number, c &Number) - -// b = a << nbits -fn C.bignum_lshift(a &Number, b &Number, nbits int) - -// b = a >> nbits -fn C.bignum_rshift(a &Number, b &Number, nbits int) - -fn C.bignum_cmp(a &Number, b &Number) int - -fn C.bignum_is_zero(a &Number) int - -// n++ -fn C.bignum_inc(n &Number) - -// n-- -fn C.bignum_dec(n &Number) - -// c = a ^ b -fn C.bignum_pow(a &Number, b &Number, c &Number) - -// b = integer_square_root_of(a) -fn C.bignum_isqrt(a &Number, b &Number) - -// copy src number to dst number -fn C.bignum_assign(dst &Number, src &Number) - -// new returns a bignum, initialized to 0 -pub fn new() Number { - return Number{} -} - -// conversion actions to/from big numbers: -// from_int converts an ordinary int number `i` to big.Number -pub fn from_int(i int) Number { - n := Number{} - C.bignum_from_int(&n, i) - return n -} - -// from_u64 converts an ordinary u64 number `u` to big.Number -pub fn from_u64(u u64) Number { - n := Number{} - C.bignum_from_int(&n, u) - return n -} - -// from_hex_string converts a hex string to big.Number -pub fn from_hex_string(input string) Number { - mut s := input.trim_prefix('0x') - if s.len == 0 { - s = '0' - } - padding := '0'.repeat((8 - s.len % 8) % 8) - s = padding + s - n := Number{} - C.bignum_from_string(&n, &char(s.str), s.len) - return n -} - -// from_string converts a decimal string to big.Number -pub fn from_string(input string) Number { - mut n := from_int(0) - for _, c in input { - d := from_int(int(c - `0`)) - n = (n * big.ten) + d - } - return n -} - -// from_bytes converts an array of bytes (little-endian) to a big.Number. -// Higher precedence bytes are expected at lower indices in the array. -pub fn from_bytes(input []byte) ?Number { - if input.len > 128 { - return error('input array too large. big.Number can only hold up to 1024 bit numbers') - } - // pad input - mut padded_input := []byte{len: ((input.len + 3) & ~0x3) - input.len, cap: (input.len + 3) & ~0x3, init: 0x0} - padded_input << input - // combine every 4 bytes into a u32 and insert into n.array - mut n := Number{} - for i := 0; i < padded_input.len; i += 4 { - x3 := u32(padded_input[i]) - x2 := u32(padded_input[i + 1]) - x1 := u32(padded_input[i + 2]) - x0 := u32(padded_input[i + 3]) - val := (x3 << 24) | (x2 << 16) | (x1 << 8) | x0 - n.array[(padded_input.len - i) / 4 - 1] = val - } - return n -} - -// .int() converts (a small) big.Number `n` to an ordinary integer. -pub fn (n &Number) int() int { - r := C.bignum_to_int(n) - return r -} - -const ( - ten = from_int(10) -) - -// .str returns a decimal representation of the big unsigned integer number n. -pub fn (n &Number) str() string { - if n.is_zero() { - return '0' - } - mut digits := []byte{} - mut x := n.clone() - - for !x.is_zero() { - // changes to reflect new api - div, mod := divmod(&x, &big.ten) - digits << byte(mod.int()) + `0` - x = div - } - return digits.reverse().bytestr() -} - -// .hexstr returns a hexadecimal representation of the bignum `n` -pub fn (n &Number) hexstr() string { - mut buf := [8192]byte{} - mut s := '' - unsafe { - bp := &buf[0] - // NB: C.bignum_to_string(), returns the HEXADECIMAL representation of the bignum n - C.bignum_to_string(n, &char(bp), 8192) - s = tos_clone(bp) - } - 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 -} - -// divmod returns a pair of quotient and remainder from div modulo operation -// between two bignums `a` and `b` -pub fn divmod(a &Number, b &Number) (Number, Number) { - c := Number{} - d := Number{} - C.bignum_divmod(a, b, &c, &d) - return c, d -} - -// ////////////////////////////////////////////////////////// -pub fn cmp(a &Number, b &Number) int { - return C.bignum_cmp(a, b) -} - -pub fn (a &Number) is_zero() bool { - return C.bignum_is_zero(a) != 0 -} - -pub fn (mut a Number) inc() { - C.bignum_inc(&a) -} - -pub fn (mut a 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 &Number) 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) Number { - return factorial(from_int(n)) -} - -// bytes returns an array of the bytes for the number `n`, -// in little endian format, where .bytes()[0] is the least -// significant byte. The result is NOT trimmed, and will contain 0s, even -// after the significant bytes. -// This method is faster than .bytes_trimmed(), but may be less convenient. -// Example: assert big.from_int(1).bytes()[0] == byte(0x01) -// Example: assert big.from_int(1024).bytes()[1] == byte(0x04) -// Example: assert big.from_int(1048576).bytes()[2] == byte(0x10) -pub fn (n &Number) bytes() []byte { - mut res := []byte{len: 128, init: 0} - unsafe { C.memcpy(res.data, n, 128) } - return res -} - -// bytes_trimmed returns an array of the bytes for the number `n`, -// in little endian format, where .bytes_trimmed()[0] is the least -// significant byte. The result is trimmed, so that *the last* byte -// of the result is also the the last meaningfull byte, != 0 . -// Example: assert big.from_int(1).bytes_trimmed() == [byte(0x01)] -// Example: assert big.from_int(1024).bytes_trimmed() == [byte(0x00), 0x04] -// Example: assert big.from_int(1048576).bytes_trimmed() == [byte(0x00), 0x00, 0x10] -pub fn (n &Number) bytes_trimmed() []byte { - mut res := []byte{len: 128, init: 0} - unsafe { C.memcpy(res.data, n, 128) } - mut non_zero_idx := 127 - for ; non_zero_idx >= 0; non_zero_idx-- { - if res[non_zero_idx] != 0 { - break - } - } - res.trim(non_zero_idx + 1) - return res -} diff --git a/vlib/math/big/big.v b/vlib/math/big/big.v new file mode 100644 index 0000000000..ab8d9df00a --- /dev/null +++ b/vlib/math/big/big.v @@ -0,0 +1,16 @@ +module big + +pub const ( + zero_int = Integer{ + digits: []u32{len: 0} + signum: 0 + } + one_int = Integer{ + digits: [u32(1)] + signum: 1 + } + two_int = Integer{ + digits: [u32(2)] + signum: 1 + } +) diff --git a/vlib/math/big/big_test.v b/vlib/math/big/big_test.v index 9da584542a..89aea9f4a1 100644 --- a/vlib/math/big/big_test.v +++ b/vlib/math/big/big_test.v @@ -1,181 +1,346 @@ import math.big -fn test_new_big() { - n := big.new() - assert sizeof(big.Number) == 128 - assert n.hexstr() == '0' +fn test_integer_from_int() { + assert big.integer_from_int(0).hex() == '0' + assert big.integer_from_int(1).hex() == '1' + assert big.integer_from_int(255).hex() == 'ff' + assert big.integer_from_int(127).hex() == '7f' + assert big.integer_from_int(1024).hex() == '400' + assert big.integer_from_int(2147483647).hex() == '7fffffff' } -fn test_from_int() { - assert big.from_int(255).hexstr() == 'ff' - assert big.from_int(127).hexstr() == '7f' - assert big.from_int(1024).hexstr() == '400' - assert big.from_int(2147483647).hexstr() == '7fffffff' - assert big.from_int(-1).hexstr() == 'ffffffffffffffff' +fn test_integer_from_u64() { + assert big.integer_from_u64(0).hex() == '0' + assert big.integer_from_u64(1).hex() == '1' + assert big.integer_from_u64(255).hex() == 'ff' + assert big.integer_from_u64(127).hex() == '7f' + assert big.integer_from_u64(1024).hex() == '400' + assert big.integer_from_u64(4294967295).hex() == 'ffffffff' + assert big.integer_from_u64(4398046511104).hex() == '40000000000' + max_value := big.integer_from_u64(-1) + + assert max_value.hex() == 'ffffffffffffffff' } -fn test_from_u64() { - assert big.from_u64(255).hexstr() == 'ff' - assert big.from_u64(127).hexstr() == '7f' - assert big.from_u64(1024).hexstr() == '400' - assert big.from_u64(4294967295).hexstr() == 'ffffffff' - assert big.from_u64(4398046511104).hexstr() == '40000000000' - assert big.from_u64(-1).hexstr() == 'ffffffffffffffff' -} +fn test_integer_from_bytes() { + assert big.integer_from_bytes([]).hex() == '0' + assert big.integer_from_bytes([byte(0)]).hex() == '0' + assert big.integer_from_bytes([byte(0x13), 0x37]).hex() == '1337' + assert big.integer_from_bytes([byte(0x13), 0x37, 0xca]).hex() == '1337ca' + assert big.integer_from_bytes([byte(0x13), 0x37, 0xca, 0xfe]).hex() == '1337cafe' + assert big.integer_from_bytes([byte(0x13), 0x37, 0xca, 0xfe, 0xba]).hex() == '1337cafeba' + assert big.integer_from_bytes([byte(0x13), 0x37, 0xca, 0xfe, 0xba, 0xbe]).hex() == '1337cafebabe' -fn test_plus() { - mut a := big.from_u64(2) - b := big.from_u64(3) - c := a + b - assert c.hexstr() == '5' - assert (big.from_u64(1024) + big.from_u64(1024)).hexstr() == '800' - a += b - assert a.hexstr() == '5' - a.inc() - assert a.hexstr() == '6' - a.dec() - a.dec() - assert a.hexstr() == '4' -} - -fn test_minus() { - a := big.from_u64(2) - mut b := big.from_u64(3) - c := b - a - assert c.hexstr() == '1' - e := big.from_u64(1024) - ee := e - e - assert ee.hexstr() == '0' - b -= a - assert b.hexstr() == '1' -} - -fn test_divide() { - a := big.from_u64(2) - mut b := big.from_u64(3) - c := b / a - assert c.hexstr() == '1' - assert (b % a).hexstr() == '1' - e := big.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' - b /= a - assert b.hexstr() == '1' -} - -fn test_multiply() { - mut a := big.from_u64(2) - b := big.from_u64(3) - c := b * a - assert c.hexstr() == '6' - e := big.from_u64(1024) - e2 := e * e - e4 := e2 * e2 - e8 := e2 * e2 * e2 * e2 - e9 := e8 + big.from_u64(1) - d := ((e9 * e9) + b) * c - assert e4.hexstr() == '10000000000' - assert e8.hexstr() == '100000000000000000000' - assert e9.hexstr() == '100000000000000000001' - assert d.hexstr() == '60000000000000000000c00000000000000000018' - a *= b - assert a.hexstr() == '6' -} - -fn test_mod() { - assert (big.from_u64(13) % big.from_u64(10)).int() == 3 - assert (big.from_u64(13) % big.from_u64(9)).int() == 4 - assert (big.from_u64(7) % big.from_u64(5)).int() == 2 -} - -fn test_divmod() { - x, y := big.divmod(big.from_u64(13), big.from_u64(10)) - assert x.int() == 1 - assert y.int() == 3 - p, q := big.divmod(big.from_u64(13), big.from_u64(9)) - assert p.int() == 1 - assert q.int() == 4 - c, d := big.divmod(big.from_u64(7), big.from_u64(5)) - assert c.int() == 1 - assert d.int() == 2 -} - -fn test_from_str() { - assert big.from_string('9870123').str() == '9870123' - assert big.from_string('').str() == '0' - assert big.from_string('0').str() == '0' - assert big.from_string('1').str() == '1' - for i := 1; i < 307; i += 61 { - input := '9'.repeat(i) - out := big.from_string(input).str() - // eprintln('>> i: $i input: $input.str()') - // eprintln('>> i: $i out: $out.str()') - assert input == out + mut bytes := []byte{cap: 1024} + mut expected := '' + for i := 0; i < bytes.cap; i++ { + bytes << byte(i) + expected = expected + byte(i).hex() } -} - -fn test_from_hex_str() { - assert big.from_hex_string('0x123').hexstr() == '123' - for i in 1 .. 33 { - input := 'e'.repeat(i) - out := big.from_hex_string(input).hexstr() - assert input == out - } - assert big.from_string('0').hexstr() == '0' -} - -fn test_str() { - assert big.from_u64(255).str() == '255' - assert big.from_u64(127).str() == '127' - assert big.from_u64(1024).str() == '1024' - assert big.from_u64(4294967295).str() == '4294967295' - assert big.from_u64(4398046511104).str() == '4398046511104' - assert big.from_int(int(4294967295)).str() == '18446744073709551615' - assert big.from_int(-1).str() == '18446744073709551615' - assert big.from_hex_string('e'.repeat(80)).str() == '1993587900192849410235353592424915306962524220866209251950572167300738410728597846688097947807470' -} - -fn test_factorial() { - f5 := big.factorial(big.from_u64(5)) - assert f5.hexstr() == '78' - f100 := big.factorial(big.from_u64(100)) - assert f100.hexstr() == '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000' -} - -fn trimbytes(n int, x []byte) []byte { - mut res := x.clone() - res.trim(n) - return res + assert big.integer_from_bytes(bytes).hex() == expected.trim_left('0') } fn test_bytes() { - assert big.from_int(0).bytes().len == 128 - assert big.from_hex_string('e'.repeat(100)).bytes().len == 128 - assert trimbytes(3, big.from_int(1).bytes()) == [byte(0x01), 0x00, 0x00] - assert trimbytes(3, big.from_int(1024).bytes()) == [byte(0x00), 0x04, 0x00] - assert trimbytes(3, big.from_int(1048576).bytes()) == [byte(0x00), 0x00, 0x10] + result1, sign1 := big.integer_from_u64(0x1337cafebabe).bytes() + assert result1 == [byte(0x13), 0x37, 0xca, 0xfe, 0xba, 0xbe] + assert sign1 == 1 + + mut bytes := []byte{cap: 1024} + mut expected := '' + for i := 0; i < bytes.cap; i++ { + bytes << byte(i | 1) + expected = expected + byte(i).hex() + } + result2, sign2 := big.integer_from_bytes(bytes).bytes() + assert result2 == bytes + assert sign2 == 1 } -fn test_bytes_trimmed() { - assert big.from_int(0).bytes_trimmed().len == 0 - assert big.from_hex_string('AB'.repeat(50)).bytes_trimmed().len == 50 - assert big.from_int(1).bytes_trimmed() == [byte(0x01)] - assert big.from_int(1024).bytes_trimmed() == [byte(0x00), 0x04] - assert big.from_int(1048576).bytes_trimmed() == [byte(0x00), 0x00, 0x10] +fn test_addition() { + a := big.integer_from_int(2) + b := big.integer_from_int(3) + c := a + b + assert c.hex() == '5' + + assert (big.integer_from_int(1024) + big.integer_from_int(1024)).hex() == '800' + + fib1 := big.integer_from_string('84885164052257330097714121751630835360966663883732297726369399') or { + panic('Cannot read decimal') + } + fib2 := big.integer_from_string('137347080577163115432025771710279131845700275212767467264610201') or { + panic('Cannot read decimal') + } + assert (fib1 + fib2).str() == '222232244629420445529739893461909967206666939096499764990979600' } -fn test_from_bytes() ? { - assert big.from_bytes([]) ?.hexstr() == '0' - assert big.from_bytes([byte(0x13)]) ?.hexstr() == '13' - assert big.from_bytes([byte(0x13), 0x37]) ?.hexstr() == '1337' - assert big.from_bytes([byte(0x13), 0x37, 0xca]) ?.hexstr() == '1337ca' - assert big.from_bytes([byte(0x13), 0x37, 0xca, 0xfe]) ?.hexstr() == '1337cafe' - assert big.from_bytes([byte(0x13), 0x37, 0xca, 0xfe, 0xba]) ?.hexstr() == '1337cafeba' - assert big.from_bytes([byte(0x13), 0x37, 0xca, 0xfe, 0xba, 0xbe]) ?.hexstr() == '1337cafebabe' - assert big.from_bytes([]byte{len: 128, init: 0x0}) ?.hexstr() == '0' - if x := big.from_bytes([]byte{len: 129, init: 0x0}) { - return error('expected error, got $x') +fn test_subtraction() { + a := big.integer_from_int(2) + b := big.integer_from_int(3) + assert (a - b).hex() == '-1' + assert (b - a).hex() == '1' + + c := big.integer_from_int(1024) + assert (c - c) == big.zero_int + + assert big.integer_from_int(-37) - big.integer_from_int(-54) == big.integer_from_int(17) +} + +fn test_multiplication() { + a := big.integer_from_int(2) + b := big.integer_from_int(3) + c := big.integer_from_int(6) + assert a * b == c + assert big.integer_from_int(-869) * big.integer_from_int(789) == big.integer_from_int(-685641) + e := big.integer_from_u32(1024) + e2 := e * e + e4 := e2 * e2 + e8 := e2 * e2 * e2 * e2 + e9 := e8 + big.one_int + d := ((e9 * e9) + b) * c + assert e4.hex() == '10000000000' + assert e8.hex() == '100000000000000000000' + assert e9.hex() == '100000000000000000001' + assert d.hex() == '60000000000000000000c00000000000000000018' +} + +fn test_division() { + a := big.integer_from_u64(2) + b := big.integer_from_u64(3) + c := b / a + assert c.hex() == '1' + assert (b % a).hex() == '1' + e := big.integer_from_u64(1024) // dec(1024) == hex(0x400) + ee := e / e + assert ee.hex() == '1' + assert (e / a).hex() == '200' + assert (e / (a * a)).hex() == '100' + + assert (b / a).hex() == '1' +} + +fn test_mod() { + assert (big.integer_from_u64(13) % big.integer_from_u64(10)).int() == 3 + assert (big.integer_from_u64(13) % big.integer_from_u64(9)).int() == 4 + assert (big.integer_from_u64(7) % big.integer_from_u64(5)).int() == 2 +} + +fn test_divmod() { + x, y := big.integer_from_u64(13).div_mod(big.integer_from_u64(10)) + assert x.int() == 1 + assert y.int() == 3 + p, q := big.integer_from_u64(13).div_mod(big.integer_from_u64(9)) + assert p.int() == 1 + assert q.int() == 4 + c, d := big.integer_from_u64(7).div_mod(big.integer_from_u64(5)) + assert c.int() == 1 + assert d.int() == 2 + x1 := big.integer_from_string('2103180314840157') or { panic('Cannot read decimal') } + y1 := big.integer_from_string('1631403814113') or { panic('Cannot read decimal') } + q0 := big.integer_from_int(1289) + r0 := big.integer_from_string('300798448500') or { panic('Cannot read decimal') } + q1, r1 := x1.div_mod(y1) + assert q1 == q0 + assert r1 == r0 + + e := big.integer_from_string('21408410031413414147401') or { panic('Cannot read decimal') } + f := big.integer_from_string('3130541314113413') or { panic('Cannot read decimal') } + g, h := e.div_mod(f) + assert g.str() == '6838564' + assert h.str() == '2900204736088469' +} + +fn test_conversion() { + ten := big.integer_from_int(10) + + mut n := big.integer_from_u64(-1) + + mut digits := []rune{} + for n.signum != 0 { + quot, rem := n.div_mod(ten) + digits << rune(rem.int()) + `0` + n = quot + } + + assert digits.reverse().string() == '18446744073709551615' +} + +fn test_integer_from_string() { + a := big.integer_from_string('00000000') or { panic('Zero int test fails') } + assert a == big.zero_int + b := big.integer_from_radix('00', 4) or { panic('Zero int test fails') } + assert b == big.zero_int + assert a == b + + string_values := ['0', '1', '0012', '1349173614', '+24', '-325'] + int_values := [0, 1, 12, 1349173614, 24, -325] + for index in 0 .. string_values.len { + x := big.integer_from_string(string_values[index]) or { + panic('Could not convert decimal string') + } + y := big.integer_from_int(int_values[index]) + assert x == y } } + +fn test_integer_from_powers_of_2() { + assert (big.integer_from_radix('101010', 2) or { panic('Cannot read binary') }).int() == 42 + assert (big.integer_from_radix('1010', 2) or { panic('Cannot read binary') }).int() == 10 + assert (big.integer_from_radix('-0000101', 2) or { panic('Cannot read binary') }).int() == -5 + + assert (big.integer_from_radix('CAFE', 16) or { panic('Cannot read hexadecimal') }).int() == 0xCAFE + assert (big.integer_from_radix('DED', 16) or { panic('Cannot read hexadecimal') }).int() == 0xDED + assert (big.integer_from_radix('-abcd', 16) or { panic('Cannot read hexadecimal') }).int() == -0xabcd +} + +fn test_from_and_to_hex() { + assert (big.integer_from_radix('123', 16) or { panic('Cannot read hexadecimal') }).hex() == '123' + for i in 1 .. 33 { + input := 'e'.repeat(i) + output := (big.integer_from_radix(input, 16) or { panic('Cannot read hexadecimal') }).hex() + assert input == output + } + assert (big.integer_from_string('0') or { panic('Cannot read decimal') }).str() == '0' +} + +fn test_str() { + assert big.integer_from_u64(255).str() == '255' + assert big.integer_from_u64(127).str() == '127' + assert big.integer_from_u64(1024).str() == '1024' + assert big.integer_from_u64(4294967295).str() == '4294967295' + assert big.integer_from_u64(4398046511104).str() == '4398046511104' + assert big.integer_from_u64(-1).str() == '18446744073709551615' + assert (big.integer_from_radix('e'.repeat(80), 16) or { panic('Cannot read hexadecimal') }).str() == '1993587900192849410235353592424915306962524220866209251950572167300738410728597846688097947807470' +} + +fn test_exponentiation() { + a := big.integer_from_int(2) + assert a.pow(0).int() == 1 + assert a.pow(1).int() == 2 + assert a.pow(5).int() == 32 + assert a.pow(10).int() == 1024 + assert a.pow(30).int() == 1073741824 + + exp_array := [u32(5), 7, 234, 524, 291, 13051] + for exp in exp_array { + expected := '1' + '0'.repeat(int(exp)) + actual := a.pow(exp) + assert actual.binary_str() == expected + } + + result := '66325146064916587705822805477951823674769212922003325230500180789514487101799702287247301347816140714887582527826252837635296749781071351621748491469338347097923896026211183517655658952346069454893422558286798338709431368762851475568899541999504754550056265493269010870696623999709399529395247064542825851568385196637089440522882877102429945439977107582295420418108331098961838419917230847980056560488541780255425015021238743932289115066701337398107639567748102191005710201353615093246958907555634902309636451244444952203735074916066229982498598205421944122042066749035283837586883383420374291325389757869347147357807188516650352693616763867685354382631931356465247637321960345782811272139101785279798666504361229957479336436466489780129445016691164329417001378480690804715301830926348058624' + + assert big.integer_from_int(324).pow(u32(315)).str() == result +} + +fn test_mod_exponentiation() { + divisor := big.integer_from_int(632) + assert big.integer_from_int(324).mod_pow(u32(315), divisor) == big.integer_from_int(512) + + a := big.integer_from_int(65) + b := big.integer_from_int(2790) + div := big.integer_from_int(3233) + + assert a.mod_pow(17, div) == b + assert b.mod_pow(413, div) == a +} + +fn test_gcd() { + assert big.integer_from_int(0).gcd(big.integer_from_int(0)) == big.zero_int + assert big.integer_from_int(10).gcd(big.integer_from_int(0)) == big.integer_from_int(10) + assert big.integer_from_int(0).gcd(big.integer_from_int(-18)) == big.integer_from_int(18) + assert big.integer_from_int(51).gcd(big.integer_from_int(22)) == big.one_int + assert big.integer_from_int(98).gcd(big.integer_from_int(56)) == big.integer_from_int(14) + assert big.integer_from_int(98).gcd(big.integer_from_int(56)) == big.integer_from_int(14) + + a := big.integer_from_string('67116917544110') or { panic('Could not read decimal') } + b := big.integer_from_string('60943431483592') or { panic('Could not read decimal') } + c := big.integer_from_string('6299482') or { panic('Could not read decimal') } + assert a.gcd(b) == c +} + +fn test_factorial() { + f5 := big.integer_from_u64(5).factorial() + assert f5.hex() == '78' + f100 := big.integer_from_u64(100).factorial() + assert f100.hex() == '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000' +} + +fn test_inc_and_dec() { + mut a := big.integer_from_int(2) + mut b := big.integer_from_int(3) + mut c := big.integer_from_int(4) + + a.inc() + c.dec() + assert a == b + assert b == c +} + +fn test_lshift() { + assert big.integer_from_int(45).lshift(2) == big.integer_from_int(45 * 4) + assert big.integer_from_int(45).lshift(3) == big.integer_from_int(45 * 8) + assert big.integer_from_int(45).lshift(4) == big.integer_from_int(45 * 16) + assert big.integer_from_int(45).lshift(5) == big.integer_from_int(45 * 32) + assert big.integer_from_u64(0xabcedabcde).lshift(20) == big.integer_from_u64(0xabcedabcde00000) + assert big.integer_from_bytes([byte(1), 1, 1]).lshift(56) == big.integer_from_bytes([ + byte(1), + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]) +} + +fn test_rshift() { + assert big.integer_from_int(45).rshift(3) == big.integer_from_int(5) + assert big.integer_from_int(0x13374956).rshift(16) == big.integer_from_int(0x1337) + assert big.integer_from_bytes([ + byte(1), + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]).rshift(56) == big.integer_from_bytes([byte(1), 1, 1]) +} + +fn test_isqrt() { + for i in 0 .. 1000 { + a := big.integer_from_int(i) + b := big.integer_from_int(i * i) + assert b.isqrt() == a + } + values := [ + '314', + '213149', + '2198614', + '318014', + '1000000000', + '1000131039410', + '2148170394871039847019843349714981', + ] + for value in values { + a := big.integer_from_string(value) or { panic('Cannot read from decimal') } + b := a * a + assert b.isqrt() == a + } +} + +fn test_bitwise_ops() { + a := big.integer_from_int(1).lshift(512) + b := a - big.one_int + assert a.bitwise_and(b) == big.zero_int + assert b.bitwise_xor(b) == big.zero_int + assert b.bitwise_or(b) == b + assert b.bitwise_and(b) == b + assert b.bitwise_not() == big.zero_int +} diff --git a/vlib/math/big/integer.v b/vlib/math/big/integer.v new file mode 100644 index 0000000000..64d76b068b --- /dev/null +++ b/vlib/math/big/integer.v @@ -0,0 +1,762 @@ +module big + +import math.util +import math.bits +import strings +import strconv + +const ( + digit_array = '0123456789abcdefghijklmnopqrstuvwxyz'.bytes() +) + +// big.Integer +// ----------- +// It has the following properties: +// 1. Every "digit" is an integer in the range [0, 2^32). +// 2. The signum can be one of three values: -1, 0, +1 for +// negative, zero, and positive values, respectively. +// 3. There should be no leading zeros in the digit array. +// 4. The digits are stored in little endian format, that is, +// the digits with a lower positional value (towards the right +// when represented as a string) have a lower index, and vice versa. +pub struct Integer { + digits []u32 +pub: + signum int +} + +fn int_signum(value int) int { + if value == 0 { + return 0 + } + return if value < 0 { -1 } else { 1 } +} + +pub fn integer_from_int(value int) Integer { + if value == 0 { + return zero_int + } + return Integer{ + digits: [u32(util.iabs(value))] + signum: int_signum(value) + } +} + +pub fn integer_from_u32(value u32) Integer { + if value == 0 { + return zero_int + } + return Integer{ + digits: [value] + signum: 1 + } +} + +pub fn integer_from_i64(value i64) Integer { + if value == 0 { + return zero_int + } + + signum_value := if value < 0 { -1 } else { 1 } + abs_value := u64(value * signum_value) + + lower := u32(abs_value) + upper := u32(abs_value >> 32) + + if upper == 0 { + return Integer{ + digits: [lower] + signum: signum_value + } + } else { + return Integer{ + digits: [lower, upper] + signum: signum_value + } + } +} + +pub fn integer_from_u64(value u64) Integer { + if value == 0 { + return zero_int + } + + lower := u32(value & 0x00000000ffffffff) + upper := u32((value & 0xffffffff00000000) >> 32) + + if upper == 0 { + return Integer{ + digits: [lower] + signum: 1 + } + } else { + return Integer{ + digits: [lower, upper] + signum: 1 + } + } +} + +pub struct IntegerConfig { + signum int = 1 +} + +pub fn integer_from_bytes(input []byte, config IntegerConfig) Integer { + // Thank you to Miccah (@mcastorina) for this implementation and relevant unit tests. + if input.len == 0 { + return integer_from_int(0) + } + // pad input + mut padded_input := []byte{len: ((input.len + 3) & ~0x3) - input.len, cap: (input.len + 3) & ~0x3, init: 0x0} + padded_input << input + mut digits := []u32{len: padded_input.len / 4} + // combine every 4 bytes into a u32 and insert into n.digits + for i := 0; i < padded_input.len; i += 4 { + x3 := u32(padded_input[i]) + x2 := u32(padded_input[i + 1]) + x1 := u32(padded_input[i + 2]) + x0 := u32(padded_input[i + 3]) + val := (x3 << 24) | (x2 << 16) | (x1 << 8) | x0 + digits[(padded_input.len - i) / 4 - 1] = val + } + return Integer{ + digits: digits + signum: config.signum + } +} + +pub fn integer_from_string(characters string) ?Integer { + return integer_from_radix(characters, 10) +} + +pub fn integer_from_radix(all_characters string, radix u32) ?Integer { + if radix < 2 || radix > 36 { + return error('Radix must be between 2 and 36 (inclusive)') + } + characters := all_characters.to_lower() + validate_string(characters, radix) ? + return match radix { + 2 { + integer_from_special_string(characters, 1) + } + 16 { + integer_from_special_string(characters, 4) + } + else { + integer_from_regular_string(characters, radix) + } + } +} + +fn validate_string(characters string, radix u32) ? { + sign_present := characters[0] == `+` || characters[0] == `-` + + start_index := if sign_present { 1 } else { 0 } + + for index := start_index; index < characters.len; index++ { + digit := characters[index] + value := big.digit_array.index(digit) + + if value == -1 { + return error('Invalid character $digit') + } + if value >= radix { + return error('Invalid character $digit for base $radix') + } + } +} + +fn integer_from_special_string(characters string, chunk_size int) Integer { + sign_present := characters[0] == `+` || characters[0] == `-` + + signum := if sign_present { + if characters[0] == `-` { -1 } else { 1 } + } else { + 1 + } + + start_index := if sign_present { 1 } else { 0 } + + mut big_digits := []u32{cap: ((characters.len * chunk_size) >> 5) + 1} + mut current := u32(0) + mut offset := 0 + for index := characters.len - 1; index >= start_index; index-- { + digit := characters[index] + value := u32(big.digit_array.index(digit)) + + current |= value << offset + offset += chunk_size + + if offset == 32 { + big_digits << current + current = u32(0) + offset = 0 + } + } + + // Store the accumulated value into the digit array + if current != 0 { + big_digits << current + } + + for big_digits.len > 0 && big_digits.last() == 0 { + big_digits.delete_last() + } + + return Integer{ + digits: big_digits + signum: if big_digits.len == 0 { 0 } else { signum } + } +} + +fn integer_from_regular_string(characters string, radix u32) Integer { + sign_present := characters[0] == `+` || characters[0] == `-` + + signum := if sign_present { + if characters[0] == `-` { -1 } else { 1 } + } else { + 1 + } + + start_index := if sign_present { 1 } else { 0 } + + mut result := zero_int + radix_int := integer_from_u32(radix) + + for index := start_index; index < characters.len; index++ { + digit := characters[index] + value := big.digit_array.index(digit) + + result *= radix_int + result += integer_from_int(value) + } + + return Integer{ + ...result + signum: result.signum * signum + } +} + +pub fn (integer Integer) abs() Integer { + return if integer.signum == 0 { + zero_int + } else { + Integer{ + ...integer + signum: 1 + } + } +} + +pub fn (integer Integer) neg() Integer { + return if integer.signum == 0 { + zero_int + } else { + Integer{ + ...integer + signum: -integer.signum + } + } +} + +pub fn (integer Integer) + (addend Integer) Integer { + // Quick exits + if integer.signum == 0 { + return addend + } + if addend.signum == 0 { + return integer + } + // Non-zero cases + return if integer.signum == addend.signum { + integer.add(addend) + } else { // Unequal signs + integer.subtract(addend) + } +} + +pub fn (integer Integer) - (subtrahend Integer) Integer { + // Quick exits + if integer.signum == 0 { + return subtrahend.neg() + } + if subtrahend.signum == 0 { + return integer + } + // Non-zero cases + return if integer.signum == subtrahend.signum { + integer.subtract(subtrahend) + } else { + integer.add(subtrahend) + } +} + +fn (integer Integer) add(addend Integer) Integer { + a := integer.digits + b := addend.digits + mut storage := []u32{len: util.imax(a.len, b.len) + 1} + add_digit_array(a, b, mut storage) + return Integer{ + ...integer + digits: storage + } +} + +fn (integer Integer) subtract(subtrahend Integer) Integer { + cmp := integer.abs_cmp(subtrahend) + if cmp == 0 { + return zero_int + } + a, b := if cmp > 0 { integer, subtrahend } else { subtrahend, integer } + mut storage := []u32{len: a.digits.len} + subtract_digit_array(a.digits, b.digits, mut storage) + return Integer{ + signum: cmp * a.signum + digits: storage + } +} + +pub fn (integer Integer) * (multiplicand Integer) Integer { + // Quick exits + if integer.signum == 0 || multiplicand.signum == 0 { + return zero_int + } + if integer == one_int { + return multiplicand + } + if multiplicand == one_int { + return integer + } + // The final sign is the product of the signs + mut storage := []u32{len: integer.digits.len + multiplicand.digits.len} + multiply_digit_array(integer.digits, multiplicand.digits, mut storage) + return Integer{ + signum: integer.signum * multiplicand.signum + digits: storage + } +} + +pub fn (integer Integer) div_mod(divisor Integer) (Integer, Integer) { + // Quick exits + if divisor.signum == 0 { + panic('Cannot divide by zero') + } + if integer.signum == 0 { + return zero_int, zero_int + } + if divisor == one_int { + return integer, zero_int + } + if divisor.signum == -1 { + q, r := integer.div_mod(divisor.neg()) + return q.neg(), r + } + if integer.signum == -1 { + q, r := integer.neg().div_mod(divisor) + if r.signum == 0 { + return q.neg(), zero_int + } else { + return q.neg() - one_int, divisor - r + } + } + // Division for positive integers + mut q := []u32{cap: integer.digits.len - divisor.digits.len + 1} + mut r := []u32{cap: integer.digits.len} + divide_digit_array(integer.digits, divisor.digits, mut q, mut r) + quotient := Integer{ + signum: if q.len == 0 { 0 } else { 1 } + digits: q + } + remainder := Integer{ + signum: if r.len == 0 { 0 } else { 1 } + digits: r + } + return quotient, remainder +} + +pub fn (a Integer) / (b Integer) Integer { + q, _ := a.div_mod(b) + return q +} + +pub fn (a Integer) % (b Integer) Integer { + _, r := a.div_mod(b) + return r +} + +pub fn (a Integer) pow(exponent u32) Integer { + if exponent == 0 { + return one_int + } + if exponent == 1 { + return a + } + mut n := exponent + mut x := a + mut y := one_int + for n > 1 { + if n & 1 == 1 { + y *= x + } + x *= x + n >>= 1 + } + return x * y +} + +pub fn (a Integer) mod_pow(exponent u32, divisor Integer) Integer { + if exponent == 0 { + return one_int + } + if exponent == 1 { + return a % divisor + } + mut n := exponent + mut x := a % divisor + mut y := one_int + for n > 1 { + if n & 1 == 1 { + y *= x % divisor + } + x *= x % divisor + n >>= 1 + } + return x * y % divisor +} + +pub fn (mut a Integer) inc() { + a = a + one_int +} + +pub fn (mut a Integer) dec() { + a = a - one_int +} + +pub fn (a Integer) == (b Integer) bool { + return a.signum == b.signum && a.digits.len == b.digits.len && a.digits == b.digits +} + +pub fn (a Integer) abs_cmp(b Integer) int { + return compare_digit_array(a.digits, b.digits) +} + +pub fn (a Integer) < (b Integer) bool { + // Quick exits based on signum value: + if a.signum < b.signum { + return true + } + if a.signum > b.signum { + return false + } + // They have equal sign + signum := a.signum + if signum == 0 { // Are they both zero? + return false + } + // If they are negative, the one with the larger absolute value is smaller + cmp := a.abs_cmp() + return if signum < 0 { cmp > 0 } else { cmp < 0 } +} + +fn check_sign(a Integer) { + if a.signum < 0 { + panic('Bitwise operations are only supported for nonnegative integers') + } +} + +pub fn (a Integer) bitwise_or(b Integer) Integer { + check_sign(a) + check_sign(b) + mut result := []u32{len: util.imax(a.digits.len, b.digits.len), init: 0} + bitwise_or_digit_array(a.digits, b.digits, mut result) + return Integer{ + digits: result + signum: if result.len == 0 { 0 } else { 1 } + } +} + +pub fn (a Integer) bitwise_and(b Integer) Integer { + check_sign(a) + check_sign(b) + mut result := []u32{len: util.imax(a.digits.len, b.digits.len), init: 0} + bitwise_and_digit_array(a.digits, b.digits, mut result) + return Integer{ + digits: result + signum: if result.len == 0 { 0 } else { 1 } + } +} + +pub fn (a Integer) bitwise_not() Integer { + check_sign(a) + mut result := []u32{len: a.digits.len, init: 0} + bitwise_not_digit_array(a.digits, mut result) + return Integer{ + digits: result + signum: if result.len == 0 { 0 } else { 1 } + } +} + +pub fn (a Integer) bitwise_xor(b Integer) Integer { + check_sign(a) + check_sign(b) + mut result := []u32{len: util.imax(a.digits.len, b.digits.len), init: 0} + bitwise_xor_digit_array(a.digits, b.digits, mut result) + return Integer{ + digits: result + signum: if result.len == 0 { 0 } else { 1 } + } +} + +pub fn (a Integer) lshift(amount u32) Integer { + if a.signum == 0 { + return a + } + if amount == 0 { + return a + } + normalised_amount := amount & 31 + digit_offset := int(amount >> 5) + mut new_array := []u32{len: a.digits.len + digit_offset, init: 0} + for index in 0 .. a.digits.len { + new_array[index + digit_offset] = a.digits[index] + } + if normalised_amount > 0 { + shift_digits_left(new_array, normalised_amount, mut new_array) + } + return Integer{ + digits: new_array + signum: a.signum + } +} + +pub fn (a Integer) rshift(amount u32) Integer { + if a.signum == 0 { + return a + } + if amount == 0 { + return a + } + normalised_amount := amount & 31 + digit_offset := int(amount >> 5) + if digit_offset >= a.digits.len { + return zero_int + } + mut new_array := []u32{len: a.digits.len - digit_offset, init: 0} + for index in 0 .. new_array.len { + new_array[index] = a.digits[index + digit_offset] + } + if normalised_amount > 0 { + shift_digits_right(new_array, normalised_amount, mut new_array) + } + return Integer{ + digits: new_array + signum: a.signum + } +} + +pub fn (integer Integer) binary_str() string { + // We have the zero integer + if integer.signum == 0 { + return '0' + } + // Add the sign if present + sign_needed := integer.signum == -1 + mut result_builder := strings.new_builder(integer.digits.len * 32 + + if sign_needed { 1 } else { 0 }) + if sign_needed { + result_builder.write_string('-') + } + + result_builder.write_string(u32_to_binary_without_lz(integer.digits[integer.digits.len - 1])) + + for index := integer.digits.len - 2; index >= 0; index-- { + result_builder.write_string(u32_to_binary_with_lz(integer.digits[index])) + } + return result_builder.str() +} + +pub fn (integer Integer) hex() string { + // We have the zero integer + if integer.signum == 0 { + return '0' + } + // Add the sign if present + sign_needed := integer.signum == -1 + mut result_builder := strings.new_builder(integer.digits.len * 8 + + if sign_needed { 1 } else { 0 }) + if sign_needed { + result_builder.write_string('-') + } + + result_builder.write_string(u32_to_hex_without_lz(integer.digits[integer.digits.len - 1])) + + for index := integer.digits.len - 2; index >= 0; index-- { + result_builder.write_string(u32_to_hex_with_lz(integer.digits[index])) + } + return result_builder.str() +} + +pub fn (integer Integer) radix_str(radix u32) string { + if integer.signum == 0 { + return '0' + } + return match radix { + 2 { + integer.binary_str() + } + 16 { + integer.hex() + } + else { + integer.general_radix_str(radix) + } + } +} + +fn (integer Integer) general_radix_str(radix u32) string { + divisor := integer_from_u32(radix) + mut rune_array := []rune{} + + mut current := integer.abs() + mut digit := zero_int + for current.signum > 0 { + current, digit = current.div_mod(divisor) + rune_array << big.digit_array[digit.int()] + } + if integer.signum == -1 { + rune_array << `-` + } + + rune_array.reverse_in_place() + return rune_array.string() +} + +pub fn (integer Integer) str() string { + return integer.radix_str(10) +} + +fn u32_to_binary_without_lz(value u32) string { + return strconv.format_uint(value, 2) +} + +fn u32_to_binary_with_lz(value u32) string { + mut result_builder := strings.new_builder(32) + binary_result := strconv.format_uint(value, 2) + + result_builder.write_string(strings.repeat(`0`, 32 - binary_result.len)) + result_builder.write_string(binary_result) + + return result_builder.str() +} + +fn u32_to_hex_without_lz(value u32) string { + return strconv.format_uint(value, 16) +} + +fn u32_to_hex_with_lz(value u32) string { + mut result_builder := strings.new_builder(8) + hex_result := strconv.format_uint(value, 16) + + result_builder.write_string(strings.repeat(`0`, 8 - hex_result.len)) + result_builder.write_string(hex_result) + + return result_builder.str() +} + +pub fn (a Integer) int() int { + if a.signum == 0 { + return 0 + } + value := int(a.digits[0] & 0x7fffffff) + return value * a.signum +} + +pub fn (a Integer) bytes() ([]byte, int) { + if a.signum == 0 { + return []byte{len: 0}, 0 + } + mut result := []byte{cap: a.digits.len * 4} + mut mask := u32(0xff000000) + mut offset := 24 + mut non_zero_found := false + for index := a.digits.len - 1; index >= 0; { + value := byte((a.digits[index] & mask) >> offset) + non_zero_found = non_zero_found || value != 0 + if non_zero_found { + result << value + } + mask >>= 8 + offset -= 8 + if offset < 0 { + mask = u32(0xff000000) + offset = 24 + index-- + } + } + return result, a.signum +} + +pub fn (a Integer) gcd(b Integer) Integer { + if a.signum == 0 { + return b.abs() + } + if b.signum == 0 { + return a.abs() + } + if a.signum < 0 { + return a.neg().gcd(b) + } + if b.signum < 0 { + return a.gcd(b.neg()) + } + mut x := a + mut y := b + mut r := x % y + for r.signum != 0 { + x = y + y = r + r = x % y + } + return y +} + +pub fn (a Integer) factorial() Integer { + if a.signum == 0 { + return one_int + } + mut product := one_int + mut current := a + for current.signum != 0 { + product *= current + current.dec() + } + return product +} + +// isqrt returns the closest integer square root of the given integer. +pub fn (a Integer) isqrt() Integer { + if a.signum < 0 { + panic('Cannot obtain square root of negative integer') + } + if a.signum == 0 { + return a + } + if a.digits.len == 1 && a.digits.last() == 1 { + return a + } + + mut shift := a.digits.len * 32 - bits.leading_zeros_32(a.digits.last()) + if shift & 1 == 1 { + shift += 1 + } + mut result := zero_int + for shift >= 0 { + result = result.lshift(1) + larger := result + one_int + if (larger * larger).abs_cmp(a.rshift(u32(shift))) <= 0 { + result = larger + } + shift -= 2 + } + return result +}