diff --git a/general.ui b/general.ui index 5569f37..c5d16b0 100644 --- a/general.ui +++ b/general.ui @@ -1,240 +1,250 @@ General 0 0 293 319 General Precision &Maximum number of digits: false kcfg_Precision Maximum number of digits displayed KCalc can compute with many more digits than the number that fits on the display. This setting gives the maximum number of digits displayed, before KCalc starts using scientific notation, i.e. notation of the type 2.34e12. Whether to use fixed decimal places Set &decimal precision false Number of fixed decimal digits Qt::Horizontal 40 20 Grouping Whether to group digits Group digits true true Binary Octal Hexadecimal Misc Whether to beep on error &Beep on error true Whether to show the result in the window title Show &result in window title Whether to use Two's Complement for non-decimal numbers Select to use Two's Complement notation for Binary, Octal and Hexidecimal numbers. This is a common notation to represent negative numbers for non-decimal numbers in computers. Two's complement true + + + + Repeat operation for each result + + + true + + + Qt::Vertical 20 40 kcfg_Precision kcfg_Fixed kcfg_FixedPrecision kcfg_Fixed toggled(bool) kcfg_FixedPrecision setEnabled(bool) 97 76 256 94 diff --git a/kcalc.kcfg b/kcalc.kcfg index 199a700..83dec22 100644 --- a/kcalc.kcfg +++ b/kcalc.kcfg @@ -1,203 +1,207 @@ QFontDatabase KColorScheme KLocalizedString KColorScheme schemeView(QPalette::Active, KColorScheme::View); QColor defaultColor = schemeView.foreground().color(); defaultColor defaultColor = schemeView.background().color(); defaultColor KColorScheme schemeButtons(QPalette::Active, KColorScheme::Button); QColor defaultButtonColor = schemeButtons.background().color(); defaultButtonColor defaultButtonColor defaultButtonColor defaultButtonColor defaultButtonColor defaultButtonColor QFontDatabase::systemFont(QFontDatabase::GeneralFont) QFont defaultDisplayFont = QFontDatabase::systemFont(QFontDatabase::GeneralFont); defaultDisplayFont.setWeight(QFont::Bold); defaultDisplayFont.setPointSizeF(defaultDisplayFont.pointSizeF() * 1.4); defaultDisplayFont KCalc can compute with many more digits than the number that fits on the display. This setting gives the maximum number of digits displayed, before KCalc starts using scientific notation, i.e. notation of the type 2.34e12. 12 8 200 2 false true false true Select to use Two's Complement notation for Binary, Octal and Hexidecimal numbers. This is a common notation to represent negative numbers in computers. true + + + false + A very simple mode where only the basic calculator buttons are shown Mode with science buttons and optional constants buttons Mode with additional statistics buttons and optional constants buttons Mode with logic buttons and selectable base. Optional bit edit available. false false 0 10 For easier readability it's possible to visible group the individual digits into pairs for example instead of 10111001 you can display 1011 1001, by setting the setting to 4, thus inserting a whitespace after every 4th digit. 4 For easier readability it's possible to visible group the individual digits into pairs for example instead of 42140213 you can display 4214 0213, by setting the setting to 4, thus inserting a whitespace after every 4th digit. 4 For easier readability it's possible to visible group the individual digits into pairs for example instead of AF1C42 you can display AF 1C 42, by setting the setting to 2, thus inserting a whitespace after every 2nd digit. 2 QString nameConstant0 = i18nc("Name of the user programmable constant", "C1"); QString nameConstant1 = i18nc("Name of the user programmable constant", "C2"); QString nameConstant2 = i18nc("Name of the user programmable constant", "C3"); QString nameConstant3 = i18nc("Name of the user programmable constant", "C4"); QString nameConstant4 = i18nc("Name of the user programmable constant", "C5"); QString nameConstant5 = i18nc("Name of the user programmable constant", "C6"); nameConstant0 nameConstant1 nameConstant2 nameConstant3 nameConstant4 nameConstant5 0 0 0 0 0 0 diff --git a/kcalc_core.cpp b/kcalc_core.cpp index 2d1fbc2..9ee3751 100644 --- a/kcalc_core.cpp +++ b/kcalc_core.cpp @@ -1,886 +1,910 @@ /* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com Copyright (C) 2003 - 2005 Klaus Niederkrueger kniederk@math.uni-koeln.de Copyright (C) 1996 - 2000 Bernd Johannes Wuebben wuebben@kde.org Copyright (C) 1995 Martin Bartlett This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "kcalc_core.h" +#include "kcalc_settings.h" #include namespace { KNumber Deg2Rad(const KNumber &x) { return x * (KNumber::Pi() / KNumber(180)); } KNumber Gra2Rad(const KNumber &x) { return x * (KNumber::Pi() / KNumber(200)); } KNumber Rad2Deg(const KNumber &x) { return x * (KNumber(180) / KNumber::Pi()); } KNumber Rad2Gra(const KNumber &x) { return x * (KNumber(200) / KNumber::Pi()); } bool error_; KNumber ExecOr(const KNumber &left_op, const KNumber &right_op) { return left_op | right_op; } KNumber ExecXor(const KNumber &left_op, const KNumber &right_op) { return left_op ^ right_op; } KNumber ExecAnd(const KNumber &left_op, const KNumber &right_op) { return left_op & right_op; } KNumber ExecLsh(const KNumber &left_op, const KNumber &right_op) { return left_op << right_op; } KNumber ExecRsh(const KNumber &left_op, const KNumber &right_op) { return left_op >> right_op; } KNumber ExecAdd(const KNumber &left_op, const KNumber &right_op) { return left_op + right_op; } KNumber ExecSubtract(const KNumber &left_op, const KNumber &right_op) { return left_op - right_op; } KNumber ExecMultiply(const KNumber &left_op, const KNumber &right_op) { return left_op * right_op; } KNumber ExecDivide(const KNumber &left_op, const KNumber &right_op) { return left_op / right_op; } KNumber ExecMod(const KNumber &left_op, const KNumber &right_op) { return left_op % right_op; } KNumber ExecIntDiv(const KNumber &left_op, const KNumber &right_op) { return (left_op / right_op).integerPart(); } KNumber ExecBinom(const KNumber &left_op, const KNumber &right_op) { return left_op.bin(right_op); } KNumber ExecPower(const KNumber &left_op, const KNumber &right_op) { return left_op.pow(right_op); } KNumber ExecPwrRoot(const KNumber &left_op, const KNumber &right_op) { return left_op.pow(KNumber::One / right_op); } KNumber ExecAddP(const KNumber &left_op, const KNumber &right_op) { return left_op * (KNumber::One + right_op / KNumber(100)); } KNumber ExecSubP(const KNumber &left_op, const KNumber &right_op) { return left_op * (KNumber::One - right_op / KNumber(100)); } KNumber ExecMultiplyP(const KNumber &left_op, const KNumber &right_op) { return left_op * right_op / KNumber(100); } KNumber ExecDivideP(const KNumber &left_op, const KNumber &right_op) { return left_op * KNumber(100) / right_op; } // move a number into the interval [0,360) by adding multiples of 360 KNumber moveIntoDegInterval(const KNumber &num) { KNumber tmp_num = num - (num / KNumber(360)).integerPart() * KNumber(360); if (tmp_num < KNumber::Zero) return tmp_num + KNumber(360); return tmp_num; } // move a number into the interval [0,400) by adding multiples of 400 KNumber moveIntoGradInterval(const KNumber &num) { KNumber tmp_num = num - (num / KNumber(400)).integerPart() * KNumber(400); if (tmp_num < KNumber::Zero) return tmp_num + KNumber(400); return tmp_num; } typedef KNumber(*Arith)(const KNumber &, const KNumber &); typedef KNumber(*Prcnt)(const KNumber &, const KNumber &); struct operator_data { int precedence; // priority of operators in " enum Operation" Arith arith_ptr; Prcnt prcnt_ptr; }; // build precedence list const struct operator_data Operator[] = { { 0, NULL, NULL}, // FUNC_EQUAL { 0, NULL, NULL}, // FUNC_PERCENT { 0, NULL, NULL}, // FUNC_BRACKET { 1, ExecOr, NULL}, // FUNC_OR { 2, ExecXor, NULL}, // FUNC_XOR { 3, ExecAnd, NULL}, // FUNC_AND { 4, ExecLsh, NULL}, // FUNC_LSH { 4, ExecRsh, NULL}, // FUNC_RSH { 5, ExecAdd, ExecAddP}, // FUNC_ADD { 5, ExecSubtract, ExecSubP}, // FUNC_SUBTRACT { 6, ExecMultiply, ExecMultiplyP}, // FUNC_MULTIPLY { 6, ExecDivide, ExecDivideP}, // FUNC_DIVIDE { 6, ExecMod, NULL}, // FUNC_MOD { 6, ExecIntDiv, NULL}, // FUNC_INTDIV { 7, ExecBinom, NULL}, // FUNC_BINOM { 7, ExecPower, NULL}, // FUNC_POWER { 7, ExecPwrRoot, NULL} // FUNC_PWR_ROOT }; } -CalcEngine::CalcEngine() : percent_mode_(false) { +CalcEngine::CalcEngine() : repeat_mode_(false), percent_mode_(false) { last_number_ = KNumber::Zero; error_ = false; + last_operation_ = FUNC_EQUAL; } KNumber CalcEngine::lastOutput(bool &error) const { error = error_; return last_number_; } void CalcEngine::ArcCosDeg(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR || input < -KNumber::One || input > KNumber::One) { last_number_ = KNumber::NaN; return; } if (input.type() == KNumber::TYPE_INTEGER) { if (input == KNumber::One) { last_number_ = KNumber::Zero; return; } if (input == - KNumber::One) { last_number_ = KNumber(180); return; } if (input == KNumber::Zero) { last_number_ = KNumber(90); return; } } last_number_ = Rad2Deg(input.acos()); } void CalcEngine::ArcCosRad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR || input < -KNumber::One || input > KNumber::One) { last_number_ = KNumber::NaN; return; } last_number_ = input.acos(); } void CalcEngine::ArcCosGrad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR || input < -KNumber::One || input > KNumber::One) { last_number_ = KNumber::NaN; return; } if (input.type() == KNumber::TYPE_INTEGER) { if (input == KNumber::One) { last_number_ = KNumber::Zero; return; } if (input == - KNumber::One) { last_number_ = KNumber(200); return; } if (input == KNumber::Zero) { last_number_ = KNumber(100); return; } } last_number_ = Rad2Gra(input.acos()); } void CalcEngine::ArcSinDeg(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR || input < -KNumber::One || input > KNumber::One) { last_number_ = KNumber::NaN; return; } if (input.type() == KNumber::TYPE_INTEGER) { if (input == KNumber::One) { last_number_ = KNumber(90); return; } if (input == - KNumber::One) { last_number_ = KNumber(-90); return; } if (input == KNumber::Zero) { last_number_ = KNumber::Zero; return; } } last_number_ = Rad2Deg(input.asin()); } void CalcEngine::ArcSinRad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR || input < -KNumber::One || input > KNumber::One) { last_number_ = KNumber::NaN; return; } last_number_ = input.asin(); } void CalcEngine::ArcSinGrad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR || input < -KNumber::One || input > KNumber::One) { last_number_ = KNumber::NaN; return; } if (input.type() == KNumber::TYPE_INTEGER) { if (input == KNumber::One) { last_number_ = KNumber(100); return; } if (input == - KNumber::One) { last_number_ = KNumber(-100); return; } if (input == KNumber::Zero) { last_number_ = KNumber::Zero; return; } } last_number_ = Rad2Gra(input.asin()); } void CalcEngine::ArcTangensDeg(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber(90); if (input == KNumber::NegInfinity) last_number_ = KNumber(-90); return; } last_number_ = Rad2Deg(input.atan()); } void CalcEngine::ArcTangensRad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::Pi() / KNumber(2); if (input == KNumber::NegInfinity) last_number_ = -KNumber::Pi() / KNumber(2); return; } last_number_ = input.atan(); } void CalcEngine::ArcTangensGrad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber(100); if (input == KNumber::NegInfinity) last_number_ = KNumber(-100); return; } last_number_ = Rad2Gra(input.atan()); } void CalcEngine::AreaCosHyp(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::PosInfinity; if (input == KNumber::NegInfinity) last_number_ = KNumber::NaN; return; } if (input < KNumber::One) { last_number_ = KNumber::NaN; return; } if (input == KNumber::One) { last_number_ = KNumber::Zero; return; } last_number_ = input.acosh(); } void CalcEngine::AreaSinHyp(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::PosInfinity; if (input == KNumber::NegInfinity) last_number_ = KNumber::NegInfinity; return; } if (input == KNumber::Zero) { last_number_ = KNumber::Zero; return; } last_number_ = input.asinh(); } void CalcEngine::AreaTangensHyp(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } if (input < -KNumber::One || input > KNumber::One) { last_number_ = KNumber::NaN; return; } if (input == KNumber::One) { last_number_ = KNumber::PosInfinity; return; } if (input == - KNumber::One) { last_number_ = KNumber::NegInfinity; return; } last_number_ = input.atanh(); } void CalcEngine::Complement(const KNumber &input) { if (input.type() != KNumber::TYPE_INTEGER) { last_number_ = KNumber::NaN; return; } last_number_ = ~input; } void CalcEngine::CosDeg(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } KNumber trunc_input = moveIntoDegInterval(input); if (trunc_input.type() == KNumber::TYPE_INTEGER) { KNumber mult = trunc_input / KNumber(90); if (mult.type() == KNumber::TYPE_INTEGER) { if (mult == KNumber::Zero) last_number_ = KNumber::One; else if (mult == KNumber::One) last_number_ = KNumber::Zero; else if (mult == KNumber(2)) last_number_ = KNumber::NegOne; else if (mult == KNumber(3)) last_number_ = KNumber::Zero; else qDebug() << "Something wrong in CalcEngine::CosDeg"; return; } } trunc_input = Deg2Rad(trunc_input); last_number_ = trunc_input.cos(); } void CalcEngine::CosRad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } last_number_ = input.cos(); } void CalcEngine::CosGrad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } KNumber trunc_input = moveIntoGradInterval(input); if (trunc_input.type() == KNumber::TYPE_INTEGER) { KNumber mult = trunc_input / KNumber(100); if (mult.type() == KNumber::TYPE_INTEGER) { if (mult == KNumber::Zero) last_number_ = KNumber::One; else if (mult == KNumber::One) last_number_ = KNumber::Zero; else if (mult == KNumber(2)) last_number_ = KNumber::NegOne; else if (mult == KNumber(3)) last_number_ = KNumber::Zero; else qDebug() << "Something wrong in CalcEngine::CosGrad"; return; } } trunc_input = Gra2Rad(trunc_input); last_number_ = trunc_input.cos(); } void CalcEngine::CosHyp(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::PosInfinity; // YES, this should be *positive* infinity. We mimic the behavior of // libc which says the following for cosh // // "If x is positive infinity or negative infinity, positive infinity is returned." if (input == KNumber::NegInfinity) last_number_ = KNumber::PosInfinity; return; } last_number_ = input.cosh(); } void CalcEngine::Cube(const KNumber &input) { last_number_ = input * input * input; } void CalcEngine::CubeRoot(const KNumber &input) { last_number_ = input.cbrt(); } void CalcEngine::Exp(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::PosInfinity; if (input == KNumber::NegInfinity) last_number_ = KNumber::Zero; return; } last_number_ = KNumber::Euler().pow(input); } void CalcEngine::Exp10(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::PosInfinity; if (input == KNumber::NegInfinity) last_number_ = KNumber::Zero; return; } last_number_ = KNumber(10).pow(input); } void CalcEngine::Factorial(const KNumber &input) { if (input == KNumber::PosInfinity) return; if (input < KNumber::Zero || input.type() == KNumber::TYPE_ERROR) { error_ = true; last_number_ = KNumber::NaN; return; } last_number_ = input.integerPart().factorial(); } void CalcEngine::Gamma(const KNumber &input) { if (input == KNumber::PosInfinity) return; if (input < KNumber::Zero || input.type() == KNumber::TYPE_ERROR) { error_ = true; last_number_ = KNumber::NaN; return; } last_number_ = input.tgamma(); } void CalcEngine::InvertSign(const KNumber &input) { last_number_ = -input; } void CalcEngine::Ln(const KNumber &input) { if (input < KNumber::Zero) last_number_ = KNumber::NaN; else if (input == KNumber::Zero) last_number_ = KNumber::NegInfinity; else if (input == KNumber::One) last_number_ = KNumber::Zero; else { last_number_ = input.ln(); } } void CalcEngine::Log10(const KNumber &input) { if (input < KNumber::Zero) last_number_ = KNumber::NaN; else if (input == KNumber::Zero) last_number_ = KNumber::NegInfinity; else if (input == KNumber::One) last_number_ = KNumber::Zero; else { last_number_ = input.log10(); } } void CalcEngine::ParenClose(KNumber input) { // evaluate stack until corresponding opening bracket while (!stack_.isEmpty()) { Node tmp_node = stack_.pop(); if (tmp_node.operation == FUNC_BRACKET) break; input = evalOperation(tmp_node.number, tmp_node.operation, input); } last_number_ = input; return; } void CalcEngine::ParenOpen(const KNumber &input) { enterOperation(input, FUNC_BRACKET); } void CalcEngine::Reciprocal(const KNumber &input) { last_number_ = KNumber::One / input; } void CalcEngine::SinDeg(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } KNumber trunc_input = moveIntoDegInterval(input); if (trunc_input.type() == KNumber::TYPE_INTEGER) { KNumber mult = trunc_input / KNumber(90); if (mult.type() == KNumber::TYPE_INTEGER) { if (mult == KNumber::Zero) last_number_ = KNumber::Zero; else if (mult == KNumber::One) last_number_ = KNumber::One; else if (mult == KNumber(2)) last_number_ = KNumber::Zero; else if (mult == KNumber(3)) last_number_ = KNumber::NegOne; else qDebug() << "Something wrong in CalcEngine::SinDeg"; return; } } trunc_input = Deg2Rad(trunc_input); last_number_ = trunc_input.sin(); } void CalcEngine::SinRad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } last_number_ = input.sin(); } void CalcEngine::SinGrad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } KNumber trunc_input = moveIntoGradInterval(input); if (trunc_input.type() == KNumber::TYPE_INTEGER) { KNumber mult = trunc_input / KNumber(100); if (mult.type() == KNumber::TYPE_INTEGER) { if (mult == KNumber::Zero) last_number_ = KNumber::Zero; else if (mult == KNumber::One) last_number_ = KNumber::One; else if (mult == KNumber(2)) last_number_ = KNumber::Zero; else if (mult == KNumber(3)) last_number_ = KNumber::NegOne; else qDebug() << "Something wrong in CalcEngine::SinGrad"; return; } } trunc_input = Gra2Rad(trunc_input); last_number_ = trunc_input.sin(); } void CalcEngine::SinHyp(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::PosInfinity; if (input == KNumber::NegInfinity) last_number_ = KNumber::NegInfinity; return; } last_number_ = input.sinh(); } void CalcEngine::Square(const KNumber &input) { last_number_ = input * input; } void CalcEngine::SquareRoot(const KNumber &input) { last_number_ = input.sqrt(); } void CalcEngine::StatClearAll(const KNumber &input) { Q_UNUSED(input); stats.clearAll(); } void CalcEngine::StatCount(const KNumber &input) { Q_UNUSED(input); last_number_ = KNumber(stats.count()); } void CalcEngine::StatDataNew(const KNumber &input) { stats.enterData(input); last_number_ = KNumber(stats.count()); } void CalcEngine::StatDataDel(const KNumber &input) { Q_UNUSED(input); stats.clearLast(); last_number_ = KNumber(stats.count()); } void CalcEngine::StatMean(const KNumber &input) { Q_UNUSED(input); last_number_ = stats.mean(); error_ = stats.error(); } void CalcEngine::StatMedian(const KNumber &input) { Q_UNUSED(input); last_number_ = stats.median(); error_ = stats.error(); } void CalcEngine::StatStdDeviation(const KNumber &input) { Q_UNUSED(input); last_number_ = stats.std(); error_ = stats.error(); } void CalcEngine::StatStdSample(const KNumber &input) { Q_UNUSED(input); last_number_ = stats.sample_std(); error_ = stats.error(); } void CalcEngine::StatSum(const KNumber &input) { Q_UNUSED(input); last_number_ = stats.sum(); } void CalcEngine::StatSumSquares(const KNumber &input) { Q_UNUSED(input); last_number_ = stats.sum_of_squares(); error_ = stats.error(); } void CalcEngine::TangensDeg(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } SinDeg(input); KNumber arg1 = last_number_; CosDeg(input); KNumber arg2 = last_number_; last_number_ = arg1 / arg2; } void CalcEngine::TangensRad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } SinRad(input); KNumber arg1 = last_number_; CosRad(input); KNumber arg2 = last_number_; last_number_ = arg1 / arg2; } void CalcEngine::TangensGrad(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { last_number_ = KNumber::NaN; return; } SinGrad(input); KNumber arg1 = last_number_; CosGrad(input); KNumber arg2 = last_number_; last_number_ = arg1 / arg2; } void CalcEngine::TangensHyp(const KNumber &input) { if (input.type() == KNumber::TYPE_ERROR) { if (input == KNumber::NaN) last_number_ = KNumber::NaN; if (input == KNumber::PosInfinity) last_number_ = KNumber::One; if (input == KNumber::NegInfinity) last_number_ = KNumber::NegOne; return; } last_number_ = input.tanh(); } KNumber CalcEngine::evalOperation(const KNumber &arg1, Operation operation, const KNumber &arg2) { if (!percent_mode_ || Operator[operation].prcnt_ptr == NULL) { return (Operator[operation].arith_ptr)(arg1, arg2); } else { percent_mode_ = false; return (Operator[operation].prcnt_ptr)(arg1, arg2); } } void CalcEngine::enterOperation(const KNumber &number, Operation func) { Node tmp_node; if (func == FUNC_BRACKET) { tmp_node.number = KNumber::Zero; tmp_node.operation = FUNC_BRACKET; stack_.push(tmp_node); return; } if (func == FUNC_PERCENT) { percent_mode_ = true; } tmp_node.number = number; tmp_node.operation = func; + if (KCalcSettings::repeatLastOperation()) { + if (func != FUNC_EQUAL && func != FUNC_PERCENT) { + last_operation_ = tmp_node.operation; + repeat_mode_ = false; + } + + if (func == FUNC_EQUAL || func == FUNC_PERCENT) { + if (!repeat_mode_) { + repeat_mode_ = last_operation_ != FUNC_EQUAL; + last_repeat_number_ = number; + } else { + Node repeat_node; + repeat_node.operation = last_operation_; + repeat_node.number = number; + tmp_node.number = last_repeat_number_; + stack_.push(repeat_node); + } + } + } + stack_.push(tmp_node); evalStack(); } bool CalcEngine::evalStack() { // this should never happen Q_ASSERT(!stack_.isEmpty()); Node tmp_node = stack_.pop(); while (! stack_.isEmpty()) { Node tmp_node2 = stack_.pop(); if (Operator[tmp_node.operation].precedence <= Operator[tmp_node2.operation].precedence) { if (tmp_node2.operation == FUNC_BRACKET) continue; const KNumber tmp_result = evalOperation(tmp_node2.number, tmp_node2.operation, tmp_node.number); tmp_node.number = tmp_result; } else { stack_.push(tmp_node2); break; } } if (tmp_node.operation != FUNC_EQUAL && tmp_node.operation != FUNC_PERCENT) stack_.push(tmp_node); last_number_ = tmp_node.number; return true; } void CalcEngine::Reset() { percent_mode_ = false; + repeat_mode_ = false; + last_operation_ = FUNC_EQUAL; error_ = false; last_number_ = KNumber::Zero; stack_.clear(); } diff --git a/kcalc_core.h b/kcalc_core.h index a5947ce..02c2609 100644 --- a/kcalc_core.h +++ b/kcalc_core.h @@ -1,149 +1,153 @@ /* Copyright (C) 2001 - 2013 Evan Teran evan.teran@gmail.com Copyright (C) 1996 - 2000 Bernd Johannes Wuebben wuebben@kde.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef KCALC_CORE_H_ #define KCALC_CORE_H_ #include #include "stats.h" #include "knumber.h" class CalcEngine { public: // operations that can be stored in calculation stack enum Operation { FUNC_EQUAL, FUNC_PERCENT, FUNC_BRACKET, FUNC_OR, FUNC_XOR, FUNC_AND, FUNC_LSH, FUNC_RSH, FUNC_ADD, FUNC_SUBTRACT, FUNC_MULTIPLY, FUNC_DIVIDE, FUNC_MOD, FUNC_INTDIV, FUNC_BINOM, FUNC_POWER, FUNC_PWR_ROOT }; CalcEngine(); KNumber lastOutput(bool &error) const; void enterOperation(const KNumber &num, Operation func); void ArcCosDeg(const KNumber &input); void ArcCosRad(const KNumber &input); void ArcCosGrad(const KNumber &input); void ArcSinDeg(const KNumber &input); void ArcSinRad(const KNumber &input); void ArcSinGrad(const KNumber &input); void ArcTangensDeg(const KNumber &input); void ArcTangensRad(const KNumber &input); void ArcTangensGrad(const KNumber &input); void AreaCosHyp(const KNumber &input); void AreaSinHyp(const KNumber &input); void AreaTangensHyp(const KNumber &input); void Complement(const KNumber &input); void CosDeg(const KNumber &input); void CosRad(const KNumber &input); void CosGrad(const KNumber &input); void CosHyp(const KNumber &input); void Cube(const KNumber &input); void CubeRoot(const KNumber &input); void Exp(const KNumber &input); void Exp10(const KNumber &input); void Factorial(const KNumber &input); void Gamma(const KNumber &input); void InvertSign(const KNumber &input); void Ln(const KNumber &input); void Log10(const KNumber &input); void ParenClose(KNumber input); void ParenOpen(const KNumber &input); void Reciprocal(const KNumber &input); void SinDeg(const KNumber &input); void SinGrad(const KNumber &input); void SinRad(const KNumber &input); void SinHyp(const KNumber &input); void Square(const KNumber &input); void SquareRoot(const KNumber &input); void StatClearAll(const KNumber &input); void StatCount(const KNumber &input); void StatDataNew(const KNumber &input); void StatDataDel(const KNumber &input); void StatMean(const KNumber &input); void StatMedian(const KNumber &input); void StatStdDeviation(const KNumber &input); void StatStdSample(const KNumber &input); void StatSum(const KNumber &input); void StatSumSquares(const KNumber &input); void TangensDeg(const KNumber &input); void TangensRad(const KNumber &input); void TangensGrad(const KNumber &input); void TangensHyp(const KNumber &input); void Reset(); private: KStats stats; struct Node { KNumber number; Operation operation; }; // Stack holds all operations and numbers that have not yet been // processed, e.g. user types "2+3*", the calculation can not be // executed, because "*" has a higher precedence than "+", so we // need to wait for the next number. // // In the stack this would be stored as ((2,+),(3,*),...) // // "enterOperation": If the introduced Operation has lower priority // than the preceding operations in the stack, then we can start to // evaluate the stack (with "evalStack"). Otherwise we append the new // Operation and number to the stack. // // E.g. "2*3+" evaluates to "6+", but "2+3*" can not be evaluated // yet. // // We also take care of brackets, by writing a marker "FUNC_BRACKET" // into the stack, each time the user opens one. When a bracket is // closed, everything in the stack is evaluated until the first // marker "FUNC_BRACKET" found. QStack stack_; KNumber last_number_; + Operation last_operation_; + KNumber last_repeat_number_; + bool repeat_mode_; + bool percent_mode_; bool evalStack(); KNumber evalOperation(const KNumber &arg1, Operation operation, const KNumber &arg2); }; #endif