/****************************************************************************
 * 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: BigFloat.h
 * Synopsis:
 *                 An implementation of BigFloat numbers with error bounds.
 *
 * Written by
 *       Chee Yap <yap@cs.nyu.edu>
 *       Chen Li <chenli@cs.nyu.edu>
 *       Zilin Du <zilin@cs.nyu.edu>
 *
 * WWW URL: https://cs.nyu.edu/exact/
 * Email: exact@cs.nyu.edu
 *
 * $URL: https://github.com/CGAL/cgal/blob/v6.0.2/CGAL_Core/include/CGAL/CORE/BigFloat.h $
 * $Id: include/CGAL/CORE/BigFloat.h e13ef800cb7 $
 * SPDX-License-Identifier: LGPL-3.0-or-later
 ***************************************************************************/

#ifndef _CORE_BIGFLOAT_H_
#define _CORE_BIGFLOAT_H_

#include <CGAL/disable_warnings.h>

#include <CGAL/CORE/BigFloatRep.h>
#include <CGAL/assertions.h>

namespace CORE {

class Expr;

/// \class BigFloat BigFloat.h
/// \brief BigFloat is a class of Float-Point number with error bounds.
typedef RCImpl<BigFloatRep> RCBigFloat;

class CGAL_CORE_EXPORT BigFloat : public RCBigFloat {
public:
  /// \name Constructors and Destructor
  //@{
  /// default constructor
  BigFloat() : RCBigFloat(new BigFloatRep()) {}
  /// constructor for <tt>short</tt>
  BigFloat(short i) : RCBigFloat(new BigFloatRep(i)) {}
  /// constructor for <tt>float</tt>
  BigFloat(float i) : RCBigFloat(new BigFloatRep(i)) {}
  /// constructor for <tt>int</tt>
  BigFloat(int i) : RCBigFloat(new BigFloatRep(i)) {}
  /// constructor for <tt>unsigned int</tt>
  BigFloat(unsigned int i) : RCBigFloat(new BigFloatRep(i)) {}
  /// constructor for <tt>long</tt>
  BigFloat(long l) : RCBigFloat(new BigFloatRep(l)) {}
  /// constructor for <tt>double</tt>
  BigFloat(double d) : RCBigFloat(new BigFloatRep(d)) {}
  /// constructor for <tt>const char* </tt>(default base = 10)
  BigFloat(const char* s) : RCBigFloat(new BigFloatRep(s)) {}
  /// constructor for <tt>std::string</tt>(default base = 10)
  BigFloat(const std::string& s) : RCBigFloat(new BigFloatRep(s.c_str())) {}

  /// constructor for <tt>int</tt> and <tt>long</tt>
  //     This is a hack because in Sturm, we need to approximate any
  //     coefficient type NT to a BigFloat, and it would complain if we
  //     do not have this method explicitly:
  BigFloat(int& i, const extLong& /*r*/, const extLong& /*a*/)
      : RCBigFloat(new BigFloatRep(i)) {}
  BigFloat(long& x, const extLong& /*r*/, const extLong& /*a*/)
      : RCBigFloat(new BigFloatRep(x)) {}
  /// constructor from <tt>BigInt</tt>, error and exponent values
  BigFloat(const BigInt& I, unsigned long er, long ex)
      : RCBigFloat(new BigFloatRep(I, er, ex)) {}
  /// constructor from <tt>BigInt</tt>, exponent values
  BigFloat(const BigInt& I, long ex)
      : RCBigFloat(new BigFloatRep(I, ex)) {}
  BigFloat(const BigInt& I)
      : RCBigFloat(new BigFloatRep(I)) {}
  /// constructor for <tt>BigRat</tt>
  BigFloat(const BigRat& R, const extLong& r = get_static_defRelPrec(),
           const extLong& a = get_static_defAbsPrec())
      : RCBigFloat(new BigFloatRep()) {
    rep->approx(R, r, a);
  }

