/* 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 "knumber_priv.h"

#include <math.h>
#include <config-kcalc.h>
#include <stdlib.h>

#include <QRegExp>

#if defined(Q_OS_SOLARIS) && defined(__SUNPRO_CC)
// Strictly by the standard, ininf() is a c99-ism which
// is unavailable in C++. The IEEE FP headers provide
// a function with similar functionality, so use that instead.
// However, !finite(a) == isinf(a) || isnan(a), so it's 
// not 100% correct.
#include <ieeefp.h>
#define isinf(a) !finite(a)
#endif


_knumerror::_knumerror(_knumber const & num)
{
  switch(num.type()) {
  case SpecialType:
    _error = dynamic_cast<_knumerror const &>(num)._error;
    break;
  case IntegerType:
  case FractionType:
  case FloatType:
    // TODO: What should I do here?
    break;
  }
}

_knuminteger::_knuminteger(qint64 num)
{
  mpz_init(_mpz);
#if SIZEOF_SIGNED_LONG == 8
  mpz_set_si(_mpz, static_cast<signed long int>(num));
#elif SIZEOF_SIGNED_LONG == 4
  mpz_set_si(_mpz, static_cast<signed long int>(num >> 32));
  mpz_mul_2exp(_mpz, _mpz, 32);
  mpz_add_ui(_mpz, _mpz, static_cast<signed long int>(num));
#else
#error "SIZEOF_SIGNED_LONG is a unhandled case"
#endif 
}

_knuminteger::_knuminteger(quint64 num)
{
  mpz_init(_mpz);
#if SIZEOF_UNSIGNED_LONG == 8
  mpz_set_ui(_mpz, static_cast<unsigned long int>(num));
#elif SIZEOF_UNSIGNED_LONG == 4
  mpz_set_ui(_mpz, static_cast<unsigned long int>(num >> 32));
  mpz_mul_2exp(_mpz, _mpz, 32);
  mpz_add_ui(_mpz, _mpz, static_cast<unsigned long int>(num));
#else
#error "SIZEOF_UNSIGNED_LONG is a unhandled case"
#endif 
}

_knuminteger::_knuminteger(_knumber const & num)
{
  mpz_init(_mpz);

  switch(num.type()) {
  case IntegerType:
    mpz_set(_mpz, dynamic_cast<_knuminteger const &>(num)._mpz);
    break;
  case FractionType:
  case FloatType:
  case SpecialType:
    // TODO: What should I do here?
    break;
  }
}

_knumfraction::_knumfraction(_knumber const & num)
{
  mpq_init(_mpq);
  
  switch(num.type()) {
  case IntegerType:
    mpq_set_z(_mpq, dynamic_cast<_knuminteger const &>(num)._mpz);
    break;
  case FractionType:
    mpq_set(_mpq, dynamic_cast<_knumfraction const &>(num)._mpq);
    break;
  case FloatType:
  case SpecialType:
    // What should I do here?
    break;
  }
}

_knumfloat::_knumfloat(_knumber const & num)
{
  mpf_init(_mpf);
  
  switch(num.type()) {
  case IntegerType:
    mpf_set_z(_mpf, dynamic_cast<_knuminteger const &>(num)._mpz);
    break;
  case FractionType:
    mpf_set_q(_mpf, dynamic_cast<_knumfraction const &>(num)._mpq);
    break;
  case FloatType:
    mpf_set(_mpf, dynamic_cast<_knumfloat const &>(num)._mpf);
    break;
  case SpecialType:
    // What should I do here?
    break;
  }
}



_knumerror::_knumerror(QString const & num)
{
  if (num == "nan")
    _error = UndefinedNumber;
  else if (num == "inf")
    _error = Infinity;
  else if (num == "-inf")
    _error = MinusInfinity;
}

_knuminteger::_knuminteger(QString const & num)
{
  mpz_init(_mpz);
  mpz_set_str(_mpz, num.toAscii(), 10);
}

_knumfraction::_knumfraction(QString const & num)
{
  mpq_init(_mpq);
  if (QRegExp("^[+-]?\\d+(\\.\\d*)?(e[+-]?\\d+)?$").exactMatch(num)) {
    // my hand-made conversion is terrible
    // first me convert the mantissa
    unsigned long int digits_after_dot = ((num.section( '.', 1, 1)).section('e', 0, 0)).length();
    QString tmp_num = num.section('e', 0, 0).remove('.');
    mpq_set_str(_mpq, tmp_num.toAscii(), 10);
    mpz_t tmp_int;
    mpz_init(tmp_int);
    mpz_ui_pow_ui (tmp_int, 10, digits_after_dot);
    mpz_mul(mpq_denref(_mpq), mpq_denref(_mpq), tmp_int);
    // now we take care of the exponent
    if (! (tmp_num = num.section('e', 1, 1)).isEmpty()) {
      long int tmp_exp = tmp_num.toLong();
      if (tmp_exp > 0) {
	mpz_ui_pow_ui (tmp_int, 10,
		       static_cast<unsigned long int>(tmp_exp));
	mpz_mul(mpq_numref(_mpq), mpq_numref(_mpq), tmp_int);
      } else {
	mpz_ui_pow_ui (tmp_int, 10,
		       static_cast<unsigned long int>(-tmp_exp));
	mpz_mul(mpq_denref(_mpq), mpq_denref(_mpq), tmp_int);
      }
    }
    mpz_clear(tmp_int);
  } else
    mpq_set_str(_mpq, num.toAscii(), 10);
  mpq_canonicalize(_mpq);
}

_knumfloat::_knumfloat(QString const & num)
{
  mpf_init(_mpf);
  mpf_set_str(_mpf, num.toAscii(), 10);
}

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

  mpz_set(_mpz, num._mpz);
  return *this;
}

QString const _knumerror::ascii(int prec) const
{
  static_cast<void>(prec);

  // TODO: i18n these strings here and elsewhere, as they're user visible
  switch(_error) {
  case UndefinedNumber:
    return QString("nan");
  case Infinity:
    return QString("inf");
  case MinusInfinity:
    return QString("-inf");
  default:
    return QString();
  }
}

QString const _knuminteger::ascii(int prec) const
{
  static_cast<void>(prec);
  char *tmp_ptr = 0;

  // get the size of the string
  size_t size = gmp_snprintf(tmp_ptr, 0, "%Zd", _mpz) + 1;
  tmp_ptr = new char[size];
  gmp_snprintf(tmp_ptr, size, "%Zd", _mpz);

  QString ret_str = tmp_ptr;

  delete tmp_ptr;
  return ret_str;
}

QString const _knumfraction::ascii(int prec) const
{
  static_cast<void>(prec);
  char *tmp_ptr = mpq_get_str(0, 10, _mpq);
  QString ret_str = tmp_ptr;
  free(tmp_ptr);

  return ret_str;
}

QString const _knumfloat::ascii(int prec) const
{
  QString ret_str;
  char *tmp_ptr = 0;
  size_t size;
  if (prec > 0)
    size = gmp_snprintf(tmp_ptr, 0, ("%." + QString().setNum(prec) + "Fg").toAscii(), _mpf) + 1;
  else
    size = gmp_snprintf(tmp_ptr, 0, "%Fg", _mpf) + 1;
  tmp_ptr = new char[size];
  if (prec > 0)
    gmp_snprintf(tmp_ptr, size, ("%." + QString().setNum(prec) + "Fg").toAscii(), _mpf);
  else
    gmp_snprintf(tmp_ptr, size, "%Fg", _mpf);

  ret_str = tmp_ptr;

  delete tmp_ptr;

  return ret_str;
}


bool _knumfraction::isInteger(void) const
{
  if (mpz_cmp_ui(mpq_denref(_mpq), 1) == 0)
    return true;
  else
    return false;
}


_knumber * _knumerror::abs(void) const
{
  _knumerror * tmp_num = new _knumerror(*this);

  if(_error == MinusInfinity) tmp_num->_error = Infinity;

  return tmp_num;
}

_knumber * _knuminteger::abs(void) const
{
  _knuminteger * tmp_num = new _knuminteger();

  mpz_abs(tmp_num->_mpz, _mpz);

  return tmp_num;
}

_knumber * _knumfraction::abs(void) const
{
  _knumfraction * tmp_num = new _knumfraction();
  
  mpq_abs(tmp_num->_mpq, _mpq);
  
  return tmp_num;
}

_knumber * _knumfloat::abs(void) const
{
  _knumfloat * tmp_num = new _knumfloat();
  
  mpf_abs(tmp_num->_mpf, _mpf);
  
  return tmp_num;
}



_knumber * _knumerror::intPart(void) const
{
  return new _knumerror(*this);
}

_knumber * _knuminteger::intPart(void) const
{
  _knuminteger *tmp_num = new _knuminteger();
  mpz_set(tmp_num->_mpz, _mpz);
  return tmp_num;
}

_knumber * _knumfraction::intPart(void) const
{
  _knuminteger *tmp_num = new _knuminteger();

  mpz_set_q(tmp_num->_mpz, _mpq);
  
  return tmp_num;
}

_knumber * _knumfloat::intPart(void) const
{
  _knuminteger *tmp_num = new _knuminteger();

  mpz_set_f(tmp_num->_mpz, _mpf);
  
  return tmp_num;
}




int _knumerror::sign(void) const
{
  switch(_error) {
  case Infinity:
    return 1;
  case MinusInfinity:
    return -1;
  default:
    return 0;
  }
}

int _knuminteger::sign(void) const
{
  return mpz_sgn(_mpz);
}

int _knumfraction::sign(void) const
{
  return mpq_sgn(_mpq);
}

int _knumfloat::sign(void) const
{
  return mpf_sgn(_mpf);
}



#ifdef __GNUC__
#warning "_cbrt for now this is a stupid work around"
#endif
static void _cbrt(mpf_t &num)
{
#ifdef Q_CC_MSVC
  double tmp_num = pow(mpf_get_d(num), 1./3.);
#else
  double tmp_num = cbrt(mpf_get_d(num));
#endif
  mpf_init_set_d(num, tmp_num);
}


_knumber * _knumerror::cbrt(void) const
{
  // infty ^3 = infty;  -infty^3 = -infty
  _knumerror *tmp_num = new _knumerror(*this);
  
  return tmp_num;
}

_knumber * _knuminteger::cbrt(void) const
{
  _knuminteger * tmp_num = new _knuminteger();
  
  if(mpz_root(tmp_num->_mpz, _mpz, 3))
    return tmp_num; // root is perfect
  
  delete tmp_num; // root was not perfect, result will be float
  
  _knumfloat * tmp_num2 = new _knumfloat();
  mpf_set_z(tmp_num2->_mpf, _mpz);

  _cbrt(tmp_num2->_mpf);
    
  return tmp_num2;
}

_knumber * _knumfraction::cbrt(void) const
{
  _knumfraction * tmp_num = new _knumfraction();
  if (mpz_root(mpq_numref(tmp_num->_mpq), mpq_numref(_mpq), 3)
      &&  mpz_root(mpq_denref(tmp_num->_mpq), mpq_denref(_mpq), 3))
    return tmp_num; // root is perfect

  delete tmp_num; // root was not perfect, result will be float

  _knumfloat * tmp_num2 = new _knumfloat();
  mpf_set_q(tmp_num2->_mpf, _mpq);

  _cbrt(tmp_num2->_mpf);
    
  return tmp_num2;
}

_knumber * _knumfloat::cbrt(void) const
{
  _knumfloat * tmp_num = new _knumfloat(*this);

  _cbrt(tmp_num->_mpf);
  
  return tmp_num;
}




_knumber * _knumerror::sqrt(void) const
{
  _knumerror *tmp_num = new _knumerror(*this);
  
  if(_error == MinusInfinity) tmp_num->_error = UndefinedNumber;

  return tmp_num;
}

_knumber * _knuminteger::sqrt(void) const
{
  if (mpz_sgn(_mpz) < 0) {
    _knumerror *tmp_num = new _knumerror(UndefinedNumber);
    return tmp_num;
  }
  if (mpz_perfect_square_p(_mpz)) {
    _knuminteger * tmp_num = new _knuminteger();
    
    mpz_sqrt(tmp_num->_mpz, _mpz);

    return tmp_num;
  } else {
    _knumfloat * tmp_num = new _knumfloat();
    mpf_set_z(tmp_num->_mpf, _mpz);
    mpf_sqrt(tmp_num->_mpf, tmp_num->_mpf);
    
    return tmp_num;
  }
}

_knumber * _knumfraction::sqrt(void) const
{
  if (mpq_sgn(_mpq) < 0) {
    _knumerror *tmp_num = new _knumerror(UndefinedNumber);
    return tmp_num;
  }
  if (mpz_perfect_square_p(mpq_numref(_mpq))
      &&  mpz_perfect_square_p(mpq_denref(_mpq))) {
    _knumfraction * tmp_num = new _knumfraction();
    mpq_set(tmp_num->_mpq, _mpq);
    mpz_sqrt(mpq_numref(tmp_num->_mpq), mpq_numref(tmp_num->_mpq));
    mpz_sqrt(mpq_denref(tmp_num->_mpq), mpq_denref(tmp_num->_mpq));

    return tmp_num;
  } else {
    _knumfloat * tmp_num = new _knumfloat();
    mpf_set_q(tmp_num->_mpf, _mpq);
    mpf_sqrt(tmp_num->_mpf, tmp_num->_mpf);
    
    return tmp_num;
  }

  _knumfraction * tmp_num = new _knumfraction();
  
  return tmp_num;
}

_knumber * _knumfloat::sqrt(void) const
{
  if (mpf_sgn(_mpf) < 0) {
    _knumerror *tmp_num = new _knumerror(UndefinedNumber);
    return tmp_num;
  }
  _knumfloat * tmp_num = new _knumfloat();
  
  mpf_sqrt(tmp_num->_mpf, _mpf);
  
  return tmp_num;
}



_knumber * _knumerror::factorial(void) const
{
  _knumerror *tmp_num = new _knumerror(*this);

  return tmp_num;
}

_knumber * _knuminteger::factorial(void) const
{
    if (mpz_sgn(_mpz) < 0) {
	_knumerror *tmp_num = new _knumerror(UndefinedNumber);
	return tmp_num;
    }
    _knuminteger * tmp_num = new _knuminteger();

    mpz_fac_ui(tmp_num->_mpz, mpz_get_ui(_mpz));

    return tmp_num;
}

_knumber * _knumfraction::factorial(void) const
{
    if (mpq_sgn(_mpq) < 0) {
	_knumerror *tmp_num = new _knumerror(UndefinedNumber);
	return tmp_num;
    }
    _knuminteger *tmp_num = new _knuminteger();
    mpz_set_q(tmp_num->_mpz, _mpq);
    unsigned long int op = mpz_get_ui(tmp_num->_mpz);

    tmp_num = new _knuminteger();

    mpz_fac_ui(tmp_num->_mpz, op);

    return tmp_num;
}

_knumber * _knumfloat::factorial(void) const
{
    if (mpf_sgn(_mpf) < 0) {
	_knumerror *tmp_num = new _knumerror(UndefinedNumber);
	return tmp_num;
    }
    _knuminteger *tmp_num = new _knuminteger();
    mpz_set_f(tmp_num->_mpz, _mpf);
    unsigned long int op = mpz_get_ui(tmp_num->_mpz);

    tmp_num = new _knuminteger();

    mpz_fac_ui(tmp_num->_mpz, op);

    return tmp_num;
}

_knumber * _knumerror::change_sign(void) const
{
  _knumerror * tmp_num = new _knumerror();

  if(_error == Infinity) tmp_num->_error = MinusInfinity;
  if(_error == MinusInfinity) tmp_num->_error = Infinity;

  return tmp_num;
}

_knumber * _knuminteger::change_sign(void) const
{
  _knuminteger * tmp_num = new _knuminteger();

  mpz_neg(tmp_num->_mpz, _mpz);

  return tmp_num;
}

_knumber * _knumfraction::change_sign(void) const
{
  _knumfraction * tmp_num = new _knumfraction();
  
  mpq_neg(tmp_num->_mpq, _mpq);
  
  return tmp_num;
}

_knumber *_knumfloat::change_sign(void) const
{
  _knumfloat * tmp_num = new _knumfloat();
  
  mpf_neg(tmp_num->_mpf, _mpf);
  
  return tmp_num;
}


_knumber * _knumerror::reciprocal(void) const
{
  switch(_error) {
  case  Infinity:
  case  MinusInfinity:
    return new _knuminteger(0);
  case  UndefinedNumber:
  default:
    return new _knumerror(UndefinedNumber);
  }
}

_knumber * _knuminteger::reciprocal(void) const
{
  if(mpz_cmp_si(_mpz, 0) == 0) return new _knumerror(Infinity);

  _knumfraction * tmp_num = new _knumfraction(*this);

  mpq_inv(tmp_num->_mpq, tmp_num->_mpq);

  return tmp_num;
}

_knumber * _knumfraction::reciprocal() const
{
  if(mpq_cmp_si(_mpq, 0, 1) == 0) return new _knumerror(Infinity);

  _knumfraction * tmp_num = new _knumfraction();
  
  mpq_inv(tmp_num->_mpq, _mpq);
  
  return tmp_num;
}

_knumber *_knumfloat::reciprocal(void) const
{
  if(mpf_cmp_si(_mpf, 0) == 0) return new _knumerror(Infinity);

  _knumfloat * tmp_num = new _knumfloat();
  
  mpf_div(tmp_num->_mpf, _knumfloat("1.0")._mpf, _mpf);
  
  return tmp_num;
}



_knumber * _knumerror::add(_knumber const & arg2) const
{
  if (arg2.type() != SpecialType)
    return new _knumerror(_error);

  _knumerror const & tmp_arg2 = static_cast<_knumerror const &>(arg2);
  
  if (_error == UndefinedNumber  
      || tmp_arg2._error == UndefinedNumber
      || (_error == Infinity && tmp_arg2._error == MinusInfinity)
      || (_error == MinusInfinity && tmp_arg2._error == Infinity)
      )
    return new _knumerror(UndefinedNumber);      

  return new _knumerror(_error);
}

_knumber * _knuminteger::add(_knumber const & arg2) const
{
  if (arg2.type() != IntegerType)
    return arg2.add(*this);
  
  _knuminteger * tmp_num = new _knuminteger();

  mpz_add(tmp_num->_mpz, _mpz,
	  dynamic_cast<_knuminteger const &>(arg2)._mpz);

  return tmp_num;
}

_knumber * _knumfraction::add(_knumber const & arg2) const
{
  if (arg2.type() == IntegerType) {
    // need to cast arg2 to fraction
    _knumfraction tmp_num(arg2);
    return tmp_num.add(*this);
  }

  
  if (arg2.type() == FloatType  ||  arg2.type() == SpecialType)
    return arg2.add(*this);
  
  _knumfraction * tmp_num = new _knumfraction();
  
  mpq_add(tmp_num->_mpq, _mpq,
	  dynamic_cast<_knumfraction const &>(arg2)._mpq);
  
  return tmp_num;
}

_knumber *_knumfloat::add(_knumber const & arg2) const
{
  if (arg2.type() == SpecialType)
    return arg2.add(*this);

  if (arg2.type() != FloatType) {
    // need to cast arg2 to float
    _knumfloat tmp_num(arg2);
    return tmp_num.add(*this);
  }
  
  _knumfloat * tmp_num = new _knumfloat();
  
  mpf_add(tmp_num->_mpf, _mpf,
	  dynamic_cast<_knumfloat const &>(arg2)._mpf);
  
  return tmp_num;
}


_knumber * _knumerror::multiply(_knumber const & arg2) const
{
  //improve this
  switch(arg2.type()) {
  case SpecialType:
    {
      _knumerror const & tmp_arg2 = static_cast<_knumerror const &>(arg2);
      if (_error == UndefinedNumber || tmp_arg2._error == UndefinedNumber)
	return new _knumerror(UndefinedNumber);
      if ( this->sign() * arg2.sign() > 0)
	return new _knumerror(Infinity);
      else
	return new _knumerror(MinusInfinity);
    }
  case IntegerType:
  case FractionType:
  case FloatType:
    {
      int sign_arg2 = arg2.sign();
      if (_error == UndefinedNumber || sign_arg2 == 0)
	return new _knumerror(UndefinedNumber);
      if ( (_error == Infinity  &&  sign_arg2 > 0)  ||
	   (_error == MinusInfinity  &&  sign_arg2 < 0) )
	return new _knumerror(Infinity);

      return new _knumerror(MinusInfinity);
    }
  }

  return new _knumerror(_error);
}


_knumber * _knuminteger::multiply(_knumber const & arg2) const
{
  if (arg2.type() != IntegerType)
    return arg2.multiply(*this);
  
  _knuminteger * tmp_num = new _knuminteger();

  mpz_mul(tmp_num->_mpz, _mpz,
	  dynamic_cast<_knuminteger const &>(arg2)._mpz);

  return tmp_num;
}

_knumber * _knumfraction::multiply(_knumber const & arg2) const
{
  if (arg2.type() == IntegerType) {
    // need to cast arg2 to fraction
    _knumfraction tmp_num(arg2);
    return tmp_num.multiply(*this);
  }

  
  if (arg2.type() == FloatType  ||  arg2.type() == SpecialType)
    return arg2.multiply(*this);
  
  _knumfraction * tmp_num = new _knumfraction();
  
  mpq_mul(tmp_num->_mpq, _mpq,
	  dynamic_cast<_knumfraction const &>(arg2)._mpq);
  
  return tmp_num;
}

_knumber *_knumfloat::multiply(_knumber const & arg2) const
{
  if (arg2.type() == SpecialType)
    return arg2.multiply(*this);
  if (arg2.type() == IntegerType  &&
      mpz_cmp_si(dynamic_cast<_knuminteger const &>(arg2)._mpz,0) == 0)
    // if arg2 == 0 return integer 0!!
    return new _knuminteger(0);

  if (arg2.type() != FloatType) {
    // need to cast arg2 to float
    _knumfloat tmp_num(arg2);
    return tmp_num.multiply(*this);
  }
  
  _knumfloat * tmp_num = new _knumfloat();
  
  mpf_mul(tmp_num->_mpf, _mpf,
	  dynamic_cast<_knumfloat const &>(arg2)._mpf);
  
  return tmp_num;
}





_knumber * _knumber::divide(_knumber const & arg2) const
{
  _knumber * tmp_num = arg2.reciprocal();
  _knumber * rslt_num = this->multiply(*tmp_num);

  delete tmp_num;

  return rslt_num;
}

_knumber *_knumfloat::divide(_knumber const & arg2) const
{
  if(mpf_cmp_si(_mpf, 0) == 0) return new _knumerror(Infinity);

  // automatically casts arg2 to float
  _knumfloat * tmp_num = new _knumfloat(arg2);

  mpf_div(tmp_num->_mpf, _mpf, tmp_num->_mpf);
  
  return tmp_num;
}




_knumber * _knumerror::power(_knumber const & /*exponent*/) const
{
  return new _knumerror(UndefinedNumber);
}

