/****************************************************************************
 * Core Library Version 1.7, August 2004
 * Copyright (c) 1995-2004 Exact Computation Project
 * All rights reserved.
 *
 * This file is part of CGAL (www.cgal.org).
 *
 * File: Real.h
 *
 * Synopsis: The Real class is a superclass for all the number
 *           systems in the Core Library (int, long, float, double,
 *           BigInt, BigRat, BigFloat, etc)
 *
 * Written by
 *       Koji Ouchi <ouchi@simulation.nyu.edu>
 *       Chee Yap <yap@cs.nyu.edu>
 *       Chen Li <chenli@cs.nyu.edu>
 *       Zilin Du <zilin@cs.nyu.edu>
 *       Sylvain Pion <pion@cs.nyu.edu>
 *
 * WWW URL: https://cs.nyu.edu/exact/
 * Email: exact@cs.nyu.edu
 *
 * $URL: https://github.com/CGAL/cgal/blob/v6.2-beta1/CGAL_Core/include/CGAL/CORE/Real.h $
 * $Id: include/CGAL/CORE/Real.h 431cfb5357d $
 * SPDX-License-Identifier: LGPL-3.0-or-later
 ***************************************************************************/
#ifndef _CORE_REAL_H_
#define _CORE_REAL_H_

#include "RealRep.h"
#include <CGAL/use.h>

namespace CORE {
// class Real
typedef RCImpl<RealRep> RCReal;
class Real : public RCReal {
public:
  Real(int i=0) : RCReal(new RealLong(i)) {}
  Real(unsigned int ui) : RCReal(nullptr) {
    (ui<=INT_MAX) ? (rep=new RealLong(static_cast<int>(ui))) : (rep=new RealBigInt(ui));
  }
  Real(long l) : RCReal(new RealLong(l)) {}
  Real(unsigned long ul) : RCReal(nullptr) {
    (ul<=LONG_MAX) ? (rep=new RealLong(static_cast<long>(ul))) : (rep=new RealBigInt(ul));
  }
  Real(float f) : RCReal(new RealDouble(f)) {}
  Real(double d) : RCReal(new RealDouble(d)) {}
  Real(const BigInt& I) : RCReal(new RealBigInt(I)) {}
  Real(const BigRat& R) : RCReal(new RealBigRat(R)) {}
  Real(const BigFloat& F) : RCReal(new RealBigFloat(F)) {}
  Real(const char* s, const extLong& prec=get_static_defInputDigits()) : RCReal(nullptr) {
    constructFromString(s, prec);
  }
  Real(const std::string& s, const extLong& prec=get_static_defInputDigits()) : RCReal(nullptr){
    constructFromString(s.c_str(), prec);
  }

  /// \name Copy-Assignment-Destructor
  //@{
  /// copy constructor
  Real(const Real& rhs) : RCReal(rhs) {
    rep->incRef();
  }
  /// assignment operator
  Real& operator=(const Real& rhs) {
    if (this != &rhs) {
      rep->decRef();
      rep = rhs.rep;
      rep->incRef();
    }
    return *this;
  }
  /// destructor
  ~Real() {
    rep->decRef();
  }
  //@}

  /// \name Compound Assignment Operators
  //@{
  /// operator+=
  Real& operator+=(const Real& x);
  /// operator-=
  Real& operator-=(const Real& x);
  /// operator*=
  Real& operator*=(const Real& x);
  /// operator/=
  Real& operator/=(const Real& x);
  //@}

  /// \name Unary Minus, Increment and Decrement Operators
  //@{
  /// unary plus
  Real operator+() const {
    return Real(*this);
  }
  /// unary minus
  Real operator-() const {
    return -(*rep);
  }
  /// left increment operator (++i)
  Real& operator++() {
    *this += 1;
    return *this;
  }
  /// left decrement operator (--i)
  Real& operator--() {
    *this -= 1;
    return *this;
  }
  /// right increment operator (i++)
  Real operator++(int) {
    Real t(*this);
    *this += 1;
    return t;
  }
  /// right decrement operator (i--)
  Real operator--(int) {
    Real t(*this);
    *this -= 1;
    return t;
  }
  //@}

