/* $Id: kcalc_core.cpp 541875 2006-05-17 14:42:51Z zander $ kCalculator, a scientific calculator for the X window system using the TQt widget libraries, available at no cost at http://www.troll.no The stack engine conatined in this file was take from Martin Bartlett's xfrmcalc portions: Copyright (C) 1996 Bernd Johannes Wuebben wuebben@math.cornell.edu portions: 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include "kcalc.h" //What's that?!? (Werner) //#define i18n( x ) x // Undefine HAVE_LONG_DOUBLE for Beta 4 since RedHat 5.0 comes with a borken // glibc #ifdef HAVE_LONG_DOUBLE #undef HAVE_LONG_DOUBLE #endif #ifndef HAVE_FUNC_ISINF #ifdef HAVE_IEEEFP_H #include #else #include #endif int isinf(double x) { return !finite(x) && x==x; } #endif extern TQPtrList temp_stack; last_input_type last_input; char display_str[DSP_SIZE+1]; stack_ptr top_of_stack = NULL; stack_ptr top_type_stack[2] = { NULL, NULL }; int stack_next, stack_last; stack_item process_stack[STACK_SIZE]; int inverse = FALSE; int precedence_base = 0; num_base current_base = NB_DECIMAL; int input_limit = 0; int input_count = 0; //item_contents display_data = { ITEM_AMOUNT, 0 }; item_contents display_data; int display_size = DEC_SIZE; int hyp_mode = 0; int angle_mode = ANG_DEGREE; int refresh_display; // if 1 we start a new number int display_error = 0; int decimal_point = 0; int percent_mode = 0; bool eestate = false; // if true then we are in ee input mode CALCAMNT pi; CALCAMNT memory_num = 0.0; int precedence[14] = { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 6 }; int adjust_op[14][3] = { {FUNC_NULL, FUNC_NULL, FUNC_NULL}, {FUNC_OR, FUNC_OR, FUNC_XOR }, {FUNC_XOR, FUNC_XOR, FUNC_XOR }, {FUNC_AND, FUNC_AND, FUNC_AND }, {FUNC_LSH, FUNC_LSH, FUNC_RSH }, {FUNC_RSH, FUNC_RSH, FUNC_RSH }, {FUNC_ADD, FUNC_ADD, FUNC_ADD }, {FUNC_SUBTRACT, FUNC_SUBTRACT, FUNC_SUBTRACT}, {FUNC_MULTIPLY, FUNC_MULTIPLY, FUNC_MULTIPLY}, {FUNC_DIVIDE, FUNC_DIVIDE, FUNC_DIVIDE}, {FUNC_MOD, FUNC_MOD, FUNC_INTDIV }, {FUNC_POWER, FUNC_POWER, FUNC_PWR_ROOT}, {FUNC_PWR_ROOT, FUNC_PWR_ROOT, FUNC_PWR_ROOT}, {FUNC_INTDIV, FUNC_INTDIV, FUNC_INTDIV}, }; /* char *function_desc[] = { "Null", "Or", "Exclusive Or", "And", "Left Shift", "Right Shift", "Add", "Subtract", "Multiply", "Divide", "Modulus" "Power" "Reciprocal Power" "Integer Division" }; */ Arith Arith_ops[14] = { NULL, ExecOr, ExecXor, ExecAnd, ExecLsh, ExecRsh, ExecAdd, ExecSubtract, ExecMultiply, ExecDivide, ExecMod, ExecPower, ExecPwrRoot, ExecIntDiv }; Prcnt Prcnt_ops[14] = { NULL, NULL, NULL, NULL, NULL, NULL, ExecAddSubP, ExecAddSubP, ExecMultiplyP, ExecDivideP, ExecDivideP, ExecPowerP, ExecPwrRootP, ExecDivideP }; void TQtCalculator::InitializeCalculator(void) { // // Basic initialization involves initializing the calcultion // stack, forcing the display to refresh to zero, and setting // up the floating point excetion signal handler to trap the // errors that the code can/has not been written to trap. // // We also calculate pi as double the arc sine of 1. // display_data.s_item_type = ITEM_AMOUNT; display_data.s_item_data.item_amount = 0.0; display_data.s_item_data.item_func_data.item_function = 0; display_data.s_item_data.item_func_data.item_precedence = 0; void fpe_handler(int fpe_parm); struct sigaction fpe_trap; fpe_trap.sa_handler = &fpe_handler; #ifdef SA_RESTART fpe_trap.sa_flags = SA_RESTART; #endif sigaction(SIGFPE, &fpe_trap, NULL); RefreshCalculator(); pi = ASIN(1.0) * 2L; } void fpe_handler(int fpe_parm) { (void) fpe_parm; display_error = 1; DISPLAY_AMOUNT = 0L; } void TQtCalculator::setData( const TQRect& _range, const char *_sheet ) { sheet_range = _range; sheet_name = _sheet; } void TQtCalculator::setValue( double _value ) { last_input = DIGIT; DISPLAY_AMOUNT = _value; decimal_point = 0; refresh_display = 1; input_count = 0; UpdateDisplay(); } void TQtCalculator::setLabel( const char *_text ) { last_input = DIGIT; DISPLAY_AMOUNT = 0L; decimal_point = 0; refresh_display = 0; input_count = 0; calc_display->setText( _text ); } void TQtCalculator::RefreshCalculator(void) { InitStack(); display_error = 0; DISPLAY_AMOUNT = 0L; inverse = FALSE; UpdateDisplay(); last_input = DIGIT; // must set last to DIGIT after Update Display in order // not to get a display holding e.g. 0.000 input_count = 0; decimal_point = 0; } void TQtCalculator::EnterDigit(int data) { if(eestate){ TQString string; string.setNum(data); strcat(display_str, string.latin1()); DISPLAY_AMOUNT = (CALCAMNT) strtod(display_str,0); UpdateDisplay(); return; } last_input = DIGIT; if (refresh_display) { DISPLAY_AMOUNT = 0L; decimal_point = 0; refresh_display = 0; input_count = 0; } if (!(input_limit && input_count >= input_limit)) if (DISPLAY_AMOUNT < 0) DISPLAY_AMOUNT = decimal_point ? DISPLAY_AMOUNT - ((CALCAMNT)data / POW((float)current_base, decimal_point++)) : (current_base * DISPLAY_AMOUNT) - data; else DISPLAY_AMOUNT = decimal_point ? DISPLAY_AMOUNT + ((CALCAMNT)data / POW((float)current_base, decimal_point++)) : (current_base * DISPLAY_AMOUNT) + data; if (decimal_point){ input_count ++; #ifdef MYDEBUG printf("EnterDigit() inc dec.point:%d\n",input_count); #endif } UpdateDisplay(); } void TQtCalculator::button0() { EnterDigit(0); } void TQtCalculator::button1() { EnterDigit(1); } void TQtCalculator::button2() { if (current_base == NB_BINARY) return; EnterDigit(2); } void TQtCalculator::button3() { if (current_base == NB_BINARY) return; EnterDigit(3); } void TQtCalculator::button4() { if (current_base == NB_BINARY) return; EnterDigit(4); } void TQtCalculator::button5() { if (current_base == NB_BINARY) return; EnterDigit(5); } void TQtCalculator::button6() { if (current_base == NB_BINARY) return; EnterDigit(6); } void TQtCalculator::button7() { if (current_base == NB_BINARY) return; EnterDigit(7); } void TQtCalculator::button8() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL)) return; EnterDigit(8); } void TQtCalculator::button9() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL)) return; EnterDigit(9); } void TQtCalculator::buttonA() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL) || (current_base == NB_DECIMAL)) return; EnterDigit(10); } void TQtCalculator::buttonB() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL) || (current_base == NB_DECIMAL)) return; EnterDigit(11); } void TQtCalculator::buttonC() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL) || (current_base == NB_DECIMAL)) return; EnterDigit(12); } void TQtCalculator::buttonD() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL) || (current_base == NB_DECIMAL)) return; EnterDigit(13); } void TQtCalculator::buttonE() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL) || (current_base == NB_DECIMAL)) return; EnterDigit(14); } void TQtCalculator::buttonF() { if ((current_base == NB_BINARY) || (current_base == NB_OCTAL) || (current_base == NB_DECIMAL)) return; EnterDigit(15); } void TQtCalculator::EnterDecimal() { if(eestate){ KNotifyClient::beep(); return; } decimal_point = 1; if (refresh_display) { DISPLAY_AMOUNT = 0L; refresh_display = 0; input_count = 0; } if (last_input == DIGIT && !strpbrk( display_str,".")){ // if the last input was a DIGIT and we don't have already a period in our // display string then display a period calc_display->setText(strcat(display_str, ".")); } else { // the last input wasn't a DIGIT so we are about to // input a new number in particular we neet do display a "0.". DISPLAY_AMOUNT = 0L; refresh_display = 0; // decimal_point = 1; // input_count = 1; strcpy(display_str, "0."); calc_display->setText(display_str); } } void TQtCalculator::Or() { eestate = false; if (inverse){ EnterStackFunction(2); // XOR inverse = FALSE; } else { EnterStackFunction(1); // OR } last_input = OPERATION; } void TQtCalculator::And() { eestate = false; last_input = OPERATION; EnterStackFunction(3); } void TQtCalculator::Shift() { eestate = false; last_input = OPERATION; if (inverse){ EnterStackFunction(5); // Rsh inverse = FALSE; } else { EnterStackFunction(4); // Lsh } } void TQtCalculator::Plus() { eestate = false; last_input = OPERATION; EnterStackFunction(6); } void TQtCalculator::Minus() { eestate = false; last_input = OPERATION; EnterStackFunction(7); } void TQtCalculator::Multiply() { eestate = false; last_input = OPERATION; EnterStackFunction(8); } void TQtCalculator::Divide() { eestate = false; last_input = OPERATION; EnterStackFunction(9); } void TQtCalculator::Mod() { eestate = false; last_input = OPERATION; if (inverse){ EnterStackFunction(13); // InvMod inverse = FALSE; } else { EnterStackFunction(10); // Mod } } void TQtCalculator::Power() { eestate = false; last_input = OPERATION; if (inverse){ EnterStackFunction(12); // InvPower inverse = FALSE; } else { EnterStackFunction(11); // Power } } void TQtCalculator::EnterStackFunction(int data) { item_contents new_item; int new_precedence; int dummy; dummy = 0; /* if (inverse ) { dummy = 3; inverse = FALSE; } else { dummy = 1; } */ // printf("data %d dummy %d\n",data,dummy); data = adjust_op[data][dummy]; // printf("data %d \n",data ); PushStack(&display_data); new_item.s_item_type = ITEM_FUNCTION; new_item.s_item_data.item_func_data.item_function = data; new_item.s_item_data.item_func_data.item_precedence = new_precedence = precedence[data] + precedence_base; refresh_display = 1; if (UpdateStack(new_precedence)) UpdateDisplay(); PushStack(&new_item); } void TQtCalculator::EnterNegate() { if(eestate){ TQString string; string = display_str; int pos; pos = string.findRev('e',-1,false); if(pos == -1) return; if(display_str[pos+1] == '+') display_str[pos+1] = '-'; else{ if(display_str[pos+1] == '-') display_str[pos+1] = '+'; else{ string.insert(pos +1,'-'); strncpy(display_str,string.latin1(),DSP_SIZE); } } DISPLAY_AMOUNT = (CALCAMNT)strtod(display_str,0); UpdateDisplay(); } else{ // last_input = OPERATION; if (DISPLAY_AMOUNT != 0) { DISPLAY_AMOUNT *= -1; UpdateDisplay(); } } last_input = DIGIT; } void TQtCalculator::EnterOpenParen() { eestate = false; last_input = OPERATION; precedence_base += PRECEDENCE_INCR; refresh_display = 1; } void TQtCalculator::EnterCloseParen() { eestate = false; last_input = OPERATION; PushStack(&display_data); refresh_display = 1; if (UpdateStack(precedence_base)) UpdateDisplay(); if ((precedence_base -= PRECEDENCE_INCR) < 0) precedence_base = 0; } void TQtCalculator::EnterRecip() { eestate = false; last_input = OPERATION; DISPLAY_AMOUNT = 1 / DISPLAY_AMOUNT; refresh_display = 1; UpdateDisplay(); } void TQtCalculator::EnterInt() { eestate = false; CALCAMNT work_amount1 = 0.0, work_amount2 = 0.0; last_input = OPERATION; if (!inverse){ work_amount2 = MODF(DISPLAY_AMOUNT, &work_amount1); DISPLAY_AMOUNT = work_amount2 ; } else { DISPLAY_AMOUNT = work_amount1; inverse = FALSE; } refresh_display = 1; UpdateDisplay(); } void TQtCalculator::EnterFactorial() { eestate = false; CALCAMNT work_amount1, work_amount2; int incr; MODF(DISPLAY_AMOUNT, &work_amount1); incr = work_amount1 < 0 ? -1 : 1; work_amount2 = work_amount1 - incr; while (work_amount1 != 0 && work_amount2 != 0 && !display_error) { work_amount1 *= work_amount2; work_amount2 -= incr; if(isinf(work_amount1)) { display_error=1; break; } } if( work_amount1 == 0.0) work_amount1 = 1.0; DISPLAY_AMOUNT = work_amount1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::EnterSquare() { eestate = false; if (!inverse){ DISPLAY_AMOUNT *= DISPLAY_AMOUNT; } else if (DISPLAY_AMOUNT < 0) display_error = 1; else DISPLAY_AMOUNT = SQRT(DISPLAY_AMOUNT); refresh_display = 1; inverse = FALSE; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::EnterNotCmp() { eestate = false; CALCAMNT boh_work_d; long boh_work; MODF(DISPLAY_AMOUNT, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) display_error = 1; else { boh_work = (long int) boh_work_d; DISPLAY_AMOUNT = ~boh_work; } refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::EnterHyp() { switch(kcalcdefaults.style){ case 2: case 1:{ if ( !sheet_name.isEmpty() ) useData(); if(!inverse){ eestate = false; // terminate ee input mode DISPLAY_AMOUNT = stats.count(); last_input = OPERATION; refresh_display = 1; UpdateDisplay(); } else{ inverse = false; eestate = false; // terminate ee input mode DISPLAY_AMOUNT = stats.sum(); last_input = OPERATION; refresh_display = 1; UpdateDisplay(); } break; } case 0: { // toggle between hyperbolic and standart trig functions hyp_mode = !hyp_mode; if (hyp_mode){ statusHYPLabel->setText("HYP"); } else{ statusHYPLabel->setText(""); } break; } } } void TQtCalculator::ExecSin(){ switch(kcalcdefaults.style){ case 0:{ // trig mode ComputeSin(); break; } case 1:{ // stats mode if ( !sheet_name.isEmpty() ) useData(); ComputeMean(); break; } case 2:{ // sheet mode if ( !sheet_name.isEmpty() ) useData(); ComputeMin(); break; } } } void TQtCalculator::ComputeSum() { inverse = false; eestate = false; DISPLAY_AMOUNT = stats.sum(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::ComputeMul() { inverse = false; eestate = false; DISPLAY_AMOUNT = stats.mul(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::ComputeMin() { inverse = false; eestate = false; DISPLAY_AMOUNT = stats.min(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::ComputeMax() { inverse = false; eestate = false; DISPLAY_AMOUNT = stats.max(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::ComputeMean(){ if(!inverse){ eestate = false; DISPLAY_AMOUNT = stats.mean(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } else{ inverse = false; eestate = false; DISPLAY_AMOUNT = stats.sum_of_squares(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } } void TQtCalculator::ComputeSin() { CALCAMNT work_amount; eestate = false; work_amount = DISPLAY_AMOUNT; if (hyp_mode){ // sinh or arcsinh if (!inverse){ DISPLAY_AMOUNT = SINH( work_amount); } else { DISPLAY_AMOUNT = ASINH( work_amount); if (errno == EDOM || errno == ERANGE) display_error = 1; inverse = FALSE; // reset the inverse flag } } else { // sine or arcsine if (!inverse){ // sine switch (angle_mode) { case ANG_DEGREE: work_amount = DEG2RAD(DISPLAY_AMOUNT); break; case ANG_GRADIENT: work_amount = GRA2RAD(DISPLAY_AMOUNT); break; case ANG_RADIAN: work_amount = DISPLAY_AMOUNT; break; } DISPLAY_AMOUNT = SIN( work_amount); } else { // arcsine DISPLAY_AMOUNT = ASIN(work_amount); switch (angle_mode) { case ANG_DEGREE: work_amount = RAD2DEG(DISPLAY_AMOUNT); break; case ANG_GRADIENT: work_amount = RAD2GRA(DISPLAY_AMOUNT); break; case ANG_RADIAN: work_amount = DISPLAY_AMOUNT; break; } DISPLAY_AMOUNT = work_amount; if (errno == EDOM || errno == ERANGE) display_error = 1; inverse = FALSE; // reset the inverse flag } } // Now a cheat to help the weird case of COS 90 degrees not being 0!!! if (DISPLAY_AMOUNT < POS_ZERO && DISPLAY_AMOUNT > NEG_ZERO) DISPLAY_AMOUNT=0; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::ExecCos(){ switch(kcalcdefaults.style){ case 0:{ // trig mode ComputeCos(); break; } case 1:{ // stats mode if ( !sheet_name.isEmpty() ) useData(); ComputeStd(); break; } case 2:{ // sheet mode if ( !sheet_name.isEmpty() ) useData(); ComputeMax(); break; } } } void TQtCalculator::ComputeStd(){ if(!inverse){ // std (n-1) inverse = false; eestate = false; DISPLAY_AMOUNT = stats.std(); if (stats.error()){ display_error = 1; } refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } else{ // std (n) inverse = false; eestate = false; DISPLAY_AMOUNT = stats.sample_std(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } } void TQtCalculator::ComputeCos() { CALCAMNT work_amount; eestate = false; work_amount = DISPLAY_AMOUNT; if (hyp_mode){ // cosh or arccosh if (!inverse){ DISPLAY_AMOUNT = COSH( work_amount); } else { DISPLAY_AMOUNT = ACOSH( work_amount); if (errno == EDOM || errno == ERANGE) display_error = 1; inverse = FALSE; // reset the inverse flag } } else { // cosine or arccosine if (!inverse){ // sine switch (angle_mode) { case ANG_DEGREE: work_amount = DEG2RAD(DISPLAY_AMOUNT); break; case ANG_GRADIENT: work_amount = GRA2RAD(DISPLAY_AMOUNT); break; case ANG_RADIAN: work_amount = DISPLAY_AMOUNT; break; } DISPLAY_AMOUNT = COS( work_amount); } else { // arccosine DISPLAY_AMOUNT = ACOS(work_amount); switch (angle_mode) { case ANG_DEGREE: work_amount = RAD2DEG(DISPLAY_AMOUNT); break; case ANG_GRADIENT: work_amount = RAD2GRA(DISPLAY_AMOUNT); break; case ANG_RADIAN: work_amount = DISPLAY_AMOUNT; break; } DISPLAY_AMOUNT = work_amount; if (errno == EDOM || errno == ERANGE) display_error = 1; inverse = FALSE; // reset the inverse flag } } // Now a cheat to help the weird case of COS 90 degrees not being 0!!! if (DISPLAY_AMOUNT < POS_ZERO && DISPLAY_AMOUNT > NEG_ZERO) DISPLAY_AMOUNT=0; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::ExecTan(){ switch(kcalcdefaults.style){ case 0:{ // trig mode ComputeTan(); break; } case 2: case 1:{ // stats mode if ( !sheet_name.isEmpty() ) useData(); ComputeMedean(); break; } } } void TQtCalculator::ComputeMedean(){ if(!inverse){ // std (n-1) inverse = false; eestate = false; DISPLAY_AMOUNT = stats.median(); if (stats.error()){ display_error = 1; } refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } else{ // std (n) inverse = false; eestate = false; DISPLAY_AMOUNT = stats.median(); if (stats.error()) display_error = 1; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } } void TQtCalculator::ComputeTan() { CALCAMNT work_amount; eestate = false; work_amount = DISPLAY_AMOUNT; if (hyp_mode){ // tanh or arctanh if (!inverse){ DISPLAY_AMOUNT = TANH( work_amount); } else { DISPLAY_AMOUNT = ATANH( work_amount); if (errno == EDOM || errno == ERANGE) display_error = 1; inverse = FALSE; // reset the inverse flag } } else { // tan or arctan if (!inverse){ // tan switch (angle_mode) { case ANG_DEGREE: work_amount = DEG2RAD(DISPLAY_AMOUNT); break; case ANG_GRADIENT: work_amount = GRA2RAD(DISPLAY_AMOUNT); break; case ANG_RADIAN: work_amount = DISPLAY_AMOUNT; break; } DISPLAY_AMOUNT = TAN( work_amount); if (errno == EDOM || errno == ERANGE) display_error = 1; } else { // arctan DISPLAY_AMOUNT = ATAN(work_amount); switch (angle_mode) { case ANG_DEGREE: work_amount = RAD2DEG(DISPLAY_AMOUNT); break; case ANG_GRADIENT: work_amount = RAD2GRA(DISPLAY_AMOUNT); break; case ANG_RADIAN: work_amount = DISPLAY_AMOUNT; break; } DISPLAY_AMOUNT = work_amount; if (errno == EDOM || errno == ERANGE) display_error = 1; inverse = FALSE; // reset the inverse flag } } // Now a cheat to help the weird case of COS 90 degrees not being 0!!! if (DISPLAY_AMOUNT < POS_ZERO && DISPLAY_AMOUNT > NEG_ZERO) DISPLAY_AMOUNT=0; refresh_display = 1; last_input = OPERATION; UpdateDisplay(); } void TQtCalculator::EnterPercent() { eestate = false; last_input = OPERATION; percent_mode = 1; EnterEqual(); percent_mode = 0; } void TQtCalculator::EnterLogr() { switch(kcalcdefaults.style){ case 2: { if ( !sheet_name.isEmpty() ) useData(); ComputeSum(); break; } case 1:{ if ( !sheet_name.isEmpty() ) useData(); if(!inverse){ eestate = false; // terminate ee input mode stats.enterData(DISPLAY_AMOUNT); last_input = OPERATION; refresh_display = 1; DISPLAY_AMOUNT = stats.count(); UpdateDisplay(); } else{ inverse = false; last_input = OPERATION; refresh_display = 1; stats.clearLast(); setStatusLabel("Last stat item erased"); DISPLAY_AMOUNT = stats.count(); UpdateDisplay(); } break; } case 0:{ eestate = false; last_input = OPERATION; if (!inverse) { if (DISPLAY_AMOUNT <= 0) display_error = 1; else DISPLAY_AMOUNT = LOG_TEN(DISPLAY_AMOUNT); refresh_display = 1; UpdateDisplay(); } else if (inverse) { DISPLAY_AMOUNT = POW(10, DISPLAY_AMOUNT); refresh_display = 1; inverse = FALSE; UpdateDisplay(); } break; } } } void TQtCalculator::EnterLogn() { switch(kcalcdefaults.style){ case 2:{ if ( !sheet_name.isEmpty() ) useData(); ComputeMul(); break; } case 1:{ if ( !sheet_name.isEmpty() ) useData(); if(!inverse){ stats.clearAll(); setStatusLabel(i18n("Stat mem cleared")); } else{ inverse = false; UpdateDisplay(); } break; } case 0:{ eestate = false; last_input = OPERATION; if (!inverse) { if (DISPLAY_AMOUNT <= 0) display_error = 1; else DISPLAY_AMOUNT = LOG(DISPLAY_AMOUNT); refresh_display = 1; UpdateDisplay(); } else if (inverse) { DISPLAY_AMOUNT = EXP(DISPLAY_AMOUNT); refresh_display = 1; inverse =FALSE; UpdateDisplay(); } break; } } } void TQtCalculator::base_selected(int number){ switch(number){ case 0: SetHex(); break; case 1: SetDec(); break; case 2: SetOct(); break; case 3: SetBin(); break; default: // we shouldn't ever end up here SetDec(); } } void TQtCalculator::angle_selected(int number){ switch(number){ case 0: SetDeg(); break; case 1: SetRad(); break; case 2: SetGra(); break; default: // we shouldn't ever end up here SetRad(); } } void TQtCalculator::SetInverse(){ inverse = ! inverse; if (inverse){ statusINVLabel->setText("INV"); } else{ statusINVLabel->setText("NORM"); } } void TQtCalculator::SetDeg() { angle_mode = ANG_DEGREE; } void TQtCalculator::SetGra() { angle_mode = ANG_GRADIENT; } void TQtCalculator::SetRad() { angle_mode = ANG_RADIAN; } void TQtCalculator::SetHex() { /* * Set Hex Mode */ current_base = NB_HEX; display_size = BOH_SIZE; decimal_point = 0; input_limit = 8; UpdateDisplay(); } void TQtCalculator::SetOct() { /* * Set Oct Mode */ current_base = NB_OCTAL; display_size = BOH_SIZE; decimal_point = 0; input_limit = 11; UpdateDisplay(); } void TQtCalculator::SetBin() { /* * Set Bin Mode */ current_base = NB_BINARY; display_size = BOH_SIZE; decimal_point = 0; input_limit = 16; UpdateDisplay(); } void TQtCalculator::SetDec() { /* * Set Dec Mode */ current_base = NB_DECIMAL; display_size = DEC_SIZE; input_limit = 0; UpdateDisplay(); } void TQtCalculator::EE() { if(inverse){ DISPLAY_AMOUNT = pi; inverse = FALSE; UpdateDisplay(); } else{ if(eestate == true) eestate = false; else{ eestate = true; strcat(display_str,"e"); } UpdateDisplay(); } } void TQtCalculator::MR() { eestate = false; last_input = OPERATION; DISPLAY_AMOUNT = memory_num; refresh_display = 1; UpdateDisplay(); } void TQtCalculator::Mplusminus() { eestate = false; EnterEqual(); if (!inverse) memory_num += DISPLAY_AMOUNT; else { memory_num -= DISPLAY_AMOUNT; inverse = FALSE; } } void TQtCalculator::MC() { memory_num = 0; refresh_display = 1; } void TQtCalculator::EnterEqual() { eestate = false; last_input = OPERATION; PushStack(&display_data); refresh_display = 1; /* if (UpdateStack(0))*/ UpdateStack(0); UpdateDisplay(); precedence_base = 0; CALCAMNT* number ; if(temp_stack.count() > TEMP_STACK_SIZE){ number = temp_stack.getFirst(); temp_stack.removeFirst(); if(number) free(number); } number = (CALCAMNT*) malloc(sizeof(CALCAMNT)); *number = DISPLAY_AMOUNT; //printf("appending %Lg\n",*number); temp_stack.append(number); } void TQtCalculator::Clear(){ eestate = false; input_count = 0; decimal_point = 0; if(last_input == OPERATION){ // printf("LAST_INPUT = OPERATION\n"); last_input = DIGIT; PopStack(); } else{ // printf("LAST_INPUT = DIGIT\n"); last_input = DIGIT; } if( display_error){ display_error = 0; refresh_display = 0; } if (!refresh_display) { DISPLAY_AMOUNT = 0L; UpdateDisplay(); } } void TQtCalculator::ClearAll() { eestate = false; // last_input = OPERATION; last_input = DIGIT; RefreshCalculator(); refresh_display = 1; } void TQtCalculator::UpdateDisplay() { // this needs to be rewritten based on whether we are currently // inputting a number so that the period and the 0 after a period // are correctly displayed. CALCAMNT boh_work_d; long boh_work = 0; int str_size = 0; if(eestate && (current_base == NB_DECIMAL)){ calc_display->setText(display_str); return; } if (current_base != NB_DECIMAL) { MODF(DISPLAY_AMOUNT, &boh_work_d); if (boh_work_d < LONG_MIN || boh_work_d > ULONG_MAX) display_error = 1; /* * We may be in that never-never land where boh numbers * turn from positive to negative - if so then we do * just that, allowing boh negative numbers to be entered * as read (from dumps and the like!) */ else if (boh_work_d > LONG_MAX) { DISPLAY_AMOUNT = LONG_MIN+(boh_work_d-LONG_MAX-1); boh_work = (long)DISPLAY_AMOUNT; } else { DISPLAY_AMOUNT = boh_work_d; boh_work = (long) boh_work_d; } } if (!display_error) { if (current_base == NB_BINARY) str_size = cvb(display_str, boh_work, BOH_SIZE); else if (current_base == NB_OCTAL) str_size = sprintf(display_str, "%lo", boh_work); else if (current_base == NB_DECIMAL) { if(!kcalcdefaults.fixed || last_input == DIGIT || (DISPLAY_AMOUNT > 1.0e+16)){ // if I don't guard against the DISPLAY_AMOUNT being too large // kcalc will segfault on larger amount. Such as from typing // from 5*5******* str_size = sprintf(display_str, #ifdef HAVE_LONG_DOUBLE "%.*Lg", // was *Lg kcalcdefaults.precision +1, #else "%.*g", kcalcdefaults.precision +1, #endif DISPLAY_AMOUNT); } else{//fixed str_size = sprintf(display_str, #ifdef HAVE_LONG_DOUBLE "%.*Lf", // was *Lg kcalcdefaults.fixedprecision , #else "%.*f", kcalcdefaults.fixedprecision , #endif DISPLAY_AMOUNT); }// fixed if ( input_count > 0 && !strpbrk(display_str,"e") && last_input == DIGIT ) { #ifdef HAVE_LONG_DOUBLE str_size = sprintf(display_str, "%.*Lf", (kcalcdefaults.precision +1 > input_count)? input_count : kcalcdefaults.precision , DISPLAY_AMOUNT); #else str_size = sprintf(display_str, "%.*f", (kcalcdefaults.precision +1 > input_count)? input_count : kcalcdefaults.precision , DISPLAY_AMOUNT); #endif } } else if (current_base == NB_HEX) str_size = sprintf(display_str, "%lX", boh_work); else display_error = 1; } if (display_error || str_size < 0) { display_error = 1; strcpy(display_str,"Error"); if(kcalcdefaults.beep) KNotifyClient::beep(); } calc_display->setText(display_str); if (inverse){ statusINVLabel->setText("INV"); } else{ statusINVLabel->setText("NORM"); } if (hyp_mode){ statusHYPLabel->setText("HYP"); } else{ statusHYPLabel->setText(""); } } int cvb(char *out_str, long amount, int max_digits) { /* * A routine that converts a long int to * binary display format */ char work_str[(sizeof(amount) * CHAR_BIT) + 1]; int work_char = 0, lead_zero = 1, lead_one = 1, lead_one_count = 0, work_size = sizeof(amount) * CHAR_BIT; unsigned long bit_mask = (1 << ((sizeof(amount) * CHAR_BIT) - 1)); while (bit_mask) { if (amount & bit_mask) { if (lead_one) lead_one_count++; lead_zero = 0; work_str[work_char++] = '1'; } else { lead_one = 0; if (!lead_zero) work_str[work_char++] = '0'; } bit_mask >>= 1; } if (!work_char) work_str[work_char++] = '0'; work_str[work_char] = '\0'; if (work_char-lead_one_count < max_digits) return strlen(strcpy(out_str, &work_str[lead_one_count ? work_size - max_digits : 0])); else return -1; } int UpdateStack(int run_precedence) { item_contents new_item, *top_item , *top_function; CALCAMNT left_op =0.0 , right_op =0.0; int op_function= 0, return_value = 0; new_item.s_item_type = ITEM_AMOUNT; while ((top_function = TopTypeStack(ITEM_FUNCTION)) && top_function->s_item_data.item_func_data.item_precedence >= run_precedence) { return_value = 1; if ((top_item = PopStack())->s_item_type != ITEM_AMOUNT){ KMessageBox::error( 0, "Stack processing error - right_op"); } right_op = top_item->s_item_data.item_amount; if (!((top_item = PopStack()) && top_item->s_item_type == ITEM_FUNCTION)) { KMessageBox::error( 0, "Stack processing error - function"); } op_function = top_item->s_item_data.item_func_data.item_function; if (!((top_item = PopStack()) && top_item->s_item_type == ITEM_AMOUNT)) { KMessageBox::error( 0, "Stack processing error - left_op"); } left_op = top_item->s_item_data.item_amount; new_item.s_item_data.item_amount = (Arith_ops[op_function])(left_op, right_op); PushStack(&new_item); } if (return_value && percent_mode && !display_error && Prcnt_ops[op_function] != NULL){ new_item.s_item_data.item_amount = (Prcnt_ops[op_function])(left_op, right_op, new_item.s_item_data.item_amount); PushStack(&new_item); } if (return_value) DISPLAY_AMOUNT = new_item.s_item_data.item_amount; return return_value; } int isoddint(CALCAMNT input) { CALCAMNT dummy; /* * Routine to check if CALCAMNT is an Odd integer */ return (MODF(input, &dummy) == 0.0 && MODF(input/2, &dummy) == 0.5); } CALCAMNT ExecOr(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecOr\n"); CALCAMNT boh_work_d; long boh_work_l, boh_work_r; MODF(left_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_l = (long int)boh_work_d; MODF(right_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_r = (long int) boh_work_d; return (boh_work_l | boh_work_r); } CALCAMNT ExecXor(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecXOr\n"); CALCAMNT boh_work_d; long boh_work_l, boh_work_r; MODF(left_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_l = (long int) boh_work_d; MODF(right_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_r = (long int) boh_work_d; return (boh_work_l ^ boh_work_r); } CALCAMNT ExecAnd(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecAnd\n"); CALCAMNT boh_work_d; long boh_work_l, boh_work_r; MODF(left_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_l = (long int ) boh_work_d; MODF(right_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_r = (long int ) boh_work_d; return (boh_work_l & boh_work_r); } CALCAMNT ExecLsh(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecLsh\n"); CALCAMNT boh_work_d; long boh_work_l, boh_work_r; MODF(left_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_l = (long int) boh_work_d; MODF(right_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_r = (long int ) boh_work_d; return (boh_work_l << boh_work_r); } CALCAMNT ExecRsh(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecRsh\n"); CALCAMNT boh_work_d; long boh_work_l, boh_work_r; MODF(left_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_l = (long int ) boh_work_d; MODF(right_op, &boh_work_d); if (FABS(boh_work_d) > LONG_MAX) { display_error = 1; return 0; } boh_work_r = ( long int ) boh_work_d; return (boh_work_l >> boh_work_r); } CALCAMNT ExecAdd(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecAdd\n"); return left_op + right_op; } CALCAMNT ExecSubtract(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecSubtract\n"); return left_op - right_op; } CALCAMNT ExecMultiply(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecMulti\n"); return left_op * right_op; } CALCAMNT ExecDivide(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecDivide\n"); if (right_op == 0) { display_error = 1; return 0L; } else return left_op / right_op; } CALCAMNT ExecMod(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecMod\n"); CALCAMNT temp =0.0; if (right_op == 0) { display_error = 1; return 0L; } else { // x mod y should be the same as x mod -y, thus: right_op = FABS(right_op); temp = FMOD(left_op, right_op); // let's make sure that -7 mod 3 = 2 and NOT -1. // In other words we wand x mod 3 to be a _positive_ number // that is 0,1 or 2. if( temp < 0 ) temp = right_op + temp; temp = FABS(temp); return temp; } } CALCAMNT ExecIntDiv(CALCAMNT left_op, CALCAMNT right_op) { // printf("IndDiv\n"); if (right_op == 0) { display_error = 1; return 0L; } else { MODF(left_op / right_op, &left_op); return left_op; } } CALCAMNT ExecPower(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecPowser %g left_op, %g right_op\n",left_op, right_op); if (right_op == 0) return 1L; if (left_op < 0 && isoddint(1/right_op)) left_op = -1L * POW((-1L * left_op), right_op); else left_op = POW(left_op, right_op); if (errno == EDOM || errno == ERANGE) { display_error = 1; return 0; } else return left_op; } CALCAMNT ExecPwrRoot(CALCAMNT left_op, CALCAMNT right_op) { // printf("ExecPwrRoot %g left_op, %g right_op\n", left_op, right_op); if (right_op == 0) { display_error = 1; return 0L; } if (left_op < 0 && isoddint(right_op)) left_op = -1L * POW((-1L * left_op), (1L)/right_op); else left_op = POW(left_op, (1L)/right_op); if (errno == EDOM || errno == ERANGE) { display_error = 1; return 0; } else return left_op; } CALCAMNT ExecAddSubP(CALCAMNT left_op, CALCAMNT right_op, CALCAMNT result) { // printf("ExecAddsubP\n"); (void) left_op; if (result == 0) { display_error = 1; return 0; } else return (result * 100L) / right_op; } CALCAMNT ExecMultiplyP(CALCAMNT left_op, CALCAMNT right_op, CALCAMNT result) { // printf("ExecMultiplyP\n"); (void) left_op; (void) right_op; return (result / 100L); } CALCAMNT ExecDivideP(CALCAMNT left_op, CALCAMNT right_op, CALCAMNT result) { // printf("ExecDivideP\n"); (void) left_op; (void) right_op; return (result * 100L); } CALCAMNT ExecPowerP(CALCAMNT left_op, CALCAMNT right_op, CALCAMNT result) { // printf("ExecPowerP\n"); (void) result; return ExecPower(left_op, (right_op / 100L)); } CALCAMNT ExecPwrRootP(CALCAMNT left_op, CALCAMNT right_op, CALCAMNT result) { // printf("ExePwrRootP\n"); (void) result; if (right_op == 0) { display_error = 1; return 0; } else return ExecPower(left_op, (100L / right_op)); } stack_ptr AllocStackItem (void) { if (stack_next <= stack_last) { process_stack[stack_next].prior_item = NULL; process_stack[stack_next].prior_type = NULL; return &process_stack[stack_next++]; } KMessageBox::error( 0, "Stack Error !"); return &process_stack[stack_next]; } void UnAllocStackItem (stack_ptr return_item) { if (return_item != &process_stack[--stack_next]) { KMessageBox::error( 0, "Stack Error !"); } } void PushStack(item_contents *add_item) { /* * Add an item to the stack */ stack_ptr new_item = top_of_stack; if (!(new_item && new_item->item_value.s_item_type == add_item->s_item_type)) { new_item = AllocStackItem(); /* Get a new item */ /* * Chain new item to existing stacks */ new_item->prior_item = top_of_stack; top_of_stack = new_item; new_item->prior_type = top_type_stack[add_item->s_item_type]; top_type_stack[add_item->s_item_type] = new_item; } new_item->item_value = *add_item; /* assign contents*/ } item_contents *PopStack(void) { /* * Remove and return the top item in the stack */ static item_contents return_item; item_contents *return_item_ptr = NULL; stack_ptr return_stack_ptr; if ((return_stack_ptr = top_of_stack)) { return_item = top_of_stack->item_value; top_type_stack[return_item.s_item_type] = top_of_stack->prior_type; top_of_stack = top_of_stack->prior_item; UnAllocStackItem(return_stack_ptr); return_item_ptr = &return_item; } return return_item_ptr; } item_contents *TopOfStack(void) { /* * Return the top item in the stack without removing */ item_contents *return_item_ptr = NULL; if (top_of_stack) { return_item_ptr = &top_of_stack->item_value; } return return_item_ptr; } item_contents *TopTypeStack(item_type rqstd_type) { /* * Return the top item in the stack without removing */ item_contents *return_item_ptr = NULL; if (top_type_stack[rqstd_type]) { return_item_ptr = &top_type_stack[rqstd_type]->item_value; } return return_item_ptr; } /* * Stack storage management Data and Functions */ void InitStack (void) { stack_next = 0; stack_last = STACK_SIZE - 1; top_of_stack = top_type_stack[0] = top_type_stack[1] = NULL; }