_knumber * _knuminteger::power(_knumber const & exponent) const
{
  if (exponent.type() == IntegerType) {

    mpz_t tmp_mpz;
    mpz_init_set(tmp_mpz,
		 dynamic_cast<_knuminteger const &>(exponent)._mpz);
    
    if (! mpz_fits_ulong_p(tmp_mpz)) { // conversion wouldn't work, so
				       // use floats
      mpz_clear(tmp_mpz);
      // need to cast everything to float
      _knumfloat tmp_num1(*this), tmp_num2(exponent);
      return tmp_num1.power(tmp_num2);
    }

    unsigned long int tmp_int = mpz_get_ui(tmp_mpz);
    mpz_clear(tmp_mpz);

    _knuminteger * tmp_num = new _knuminteger();
    mpz_pow_ui(tmp_num->_mpz, _mpz, tmp_int);
    return tmp_num;
  }
  if (exponent.type() == FractionType) {
    if (mpz_sgn(_mpz) < 0)
      return new _knumerror(UndefinedNumber);
    // GMP only supports few root functions, so we need to convert
    // into signed long int
    mpz_t tmp_mpz;
    mpz_init_set(tmp_mpz,
		 mpq_denref(dynamic_cast<_knumfraction const &>(exponent)._mpq));
    
    if (! mpz_fits_ulong_p(tmp_mpz)) { // conversion wouldn't work, so
				       // use floats
      mpz_clear(tmp_mpz);
      // need to cast everything to float
      _knumfloat tmp_num1(*this), tmp_num2(exponent);
      return tmp_num1.power(tmp_num2);
    }

    unsigned long int tmp_int = mpz_get_ui(tmp_mpz);
    mpz_clear(tmp_mpz);

    // first check if result will be an integer
    _knuminteger * tmp_num = new _knuminteger();
    int flag = mpz_root(tmp_num->_mpz, _mpz, tmp_int);
    if (flag == 0) { // result is not exact
      delete tmp_num;
      // need to cast everything to float
      _knumfloat tmp_num1(*this), tmp_num2(exponent);
      return tmp_num1.power(tmp_num2);
    }

    // result is exact

    mpz_init_set(tmp_mpz,
		 mpq_numref(dynamic_cast<_knumfraction const &>(exponent)._mpq));
    
    if (! mpz_fits_ulong_p(tmp_mpz)) { // conversion wouldn't work, so
				       // use floats
      mpz_clear(tmp_mpz);
      // need to cast everything to float
      _knumfloat tmp_num1(*this), tmp_num2(exponent);
      return tmp_num1.power(tmp_num2);
    }
    tmp_int = mpz_get_ui(tmp_mpz);
    mpz_clear(tmp_mpz);

    mpz_pow_ui(tmp_num->_mpz, tmp_num->_mpz, tmp_int);

    return tmp_num;
  }
  if (exponent.type() == FloatType) {
    // need to cast everything to float
    _knumfloat tmp_num(*this);
    return tmp_num.power(exponent);
  }

  return new _knumerror(Infinity);
}