  /// \name String Conversion Functions
  //@{
  /// set value from <tt>const char*</tt>
  void fromString(const char* s, const extLong& prec = get_static_defInputDigits()) {
    *this = Real(s, prec);
  }
  /// convert to <tt>std::string</tt>
  /** give decimal string representation */
  std::string toString(long prec=get_static_defOutputDigits(), bool sci=false) const {
    return rep->toString(prec, sci);
  }
  //@}

  /// \name Conversion Functions
  //@{
  /// convert to \c int
  int intValue() const {
    return static_cast<int>(longValue());
  }
  /// convert to \c long
  long longValue() const {
    return rep->longValue();
  }
  /// convert to \c float
  float floatValue() const {
    return static_cast<float>(doubleValue());
  }
  /// convert to \c double
  double doubleValue() const {
    return rep->doubleValue();
  }
  /// convert to \c BigInt
  BigInt BigIntValue() const {
    return rep->BigIntValue();
  }
  /// convert to \c BigRat
  BigRat BigRatValue() const {
    return rep->BigRatValue();
  }
  /// convert to \c BigFloat (approximate it first!)
  BigFloat BigFloatValue() const {
    return rep->BigFloatValue();
  }
  //@}

  /// \name Approximation Function
  //@{
  /// approximation
  Real approx(const extLong& r=get_static_defRelPrec(),
              const extLong& a=get_static_defAbsPrec()) const {
    return rep->approx(r, a);
  }
  //@}

  /// \name Helper Functions
  //@{
  /// sign function
  int sign() const {
    return rep->sgn();
  }
  /// isZero function
  bool isZero() const {
    return sign() == 0;
  }
  /// return true if interval contains zero
  bool isZeroIn() const {
    return rep->isZeroIn();
  }
  /// absolute value function
  Real abs() const {
    return (sign() >= 0) ? +(*this) : -(*this);
  }

  /// get mantissa of current approximate value
  BigInt getMantissa() const {
    return BigFloatValue().m();
  }
  /// get exponent of current approximate value
  long getExponent() const {
    return BigFloatValue().exp();
  }

  /// return true if error free otherwise return false;
  bool  isExact() const {
    return rep->isExact();
  }

  /// low bound of MSB
  extLong lMSB() const {
    return isExact() ? MSB():(rep->BigFloatValue()).lMSB();
  }
  /// upper bound of MSB
  extLong uMSB() const {
    return isExact() ? MSB():(rep->BigFloatValue()).uMSB();
  }
  /// MSB - Most Significant Bit
  extLong MSB() const {
    return rep->mostSignificantBit;
  }

  /// floor of log_2 of Error
  extLong flrLgErr() const {
    return rep->flrLgErr();
  }
  /// ceil of log_2 of Error
  extLong clLgErr() const {
    return rep->clLgErr();
  }

  /// division with desired precision
  Real div(const Real& x, const extLong& r) const;
  /// squareroot
  Real sqrt(const extLong& x) const {
    return rep->sqrt(x);
  }
  /// squareroot with initial approximation
  Real sqrt(const extLong& x, const BigFloat& A) const {
    return rep->sqrt(x, A);
  }

  /// correspond to the variables "u25, l25, v2p, v2m, v5p, v5m" in Expr
  void ULV_E(extLong &up, extLong &lp, extLong &v2p, extLong &v2m,
             extLong &v5p, extLong &v5m) const {
    rep->ULV_E(up, lp, v2p, v2m, v5p, v5m);
  }

  /// degree of polynomial P(x)
  unsigned long degree() const {
    return rep->degree();
  }
  /// \f$ lg(|| P(X) ||_2) \f$
  unsigned long length() const {
    return rep->length();
  }
  /// \f$ lg(|| P(X) ||_\infty) \f$
  unsigned long height() const {
    return rep->height();
  }
  //@}

