zig/src/bignum.cpp

329 lines
8.9 KiB
C++
Raw Normal View History

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) {
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);
}
}
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;
}
}