_knumber * _knumfraction::power(_knumber const & exponent) const
{
  _knuminteger tmp_num = _knuminteger();

  mpz_set(tmp_num._mpz, mpq_numref(_mpq));
  _knumber *numer = tmp_num.power(exponent);

  mpz_set(tmp_num._mpz, mpq_denref(_mpq));
  _knumber *denom = tmp_num.power(exponent);

  _knumber *result;

  if (numer->type() == SpecialType) {
    result = new _knumerror(*numer);
  } else if (denom->type() == SpecialType) {
    result = new _knumerror(*denom);
  } else {
    result = numer->divide(*denom);
  }

  delete numer;
  delete denom;
  return result;
}

_knumber * _knumfloat::power(_knumber const & exponent) const
{
  double result = pow(static_cast<double>(*this),
			static_cast<double>(exponent));
  if (isnan(result)) {
    return new _knumerror(UndefinedNumber);
  }
  if (isinf(result)) {
    return new _knumerror(Infinity);
  }

  return new _knumfloat(result);
}


int _knumerror::compare(_knumber const &arg2) const
{
  if (arg2.type() != SpecialType) {
    switch(_error) {
    case Infinity:
      return 1;
    case MinusInfinity:
      return -1;
    default:
      return 1; // Not really o.k., but what should I return
    }
  }

  switch(_error) {
  case Infinity:
    if (dynamic_cast<_knumerror const &>(arg2)._error == Infinity)
      // Infinity is larger than anything else, but itself
      return 0;
    return 1;
  case MinusInfinity:
    if (dynamic_cast<_knumerror const &>(arg2)._error == MinusInfinity)
      // MinusInfinity is smaller than anything else, but itself
      return 0;
    return -1;
  default:
    if (dynamic_cast<_knumerror const &>(arg2)._error == UndefinedNumber)
      // Undefined only equal to itself
      return 0;
    return -arg2.compare(*this);
  }
}

