You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kspread/plugins/calculator/kcalc_core.cpp

2193 lines
48 KiB

/*
$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 <config.h>
#include <assert.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <knotifyclient.h>
#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 <ieeefp.h>
#else
#include <math.h>
#endif
int isinf(double x) { return !finite(x) && x==x; }
#endif
extern TQPtrList<CALCAMNT> 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;
}