summaryrefslogtreecommitdiffstats
path: root/kcalc/knumber/knumber.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit2bda8f7717adf28da4af0d34fb82f63d2868c31d (patch)
tree8d927b7b47a90c4adb646482a52613f58acd6f8c /kcalc/knumber/knumber.cpp
downloadtdeutils-2bda8f7717adf28da4af0d34fb82f63d2868c31d.tar.gz
tdeutils-2bda8f7717adf28da4af0d34fb82f63d2868c31d.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeutils@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kcalc/knumber/knumber.cpp')
-rw-r--r--kcalc/knumber/knumber.cpp693
1 files changed, 693 insertions, 0 deletions
diff --git a/kcalc/knumber/knumber.cpp b/kcalc/knumber/knumber.cpp
new file mode 100644
index 0000000..d282b26
--- /dev/null
+++ b/kcalc/knumber/knumber.cpp
@@ -0,0 +1,693 @@
+// -*- 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 <qregexp.h>
+#include <qstring.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(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::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].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(QString &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(QString().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 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::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);
+}