#include "ficl.h"
#if FICL_PLATFORM_HAS_2INTEGER
ficl2UnsignedQR
ficl2UnsignedDivide(ficl2Unsigned q, ficlUnsigned y)
{
ficl2UnsignedQR result;
result.quotient = q / y;
result.remainder = (ficlInteger)(q - (result.quotient * y));
return (result);
}
#else
#define FICL_CELL_HIGH_BIT ((uintmax_t)1 << (FICL_BITS_PER_CELL-1))
#define UMOD_SHIFT (FICL_BITS_PER_CELL / 2)
#define UMOD_MASK ((1L << (FICL_BITS_PER_CELL / 2)) - 1)
int
ficl2IntegerIsNegative(ficl2Integer x)
{
return (x.high < 0);
}
ficl2Integer
ficl2IntegerNegate(ficl2Integer x)
{
x.high = ~x.high;
x.low = ~x.low;
x.low ++;
if (x.low == 0)
x.high++;
return (x);
}
ficl2Unsigned
ficl2UnsignedMultiplyAccumulate(ficl2Unsigned u, ficlUnsigned mul,
ficlUnsigned add)
{
ficl2Unsigned resultLo = ficl2UnsignedMultiply(u.low, mul);
ficl2Unsigned resultHi = ficl2UnsignedMultiply(u.high, mul);
resultLo.high += resultHi.low;
resultHi.low = resultLo.low + add;
if (resultHi.low < resultLo.low)
resultLo.high++;
resultLo.low = resultHi.low;
return (resultLo);
}
ficl2Integer
ficl2IntegerMultiply(ficlInteger x, ficlInteger y)
{
ficl2Unsigned prod;
ficl2Integer result;
int sign = 1;
if (x < 0) {
sign = -sign;
x = -x;
}
if (y < 0) {
sign = -sign;
y = -y;
}
prod = ficl2UnsignedMultiply(x, y);
FICL_2INTEGER_SET(FICL_2UNSIGNED_GET_HIGH(prod),
FICL_2UNSIGNED_GET_LOW(prod), result);
if (sign > 0)
return (result);
else
return (ficl2IntegerNegate(result));
}
ficl2Integer
ficl2IntegerDecrement(ficl2Integer x)
{
if (x.low == INTMAX_MIN)
x.high--;
x.low--;
return (x);
}
ficl2Unsigned
ficl2UnsignedAdd(ficl2Unsigned x, ficl2Unsigned y)
{
ficl2Unsigned result;
result.high = x.high + y.high;
result.low = x.low + y.low;
if (result.low < y.low)
result.high++;
return (result);
}
ficl2Unsigned
ficl2UnsignedMultiply(ficlUnsigned x, ficlUnsigned y)
{
ficl2Unsigned result = { 0, 0 };
ficl2Unsigned addend;
addend.low = y;
addend.high = 0;
while (x != 0) {
if (x & 1) {
result = ficl2UnsignedAdd(result, addend);
}
x >>= 1;
addend = ficl2UnsignedArithmeticShiftLeft(addend);
}
return (result);
}
ficl2Unsigned
ficl2UnsignedSubtract(ficl2Unsigned x, ficl2Unsigned y)
{
ficl2Unsigned result;
result.high = x.high - y.high;
result.low = x.low - y.low;
if (x.low < y.low) {
result.high--;
}
return (result);
}
ficl2Unsigned
ficl2UnsignedArithmeticShiftLeft(ficl2Unsigned x)
{
ficl2Unsigned result;
result.high = x.high << 1;
if (x.low & FICL_CELL_HIGH_BIT) {
result.high++;
}
result.low = x.low << 1;
return (result);
}
ficl2Unsigned
ficl2UnsignedArithmeticShiftRight(ficl2Unsigned x)
{
ficl2Unsigned result;
result.low = x.low >> 1;
if (x.high & 1) {
result.low |= FICL_CELL_HIGH_BIT;
}
result.high = x.high >> 1;
return (result);
}
ficl2Unsigned
ficl2UnsignedOr(ficl2Unsigned x, ficl2Unsigned y)
{
ficl2Unsigned result;
result.high = x.high | y.high;
result.low = x.low | y.low;
return (result);
}
int
ficl2UnsignedCompare(ficl2Unsigned x, ficl2Unsigned y)
{
if (x.high > y.high)
return (1);
if (x.high < y.high)
return (-1);
if (x.low > y.low)
return (1);
else if (x.low < y.low)
return (-1);
return (0);
}
ficl2UnsignedQR
ficl2UnsignedDivide(ficl2Unsigned q, ficlUnsigned y)
{
ficl2UnsignedQR result;
ficl2Unsigned quotient;
ficl2Unsigned subtrahend;
ficl2Unsigned mask;
quotient.low = 0;
quotient.high = 0;
subtrahend.low = y;
subtrahend.high = 0;
mask.low = 1;
mask.high = 0;
while ((ficl2UnsignedCompare(subtrahend, q) < 0) &&
(subtrahend.high & FICL_CELL_HIGH_BIT) == 0) {
mask = ficl2UnsignedArithmeticShiftLeft(mask);
subtrahend = ficl2UnsignedArithmeticShiftLeft(subtrahend);
}
while (mask.low != 0 || mask.high != 0) {
if (ficl2UnsignedCompare(subtrahend, q) <= 0) {
q = ficl2UnsignedSubtract(q, subtrahend);
quotient = ficl2UnsignedOr(quotient, mask);
}
mask = ficl2UnsignedArithmeticShiftRight(mask);
subtrahend = ficl2UnsignedArithmeticShiftRight(subtrahend);
}
result.quotient = quotient;
result.remainder = q.low;
return (result);
}
#endif
ficl2IntegerQR
ficl2IntegerDivideFloored(ficl2Integer num, ficlInteger den)
{
ficl2IntegerQR qr;
ficl2UnsignedQR uqr;
ficl2Unsigned u;
int signRem = 1;
int signQuot = 1;
if (ficl2IntegerIsNegative(num)) {
num = ficl2IntegerNegate(num);
signQuot = -signQuot;
}
if (den < 0) {
den = -den;
signRem = -signRem;
signQuot = -signQuot;
}
FICL_2UNSIGNED_SET(FICL_2UNSIGNED_GET_HIGH(num),
FICL_2UNSIGNED_GET_LOW(num), u);
uqr = ficl2UnsignedDivide(u, (ficlUnsigned)den);
qr = FICL_2UNSIGNEDQR_TO_2INTEGERQR(uqr);
if (signQuot < 0) {
qr.quotient = ficl2IntegerNegate(qr.quotient);
if (qr.remainder != 0) {
qr.quotient = ficl2IntegerDecrement(qr.quotient);
qr.remainder = den - qr.remainder;
}
}
if (signRem < 0)
qr.remainder = -qr.remainder;
return (qr);
}
ficl2IntegerQR
ficl2IntegerDivideSymmetric(ficl2Integer num, ficlInteger den)
{
ficl2IntegerQR qr;
ficl2UnsignedQR uqr;
ficl2Unsigned u;
int signRem = 1;
int signQuot = 1;
if (ficl2IntegerIsNegative(num)) {
num = ficl2IntegerNegate(num);
signRem = -signRem;
signQuot = -signQuot;
}
if (den < 0) {
den = -den;
signQuot = -signQuot;
}
FICL_2UNSIGNED_SET(FICL_2UNSIGNED_GET_HIGH(num),
FICL_2UNSIGNED_GET_LOW(num), u);
uqr = ficl2UnsignedDivide(u, (ficlUnsigned)den);
qr = FICL_2UNSIGNEDQR_TO_2INTEGERQR(uqr);
if (signRem < 0)
qr.remainder = -qr.remainder;
if (signQuot < 0)
qr.quotient = ficl2IntegerNegate(qr.quotient);
return (qr);
}