//
// Addition
//
 /*
    (C) W. Kahan 1989 NOTICE: Copyrighted programs may not be translated,
    used, nor reproduced without the author's permission.  Normally that
    permission is granted freely for academic and scientific purposes subject
    to the following three requirements: 1. This NOTICE and the copyright
    notices must remain attached to the programs and their translations. 2.
    Users of such a program should regard themselves as voluntary
    participants in the author's researches and so are obliged to report
    their experience with the program back to the author. 3. Neither the
    author nor the institution that employs him will be held responsible for
    the consequences of using a program for which neither has received
    payment. Would-be commercial users of these programs must correspond with
    the author to arrange terms of payment and warranty.
 */
XDBL_INLINE xdouble operator + (const xdouble& x, const xdouble& y)
{
  xdouble z;
  double H, h, T, t, S, s, e, f;
  S = x.hi + y.hi;
  T = x.lo + y.lo;
  e = S - x.hi;
  f = T - x.lo;
  s = S - e;
  t = T - f;
  s = (y.hi - e) + (x.hi - s);
  t = (y.lo - f) + (x.lo - t);
  H = S + (s + T);
  h = (s + T) + (S - H);
  z.hi = H + (t + h);
  z.lo = (t + h) + (H - z.hi);
#if DEBUG_XDOUBLE
  if ((z.hi != 0.0) && (z.lo != 0.0))
    {
      if (fabs(z.lo / z.hi) < 1.0e-32)
	{
	  cerr << "possible xdouble error in +\n" << endl;
	  cerr << "input = " << x.hi << "\t" << x.lo << " + " << y.hi << "\t" << y.lo << endl;
	  cout << z.hi << "   " << z.lo << endl;
	  cout << "Ratio=" << z.lo / z.hi << endl;
	  exit(3);
	}
    }
#endif
  return z;
}

XDBL_INLINE xdouble operator + (const double& x, const xdouble& y)
{
  xdouble z;
  double H, h, T, S, s, e;
  S = x + y.hi;
  T = y.lo;
  e = S - x;
  s = S - e;
  s = (y.hi - e) + (x - s);
  H = S + (s + T);
  h = (s + T) + (S - H);
  z.hi = H + h;
  z.lo = h + (H - z.hi);
  return z;
}

