#include "lint.h"
#include "base_conversion.h"
#include <sys/types.h>
#include <malloc.h>
#include <memory.h>
#include <stdlib.h>
#include <errno.h>
static unsigned int
__quorem10000(unsigned int x, unsigned short *pr)
{
*pr = x % 10000;
return (x / 10000);
}
static void
__multiply_base_two(_big_float *pbf, unsigned short multiplier)
{
unsigned int p, carry;
int j, length = pbf->blength;
carry = 0;
for (j = 0; j < length; j++) {
p = (unsigned int)pbf->bsignificand[j] * multiplier + carry;
pbf->bsignificand[j] = p & 0xffff;
carry = p >> 16;
}
if (carry != 0)
pbf->bsignificand[j++] = carry;
pbf->blength = j;
}
static void
__multiply_base_ten(_big_float *pbf, unsigned short multiplier)
{
unsigned int p, carry;
int j, length = pbf->blength;
carry = 0;
for (j = 0; j < length; j++) {
p = (unsigned int)pbf->bsignificand[j] * multiplier + carry;
carry = __quorem10000(p, &pbf->bsignificand[j]);
}
while (carry != 0) {
carry = __quorem10000(carry, &pbf->bsignificand[j]);
j++;
}
pbf->blength = j;
}
static void
__multiply_base_ten_by_two(_big_float *pbf, unsigned short multiplier)
{
unsigned int p, carry;
int j, length = pbf->blength;
carry = 0;
for (j = 0; j < length; j++) {
p = ((unsigned int)pbf->bsignificand[j] << multiplier) + carry;
carry = __quorem10000(p, &pbf->bsignificand[j]);
}
while (carry != 0) {
carry = __quorem10000(carry, &pbf->bsignificand[j]);
j++;
}
pbf->blength = j;
}
static void
__carry_propagate_two(unsigned int carry, unsigned short *psignificand)
{
unsigned int p;
int j;
j = 0;
while (carry != 0) {
p = psignificand[j] + carry;
psignificand[j++] = p & 0xffff;
carry = p >> 16;
}
}
static void
__carry_propagate_ten(unsigned int carry, unsigned short *psignificand)
{
unsigned int p;
int j;
j = 0;
while (carry != 0) {
p = psignificand[j] + carry;
carry = __quorem10000(p, &psignificand[j]);
j++;
}
}
static void
__multiply_base_two_vector(unsigned short n, unsigned short *px,
unsigned short *py, unsigned short *product)
{
unsigned int acc, p;
unsigned short carry;
int i;
acc = 0;
carry = 0;
for (i = 0; i < (int)n; i++) {
p = px[i] * py[n - 1 - i] + acc;
if (p < acc)
carry++;
acc = p;
}
product[0] = acc & 0xffff;
product[1] = acc >> 16;
product[2] = carry;
}
#define ABASE 3000000000u
static void
__multiply_base_ten_vector(unsigned short n, unsigned short *px,
unsigned short *py, unsigned short *product)
{
unsigned int acc;
unsigned short carry;
int i;
acc = 0;
carry = 0;
for (i = 0; i < (int)n; i++) {
acc = px[i] * py[n - 1 - i] + acc;
if (acc >= ABASE) {
carry++;
acc -= ABASE;
}
}
product[0] = acc % 10000;
acc = acc / 10000;
product[1] = acc % 10000;
acc = acc / 10000;
product[2] = acc + (ABASE / 100000000) * carry;
}
void
__big_float_times_power(_big_float *pbf, int mult, int n, int precision,
_big_float **pnewbf)
{
int base, needed_precision, productsize;
int i, j, itlast, trailing_zeros_to_delete;
int tablepower[3], length[3];
int lengthx, lengthp, istart, istop;
int excess_check;
unsigned short *pp, *table[3], canquit;
unsigned short multiplier, product[3];
if (pbf->blength == 0) {
*pnewbf = pbf;
return;
}
if (mult == 2) {
if (n <= 16 && pbf->blength + 2 < pbf->bsize) {
__multiply_base_ten_by_two(pbf, n);
*pnewbf = pbf;
return;
}
base = 10;
needed_precision = 2 + (precision >> 2);
if (n < __TBL_2_SMALL_SIZE) {
itlast = 0;
tablepower[0] = n;
tablepower[1] = tablepower[2] = 0;
} else if (n < (__TBL_2_SMALL_SIZE * __TBL_2_BIG_SIZE)) {
itlast = 1;
tablepower[0] = n % __TBL_2_SMALL_SIZE;
tablepower[1] = n / __TBL_2_SMALL_SIZE;
tablepower[2] = 0;
} else if (n < (__TBL_2_SMALL_SIZE * __TBL_2_BIG_SIZE *
__TBL_2_HUGE_SIZE)) {
itlast = 2;
tablepower[0] = n % __TBL_2_SMALL_SIZE;
n /= __TBL_2_SMALL_SIZE;
tablepower[1] = n % __TBL_2_BIG_SIZE;
tablepower[2] = n / __TBL_2_BIG_SIZE;
} else {
errno = ERANGE;
abort();
}
pp = (unsigned short *)__tbl_2_small_start + tablepower[0];
table[0] = (unsigned short *)__tbl_2_small_digits + pp[0];
length[0] = pp[1] - pp[0];
pp = (unsigned short *)__tbl_2_big_start + tablepower[1];
table[1] = (unsigned short *)__tbl_2_big_digits + pp[0];
length[1] = pp[1] - pp[0];
pp = (unsigned short *)__tbl_2_huge_start + tablepower[2];
table[2] = (unsigned short *)__tbl_2_huge_digits + pp[0];
length[2] = pp[1] - pp[0];
} else {
if (n <= 4 && pbf->blength + 1 < pbf->bsize) {
pbf->bexponent += (short)n;
__multiply_base_two(pbf, __tbl_10_small_digits[n]);
*pnewbf = pbf;
return;
}
base = 2;
pbf->bexponent += (short)n;
needed_precision = 2 + (precision >> 4);
if (n < __TBL_10_SMALL_SIZE) {
itlast = 0;
tablepower[0] = n;
tablepower[1] = tablepower[2] = 0;
} else if (n < (__TBL_10_SMALL_SIZE * __TBL_10_BIG_SIZE)) {
itlast = 1;
tablepower[0] = n % __TBL_10_SMALL_SIZE;
tablepower[1] = n / __TBL_10_SMALL_SIZE;
tablepower[2] = 0;
} else if (n < (__TBL_10_SMALL_SIZE * __TBL_10_BIG_SIZE *
__TBL_10_HUGE_SIZE)) {
itlast = 2;
tablepower[0] = n % __TBL_10_SMALL_SIZE;
n /= __TBL_10_SMALL_SIZE;
tablepower[1] = n % __TBL_10_BIG_SIZE;
tablepower[2] = n / __TBL_10_BIG_SIZE;
} else {
errno = ERANGE;
abort();
}
pp = (unsigned short *)__tbl_10_small_start + tablepower[0];
table[0] = (unsigned short *)__tbl_10_small_digits + pp[0];
length[0] = pp[1] - pp[0];
pp = (unsigned short *)__tbl_10_big_start + tablepower[1];
table[1] = (unsigned short *)__tbl_10_big_digits + pp[0];
length[1] = pp[1] - pp[0];
pp = (unsigned short *)__tbl_10_huge_start + tablepower[2];
table[2] = (unsigned short *)__tbl_10_huge_digits + pp[0];
length[2] = pp[1] - pp[0];
}
productsize = pbf->blength;
for (i = 0; i <= itlast; i++)
productsize += length[i];
if (productsize < needed_precision)
needed_precision = productsize;
if (productsize <= pbf->bsize) {
*pnewbf = pbf;
} else {
i = sizeof (_big_float) + sizeof (unsigned short) *
(productsize - _BIG_FLOAT_SIZE);
if ((*pnewbf = malloc(i)) == NULL) {
errno = ENOMEM;
abort();
}
(void) memcpy((*pnewbf)->bsignificand, pbf->bsignificand,
pbf->blength * sizeof (unsigned short));
(*pnewbf)->blength = pbf->blength;
(*pnewbf)->bexponent = pbf->bexponent;
pbf = *pnewbf;
pbf->bsize = productsize;
}
for (i = 0; i <= itlast; i++) {
if (tablepower[i] == 0)
continue;
lengthp = length[i];
if (lengthp == 1) {
if (base == 10) {
__multiply_base_ten_by_two(pbf, tablepower[i]);
} else {
__multiply_base_two(pbf, (table[i])[0]);
}
continue;
}
lengthx = pbf->blength;
if (lengthx == 1) {
multiplier = pbf->bsignificand[0];
(void) memcpy(pbf->bsignificand, table[i],
lengthp * sizeof (unsigned short));
pbf->blength = lengthp;
if (base == 10)
__multiply_base_ten(pbf, multiplier);
else
__multiply_base_two(pbf, multiplier);
continue;
}
trailing_zeros_to_delete = 0;
pbf->bsignificand[lengthx + lengthp - 1] = 0;
for (j = lengthx + lengthp - 2; j >= 0; j--) {
istart = j - lengthp + 1;
if (istart < 0)
istart = 0;
istop = lengthx - 1;
if (istop > j)
istop = j;
pp = table[i];
if (base == 2) {
__multiply_base_two_vector(istop - istart + 1,
&(pbf->bsignificand[istart]),
&(pp[j - istop]), product);
if (product[2] != 0)
__carry_propagate_two(
(unsigned int)product[2],
&(pbf->bsignificand[j + 2]));
if (product[1] != 0)
__carry_propagate_two(
(unsigned int)product[1],
&(pbf->bsignificand[j + 1]));
} else {
__multiply_base_ten_vector(istop - istart + 1,
&(pbf->bsignificand[istart]),
&(pp[j - istop]), product);
if (product[2] != 0)
__carry_propagate_ten(
(unsigned int)product[2],
&(pbf->bsignificand[j + 2]));
if (product[1] != 0)
__carry_propagate_ten(
(unsigned int)product[1],
&(pbf->bsignificand[j + 1]));
}
pbf->bsignificand[j] = product[0];
if (i < itlast || j > lengthx + lengthp - 4
- needed_precision)
continue;
excess_check = lengthx + lengthp;
if (pbf->bsignificand[excess_check - 1] == 0)
excess_check--;
excess_check -= needed_precision + 4;
canquit = ((base == 2)? 65535 : 9999) -
((lengthx < lengthp)? lengthx : lengthp);
if (j <= excess_check &&
pbf->bsignificand[j + 1] <= canquit &&
(pbf->bsignificand[j + 1] | pbf->bsignificand[j])
!= 0) {
trailing_zeros_to_delete = j + 2;
pbf->bsignificand[j + 2] |= 1;
break;
}
}
pbf->blength = lengthx + lengthp;
if (pbf->bsignificand[pbf->blength - 1] == 0)
pbf->blength--;
for (; pbf->bsignificand[trailing_zeros_to_delete] == 0;
trailing_zeros_to_delete++)
continue;
if (trailing_zeros_to_delete > 0) {
for (j = 0; j < (int)pbf->blength -
trailing_zeros_to_delete; j++) {
pbf->bsignificand[j] = pbf->bsignificand[j
+ trailing_zeros_to_delete];
}
pbf->blength -= trailing_zeros_to_delete;
pbf->bexponent += trailing_zeros_to_delete <<
((base == 2)? 4 : 2);
}
}
}