int _knuminteger::compare(_knumber const &arg2) const
{
  if (arg2.type() != IntegerType)
    return - arg2.compare(*this);

  return mpz_cmp(_mpz, dynamic_cast<_knuminteger const &>(arg2)._mpz);
}

int _knumfraction::compare(_knumber const &arg2) const
{
  if (arg2.type() != FractionType) {
    if (arg2.type() == IntegerType) {
      mpq_t tmp_frac;
      mpq_init(tmp_frac);
      mpq_set_z(tmp_frac,
		dynamic_cast<_knuminteger const &>(arg2)._mpz);
      int cmp_result =  mpq_cmp(_mpq, tmp_frac);
      mpq_clear(tmp_frac);
      return cmp_result;
    } else
      return - arg2.compare(*this);
  }

  return mpq_cmp(_mpq, dynamic_cast<_knumfraction const &>(arg2)._mpq);
}

int _knumfloat::compare(_knumber const &arg2) const
{
  if (arg2.type() != FloatType) {
    mpf_t tmp_float;
    if (arg2.type() == IntegerType) {
      mpf_init(tmp_float);
      mpf_set_z(tmp_float,
		dynamic_cast<_knuminteger const &>(arg2)._mpz);
    } else if (arg2.type() == FractionType) {
      mpf_init(tmp_float);
      mpf_set_q(tmp_float,
		dynamic_cast<_knumfraction const &>(arg2)._mpq);
    } else
      return - arg2.compare(*this);
    
    int cmp_result =  mpf_cmp(_mpf, tmp_float);
    mpf_clear(tmp_float);
    return cmp_result;
  }

  return mpf_cmp(_mpf, dynamic_cast<_knumfloat const &>(arg2)._mpf);
}