XDBL_INLINE xdouble operator + (const xdouble& x, const double& d)
{ return ((double )d + x); }
XDBL_INLINE xdouble operator + (const unsigned long& ul, const xdouble& x)
{ return ((double )ul + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const unsigned long& ul)
{ return ((double )ul + x); }
XDBL_INLINE xdouble operator + (const long& l, const xdouble& x)
{ return ((double )l + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const long& l)
{ return ((double )l + x); }
XDBL_INLINE xdouble operator + (const unsigned int& ui, const xdouble& x)
{ return ((double )ui + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const unsigned int& ui)
{ return ((double )ui + x); }
XDBL_INLINE xdouble operator + (const int& i, const xdouble& x)
{ return ((double )i + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const int& i)
{ return ((double )i + x); }
XDBL_INLINE xdouble operator + (const unsigned short& us, const xdouble& x)
{ return ((double )us + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const unsigned short& us)
{ return ((double )us + x); }
XDBL_INLINE xdouble operator + (const short& s, const xdouble& x)
{ return ((double )s + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const short& s)
{ return ((double )s + x); }
XDBL_INLINE xdouble operator + (const unsigned char& uc, const xdouble& x)
{ return ((double )uc + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const unsigned char& uc)
{ return ((double )uc + x); }
XDBL_INLINE xdouble operator + (const char& c, const xdouble& x)
{ return ((double )c + x); }
XDBL_INLINE xdouble operator + (const xdouble& x, const char& c)
{ return ((double )c + x); }

//
// Assignment & Addition
//
XDBL_INLINE xdouble& xdouble::operator += (const xdouble& x)
{ *this = *this + x; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const double& d)
{ *this = *this + d; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const unsigned long& ul)
{ *this = *this + ul; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const long& l)
{ *this = *this + l; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const unsigned int& ui)
{ *this = *this + ui; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const int& i)
{ *this = *this + i; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const unsigned short& us)
{ *this = *this + us; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const short& s)
{ *this = *this + s; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const unsigned char& uc)
{ *this = *this + uc; return (*this); };
XDBL_INLINE xdouble& xdouble::operator += (const char& c)
{ *this = *this + c; return (*this); };

//
// Unary minus
//
XDBL_INLINE xdouble operator - (const xdouble & x)
{
  return xdouble(-x.hi, -x.lo);
}

//
// Absolute value
//
XDBL_INLINE xdouble fabs(const xdouble & x)
{
  if (x.h() >= 0.0)
    return x;
  else
    return -x;
}

//
// Subtraction
//
XDBL_INLINE xdouble operator - (const xdouble& x, const xdouble& y)
{
  return x + xdouble(-y.hi, -y.lo);
}
XDBL_INLINE xdouble operator - (const double& x, const xdouble& y)
{
  return x + xdouble(-y.hi, -y.lo);
}

//
// Subtraction
//
XDBL_INLINE xdouble operator - (const xdouble& x, const double& d)
{ return (-d + x); }
XDBL_INLINE xdouble operator - (const unsigned long& ul, const xdouble& x)
{ return ((double )ul + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const unsigned long& ul)
{ return (-((double )ul) + x); }
XDBL_INLINE xdouble operator - (const long& l, const xdouble& x)
{ return ((double )l + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const long& l)
{ return (-((double )l) + x); }
XDBL_INLINE xdouble operator - (const unsigned int& ui, const xdouble& x)
{ return ((double )ui + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const unsigned int& ui)
{ return (-((double )ui) + x); }
XDBL_INLINE xdouble operator - (const int& i, const xdouble& x)
{ return ((double )i + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const int& i)
{ return (-((double )i) + x); }
XDBL_INLINE xdouble operator - (const unsigned short& us, const xdouble& x)
{ return ((double )us + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const unsigned short& us)
{ return (-((double )us) + x); }
XDBL_INLINE xdouble operator - (const short& s, const xdouble& x)
{ return ((double )s + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const short& s)
{ return (-((double )s) + x); }
XDBL_INLINE xdouble operator - (const unsigned char& uc, const xdouble& x)
{ return ((double )uc + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const unsigned char& uc)
{ return (-((double )uc) + x); }
XDBL_INLINE xdouble operator - (const char& c, const xdouble& x)
{ return ((double )c + x); }
XDBL_INLINE xdouble operator - (const xdouble& x, const char& c)
{ return (-((double )c) + x); }

//
// Assignment & Subtraction
//
XDBL_INLINE xdouble& xdouble::operator -= (const xdouble& x)
{ *this = *this - x; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const double& d)
{ *this = *this - d; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const unsigned long& ul)
{ *this = *this - ul; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const long& l)
{ *this = *this - l; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const unsigned int& ui)
{ *this = *this - ui; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const int& i)
{ *this = *this - i; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const unsigned short& us)
{ *this = *this - us; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const short& s)
{ *this = *this - s; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const unsigned char& uc)
{ *this = *this - uc; return (*this); };
XDBL_INLINE xdouble& xdouble::operator -= (const char& c)
{ *this = *this - c; return (*this); };

//
// Multiplication
//
XDBL_INLINE xdouble operator *(const xdouble& x, const xdouble& y)
{
  xdouble z;
  double hx, tx, hy, ty, C, c;
  C = Split * x.hi;
  hx = C - x.hi;
  c = Split * y.hi;
  hx = C - hx;
  tx = x.hi - hx;
  hy = c - y.hi;
  C = x.hi * y.hi;
  hy = c - hy;
  ty = y.hi - hy;
  c = ((((hx * hy - C) + hx * ty) + tx * hy) + tx * ty) + (x.hi * y.lo + x.lo * y.hi);
  hx = C + c;
  z.hi = hx;
  z.lo = c + double (C - hx);
  return z;
}

// double*xdouble
XDBL_INLINE xdouble operator *(const double& x, const xdouble& y)
{
  xdouble z;
  double hx, tx, hy, ty, C, c;
  C = Split * x;
  hx = C - x;
  c = Split * y.hi;
  hx = C - hx;
  tx = x - hx;
  hy = c - y.hi;
  C = x * y.hi;
  hy = c - hy;
  ty = y.hi - hy;
  c = ((((hx * hy - C) + hx * ty) + tx * hy) + tx * ty) + x * y.lo;
  hx = C + c;
  z.hi = hx;
  z.lo = c + double (C - hx);
  return z;
}

//
// unsigned long*xdouble
//
XDBL_INLINE xdouble operator *(const unsigned long& x, const xdouble& y)
{
  if (x == 0)
    return xdouble(0.0);
  if (x == 1)
    return y;
  if (x == 2)
    return y + y;
  xdouble z;
  double hx, tx, hy, ty, C, c;
  C = Split * x;
  hx = C - x;
  c = Split * y.hi;
  hx = C - hx;
  tx = x - hx;
  hy = c - y.hi;
  C = x * y.hi;
  hy = c - hy;
  ty = y.hi - hy;
  c = ((((hx * hy - C) + hx * ty) + tx * hy) + tx * ty) + (x * y.lo);
  hx = C + c;
  z.hi = hx;
  z.lo = c + double (C - hx);
  return z;
}

//
// long*xdouble
//
XDBL_INLINE xdouble operator *(const long& x, const xdouble& y)
{
  if (x == -1)
    return -y;
  if (x == 0)
    return xdouble(0.0);
  if (x == 1)
    return y;
  if (x == 2)
    return y + y;
  xdouble z;
  double hx, tx, hy, ty, C, c;
  C = Split * x;
  hx = C - x;
  c = Split * y.hi;
  hx = C - hx;
  tx = x - hx;
  hy = c - y.hi;
  C = x * y.hi;
  hy = c - hy;
  ty = y.hi - hy;
  c = ((((hx * hy - C) + hx * ty) + tx * hy) + tx * ty) + (x * y.lo);
  hx = C + c;
  z.hi = hx;
  z.lo = c + double (C - hx);
  return z;
}

//
// Multiplication
//
XDBL_INLINE xdouble operator * (const xdouble& x, const double& d)
{ return (d * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const unsigned long& ul)
{ return (ul * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const long& l)
{ return (l * x); }
XDBL_INLINE xdouble operator * (const unsigned int& ui, const xdouble& x)
{ return ((unsigned long )ui * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const unsigned int& ui)
{ return ((unsigned long )ui * x); }
XDBL_INLINE xdouble operator * (const int& i, const xdouble& x)
{ return ((long )i * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const int& i)
{ return ((long )i * x); }
XDBL_INLINE xdouble operator * (const unsigned short& us, const xdouble& x)
{ return ((unsigned long )us * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const unsigned short& us)
{ return ((unsigned long )us * x); }
XDBL_INLINE xdouble operator * (const short& s, const xdouble& x)
{ return ((long )s * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const short& s)
{ return ((long )s * x); }
XDBL_INLINE xdouble operator * (const unsigned char& uc, const xdouble& x)
{ return ((unsigned long )uc * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const unsigned char& uc)
{ return ((unsigned long )uc * x); }
XDBL_INLINE xdouble operator * (const char& c, const xdouble& x)
{ return ((long )c * x); }
XDBL_INLINE xdouble operator * (const xdouble& x, const char& c)
{ return ((long )c * x); }

//
// Assignment & Multiplication
//
XDBL_INLINE xdouble& xdouble::operator *= (const xdouble& x)
{ *this = *this * x; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const double& d)
{ *this = *this * d; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const unsigned long& ul)
{ *this = *this *ul; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const long& l)
{ *this = *this *l; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const unsigned int& ui)
{ *this = *this * ui; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const int& i)
{ *this = *this * i; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const unsigned short& us)
{ *this = *this * us; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const short& s)
{ *this = *this * s; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const unsigned char& uc)
{ *this = *this * uc; return (*this); };
XDBL_INLINE xdouble& xdouble::operator *= (const char& c)
{ *this = *this * c; return (*this); };


//
// Divide using Newton's method (EXPERIMENTAL)
//
XDBL_INLINE xdouble operator | (const xdouble& a, const xdouble& b)
{
  double dx = 1.0 / b.hi;
  xdouble x = dx + (1.0 - dx * b.hi) * dx;	// Newton

  xdouble y = a.hi * x.hi;
  return y + (a - y * b).hi * x.hi;
}

//
// Division
//
XDBL_INLINE xdouble operator / (const xdouble& x, const xdouble& y)
{
  xdouble z;
  double hc, tc, hy, ty, C, c, U, u;
  C = x.hi / y.hi;
  c = Split * C;
  hc = c - C;
  u = Split * y.hi;
  hc = c - hc;
  tc = C - hc;
  hy = u - y.hi;
  U = C * y.hi;
  hy = u - hy;
  ty = y.hi - hy;
  u = (((hc * hy - U) + hc * ty) + tc * hy) + tc * ty;
  c = ((((x.hi - U) - u) + x.lo) - C * y.lo) / y.hi;
  u = C + c;
  z.hi = u;
  z.lo = double (C - u) + c;
  return z;
}

//
// double/xdouble
//
XDBL_INLINE xdouble operator / (const double& x, const xdouble& y)
{
  xdouble z;
  double hc, tc, hy, ty, C, c, U, u;
  C = x / y.hi;
  c = Split * C;
  hc = c - C;
  u = Split * y.hi;
  hc = c - hc;
  tc = C - hc;
  hy = u - y.hi;
  U = C * y.hi;
  hy = u - hy;
  ty = y.hi - hy;
  u = (((hc * hy - U) + hc * ty) + tc * hy) + tc * ty;
  c = ((((x - U) - u)) - C * y.lo) / y.hi;
  u = C + c;
  z.hi = u;
  z.lo = double (C - u) + c;
  return z;
}

//
// xdouble/double ?? BUGGY? KMB95Apr5
//
XDBL_INLINE xdouble operator / (const xdouble& x, const double& y)
{
  xdouble z;
  double hc, tc, hy, ty, C, c, U, u;
  C = x.hi / y;
  c = Split * C;
  hc = c - C;
  u = Split * y;
  hc = c - hc;
  tc = C - hc;
  hy = u - y;
  U = C * y;
  hy = u - hy;
  ty = y - hy;
  u = (((hc * hy - U) + hc * ty) + tc * hy) + tc * ty;
  c = (((x.hi - U) - u) + x.lo) / y;
  u = C + c;
  z.hi = u;
  z.lo = double (C - u) + c;
  return z;
}

//
// xdouble/long
//
XDBL_INLINE xdouble operator / (const xdouble& x, const long& y)
{
  xdouble z;
  double hc, tc, hy, ty, C, c, U, u;
  C = x.hi / y;
  c = Split * C;
  hc = c - C;
  u = Split * y;
  hc = c - hc;
  tc = C - hc;
  hy = u - y;
  U = C * y;
  hy = u - hy;
  ty = y - hy;
  u = (((hc * hy - U) + hc * ty) + tc * hy) + tc * ty;
  c = ((((x.hi - U) - u) + x.lo)) / y;
  u = C + c;
  z.hi = u;
  z.lo = double (C - u) + c;
  return z;
}

//
// Division
//
XDBL_INLINE xdouble operator / (const xdouble& x, const unsigned long& ul)
{ return (x / (double )ul); }
XDBL_INLINE xdouble operator / (const unsigned long& ul, const xdouble& x)
{ return ((double )ul / x); }
XDBL_INLINE xdouble operator / (const long& l, const xdouble& x)
{ return ((double )l / x); }
XDBL_INLINE xdouble operator / (const unsigned int& ui, const xdouble& x)
{ return ((double )ui / x); }
XDBL_INLINE xdouble operator / (const xdouble& x, const unsigned int& ui)
{ return (x / (double )ui); }
XDBL_INLINE xdouble operator / (const int& i, const xdouble& x)
{ return ((double )i / x); }
XDBL_INLINE xdouble operator / (const xdouble& x, const int& i)
{ return (x / (double )i); }
XDBL_INLINE xdouble operator / (const unsigned short& us, const xdouble& x)
{ return ((double )us / x); }
XDBL_INLINE xdouble operator / (const xdouble& x, const unsigned short& us)
{ return (x / (double )us); }
XDBL_INLINE xdouble operator / (const short& s, const xdouble& x)
{ return ((double )s / x); }
XDBL_INLINE xdouble operator / (const xdouble& x, const short& s)
{ return (x / (double )s); }
XDBL_INLINE xdouble operator / (const unsigned char& uc, const xdouble& x)
{ return ((double )uc / x); }
XDBL_INLINE xdouble operator / (const xdouble& x, const unsigned char& uc)
{ return (x / (double )uc); }
XDBL_INLINE xdouble operator / (const char& c, const xdouble& x)
{ return ((double )c / x); }
XDBL_INLINE xdouble operator / (const xdouble& x, const char& c)
{ return (x / (double )c); }


//
// Assignment & Division
//
XDBL_INLINE xdouble& xdouble::operator /= (const xdouble& x)
{ *this = *this / x; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const double& d)
{ *this = *this / d; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const unsigned long& ul)
{ *this = *this / ul; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const long& l)
{ *this = *this / l; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const unsigned int& ui)
{ *this = *this / ui; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const int& i)
{ *this = *this / i; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const unsigned short& us)
{ *this = *this / us; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const short& s)
{ *this = *this / s; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const unsigned char& uc)
{ *this = *this / uc; return (*this); };
XDBL_INLINE xdouble& xdouble::operator /= (const char& c)
{ *this = *this / c; return (*this); };


