#include #include #include #include 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]; 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; 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; /* 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); /* Set result sign */ if (!code) to->type = type; /* 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); /* Set result sign */ if (!code) to->type = type; /* 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_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) { to->error = 1; return BH_ERROR; } /* Clear up result error flag */ to->error = 0; /* 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); /* 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); /* 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; /* Left is bigger then right */ if (bh_bigint_equal_mod(left, right) < 0) return 0; /* Make a guess */ tmp = left->data[left->size - 1]; 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--; } 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 */ bh_bigint_sub(remainder, remainder, tmp); 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; } 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) { /* If remainder required - normilize it */ if (tmp == remainder) bh_bigint_rsh(remainder, remainder, shift); /* 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; /* 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; /* 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; } } /* 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 */ 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; } /* Update fields */ to->type = 1; to->size = size; 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; } /* Update fields */ to->type = 1; to->size = size; 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; } /* Update fields */ to->type = 1; to->size = size; 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 */ if (to->type < 0) to->type = 1; else if (to->type > 0) to->type = -1; 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; }