//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigfloat_int.c 
// Author      : Nigel Smart (NiSm), Patrick Theobald (PT)
// Last change : PT, Jan 23 1997, initial version
//		

#include <LiDIA/bigfloat.h>
#include <LiDIA/bigfloat_config.h>
#include <stdlib.h>
#include <LiDIA/bigfloat_int.h>

void bigfloat_int::normalize(bigfloat& Lo1,bigfloat &Lo2,bigfloat& Lo3,bigfloat &Lo4)
{
  Low=Lo1; Upp=Lo1;
  if (Lo1.compare(Lo2)==1) { Low=Lo2; }
  else			   { Upp=Lo2; }
  if (Lo3.compare(Low)==-1)  Low=Lo3;
  if (Lo3.compare(Upp)==1)   Upp=Lo3;
  if (Lo4.compare(Low)==-1)  Low=Lo4;
  if (Lo4.compare(Upp)==1)   Upp=Lo4;
}

bigfloat_int::bigfloat_int()
{ 
  Upp.assign_zero();
  Low.assign_zero();
}

bigfloat_int::bigfloat_int(int i)
{
  Upp.assign(i);
  Low.assign(i);
}

bigfloat_int::bigfloat_int(long l)
{
  Upp.assign(l);
  Low.assign(l);
}

bigfloat_int::bigfloat_int(unsigned long ul)
{
  Upp.assign(ul);
  Low.assign(ul);
}

bigfloat_int::bigfloat_int(double d)
{
  this->assign(d);
}

bigfloat_int::bigfloat_int(const bigint & bi)
{
  this->assign(bi);
}

bigfloat_int::bigfloat_int(const bigfloat_int & bf)
{
  this->assign(bf);
}

bigfloat_int::bigfloat_int(const bigfloat & bf1,const bigfloat & bf2)
{
  this->assign(bf1,bf2);
}

bigfloat_int::bigfloat_int(const bigfloat & bf)
{
  this->assign(bf);
}

bigfloat_int::bigfloat_int(const bigint & n, const bigint & d)
{
  this->assign(n, d);
}

bigfloat_int::bigfloat_int(const bigrational & r)
{
  this->assign(r); 
}

int bigfloat_int::sign() const
{
  int s=0;
  if (Upp.is_lt_zero()) { s=-1; }
  if (Low.is_gt_zero()) { s=1;  }
  return s;
}

void bigfloat_int::absolute_value()
{
  bigfloat u=Upp;
  bigfloat l=Low;
  if (Upp.is_lt_zero())
	{ u=-Low; l=-Upp; }
  else
	{ if (Low.is_lt_zero())
	     { l=0; u=-Low;
	       if (Upp.compare(u)==1) { u=Upp; }
             }
        }
  Upp=u; Low=l;
}

void bigfloat_int::negate()
{ 
  bigfloat t=Upp;
  bigfloat::mode(MP_RND_UP);
  Upp=-Low;
  bigfloat::mode(MP_RND_DOWN);
  Low=-t;
}

void bigfloat_int::assign(const bigint & y)
{
  Upp.assign(y);
  Low.assign(y);
}

void bigfloat_int::assign(const bigint & x, const bigint & y)
{
  bigfloat_int tmp(y);
  this->assign(x);
  divide(*this, *this, tmp);
}

void bigfloat_int::assign(const bigrational & r)
{ 
  bigfloat_int tmp(denominator(r));
  this->assign(numerator(r));
  divide(*this,*this,tmp);
}

void bigfloat_int::assign(const bigfloat_int & y)
{
  Upp.assign(y.Upp);
  Low.assign(y.Low);
}

void bigfloat_int::assign(const bigfloat & y)
{
  Upp.assign(y);
  Low.assign(y);
}

void bigfloat_int::assign(const bigfloat & bf1,const bigfloat & bf2)
{
  Upp.assign(bf2); 
  Low.assign(bf1); 
}

void bigfloat_int::assign(double d)
{
  Upp.assign(d);
  Low.assign(d);
}

void bigfloat_int::assign(int i)
{
  Upp.assign(i);
  Low.assign(i);
}

void bigfloat_int::assign(long i)
{
  Upp.assign(i);
  Low.assign(i);
}

void bigfloat_int::assign(unsigned long ui)
{
  Upp.assign(ui);
  Low.assign(ui);
}

void bigfloat_int::assign_zero()
{
  Upp.assign_zero();
  Low.assign_zero();
}