_knumerror::operator long int (void) const
{
  // what would be the correct return values here?
  if (_error == Infinity)
    return 0;
  if (_error == MinusInfinity)
    return 0;
  else // if (_error == UndefinedNumber)
    return 0;
}

_knumerror::operator unsigned long int (void) const
{
  // what would be the correct return values here?
  if (_error == Infinity)
    return 0;
  if (_error == MinusInfinity)
    return 0;
  else // if (_error == UndefinedNumber)
    return 0;
}

_knumerror::operator long long int (void) const
{
  // what would be the correct return values here?
  if (_error == Infinity)
    return 0;
  if (_error == MinusInfinity)
    return 0;
  else // if (_error == UndefinedNumber)
    return 0;
}

_knumerror::operator unsigned long long int (void) const
{
  // what would be the correct return values here?
  if (_error == Infinity)
    return 0;
  if (_error == MinusInfinity)
    return 0;
  else // if (_error == UndefinedNumber)
    return 0;
}

_knuminteger::operator long int (void) const
{
  return mpz_get_si(_mpz);
}

_knumfraction::operator long int (void) const
{
  return static_cast<long int>(mpq_get_d(_mpq));
}

_knumfloat::operator long int (void) const
{
  return mpf_get_si(_mpf);
}

_knuminteger::operator unsigned long int (void) const
{
  return mpz_get_ui(_mpz);
}