  /// return Real(0)
  CGAL_CORE_EXPORT static const Real& getZero();
private:
  CGAL_CORE_EXPORT void constructFromString(const char *str, const extLong& prec);
};

#define CORE_REAL_ZERO Real::getZero()

const long halfLongMax = LONG_MAX /2;
const long halfLongMin = LONG_MIN /2;

struct _real_add {
  template <class T>
  static Real eval(const T& a, const T& b) {
    return T(a+b);
  }
  // specialized for two long values
  static Real eval(long a, long b) {
    if ((a > halfLongMax && b > halfLongMax) || (a < halfLongMin && b < halfLongMin))
      return BigInt(BigInt(a)+ BigInt(b));
    else
      return a+b;
  }
};

struct _real_sub {
  template <class T>
  static Real eval(const T& a, const T& b) {
    return T(a-b);
  }
  // specialized for two long values
  static Real eval(long a, long b) {
    if ((a > halfLongMax && b < halfLongMin) || (a < halfLongMin && b > halfLongMax))
      return BigInt(BigInt(a)-BigInt(b));
    else
      return a-b;
  }
};

struct _real_mul {
  template <class T>
  static Real eval(const T& a, const T& b) {
    return T(a*b);
  }
  // specialized for two long values
  static Real eval(long a, long b) {
    if (flrLg(a) + flrLg(b) >= static_cast<int>(LONG_BIT-2))
      return BigInt(BigInt(a)*BigInt(b));
    else
      return a*b;
  }
};

template <class Op>
struct _real_binary_op {
  static Real eval(const RealRep& a, const RealRep& b) {
    if (a.ID() == REAL_BIGRAT || b.ID() == REAL_BIGRAT) {
      if (!a.isExact()) { // a must be a BigFloat and b must be a BigRat
        BigFloat bf_a = a.BigFloatValue(), bf_b;
        bf_b.approx(b.BigRatValue(), CORE_posInfty, -bf_a.flrLgErr());
        return Op::eval(bf_a, bf_b);
      } else if (!b.isExact()) { // a must be a BigRat and b must be a BigFloat
        BigFloat bf_a, bf_b = b.BigFloatValue();
        bf_a.approx(a.BigRatValue(), CORE_posInfty, -bf_b.flrLgErr());
        return Op::eval(bf_a, bf_b);
      } else // both are BigRat
        return Op::eval(a.BigRatValue(), b.BigRatValue());
    } else if (a.ID() == REAL_BIGFLOAT || b.ID() == REAL_BIGFLOAT
               || a.ID() == REAL_DOUBLE || b.ID() == REAL_DOUBLE) {
      return Op::eval(a.BigFloatValue(), b.BigFloatValue());
    } else if (a.ID() == REAL_BIGINT || b.ID() == REAL_BIGINT) {
      return Op::eval(a.BigIntValue(), b.BigIntValue());
    } else { // a.ID() == REAL_LONG && b.ID() == REAL_LONG
      return Op::eval(a.longValue(), b.longValue());
    }
  }
};

typedef _real_binary_op<_real_add> real_add;
typedef _real_binary_op<_real_sub> real_sub;
typedef _real_binary_op<_real_mul> real_mul;

struct real_div {
  static Real eval(const RealRep& a, const RealRep& b, const extLong& r) {
    if (a.ID() == REAL_BIGRAT || b.ID() == REAL_BIGRAT) {
      if (!a.isExact()) { // a must be a BigFloat and b must be a BigRat
        BigFloat bf_a = a.BigFloatValue(), bf_b;
        bf_b.approx(b.BigRatValue(), bf_a.MSB() - bf_a.flrLgErr() + 1, CORE_posInfty);
        return bf_a.div(bf_b, r);
      } else if (!b.isExact()) { // a must be a BigRat and b must be a BigFloat
        BigFloat bf_a, bf_b = b.BigFloatValue();
        bf_a.approx(a.BigRatValue(), bf_b.MSB() - bf_b.flrLgErr() + 1, CORE_posInfty);
        return bf_a.div(bf_b, r);
      } else // both are BigRat
        return BigRat(a.BigRatValue() / b.BigRatValue());
    } else if (a.ID() == REAL_BIGFLOAT || b.ID() == REAL_BIGFLOAT
               || a.ID() == REAL_DOUBLE || b.ID() == REAL_DOUBLE) {
      return a.BigFloatValue().div(b.BigFloatValue(), r);
    } else if (a.ID() == REAL_BIGINT || b.ID() == REAL_BIGINT) {
      return BigRat(a.BigIntValue(), b.BigIntValue());
    } else { // a.ID() == REAL_LONG && b.ID() == REAL_LONG
      return BigRat(a.longValue(), b.longValue());
    }
  }
};

CGAL_CORE_EXPORT std::istream& operator>>(std::istream& i, Real& r);

inline std::ostream& operator<<(std::ostream& o, const Real& r) {
  return r.getRep().operator<<(o);
}

inline Real& Real::operator+=(const Real& rhs) {
  *this = real_add::eval(getRep(), rhs.getRep());
  return *this;
}
inline Real& Real::operator-=(const Real& rhs) {
  *this = real_sub::eval(getRep(), rhs.getRep());
  return *this;
}
inline Real& Real::operator*=(const Real& rhs) {
  *this = real_mul::eval(getRep(), rhs.getRep());
  return *this;
}
inline Real& Real::operator/=(const Real& rhs) {
  *this = real_div::eval(getRep(), rhs.getRep(), get_static_defRelPrec());
  return *this;
}

// operator+
inline Real operator+(const Real& x, const Real& y) {
  return real_add::eval(x.getRep(), y.getRep());
}
// operator-
inline Real operator-(const Real& x, const Real& y) {
  return real_sub::eval(x.getRep(), y.getRep());
}
// operator*
inline Real operator*(const Real& x, const Real& y) {
  return real_mul::eval(x.getRep(), y.getRep());
}
// operator/
inline Real operator/(const Real& x, const Real& y) {
  return real_div::eval(x.getRep(), y.getRep(), get_static_defRelPrec());
}
// div w/ precision
inline Real Real::div(const Real& x, const extLong& r) const {
  return real_div::eval(getRep(), x.getRep(), r);
}

inline int cmp(const Real& x, const Real& y) {
  return (x-y).sign();
}
inline bool operator==(const Real& x, const Real& y) {
  return cmp(x, y) == 0;
}
inline bool operator!=(const Real& x, const Real& y) {
  return cmp(x, y) != 0;
}
inline bool operator>=(const Real& x, const Real& y) {
  return cmp(x, y) >= 0;
}
inline bool operator>(const Real& x, const Real& y) {
  return cmp(x, y) > 0;
}
inline bool operator<=(const Real& x, const Real& y) {
  return cmp(x, y) <= 0;
}
inline bool operator<(const Real& x, const Real& y) {
  return cmp(x, y) < 0;
}

/// floor function
CGAL_CORE_EXPORT BigInt floor(const Real&, Real&);
/// power function
CGAL_CORE_EXPORT Real pow(const Real&, unsigned long);

/// return sign
inline int sign(const Real& r) {
  return r.sign();
}
/// is zero?
inline bool isZero(const Real& r) {
  return r.sign() == 0;
}
/// absolute value
inline Real abs(const Real& x) {
  return x.abs();
}
/// absolute value (same as abs)
inline Real fabs(const Real& x) {
  return abs(x);
}
/// floor
inline BigInt floor(const Real& r) {
  Real tmp;
  return floor(r, tmp);
}
/// ceiling
inline BigInt ceil(const Real& r) {
  return -floor(-r);
}
/// power
inline Real power(const Real& r, unsigned long p) {
  return pow(r, p);
}
/// square root
inline Real sqrt(const Real& x) {
  return x.sqrt(get_static_defAbsPrec());
}

// class Realbase_for (need defined after Real)
// unary minus operator
template <class T>
inline Real Realbase_for<T>::operator-() const {
  return T(- T(ker));
}
template <>
inline Real RealLong::operator-() const {
  return ker < -LONG_MAX ? BigInt(- BigInt(ker)) : -ker;
}

inline void init_CORE() {
  using RealRep = CORE::RealDouble;
  CGAL_STATIC_THREAD_LOCAL_VARIABLE(MemoryPool<RealRep>*, pool_real_rep, &MemoryPool<RealRep>::global_allocator());
  CGAL_USE(pool_real_rep);
}

} //namespace CORE

#ifdef CGAL_HEADER_ONLY
#include <CGAL/CORE/Real_impl.h>
#endif // CGAL_HEADER_ONLY

#endif // _CORE_REAL_H_