void bigfloat_int::assign_one()
{
  Upp.assign_one();
  Low.assign_one();
}

void bigfloat_int::invert()
{
  bigfloat t=Upp;
  bigfloat::mode(MP_RND_UP);
  Upp=inverse(Low);
  bigfloat::mode(MP_RND_DOWN);
  Low=inverse(t);
}

void bigfloat_int::dble(double & d)
{ 
  bigfloat c=Low+Upp;
  c.divide_by_2();

  if (c.doublify(d)==1)
    { lidia_error_handler("bigfloat_int","dble:Cannot assign to double"); }
}

void bigfloat_int::Approx()
{
  bigfloat c=Low+Upp;
  c.divide_by_2();
  Low=c; Upp=c;
}

void bigfloat_int::approx(bigfloat & c)
{
  c=Low+Upp;
  c.divide_by_2(); 
}

// Length of interval
void bigfloat_int::int_length(bigfloat& a)
{ a=abs(Low-Upp); }

bigfloat_int & bigfloat_int::operator = (const bigfloat & bf)
{
  Upp.assign(bf);
  Low.assign(bf);
  return *this;
}

bigfloat_int & bigfloat_int::operator = (const bigfloat_int & bf)
{
  Upp.assign(bf.Upp);
  Low.assign(bf.Low); 
  return *this; 
}

bool operator != (const bigfloat_int & x, const bigfloat_int & y)
{
  if ((x.Low>y.Upp) || (x.Upp<y.Low)) return true;
  return false;
}

bool operator > (const bigfloat_int & x, const bigfloat_int & y)
{
  if (x.Low>y.Upp) return true;
  return false;
}

bool operator < (const bigfloat_int & x, const bigfloat_int & y)
{
  if (x.Upp<y.Low) return true;
  return false;
}


bool bigfloat_int::contains_zero() const
{
  if ((Upp.is_ge_zero()) && (Low.is_le_zero())) return true;
  return false;
}

bool bigfloat_int::is_gt_zero() const
{
  return Low.is_gt_zero();
}

bool bigfloat_int::is_lt_zero() const
{
  return Upp.is_lt_zero();
}

void negate(bigfloat_int & x, const bigfloat_int & y)
{
  x.assign(y);
  x.negate();
}

void invert(bigfloat_int & x, const bigfloat_int & y)
{
  if (&x != &y)
     divide(x, 1, y);
  else
  {
     bigfloat_int tmp;
     divide(tmp, 1, y);
     x.assign(tmp);
  }
}

bigfloat_int inverse(const bigfloat_int & y)
{
     bigfloat_int x;
     divide(x, 1, y);
     return x;
}

void add(bigfloat_int & sum, const bigfloat_int & x, const bigfloat_int & y)
{
  bigfloat::mode(MP_RND_DOWN);
  add(sum.Low,x.Low,y.Low);
  bigfloat::mode(MP_RND_UP);
  add(sum.Upp,x.Upp,y.Upp);
}

void add(bigfloat_int & sum, const bigfloat_int & x, long i)
{
  bigfloat::mode(MP_RND_DOWN);
  add(sum.Low,x.Low,i);
  bigfloat::mode(MP_RND_UP);
  add(sum.Upp,x.Upp,i);
}

void add(bigfloat_int & sum, long i, const bigfloat_int & y)
{
  bigfloat::mode(MP_RND_DOWN);
  add(sum.Low,y.Low,i);
  bigfloat::mode(MP_RND_UP);
  add(sum.Upp,y.Upp,i); 
}

void inc(bigfloat_int & sum)
{
  bigfloat_int tmp(1);
  add(sum, sum, tmp);
}

void subtract(bigfloat_int & dif, const bigfloat_int & x, const bigfloat_int & y)
{
  bigfloat::mode(MP_RND_DOWN);
  subtract(dif.Low,x.Low,y.Upp);
  bigfloat::mode(MP_RND_UP);
  subtract(dif.Upp,x.Upp,y.Low);
}

void subtract(bigfloat_int & dif, const bigfloat_int & x, long i)
{
  bigfloat::mode(MP_RND_DOWN); 
  subtract(dif.Low,x.Low,i); 
  bigfloat::mode(MP_RND_UP); 
  subtract(dif.Upp,x.Upp,i); 
}

void subtract(bigfloat_int & dif, long i, const bigfloat_int & y)
{
  bigfloat::mode(MP_RND_DOWN); 
  subtract(dif.Low,i,y.Upp); 
  bigfloat::mode(MP_RND_UP); 
  subtract(dif.Upp,i,y.Low); 
}