_knuminteger::operator long long int (void) const
{
  // libgmp doesn't have long long conversion
  // so convert to string and then to long long
  char *tmpchar = new char[mpz_sizeinbase (_mpz, 10) + 2];
  mpz_get_str(tmpchar, 10, _mpz);
  QString tmpstring(tmpchar);
  free(tmpchar);
  bool ok;
  long long int value = tmpstring.toLongLong(&ok, 10);

  if (!ok) {
    // TODO: what to do if error?
    value = 0;
  }
  return value;
}

_knuminteger::operator unsigned long long int (void) const
{
  // libgmp doesn't have unsigned long long conversion
  // so convert to string and then to unsigned long long
  char *tmpchar = new char[mpz_sizeinbase (_mpz, 10) + 2];
  mpz_get_str(tmpchar, 10, _mpz);
  QString tmpstring(tmpchar);
  free(tmpchar);

  bool ok;
  unsigned long long int value;
  if (sign() < 0) {
      long long signedvalue = tmpstring.toLongLong(&ok, 10);
      value = static_cast<unsigned long long>(signedvalue);
  } else {
      value = tmpstring.toULongLong(&ok, 10);
  }

  if (!ok) {
    // TODO: what to do if error?
    value = 0;
  }
  return value;
}

_knumfraction::operator unsigned long int (void) const
{
  return static_cast<unsigned long int>(mpq_get_d(_mpq));
}

