//
// LiDIA - a library for computational number theory
//   Copyright (c) 1995 by the LiDIA Group
//
// File        : bigfloat.h 
// Author      : Thomas Papanikolaou (TP)
// Last change : TP, Feb 7 1995, initial version
//

#ifndef LIDIA_BIGFLOAT_H
#define LIDIA_BIGFLOAT_H

#include <LiDIA/bigrational.h>
#include <LiDIA/bigfloat_config.h>
#include <LiDIA/xdouble.h>

/**
 ** A bigfloat x is represented by a pair (bigint m, long e)
 ** This pair denotes the number
 **
 **               x = m * pow(2, e)
 **
 ** where m is a bigint of size prec bits. In order to
 ** be able to work with exceptional values, each bigfloat
 ** contains a flag indicating that it is a not exact number,
 ** exact number (integer), +0, -0, +oo, -oo, overflow or underflow.
 **
 **/

enum bigfloat_value {
   NotExact=0, Exact=1, PlusZero=2, MinusZero=4, PlusInf=8, MinusInf=16, Nan=32, Overflow=64, Underflow=128
};

class bigfloat_flag
{
  bigfloat_value v;

 public:

  bigfloat_flag() { v = NotExact; }
  bigfloat_flag(const bigfloat_flag & x) { v = x.v; }
  ~bigfloat_flag() { }

#ifndef HEADBANGER

  int is_exact() const { return (v & Exact); }
  int is_not_exact() const { return !(v & Exact); }
  int is_plus_zero() const { return (v & PlusZero); }
  int is_minus_zero() const { return (v & MinusZero); }
  int is_pm_zero() const { return ((v & PlusZero) || (v & MinusZero)); }
  int is_plus_infinity() const { return (v & PlusInf); }
  int is_minus_infinity() const { return (v & MinusInf); }
  int is_nan() const { return (v & Nan); }
  int is_overflow() const { return (v & Overflow); }
  int is_underflow() const { return (v & Underflow); }
 
  const bigfloat_value flag() const
  { return v; }

  bigfloat_value & flag()
  { return v; }

#endif

};

class bigfloat : public bigfloat_flag
{
 private:

  bigint m;
  long e;
  long prec;

  /**
   ** These are the global variables for every bigfloat
   **/

  static bigfloat C_Pi;
  static bigfloat C_E;
  static bigfloat C_Euler;
  static bigfloat C_Catalan;

  static long binary_precision;
  static long decimal_precision;
  static long digit_precision;
  static int  rounding_mode;

  /**
   ** Private functions used for normalisation
   **/

  int leading_zeros() const;
  void base_digit_normalize();
  void normalize();
  void cut(bigfloat &x, long l);
  void cut(long l);

 public:

  /**
   ** constructors and destructor; we could leave out some of these
   **/

  bigfloat();
  bigfloat(int i);
  bigfloat(long l);
  bigfloat(unsigned long ul);
  bigfloat(double d);
  bigfloat(xdouble xd);
  bigfloat(const bigint & bi);
  bigfloat(const bigint & n, const bigint & d);
  bigfloat(const bigrational & br);
  bigfloat(const bigfloat & bf);
  ~bigfloat();

#ifndef HEADBANGER

  /**
   ** member functions
   **/

  bool is_zero() const;
  bool is_exact_zero() const;
  bool is_approx_zero() const;
  bool is_gt_zero() const;
  bool is_lt_zero() const;
  bool is_ge_zero() const;
  bool is_le_zero() const;
  bool is_one() const;
  int abs_compare(const bigfloat & b) const;
  int compare(const bigfloat & b) const;

  int sign() const;
  long exponent() const;
  bigint mantissa() const;
  lidia_size_t length() const;
  lidia_size_t bit_length() const;

  void bigintify(bigint & bi) const;
  bool doublify(double & d) const;
  bool xdoublify(xdouble & xd) const;
  bool intify(int & i) const;
  bool longify(long & i) const;

  void absolute_value();
  void negate();
  void assign_zero();
  void assign_exact_zero();
  void assign_one();
  void assign(int i);
  void assign(long i);
  void assign(unsigned long i);
  void assign(double d);
  void assign(xdouble d);
  void assign(const bigint & bi);
  void assign(const bigint & n, const bigint & d);
  void assign(const bigfloat & bf);
  void invert();
  void multiply_by_2();
  void divide_by_2();

#endif

  /**
   ** Precision and rounding mode setting
   **/

  static void precision(long l);
  static void set_precision(long l);
  static long get_precision();
  static void mode(int i);
  static int get_mode();

#ifndef HEADBANGER

  /**
   ** type checking
   **/

