diff options
Diffstat (limited to 'kcalc/knumber')
-rw-r--r-- | kcalc/knumber/Makefile.am | 16 | ||||
-rw-r--r-- | kcalc/knumber/configure.in.in | 73 | ||||
-rw-r--r-- | kcalc/knumber/knumber.cpp | 693 | ||||
-rw-r--r-- | kcalc/knumber/knumber.h | 289 | ||||
-rw-r--r-- | kcalc/knumber/knumber_priv.cpp | 1083 | ||||
-rw-r--r-- | kcalc/knumber/knumber_priv.h | 313 | ||||
-rw-r--r-- | kcalc/knumber/tests/Makefile.am | 32 | ||||
-rw-r--r-- | kcalc/knumber/tests/knumbertest.cpp | 582 | ||||
-rw-r--r-- | kcalc/knumber/tests/knumbertest.h | 9 |
9 files changed, 3090 insertions, 0 deletions
diff --git a/kcalc/knumber/Makefile.am b/kcalc/knumber/Makefile.am new file mode 100644 index 0000000..6e5673b --- /dev/null +++ b/kcalc/knumber/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS=-D_GNU_SOURCE -D_ISOC99_SOURCE $(all_includes) + +SUBDIRS = tests + +bin_PROGRAMS = +lib_LTLIBRARIES = +noinst_LTLIBRARIES = libknumber.la + +libknumber_la_SOURCES = knumber.cpp knumber_priv.cpp +libknumber_la_LIBADD = $(LIBGMP) -lm + +METASOURCES = AUTO + +noinst_HEADERS = knumber.h knumber_priv.h + +include ../../admin/Doxyfile.am diff --git a/kcalc/knumber/configure.in.in b/kcalc/knumber/configure.in.in new file mode 100644 index 0000000..8ef670b --- /dev/null +++ b/kcalc/knumber/configure.in.in @@ -0,0 +1,73 @@ +AC_DEFUN([KDE_C_LONG_DOUBLE], +[ + AC_CACHE_CHECK(for long double, ac_cv_c_long_double, + [ + ac_save_LIBS="$LIBS" + LIBS="-lm $LIBS" + AC_TRY_RUN( + [ +#define _ISOC99_SOURCE 1 +#define _GNU_SOURCE 1 +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +int main() { +/* The Stardent Vistra knows sizeof(long double), but does not support it. */ +long double foo = 1.0; +char buffer[10]; +/* On Ultrix 4.3 cc, long double is 4 and double is 8. */ +int result = (sizeof(long double) <= sizeof(double)); +/* the following is needed for a broken printf in glibc2 */ +if (!result) { + foo = foo * 3; + sprintf(buffer,"%0.0Lf",foo); + result = strcmp(buffer, "3"); +/* and now something mean ;-) */ + foo = powl(fabsl(foo), 1); +} +exit(result); } + ], + ac_cv_c_long_double=yes, ac_cv_c_long_double=no, + ac_cv_c_long_double=no + ) + LIBS="$ac_save_LIBS" + ]) + if test $ac_cv_c_long_double = yes; then + AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have support for long double in printf]) + fi +]) +KDE_C_LONG_DOUBLE + +have_l_funcs=yes +AC_CHECK_LIB(m, sqrtl,,have_l_funcs=no) + +if test "xyes" = "x$have_l_funcs" ; then + AC_DEFINE(HAVE_L_FUNCS,1,[Define if you have *l math functions (absl, ...)]) +fi + +LIBGMP= +KDE_CHECK_HEADER([gmp.h], [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + + kde_save_LIBS=$LIBS + LIBS="$all_libraries -lgmp" + AC_TRY_LINK(dnl + [ + #include <gmp.h> + ], + [ + mpz_t _mpz; + mpz_init_set_si(_mpz, 0); + ], + [LIBGMP="-lgmp"], + [ + DO_NOT_COMPILE="$DO_NOT_COMPILE kcalc" + ]) + LIBS=$kde_save_LIBS + AC_LANG_RESTORE + +]) +AC_SUBST(LIBGMP) 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); +} diff --git a/kcalc/knumber/knumber.h b/kcalc/knumber/knumber.h new file mode 100644 index 0000000..489a579 --- /dev/null +++ b/kcalc/knumber/knumber.h @@ -0,0 +1,289 @@ +// -*- 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. +*/ +#ifndef _KNUMBER_H +#define _KNUMBER_H + +#include <kdelibs_export.h> + +#include "knumber_priv.h" + +class QString; + +/** + * + * @short Class that provides arbitrary precision numbers + * + * KNumber provides access to arbitrary precision numbers from within + * KDE. + * + * KNumber is based on the GMP (GNU Multiprecision) library and + * provides transparent support to integer, fractional and floating + * point number. It contains rudimentary error handling, and also + * includes methods for converting the numbers to QStrings for + * output, and to read QStrings to obtain a KNumber. + * + * The different types of numbers that can be represented by objects + * of this class will be described below: + * + * @li @p NumType::SpecialType - This type represents an error that + * has occurred, e.g. trying to divide 1 by 0 gives an object that + * represents infinity. + * + * @li @p NumType::IntegerType - The number is an integer. It can be + * arbitrarily large (restricted by the memory of the system). + * + * @li @p NumType::FractionType - A fraction is a number of the form + * denominator divided by nominator, where both denominator and + * nominator are integers of arbitrary size. + * + * @li @p NumType::FloatType - The number is of floating point + * type. These numbers are usually rounded, so that they do not + * represent precise values. + * + * + * @author Klaus Niederkrueger <kniederk@math.uni-koeln.de> + */ +class KDE_EXPORT KNumber +{ + public: + static KNumber const Zero; + static KNumber const One; + static KNumber const MinusOne; + static KNumber const Pi; + static KNumber const Euler; + static KNumber const NotDefined; + + /** + * KNumber tries to provide transparent access to the following type + * of numbers: + * + * @li @p NumType::SpecialType - Some type of error has occurred, + * further inspection with @p KNumber::ErrorType + * + * @li @p NumType::IntegerType - the number is an integer + * + * @li @p NumType::FractionType - the number is a fraction + * + * @li @p NumType::FloatType - the number is of floating point type + * + */ + enum NumType {SpecialType, IntegerType, FractionType, FloatType}; + + /** + * A KNumber that represents an error, i.e. that is of type @p + * NumType::SpecialType can further distinguished: + * + * @li @p ErrorType::UndefinedNumber - This is e.g. the result of + * taking the square root of a negative number or computing + * \f$ \infty - \infty \f$. + * + * @li @p ErrorType::Infinity - Such a number can be e.g. obtained + * by dividing 1 by 0. Some further calculations are still allowed, + * e.g. \f$ \infty + 5 \f$ still gives \f$\infty\f$. + * + * @li @p ErrorType::MinusInfinity - MinusInfinity behaves similarly + * to infinity above. It can be obtained by changing the sign of + * infinity. + * + */ + enum ErrorType {UndefinedNumber, Infinity, MinusInfinity}; + + KNumber(signed int num = 0); + KNumber(unsigned int num); + KNumber(signed long int num); + KNumber(unsigned long int num); + KNumber(unsigned long long int num); + + KNumber(double num); + + KNumber(KNumber const & num); + + KNumber(QString const & num); + + ~KNumber() + { + delete _num; + } + + KNumber const & operator=(KNumber const & num); + + /** + * Returns the type of the number, as explained in @p KNumber::NumType. + */ + NumType type(void) const; + + /** + * Set whether the output of numbers (with KNumber::toQString) + * should happen as floating point numbers or not. This method has + * in fact only an effect on numbers of type @p + * NumType::FractionType, which can be either displayed as fractions + * or in decimal notation. + * + * The default behavior is not to display fractions in floating + * point notation. + */ + static void setDefaultFloatOutput(bool flag); + + /** + * Set whether a number constructed from a QString should be + * initialized as a fraction or as a float, e.g. "1.01" would be + * treated as 101/100, if this flag is set to true. + * + * The default setting is false. + */ + static void setDefaultFractionalInput(bool flag); + + /** + * Set the default precision to be *at least* @p prec (decimal) + * digits. All subsequent initialized floats will use at least this + * precision, but previously initialized variables are unaffected. + */ + static void setDefaultFloatPrecision(unsigned int prec); + + /** + * What a terrible method name!! When displaying a fraction, the + * default mode gives @p "nomin/denom". With this method one can + * choose to display a fraction as @p "integer nomin/denom". + * + * Examples: Default representation mode is 47/17, but if @p flag is + * @p true, then the result is 2 13/17. + */ + static void setSplitoffIntegerForFractionOutput(bool flag); + + /** + * Return a QString representing the KNumber. + * + * @param width This number specifies the maximal length of the + * output, before the method switches to exponential notation and + * does rounding. For negative numbers, this option is ignored. + * + * @param prec This parameter controls the number of digits + * following the decimal point. For negative numbers, this option + * is ignored. + * + */ + QString const toQString(int width = -1, int prec = -1) const; + + /** + * Compute the absolute value, i.e. @p x.abs() returns the value + * + * \f[ \left\{\begin{array}{cl} x, & x \ge 0 \\ -x, & x < + * 0\end{array}\right.\f] + * This method works for \f$ x = \infty \f$ and \f$ x = -\infty \f$. + */ + KNumber const abs(void) const; + + /** + * Compute the square root. If \f$ x < 0 \f$ (including \f$ + * x=-\infty \f$), then @p x.sqrt() returns @p + * ErrorType::UndefinedNumber. + * + * If @p x is an integer or a fraction, then @p x.sqrt() tries to + * compute the exact square root. If the square root is not a + * fraction, then a float with the default precision is returned. + * + * This method works for \f$ x = \infty \f$ giving \f$ \infty \f$. + */ + KNumber const sqrt(void) const; + + /** + * Compute the cube root. + * + * If @p x is an integer or a fraction, then @p x.cbrt() tries to + * compute the exact cube root. If the cube root is not a fraction, + * then a float is returned, but + * + * WARNING: A float cube root is computed as a standard @p double + * that is later transformed back into a @p KNumber. + * + * This method works for \f$ x = \infty \f$ giving \f$ \infty \f$, + * and for \f$ x = -\infty \f$ giving \f$ -\infty \f$. + */ + KNumber const cbrt(void) const; + + /** + * Truncates a @p KNumber to its integer type returning a number of + * type @p NumType::IntegerType. + * + * If \f$ x = \pm\infty \f$, integerPart leaves the value unchanged, + * i.e. it returns \f$ \pm\infty \f$. + */ + KNumber const integerPart(void) const; + + KNumber const power(KNumber const &exp) const; + + KNumber const operator+(KNumber const & arg2) const; + KNumber const operator -(void) const; + KNumber const operator-(KNumber const & arg2) const; + KNumber const operator*(KNumber const & arg2) const; + KNumber const operator/(KNumber const & arg2) const; + KNumber const operator%(KNumber const & arg2) const; + + KNumber const operator&(KNumber const & arg2) const; + KNumber const operator|(KNumber const & arg2) const; + KNumber const operator<<(KNumber const & arg2) const; + KNumber const operator>>(KNumber const & arg2) const; + + operator bool(void) const; + operator signed long int(void) const; + operator unsigned long int(void) const; + operator unsigned long long int(void) const; + operator double(void) const; + + bool const operator==(KNumber const & arg2) const + { return (compare(arg2) == 0); } + + bool const operator!=(KNumber const & arg2) const + { return (compare(arg2) != 0); } + + bool const operator>(KNumber const & arg2) const + { return (compare(arg2) > 0); } + + bool const operator<(KNumber const & arg2) const + { return (compare(arg2) < 0); } + + bool const operator>=(KNumber const & arg2) const + { return (compare(arg2) >= 0); } + + bool const operator<=(KNumber const & arg2) const + { return (compare(arg2) <= 0); } + + KNumber & operator +=(KNumber const &arg); + KNumber & operator -=(KNumber const &arg); + + + //KNumber const toFloat(void) const; + + + + + private: + void simplifyRational(void); + int const compare(KNumber const & arg2) const; + + _knumber *_num; + static bool _float_output; + static bool _fraction_input; + static bool _splitoffinteger_output; +}; + + + +#endif // _KNUMBER_H diff --git a/kcalc/knumber/knumber_priv.cpp b/kcalc/knumber/knumber_priv.cpp new file mode 100644 index 0000000..1326d44 --- /dev/null +++ b/kcalc/knumber/knumber_priv.cpp @@ -0,0 +1,1083 @@ +/* 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> + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#include <qregexp.h> +#include <qstring.h> + +#include "knumber_priv.h" + +_knumerror::_knumerror(_knumber const & num) +{ + switch(num.type()) { + case SpecialType: + _error = dynamic_cast<_knumerror const &>(num)._error; + break; + case IntegerType: + case FractionType: + case FloatType: + // What should I do here? + break; + } +} + + + +_knuminteger::_knuminteger(unsigned long long int num) +{ + mpz_init(_mpz); +#if SIZEOF_UNSIGNED_LONG == 8 + mpz_init_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: + // 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.ascii(), 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.ascii(), 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.ascii(), 10); + mpq_canonicalize(_mpq); +} + +_knumfloat::_knumfloat(QString const & num) +{ + mpf_init(_mpf); + mpf_set_str(_mpf, num.ascii(), 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); + + switch(_error) { + case UndefinedNumber: + return QString("nan"); + case Infinity: + return QString("inf"); + case MinusInfinity: + return QString("-inf"); + default: + return QString::null; + } +} + +QString const _knuminteger::ascii(int prec) const +{ + static_cast<void>(prec); + char *tmp_ptr; + + gmp_asprintf(&tmp_ptr, "%Zd", _mpz); + QString ret_str = tmp_ptr; + + free(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; + if (prec > 0) + gmp_asprintf(&tmp_ptr, ("%." + QString().setNum(prec) + "Fg").ascii(), _mpf); + else + gmp_asprintf(&tmp_ptr, "%Fg", _mpf); + + ret_str = tmp_ptr; + + free(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); +} + + + +#warning _cbrt for now this is a stupid work around +static void _cbrt(mpf_t &num) +{ + double tmp_num = cbrt(mpf_get_d(num)); + 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::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 = dynamic_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 = dynamic_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 +{ + static_cast<void>(exponent); + 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 = numer->divide(*denom); + delete numer; + delete denom; + return result; +} + +_knumber * _knumfloat::power(_knumber const & exponent) const +{ + return new _knumfloat(pow(static_cast<double>(*this), + static_cast<double>(exponent))); +} + + +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 signed 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; +} + + +_knuminteger::operator signed long int (void) const +{ + return mpz_get_si(_mpz); +} + +_knumfraction::operator signed long int (void) const +{ + return static_cast<signed long int>(mpq_get_d(_mpq)); +} + +_knumfloat::operator signed long int (void) const +{ + return mpf_get_si(_mpf); +} + +_knuminteger::operator unsigned long int (void) const +{ + return mpz_get_ui(_mpz); +} + +_knumfraction::operator unsigned long int (void) const +{ + return static_cast<unsigned long int>(mpq_get_d(_mpq)); +} + +_knumfloat::operator unsigned long int (void) const +{ + return 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; +} + diff --git a/kcalc/knumber/knumber_priv.h b/kcalc/knumber/knumber_priv.h new file mode 100644 index 0000000..7dd58a6 --- /dev/null +++ b/kcalc/knumber/knumber_priv.h @@ -0,0 +1,313 @@ +/* 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. +*/ +#ifndef _KNUMBER_PRIV_H +#define _KNUMBER_PRIV_H + +class QString; + +#include <cstdio> +#include <gmp.h> + +// work-around for pre-C99-libs +#ifndef INFINITY +#define INFINITY HUGE_VAL +#endif +// this is really ugly +#ifndef NAN +#define NAN (atof("nan")) +#endif + +class _knumber +{ + public: + enum NumType {SpecialType, IntegerType, FractionType, FloatType}; + enum ErrorType {UndefinedNumber, Infinity, MinusInfinity}; + + _knumber() {} + + virtual ~_knumber() {} + + virtual void copy(_knumber const & num) = 0; + + virtual NumType type(void) const = 0; + + virtual QString const ascii(int prec = -1) const = 0; + + virtual _knumber * abs(void) const = 0; + virtual _knumber * intPart(void) const = 0; + virtual int sign(void) const = 0; + virtual _knumber * sqrt(void) const = 0; + virtual _knumber * cbrt(void) const = 0; + virtual _knumber * change_sign(void) const = 0; + virtual _knumber * reciprocal(void) const = 0; + virtual _knumber * add(_knumber const & arg2) const = 0; + virtual _knumber * multiply(_knumber const & arg2) const = 0; + _knumber * divide(_knumber const & arg2) const; + + virtual _knumber * power(_knumber const & exponent) const = 0; + + virtual int compare(_knumber const &arg2) const = 0; + + virtual operator signed long int (void) const = 0; + virtual operator unsigned long int (void) const = 0; + virtual operator double (void) const = 0; +}; + + + +class _knumerror : public _knumber +{ + public: + _knumerror(ErrorType error = UndefinedNumber) + : _error(error) { } + + _knumerror(_knumber const & num); + + _knumerror(const QString & num); + + //virtual ~_knumerror() { } + + _knumerror const & operator = (_knumerror const & num); + + virtual void copy(_knumber const & num) + { + _error = dynamic_cast<_knumerror const &>(num)._error; + } + + virtual NumType type(void) const {return SpecialType;} + + virtual QString const ascii(int prec = -1) const; + + virtual _knumber * abs(void) const; + virtual _knumber * intPart(void) const; + virtual int sign(void) const; + virtual _knumber * cbrt(void) const; + virtual _knumber * sqrt(void) const; + virtual _knumber * change_sign(void) const; + virtual _knumber * reciprocal(void) const; + virtual _knumber * add(_knumber const & arg2) const; + virtual _knumber * multiply(_knumber const & arg2) const; + + virtual _knumber * power(_knumber const & exponent) const; + + virtual int compare(_knumber const &arg2) const; + + virtual operator signed long int (void) const; + virtual operator unsigned long int (void) const; + virtual operator double (void) const; + + private: + + ErrorType _error; + + friend class _knuminteger; + friend class _knumfraction; + friend class _knumfloat; +}; + + + +class _knuminteger : public _knumber +{ + public: + _knuminteger(signed int num = 0) + { + mpz_init_set_si(_mpz, num); + } + + _knuminteger(unsigned int num) + { + mpz_init_set_ui(_mpz, num); + } + + _knuminteger(signed long int num) + { + mpz_init_set_si(_mpz, num); + } + + _knuminteger(unsigned long int num) + { + mpz_init_set_ui(_mpz, num); + } + + _knuminteger(unsigned long long int num); + + _knuminteger(_knumber const & num); + + _knuminteger(const QString & num); + + virtual ~_knuminteger() + { + mpz_clear(_mpz); + } + + _knuminteger const & operator = (_knuminteger const & num); + + virtual void copy(_knumber const & num) + { + mpz_set(_mpz, dynamic_cast<_knuminteger const &>(num)._mpz); + } + + virtual NumType type(void) const {return IntegerType;} + + virtual QString const ascii(int prec = -1) const; + + virtual _knumber * abs(void) const; + virtual _knumber * intPart(void) const; + virtual int sign(void) const; + virtual _knumber * cbrt(void) const; + virtual _knumber * sqrt(void) const; + virtual _knumber * change_sign(void) const; + virtual _knumber * reciprocal(void) const; + virtual _knumber * add(_knumber const & arg2) const; + virtual _knumber * multiply(_knumber const & arg2) const; + + virtual int compare(_knumber const &arg2) const; + + virtual _knumber * power(_knumber const & exponent) const; + + virtual operator signed long int (void) const; + virtual operator unsigned long int (void) const; + virtual operator double (void) const; + + _knuminteger * intAnd(_knuminteger const &arg2) const; + _knuminteger * intOr(_knuminteger const &arg2) const; + _knumber * mod(_knuminteger const &arg2) const; + _knumber * shift(_knuminteger const &arg2) const; + + private: + mpz_t _mpz; + + friend class _knumfraction; + friend class _knumfloat; +}; + + + +class _knumfraction : public _knumber +{ + public: + + _knumfraction(signed long int nom = 0, signed long int denom = 1) + { + mpq_init(_mpq); + mpq_set_si(_mpq, nom, denom); + mpq_canonicalize(_mpq); + } + + _knumfraction(_knumber const & num); + + _knumfraction(QString const & num); + + virtual ~_knumfraction() + { + mpq_clear(_mpq); + } + + virtual void copy(_knumber const & num) + { + mpq_set(_mpq, dynamic_cast<_knumfraction const &>(num)._mpq); + } + + virtual NumType type(void) const {return FractionType;} + + virtual QString const ascii(int prec = -1) const; + + bool isInteger(void) const; + + virtual _knumber * abs(void) const; + virtual _knumber * intPart(void) const; + virtual int sign(void) const; + virtual _knumber * cbrt(void) const; + virtual _knumber * sqrt(void) const; + virtual _knumber * change_sign(void) const; + virtual _knumber * reciprocal(void) const; + virtual _knumber * add(_knumber const & arg2) const; + virtual _knumber * multiply(_knumber const & arg2) const; + + virtual _knumber * power(_knumber const & exponent) const; + + virtual int compare(_knumber const &arg2) const; + + virtual operator signed long int (void) const; + virtual operator unsigned long int (void) const; + virtual operator double (void) const; + + private: + mpq_t _mpq; + + friend class _knuminteger; + friend class _knumfloat; +}; + +class _knumfloat : public _knumber +{ + public: + _knumfloat(double num = 1.0) + { + mpf_init(_mpf); + mpf_set_d(_mpf, num); + } + + _knumfloat(_knumber const & num); + + _knumfloat(QString const & num); + + virtual ~_knumfloat() + { + mpf_clear(_mpf); + } + + virtual void copy(_knumber const & num) + { + mpf_set(_mpf, dynamic_cast<_knumfloat const &>(num)._mpf); + } + + virtual NumType type(void) const {return FloatType;} + + virtual QString const ascii(int prec = -1) const; + + virtual _knumber * abs(void) const; + virtual _knumber * intPart(void) const; + virtual int sign(void) const; + virtual _knumber * cbrt(void) const; + virtual _knumber * sqrt(void) const; + virtual _knumber * change_sign(void) const; + virtual _knumber * reciprocal(void) const; + virtual _knumber * add(_knumber const & arg2) const; + virtual _knumber * multiply(_knumber const & arg2) const; + virtual _knumber * divide(_knumber const & arg2) const; + + virtual _knumber * power(_knumber const & exponent) const; + + virtual int compare(_knumber const &arg2) const; + + virtual operator signed long int (void) const; + virtual operator unsigned long int (void) const; + virtual operator double (void) const; + + private: + mpf_t _mpf; + + friend class _knuminteger; + friend class _knumfraction; +}; + + +#endif // _KNUMBER_PRIV_H diff --git a/kcalc/knumber/tests/Makefile.am b/kcalc/knumber/tests/Makefile.am new file mode 100644 index 0000000..73b0600 --- /dev/null +++ b/kcalc/knumber/tests/Makefile.am @@ -0,0 +1,32 @@ +# This file is part of the KDE libraries +# Copyright (C) 1996-1997 Matthias Kalle Dalheimer (kalle@kde.org) +# (C) 1997-1998 Stephan Kulow (coolo@kde.org) + +# 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. + +INCLUDES = -I$(top_srcdir)/kcalc/knumber $(all_includes) + +check_PROGRAMS = knumbertest + +TESTS = knumbertest + +noinst_HEADERS = knumbertest.h + +METASOURCES = AUTO + +knumbertest_SOURCES = knumbertest.cpp +knumbertest_LDADD = ../libknumber.la $(LIB_QT) $(LIBGMP) +knumbertest_LDFLAGS = $(all_libraries) $(KDE_RPATH) diff --git a/kcalc/knumber/tests/knumbertest.cpp b/kcalc/knumber/tests/knumbertest.cpp new file mode 100644 index 0000000..814410e --- /dev/null +++ b/kcalc/knumber/tests/knumbertest.cpp @@ -0,0 +1,582 @@ +// +// Author: Klaus Niederkrueger <kniederk@math.uni-koeln.de> +// + +#include <stdlib.h> +#include <stdio.h> + +#include <iostream> +#include <qstring.h> + +#include "knumbertest.h" + +QString const numtypeToString(KNumber::NumType arg) +{ + switch(arg) { + case KNumber::SpecialType: + return QString("Special"); + case KNumber::IntegerType: + return QString("Integer"); + case KNumber::FractionType: + return QString("Fraction"); + case KNumber::FloatType: + return QString("Float"); + default: + return QString("Unknown:") + QString::number(static_cast<int>(arg)); + + } +} + +void checkResult(QString const &string, KNumber const & result, + QString const & desired_string, KNumber::NumType desired) +{ + std::cout << "Testing result of: " << string.ascii() << + " should give " << desired_string.ascii() << " and gives " << + result.toQString(8).ascii() << "....\n"; + std::cout << "The type of the result should be " << + numtypeToString(desired).ascii() << " and gives " << + numtypeToString(result.type()).ascii() << ".... "; + + if (result.type() == desired && + result.toQString(8) == desired_string) { + std::cout << "OK\n"; + return; + } + + std::cout << "Failed\n"; + exit(1); +} + +void checkTruth(QString const &string, bool computation, + bool desired_result) +{ + std::cout << "Testing truth of: " << string.ascii() << + " should be " << desired_result << " and is " << + computation << "....\n"; + + if (computation == desired_result) { + std::cout << "OK\n"; + return; + } + + std::cout << "Failed\n"; + exit(1); +} + + +void checkType(QString const &string, KNumber::NumType test_arg, + KNumber::NumType desired) +{ + std::cout << "Testing type of: " << string.ascii() << " should give " << + numtypeToString(desired).ascii() << " and gives " << + numtypeToString(test_arg).ascii() << "...."; + + if (test_arg == desired) { + std::cout << "OK\n"; + return; + } + + std::cout << "Failed\n"; + exit(1); + +} + + +void testingCompare(void) +{ + std::cout << "\n\nTesting Compare:\n"; + + checkTruth("KNumber(5) == KNumber(2)", KNumber(5) == KNumber(2), false); + checkTruth("KNumber(5) > KNumber(2)", KNumber(5) > KNumber(2), true); + checkTruth("KNumber(5) < KNumber(2)", KNumber(5) < KNumber(2), false); + checkTruth("KNumber(5) < KNumber(0)", KNumber(5) < KNumber(0), false); + checkTruth("KNumber(-5) < KNumber(0)", KNumber(-5) < KNumber(0), true); + checkTruth("KNumber(5) >= KNumber(2)", KNumber(5) >= KNumber(2), true); + checkTruth("KNumber(5) <= KNumber(2)", KNumber(5) <= KNumber(2), false); + checkTruth("KNumber(5) != KNumber(2)", KNumber(5) != KNumber(2), true); + + checkTruth("KNumber(2) == KNumber(2)", KNumber(2) == KNumber(2), true); + checkTruth("KNumber(2) > KNumber(2)", KNumber(2) > KNumber(2), false); + checkTruth("KNumber(2) < KNumber(2)", KNumber(2) < KNumber(2), false); + checkTruth("KNumber(2) >= KNumber(2)", KNumber(2) >= KNumber(2), true); + checkTruth("KNumber(2) <= KNumber(2)", KNumber(2) <= KNumber(2), true); + checkTruth("KNumber(2) != KNumber(2)", KNumber(2) != KNumber(2), false); + + checkTruth("KNumber(5) == KNumber(\"1/2\")", KNumber(5) == KNumber("1/2"), false); + checkTruth("KNumber(5) > KNumber(\"1/2\")", KNumber(5) > KNumber("1/2"), true); + checkTruth("KNumber(5) < KNumber(\"1/2\")", KNumber(5) < KNumber("1/2"), false); + checkTruth("KNumber(5) >= KNumber(\"1/2\")", KNumber(5) >= KNumber("1/2"), true); + checkTruth("KNumber(5) <= KNumber(\"1/2\")", KNumber(5) <= KNumber("1/2"), false); + checkTruth("KNumber(5) != KNumber(\"1/2\")", KNumber(5) != KNumber("1/2"), true); + + checkTruth("KNumber(\"1/2\") == KNumber(\"1/2\")", KNumber("1/2") == KNumber("1/2"), true); + checkTruth("KNumber(\"1/2\") > KNumber(\"1/2\")", KNumber("1/2") > KNumber("1/2"), false); + checkTruth("KNumber(\"1/2\") < KNumber(\"1/2\")", KNumber("1/2") < KNumber("1/2"), false); + checkTruth("KNumber(\"1/2\") >= KNumber(\"1/2\")", KNumber("1/2") >= KNumber("1/2"), true); + checkTruth("KNumber(\"1/2\") <= KNumber(\"1/2\")", KNumber("1/2") <= KNumber("1/2"), true); + checkTruth("KNumber(\"1/2\") != KNumber(\"1/2\")", KNumber("1/2") != KNumber("1/2"), false); + + checkTruth("KNumber(\"3/2\") == KNumber(\"1/2\")", KNumber("3/2") == KNumber("1/2"), false); + checkTruth("KNumber(\"3/2\") > KNumber(\"1/2\")", KNumber("3/2") > KNumber("1/2"), true); + checkTruth("KNumber(\"3/2\") < KNumber(\"1/2\")", KNumber("3/2") < KNumber("1/2"), false); + checkTruth("KNumber(\"3/2\") >= KNumber(\"1/2\")", KNumber("3/2") >= KNumber("1/2"), true); + checkTruth("KNumber(\"3/2\") <= KNumber(\"1/2\")", KNumber("3/2") <= KNumber("1/2"), false); + checkTruth("KNumber(\"3/2\") != KNumber(\"1/2\")", KNumber("3/2") != KNumber("1/2"), true); + +} + + +void testingAdditions(void) +{ + std::cout << "\n\nTesting additions:\n"; + + checkResult("KNumber(5) + KNumber(2)", KNumber(5) + KNumber(2), "7", KNumber::IntegerType); + checkResult("KNumber(5) + KNumber(\"2/3\")", KNumber(5) + KNumber("2/3"), "17/3", KNumber::FractionType); + checkResult("KNumber(5) + KNumber(\"2.3\")", KNumber(5) + KNumber("2.3"), "7.3", KNumber::FloatType); + + checkResult("KNumber(\"5/3\") + KNumber(2)", KNumber("5/3") + KNumber(2), "11/3", KNumber::FractionType); + checkResult("KNumber(\"5/3\") + KNumber(\"2/3\")", KNumber("5/3") + KNumber("2/3"), "7/3", KNumber::FractionType); + checkResult("KNumber(\"5/3\") + KNumber(\"1/3\")", KNumber("5/3") + KNumber("1/3"), "2", KNumber::IntegerType); + checkResult("KNumber(\"5/3\") + KNumber(\"-26/3\")", KNumber("5/3") + KNumber("-26/3"), "-7", KNumber::IntegerType); + checkResult("KNumber(\"5/2\") + KNumber(2.3)", KNumber("5/2") + KNumber(2.3), "4.8", KNumber::FloatType); + + checkResult("KNumber(5.3) + KNumber(2)", KNumber(5.3) + KNumber(2), "7.3", KNumber::FloatType); + checkResult("KNumber(5.3) + KNumber(\"2/4\")", KNumber(5.3) + KNumber("2/4"), "5.8", KNumber::FloatType); + checkResult("KNumber(5.3) + KNumber(2.3)", KNumber(5.3) + KNumber(2.3), "7.6", KNumber::FloatType); + +} + +void testingSubtractions(void) +{ + std::cout << "\n\nTesting subtractions:\n"; + + checkResult("KNumber(5) - KNumber(2)", KNumber(5) - KNumber(2), "3", KNumber::IntegerType); + checkResult("KNumber(5) - KNumber(\"2/3\")", KNumber(5) - KNumber("2/3"), "13/3", KNumber::FractionType); + checkResult("KNumber(5) - KNumber(2.3)", KNumber(5) - KNumber(2.3), "2.7", KNumber::FloatType); + + checkResult("KNumber(\"5/3\") - KNumber(2)", KNumber("5/3") - KNumber(2), "-1/3", KNumber::FractionType); + checkResult("KNumber(\"5/3\") - KNumber(\"1/3\")", KNumber("5/3") - KNumber("1/3"), "4/3", KNumber::FractionType); + checkResult("KNumber(\"5/3\") - KNumber(\"2/3\")", KNumber("5/3") - KNumber("2/3"), "1", KNumber::IntegerType); + checkResult("KNumber(\"-5/3\") - KNumber(\"4/3\")", KNumber("-5/3") - KNumber("4/3"), "-3", KNumber::IntegerType); + checkResult("KNumber(\"5/4\") - KNumber(2.2)", KNumber("5/4") - KNumber(2.2), "-0.95", KNumber::FloatType); + + checkResult("KNumber(5.3) - KNumber(2)", KNumber(5.3) - KNumber(2), "3.3", KNumber::FloatType); + checkResult("KNumber(5.3) - KNumber(\"3/4\")", KNumber(5.3) - KNumber("3/4"), "4.55", KNumber::FloatType); + checkResult("KNumber(5.3) - KNumber(2.3)", KNumber(5.3) - KNumber(2.3), "3", KNumber::FloatType); + +} + + +void testingMultiplications(void) +{ + std::cout << "\n\nTesting multiplications:\n"; + + checkResult("KNumber(5) * KNumber(2)", KNumber(5) * KNumber(2), "10", KNumber::IntegerType); + checkResult("KNumber(5) * KNumber(\"2/3\")", KNumber(5) * KNumber("2/3"), "10/3", KNumber::FractionType); + checkResult("KNumber(5) * KNumber(\"2/5\")", KNumber(5) * KNumber("2/5"), "2", KNumber::IntegerType); + checkResult("KNumber(5) * KNumber(2.3)", KNumber(5) * KNumber(2.3), "11.5", KNumber::FloatType); + checkResult("KNumber(0) * KNumber(\"2/5\")", KNumber(0) * KNumber("2/5"), "0", KNumber::IntegerType); + checkResult("KNumber(0) * KNumber(2.3)", KNumber(0) * KNumber(2.3), "0", KNumber::IntegerType); + + checkResult("KNumber(\"5/3\") * KNumber(2)", KNumber("5/3") * KNumber(2), "10/3", KNumber::FractionType); + checkResult("KNumber(\"5/3\") * KNumber(0)", KNumber("5/3") * KNumber(0), "0", KNumber::IntegerType); + checkResult("KNumber(\"5/3\") * KNumber(\"2/3\")", KNumber("5/3") * KNumber("2/3"), "10/9", KNumber::FractionType); + checkResult("KNumber(\"25/6\") * KNumber(\"12/5\")", KNumber("25/6") * KNumber("12/5"), "10", KNumber::IntegerType); + checkResult("KNumber(\"5/2\") * KNumber(2.3)", KNumber("5/2") * KNumber(2.3), "5.75",KNumber::FloatType); + + checkResult("KNumber(5.3) * KNumber(2)", KNumber(5.3) * KNumber(2), "10.6", KNumber::FloatType); + checkResult("KNumber(5.3) * KNumber(0)", KNumber(5.3) * KNumber(0), "0", KNumber::IntegerType); + checkResult("KNumber(5.3) * KNumber(\"1/2\")", KNumber(5.3) * KNumber("1/2"), "2.65", KNumber::FloatType); + checkResult("KNumber(5.3) * KNumber(2.3)", KNumber(5.3) * KNumber(2.3), "12.19", KNumber::FloatType); + +} + +void testingDivisions(void) +{ + std::cout << "\n\nTesting divisions:\n"; + + checkResult("KNumber(5) / KNumber(2)", KNumber(5) / KNumber(2), "5/2", KNumber::FractionType); + checkResult("KNumber(122) / KNumber(2)", KNumber(122) / KNumber(2), "61", KNumber::IntegerType); + checkResult("KNumber(12) / KNumber(0)", KNumber(12) / KNumber(0), "inf", KNumber::SpecialType); + checkResult("KNumber(-12) / KNumber(0)", KNumber(-12) / KNumber(0), "-inf", KNumber::SpecialType); + checkResult("KNumber(5) / KNumber(\"2/3\")", KNumber(5) / KNumber("2/3"), "15/2", KNumber::FractionType); + checkResult("KNumber(6) / KNumber(\"2/3\")", KNumber(6) / KNumber("2/3"), "9", KNumber::IntegerType); + checkResult("KNumber(5) / KNumber(2.5)", KNumber(5) / KNumber(2.5), "2", KNumber::FloatType); + checkResult("KNumber(5) / KNumber(0.0)", KNumber(5) / KNumber(0.0), "inf", KNumber::SpecialType); + checkResult("KNumber(-5) / KNumber(0.0)", KNumber(-5) / KNumber(0.0), "-inf", KNumber::SpecialType); + + checkResult("KNumber(\"5/3\") / KNumber(2)", KNumber("5/3") / KNumber(2), "5/6", KNumber::FractionType); + checkResult("KNumber(\"5/3\") / KNumber(0)", KNumber("5/3") / KNumber(0), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") / KNumber(0)", KNumber("-5/3") / KNumber(0), "-inf", KNumber::SpecialType); + checkResult("KNumber(\"5/3\") / KNumber(\"2/3\")", KNumber("5/3") / KNumber("2/3"), "5/2", KNumber::FractionType); + checkResult("KNumber(\"49/3\") / KNumber(\"7/9\")", KNumber("49/3") / KNumber("7/9"), "21", KNumber::IntegerType); + checkResult("KNumber(\"5/2\") / KNumber(2.5)", KNumber("5/2") / KNumber(2.5), "1", KNumber::FloatType); + checkResult("KNumber(\"5/2\") / KNumber(0.0)", KNumber("5/2") / KNumber(0.0), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/2\") / KNumber(0.0)", KNumber("-5/2") / KNumber(0.0), "-inf", KNumber::SpecialType); + + checkResult("KNumber(5.3) / KNumber(2)", KNumber(5.3) / KNumber(2), "2.65", KNumber::FloatType); + checkResult("KNumber(5.3) / KNumber(0)", KNumber(5.3) / KNumber(0), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.3) / KNumber(0)", KNumber(-5.3) / KNumber(0), "-inf", KNumber::SpecialType); + checkResult("KNumber(5.3) / KNumber(\"2/3\")", KNumber(5.3) / KNumber("2/3"), "7.95", KNumber::FloatType); + checkResult("KNumber(5.5) / KNumber(2.5)", KNumber(5.5) / KNumber(2.5), "2.2", KNumber::FloatType); + checkResult("KNumber(5.5) / KNumber(0.0)", KNumber(5.5) / KNumber(0.0), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.5) / KNumber(0.0)", KNumber(-5.5) / KNumber(0.0), "-inf", KNumber::SpecialType); +} + +void testingModulus(void) +{ + std::cout << "\n\nTesting modulus:\n"; + + checkResult("KNumber(23) % KNumber(4)", KNumber(23) % KNumber(4), "3", KNumber::IntegerType); + checkResult("KNumber(12) % KNumber(-5)", KNumber(12) % KNumber(-5), "2", KNumber::IntegerType); + checkResult("KNumber(-12) % KNumber(5)", KNumber(-12) % KNumber(5), "3", KNumber::IntegerType); + checkResult("KNumber(12) % KNumber(0)", KNumber(-12) % KNumber(0), "nan", KNumber::SpecialType); + checkResult("KNumber(-12) % KNumber(0)", KNumber(-12) % KNumber(0), "nan", KNumber::SpecialType); + +#warning test for other types + +} + +void testingAndOr(void) +{ + std::cout << "\n\nTesting And/Or:\n"; + + checkResult("KNumber(17) & KNumber(9)", KNumber(17) & KNumber(9), "1", KNumber::IntegerType); + checkResult("KNumber(17) | KNumber(9)", KNumber(17) | KNumber(9), "25", KNumber::IntegerType); + checkResult("KNumber(1023) & KNumber(255)", KNumber(1023) & KNumber(255), "255", KNumber::IntegerType); + checkResult("KNumber(1023) | KNumber(255)", KNumber(1023) | KNumber(255), "1023", KNumber::IntegerType); + +#warning test for other types + +} + + +void testingAbs(void) +{ + std::cout << "\n\nTesting absolute value:\n"; + + checkResult("KNumber(5).abs()", KNumber(5).abs(), "5", KNumber::IntegerType); + checkResult("KNumber(\"2/3\").abs()", KNumber("2/3").abs(), "2/3", KNumber::FractionType); + checkResult("KNumber(\"2.3\").abs()", KNumber("2.3").abs(), "2.3", KNumber::FloatType); + + checkResult("KNumber(-5).abs()", KNumber(-5).abs(), "5", KNumber::IntegerType); + checkResult("KNumber(\"-2/3\").abs()", KNumber("-2/3").abs(), "2/3", KNumber::FractionType); + checkResult("KNumber(\"-2.3\").abs()", KNumber("-2.3").abs(), "2.3", KNumber::FloatType); +} + +void testingTruncateToInteger(void) +{ + std::cout << "\n\nTesting truncate to an integer:\n"; + + checkResult("KNumber(16).integerPart()", KNumber(16).integerPart(), "16", KNumber::IntegerType); + checkResult("KNumber(\"43/9\").integerPart()", KNumber("43/9").integerPart(), "4", KNumber::IntegerType); + checkResult("KNumber(\"-43/9\").integerPart()", KNumber("-43/9").integerPart(), "-4", KNumber::IntegerType); + checkResult("KNumber(\"5.25\").integerPart()", KNumber("5.25").integerPart(), "5", KNumber::IntegerType); + checkResult("KNumber(\"-5.25\").integerPart()", KNumber("-5.25").integerPart(), "-5", KNumber::IntegerType); +} + + +void testingSqrt(void) +{ + std::cout << "\n\nTesting square root, cubic root:\n"; + + checkResult("KNumber(16).sqrt()", KNumber(16).sqrt(), "4", KNumber::IntegerType); + checkResult("KNumber(-16).sqrt()", KNumber(-16).sqrt(), "nan", KNumber::SpecialType); + checkResult("KNumber(\"16/9\").sqrt()", KNumber("16/9").sqrt(), "4/3", KNumber::FractionType); + checkResult("KNumber(\"-16/9\").sqrt()", KNumber("-16/9").sqrt(), "nan", KNumber::SpecialType); + checkResult("KNumber(2).sqrt()", KNumber(2).sqrt(), "1.4142136", KNumber::FloatType); + checkResult("KNumber(\"2/3\").sqrt()", KNumber("2/3").sqrt(), "0.81649658", KNumber::FloatType); + checkResult("KNumber(\"0.25\").sqrt()", KNumber("0.25").sqrt(), "0.5", KNumber::FloatType); + checkResult("KNumber(\"-0.25\").sqrt()", KNumber("-0.25").sqrt(), "nan", KNumber::SpecialType); + + + checkResult("KNumber(27).cbrt()", KNumber(27).cbrt(), "3", KNumber::IntegerType); + checkResult("KNumber(-27).cbrt()", KNumber(-27).cbrt(), "-3", KNumber::IntegerType); + checkResult("KNumber(\"27/8\").cbrt()", KNumber("27/8").cbrt(), "3/2", KNumber::FractionType); + checkResult("KNumber(\"-8/27\").cbrt()", KNumber("-8/27").cbrt(), "-2/3", KNumber::FractionType); +#warning need to check non-perfect cube roots + // checkResult("KNumber(2).cbrt()", KNumber(2).cbrt(), "1.4142136", KNumber::FloatType); + // checkResult("KNumber(\"2/3\").cbrt()", KNumber("2/3").cbrt(), "0.81649658", KNumber::FloatType); + // checkResult("KNumber(\"0.25\").cbrt()", KNumber("0.25").cbrt(), "0.5", KNumber::FloatType); + // checkResult("KNumber(\"-0.25\").cbrt()", KNumber("-0.25").cbrt(), "nan", KNumber::SpecialType); + +} + +void testingShifts(void) +{ + std::cout << "\n\nTesting left/right shift:\n"; + + checkResult("KNumber(16) << KNumber(2)", KNumber(16) << KNumber(2), "64", KNumber::IntegerType); + checkResult("KNumber(16) >> KNumber(2)", KNumber(16) >> KNumber(2), "4", KNumber::IntegerType); + +} + +void testingPower(void) +{ + std::cout << "\n\nTesting Power:\n"; + + checkResult("KNumber(0) ^ KNumber(-4)", KNumber(0).power(KNumber(-4)), "inf", KNumber::SpecialType); + checkResult("KNumber(5) ^ KNumber(4)", KNumber(5).power(KNumber(4)), "625", KNumber::IntegerType); + checkResult("KNumber(122) ^ KNumber(0)", KNumber(122).power(KNumber(0)), "1", KNumber::IntegerType); + checkResult("KNumber(-5) ^ KNumber(0)", KNumber(-5).power(KNumber(0)), "nan", KNumber::SpecialType); + checkResult("KNumber(-2) ^ KNumber(3)", KNumber(-2).power(KNumber(3)), "-8", KNumber::IntegerType); + checkResult("KNumber(-2) ^ KNumber(4)", KNumber(-2).power(KNumber(4)), "16", KNumber::IntegerType); + checkResult("KNumber(5) ^ KNumber(-2)", KNumber(5).power(KNumber(-2)), "1/25", KNumber::FractionType); + checkResult("KNumber(8) ^ KNumber(\"2/3\")", KNumber(8).power(KNumber("2/3")), "4", KNumber::IntegerType); + checkResult("KNumber(8) ^ KNumber(\"-2/3\")", KNumber(8).power(KNumber("-2/3")), "1/4", KNumber::FractionType); + checkResult("KNumber(-16) ^ KNumber(\"1/4\")", KNumber(-16).power(KNumber("1/4")), "nan", KNumber::SpecialType); + checkResult("KNumber(-8) ^ KNumber(\"1/3\")", KNumber(-8).power(KNumber("1/3")), "nan", KNumber::SpecialType); + checkResult("KNumber(5) ^ KNumber(0.0)", KNumber(5).power(KNumber(0.0)), "1", KNumber::IntegerType); + checkResult("KNumber(-5) ^ KNumber(0.0)", KNumber(-5).power(KNumber(0.0)), "nan", KNumber::SpecialType); + + checkResult("KNumber(\"5/3\") ^ KNumber(2)", KNumber("5/3").power(KNumber(2)), "25/9", KNumber::FractionType); + checkResult("KNumber(\"5/3\") ^ KNumber(0)", KNumber("5/3").power(KNumber(0)), "1", KNumber::IntegerType); + checkResult("KNumber(\"-5/3\") ^ KNumber(0)", KNumber("-5/3").power(KNumber(0)), "nan", KNumber::SpecialType); + checkResult("KNumber(\"8/27\") ^ KNumber(\"2/3\")", KNumber("8/27").power(KNumber("2/3")), "4/9", KNumber::FractionType); + checkResult("KNumber(\"49/3\") ^ KNumber(\"7/9\")", KNumber("49/3").power(KNumber("7/9")), "21", KNumber::IntegerType); + checkResult("KNumber(\"5/2\") ^ KNumber(2.5)", KNumber("5/2").power(KNumber(2.5)), "1", KNumber::FloatType); + checkResult("KNumber(\"5/2\") ^ KNumber(0.0)", KNumber("5/2").power(KNumber(0.0)), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/2\") ^ KNumber(0.0)", KNumber("-5/2").power(KNumber(0.0)), "-inf", KNumber::SpecialType); + + checkResult("KNumber(5.3) ^ KNumber(2)", KNumber(5.3).power(KNumber(2)), "2.65", KNumber::FloatType); + checkResult("KNumber(5.3) ^ KNumber(0)", KNumber(5.3).power(KNumber(0)), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.3) ^ KNumber(0)", KNumber(-5.3).power(KNumber(0)), "-inf", KNumber::SpecialType); + checkResult("KNumber(5.3) ^ KNumber(\"2/3\")", KNumber(5.3).power(KNumber("2/3")), "7.95", KNumber::FloatType); + checkResult("KNumber(5.5) ^ KNumber(2.5)", KNumber(5.5).power(KNumber(2.5)), "2.2", KNumber::FloatType); + checkResult("KNumber(5.5) ^ KNumber(0.0)", KNumber(5.5).power(KNumber(0.0)), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.5) ^ KNumber(0.0)", KNumber(-5.5).power(KNumber(0.0)), "-inf", KNumber::SpecialType); +} + +void testingInfArithmetic(void) +{ + std::cout << "\n\nTesting inf/nan-arithmetics:\n"; + + KNumber tmp_inf = KNumber("inf"); + KNumber tmp_mininf = KNumber("-inf"); + KNumber tmp_nan = KNumber("nan"); + + checkResult("inf + KNumber(2)", tmp_inf + KNumber(2), "inf", KNumber::SpecialType); + checkResult("KNumber(-5) + inf", KNumber(-5) + tmp_inf, "inf", KNumber::SpecialType); + checkResult("inf + KNumber(\"1/2\")", tmp_inf + KNumber("1/2"), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") + inf", KNumber("-5/3") + tmp_inf, "inf", KNumber::SpecialType); + checkResult("inf + KNumber(2.01)", tmp_inf + KNumber(2.01), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) + inf", KNumber(-5.4) + tmp_inf, "inf", KNumber::SpecialType); + checkResult("mininf + KNumber(2)", tmp_mininf + KNumber(2), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5) + mininf", KNumber(-5) + tmp_mininf, "-inf", KNumber::SpecialType); + checkResult("mininf + KNumber(\"1/2\")", tmp_mininf + KNumber("1/2"), "-inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") + mininf", KNumber("-5/3") + tmp_mininf, "-inf", KNumber::SpecialType); + checkResult("mininf + KNumber(2.01)", tmp_mininf + KNumber(2.01), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) + mininf", KNumber(-5.4) + tmp_mininf, "-inf", KNumber::SpecialType); + checkResult("nan + KNumber(2)", tmp_nan + KNumber(2), "nan", KNumber::SpecialType); + checkResult("KNumber(-5) + nan", KNumber(-5) + tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan + KNumber(\"1/2\")", tmp_nan + KNumber("1/2"), "nan", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") + nan", KNumber("-5/3") + tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan + KNumber(2.01)", tmp_nan + KNumber(2.01), "nan", KNumber::SpecialType); + checkResult("KNumber(-5.4) + nan", KNumber(-5.4) + tmp_nan, "nan", KNumber::SpecialType); + checkResult("inf + inf", tmp_inf + tmp_inf, "inf", KNumber::SpecialType); + checkResult("inf + mininf", tmp_inf + tmp_mininf, "nan", KNumber::SpecialType); + checkResult("mininf + inf", tmp_mininf + tmp_inf, "nan", KNumber::SpecialType); + checkResult("mininf + mininf", tmp_mininf + tmp_mininf, "-inf", KNumber::SpecialType); + checkResult("inf + nan", tmp_inf + tmp_nan, "nan", KNumber::SpecialType); + checkResult("mininf + nan", tmp_mininf + tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan + inf", tmp_nan + tmp_inf, "nan", KNumber::SpecialType); + checkResult("mininf + nan", tmp_mininf + tmp_nan, "nan", KNumber::SpecialType); + + + checkResult("inf - KNumber(2)", tmp_inf - KNumber(2), "inf", KNumber::SpecialType); + checkResult("KNumber(-5) - inf", KNumber(-5) - tmp_inf, "-inf", KNumber::SpecialType); + checkResult("inf - KNumber(\"1/2\")", tmp_inf - KNumber("1/2"), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") - inf", KNumber("-5/3") - tmp_inf, "-inf", KNumber::SpecialType); + checkResult("inf - KNumber(2.01)", tmp_inf - KNumber(2.01), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) - inf", KNumber(-5.4) - tmp_inf, "-inf", KNumber::SpecialType); + checkResult("mininf - KNumber(2)", tmp_mininf - KNumber(2), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5) - mininf", KNumber(-5) - tmp_mininf, "inf", KNumber::SpecialType); + checkResult("mininf - KNumber(\"1/2\")", tmp_mininf - KNumber("1/2"), "-inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") - mininf", KNumber("-5/3") - tmp_mininf, "inf", KNumber::SpecialType); + checkResult("mininf - KNumber(2.01)", tmp_mininf - KNumber(2.01), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) - mininf", KNumber(-5.4) - tmp_mininf, "inf", KNumber::SpecialType); + checkResult("nan - KNumber(2)", tmp_nan - KNumber(2), "nan", KNumber::SpecialType); + checkResult("KNumber(-5) - nan", KNumber(-5) - tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan - KNumber(\"1/2\")", tmp_nan - KNumber("1/2"), "nan", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") - nan", KNumber("-5/3") - tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan - KNumber(2.01)", tmp_nan - KNumber(2.01), "nan", KNumber::SpecialType); + checkResult("KNumber(-5.4) - nan", KNumber(-5.4) - tmp_nan, "nan", KNumber::SpecialType); + checkResult("inf - inf", tmp_inf - tmp_inf, "nan", KNumber::SpecialType); + checkResult("inf - mininf", tmp_inf - tmp_mininf, "inf", KNumber::SpecialType); + checkResult("mininf - inf", tmp_mininf - tmp_inf, "-inf", KNumber::SpecialType); + checkResult("mininf - mininf", tmp_mininf - tmp_mininf, "nan", KNumber::SpecialType); + checkResult("inf - nan", tmp_inf - tmp_nan, "nan", KNumber::SpecialType); + checkResult("mininf - nan", tmp_mininf - tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan - inf", tmp_nan - tmp_inf, "nan", KNumber::SpecialType); + checkResult("mininf - nan", tmp_mininf - tmp_nan, "nan", KNumber::SpecialType); + + + checkResult("inf * KNumber(2)", tmp_inf * KNumber(2), "inf", KNumber::SpecialType); + checkResult("KNumber(-5) * inf", KNumber(-5) * tmp_inf, "-inf", KNumber::SpecialType); + checkResult("inf * KNumber(\"1/2\")", tmp_inf * KNumber("1/2"), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") * inf", KNumber("-5/3") * tmp_inf, "-inf", KNumber::SpecialType); + checkResult("inf * KNumber(2.01)", tmp_inf * KNumber(2.01), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) * inf", KNumber(-5.4) * tmp_inf, "-inf", KNumber::SpecialType); + checkResult("mininf * KNumber(2)", tmp_mininf * KNumber(2), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5) * mininf", KNumber(-5) * tmp_mininf, "inf", KNumber::SpecialType); + checkResult("mininf * KNumber(\"1/2\")", tmp_mininf * KNumber("1/2"), "-inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") * mininf", KNumber("-5/3") * tmp_mininf, "inf", KNumber::SpecialType); + checkResult("mininf * KNumber(2.01)", tmp_mininf * KNumber(2.01), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) * mininf", KNumber(-5.4) * tmp_mininf, "inf", KNumber::SpecialType); + checkResult("nan * KNumber(2)", tmp_nan * KNumber(2), "nan", KNumber::SpecialType); + checkResult("KNumber(-5) * nan", KNumber(-5) * tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan * KNumber(\"1/2\")", tmp_nan * KNumber("1/2"), "nan", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") * nan", KNumber("-5/3") * tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan * KNumber(2.01)", tmp_nan * KNumber(2.01), "nan", KNumber::SpecialType); + checkResult("KNumber(-5.4) * nan", KNumber(-5.4) * tmp_nan, "nan", KNumber::SpecialType); + checkResult("inf * inf", tmp_inf * tmp_inf, "inf", KNumber::SpecialType); + checkResult("inf * mininf", tmp_inf * tmp_mininf, "-inf", KNumber::SpecialType); + checkResult("mininf * inf", tmp_mininf * tmp_inf, "-inf", KNumber::SpecialType); + checkResult("mininf * mininf", tmp_mininf * tmp_mininf, "inf", KNumber::SpecialType); + checkResult("inf * nan", tmp_inf * tmp_nan, "nan", KNumber::SpecialType); + checkResult("mininf * nan", tmp_mininf * tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan * inf", tmp_nan * tmp_inf, "nan", KNumber::SpecialType); + checkResult("mininf * nan", tmp_mininf * tmp_nan, "nan", KNumber::SpecialType); + checkResult("KNumber(0) * inf", KNumber(0) * tmp_inf, "nan", KNumber::SpecialType); + checkResult("KNumber(0) * mininf", KNumber(0) * tmp_mininf, "nan", KNumber::SpecialType); + checkResult("inf * KNumber(0)", tmp_inf * KNumber(0), "nan", KNumber::SpecialType); + checkResult("mininf * KNumber(0)", tmp_mininf * KNumber(0), "nan", KNumber::SpecialType); + checkResult("KNumber(0.0) * inf", KNumber(0.0) * tmp_inf, "nan", KNumber::SpecialType); + checkResult("KNumber(0.0) * mininf", KNumber(0.0) * tmp_mininf, "nan", KNumber::SpecialType); + checkResult("inf * KNumber(0.0)", tmp_inf * KNumber(0.0), "nan", KNumber::SpecialType); + checkResult("mininf * KNumber(0.0)", tmp_mininf * KNumber(0.0), "nan", KNumber::SpecialType); + + + checkResult("inf / KNumber(2)", tmp_inf / KNumber(2), "inf", KNumber::SpecialType); + checkResult("KNumber(-5) / inf", KNumber(-5) / tmp_inf, "0", KNumber::IntegerType); + checkResult("inf / KNumber(\"1/2\")", tmp_inf / KNumber("1/2"), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") / inf", KNumber("-5/3") / tmp_inf, "0", KNumber::IntegerType); + checkResult("inf / KNumber(2.01)", tmp_inf / KNumber(2.01), "inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) / inf", KNumber(-5.4) / tmp_inf, "0", KNumber::IntegerType); + checkResult("mininf / KNumber(2)", tmp_mininf / KNumber(2), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5) / mininf", KNumber(-5) / tmp_mininf, "0", KNumber::IntegerType); + checkResult("mininf / KNumber(\"1/2\")", tmp_mininf / KNumber("1/2"), "-inf", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") / mininf", KNumber("-5/3") / tmp_mininf, "0", KNumber::IntegerType); + checkResult("mininf / KNumber(2.01)", tmp_mininf / KNumber(2.01), "-inf", KNumber::SpecialType); + checkResult("KNumber(-5.4) / mininf", KNumber(-5.4) / tmp_mininf, "0", KNumber::IntegerType); + checkResult("nan / KNumber(2)", tmp_nan / KNumber(2), "nan", KNumber::SpecialType); + checkResult("KNumber(-5) / nan", KNumber(-5) / tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan / KNumber(\"1/2\")", tmp_nan / KNumber("1/2"), "nan", KNumber::SpecialType); + checkResult("KNumber(\"-5/3\") / nan", KNumber("-5/3") / tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan / KNumber(2.01)", tmp_nan / KNumber(2.01), "nan", KNumber::SpecialType); + checkResult("KNumber(-5.4) / nan", KNumber(-5.4) / tmp_nan, "nan", KNumber::SpecialType); + checkResult("inf / inf", tmp_inf / tmp_inf, "nan", KNumber::SpecialType); + checkResult("inf / mininf", tmp_inf / tmp_mininf, "nan", KNumber::SpecialType); + checkResult("mininf / inf", tmp_mininf / tmp_inf, "nan", KNumber::SpecialType); + checkResult("mininf / mininf", tmp_mininf / tmp_mininf, "nan", KNumber::SpecialType); + checkResult("inf / nan", tmp_inf / tmp_nan, "nan", KNumber::SpecialType); + checkResult("mininf / nan", tmp_mininf / tmp_nan, "nan", KNumber::SpecialType); + checkResult("nan / inf", tmp_nan / tmp_inf, "nan", KNumber::SpecialType); + checkResult("mininf / nan", tmp_mininf / tmp_nan, "nan", KNumber::SpecialType); + checkResult("KNumber(0) / inf", KNumber(0) / tmp_inf, "0", KNumber::IntegerType); + checkResult("KNumber(0) / mininf", KNumber(0) / tmp_mininf, "0", KNumber::IntegerType); + checkResult("inf / KNumber(0)", tmp_inf / KNumber(0), "inf", KNumber::SpecialType); + checkResult("mininf / KNumber(0)", tmp_mininf / KNumber(0), "-inf", KNumber::SpecialType); + checkResult("KNumber(0.0) / inf", KNumber(0.0) / tmp_inf, "0", KNumber::IntegerType); + checkResult("KNumber(0.0) / mininf", KNumber(0.0) / tmp_mininf, "0", KNumber::IntegerType); + checkResult("inf / KNumber(0.0)", tmp_inf / KNumber(0.0), "inf", KNumber::SpecialType); + checkResult("mininf / KNumber(0.0)", tmp_mininf / KNumber(0.0), "-inf", KNumber::SpecialType); +} + +void testingFloatPrecision(void) +{ + KNumber::setDefaultFloatPrecision(100); + checkResult("Precision >= 100: (KNumber(1) + KNumber(\"1e-80\")) - KNumber(1)", + (KNumber(1) + KNumber("1e-80")) - KNumber(1), "1e-80", KNumber::FloatType); + checkResult("Precision >= 100: (KNumber(1) + KNumber(\"1e-980\")) - KNumber(1)", + (KNumber(1) + KNumber("1e-980")) - KNumber(1), "0", KNumber::FloatType); + + KNumber::setDefaultFloatPrecision(1000); + checkResult("Precision >= 1000: (KNumber(1) + KNumber(\"1e-980\")) - KNumber(1)", + (KNumber(1) + KNumber("1e-980")) - KNumber(1), "1e-980", KNumber::FloatType); + +} + +void testingOutput(void) +{ + KNumber::setDefaultFloatOutput(false); + checkResult("Fractional output: KNumber(\"1/4\")", KNumber("1/4"), "1/4", KNumber::FractionType); + KNumber::setDefaultFloatOutput(true); + checkResult("Float: KNumber(\"1/4\")", KNumber("1/4"), "0.25", KNumber::FractionType); + KNumber::setDefaultFloatOutput(false); + KNumber::setSplitoffIntegerForFractionOutput(true); + checkResult("Fractional output: KNumber(\"1/4\")", KNumber("1/4"), "1/4", KNumber::FractionType); + checkResult("Fractional output: KNumber(\"-1/4\")", KNumber("-1/4"), "-1/4", KNumber::FractionType); + checkResult("Fractional output: KNumber(\"21/4\")", KNumber("21/4"), "5 1/4", KNumber::FractionType); + checkResult("Fractional output: KNumber(\"-21/4\")", KNumber("-21/4"), "-5 1/4", KNumber::FractionType); + KNumber::setSplitoffIntegerForFractionOutput(false); + checkResult("Fractional output: KNumber(\"1/4\")", KNumber("1/4"), "1/4", KNumber::FractionType); + checkResult("Fractional output: KNumber(\"-1/4\")", KNumber("-1/4"), "-1/4", KNumber::FractionType); + checkResult("Fractional output: KNumber(\"21/4\")", KNumber("21/4"), "21/4", KNumber::FractionType); + checkResult("Fractional output: KNumber(\"-21/4\")", KNumber("-21/4"), "-21/4", KNumber::FractionType); +} + + +int main(void) +{ + std::cout << "Testing Constructors:\n"; + + checkResult("KNumber(5)", KNumber(5), "5", KNumber::IntegerType); + checkType("KNumber(5.3)", KNumber(5.3).type(), KNumber::FloatType); + checkType("KNumber(0.0)", KNumber(0.0).type(), KNumber::FloatType); + + checkResult("KNumber(\"5\")", KNumber("5"), "5", KNumber::IntegerType); + checkResult("KNumber(\"5/3\")", KNumber("5/3"), "5/3", KNumber::FractionType); + checkResult("KNumber(\"5/1\")", KNumber("5/1"), "5", KNumber::IntegerType); + checkResult("KNumber(\"0/12\")", KNumber("0/12"), "0", KNumber::IntegerType); + KNumber::setDefaultFractionalInput(true); + std::cout << "Read decimals as fractions:\n"; + checkResult("KNumber(\"5\")", KNumber("5"), "5", KNumber::IntegerType); + checkResult("KNumber(\"1.2\")", KNumber("1.2"), "6/5", KNumber::FractionType); + checkResult("KNumber(\"-0.02\")", KNumber("-0.02"), "-1/50", KNumber::FractionType); + checkResult("KNumber(\"5e-2\")", KNumber("5e-2"), "1/20", KNumber::FractionType); + checkResult("KNumber(\"1.2e3\")", KNumber("1.2e3"), "1200", KNumber::IntegerType); + checkResult("KNumber(\"0.02e+1\")", KNumber("0.02e+1"), "1/5", KNumber::FractionType); + + KNumber::setDefaultFractionalInput(false); + std::cout << "Read decimals as floats:\n"; + checkResult("KNumber(\"5.3\")", KNumber("5.3"), "5.3", KNumber::FloatType); + + checkResult("KNumber(\"nan\")", KNumber("nan"), "nan", KNumber::SpecialType); + checkResult("KNumber(\"inf\")", KNumber("inf"), "inf", KNumber::SpecialType); + checkResult("KNumber(\"-inf\")", KNumber("-inf"), "-inf", KNumber::SpecialType); + + std::cout << "\n\nConstants:\n"; + + checkType("KNumber::Zero", KNumber::Zero.type(), KNumber::IntegerType); + checkType("KNumber::One", KNumber::One.type(), KNumber::IntegerType); + checkType("KNumber::MinusOne", KNumber::MinusOne.type(), KNumber::IntegerType); + checkType("KNumber::Pi", KNumber::Pi.type(), KNumber::FloatType); + + testingCompare(); + + testingAdditions(); + testingSubtractions(); + testingMultiplications(); + testingDivisions(); + + testingAndOr(); + testingModulus(); + + testingAbs(); + testingSqrt(); + //testingPower(); + testingTruncateToInteger(); + + testingShifts(); + + testingInfArithmetic(); + + testingFloatPrecision(); + + testingOutput(); + + return 0; +} + + diff --git a/kcalc/knumber/tests/knumbertest.h b/kcalc/knumber/tests/knumbertest.h new file mode 100644 index 0000000..b93dc4a --- /dev/null +++ b/kcalc/knumber/tests/knumbertest.h @@ -0,0 +1,9 @@ +#ifndef KNUMBERTEST_H +#define KNUMBERTEST_H + +#include "knumber.h" + +/** test: a small test program for KNumber + */ + +#endif // KNUMBERTEST_H |