_knumfraction::operator long long int (void) const
{
  return static_cast<long long int>(mpq_get_d(_mpq));
}

_knumfraction::operator unsigned long long int (void) const
{
  return static_cast<unsigned long long int>(mpq_get_d(_mpq));
}

_knumfloat::operator unsigned long int (void) const
{
  return mpf_get_ui(_mpf);
}

_knumfloat::operator long long int (void) const
{
  return static_cast<long long int>(mpf_get_si(_mpf));
}

_knumfloat::operator unsigned long long int (void) const
{
  return static_cast<unsigned long long int>(mpf_get_ui(_mpf));
}

_knumerror::operator double (void) const
{
  if (_error == Infinity)
    return INFINITY;
  if (_error == MinusInfinity)
    return -INFINITY;
  else // if (_error == UndefinedNumber)
    return NAN;
}

_knuminteger::operator double (void) const
{
  return mpz_get_d(_mpz);
}

_knumfraction::operator double (void) const
{
  return mpq_get_d(_mpq);
}

_knumfloat::operator double (void) const
{
  return mpf_get_d(_mpf);
}




_knuminteger * _knuminteger::intAnd(_knuminteger const &arg2) const
{
  _knuminteger * tmp_num = new _knuminteger();

  mpz_and(tmp_num->_mpz, _mpz, arg2._mpz);
  
  return tmp_num;
}

