This repository has been archived on 2026-04-17. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
bhlib-old/src/bigint.c

1658 lines
37 KiB
C
Raw Normal View History

2024-06-16 12:29:48 +03:00
#include <bh/internal/bigint.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
static size_t bh_bigint_type_clz(BH_BIGINT_TYPE x)
{
static const bh_uint8_t lookup[256] = {
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
size_t i, tmp;
for (i = 0; i < BH_BIGINT_BITS / 8 + 1; i++)
{
tmp = (x >> (BH_BIGINT_BITS - 7)) & 0xFF;
if (tmp)
return lookup[tmp] + 8 * i - 1;
x <<= 8;
}
return BH_BIGINT_BITS;
}
static size_t bh_bigint_type_log2(BH_BIGINT_TYPE x)
{
static const bh_uint8_t lookup[256] = {
0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};
size_t i, tmp;
for (i = 0; i < BH_BIGINT_BITS / 8 + 1; i++)
{
tmp = (x >> (BH_BIGINT_BITS - 7)) & 0xFF;
if (tmp)
return ((BH_BIGINT_BITS + 1) - 8 * (i + 1)) + lookup[tmp];
2024-06-16 12:29:48 +03:00
x <<= 8;
}
return 0;
}
bh_bigint_t *bh_bigint_new(void)
{
bh_bigint_t *result;
result = malloc(sizeof(*result));
if (result)
bh_bigint_init(result);
return result;
}
void bh_bigint_free(bh_bigint_t *bigint)
{
bh_bigint_destroy(bigint);
free(bigint);
}
void bh_bigint_init(bh_bigint_t *bigint)
{
bigint->data = NULL;
bigint->capacity = 0;
bh_bigint_clear(bigint);
}
void bh_bigint_destroy(bh_bigint_t *bigint)
{
if (bigint->data)
free(bigint->data);
}
int bh_bigint_block(void)
{
return BH_BIGINT_BITS;
}
int bh_bigint_clear(bh_bigint_t *bigint)
{
bigint->error = 0;
bigint->size = 0;
bigint->type = 0;
return BH_OK;
}
int bh_bigint_set(bh_bigint_t* to,
bh_bigint_t* from)
{
/* Shortcut - one of the arguments has error flag set */
if (from->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Reserve space for big integer data */
if (to->capacity < from->size)
{
if (bh_bigint_reserve(to, from->size))
{
to->error = 1;
return BH_OOM;
}
}
/* Copy data */
memmove(to->data, from->data, sizeof(*from->data) * from->size);
/* Set fields */
to->size = from->size;
to->type = from->type;
return BH_OK;
}
/*
* Universal implementation of set.
*
* This function:
* - determines sign of the input integer
* - reserves space in target big integer
* - copies data in little-endian like format
*/
#define BH_BIGINT_SET_BODY(to, from) \
size_t size; \
bh_bigint_clear((to)); \
if ((from) < 0) { (to)->type = -1; (from) = -(from); } \
else if ((from) == 0) { (to)->type = 0; return BH_OK; } \
else (to)->type = 1; \
if ((to)->capacity < (64 / BH_BIGINT_BITS + 1)) \
{ \
if (bh_bigint_reserve((to), (64 / BH_BIGINT_BITS + 1))) \
{ \
(to)->error = 1; \
return BH_OOM; \
} \
} \
for (size = 0; (from); size++) \
{ \
(to)->data[size] = (from) & BH_BIGINT_MASK; \
(from) >>= BH_BIGINT_BITS; \
} \
(to)->size = size; \
return BH_OK;
/*
* Universal implementation of get.
*
* This function:
* - copies blocks from the most to less significant units
* - left shifts result by unit bit width - 1
*/
#define BH_BIGINT_GET_BODY(from, rtype) \
size_t i; \
rtype result = 0; \
if ((from)->error) return 0; \
for (i = (from)->size; i > 0; i--) \
{ \
result <<= BH_BIGINT_BITS; \
result += (from)->data[i - 1]; \
} \
result *= (from)->type; \
return result; \
int bh_bigint_set_int(bh_bigint_t *to,
int from)
{
BH_BIGINT_SET_BODY(to, from);
}
int bh_bigint_set_uint(bh_bigint_t *to,
unsigned int from)
{
BH_BIGINT_SET_BODY(to, from);
}
int bh_bigint_get_int(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, int);
}
unsigned int bh_bigint_get_uint(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, unsigned int);
}
int bh_bigint_set_int8(bh_bigint_t *to,
bh_int8_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
int bh_bigint_set_uint8(bh_bigint_t *to,
bh_uint8_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
bh_int8_t bh_bigint_get_int8(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_int8_t);
}
bh_uint8_t bh_bigint_get_uint8(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_uint8_t);
}
int bh_bigint_set_int16(bh_bigint_t *to,
bh_int16_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
int bh_bigint_set_uint16(bh_bigint_t *to,
bh_uint16_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
bh_int16_t bh_bigint_get_int16(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_int16_t);
}
bh_uint16_t bh_bigint_get_uint16(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_uint16_t);
}
int bh_bigint_set_int32(bh_bigint_t *to,
bh_int32_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
int bh_bigint_set_uint32(bh_bigint_t *to,
bh_uint32_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
bh_int32_t bh_bigint_get_int32(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_int32_t);
}
bh_uint32_t bh_bigint_get_uint32(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_uint32_t);
}
int bh_bigint_set_int64(bh_bigint_t *to,
bh_int64_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
int bh_bigint_set_uint64(bh_bigint_t *to,
bh_uint64_t from)
{
BH_BIGINT_SET_BODY(to, from);
}
bh_int64_t bh_bigint_get_int64(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_int64_t);
}
bh_uint64_t bh_bigint_get_uint64(bh_bigint_t *bigint)
{
BH_BIGINT_GET_BODY(bigint, bh_uint64_t);
}
size_t bh_bigint_capacity(bh_bigint_t *bigint)
{
return bigint->capacity;
}
size_t bh_bigint_length(bh_bigint_t *bigint)
{
return bigint->size;
}
int bh_bigint_reserve(bh_bigint_t *bigint,
size_t size)
{
void *data;
/* Initialize variables */
data = NULL;
/* New capacity can't be lower than current big integer size */
if (size < bigint->size)
size = bigint->size;
/* Prevent same size reallocation */
if (size == bigint->size)
return BH_OK;
if (size)
{
/* Allocate memory for the new digit storage */
data = malloc(sizeof(*bigint->data) * size);
if (!data)
return BH_OOM;
/* Copy existing data */
if (bigint->size)
memmove(data, bigint->data, sizeof(*bigint->data) * bigint->size);
}
/* If big integer had existing data - free it */
if (bigint->data)
free(bigint->data);
/* Update fields */
bigint->data = data;
bigint->capacity = size;
return BH_OK;
}
static int bh_bigint_add_base(bh_bigint_t *to,
bh_bigint_t *most,
bh_bigint_t *least)
{
BH_BIGINT_TYPE carry, tmp;
size_t i;
/* Clear up result error flag */
to->error = 0;
/* Reserve space for output */
if (to->capacity < most->size + 1)
{
if (bh_bigint_reserve(to, most->size + 1))
{
to->error = 1;
return BH_OOM;
}
}
/* Reset carry value */
carry = 0;
/* Main addition loop */
for (i = 0; i < least->size; i++)
{
tmp = most->data[i] + least->data[i] + carry;
carry = tmp >> BH_BIGINT_BITS;
to->data[i] = tmp & BH_BIGINT_MASK;
}
/* Carry propogation loop */
for (; i < most->size; i++)
{
tmp = most->data[i] + carry;
carry = tmp >> BH_BIGINT_BITS;
to->data[i] = tmp & BH_BIGINT_MASK;
}
/* Write carry value and update result size */
to->data[i] = carry;
if (carry)
to->size = most->size + 1;
else
to->size = most->size;
2024-06-16 12:29:48 +03:00
return BH_OK;
}
static int bh_bigint_sub_base(bh_bigint_t *to,
bh_bigint_t *most,
bh_bigint_t *least)
{
BH_BIGINT_TYPE carry, tmp;
size_t size, i;
2024-06-16 12:29:48 +03:00
/* Clear up result error flag */
to->error = 0;
/* Reserve space for output */
if (to->capacity < most->size)
{
if (bh_bigint_reserve(to, most->size))
{
to->error = 1;
return BH_OOM;
}
}
/* Reset carry and size value */
carry = 0;
size = 0;
/* Main subtraction loop */
for (i = 0; i < least->size; i++)
{
tmp = most->data[i] - least->data[i] - carry;
carry = tmp >> BH_BIGINT_BITS;
to->data[i] = tmp & BH_BIGINT_MASK;
if (to->data[i])
size = i + 1;
}
/* Carry propogation loop */
for (; i < most->size; i++)
{
tmp = most->data[i] - carry;
carry = tmp >> BH_BIGINT_BITS;
to->data[i] = tmp & BH_BIGINT_MASK;
if (to->data[i])
size = i + 1;
}
/* Update result size */
to->size = size;
to->error = 0;
return BH_OK;
}
int bh_bigint_add(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
bh_bigint_t *least, *most;
int type, code;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error)
{
to->error = 1;
return BH_ERROR;
}
/* Determine most and least numbers */
if (bh_bigint_equal_mod(left, right) >= 0)
{
most = left;
least = right;
type = left->type;
}
else
{
most = right;
least = left;
type = right->type;
}
/* Perform addition or subtraction */
if (most->type == least->type)
code = bh_bigint_add_base(to, most, least);
else
code = bh_bigint_sub_base(to, most, least);
if (!code)
{
/* Set result sign or zero */
if (to->size)
to->type = type;
else
to->type = 0;
}
2024-06-16 12:29:48 +03:00
/* Return result code */
return code;
}
int bh_bigint_sub(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
bh_bigint_t *least, *most;
int type, code;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error)
{
to->error = 1;
return BH_ERROR;
}
/* Determine most and least numbers */
if (bh_bigint_equal_mod(left, right) >= 0)
{
most = left;
least = right;
type = left->type;
}
else
{
most = right;
least = left;
type = right->type * -1;
}
/* Perform addition or subtraction */
if (most->type == least->type)
code = bh_bigint_sub_base(to, most, least);
else
code = bh_bigint_add_base(to, most, least);
if (!code)
{
/* Set result sign or zero */
if (to->size)
to->type = type;
else
to->type = 0;
}
2024-06-16 12:29:48 +03:00
/* Return result code */
return code;
}
int bh_bigint_mul(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
bh_bigint_t *result;
BH_BIGINT_TYPE carry;
BH_BIGINT_TMP tmp;
size_t i, j, size;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Shortcut - multiplication by zero */
if (left->type == 0 || right->type == 0)
{
to->type = 0;
to->size = 0;
return BH_OK;
}
/* If to is left or right argument - allocate temporary storage */
result = to;
if (to == left || to == right)
{
result = bh_bigint_new();
if (!result)
{
to->error = 1;
return BH_OOM;
}
}
/* Reserve space for result */
if (result->capacity < (left->size + right->size + 1))
{
if (bh_bigint_reserve(result, left->size + right->size + 1))
{
to->error = 1;
return BH_OOM;
}
}
/* Clear the result array */
memset(result->data, 0, sizeof(*result->data) * (left->size + right->size));
/* Prepare variables */
size = 0;
/* Multiplication loop */
for (i = 0; i < left->size; i++)
{
carry = 0;
for (j = 0; j < right->size; j++)
{
tmp = (BH_BIGINT_TMP)left->data[i] * (BH_BIGINT_TMP)right->data[j];
tmp += carry + result->data[j + i];
carry = tmp >> BH_BIGINT_BITS;
result->data[j + i] = tmp & BH_BIGINT_MASK;
}
result->data[j + i] = carry;
}
/* Truncate multiplication result size */
size = left->size + right->size;
while (size && !result->data[size - 1]) size--;
/* Determine sign */
if (left->type != right->type)
result->type = -1;
else
result->type = 1;
/* Update size */
result->size = size;
/* If tmp is temporary storage - copy data back and deallocate */
if (result != to)
{
bh_bigint_set(to, result);
bh_bigint_free(result);
}
return BH_OK;
}
int bh_bigint_mul_digit(bh_bigint_t *to,
bh_bigint_t *left,
bh_int16_t right)
{
BH_BIGINT_TYPE carry;
BH_BIGINT_TMP tmp;
size_t i, size;
/* Shortcut - one of the arguments has error flag set */
if (left->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Shortcut - multiplication by zero */
if (left->type == 0 || right == 0)
{
to->type = 0;
to->size = 0;
return BH_OK;
}
/* Reserve space for result */
if (to->capacity < (left->size + 1))
{
if (bh_bigint_reserve(to, left->size + 1))
{
to->error = 1;
return BH_OOM;
}
}
/* Determine sign */
if ((left->type > 0 && right < 0) || (left->type < 0 && right > 0))
to->type = -1;
else
to->type = 1;
if (right < 0)
right = -right;
/* Prepare variables */
carry = 0;
/* Multiplication loop */
for (i = 0; i < left->size; i++)
{
tmp = (BH_BIGINT_TMP)left->data[i] * (BH_BIGINT_TMP)right + carry;
carry = tmp >> BH_BIGINT_BITS;
to->data[i] = tmp & BH_BIGINT_MASK;
}
to->data[i] = carry;
/* Truncate multiplication result size */
size = left->size + 1;
while (size && !to->data[size - 1]) size--;
/* Update size */
to->size = size;
return BH_OK;
}
2024-06-16 12:29:48 +03:00
int bh_bigint_pow(bh_bigint_t *to,
bh_bigint_t *from,
bh_uint32_t power)
{
bh_bigint_t *base;
int code, type;
/* Initialize variables */
base = NULL;
code = BH_OK;
/* Shortcut - one of the arguments has error flag set */
if (from->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Shortcut - power is equal to 0 */
if (!power)
return bh_bigint_set_int(to, 1);
/* Shortcut - value is 0 */
if (bh_bigint_is_zero(from))
return bh_bigint_clear(to);
/* Determine sign */
if (from->type < 0 && power & 0x1)
type = -1;
else
type = 1;
/* Setup base variable */
base = bh_bigint_new();
if (!base || bh_bigint_set(base, from))
{
to->error = 1;
code = BH_OOM;
goto finish;
}
if (bh_bigint_set_int(to, 1))
{
to->error = 1;
code = BH_OOM;
goto finish;
}
/* Perform exponentiation by squaring */
while (power)
{
if (power & 0x1)
bh_bigint_mul(to, to, base);
bh_bigint_mul(base, base, base);
power >>= 1;
/* Stop if error occured */
if (base->error || to->error)
{
to->error = 1;
code = BH_ERROR;
break;
}
}
/* Fix the sign */
to->type = type;
finish:
/* Free internal variables */
if (base)
bh_bigint_free(base);
return code;
}
int bh_bigint_powm(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right,
bh_bigint_t *mod)
{
bh_bigint_t *result, *base, *exponent;
int code, type;
/* Initialize variables */
result = NULL; base = NULL; exponent = NULL;
code = BH_OK;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error || mod->error)
2024-06-16 12:29:48 +03:00
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
2024-06-16 12:29:48 +03:00
/* Shortcut - power is equal to 0 */
if (bh_bigint_is_zero(right))
return bh_bigint_set_int(to, 1);
/* Shortcut - value is 0 */
if (bh_bigint_is_zero(left))
return bh_bigint_clear(to);
/* Shortcut - power is negative */
if (bh_bigint_is_negative(right))
return bh_bigint_clear(to);
2024-06-16 12:29:48 +03:00
/* Figure out result sign */
if (left->type < 0 && right->data[0] & 0x1)
type = -1;
else
type = 1;
/* Setup result variable */
result = to;
if (to == mod)
{
result = bh_bigint_new();
if (!result)
{
to->error = 1;
code = BH_OOM;
goto finish;
}
}
/* Reserve space for result */
if (bh_bigint_reserve(result, mod->size * 2 + 1) || bh_bigint_set_int(result, 1))
{
to->error = 1;
code = BH_OOM;
goto finish;
}
/* Setup base variable */
base = bh_bigint_new();
if (!base || bh_bigint_reserve(base, mod->size * 2 + 1))
{
to->error = 1;
code = BH_OOM;
goto finish;
}
if (bh_bigint_mod(base, left, mod))
{
to->error = 1;
code = BH_OOM;
goto finish;
}
/* Setup exponent variable */
exponent = bh_bigint_new();
if (!exponent || bh_bigint_set(exponent, right))
{
to->error = 1;
code = BH_OOM;
goto finish;
}
/* Perform exponentiation by squaring */
while (!bh_bigint_is_zero(exponent))
{
if (exponent->data[0] & 0x1)
{
bh_bigint_mul(result, result, base);
bh_bigint_mod(result, result, mod);
}
bh_bigint_mul(base, base, base);
bh_bigint_mod(base, base, mod);
bh_bigint_rsh(exponent, exponent, 1);
2024-06-16 12:29:48 +03:00
/* Stop if error occured */
if (base->error || result->error)
{
to->error = 1;
code = BH_ERROR;
break;
}
}
/* Fix the sign */
result->type = type;
finish:
/* Free internal variables */
if (base)
bh_bigint_free(base);
if (exponent)
bh_bigint_free(exponent);
if (result && result != to)
{
bh_bigint_set(to, result);
bh_bigint_free(result);
}
return code;
}
static BH_BIGINT_TYPE bh_bigint_div_guess(bh_bigint_t *left,
bh_bigint_t *right)
{
BH_BIGINT_TMP tmp;
2024-06-16 12:29:48 +03:00
/* Left is bigger then right */
if (bh_bigint_equal_mod(left, right) < 0)
return 0;
/* Make a guess */
tmp = left->data[left->size - 1];
2024-06-16 12:29:48 +03:00
if (left->size != right->size)
tmp = (tmp << BH_BIGINT_BITS) + left->data[left->size - 2];
return tmp / right->data[right->size - 1];
}
static int bh_bigint_div_base(bh_bigint_t *quotient,
bh_bigint_t *remainder,
bh_bigint_t *left,
bh_bigint_t *right,
int *shift)
{
bh_bigint_t *nleft, *nright, *tmp;
BH_BIGINT_TYPE q;
int code;
/* Initialize variables */
nleft = NULL; nright = NULL; tmp = NULL;
code = BH_OK;
/* Clear up result error flags */
if (quotient)
quotient->error = 0;
remainder->error = 0;
/* Allocate space for normilized versions of integers and temporary */
nleft = bh_bigint_new();
if (!nleft || bh_bigint_reserve(nleft, left->size + 1))
{
if (quotient)
quotient->error = 1;
remainder->error = 1;
code = BH_OOM;
goto finish;
}
nright = bh_bigint_new();
if (!nright || bh_bigint_reserve(nright, right->size + 1))
{
if (quotient)
quotient->error = 1;
remainder->error = 1;
code = BH_OOM;
goto finish;
}
tmp = bh_bigint_new();
if (!tmp || bh_bigint_reserve(tmp, left->size))
{
if (quotient)
quotient->error = 1;
remainder->error = 1;
code = BH_OOM;
goto finish;
}
/* Allocate space for remainder and quotient */
if (quotient && (quotient->capacity < left->size + 1))
{
if (bh_bigint_reserve(quotient, left->size + 1))
{
if (quotient)
quotient->error = 1;
remainder->error = 1;
code = BH_OOM;
goto finish;
}
}
if (remainder->capacity < left->size + 1)
{
if (bh_bigint_reserve(remainder, left->size + 1))
{
if (quotient)
quotient->error = 1;
remainder->error = 1;
code = BH_OOM;
goto finish;
}
}
/* Normilize integers */
*shift = bh_bigint_type_clz(right->data[right->size - 1]);
bh_bigint_lsh(nleft, left, *shift);
bh_bigint_lsh(nright, right, *shift);
/* Reset quotient and remainder. */
if (quotient)
bh_bigint_set_int(quotient, 0);
bh_bigint_set_int(remainder, 0);
/* Get as many digits from nleft so that remainder >= right */
while (bh_bigint_equal_mod(remainder, nright) < 0)
{
bh_bigint_lsh(remainder, remainder, BH_BIGINT_BITS);
remainder->data[0] = nleft->data[nleft->size - 1];
if (!remainder->size)
{
remainder->size = 1;
remainder->type = 1;
}
nleft->size--;
}
2024-06-16 12:29:48 +03:00
while (1)
{
/* Make a guess at what first digit of quotient should be */
q = bh_bigint_div_guess(remainder, nright);
tmp->size = 1;
tmp->type = 1;
tmp->data[0] = q;
bh_bigint_mul(tmp, nright, tmp);
/* Decriment quotient digit if our guess was wrong */
while (bh_bigint_equal_mod(remainder, tmp) < 0)
{
q--;
tmp->size = 1;
tmp->type = 1;
tmp->data[0] = q;
bh_bigint_mul(tmp, nright, tmp);
}
/* Append guessed digit to quotient (if needed) */
if (quotient)
{
bh_bigint_lsh(quotient, quotient, BH_BIGINT_BITS);
quotient->data[0] = q;
if (!quotient->size)
{
quotient->size = 1;
quotient->type = 1;
}
}
/* Calculate remainder */
/* Prevent using addition logic and use subtraction */
bh_bigint_sub_base(remainder, remainder, tmp);
2024-06-16 12:29:48 +03:00
if (!nleft->size)
break;
/* Fetch remaining digit from nleft */
bh_bigint_lsh(remainder, remainder, BH_BIGINT_BITS);
remainder->data[0] = nleft->data[nleft->size - 1];
if (!remainder->size)
{
remainder->size = 1;
remainder->type = 1;
}
nleft->size--;
}
finish:
if (nleft)
bh_bigint_free(nleft);
if (nright)
bh_bigint_free(nright);
if (tmp)
bh_bigint_free(tmp);
return code;
}
/**
* This follows C99 model (a/b)*b + a%b
*/
2024-06-16 12:29:48 +03:00
int bh_bigint_divmod(bh_bigint_t *quotient,
bh_bigint_t *remainder,
bh_bigint_t *left,
bh_bigint_t *right)
{
int shift, code, type;
bh_bigint_t *tmp;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error)
{
if (quotient)
quotient->error = 1;
if (remainder)
remainder->error = 1;
return BH_ERROR;
}
/* Clear up result error flags */
if (quotient)
quotient->error = 0;
if (remainder)
remainder->error = 0;
/* Shortcut - left is zero - result is zero */
if (left->type == 0)
{
if (quotient)
bh_bigint_clear(quotient);
if (remainder)
bh_bigint_clear(remainder);
return BH_OK;
}
/* Shortcut - division by zero */
if (right->type == 0)
{
if (quotient)
quotient->error = 1;
if (remainder)
remainder->error = 1;
return BH_ERROR;
}
/* Shortcut - left is less then right - quotient is zero, remainder is left */
if (bh_bigint_equal_mod(left, right) < 0)
{
if (remainder && bh_bigint_set(remainder, left))
{
if (quotient)
quotient->error = 1;
remainder->error = 1;
return BH_OOM;
}
if (quotient && bh_bigint_clear(quotient))
{
quotient->error = 1;
if (remainder)
remainder->error = 1;
return BH_OOM;
}
return BH_OK;
}
/* Figure out result sign */
if (left->type != right->type)
type = -1;
else
type = 1;
/* Make sure we have remainder as our working buffer */
tmp = remainder;
if (!remainder)
{
tmp = bh_bigint_new();
if (!tmp)
{
if (quotient)
quotient->error = 1;
if (remainder)
remainder->error = 1;
return BH_OOM;
}
}
code = bh_bigint_div_base(quotient, tmp, left, right, &shift);
if (!code)
{
/* Remainder should use sign of the left operand */
tmp->type = left->type;
2024-06-16 12:29:48 +03:00
/* If remainder required - normilize it */
if (tmp == remainder)
bh_bigint_rsh(remainder, remainder, shift);
2024-06-16 12:29:48 +03:00
/* Fix the sign */
if (quotient)
quotient->type = type;
}
if (tmp != remainder)
bh_bigint_free(tmp);
return code;
}
int bh_bigint_div(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
return bh_bigint_divmod(to, NULL, left, right);
}
int bh_bigint_mod(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
return bh_bigint_divmod(NULL, to, left, right);
}
int bh_bigint_lsh(bh_bigint_t *to,
bh_bigint_t *left,
bh_uint32_t shift)
{
size_t i, bits, blocks;
BH_BIGINT_TYPE tmp;
2024-06-16 12:29:48 +03:00
/* Shortcut - one of the arguments has error flag set */
if (left->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Calculate distance in blocks and bits */
blocks = shift / BH_BIGINT_BITS;
bits = shift % BH_BIGINT_BITS;
/* Shortcut - initial value is zero */
if (left->type == 0)
{
to->type = 0;
to->size = 0;
return BH_OK;
}
/* Check if shift is block aligned */
if (bits)
{
/* Shift is not block aligned - reserve space for 1 additional block */
if (to->capacity < left->size + blocks + 1)
{
if (bh_bigint_reserve(to, left->size + blocks + 1))
{
to->error = 1;
return BH_OOM;
}
}
/* Main shift loop for unaligned shift */
for (i = left->size + blocks + 1; i > blocks; i--)
{
tmp = 0;
if (i - blocks - 1 < left->size)
tmp |= left->data[i - blocks - 1] << bits;
if (i - blocks - 2 < left->size)
tmp |= left->data[i - blocks - 2] >> (BH_BIGINT_BITS - bits);
to->data[i - 1] = tmp & BH_BIGINT_MASK;
}
/* Zero out shifted in blocks */
for (; i > 0; i--)
to->data[i - 1] = 0;
/* Trim size to nearest block */
i = left->size + blocks + 1;
while (i && !to->data[i - 1]) i--;
}
else
{
/* Shift is block aligned */
if (to->capacity < left->size + blocks)
{
if (bh_bigint_reserve(to, left->size + blocks))
{
to->error = 1;
return BH_OOM;
}
}
/* Main loop for aligned shift */
for (i = left->size + blocks; i > blocks; i--)
to->data[i - 1] = left->data[i - blocks - 1];
/* Zero out shifted in blocks */
for (; i > 0; i--)
to->data[i - 1] = 0;
/* Trim size to nearest block */
i = left->size + blocks;
while (i && !to->data[i - 1]) i--;
}
/* Update fields */
to->size = i;
to->type = left->type;
return BH_OK;
}
int bh_bigint_rsh(bh_bigint_t *to,
bh_bigint_t *left,
bh_uint32_t shift)
{
size_t i, bits, blocks;
BH_BIGINT_TYPE tmp;
/* Shortcut - one of the arguments has error flag set */
if (left->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Calculate distance in blocks and bits */
blocks = shift / BH_BIGINT_BITS;
bits = shift % BH_BIGINT_BITS;
2024-06-16 12:29:48 +03:00
/* Shortcut - shift is bigger than value or value is 0 */
if (left->type == 0 || left->size < blocks)
{
to->type = 0;
to->size = 0;
return BH_OK;
}
/* Check if shift is block aligned */
if (bits)
{
/* Shift is not block aligned */
if (to->capacity < left->size - blocks)
{
if (bh_bigint_reserve(to, left->size - blocks))
{
to->error = 1;
return BH_OOM;
}
}
/* Main loop for unaligned shift */
for (i = 0; i < left->size - blocks; i++)
{
tmp = 0;
if (i + blocks < left->size)
tmp |= left->data[i + blocks] >> bits;
if (i + blocks + 1 < left->size)
tmp |= left->data[i + blocks + 1] << (BH_BIGINT_BITS - bits);
tmp &= BH_BIGINT_MASK;
to->data[i] = tmp;
}
}
else
{
/* Shift is block aligned */
if (to->capacity < left->size - blocks)
{
if (bh_bigint_reserve(to, left->size - blocks))
{
to->error = 1;
return BH_OOM;
}
}
2024-06-16 12:29:48 +03:00
/* Main loop for aligned shift */
for (i = 0; i < left->size - blocks; i++)
to->data[i] = left->data[i + blocks];
}
/* Trim remaining blocks */
i = left->size - blocks;
while (i && !to->data[i - 1]) i--;
/* Update fields */
2024-06-16 12:29:48 +03:00
to->size = i;
to->type = left->type;
if (!to->size)
to->type = 0;
return BH_OK;
}
int bh_bigint_or(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
size_t i, size;
BH_BIGINT_TYPE tmp;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Calculate resulting size and reserve space */
size = (left->size > right->size) ? (left->size) : (right->size);
if (to->capacity < size)
{
if (bh_bigint_reserve(to, size))
{
to->error = 1;
return BH_OOM;
}
}
/* Apply OR for each block */
for (i = 0; i < size; i++)
{
tmp = (i < left->size) ? (left->data[i]) : (0);
tmp |= (i < right->size) ? (right->data[i]) : (0);
to->data[i] = tmp;
}
/* Shrink size */
while (size && !to->data[size - 1]) size--;
2024-06-16 12:29:48 +03:00
/* Update fields */
to->type = 1;
to->size = size;
if (!size)
to->type = 0;
2024-06-16 12:29:48 +03:00
return BH_OK;
}
int bh_bigint_and(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
size_t i, size;
BH_BIGINT_TYPE tmp;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Calculate resulting size and reserve space */
size = (left->size > right->size) ? (left->size) : (right->size);
if (to->capacity < size)
{
if (bh_bigint_reserve(to, size))
{
to->error = 1;
return BH_OOM;
}
}
/* Apply AND for each block */
for (i = 0; i < size; i++)
{
tmp = (i < left->size) ? (left->data[i]) : (0);
tmp &= (i < right->size) ? (right->data[i]) : (0);
to->data[i] = tmp;
}
/* Shrink size */
while (size && !to->data[size - 1]) size--;
2024-06-16 12:29:48 +03:00
/* Update fields */
to->type = 1;
to->size = size;
if (!size)
to->type = 0;
2024-06-16 12:29:48 +03:00
return BH_OK;
}
int bh_bigint_xor(bh_bigint_t *to,
bh_bigint_t *left,
bh_bigint_t *right)
{
size_t i, size;
BH_BIGINT_TYPE tmp;
/* Shortcut - one of the arguments has error flag set */
if (left->error || right->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* Calculate resulting size and reserve space */
size = (left->size > right->size) ? (left->size) : (right->size);
if (to->capacity < size)
{
if (bh_bigint_reserve(to, size))
{
to->error = 1;
return BH_OOM;
}
}
/* Apply XOR for each block */
for (i = 0; i < size; i++)
{
tmp = (i < left->size) ? (left->data[i]) : (0);
tmp ^= (i < right->size) ? (right->data[i]) : (0);
to->data[i] = tmp;
}
/* Shrink size */
while (size && !to->data[size - 1]) size--;
2024-06-16 12:29:48 +03:00
/* Update fields */
to->type = 1;
to->size = size;
if (!size)
to->type = 0;
2024-06-16 12:29:48 +03:00
return BH_OK;
}
int bh_bigint_negate(bh_bigint_t *to,
bh_bigint_t *from)
{
/* Shortcut - one of the arguments has error flag set */
if (from->error)
{
to->error = 1;
return BH_ERROR;
}
/* Clear up result error flag */
to->error = 0;
/* If to and from are not the same - copy data */
if (to != from && bh_bigint_set(to, from))
{
to->error = 1;
return BH_OOM;
}
/* Change the sign */
to->type = 0 - to->type;
2024-06-16 12:29:48 +03:00
return BH_OK;
}
int bh_bigint_equal(bh_bigint_t *left,
bh_bigint_t *right)
{
/* Shortcut - one of the arguments have error flag set */
if (left->error || right->error)
return 0;
/* Shortcut - check for sign */
if (left->type - right->type)
return left->type - right->type;
/* Same sign - check contents */
return bh_bigint_equal_mod(left, right);
}
int bh_bigint_equal_mod(bh_bigint_t *left,
bh_bigint_t *right)
{
size_t i;
/* Shortcut - one of the arguments have error flag set */
if (left->error || right->error)
return 0;
/* Shortcut - check for length */
if (left->size < right->size)
return -1;
else if (left->size > right->size)
return 1;
/* Equal length - compare unit by unit from the most significant */
for (i = 0; i < left->size; i++)
{
size_t index = left->size - i - 1;
if (left->data[index] < right->data[index])
return -1;
else if (left->data[index] > right->data[index])
return 1;
}
/* Units are equal */
return 0;
}
int bh_bigint_is_negative(bh_bigint_t *bigint)
{
if (bigint->error)
return 1;
return bigint->type < 0;
}
int bh_bigint_is_zero(bh_bigint_t *bigint)
{
if (bigint->error)
return 1;
return bigint->type == 0;
}
int bh_bigint_is_error(bh_bigint_t *bigint)
{
return bigint->error;
}
size_t bh_bigint_log2(bh_bigint_t *bigint)
{
size_t result;
if (bigint->size == 0 || bigint->type == 0)
return 0;
result = bh_bigint_type_log2(bigint->data[bigint->size - 1]);
return result + (bigint->size - 1) * BH_BIGINT_BITS;
}