You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
694 lines
15 KiB
694 lines
15 KiB
// -*- c-basic-offset: 2 -*-
|
|
/* 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 <config.h>
|
|
|
|
#include <tqregexp.h>
|
|
#include <tqstring.h>
|
|
|
|
#include "knumber.h"
|
|
|
|
KNumber const KNumber::Zero(0);
|
|
KNumber const KNumber::One(1);
|
|
KNumber const KNumber::MinusOne(-1);
|
|
KNumber const KNumber::Pi("3.141592653589793238462643383279502884197169"
|
|
"39937510582097494459230781640628620899862803"
|
|
"4825342117068");
|
|
KNumber const KNumber::Euler("2.718281828459045235360287471352662497757"
|
|
"24709369995957496696762772407663035354759"
|
|
"4571382178525166427");
|
|
KNumber const KNumber::NotDefined("nan");
|
|
|
|
bool KNumber::_float_output = false;
|
|
bool KNumber::_fraction_input = false;
|
|
bool KNumber::_splitoffinteger_output = false;
|
|
|
|
KNumber::KNumber(signed int num)
|
|
{
|
|
_num = new _knuminteger(num);
|
|
}
|
|
|
|
KNumber::KNumber(unsigned int num)
|
|
{
|
|
_num = new _knuminteger(num);
|
|
}
|
|
|
|
KNumber::KNumber(signed long int num)
|
|
{
|
|
_num = new _knuminteger(num);
|
|
}
|
|
|
|
KNumber::KNumber(unsigned long int num)
|
|
{
|
|
_num = new _knuminteger(num);
|
|
}
|
|
|
|
KNumber::KNumber(unsigned long long int 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(TQString const & num)
|
|
{
|
|
if (TQRegExp("^(inf|-inf|nan)$").exactMatch(num))
|
|
_num = new _knumerror(num);
|
|
else if (TQRegExp("^[+-]?\\d+$").exactMatch(num))
|
|
_num = new _knuminteger(num);
|
|
else if (TQRegExp("^[+-]?\\d+/\\d+$").exactMatch(num)) {
|
|
_num = new _knumfraction(num);
|
|
simplifyRational();
|
|
}
|
|
else if (TQRegExp("^[+-]?\\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::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(TQString &str, int position)
|
|
{
|
|
for (int i = position; i >= 0; i--)
|
|
{
|
|
char last_char = str[i].latin1();
|
|
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(TQString &str, int precision)
|
|
{
|
|
int decimalSymbolPos = str.tqfind('.');
|
|
|
|
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(TQString().fill('0', precision));
|
|
|
|
// Now decide whether to round up or down
|
|
char last_char = str[decimalSymbolPos + precision + 1].latin1();
|
|
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.tqfind('.');
|
|
str.truncate(decimalSymbolPos + precision + 1);
|
|
|
|
// if precision == 0 delete also '.'
|
|
if (precision == 0) str = str.section('.', 0, 0);
|
|
}
|
|
|
|
static TQString roundNumber(const TQString &numStr, int precision)
|
|
{
|
|
TQString tmpString = numStr;
|
|
if (precision < 0 ||
|
|
! TQRegExp("^[+-]?\\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)
|
|
TQString mantString = tmpString.section('e', 0, 0,
|
|
TQString::SectionCaseInsensitiveSeps);
|
|
TQString expString = tmpString.section('e', 1, 1,
|
|
TQString::SectionCaseInsensitiveSeps |
|
|
TQString::SectionIncludeLeadingSep);
|
|
if (expString.length() == 1) expString = TQString();
|
|
|
|
|
|
_round(mantString, precision);
|
|
|
|
if(neg) mantString.prepend('-');
|
|
|
|
return mantString + expString;
|
|
}
|
|
|
|
|
|
TQString const KNumber::toTQString(int width, int prec) const
|
|
{
|
|
TQString 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)).toTQString(width, -1);
|
|
_fraction_input = tmp_bool;
|
|
} else
|
|
tmp_str = TQString(_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)).toTQString(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 = TQString(_num->ascii());
|
|
else if (int_part < Zero)
|
|
tmp_str = int_part.toTQString() + " " + (int_part - *this)._num->ascii();
|
|
else
|
|
tmp_str = int_part.toTQString() + " " + (*this - int_part)._num->ascii();
|
|
} else
|
|
tmp_str = TQString(_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)).toTQString(width, -1);
|
|
_fraction_input = tmp_bool;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case FloatType:
|
|
if (width > 0)
|
|
tmp_str = TQString(_num->ascii(width));
|
|
else
|
|
// rough estimate for maximal decimal precision (10^3 = 2^10)
|
|
tmp_str = TQString(_num->ascii(3*mpf_get_default_prec()/10));
|
|
break;
|
|
default:
|
|
return TQString(_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::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 signed long int(void) const
|
|
{
|
|
return static_cast<signed long int>(*_num);
|
|
}
|
|
|
|
KNumber::operator unsigned long int(void) const
|
|
{
|
|
return static_cast<unsigned long int>(*_num);
|
|
}
|
|
|
|
KNumber::operator unsigned long long int(void) const
|
|
{
|
|
#if SIZEOF_UNSIGNED_LONG == 8
|
|
return static_cast<unsigned long int>(*this);
|
|
#elif SIZEOF_UNSIGNED_LONG == 4
|
|
KNumber tmp_num1 = this->abs().integerPart();
|
|
unsigned long long int tmp_num2 = static_cast<unsigned long int>(tmp_num1) +
|
|
(static_cast<unsigned long long int>(
|
|
static_cast<unsigned long int>(tmp_num1 >> KNumber("32"))) << 32) ;
|
|
|
|
#warning the cast operator from KNumber to unsigned long long int is probably buggy, when a sign is involved
|
|
if (*this > KNumber(0))
|
|
return tmp_num2;
|
|
else
|
|
return static_cast<unsigned long long int> (- static_cast<signed long long int>(tmp_num2));
|
|
#else
|
|
#error "SIZEOF_UNSIGNED_LONG is a unhandled case"
|
|
#endif
|
|
}
|
|
|
|
KNumber::operator double(void) const
|
|
{
|
|
return static_cast<double>(*_num);
|
|
}
|
|
|
|
int const KNumber::compare(KNumber const & arg2) const
|
|
{
|
|
return _num->compare(*arg2._num);
|
|
}
|