void dec(bigfloat_int & dif)
{
  bigfloat_int tmp(1);
  subtract(dif, dif, tmp);
}

void multiply(bigfloat_int & prod, const bigfloat_int & x, const bigfloat_int & y)
{
  bigfloat::mode(MP_RND_DOWN);
  multiply(prod.Low,x.Low,y.Low);
  bigfloat::mode(MP_RND_UP);
  multiply(prod.Upp,x.Upp,y.Upp);
}

void multiply(bigfloat_int & prod, const bigfloat_int & x, long i)
{
  bigfloat::mode(MP_RND_DOWN);
  multiply(prod.Low,x.Low,i); 
  bigfloat::mode(MP_RND_UP); 
  multiply(prod.Upp,x.Upp,i); 
}

void multiply(bigfloat_int & prod, long i, const bigfloat_int & x)
{
  bigfloat::mode(MP_RND_DOWN);
  multiply(prod.Low,x.Low,i); 
  bigfloat::mode(MP_RND_UP); 
  multiply(prod.Upp,x.Upp,i); 
}

void square(bigfloat_int & c, const bigfloat_int & a)
{
  multiply(c,a,a);
}

bigfloat_int square(const bigfloat_int & a)
{
  bigfloat_int c;
  square(c, a);
  return c;
}

void divide(bigfloat_int & quot, const bigfloat_int & x, const bigfloat_int & y)
{
  bigfloat::mode(MP_RND_DOWN);
  divide(quot.Low,x.Low,y.Upp);
  bigfloat::mode(MP_RND_UP);
  divide(quot.Upp,x.Upp,y.Low);
}

void divide(bigfloat_int & quot, const bigfloat_int & x, long i)
{
  bigfloat::mode(MP_RND_DOWN); 
  divide(quot.Low,x.Low,i); 
  bigfloat::mode(MP_RND_UP); 
  divide(quot.Upp,x.Upp,i); 
}

void divide(bigfloat_int & quot, long i, const bigfloat_int & x)
{
  bigfloat::mode(MP_RND_DOWN); 
  divide(quot.Low,i,x.Upp); 
  bigfloat::mode(MP_RND_UP); 
  divide(quot.Upp,i,x.Low); 

}

bigfloat_int operator - (const bigfloat_int & a)
{
  bigfloat_int c(a);
  c.negate();
  return c;
}

bigfloat_int operator + (const bigfloat_int & a, const bigfloat_int & b)
{
  bigfloat_int c;
  add(c, a, b);
  return c;
}

bigfloat_int operator - (const bigfloat_int & a, const bigfloat_int & b)
{
  bigfloat_int c;
  subtract(c, a, b);
  return c;
}

bigfloat_int operator * (const bigfloat_int & a, const bigfloat_int & b)
{
  bigfloat_int c;
  multiply(c, a, b);
  return c;
}

bigfloat_int operator / (const bigfloat_int & a, const bigfloat_int & b)
{
  bigfloat_int c;
  divide(c, a, b);
  return c;
}

bigfloat_int & bigfloat_int::operator += (const bigfloat_int & a)
{
  *this = *this + a;
  return *this;
}

bigfloat_int & bigfloat_int::operator -= (const bigfloat_int & a)
{
  *this = *this - a;
  return *this;
}

bigfloat_int & bigfloat_int::operator *= (const bigfloat_int & a)
{
  *this = *this * a;
  return *this;
}

bigfloat_int & bigfloat_int::operator /= (const bigfloat_int & a)
{
  *this = *this / a;
  return *this;
}

bigfloat_int & bigfloat_int::operator++ ()
{
  inc(*this);
  return *this;
}

bigfloat_int & bigfloat_int::operator-- ()
{
  dec(*this);
  return *this;
}


void constant_E(bigfloat_int & x)
{
  bigfloat r,l;
  bigfloat::mode(MP_RND_DOWN);
  constant_E(l);
  bigfloat::mode(MP_RND_UP);
  constant_E(r);
  x.assign(l,r);
}


void constant_Pi(bigfloat_int & x)
{
  bigfloat r,l;
  bigfloat::mode(MP_RND_DOWN);
  constant_Pi(l);
  bigfloat::mode(MP_RND_UP);
  constant_Pi(r);
  x.assign(l,r);
}


void constant_Euler(bigfloat_int & x)
{
  bigfloat r,l;
  bigfloat::mode(MP_RND_DOWN);
  constant_Euler(l);
  bigfloat::mode(MP_RND_UP);
  constant_Euler(r);
  x.assign(l,r);
}