  friend bool is_char(const bigfloat & a);
  friend bool is_uchar(const bigfloat & a);
  friend bool is_short(const bigfloat & a);
  friend bool is_ushort(const bigfloat & a);
  friend bool is_int(const bigfloat & a);
  friend bool is_uint(const bigfloat & a);
  friend bool is_long(const bigfloat & a);
  friend bool is_ulong(const bigfloat & a);
  friend bool is_double(const bigfloat & a);

#endif

  /**
   ** assignment
   **/

  int operator = (int i);
  long operator = (long l);
  unsigned long operator = (unsigned long ul);
  double operator = (double d);
  xdouble operator = (xdouble d);
  bigint operator = (const bigint & bi);
  bigrational operator = (const bigrational & br);
  bigfloat & operator = (const bigfloat & bf);

  /**
   ** comparisons
   **/

  friend bool operator == (const bigfloat & a, const bigfloat & b);
  friend bool operator != (const bigfloat & a, const bigfloat & b);
  friend bool operator >  (const bigfloat & a, const bigfloat & b);
  friend bool operator >= (const bigfloat & a, const bigfloat & b);
  friend bool operator <  (const bigfloat & a, const bigfloat & b);
  friend bool operator <= (const bigfloat & a, const bigfloat & b);

  /**
   ** operator overloading
   **/

  friend bigfloat operator -  (const bigfloat & a);
  friend bigfloat operator +  (const bigfloat & a, const bigfloat & b);
  friend bigfloat operator -  (const bigfloat & a, const bigfloat & b);
  friend bigfloat operator *  (const bigfloat & a, const bigfloat & b);
  friend bigfloat operator /  (const bigfloat & a, const bigfloat & b);
  friend bigfloat operator << (const bigfloat & a, unsigned long u);
  friend bigfloat operator >> (const bigfloat & a, unsigned long u);
  
  bigfloat & operator +=  (const bigfloat & a);
  bigfloat & operator -=  (const bigfloat & a);
  bigfloat & operator *=  (const bigfloat & a);
  bigfloat & operator /=  (const bigfloat & a);
  bigfloat & operator <<= (unsigned long ui);
  bigfloat & operator >>= (unsigned long ui);

  bigfloat & operator ++ ();
  bigfloat & operator -- ();
  bool operator ! ();

#ifndef HEADBANGER

  /**
   ** Procedural versions
   **/

  friend void negate(bigfloat & a, const bigfloat & b);
  friend void invert(bigfloat & a, const bigfloat & b);
  friend void add(bigfloat & c, const bigfloat & a, const bigfloat & b);
  friend void subtract(bigfloat & c, const bigfloat & a, const bigfloat & b);
  friend void multiply(bigfloat & c, const bigfloat & a, const bigfloat & b);
  friend void divide(bigfloat & c, const bigfloat & a, const bigfloat & b);
  
  friend void add(bigfloat & c, const bigfloat & a, long i);
  friend void subtract(bigfloat & c, const bigfloat & a, long i);
  friend void multiply(bigfloat & c, const bigfloat & a, long i);
  friend void divide(bigfloat & c, const bigfloat & a, long i);
  friend void shift_left(bigfloat & c, const bigfloat & a, long i);
  friend void shift_right(bigfloat & c, const bigfloat & a, long i);
  friend void power(bigfloat & c, const bigfloat & a, long i);
  
  friend void add(bigfloat & c, long i, const bigfloat & b);
  friend void subtract(bigfloat & c, long i, const bigfloat & b);
  friend void multiply(bigfloat & c, long i, const bigfloat & b);
  friend void divide(bigfloat & c, long i, const bigfloat & a);
  friend void divide(bigfloat & c, long i, long j);
  
  friend void inc(bigfloat & c);
  friend void dec(bigfloat & c);

  friend void truncate(bigint &b, const bigfloat & a);
  friend void round(bigint &b, const bigfloat & a);
  friend void floor(bigint &b, const bigfloat & a);
  friend void ceil(bigint &b, const bigfloat & a);

  friend void truncate(bigfloat &b, const bigfloat & a);
  friend void round(bigfloat &b, const bigfloat & a);
  friend void floor(bigfloat &b, const bigfloat & a);
  friend void ceil(bigfloat &b, const bigfloat & a);

  friend void square(bigfloat & c, const bigfloat & a);
  friend void swap(bigfloat & c, bigfloat & a);

  /**
   ** Transcedental functions(procedural)
   **/

  friend void sqrt(bigfloat & c, const bigfloat & a);
  friend void exp(bigfloat & c, const bigfloat & a);
  friend void log(bigfloat & c, const bigfloat & a);
  friend void power(bigfloat & c, const bigfloat & a, const bigfloat & b);
  friend void besselj(bigfloat & c, int n, const bigfloat & a);

  /**
   ** (Inverse) trigonometrical functions(procedural)
   **/

