//
// LiDIA - a library for computational number theory
// Copyright (c) 1995 by the LiDIA Group
//
// xdouble.h
// Copyright (c) 1995, 1996 by Keith Briggs
//
// File        : xdouble.h
// Author      : Keith Briggs (KB)
// Last change : KB, Aug 21 1995, initial version (1.4)
//               TP, Oct  5 1995, ported to LiDIA
//               Changes:
//               (a) renamed Quad to xdouble and quad.[h,cc]
//                   to xdouble.[h,c]
//               (b) Placed inline function in xdouble.inl and
//                   changed xdouble.c appropriately.
//               WB, Aug 09 1996, ported to LiDIA (TEL%43)
//                                made it usable
//

/******************************************************************************
 
C++ functions for xdouble (i.e. double+double) precision.

These functions use techniques due to Dekker, Linnainmaa, Kahan, Knuth
and Priest.  I credit Kahan with the addition algorithm (the simplification
which permits the elimination of the tests and branches is due to Knuth);
Dekker and Linnainmaa with the multiply, divide, and square root routines,
and Priest for the initial transcription into C++.

See:

T. J. Dekker
   Point Technique for Extending the Available Precision,
   Numer. Math. 18 (1971), pp. 224-242.
S. Linnainmaa
   Software for doubled-precision floating point computations
   ACM TOMS 7, 172-283 (1081).
D. Priest
  On properties of floating point arithmetics: numerical stability
  and the cost of accurate computations.   Ph.D. Diss, Berkeley 1992.

NB.   These functions assume IEEE double!
Note that all functions returns values such that |x.lo|<|x.hi|.

*******************************************************************************/

#ifndef __xdouble_h__
#define __xdouble_h__

#include <LiDIA/lidia.h>
#include <iostream.h>
#include <iomanip.h>
#include <math.h>
#include <stdlib.h>
#ifndef __EMX__
#include <nan.h>  // defines NAN,at least in gcc;in EMX,it's already in math.h
#endif

//
// Some system use a copysign define
//
#ifdef copysign
#undef copysign
#undef HAVE_COPYSIGN
#endif

//
// 2^27 + 1, appropriate for IEEE double
// 
static const double Split = 134217729.0L;	

class xdouble
{
  protected:
    double hi, lo;
  public:

// 
// Useful constants
//
    static const xdouble Log2;
    static const xdouble Log10;
    static const xdouble Pi;
    static const xdouble TwoPi;
    static const xdouble Pion2;
    static const xdouble Pion4;

//
// Public access to hi and lo
// 
    double h() const;
    double l() const;
    
//
// Constructors
//
    xdouble();
    xdouble(const xdouble&);
    xdouble(double, double);
    xdouble(char *);
    
//
// Conversions
//
    friend double double_xdbl(const xdouble&);
    friend unsigned long ulong_xdbl(const xdouble&);
    friend long long_xdbl(const xdouble&);
    friend unsigned int uint_xdbl(const xdouble&);
    friend int int_xdbl(const xdouble&);
    friend unsigned short ushort_xdbl(const xdouble&);
    friend short short_xdbl(const xdouble&);
    friend unsigned char uchar_xdbl(const xdouble&);
    friend char char_xdbl(const xdouble&);

//
// Operators
//
    xdouble& operator = (const char&); 
    xdouble& operator = (const unsigned char&); 
    xdouble& operator = (const short&); 
    xdouble& operator = (const unsigned short&); 
    xdouble& operator = (const int&); 
    xdouble& operator = (const unsigned int&);
    xdouble& operator = (const long&); 
    xdouble& operator = (const unsigned long&); // Get funny errors without this
    xdouble& operator = (const double&);
    xdouble& operator = (const xdouble&);
    xdouble& operator = (char*);

//
// Unary minus
//
    friend xdouble operator - (const xdouble&);

//
// Addition
//
    friend xdouble operator + (const xdouble&, const xdouble&);
    friend xdouble operator + (const double&, const xdouble&);
    friend xdouble operator + (const xdouble& x, const double& d);
    friend xdouble operator + (const unsigned long& ul, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const unsigned long& ul);
    friend xdouble operator + (const long& l, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const long& l);
    friend xdouble operator + (const unsigned int& ui, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const unsigned int& ui);
    friend xdouble operator + (const int& i, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const int& i);
    friend xdouble operator + (const unsigned short& us, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const unsigned short& us);
    friend xdouble operator + (const short& s, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const short& s);
    friend xdouble operator + (const unsigned char& uc, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const unsigned char& uc);
    friend xdouble operator + (const char& c, const xdouble& x);
    friend xdouble operator + (const xdouble& x, const char& c);

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

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

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

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

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

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

//
// Assignment & Division
//
    xdouble& operator /= (const xdouble& x);
    xdouble& operator /= (const double& d);
    xdouble& operator /= (const unsigned long& ul);
    xdouble& operator /= (const long& l);
    xdouble& operator /= (const unsigned int& ui);
    xdouble& operator /= (const int& i);
    xdouble& operator /= (const unsigned short& us);
    xdouble& operator /= (const short& s);
    xdouble& operator /= (const unsigned char& uc);
    xdouble& operator /= (const char& c);

//    friend xdouble operator %(const xdouble&, const xdouble& );
    friend xdouble operator | (const xdouble &, const xdouble &);

//
// Comparison
//
    friend bool operator > (const xdouble &, const xdouble &);
    friend bool operator >= (const xdouble &, const xdouble &);
    friend bool operator < (const xdouble &, const xdouble &);
    friend bool operator <= (const xdouble &, const xdouble &);
    friend bool operator == (const xdouble &, const xdouble &);
    friend bool operator != (const xdouble &, const xdouble &);