void constant_Catalan(bigfloat_int & x)
{
  bigfloat r,l;
  bigfloat::mode(MP_RND_DOWN);
  constant_Catalan(l);
  bigfloat::mode(MP_RND_UP);
  constant_Catalan(r);
  x.assign(l,r);
}


void truncate(bigint & y, const bigfloat_int & x)
{
  long s=x.sign();
  y=floor(abs(x));
  multiply(y,y,s);
}


void round(bigint & y, const bigfloat_int & x)
{
  bigint t;
  round(x.Low).bigintify(t);
  round(x.Upp).bigintify(y);
  if (t!=y) 
    lidia_error_handler("bigfloat_int","round()::Not Enough Precision In Round");
}


void floor(bigint & y, const bigfloat_int & x)
{
  floor(x.Low).bigintify(y);
}

void ceil(bigint & y, const bigfloat_int & x)
{
  ceil(x.Upp).bigintify(y); 
}

int sign(const bigfloat_int & x)
{
  return x.sign();
}

bigfloat_int abs(const bigfloat_int & x)
{
  bigfloat_int y(x);
  y.absolute_value();
  return y;
}

bigint truncate(const bigfloat_int & x)
{
  bigint y;
  truncate(y, x);
  return y;
}

bigint round(const bigfloat_int & x)
{
  bigint y;
  round(y, x);
  return y;
}

bigint floor(const bigfloat_int & x)
{
  bigint y;
  floor(y, x);
  return y;
}

bigint ceil(const bigfloat_int & x)
{
  bigint y;
  ceil(y, x);
  return y;
}

void sqrt(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat::mode(MP_RND_DOWN);
  sqrt(y.Low,x.Low);
  bigfloat::mode(MP_RND_UP);
  sqrt(y.Upp,x.Upp);
}

void sqr(bigfloat_int & y, const bigfloat_int & x)
{ multiply(y,x,x); }

void exp(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat::mode(MP_RND_DOWN); 
  exp(y.Low,x.Low);
  bigfloat::mode(MP_RND_UP); 
  exp(y.Upp,x.Upp); 
}

void log(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat::mode(MP_RND_DOWN); 
  log(y.Low,x.Low);
  bigfloat::mode(MP_RND_UP); 
  log(y.Upp,x.Upp); 
}

void power(bigfloat_int & z, const bigfloat_int & x, const bigfloat_int & y)
{
  bigfloat l1,l2,l3,l4,l5,l6;
  bigfloat_int s;
  bigfloat::mode(MP_RND_DOWN);  
  power(l1,x.Low,y.Low);
  power(l2,x.Low,y.Upp);
  power(l3,x.Upp,y.Low);
  power(l4,x.Upp,y.Upp);
  z.normalize(l1,l2,l3,l4);
  l5=z.Low; l6=z.Upp;
  bigfloat::mode(MP_RND_UP); 
  power(l1,x.Low,y.Low);   
  power(l2,x.Low,y.Upp);
  power(l3,x.Upp,y.Low);
  power(l4,x.Upp,y.Upp);
  s.normalize(l1,l2,l3,l4); 
  z.normalize(s.Upp,s.Low,l5,l6);
}

void power(bigfloat_int & x, const bigfloat_int & a, long n)
{
  bigfloat::mode(MP_RND_DOWN);   
  power(x.Low,a.Low,n); 
  bigfloat::mode(MP_RND_UP);  
  power(x.Upp,a.Upp,n);   
}

void besselj(bigfloat_int & y, int n, const bigfloat_int & x)
{ 
  bigfloat u1,l1,u2,l2;
  bigfloat::mode(MP_RND_DOWN);   
  besselj(l1,n,x.Low);  
  besselj(u1,n,x.Upp);  
  bigfloat::mode(MP_RND_UP);   
  besselj(l2,n,x.Low);
  besselj(u2,n,x.Upp);
  y.normalize(l1,l2,u1,u2);
}

bigfloat_int sqrt(const bigfloat_int & x)
{
  bigfloat_int y;
  sqrt(y, x);
  return y;
}

bigfloat_int sqr(const bigfloat_int & x)
{
  bigfloat_int y;
  multiply(y,x,x);
  return y; 
}


bigfloat_int exp(const bigfloat_int & x)
{
  bigfloat_int y;
  exp(y, x);
  return y;
}