  // REMARK: it is somewhat against our principles to have BigFloat
  // know about Expr, but BigFloat has a special role in our system!
  // ===============================
  /// constructor for <tt>Expr</tt>
  explicit BigFloat(const Expr& E, const extLong& r = get_static_defRelPrec(),
           const extLong& a = get_static_defAbsPrec());

  //Dummy
  explicit BigFloat(const BigFloat& E, const extLong& ,
           const extLong&): RCBigFloat(E) {
    rep->incRef();
  }

  /// constructor for <tt>BigFloatRep</tt>
  explicit BigFloat(BigFloatRep* r) : RCBigFloat(new BigFloatRep()) {
    rep = r;
  }

  // constructor for exp2
  explicit BigFloat(BigFloatRep* r, bool) : RCBigFloat(r) {
  }

  operator BigRat() const {
    return this->BigRatValue();
  }

  //@}

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

  /// \name Compound Assignment Operators
  //@{
  /// operator+=
  BigFloat& operator+= (const BigFloat& x) {
    BigFloat z;
    z.rep->add(*rep, *x.rep);
    *this = z;
    return *this;
  }
  /// operator-=
  BigFloat& operator-= (const BigFloat& x) {
    BigFloat z;
    z.rep->sub(*rep, *x.rep);
    *this = z;
    return *this;
  }
  /// operator*=
  BigFloat& operator*= (const BigFloat& x) {
    BigFloat z;
    z.rep->mul(*rep, *x.rep);
    *this = z;
    return *this;
  }
  /// operator/=
  BigFloat& operator/= (const BigFloat& x) {
    BigFloat z;
    z.rep->div(*rep, *x.rep, get_static_defBFdivRelPrec());
    *this = z;
    return *this;
  }
  //@}

  /// \name Unary Minus Operator
  //@{
  /// unary plus
  BigFloat operator+() const {
    return BigFloat(*this);
  }
  /// unary minus
  BigFloat operator-() const {
    return BigFloat(-rep->m, rep->err, rep->exp);
  }
  //@}

  /// \name String Conversion Functions
  //@{
  /// set value from <tt>const char*</tt> (base = 10)
  void fromString(const char* s, extLong p = getBigFloatInputDigits()) {
    rep->fromString(s, p);
  }
  /// convert to <tt>std::string</tt> (base = 10)
  std::string toString(long prec=get_static_defBigFloatOutputDigits(), bool sci=false) const {
    return rep->toString(prec, sci);
  }
  std::string str() const {
    return toString();
  }
  //@}

  /// \name Conversion Functions
  //@{
  /// return int value
  int intValue() const {
    return static_cast<int>(rep->toLong());
  }
  /// return long value
  long longValue() const {
    long l = rep->toLong();
    if ((l == LONG_MAX) || (l == LONG_MIN))
      return l; // return the overflown value.
    if ((sign() < 0) && (cmp(BigFloat(l)) != 0)) {
      // a negative value not exactly rounded.
      l--; // rounded to floor.
    }
    return l;
  }
  /// return float value
  float floatValue() const {
    return static_cast<float>(rep->toDouble());
  }
  /// return double value
  double doubleValue() const {
    return rep->toDouble();
  }
  /// return BigInt value
  BigInt BigIntValue() const {
    return rep->toBigInt();
  }
  /// return BigRat value
  BigRat BigRatValue() const {
    return rep->BigRatize();
  }
  //@}

  /// \name Helper Functions
  //@{
  /// Has Exact Division
  static bool hasExactDivision() {
    return false;
  }

  //CONSTANTS
  /// return BigFloat(0)
  static const BigFloat& getZero();
  /// return BigFloat(1)
  static const BigFloat& getOne();