    friend double dnorm(const xdouble &);

//
// member functions
//
    void dump(char *); // debugging use only
  
//
// non inline functions (defined in xdouble.c)
//
// Input/Output
//
    friend istream & operator >> (istream &, xdouble &);
    friend ostream & operator << (ostream &, const xdouble &);

// 
// String conversion
//
    friend xdouble string_to_xdouble(const char*);
    friend void xdouble_to_string(char*, const xdouble&);

//
// for compatibility to double
//
    friend int sign(const xdouble &);
    friend xdouble hypot(const xdouble &, const xdouble &);
    friend xdouble recip(const xdouble &);
    friend xdouble sqrt(const xdouble &);
    friend xdouble sqr(const xdouble &);
    friend xdouble cub(const xdouble &);
    friend xdouble sqr_double(const double &);
    friend xdouble rint(const xdouble &);
    friend xdouble ceil(const xdouble &);
    friend xdouble floor(const xdouble &);
    friend xdouble trunc(const xdouble &);
    friend xdouble fmod(const xdouble &, const int);
    friend xdouble fabs(const xdouble &);
    friend xdouble exp(const xdouble &);
    friend xdouble log(const xdouble &);
    friend xdouble log10(const xdouble &);
    friend xdouble powint(const xdouble &, const long);
    friend xdouble pow(const xdouble &, const xdouble &);
    friend xdouble sin(const xdouble &);
    friend xdouble cos(const xdouble &);
    friend xdouble atan(const xdouble &);
    friend xdouble atan2(const xdouble &, const xdouble &);
    friend xdouble asin(const xdouble &);
    friend xdouble sinh(const xdouble &);
    friend xdouble cosh(const xdouble &);

    friend int digits(const xdouble &, const xdouble &);

#ifndef HAVE_COPYSIGN
    friend double copysign(double x, double y);
#endif
    friend xdouble copysign(const xdouble& x, const double y);
};				// end class xdouble

//
// inline members
//
inline double xdouble::h() const
{
  return hi;
}

inline double xdouble::l() const
{
  return lo;
}

//
// Constructors
//
inline xdouble::xdouble():hi(0.0), lo(0.0)
{
}

inline xdouble::xdouble(double x, double y = 0.0) 
{
  double t;
  hi = x + y; t = x - hi; lo = y + t;
}

inline xdouble::xdouble(const xdouble& x):hi(x.h()), lo(x.l())
{
}

//
// Assignments
//
inline xdouble& xdouble::operator = (const char& c) 
{
  hi = (double )c;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const unsigned char& uc) 
{
  hi = (double )uc;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const short& s) 
{
  hi = (double )s;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const unsigned short& us) 
{
  hi = (double )us;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const int& i) 
{
  hi = (double )i;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const unsigned int& ui) 
{
  hi = (double )ui;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const long& l) 
{
  hi = (double )l;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const unsigned long& ul) 
{
  hi = (double )ul;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const double &d) 
{
  hi = d;
  lo = 0.0;
  return *this;
}

inline xdouble& xdouble::operator = (const xdouble& x) 
{
  hi = x.hi;
  lo = x.lo;
  return *this;
}

#ifdef XDBL_NO_INLINE
#define XDBL_INLINE 
#else
#define XDBL_INLINE inline
#endif

#ifndef XDBL_NO_INLINE
#include <LiDIA/xdouble.inl>
#endif

//
// inline functions
//
inline double dnorm(const xdouble & x)
{
  return fabs(x.h());
}

//
// Conversions
//
inline double double_xdbl(const xdouble& xd)
{ return (xd.hi); }
inline unsigned long ulong_xdbl(const xdouble& xd)
{ return ((unsigned long )xd.hi); }
inline long long_xdbl(const xdouble& xd)
{ return ((long )xd.hi); }
inline unsigned int uint_xdbl(const xdouble& xd)
{ return ((unsigned int )xd.hi); }
inline int int_xdbl(const xdouble& xd)
{ return ((int )xd.hi); }
inline unsigned short ushort_xdbl(const xdouble& xd)
{ return ((unsigned short )xd.hi); }
inline short short_xdbl(const xdouble& xd)
{ return ((short )xd.hi); }
inline unsigned char uchar_xdbl(const xdouble& xd)
{ return ((unsigned char )xd.hi); }
inline char char_xdbl(const xdouble& xd)
{ return ((char )xd.hi); }

#endif