bigfloat_int log(const bigfloat_int & x)
{
  bigfloat_int y;
  log(y, x);
  return y;
}

bigfloat_int power(const bigfloat_int & x, const bigfloat_int & y)
{
  bigfloat_int z;
  power(z, x, y);
  return z;
}

bigfloat_int power(const bigfloat_int & x, long n)
{
  bigfloat_int z;
  power(z, x, n);
  return z;
}

bigfloat_int besselj(int n, const bigfloat_int & x)
{
  bigfloat_int y;
  besselj(y, n, x);
  return y;
}

void sinh(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int tmp(x);
  exp(y, tmp);
  divide(tmp, 1, y);
  subtract(y, y, tmp);
  divide(y,y,2);
}

void cosh(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int tmp(x);
  exp(y, tmp);
  divide(tmp, 1, y);
  add(y, y, tmp);
  divide(y,y,2);
}

void tanh(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int tmp1, tmp2;
  exp(y, x);
  divide(tmp1, 1, y);
  add(tmp2, y, tmp1);
  subtract(y, y, tmp1);
  divide(y, y, tmp2);
}

void coth(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int tmp1, tmp2;
  exp(y, x);
  divide(tmp1, 1, y);
  subtract(tmp2, y, tmp1);
  add(y, y, tmp1);
  divide(y, y, tmp2);
}


bigfloat_int sinh(const bigfloat_int & x)
{
  bigfloat_int y;
  sinh(y, x);
  return y;
}

bigfloat_int cosh(const bigfloat_int & x)
{
  bigfloat_int y;
  cosh(y, x);
  return y;
}

bigfloat_int tanh(const bigfloat_int & x)
{
  bigfloat_int y;
  tanh(y, x);
  return y;
}

bigfloat_int coth(const bigfloat_int & x)
{
  bigfloat_int y;
  coth(y, x);
  return y;
}

void asinh(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int p1;
  square(p1, x);
  inc(p1);
  sqrt(p1, p1);
  add(y, x, p1);
  log(y, y);
}

void acosh(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int p1;
  square(p1, x);
  dec(p1);
  if (p1.is_lt_zero())
    lidia_error_handler("bigfloat_int", "acosh::cannot handle arguments <= 1");
  sqrt(p1, p1);
  add(y, x, p1);
  log(y, y);
}


void atanh(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int p1(x);
  
  p1.negate();
  inc(p1);
  divide(y, 2, p1);
  dec(y);
  if (p1.is_lt_zero())
    lidia_error_handler("bigfloat_int", "atanh::cannot handle arguments <= -1 and >= 1");
  log(y, y);
  divide(y,y,2);
}


void acoth(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int p1(x);
  dec(p1);
  divide(y, 2, p1);
  inc(p1);
  if (p1.is_lt_zero())
    lidia_error_handler("bigfloat_int", "acoth::cannot handle arguments >= -1 and <= 1");
  log(y, y);
  divide(y,y,2);
}

bigfloat_int asinh(const bigfloat_int & x)
{
  bigfloat_int y;
  asinh(y, x);
  return y;
}

bigfloat_int acosh(const bigfloat_int & x)
{
  bigfloat_int y;
  acosh(y, x);
  return y;
}

bigfloat_int atanh(const bigfloat_int & x)
{
  bigfloat_int y;
  atanh(y, x);
  return y;
}

bigfloat_int acoth(const bigfloat_int & x)
{
  bigfloat_int y;
  acoth(y, x);
  return y;
}

void sin(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2;
  bigfloat::mode(MP_RND_DOWN);
  sin(l1,x.Low);
  sin(u1,x.Upp);
  bigfloat::mode(MP_RND_UP);
  sin(l2,x.Low);
  sin(u2,x.Upp);
  y.normalize(l1,l2,u1,u2);
}

void cos(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2; 
  bigfloat::mode(MP_RND_DOWN); 
  cos(l1,x.Low);
  cos(u1,x.Upp); 
  bigfloat::mode(MP_RND_UP); 
  cos(l2,x.Low); 
  cos(u2,x.Upp);
  y.normalize(l1,l2,u1,u2); 
}

void tan(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2; 
  bigfloat::mode(MP_RND_DOWN); 
  tan(l1,x.Low);
  tan(u1,x.Upp); 
  bigfloat::mode(MP_RND_UP); 
  tan(l2,x.Low); 
  tan(u2,x.Upp);
  y.normalize(l1,l2,u1,u2); 
}