  /// sign function
  /** \note This is only the sign of the mantissa, it can be taken to be
      the sign of the BigFloat only if !(isZeroIn()). */
  int sign() const {
    CGAL_assertion((err() == 0 && m() == 0) || !(isZeroIn()));
    return rep->signM();
  }
  /// check whether contains zero
  /** \return true if contains zero, otherwise false */
  bool isZeroIn() const {
    return rep->isZeroIn();
  }
  /// absolute value function
  BigFloat abs() const {
    return (sign()>0) ? +(*this) : -(*this);
  }
  ///  comparison function
  int cmp(const BigFloat& x) const {
    return rep->compareMExp(*x.rep);
  }

  /// get mantissa
  const BigInt& m() const {
    return rep->m;
  }
  /// get error bits
  unsigned long err() const {
    return rep->err;
  }
  /// get exponent
  long exp() const {
    return rep->exp;
  }

  /// check whether err == 0
  /** \return true if err == 0, otherwise false */
  bool isExact() const {
    return rep->err == 0;
  }
  /// set err to 0
  /** \return an exact BigFloat, see Tutorial for why this is useful! */
  BigFloat& makeExact() {
    makeCopy();
    rep->err =0;
    return *this;
  }
  /// set err to 0, but first add err to the mantissa (m)
  /** \return the ceiling exact BigFloat, variant of makeExact */
  BigFloat& makeCeilExact() {
    makeCopy();
    rep->m += rep->err;
    rep->err =0;
    return *this;
  }
  /// set err to 0, but subtract err from the mantissa (m)
  /** \return the floor exact BigFloat, variant of makeExact */
  BigFloat& makeFloorExact() {
    makeCopy();
    rep->m -= rep->err;
    rep->err =0;
    return *this;
  }
  /// set err to 1
  /** \return an inexact BigFloat, see Tutorial for why this is useful! */
  BigFloat& makeInexact() {
    makeCopy();
    rep->err =1;
    return *this;
  }

  /// return lower bound of Most Significant Bit
  extLong lMSB() const {
    return rep->lMSB();
  }
  /// return upper bound of Most Significant Bit
  extLong uMSB() const {
    return rep->uMSB();
  }
  /// return Most Significant Bit
  extLong MSB() const {
    return rep->MSB();
  }

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

  /// division with relative precision <tt>r</tt>
  BigFloat div(const BigFloat& x, const extLong& r) const {
    BigFloat y;
    y.rep->div(*rep, *x.rep, r);
    return y;
  }
  /// exact division by 2
  BigFloat div2() const {
    BigFloat y;
    y.rep->div2(*rep);
    return y;
  }

  /// squareroot
  BigFloat sqrt(const extLong& a) const {
    BigFloat x;
    x.rep->sqrt(*rep, a);
    return x;
  }
  /// squareroot with initial approximation <tt>init</tt>
  BigFloat sqrt(const extLong& a, const BigFloat& init) const {
    BigFloat x;
    x.rep->sqrt(*rep, a, init);
    return x;
  }
  //@}

  /// \name Utility Functions
  //@{
  /// approximate BigInt number
  void approx(const BigInt& I, const extLong& r, const extLong& a) {
    makeCopy();
    rep->trunc(I, r, a);
  }
  /// approximate BigFloat number
  void approx(const BigFloat& B, const extLong& r, const extLong& a) {
    makeCopy();
    rep->approx(*B.rep, r, a);
  }
  /// approximate BigRat number
  void approx(const BigRat& R, const extLong& r, const extLong& a) {
    makeCopy();
    rep->approx(R, r, a);
  }
  /// dump internal data
  void dump() const {
    rep->dump();
  }
  //@}

