// -*- indent-tabs-mode: nil -*-
/* This file is part of the KDE libraries
   Copyright (c) 2005 Klaus Niederkrueger <kniederk@math.uni-koeln.de>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include <math.h>

#include "knumber.h"
#include "knumber_priv.h"
#include "kglobal.h"

#include <cmath>
#include <cstdio>

#include <config-kcalc.h>

#include <QRegExp>


using namespace std;

KNumber const KNumber::Zero(0);
KNumber const KNumber::One(1);
KNumber const KNumber::MinusOne(-1);
KNumber const KNumber::NotDefined("nan");

bool KNumber::_float_output = false;
bool KNumber::_fraction_input = false;
bool KNumber::_splitoffinteger_output = false;

#ifndef HAVE_FUNC_ISINF

#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#else
#include <math.h>
#endif

#define isinf(x) (!finite(x) && x == x)
#endif

KNumber::KNumber(qint32 num)
{
  _num = new _knuminteger(num);
}

KNumber::KNumber(quint32 num)
{
  _num = new _knuminteger(num);
}

KNumber::KNumber(qint64 num)
{
  _num = new _knuminteger(num);
}

KNumber::KNumber(quint64 num)
{
  _num = new _knuminteger(num);
}

KNumber::KNumber(double num)
{
  if ( isinf(num) ) _num = new _knumerror( _knumber::Infinity );
  else if ( isnan(num) ) _num = new _knumerror( _knumber::UndefinedNumber );
  else _num = new _knumfloat(num);

}

KNumber::KNumber(KNumber const & num)
{
  switch(num.type()) {
  case SpecialType:
    _num = new _knumerror(*(num._num));
    return;
  case IntegerType:
    _num = new _knuminteger(*(num._num));
    return;
  case  FractionType:
    _num = new _knumfraction(*(num._num));
    return;
  case FloatType:
    _num = new _knumfloat(*(num._num));
    return;
  };
}

KNumber::KNumber(QString const & num)
{
  if (QRegExp("^(inf|-inf|nan)$").exactMatch(num))
    _num = new _knumerror(num);
  else if (QRegExp("^[+-]?\\d+$").exactMatch(num))
    _num = new _knuminteger(num);
  else if (QRegExp("^[+-]?\\d+/\\d+$").exactMatch(num)) {
    _num = new _knumfraction(num);
    simplifyRational();
  }
  else if (QRegExp("^[+-]?\\d+(\\.\\d*)?(e[+-]?\\d+)?$").exactMatch(num))
    if (_fraction_input == true) {
      _num = new _knumfraction(num);
      simplifyRational();
    } else
      _num = new _knumfloat(num);
  else
    _num = new _knumerror("nan");
}

KNumber::~KNumber()
{
  delete _num;
}

KNumber::NumType KNumber::type(void) const
{
  if(dynamic_cast<_knumerror *>(_num))
    return SpecialType;
  if(dynamic_cast<_knuminteger *>(_num))
    return IntegerType;
  if(dynamic_cast<_knumfraction *>(_num))
    return FractionType;
  if(dynamic_cast<_knumfloat *>(_num))
    return FloatType;

  return SpecialType;
}

// This method converts a fraction to an integer, whenever possible,
// i.e. 5/1 --> 5
// This method should be called, whenever such a inproper fraction can occur,
// e.g. when adding 4/3 + 2/3....
void KNumber::simplifyRational(void)
{
  if (type() != FractionType)
    return;

  _knumfraction *tmp_num = dynamic_cast<_knumfraction *>(_num);

  if (tmp_num->isInteger()) {
    _knumber *tmp_num2 = tmp_num->intPart();
    delete tmp_num;
    _num = tmp_num2;
  }

}


KNumber const & KNumber::operator=(KNumber const & num)
{
  if (this == & num)
    return *this;

  delete _num;

  switch(num.type()) {
  case SpecialType:
    _num = new _knumerror();
    break;
  case IntegerType:
    _num = new _knuminteger();
    break;
  case FractionType:
    _num = new _knumfraction();
    break;
  case FloatType:
    _num = new _knumfloat();
    break;
  };

  _num->copy(*(num._num));

  return *this;
}

KNumber & KNumber::operator +=(KNumber const &arg)
{
  KNumber tmp_num = *this + arg;

  delete _num;

  switch(tmp_num.type()) {
  case SpecialType:
    _num = new _knumerror();
    break;
  case IntegerType:
    _num = new _knuminteger();
    break;
  case FractionType:
    _num = new _knumfraction();
    break;
  case FloatType:
    _num = new _knumfloat();
    break;
  };

  _num->copy(*(tmp_num._num));

  return *this;
}

KNumber & KNumber::operator -=(KNumber const &arg)
{
  KNumber tmp_num = *this - arg;

  delete _num;

  switch(tmp_num.type()) {
  case SpecialType:
    _num = new _knumerror();
    break;
  case IntegerType:
    _num = new _knuminteger();
    break;
  case FractionType:
    _num = new _knumfraction();
    break;
  case FloatType:
    _num = new _knumfloat();
    break;
  };

  _num->copy(*(tmp_num._num));

  return *this;
}

// increase the digit at 'position' by one
static void _inc_by_one(QString &str, int position)
{
  for (int i = position; i >= 0; i--)
    {
      char last_char = str[i].toLatin1();
      switch(last_char)
	{
	case '0':
	  str[i] = '1';
	  break;
	case '1':
	  str[i] = '2';
	  break;
	case '2':
	  str[i] = '3';
	  break;
	case '3':
	  str[i] = '4';
	  break;
	case '4':
	  str[i] = '5';
	  break;
	case '5':
	  str[i] = '6';
	  break;
	case '6':
	  str[i] = '7';
	  break;
	case '7':
	  str[i] = '8';
	  break;
	case '8':
	  str[i] = '9';
	  break;
	case '9':
	  str[i] = '0';
	  if (i == 0) str.prepend('1');
	  continue;
	case '.':
	  continue;
	}
      break;
    }
}

// Cut off if more digits in fractional part than 'precision'
static void _round(QString &str, int precision)
{
  int decimalSymbolPos = str.indexOf('.');

  if (decimalSymbolPos == -1) {
    if (precision == 0) {
      return;
    } else if (precision > 0) { // add dot if missing (and needed)
      str.append('.');
      decimalSymbolPos = str.length() - 1;
    }
  }

  // fill up with more than enough zeroes (in case fractional part too short)
  str.append(QString().fill('0', precision));

  // Now decide whether to round up or down
  char last_char = str[decimalSymbolPos + precision + 1].toLatin1();
  switch (last_char)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
      // nothing to do, rounding down
      break;
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      // rounding up
      _inc_by_one(str, decimalSymbolPos + precision);
      break;
    default:
      break;
    }

  decimalSymbolPos = str.indexOf('.');
  str.truncate(decimalSymbolPos + precision + 1);

  // if precision == 0 delete also '.'
  if (precision == 0) str = str.section('.', 0, 0);
}

static QString roundNumber(const QString &numStr, int precision)
{
  QString tmpString = numStr;
  if (precision < 0  ||
      ! QRegExp("^[+-]?\\d+(\\.\\d+)*(e[+-]?\\d+)?$").exactMatch(tmpString))
    return numStr;


  // Skip the sign (for now)
  bool neg = (tmpString[0] == '-');
  if (neg  ||  tmpString[0] == '+') tmpString.remove(0, 1);


  // Split off exponential part (including 'e'-symbol)
  QString mantString = tmpString.section('e', 0, 0,
					 QString::SectionCaseInsensitiveSeps);
  QString expString = tmpString.section('e', 1, 1,
					QString::SectionCaseInsensitiveSeps |
					QString::SectionIncludeLeadingSep);
  if (expString.length() == 1) expString = QString();


  _round(mantString, precision);

  if(neg) mantString.prepend('-');

  return mantString +  expString;
}


QString const KNumber::toQString(int width, int prec) const
{
  QString tmp_str;

  if (*this == Zero) // important to avoid infinite loops below
    return "0";
  switch (type()) {
  case IntegerType:
    if (width > 0) { //result needs to be cut-off
      bool tmp_bool = _fraction_input; // stupid work-around
      _fraction_input = false;
      tmp_str = (KNumber(1.0)*(*this)).toQString(width, -1);
      _fraction_input = tmp_bool;
    } else
      tmp_str = QString(_num->ascii());
    break;
  case FractionType:
    if (_float_output) {
      bool tmp_bool = _fraction_input; // stupid work-around
      _fraction_input = false;
      tmp_str = (KNumber(1.0)*(*this)).toQString(width, -1);
      _fraction_input = tmp_bool;
    } else { // _float_output == false
      if(_splitoffinteger_output) {
	// split off integer part
	KNumber int_part = this->integerPart();
	if (int_part == Zero)
	  tmp_str = QString(_num->ascii());
	else if (int_part < Zero)
	  tmp_str = int_part.toQString() + ' ' + (int_part - *this)._num->ascii();
	else
	  tmp_str = int_part.toQString() + ' ' + (*this - int_part)._num->ascii();
      } else
	tmp_str = QString(_num->ascii());

      if (width > 0  &&  tmp_str.length() > width) {
	//result needs to be cut-off
	bool tmp_bool = _fraction_input; // stupid work-around
	_fraction_input = false;
	tmp_str = (KNumber(1.0)*(*this)).toQString(width, -1);
	_fraction_input = tmp_bool;
      }
    }

    break;
  case FloatType:
    if (width > 0)
      tmp_str = QString(_num->ascii(width));
    else
      // rough estimate for  maximal decimal precision (10^3 = 2^10)
      tmp_str = QString(_num->ascii(3*mpf_get_default_prec()/10));
    break;
  default:
    return QString(_num->ascii());
  }

  if (prec >= 0)
    return roundNumber(tmp_str, prec);
  else
    return tmp_str;
}

void KNumber::setDefaultFloatOutput(bool flag)
{
  _float_output = flag;
}

void KNumber::setDefaultFractionalInput(bool flag)
{
  _fraction_input = flag;
}

void KNumber::setSplitoffIntegerForFractionOutput(bool flag)
{
  _splitoffinteger_output = flag;
}

void KNumber::setDefaultFloatPrecision(unsigned int prec)
{
  // Need to transform decimal digits into binary digits
  unsigned long int bin_prec = static_cast<unsigned long int>
    (double(prec) * M_LN10 / M_LN2 + 1);

  mpf_set_default_prec(bin_prec);
}

KNumber const KNumber::abs(void) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->abs();

  return tmp_num;
}

KNumber const KNumber::cbrt(void) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->cbrt();

  return tmp_num;
}

KNumber const KNumber::sqrt(void) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->sqrt();

  return tmp_num;
}

KNumber const KNumber::factorial(void) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->factorial();

  return tmp_num;
}

KNumber const KNumber::integerPart(void) const
{
  KNumber tmp_num;
  delete tmp_num._num;
  tmp_num._num = _num->intPart();

  return tmp_num;
}

KNumber const KNumber::power(KNumber const &exp) const
{
  if (*this == Zero) {
    if(exp == Zero)
      return KNumber("nan"); // 0^0 not defined
    else if (exp < Zero)
      return KNumber("inf");
    else
      return KNumber(0);
  }

  if (exp == Zero) {
    if (*this != Zero)
      return One;
    else
      return KNumber("nan");
  }
  else if (exp < Zero) {
    KNumber tmp_num;
    KNumber tmp_num2 = -exp;
    delete tmp_num._num;
    tmp_num._num = _num->power(*(tmp_num2._num));

    return One/tmp_num;
  }
  else {
    KNumber tmp_num;
    delete tmp_num._num;
    tmp_num._num = _num->power(*(exp._num));

    return tmp_num;
  }

}

KNumber const KNumber::operator-(void) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->change_sign();

  return tmp_num;
}

KNumber const KNumber::operator+(KNumber const & arg2) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->add(*arg2._num);

  tmp_num.simplifyRational();

  return tmp_num;
}

KNumber const KNumber::operator-(KNumber const & arg2) const
{
  return *this + (-arg2);
}

KNumber const KNumber::operator*(KNumber const & arg2) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->multiply(*arg2._num);

  tmp_num.simplifyRational();

  return tmp_num;
}

KNumber const KNumber::operator/(KNumber const & arg2) const
{
  KNumber tmp_num;
  delete tmp_num._num;

  tmp_num._num = _num->divide(*arg2._num);

  tmp_num.simplifyRational();

  return tmp_num;
}


KNumber const KNumber::operator%(KNumber const & arg2) const
{
  if (type() != IntegerType  ||  arg2.type() != IntegerType)
    return Zero;

  KNumber tmp_num;
  delete tmp_num._num;

  _knuminteger const *tmp_arg1 = dynamic_cast<_knuminteger const *>(_num);
  _knuminteger const *tmp_arg2 = dynamic_cast<_knuminteger const *>(arg2._num);

  tmp_num._num = tmp_arg1->mod(*tmp_arg2);

  return tmp_num;
}

KNumber const KNumber::operator&(KNumber const & arg2) const
{
  if (type() != IntegerType  ||  arg2.type() != IntegerType)
    return Zero;

  KNumber tmp_num;
  delete tmp_num._num;

  _knuminteger const *tmp_arg1 = dynamic_cast<_knuminteger const *>(_num);
  _knuminteger const *tmp_arg2 = dynamic_cast<_knuminteger const *>(arg2._num);

  tmp_num._num = tmp_arg1->intAnd(*tmp_arg2);

  return tmp_num;

}

KNumber const KNumber::operator|(KNumber const & arg2) const
{
  if (type() != IntegerType  ||  arg2.type() != IntegerType)
    return Zero;

  KNumber tmp_num;
  delete tmp_num._num;

  _knuminteger const *tmp_arg1 = dynamic_cast<_knuminteger const *>(_num);
  _knuminteger const *tmp_arg2 = dynamic_cast<_knuminteger const *>(arg2._num);

  tmp_num._num = tmp_arg1->intOr(*tmp_arg2);

  return tmp_num;
}


KNumber const KNumber::operator<<(KNumber const & arg2) const
{
  if (type() != IntegerType  ||  arg2.type() != IntegerType)
    return KNumber("nan");

  _knuminteger const *tmp_arg1 = dynamic_cast<_knuminteger const *>(_num);
  _knuminteger const *tmp_arg2 = dynamic_cast<_knuminteger const *>(arg2._num);

  KNumber tmp_num;
  delete tmp_num._num;
  tmp_num._num = tmp_arg1->shift(*tmp_arg2);

  return tmp_num;
}

KNumber const KNumber::operator>>(KNumber const & arg2) const
{
  if (type() != IntegerType  ||  arg2.type() != IntegerType)
    return KNumber("nan");

  KNumber tmp_num = -arg2;

  _knuminteger const *tmp_arg1 = dynamic_cast<_knuminteger const *>(_num);
  _knuminteger const *tmp_arg2 = dynamic_cast<_knuminteger const *>(tmp_num._num);

  KNumber tmp_num2;
  delete tmp_num2._num;
  tmp_num2._num = tmp_arg1->shift(*tmp_arg2);

  return tmp_num2;
}



KNumber::operator bool(void) const
{
  if (*this == Zero)
    return false;
  return true;
}

KNumber::operator qint32(void) const
{
  return static_cast<long int>(*_num);
}

KNumber::operator quint32(void) const
{
  return static_cast<unsigned long int>(*_num);
}

KNumber::operator qint64(void) const
{
  return static_cast<long long int>(*_num);
}

KNumber::operator quint64(void) const
{
  return static_cast<unsigned long long int>(*_num);
}

KNumber::operator double(void) const
{
  return static_cast<double>(*_num);
}

int KNumber::compare(KNumber const & arg2) const
{
  return _num->compare(*arg2._num);
}

K_GLOBAL_STATIC_WITH_ARGS(KNumber, g_Pi, (
"3.141592653589793238462643383279502884197169"
"39937510582097494459230781640628620899862803"
"4825342117068") )
KNumber KNumber::Pi()
{
  return *g_Pi;
}

K_GLOBAL_STATIC_WITH_ARGS(KNumber, g_Euler, (
"2.718281828459045235360287471352662497757"
"24709369995957496696762772407663035354759"
"4571382178525166427") )
KNumber KNumber::Euler()
{
  return *g_Euler;
}
