// -*- c-basic-offset: 2 -*- /* This file is part of the KDE libraries Copyright (c) 2005 Klaus Niederkrueger 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 #include #include #include #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.find('.'); 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.find('.'); 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 (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(*_num); } KNumber::operator unsigned long int(void) const { return static_cast(*_num); } KNumber::operator unsigned long long int(void) const { #if SIZEOF_UNSIGNED_LONG == 8 return static_cast(*this); #elif SIZEOF_UNSIGNED_LONG == 4 KNumber tmp_num1 = this->abs().integerPart(); unsigned long long int tmp_num2 = static_cast(tmp_num1) + (static_cast( static_cast(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 (- static_cast(tmp_num2)); #else #error "SIZEOF_UNSIGNED_LONG is a unhandled case" #endif } KNumber::operator double(void) const { return static_cast(*_num); } int const KNumber::compare(KNumber const & arg2) const { return _num->compare(*arg2._num); }