  /// returns a BigFloat of value \f$ 2^e \f$
  static BigFloat exp2(int e) {
    return BigFloat(BigFloatRep::exp2(e),true);
  }

}; // class BigFloat

//@} // For compatibility with BigInt

/// \name File I/O Functions
//@{
/// read from file
void readFromFile(BigFloat& bf, std::istream& in, long maxLength = 0);
/// write to file
void writeToFile(const BigFloat& bf, std::ostream& in,
                int base=10, int charsPerLine=80);

/// IO stream operator<<
inline std::ostream& operator<< (std::ostream& o, const BigFloat& x) {
  x.getRep().operator<<(o);
  return o;
}
/// IO stream operator>>
inline std::istream& operator>> (std::istream& i, BigFloat& x) {
  x.makeCopy();
  x.getRep().operator>>(i);
  return i;
}
//@}

/// operator+
inline BigFloat operator+ (const BigFloat& x, const BigFloat& y) {
  BigFloat z;
  z.getRep().add(x.getRep(), y.getRep());
  return z;
}
/// operator-
inline BigFloat operator- (const BigFloat& x, const BigFloat& y) {
  BigFloat z;
  z.getRep().sub(x.getRep(), y.getRep());
  return z;
}
/// operator*
inline BigFloat operator* (const BigFloat& x, const BigFloat& y) {
  BigFloat z;
  z.getRep().mul(x.getRep(), y.getRep());
  return z;
}
/// operator/
inline BigFloat operator/ (const BigFloat& x, const BigFloat& y) {
  BigFloat z;
  z.getRep().div(x.getRep(),y.getRep(),get_static_defBFdivRelPrec());
  return z;
}

/// operator==
inline bool operator== (const BigFloat& x, const BigFloat& y) {
  return x.cmp(y) == 0;
}
/// operator!=
inline bool operator!= (const BigFloat& x, const BigFloat& y) {
  return x.cmp(y) != 0;
}
/// operator>=
inline bool operator>= (const BigFloat& x, const BigFloat& y) {
  return x.cmp(y) >= 0;
}
/// operator>
inline bool operator> (const BigFloat& x, const BigFloat& y) {
  return x.cmp(y) > 0;
}
/// operator<=
inline bool operator<= (const BigFloat& x, const BigFloat& y) {
  return x.cmp(y) <= 0;
}
/// operator<
inline bool operator< (const BigFloat& x, const BigFloat& y) {
  return x.cmp(y) < 0;
}

/// sign
inline int sign(const BigFloat& x) {
  return x.sign();
}
/// div2
inline BigFloat div2(const BigFloat& x){
  return x.div2();
}
/// abs
inline BigFloat abs(const BigFloat& x) {
  return x.abs();
}
/// cmp
inline int cmp(const BigFloat& x, const BigFloat& y) {
  return x.cmp(y);
}
/// pow
CGAL_CORE_EXPORT BigFloat pow(const BigFloat&, unsigned long);
/// power
inline BigFloat power(const BigFloat& x, unsigned long p) {
  return pow(x, p);
}
/// root(x,k,prec,xx) returns the k-th root of x to precision prec.
///   The argument x is an initial approximation.
BigFloat root(const BigFloat&, unsigned long k, const extLong&, const BigFloat&);
inline BigFloat root(const BigFloat& x, unsigned long k) {
  return root(x, k, get_static_defBFsqrtAbsPrec(), x);
}

/// sqrt to defAbsPrec:
inline BigFloat sqrt(const BigFloat& x) {
  return x.sqrt(get_static_defBFsqrtAbsPrec());
}

/// convert an BigFloat Interval to a BigFloat with error bits
inline BigFloat centerize(const BigFloat& a, const BigFloat& b) {
  BigFloat z;
  z.getRep().centerize(a.getRep(), b.getRep());
  return z;
}

/// minStar(m,n) returns the min-star of m and n
inline long minStar(long m, long n) {
  if (m*n <= 0) return 0;
  if (m>0)
    return core_min(m, n);
  else
    return core_max(m, n);
}
/// \name Functions for Compatibility with BigInt (needed by Poly, Curves)
//@{

/// div_exact(x,y) returns the BigFloat quotient of x divided by y
/**        This is defined only if isDivisible(x,y).
 */
// Chee (8/1/2004)   The definition of div_exact(x,y)
//   ensure that Polynomials<NT> works with NT=BigFloat and NT=double.
//Bug: We should first normalize the mantissas of the Bigfloats and
//then do the BigInt division. For e.g. 1 can be written as 2^{14}*2{-14}.
//Now if we divide 2 by one using this representation of one and without
// normalizing it then we get zero.
inline BigFloat div_exact(const BigFloat& x, const BigFloat& y) {
  BigInt z;
  unsigned long bin_x = getBinExpo(x.m());
  unsigned long bin_y = getBinExpo(y.m());

  BigInt m_x = x.m() >> bin_x;
  BigInt m_y = y.m() >> bin_y;
  long e_x = bin_x + BigFloatRep::bits(x.exp());
  long e_y = bin_y + BigFloatRep::bits(y.exp());
  //Since y divides x, e_y = minstar(e_x, e_y)
  z = div_exact(m_x, m_y);

  //  mpz_divexact(z.get_mp(), x.m().get_mp(), y.m().get_mp()); THIS WAS THE BUG
  // assert: x.exp() - y.exp() does not under- or over-flow.
  return BigFloat(z, e_x - e_y);
}

inline BigFloat div_exact(double x, double y) {
  return div_exact(BigFloat(x), BigFloat(y));
}
// Remark: there is another notion of "exact division" for BigFloats,
//         and that is to make the division return an "exact" BigFloat
//         i.e., err()=0.

/// gcd(a,b) =  BigFloat(gcd(a.mantissa,b.matissa), min(a.exp(), b.exp()) )
inline BigFloat gcd(const BigFloat& a, const BigFloat& b) {
  if (sign(a.m()) == 0) return core_abs(b);
  if (sign(b.m()) == 0) return core_abs(a);

  BigInt r;
  long dx;
  unsigned long bin_a = getBinExpo(a.m());
  unsigned long bin_b = getBinExpo(b.m());

/* THE FOLLOWING IS ALTERNATIVE CODE, for GCD using base B=2^{14}:
 *std::cout << "bin_a=" << bin_a << ",bin_b=" << bin_b << std::endl;
  std::cout << "a.exp()=" << a.exp() << ",b.exp()=" << b.exp() << std::endl;
  long chunk_a = BigFloatRep::chunkFloor(bin_a);
  long chunk_b = BigFloatRep::chunkFloor(bin_b);
  BigInt m_a = BigFloatRep::chunkShift(a.m(), chunk_a);
  BigInt m_b = BigFloatRep::chunkShift(b.m(), chunk_b);

  r = gcd(m_a, m_b);
  dx = minStar(chunk_a + a.exp(), chunk_b + b.exp());
*/
  BigInt m_a = a.m() >> bin_a;
  BigInt m_b = b.m() >> bin_b;
  r = gcd(m_a, m_b);
  dx = minStar(bin_a + BigFloatRep::bits(a.exp()),
                  bin_b + BigFloatRep::bits(b.exp()));

  long chunks = BigFloatRep::chunkFloor(dx);
  r <<= (dx - BigFloatRep::bits(chunks));
  dx = chunks;

  return BigFloat(r, 0, dx);
}

// Not needed for now:
/// div_rem
// inline void div_rem(BigFloat& q, BigFloat& r,
//         const BigFloat& a, const BigFloat& b) {
  //q.makeCopy();
  //r.makeCopy();
  //mpz_tdiv_qr(q.get_mp(), r.get_mp(), a.get_mp(), b.get_mp());
//}//


inline double doubleValue(const BigFloat& bf)
{
  return bf.doubleValue();
}

inline long longValue(const BigFloat& bf)
{
  return bf.longValue();
}

} //namespace CORE

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

#include <CGAL/enable_warnings.h>

#endif // _CORE_BIGFLOAT_H_