_knuminteger * _knuminteger::intOr(_knuminteger const &arg2) const
{
  _knuminteger * tmp_num = new _knuminteger();

  mpz_ior(tmp_num->_mpz, _mpz, arg2._mpz);
  
  return tmp_num;
}

_knumber * _knuminteger::mod(_knuminteger const &arg2) const
{
  if(mpz_cmp_si(arg2._mpz, 0) == 0) return new _knumerror(UndefinedNumber);

  _knuminteger * tmp_num = new _knuminteger();

  mpz_mod(tmp_num->_mpz, _mpz, arg2._mpz);
  
  return tmp_num;
}

_knumber * _knuminteger::shift(_knuminteger const &arg2) const
{
  mpz_t tmp_mpz;

  mpz_init_set (tmp_mpz, arg2._mpz);

  if (! mpz_fits_slong_p(tmp_mpz)) {
    mpz_clear(tmp_mpz);
    return new _knumerror(UndefinedNumber);
  }
  
  signed long int tmp_arg2 = mpz_get_si(tmp_mpz);
  mpz_clear(tmp_mpz);


  _knuminteger * tmp_num = new _knuminteger();

  if (tmp_arg2 > 0)  // left shift
    mpz_mul_2exp(tmp_num->_mpz, _mpz, tmp_arg2);
  else  // right shift
    mpz_tdiv_q_2exp(tmp_num->_mpz, _mpz, -tmp_arg2);


  return tmp_num;
}