void cot(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2; 
  bigfloat::mode(MP_RND_DOWN); 
  cot(l1,x.Low);
  cot(u1,x.Upp); 
  bigfloat::mode(MP_RND_UP); 
  cot(l2,x.Low); 
  cot(u2,x.Upp);
  y.normalize(l1,l2,u1,u2); 
}

void asin(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2; 
  bigfloat::mode(MP_RND_DOWN); 
  asin(l1,x.Low);
  asin(u1,x.Upp); 
  bigfloat::mode(MP_RND_UP); 
  asin(l2,x.Low); 
  asin(u2,x.Upp);
  y.normalize(l1,l2,u1,u2); 
}

void acos(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2;
  bigfloat::mode(MP_RND_DOWN);
  acos(l1,x.Low);
  acos(u1,x.Upp);
  bigfloat::mode(MP_RND_UP);
  acos(l2,x.Low);
  acos(u2,x.Upp);
  y.normalize(l1,l2,u1,u2);
}

void atan(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2;
  bigfloat::mode(MP_RND_DOWN);
  atan(l1,x.Low);
  atan(u1,x.Upp);
  bigfloat::mode(MP_RND_UP);
  atan(l2,x.Low);
  atan(u2,x.Upp);
  y.normalize(l1,l2,u1,u2);
}

void atan2(bigfloat_int & z, const bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat u1,l1,u2,l2,u3,l3; 
  bigfloat_int s;
  bigfloat::mode(MP_RND_DOWN); 
  atan2(l1,y.Low,x.Low); 
  atan2(u1,y.Low,x.Upp); 
  atan2(l2,y.Upp,x.Low);  
  atan2(u2,y.Upp,x.Upp);
  z.normalize(l1,l2,u1,u2); 
  u3=z.Upp; l3=z.Low;
  bigfloat::mode(MP_RND_UP);
  atan2(l1,y.Low,x.Low);  
  atan2(u1,y.Low,x.Upp); 
  atan2(l2,y.Upp,x.Low);   
  atan2(u2,y.Upp,x.Upp); 
  s.normalize(l1,u1,l2,u2);
  z.normalize(l3,u3,s.Upp,s.Low);
  if (x.Upp.is_le_zero() && y.Upp.is_approx_zero()
			 && y.Low.is_approx_zero())
  { z.Low=z.Upp; }
}

void acot(bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int p1, p2;
  atan(p1, x);
  constant_Pi(p2);
  divide(p2,p2,2);
  subtract(y, p2, p1);
}


bigfloat_int sin(const bigfloat_int & x)
{
  bigfloat_int y;
  sin(y, x);
  return y;
}

bigfloat_int cos(const bigfloat_int & x)
{
  bigfloat_int y;
  cos(y, x);
  return y;
}

bigfloat_int tan(const bigfloat_int & x)
{
  bigfloat_int y;
  tan(y, x);
  return y;
}

bigfloat_int cot(const bigfloat_int & x)
{
  bigfloat_int y;
  cot(y, x);
  return y;
}

bigfloat_int asin(const bigfloat_int & x)
{
  bigfloat_int y;
  asin(y, x);
  return y;
}

bigfloat_int acos(const bigfloat_int & x)
{
  bigfloat_int y;
  acos(y, x);
  return y;
}

bigfloat_int atan(const bigfloat_int & x)
{
  bigfloat_int y;
  atan(y, x);
  return y;
}

bigfloat_int atan2(const bigfloat_int & y, const bigfloat_int & x)
{
  bigfloat_int z;
  atan2(z, y, x);
  return z;
}

bigfloat_int acot(const bigfloat_int & x)
{
  bigfloat_int y;
  acot(y, x);
  return y;
}


ostream & operator << (ostream & out, const bigfloat_int & a)
{ 
  out << "(" << a.Low << "," << a.Upp << ")";
  return out;
}

istream & operator >> (istream & in, bigfloat_int & a)
{
  char ch;
  in  >> ch;
  if (ch!='(') 
     lidia_error_handler("bigfloat_int",">>:: Expected (");
  in >> a.Low;
  in >> ch;
  if (ch!=',')
     lidia_error_handler("bigfloat_int",">>:: Expected ,"); 
  in >> a.Upp; 
  in >> ch; 
  if (ch!=')')  
     lidia_error_handler("bigfloat_int",">>:: Expected )"); 
  return in;
}

void swap(bigfloat_int& a, bigfloat_int& b)
{
        swap(a.Upp, b.Upp);
        swap(a.Low, b.Low);
}

