2016-01-21 18:02:25 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2016 Andrew Kelley
|
|
|
|
*
|
|
|
|
* This file is part of zig, which is MIT licensed.
|
|
|
|
* See http://opensource.org/licenses/MIT
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "bignum.hpp"
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
static void bignum_normalize(BigNum *bn) {
|
|
|
|
assert(bn->kind == BigNumKindInt);
|
|
|
|
if (bn->data.x_uint == 0) {
|
|
|
|
bn->is_negative = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bignum_init_float(BigNum *dest, double x) {
|
|
|
|
dest->kind = BigNumKindFloat;
|
|
|
|
dest->is_negative = false;
|
|
|
|
dest->data.x_float = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bignum_init_unsigned(BigNum *dest, uint64_t x) {
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
dest->is_negative = false;
|
|
|
|
dest->data.x_uint = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
void bignum_init_signed(BigNum *dest, int64_t x) {
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
if (x < 0) {
|
|
|
|
dest->is_negative = true;
|
|
|
|
dest->data.x_uint = ((uint64_t)(-(x + 1))) + 1;
|
|
|
|
} else {
|
|
|
|
dest->is_negative = false;
|
|
|
|
dest->data.x_uint = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_fits_in_bits(BigNum *bn, int bit_count, bool is_signed) {
|
|
|
|
assert(bn->kind == BigNumKindInt);
|
|
|
|
|
|
|
|
if (is_signed) {
|
|
|
|
if (bn->data.x_uint <= ((uint64_t)(INT8_MAX)) + 1) {
|
|
|
|
return bit_count >= 8;
|
|
|
|
} else if (bn->data.x_uint <= ((uint64_t)(INT16_MAX)) + 1) {
|
|
|
|
return bit_count >= 16;
|
|
|
|
} else if (bn->data.x_uint <= ((uint64_t)(INT32_MAX)) + 1) {
|
|
|
|
return bit_count >= 32;
|
|
|
|
} else {
|
|
|
|
return bit_count >= 64;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (bn->is_negative) {
|
|
|
|
return bn->data.x_uint == 0;
|
|
|
|
} else {
|
|
|
|
if (bn->data.x_uint <= UINT8_MAX) {
|
|
|
|
return bit_count >= 8;
|
|
|
|
} else if (bn->data.x_uint <= UINT16_MAX) {
|
|
|
|
return bit_count >= 16;
|
|
|
|
} else if (bn->data.x_uint <= UINT32_MAX) {
|
|
|
|
return bit_count >= 32;
|
|
|
|
} else {
|
|
|
|
return bit_count >= 64;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t bignum_to_twos_complement(BigNum *bn) {
|
|
|
|
assert(bn->kind == BigNumKindInt);
|
|
|
|
|
|
|
|
if (bn->is_negative) {
|
|
|
|
int64_t x = bn->data.x_uint;
|
|
|
|
return -x;
|
|
|
|
} else {
|
|
|
|
return bn->data.x_uint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// returns true if overflow happened
|
|
|
|
bool bignum_add(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == op2->kind);
|
|
|
|
dest->kind = op1->kind;
|
|
|
|
|
|
|
|
if (dest->kind == BigNumKindFloat) {
|
|
|
|
dest->data.x_float = op1->data.x_float + op2->data.x_float;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op1->is_negative == op2->is_negative) {
|
|
|
|
return __builtin_uaddll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint);
|
|
|
|
} else if (!op1->is_negative && op2->is_negative) {
|
|
|
|
if (__builtin_usubll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
|
|
|
|
dest->data.x_uint = (UINT64_MAX - dest->data.x_uint) + 1;
|
|
|
|
dest->is_negative = true;
|
|
|
|
bignum_normalize(dest);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
bignum_normalize(dest);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return bignum_add(dest, op2, op1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bignum_negate(BigNum *dest, BigNum *op) {
|
|
|
|
dest->kind = op->kind;
|
|
|
|
|
|
|
|
if (dest->kind == BigNumKindFloat) {
|
2016-02-01 12:50:41 +08:00
|
|
|
dest->data.x_float = -op->data.x_float;
|
2016-01-21 18:02:25 +08:00
|
|
|
} else {
|
|
|
|
dest->data.x_uint = op->data.x_uint;
|
|
|
|
dest->is_negative = !op->is_negative;
|
|
|
|
bignum_normalize(dest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-01 12:05:17 +08:00
|
|
|
void bignum_cast_to_float(BigNum *dest, BigNum *op) {
|
|
|
|
assert(op->kind == BigNumKindInt);
|
|
|
|
dest->kind = BigNumKindFloat;
|
|
|
|
|
|
|
|
dest->data.x_float = op->data.x_uint;
|
|
|
|
|
|
|
|
if (op->is_negative) {
|
|
|
|
dest->data.x_float = -dest->data.x_float;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void bignum_cast_to_int(BigNum *dest, BigNum *op) {
|
|
|
|
assert(op->kind == BigNumKindFloat);
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
|
|
|
|
if (op->data.x_float >= 0) {
|
|
|
|
dest->data.x_uint = op->data.x_float;
|
|
|
|
dest->is_negative = false;
|
|
|
|
} else {
|
|
|
|
dest->data.x_uint = -op->data.x_float;
|
|
|
|
dest->is_negative = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 18:02:25 +08:00
|
|
|
bool bignum_sub(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
BigNum op2_negated;
|
|
|
|
bignum_negate(&op2_negated, op2);
|
|
|
|
return bignum_add(dest, op1, &op2_negated);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_mul(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == op2->kind);
|
|
|
|
dest->kind = op1->kind;
|
|
|
|
|
|
|
|
if (dest->kind == BigNumKindFloat) {
|
|
|
|
dest->data.x_float = op1->data.x_float * op2->data.x_float;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (__builtin_umulll_overflow(op1->data.x_uint, op2->data.x_uint, &dest->data.x_uint)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest->is_negative = op1->is_negative != op2->is_negative;
|
|
|
|
bignum_normalize(dest);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_div(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == op2->kind);
|
|
|
|
dest->kind = op1->kind;
|
|
|
|
|
|
|
|
if (dest->kind == BigNumKindFloat) {
|
|
|
|
dest->data.x_float = op1->data.x_float / op2->data.x_float;
|
|
|
|
} else {
|
|
|
|
dest->data.x_uint = op1->data.x_uint / op2->data.x_uint;
|
|
|
|
dest->is_negative = op1->is_negative != op2->is_negative;
|
|
|
|
bignum_normalize(dest);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_mod(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == op2->kind);
|
|
|
|
dest->kind = op1->kind;
|
|
|
|
|
|
|
|
if (dest->kind == BigNumKindFloat) {
|
|
|
|
dest->data.x_float = fmod(op1->data.x_float, op2->data.x_float);
|
|
|
|
} else {
|
|
|
|
if (op1->is_negative || op2->is_negative) {
|
|
|
|
zig_panic("TODO handle mod with negative numbers");
|
|
|
|
}
|
|
|
|
dest->data.x_uint = op1->data.x_uint % op2->data.x_uint;
|
|
|
|
bignum_normalize(dest);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_or(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == BigNumKindInt);
|
|
|
|
assert(op2->kind == BigNumKindInt);
|
|
|
|
|
|
|
|
assert(!op1->is_negative);
|
|
|
|
assert(!op2->is_negative);
|
|
|
|
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
dest->data.x_uint = op1->data.x_uint | op2->data.x_uint;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_and(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == BigNumKindInt);
|
|
|
|
assert(op2->kind == BigNumKindInt);
|
|
|
|
|
|
|
|
assert(!op1->is_negative);
|
|
|
|
assert(!op2->is_negative);
|
|
|
|
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
dest->data.x_uint = op1->data.x_uint & op2->data.x_uint;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_xor(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == BigNumKindInt);
|
|
|
|
assert(op2->kind == BigNumKindInt);
|
|
|
|
|
|
|
|
assert(!op1->is_negative);
|
|
|
|
assert(!op2->is_negative);
|
|
|
|
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
dest->data.x_uint = op1->data.x_uint ^ op2->data.x_uint;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_shl(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == BigNumKindInt);
|
|
|
|
assert(op2->kind == BigNumKindInt);
|
|
|
|
|
|
|
|
assert(!op1->is_negative);
|
|
|
|
assert(!op2->is_negative);
|
|
|
|
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
dest->data.x_uint = op1->data.x_uint << op2->data.x_uint;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_shr(BigNum *dest, BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == BigNumKindInt);
|
|
|
|
assert(op2->kind == BigNumKindInt);
|
|
|
|
|
|
|
|
assert(!op1->is_negative);
|
|
|
|
assert(!op2->is_negative);
|
|
|
|
|
|
|
|
dest->kind = BigNumKindInt;
|
|
|
|
dest->data.x_uint = op1->data.x_uint >> op2->data.x_uint;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Buf *bignum_to_buf(BigNum *bn) {
|
|
|
|
if (bn->kind == BigNumKindFloat) {
|
|
|
|
return buf_sprintf("%f", bn->data.x_float);
|
|
|
|
} else {
|
|
|
|
const char *neg = bn->is_negative ? "-" : "";
|
|
|
|
return buf_sprintf("%s%llu", neg, bn->data.x_uint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_cmp_eq(BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == op2->kind);
|
|
|
|
if (op1->kind == BigNumKindFloat) {
|
|
|
|
return op1->data.x_float == op2->data.x_float;
|
|
|
|
} else {
|
|
|
|
return op1->data.x_uint == op2->data.x_uint &&
|
|
|
|
(op1->is_negative == op2->is_negative || op1->data.x_uint == 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_cmp_neq(BigNum *op1, BigNum *op2) {
|
|
|
|
return !bignum_cmp_eq(op1, op2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_cmp_lt(BigNum *op1, BigNum *op2) {
|
|
|
|
return !bignum_cmp_gte(op1, op2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_cmp_gt(BigNum *op1, BigNum *op2) {
|
|
|
|
return !bignum_cmp_lte(op1, op2);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_cmp_lte(BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == op2->kind);
|
|
|
|
if (op1->kind == BigNumKindFloat) {
|
|
|
|
return (op1->data.x_float <= op2->data.x_float);
|
|
|
|
}
|
|
|
|
|
|
|
|
// assume normalized is_negative
|
|
|
|
if (!op1->is_negative && !op2->is_negative) {
|
|
|
|
return op1->data.x_uint <= op2->data.x_uint;
|
|
|
|
} else if (op1->is_negative && op2->is_negative) {
|
|
|
|
return op1->data.x_uint >= op2->data.x_uint;
|
|
|
|
} else if (op1->is_negative && !op2->is_negative) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool bignum_cmp_gte(BigNum *op1, BigNum *op2) {
|
|
|
|
assert(op1->kind == op2->kind);
|
|
|
|
|
|
|
|
if (op1->kind == BigNumKindFloat) {
|
|
|
|
return (op1->data.x_float >= op2->data.x_float);
|
|
|
|
}
|
|
|
|
|
|
|
|
// assume normalized is_negative
|
|
|
|
if (!op1->is_negative && !op2->is_negative) {
|
|
|
|
return op1->data.x_uint >= op2->data.x_uint;
|
|
|
|
} else if (op1->is_negative && op2->is_negative) {
|
|
|
|
return op1->data.x_uint <= op2->data.x_uint;
|
|
|
|
} else if (op1->is_negative && !op2->is_negative) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|