#include <NTL/RR.h>
#include <NTL/new.h>
NTL_START_IMPL
// FIXME: I just converted all the static RR's to thread local static RR's.
// Perhaps I should at some point make the equivalent of an RR Register.
// But be careful: certain computations, like ComputePi, actually cache
// results, so that will take more work. In any case, RR is not a high
// priority right now.
NTL_CHEAP_THREAD_LOCAL
long RR::prec = 150;
void RR::SetPrecision(long p)
{
if (p < 53)
p = 53;
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("RR: precision too high");
prec = p;
}
NTL_CHEAP_THREAD_LOCAL
long RR::oprec = 10;
void RR::SetOutputPrecision(long p)
{
if (p < 1)
p = 1;
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("RR: output precision too high");
oprec = p;
}
static
void normalize1(RR& z, const ZZ& y_x, long y_e, long prec, long residual)
{
long len = NumBits(y_x);
if (len > prec) {
long correction = ZZ_RoundCorrection(y_x, len - prec, residual);
RightShift(z.x, y_x, len - prec);
if (correction)
add(z.x, z.x, correction);
z.e = y_e + len - prec;
}
else if (len == 0) {
clear(z.x);
z.e = 0;
}
else {
z.x = y_x;
z.e = y_e;
}
if (!IsOdd(z.x))
z.e += MakeOdd(z.x);
if (z.e >= NTL_OVFBND)
ResourceError("RR: overflow");
if (z.e <= -NTL_OVFBND)
ResourceError("RR: underflow");
}
void normalize(RR& z, const RR& y, long residual = 0)
{
normalize1(z, y.x, y.e, RR::prec, residual);
}
void MakeRR(RR& z, const ZZ& a, long e)
{
if (e >= NTL_OVFBND)
ResourceError("MakeRR: e too big");
if (e <= -NTL_OVFBND)
ResourceError("MakeRR: e too small");
normalize1(z, a, e, RR::prec, 0);
}
void MakeRRPrec(RR& x, const ZZ& a, long e, long p)
{
if (p < 1)
LogicError("MakeRRPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("MakeRRPrec: precsion too big");
RRPush push;
RR::prec = p;
MakeRR(x, a, e);
}
void random(RR& z)
{
NTL_TLS_LOCAL(RR, t);
RandomBits(t.x, RR::prec);
t.e = -RR::prec;
normalize(z, t);
}
static inline
void xcopy(RR& x, const RR& a)
{ normalize(x, a); }
// xcopy emulates old assignment semantics...
// many routines here implicitly assume assignment normalizes,
// but that is no longer the case as of v3.0.
void ConvPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: precsion too big");
RRPush push;
RR::prec = p;
normalize(x, a);
}
void RoundToPrecision(RR& x, const RR& a, long p)
{
ConvPrec(x, a, p);
}
void conv(RR& x, const RR& a)
{
normalize(x, a);
}
long IsZero(const RR& a)
{
return IsZero(a.x);
}
long IsOne(const RR& a)
{
return a.e == 0 && IsOne(a.x);
}
long sign(const RR& a)
{
return sign(a.x);
}
void clear(RR& z)
{
z.e = 0;
clear(z.x);
}
void set(RR& z)
{
z.e = 0;
set(z.x);
}
void add(RR& z, const RR& a, const RR& b)
{
NTL_TLS_LOCAL(RR, t);
if (IsZero(a.x)) {
xcopy(z, b);
return;
}
if (IsZero(b.x)) {
xcopy(z, a);
return;
}
if (a.e > b.e) {
if (a.e-b.e - max(RR::prec-NumBits(a.x),0) >= NumBits(b.x) + 2)
normalize(z, a, sign(b));
else {
LeftShift(t.x, a.x, a.e-b.e);
add(t.x, t.x, b.x);
t.e = b.e;
normalize(z, t);
}
}
else if (a.e < b.e) {
if (b.e-a.e - max(RR::prec-NumBits(b.x),0) >= NumBits(a.x) + 2)
normalize(z, b, sign(a));
else {
LeftShift(t.x, b.x, b.e-a.e);
add(t.x, t.x, a.x);
t.e = a.e;
normalize(z, t);
}
}
else {
add(t.x, a.x, b.x);
t.e = a.e;
normalize(z, t);
}
}
void AddPrec(RR& x, const RR& a, const RR& b, long p)
{
if (p < 1)
LogicError("AddPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("AddPrec: bad precsion");
RRPush push;
RR::prec = p;
add(x, a, b);
}
void sub(RR& z, const RR& a, const RR& b)
{
NTL_TLS_LOCAL(RR, t);
if (IsZero(a.x)) {
negate(z, b);
return;
}
if (IsZero(b.x)) {
xcopy(z, a);
return;
}
if (a.e > b.e) {
if (a.e-b.e - max(RR::prec-NumBits(a.x),0) >= NumBits(b.x) + 2)
normalize(z, a, -sign(b));
else {
LeftShift(t.x, a.x, a.e-b.e);
sub(t.x, t.x, b.x);
t.e = b.e;
xcopy(z, t);
}
}
else if (a.e < b.e) {
if (b.e-a.e - max(RR::prec-NumBits(b.x),0) >= NumBits(a.x) + 2) {
normalize(z, b, -sign(a));
negate(z.x, z.x);
}
else {
LeftShift(t.x, b.x, b.e-a.e);
sub(t.x, a.x, t.x);
t.e = a.e;
xcopy(z, t);
}
}
else {
sub(t.x, a.x, b.x);
t.e = a.e;
normalize(z, t);
}
}
void SubPrec(RR& x, const RR& a, const RR& b, long p)
{
if (p < 1)
LogicError("SubPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("SubPrec: bad precsion");
RRPush push;
RR::prec = p;
sub(x, a, b);
}
void negate(RR& z, const RR& a)
{
xcopy(z, a);
negate(z.x, z.x);
}
void NegatePrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("NegatePrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("NegatePrec: bad precsion");
RRPush push;
RR::prec = p;
negate(x, a);
}
void abs(RR& z, const RR& a)
{
xcopy(z, a);
abs(z.x, z.x);
}
void AbsPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("AbsPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("AbsPrec: bad precsion");
RRPush push;
RR::prec = p;
abs(x, a);
}
void mul(RR& z, const RR& a, const RR& b)
{
NTL_TLS_LOCAL(RR, t);
mul(t.x, a.x, b.x);
t.e = a.e + b.e;
xcopy(z, t);
}
void MulPrec(RR& x, const RR& a, const RR& b, long p)
{
if (p < 1)
LogicError("MulPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("MulPrec: bad precsion");
RRPush push;
RR::prec = p;
mul(x, a, b);
}
void sqr(RR& z, const RR& a)
{
NTL_TLS_LOCAL(RR, t);
sqr(t.x, a.x);
t.e = a.e + a.e;
xcopy(z, t);
}
void SqrPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("SqrPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("SqrPrec: bad precsion");
RRPush push;
RR::prec = p;
sqr(x, a);
}
void div(RR& z, const RR& a, const RR& b)
{
if (IsZero(b))
ArithmeticError("RR: division by zero");
if (IsZero(a)) {
clear(z);
return;
}
long la = NumBits(a.x);
long lb = NumBits(b.x);
long neg = (sign(a) != sign(b));
long k = RR::prec - la + lb + 1;
if (k < 0) k = 0;
NTL_TLS_LOCAL(RR, t);
NTL_ZZRegister(A);
NTL_ZZRegister(B);
NTL_ZZRegister(R);
abs(A, a.x);
LeftShift(A, A, k);
abs(B, b.x);
DivRem(t.x, R, A, B);
t.e = a.e - b.e - k;
normalize(z, t, !IsZero(R));
if (neg)
negate(z.x, z.x);
}
void DivPrec(RR& x, const RR& a, const RR& b, long p)
{
if (p < 1)
LogicError("DivPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("DivPrec: bad precsion");
RRPush push;
RR::prec = p;
div(x, a, b);
}
void SqrRoot(RR& z, const RR& a)
{
if (sign(a) < 0)
ArithmeticError("RR: attempt to take square root of negative number");
if (IsZero(a)) {
clear(z);
return;
}
RR t;
ZZ T1, T2;
long k;
k = 2*RR::prec - NumBits(a.x) + 1;
if (k < 0) k = 0;
if ((a.e - k) & 1) k++;
LeftShift(T1, a.x, k);
// since k >= 2*prec - bits(a) + 1, T1 has at least 2*prec+1 bits,
// thus T1 >= 2^(2*prec)
SqrRoot(t.x, T1); // t.x >= 2^prec thus t.x contains the round bit
t.e = (a.e - k)/2;
sqr(T2, t.x);
// T1-T2 is the (lower part of the) sticky bit
normalize(z, t, T2 < T1);
}
void SqrRootPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("SqrRootPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("SqrRootPrec: bad precsion");
RRPush push;
RR::prec = p;
SqrRoot(x, a);
}
long compare(const RR& a, const RR& b)
{
NTL_TLS_LOCAL(RR, t);
SubPrec(t, a, b, 1);
return sign(t);
}
long operator==(const RR& a, const RR& b)
{
return a.e == b.e && a.x == b.x;
}
void trunc(RR& z, const RR& a)
{
NTL_TLS_LOCAL(RR, t);
if (a.e >= 0)
xcopy(z, a);
else {
RightShift(t.x, a.x, -a.e);
t.e = 0;
xcopy(z, t);
}
}
void TruncPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("TruncPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("TruncPrec: bad precsion");
RRPush push;
RR::prec = p;
trunc(x, a);
}
void floor(RR& z, const RR& a)
{
NTL_TLS_LOCAL(RR, t);
if (a.e >= 0)
xcopy(z, a);
else {
RightShift(t.x, a.x, -a.e);
if (sign(a.x) < 0)
add(t.x, t.x, -1);
t.e = 0;
xcopy(z, t);
}
}
void FloorPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("FloorPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("FloorPrec: bad precsion");
RRPush push;
RR::prec = p;
floor(x, a);
}
void ceil(RR& z, const RR& a)
{
NTL_TLS_LOCAL(RR, t);
if (a.e >= 0)
xcopy(z, a);
else {
RightShift(t.x, a.x, -a.e);
if (sign(a.x) > 0)
add(t.x, t.x, 1);
t.e = 0;
xcopy(z, t);
}
}
void CeilPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("CeilPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("CeilPrec: bad precsion");
RRPush push;
RR::prec = p;
ceil(x, a);
}
void round(RR& z, const RR& a)
{
if (a.e >= 0) {
xcopy(z, a);
return;
}
long len = NumBits(a.x);
if (-a.e > len) {
z = 0;
return;
}
if (-a.e == len) {
if (len == 1)
z = 0;
else
z = sign(a.x);
return;
}
NTL_TLS_LOCAL(RR, t);
ConvPrec(t, a, len+a.e);
xcopy(z, t);
}
void RoundPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("RoundPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("RoundPrec: bad precsion");
RRPush push;
RR::prec = p;
round(x, a);
}
void conv(RR& z, const ZZ& a)
{
normalize1(z, a, 0, RR::prec, 0);
}
void ConvPrec(RR& x, const ZZ& a, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: bad precsion");
RRPush push;
RR::prec = p;
conv(x, a);
}
void conv(RR& z, long a)
{
if (a == 0) {
clear(z);
return;
}
if (a == 1) {
set(z);
return;
}
NTL_ZZRegister(t);
t = a;
conv(z, t);
}
void ConvPrec(RR& x, long a, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: bad precsion");
RRPush push;
RR::prec = p;
conv(x, a);
}
void conv(RR& z, unsigned long a)
{
if (a == 0) {
clear(z);
return;
}
if (a == 1) {
set(z);
return;
}
NTL_ZZRegister(t);
conv(t, a);
conv(z, t);
}
void ConvPrec(RR& x, unsigned long a, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: bad precsion");
RRPush push;
RR::prec = p;
conv(x, a);
}
void conv(RR& z, double a)
{
if (a == 0) {
clear(z);
return;
}
if (a == 1) {
set(z);
return;
}
if (!IsFinite(&a))
ArithmeticError("RR: conversion of a non-finite double");
int e;
double f;
NTL_TLS_LOCAL(RR, t);
f = frexp(a, &e);
f = f * NTL_FDOUBLE_PRECISION;
f = f * 4;
conv(t.x, f);
t.e = e - (NTL_DOUBLE_PRECISION + 1);
xcopy(z, t);
}
void ConvPrec(RR& x, double a, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: bad precsion");
RRPush push;
RR::prec = p;
conv(x, a);
}
void conv(ZZ& z, const RR& a)
{
if (a.e >= 0)
LeftShift(z, a.x, a.e);
else {
long sgn = sign(a.x);
RightShift(z, a.x, -a.e);
if (sgn < 0)
sub(z, z, 1);
}
}
void CeilToZZ(ZZ& z, const RR& a)
{
if (a.e >= 0)
LeftShift(z, a.x, a.e);
else {
long sgn = sign(a.x);
RightShift(z, a.x, -a.e);
if (sgn > 0)
add(z, z, 1);
}
}
void TruncToZZ(ZZ& z, const RR& a)
{
if (a.e >= 0)
LeftShift(z, a.x, a.e);
else
RightShift(z, a.x, -a.e);
}
void RoundToZZ(ZZ& z, const RR& a)
{
if (a.e >= 0) {
LeftShift(z, a.x, a.e);
return;
}
long len = NumBits(a.x);
if (-a.e > len) {
z = 0;
return;
}
if (-a.e == len) {
if (len == 1)
z = 0;
else
z = sign(a.x);
return;
}
NTL_TLS_LOCAL(RR, t);
ConvPrec(t, a, len+a.e);
LeftShift(z, t.x, t.e);
}
void conv(long& z, const RR& a)
{
ZZ t;
if (a.e >= NTL_BITS_PER_LONG)
z = 0;
else {
conv(t, a);
conv(z, t);
}
}
void conv(double& z, const RR& aa)
{
double x;
NTL_TLS_LOCAL(RR, a);
ConvPrec(a, aa, NTL_DOUBLE_PRECISION);
// round to NTL_DOUBLE_PRECISION bits to avoid double overflow
conv(x, a.x);
z = _ntl_ldexp(x, a.e);
}
void add(RR& z, const RR& a, double b)
{
NTL_TLS_LOCAL(RR, B);
B = b;
add(z, a, B);
}
void sub(RR& z, const RR& a, double b)
{
NTL_TLS_LOCAL(RR, B);
B = b;
sub(z, a, B);
}
void sub(RR& z, double a, const RR& b)
{
NTL_TLS_LOCAL(RR, A);
A = a;
sub(z, A, b);
}
void mul(RR& z, const RR& a, double b)
{
NTL_TLS_LOCAL(RR, B);
B = b;
mul(z, a, B);
}
void div(RR& z, const RR& a, double b)
{
NTL_TLS_LOCAL(RR, B);
B = b;
div(z, a, B);
}
void div(RR& z, double a, const RR& b)
{
NTL_TLS_LOCAL(RR, A);
A = a;
div(z, A, b);
}
void inv(RR& z, const RR& a)
{
NTL_TLS_LOCAL_INIT(RR, one, (to_RR(1)));
div(z, one, a);
}
void InvPrec(RR& x, const RR& a, long p)
{
if (p < 1)
LogicError("InvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("InvPrec: bad precsion");
RRPush push;
RR::prec = p;
inv(x, a);
}
long compare(const RR& a, double b)
{
if (b == 0) return sign(a);
NTL_TLS_LOCAL(RR, B);
B = b;
return compare(a, B);
}
long operator==(const RR& a, double b)
{
if (b == 0) return IsZero(a);
if (b == 1) return IsOne(a);
NTL_TLS_LOCAL(RR, B);
B = b;
return a == B;
}
void power(RR& z, const RR& a, long e)
{
RR b, res;
long n = NumBits(e);
RRPush push;
long p = RR::precision();
RR::SetPrecision(p + n + 10);
xcopy(b, a);
set(res);
long i;
for (i = n-1; i >= 0; i--) {
sqr(res, res);
if (bit(e, i))
mul(res, res, b);
}
RR::SetPrecision(p);
if (e < 0)
inv(z, res);
else
xcopy(z, res);
}
istream& operator>>(istream& s, RR& x)
{
RR v;
{
RRPush push;
long c;
long cval;
long sign;
ZZ a, b;
if (!s) NTL_INPUT_ERROR(s, "bad RR input");
c = s.peek();
while (IsWhiteSpace(c)) {
s.get();
c = s.peek();
}
if (c == '-') {
sign = -1;
s.get();
c = s.peek();
}
else
sign = 1;
long got1 = 0;
long got_dot = 0;
long got2 = 0;
a = 0;
b = 1;
cval = CharToIntVal(c);
if (cval >= 0 && cval <= 9) {
got1 = 1;
while (cval >= 0 && cval <= 9) {
mul(a, a, 10);
add(a, a, cval);
s.get();
c = s.peek();
cval = CharToIntVal(c);
}
}
if (c == '.') {
got_dot = 1;
s.get();
c = s.peek();
cval = CharToIntVal(c);
if (cval >= 0 && cval <= 9) {
got2 = 1;
while (cval >= 0 && cval <= 9) {
mul(a, a, 10);
add(a, a, cval);
mul(b, b, 10);
s.get();
c = s.peek();
cval = CharToIntVal(c);
}
}
}
if (got_dot && !got1 && !got2) NTL_INPUT_ERROR(s, "bad RR input");
ZZ e;
long got_e = 0;
long e_sign;
if (c == 'e' || c == 'E') {
got_e = 1;
s.get();
c = s.peek();
if (c == '-') {
e_sign = -1;
s.get();
c = s.peek();
}
else if (c == '+') {
e_sign = 1;
s.get();
c = s.peek();
}
else
e_sign = 1;
cval = CharToIntVal(c);
if (cval < 0 || cval > 9) NTL_INPUT_ERROR(s, "bad RR input");
e = 0;
while (cval >= 0 && cval <= 9) {
mul(e, e, 10);
add(e, e, cval);
s.get();
c = s.peek();
cval = CharToIntVal(c);
}
}
if (!got1 && !got2 && !got_e) NTL_INPUT_ERROR(s, "bad RR input");
RR t1, t2;
long old_p = RR::precision();
if (got1 || got2) {
ConvPrec(t1, a, max(NumBits(a), 1));
ConvPrec(t2, b, NumBits(b));
if (got_e)
RR::SetPrecision(old_p + 10);
div(v, t1, t2);
}
else
set(v);
if (sign < 0)
negate(v, v);
if (got_e) {
if (e >= NTL_OVFBND) ResourceError("RR input overflow");
long E;
conv(E, e);
if (e_sign < 0) E = -E;
RR::SetPrecision(old_p + 10);
power(t1, to_RR(10), E);
mul(v, v, t1);
}
}
xcopy(x, v);
return s;
}
istream& InputPrec(RR& x, istream& s, long p)
{
if (p < 1)
LogicError("InputPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("InputPrec: bad precsion");
RRPush push;
RR::prec = p;
s >> x;
return s;
}
void conv(RR& z, const xdouble& a)
{
conv(z, a.mantissa());
if (a.exponent() > ((2*NTL_OVFBND)/(2*NTL_XD_HBOUND_LOG)))
ResourceError("RR: overlow");
if (a.exponent() < -((2*NTL_OVFBND)/(2*NTL_XD_HBOUND_LOG)))
ResourceError("RR: underflow");
z.e += a.exponent()*(2*NTL_XD_HBOUND_LOG);
if (z.e >= NTL_OVFBND)
ResourceError("RR: overflow");
if (z.e <= -NTL_OVFBND)
ResourceError("RR: underflow");
}
void ConvPrec(RR& x, const xdouble& a, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: bad precsion");
RRPush push;
RR::prec = p;
conv(x, a);
}
void conv(xdouble& z, const RR& a)
{
xdouble x;
xdouble y;
conv(x, a.x);
power2(y, a.e);
z = x*y;
}
void power2(RR& z, long e)
{
if (e >= NTL_OVFBND)
ResourceError("RR: overflow");
if (e <= -NTL_OVFBND)
ResourceError("RR: underflow");
set(z.x);
z.e = e;
}
void conv(RR& z, const quad_float& a)
{
NTL_TLS_LOCAL(RR, hi);
NTL_TLS_LOCAL(RR, lo);
NTL_TLS_LOCAL(RR, res);
ConvPrec(hi, a.hi, NTL_DOUBLE_PRECISION);
ConvPrec(lo, a.lo, NTL_DOUBLE_PRECISION);
add(res, hi, lo);
z = res;
}
void ConvPrec(RR& x, const quad_float& a, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: bad precsion");
RRPush push;
RR::prec = p;
conv(x, a);
}
void conv(quad_float& z, const RR& a)
{
NTL_TLS_LOCAL(RR, a_hi);
NTL_TLS_LOCAL(RR, a_lo);
ConvPrec(a_hi, a, NTL_DOUBLE_PRECISION); // high order bits
SubPrec(a_lo, a, a_hi, NTL_DOUBLE_PRECISION); // low order bits
z = to_quad_float(a_hi.x)*power2_quad_float(a_hi.e) +
to_quad_float(a_lo.x)*power2_quad_float(a_lo.e);
}
void conv(RR& x, const char *s)
{
long c;
long cval;
long sign;
ZZ a, b;
long i = 0;
if (!s) InputError("bad RR input");
RRPush push;
c = s[i];
while (IsWhiteSpace(c)) {
i++;
c = s[i];
}
if (c == '-') {
sign = -1;
i++;
c = s[i];
}
else
sign = 1;
long got1 = 0;
long got_dot = 0;
long got2 = 0;
a = 0;
b = 1;
cval = CharToIntVal(c);
if (cval >= 0 && cval <= 9) {
got1 = 1;
while (cval >= 0 && cval <= 9) {
mul(a, a, 10);
add(a, a, cval);
i++;
c = s[i];
cval = CharToIntVal(c);
}
}
if (c == '.') {
got_dot = 1;
i++;
c = s[i];
cval = CharToIntVal(c);
if (cval >= 0 && cval <= 9) {
got2 = 1;
while (cval >= 0 && cval <= 9) {
mul(a, a, 10);
add(a, a, cval);
mul(b, b, 10);
i++;
c = s[i];
cval = CharToIntVal(c);
}
}
}
if (got_dot && !got1 && !got2) InputError("bad RR input");
ZZ e;
long got_e = 0;
long e_sign;
if (c == 'e' || c == 'E') {
got_e = 1;
i++;
c = s[i];
if (c == '-') {
e_sign = -1;
i++;
c = s[i];
}
else if (c == '+') {
e_sign = 1;
i++;
c = s[i];
}
else
e_sign = 1;
cval = CharToIntVal(c);
if (cval < 0 || cval > 9) InputError("bad RR input");
e = 0;
while (cval >= 0 && cval <= 9) {
mul(e, e, 10);
add(e, e, cval);
i++;
c = s[i];
cval = CharToIntVal(c);
}
}
if (!got1 && !got2 && !got_e) InputError("bad RR input");
RR t1, t2, v;
long old_p = RR::precision();
if (got1 || got2) {
ConvPrec(t1, a, max(NumBits(a), 1));
ConvPrec(t2, b, NumBits(b));
if (got_e)
RR::SetPrecision(old_p + 10);
div(v, t1, t2);
}
else
set(v);
if (sign < 0)
negate(v, v);
if (got_e) {
if (e >= NTL_OVFBND) ResourceError("RR input overflow");
long E;
conv(E, e);
if (e_sign < 0) E = -E;
RR::SetPrecision(old_p + 10);
power(t1, to_RR(10), E);
mul(v, v, t1);
RR::prec = old_p;
}
xcopy(x, v);
}
void ConvPrec(RR& x, const char *s, long p)
{
if (p < 1)
LogicError("ConvPrec: bad precsion");
if (NTL_OVERFLOW(p, 1, 0))
ResourceError("ConvPrec: bad precsion");
RRPush push;
RR::prec = p;
conv(x, s);
}
void ReallyComputeE(RR& res)
{
RRPush push;
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR s, s1, t;
s = 1;
t = 1;
long i;
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
div(t, t, i);
}
RR::SetPrecision(p);
xcopy(res, s);
}
void ComputeE(RR& res)
{
static NTL_CHEAP_THREAD_LOCAL long prec = 0;
NTL_TLS_LOCAL(RR, e);
RRPush push;
long p = RR::precision();
if (prec <= p + 10) {
prec = p + 20;
RR::SetPrecision(prec);
ReallyComputeE(e);
RR::SetPrecision(p);
}
xcopy(res, e);
}
void exp(RR& res, const RR& x)
{
if (x >= NTL_OVFBND || x <= -NTL_OVFBND)
ResourceError("RR: overflow");
RRPush push;
long p = RR::precision();
// step 0: write x = n + f, n an integer and |f| <= 1/2
// careful -- we want to compute f to > p bits of precision
RR f, nn;
RR::SetPrecision(NTL_BITS_PER_LONG);
round(nn, x);
RR::SetPrecision(p + 10);
sub(f, x, nn);
long n = to_long(nn);
// step 1: calculate t1 = e^n by repeated squaring
RR::SetPrecision(p + NumBits(n) + 10);
RR e;
ComputeE(e);
RR::SetPrecision(p + 10);
RR t1;
power(t1, e, n);
// step 2: calculate t2 = e^f using Taylor series expansion
RR::SetPrecision(p + NumBits(p) + 10);
RR t2, s, s1, t;
long i;
s = 0;
t = 1;
for (i = 1; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
div(t, t, i);
}
xcopy(t2, s);
RR::SetPrecision(p);
mul(res, t1, t2);
}
void ReallyComputeLn2(RR& res)
{
RRPush push;
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR s, s1, t, t1;
s = 0;
t = 0.5;
t1 = 0.5;
long i;
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, 0.5);
div(t, t1, i);
}
RR::SetPrecision(p);
xcopy(res, s);
}
void ComputeLn2(RR& res)
{
static NTL_CHEAP_THREAD_LOCAL long prec = 0;
NTL_TLS_LOCAL(RR, ln2);
RRPush push;
long p = RR::precision();
if (prec <= p + 10) {
prec = p + 20;
RR::SetPrecision(prec);
ReallyComputeLn2(ln2);
RR::SetPrecision(p);
}
xcopy(res, ln2);
}
long Lg2(const RR& x)
{
return NumBits(x.mantissa()) + x.exponent();
}
void log(RR& res, const RR& x)
{
if (x <= 0) ArithmeticError("argument to log must be positive");
RRPush push;
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR y;
long n;
// re-write x = 2^n * (1 - y), where -1/2 < y < 1/4 (so 3/4 < 1-y < 3/2)
if (x > 0.75 && x < 1.5) {
n = 0;
sub(y, 1, x);
}
else {
n = Lg2(x) - 1;
RR t;
power2(t, -n);
mul(t, t, x);
while (t > 1.5) {
mul(t, t, 0.5);
n++;
}
sub(y, 1, t);
}
// compute s = - ln(1-y) by power series expansion
RR s, s1, t, t1;
s = 0;
xcopy(t, y);
xcopy(t1, y);
long i;
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, y);
div(t, t1, i);
}
if (n == 0)
t = 0;
else {
ComputeLn2(t);
mul(t, t, n);
}
RR::SetPrecision(p);
sub(res, t, s);
}
void ComputeLn10(RR& res)
{
static NTL_CHEAP_THREAD_LOCAL long prec = 0;
NTL_TLS_LOCAL(RR, ln10);
RRPush push;
long p = RR::precision();
if (prec <= p + 10) {
prec = p + 20;
RR::SetPrecision(prec);
log(ln10, to_RR(10));
RR::SetPrecision(p);
}
xcopy(res, ln10);
}
void log10(RR& res, const RR& x)
{
RRPush push;
long p = RR::precision();
RR::SetPrecision(p + 10);
RR ln10, t1, t2;
ComputeLn10(ln10);
log(t1, x);
div(t2, t1, ln10);
RR::SetPrecision(p);
xcopy(res, t2);
}
void expm1(RR& res, const RR& x)
{
RRPush push;
long p = RR::precision();
if (x < -0.5 || x > 0.5) {
RR t;
RR::SetPrecision(p + 10);
exp(t, x);
RR::SetPrecision(p);
sub(res, t, 1);
return;
}
RR::SetPrecision(p + NumBits(p) + 10);
RR f;
xcopy(f, x);
RR s, s1, t;
long i;
s = 0;
xcopy(t, f);
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
div(t, t, i);
}
RR::SetPrecision(p);
xcopy(res, s);
}
void log1p(RR& res, const RR& x)
{
RRPush push;
long p = RR::precision();
RR y;
if (x < -0.5 || x > 0.5) {
RR::SetPrecision(p + 10);
log(y, 1 + x);
RR::SetPrecision(p);
xcopy(res, y);
return;
}
RR::SetPrecision(p + NumBits(p) + 10);
negate(y, x);
// compute s = - ln(1-y) by power series expansion
RR s, s1, t, t1;
s = 0;
xcopy(t, y);
xcopy(t1, y);
long i;
for (i = 2; ; i++) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, y);
div(t, t1, i);
}
RR::SetPrecision(p);
negate(res, s);
}
void pow(RR& res, const RR& x, const RR& y)
{
if (y == 0) {
res = 1;
return;
}
if (x == 0) {
res = 0;
return;
}
if (x == 1) {
res = 1;
return;
}
if (x < 0) {
ArithmeticError("pow: sorry...first argument to pow must be nonnegative");
}
RRPush push;
long p = RR::precision();
// calculate working precison...one could use p + NTL_BITS_PER_LONG + 10,
// for example, but we want the behaviour to be machine independent.
// so we calculate instead a rough approximation to log |y log(x)|
RR t1, t2;
long k;
if (x > 0.5 && x < 1.5) {
xcopy(t1, x - 1);
k = Lg2(t1);
}
else {
k = NumBits(Lg2(x));
}
k += Lg2(y);
if (k > NTL_BITS_PER_LONG+10) ResourceError("RR: overflow");
if (k < 0) k = 0;
RR::SetPrecision(p + k + 10);
t1 = y*log(x);
RR::SetPrecision(p);
t2 = exp(t1);
res = t2;
}
void ReallyComputePi(RR& res)
{
RRPush push;
long p = RR::precision();
RR::SetPrecision(p + NumBits(p) + 10);
RR sum1;
RR s, s1, t, t1;
s = 0;
t = 0.5;
t1 = 0.5;
long i;
for (i = 3; ; i+=2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, -0.25);
div(t, t1, i);
}
xcopy(sum1, s);
RR g;
inv(g, to_RR(3)); // g = 1/3
s = 0;
xcopy(t, g);
xcopy(t1, g);
sqr(g, g);
negate(g, g); // g = -1/9
for (i = 3; ; i+=2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t1, t1, g);
div(t, t1, i);
}
add(s, s, sum1);
mul(s, s, 4);
RR::SetPrecision(p);
xcopy(res, s);
}
void ComputePi(RR& res)
{
static NTL_CHEAP_THREAD_LOCAL long prec = 0;
NTL_TLS_LOCAL(RR, pi);
RRPush push;
long p = RR::precision();
if (prec <= p + 10) {
prec = p + 20;
RR::SetPrecision(prec);
ReallyComputePi(pi);
RR::SetPrecision(p);
}
xcopy(res, pi);
}
void sin(RR& res, const RR& x)
{
if (x == 0) {
res = 0;
return;
}
if (Lg2(x) > 1000)
ResourceError("sin: sorry...argument too large in absolute value");
RRPush push;
long p = RR::precision();
RR pi, t1, f;
RR n;
// we want to make x^2 < 3, so that the series for sin(x)
// converges nicely, without any nasty cancellations in the
// first terms of the series.
RR::SetPrecision(p + NumBits(p) + 10);
if (x*x < 3) {
xcopy(f, x);
}
else {
// we want to write x/pi = n + f, |f| < 1/2....
// but we have to do *this* very carefully, so that f is computed
// to precision > p. I know, this is sick!
long p1;
p1 = p + Lg2(x) + 20;
for (;;) {
RR::SetPrecision(p1);
ComputePi(pi);
xcopy(t1, x/pi);
xcopy(n, floor(t1));
xcopy(f, t1 - n);
if (f > 0.5) {
n++;
xcopy(f, t1 - n);
}
if (f == 0 || p1 < p - Lg2(f) + Lg2(n) + 10) {
// we don't have enough bits of f...increase p1 and continue
p1 = p1 + max(20, p1/10);
}
else
break;
}
RR::SetPrecision(p + NumBits(p) + 10);
ComputePi(pi);
xcopy(f, pi * f);
if (n != 0 && n.exponent() == 0) {
// n is odd, so we negate f, which negates sin(f)
xcopy(f, -f);
}
}
// Boy, that was painful, but now its over, and we can simply apply
// the series for sin(f)
RR t2, s, s1, t;
long i;
s = 0;
xcopy(t, f);
for (i = 3; ; i=i+2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
mul(t, t, f);
div(t, t, i-1);
div(t, t, i);
negate(t, t);
}
RR::SetPrecision(p);
xcopy(res, s);
}
void cos(RR& res, const RR& x)
{
if (x == 0) {
res = 1;
return;
}
if (Lg2(x) > 1000)
ResourceError("cos: sorry...argument too large in absolute value");
RRPush push;
long p = RR::precision();
RR pi, t1, f;
RR n;
// we want to write x/pi = (n+1/2) + f, |f| < 1/2....
// but we have to do *this* very carefully, so that f is computed
// to precision > p. I know, this is sick!
long p1;
p1 = p + Lg2(x) + 20;
for (;;) {
RR::SetPrecision(p1);
ComputePi(pi);
xcopy(t1, x/pi);
xcopy(n, floor(t1));
xcopy(f, t1 - (n + 0.5));
if (f == 0 || p1 < p - Lg2(f) + Lg2(n) + 10) {
// we don't have enough bits of f...increase p1 and continue
p1 = p1 + max(20, p1/10);
}
else
break;
}
RR::SetPrecision(p + NumBits(p) + 10);
ComputePi(pi);
xcopy(f, pi * f);
if (n == 0 || n.exponent() != 0) {
// n is even, so we negate f, which negates sin(f)
xcopy(f, -f);
}
// Boy, that was painful, but now its over, and we can simply apply
// the series for sin(f)
RR t2, s, s1, t;
long i;
s = 0;
xcopy(t, f);
for (i = 3; ; i=i+2) {
add(s1, s, t);
if (s == s1) break;
xcopy(s, s1);
mul(t, t, f);
mul(t, t, f);
div(t, t, i-1);
div(t, t, i);
negate(t, t);
}
RR::SetPrecision(p);
xcopy(res, s);
}
ostream& operator<<(ostream& s, const RR& a)
{
if (IsZero(a)) {
s << "0";
return s;
}
RRPush push;
// we compute new_p and log_10_a precisely using sufficient
// precision---this is necessary to achieve accuracy and
// platform independent behaviour
long temp_p = max(NumBits(RR::OutputPrecision()),
NumBits(Lg2(a))) + 10;
RR::SetPrecision(temp_p);
RR ln2, ln10, log_2_10;
ComputeLn2(ln2);
ComputeLn10(ln10);
log_2_10 = ln10/ln2;
long new_p = to_long(RR::OutputPrecision()*log_2_10) + 20;
long log_10_a = to_long(Lg2(a)/log_2_10);
RR::SetPrecision(new_p);
RR b;
long neg;
if (a < 0) {
negate(b, a);
neg = 1;
}
else {
xcopy(b, a);
neg = 0;
}
long k = RR::OutputPrecision() - log_10_a;
RR c, d;
power(c, to_RR(10), RR::OutputPrecision());
power(d, to_RR(10), log_10_a);
div(b, b, d);
mul(b, b, c);
while (b < c) {
mul(b, b, 10);
k++;
}
while (b >= c) {
div(b, b, 10);
k--;
}
add(b, b, 0.5);
k = -k;
ZZ B;
conv(B, b);
long bp_len = RR::OutputPrecision()+10;
UniqueArray<char> bp_store;
bp_store.SetLength(bp_len);
char *bp = bp_store.get();
long len, i;
len = 0;
do {
if (len >= bp_len) LogicError("RR output: buffer overflow");
bp[len] = IntValToChar(DivRem(B, B, 10));
len++;
} while (B > 0);
for (i = 0; i < len/2; i++) {
char tmp;
tmp = bp[i];
bp[i] = bp[len-1-i];
bp[len-1-i] = tmp;
}
i = len-1;
while (bp[i] == '0') i--;
k += (len-1-i);
len = i+1;
bp[len] = '\0';
if (k > 3 || k < -len - 3) {
// use scientific notation
if (neg) s << "-";
s << "0." << bp << "e" << (k + len);
}
else if (k >= 0) {
if (neg) s << "-";
s << bp;
for (i = 0; i < k; i++)
s << "0";
}
else if (k <= -len) {
if (neg) s << "-";
s << "0.";
for (i = 0; i < -len-k; i++)
s << "0";
s << bp;
}
else {
if (neg) s << "-";
for (i = 0; i < len+k; i++)
s << bp[i];
s << ".";
for (i = len+k; i < len; i++)
s << bp[i];
}
return s;
}
NTL_END_IMPL