  friend void sin(bigfloat & c, const bigfloat & a);
  friend void cos(bigfloat & c, const bigfloat & a);
  friend void tan(bigfloat & c, const bigfloat & a);
  friend void cot(bigfloat & c, const bigfloat & a);
  friend void asin(bigfloat & c, const bigfloat & a);
  friend void acos(bigfloat & c, const bigfloat & a);
  friend void atan(bigfloat & c, const bigfloat & a);
  friend void atan2(bigfloat & c, const bigfloat & a, const bigfloat & b);
  friend void acot(bigfloat & c, const bigfloat & a);

  /**
   ** (Inverse) Hyperbolic trigonometrical functions(procedural)
   **/

  friend void sinh(bigfloat & c, const bigfloat & a);
  friend void cosh(bigfloat & c, const bigfloat & a);
  friend void tanh(bigfloat & c, const bigfloat & a);
  friend void coth(bigfloat & c, const bigfloat & a);
  friend void asinh(bigfloat & c, const bigfloat & a);
  friend void acosh(bigfloat & c, const bigfloat & a);
  friend void atanh(bigfloat & c, const bigfloat & a);
  friend void acoth(bigfloat & c, const bigfloat & a);

#endif

  /**
   ** Transcedental functions
   **/

  friend bigfloat square(const bigfloat & a);
  friend bigfloat sqrt(const bigfloat & a);
  friend bigfloat exp(const bigfloat & a);
  friend bigfloat log(const bigfloat & a);

  friend bigfloat power(const bigfloat & a, const bigfloat & b);
  friend bigfloat power(const bigfloat & a, int n);
  friend bigfloat besselj(int n, const bigfloat & a);

  /**
   ** (Inverse) trigonometrical functions
   **/

  friend bigfloat sin(const bigfloat & a);
  friend bigfloat cos(const bigfloat & a);
  friend bigfloat tan(const bigfloat & a);
  friend bigfloat cot(const bigfloat & a);
  friend bigfloat asin(const bigfloat & a);
  friend bigfloat acos(const bigfloat & a);
  friend bigfloat atan(const bigfloat & a);
  friend bigfloat atan2(const bigfloat & a, const bigfloat & b);
  friend bigfloat acot(const bigfloat & a);

  /**
   ** (Inverse) Hyperbolic trigonometrical functions
   **/

  friend bigfloat sinh(const bigfloat & a);
  friend bigfloat cosh(const bigfloat & a);
  friend bigfloat tanh(const bigfloat & a);
  friend bigfloat coth(const bigfloat & a);
  friend bigfloat asinh(const bigfloat & a);
  friend bigfloat acosh(const bigfloat & a);
  friend bigfloat atanh(const bigfloat & a);
  friend bigfloat acoth(const bigfloat & a);

  /**
   ** Constants (procedural/functional)
   **/

  friend void constant_E(bigfloat &x);
  friend void constant_Pi(bigfloat &x);
  friend void constant_Euler(bigfloat &x);
  friend void constant_Catalan(bigfloat &x);

  friend bigfloat E();
  friend bigfloat Pi();
  friend bigfloat Euler();
  friend bigfloat Catalan();

  /**
   ** Functions
   **/

  friend int sign(const bigfloat & a);
  friend long exponent(const bigfloat & a);
  friend bigint mantissa(const bigfloat & a);
  friend bigfloat abs(const bigfloat & a);

  friend bigfloat inverse(const bigfloat & a);
  friend bigfloat truncate(const bigfloat & a);
  friend bigfloat round(const bigfloat & a);
  friend bigfloat floor(const bigfloat & a);
  friend bigfloat ceil(const bigfloat & a);

  /**
   ** input/output
   **/

  friend istream & operator >> (istream & in, bigfloat & a);
  friend ostream & operator << (ostream & out, const bigfloat & a);
  
  friend int string_to_bigfloat(char *s, bigfloat & a);
  friend int bigfloat_to_string(const bigfloat & a, char *s);

  /**
   ** Using fscanf/fprintf
   **/

  int scan_from_file(FILE * fp);
  int print_to_file(FILE * fp);

  void print(char *s);
};

#ifndef HEADBANGER

inline int check_overflow(long &z, long x, long y)
{
  if (x == 0) { z = y; return 0; }
  if (y == 0) { z = x; return 0; }
 
  unsigned long uz;
  int over_under = 0;
  int condition = (x > 0) * 2 + (y > 0);
  int sign = 0;
 
  switch (condition)
    {
    case 3: uz = x + y; break;
    case 2: if (x >= -y) uz = x - (-y);
      else { uz = (-y) - x; sign = 1; }
      break;
    case 1: if (-x >= y) { uz = (-x) - y; sign = 1; }
      else uz = y - (-x);
      break;
    case 0: uz = (-x) + (-y); sign = 1;
      break;
    }
  z = (long) uz;
  over_under = (int)(uz >> bits_per_base_digit_minus_1);
  if (sign) { z = -z; over_under = -over_under; }
  return over_under;
}

#endif

#endif









