1 /*
2 * Calcoo: c_op.c
3 *
4 * Copyright (C) 2001, 2002, 2003 Alexei Kaminski
5 *
6 * handles keypressings for arithmetic and algebraic operations
7 * and also "=" key, push key, paren keys, and register swap (exchange) keys
8 */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <math.h>
13
14 #include "codes.h"
15 #include "const.h"
16 #include "cpu.h"
17 #include "c_headers.h"
18 #include "io_headers.h"
19 #include "aux_headers.h"
20
21 void perform_binary_operation(void);
22 void perform_binop_chain(int, int);
23 int no_open_parens(void);
24
call_binary_op(int received_code)25 void call_binary_op(int received_code)
26 {
27 if (cpu->last_action == ACTION_INPUT)
28 input_to_x();
29
30 if (cpu->rpn_mode) {
31 cpu->op = received_code;
32 perform_binary_operation();
33 pop_stack();
34 cpu->last_action = ACTION_ENTER;
35 /* no need to use ACTION_BINOP in RPN */
36 } else {
37 if (cpu->op == CODE_NO_OPERATION) {
38 cpu->y = cpu->x;
39 cpu->last_action = ACTION_BINOP;
40 cpu->op = received_code;
41 cpu->number_of_parens = 0;
42 } else {
43 if(priority(received_code) > priority (cpu->op) ||
44 cpu->number_of_parens > 0 ) {
45 push_stack();
46 cpu->y = cpu->x;
47 cpu->op = received_code;
48 cpu->number_of_parens = 0;
49 cpu->last_action = ACTION_BINOP;
50 } else {
51 perform_binop_chain(priority(received_code),
52 FALSE);
53 push_stack();
54 cpu->y = cpu->x;
55 cpu->op = received_code;
56 cpu->number_of_parens = 0;
57 cpu->last_action = ACTION_BINOP;
58 }
59 }
60 }
61
62 aftermath();
63 }
64
perform_binary_operation(void)65 void perform_binary_operation(void)
66 /* aux function; makes only one binop itself, no other operations;
67 * is called by other functions like call_binary_op(), call_eq() */
68 {
69 switch (cpu->op) {
70 case CODE_ADD :
71 cpu->x = smart_sum(cpu->y, cpu->x, cpu->precision);
72 break;
73 case CODE_SUB :
74 cpu->x = smart_sum(cpu->y, -cpu->x, cpu->precision);
75 break;
76 case CODE_MUL :
77 cpu->x = cpu->y * cpu->x;
78 break;
79 case CODE_DIV :
80 if (cpu->x != 0.0)
81 cpu->x = cpu->y / cpu->x;
82 else
83 cpu->x_overflow = TRUE;
84 break;
85 case CODE_POW :
86 if ( !(
87 (cpu->y == 0 && cpu->x <= 0) ||
88 (cpu->y < 0 && (cpu->x != floor(cpu->x)))
89 ) )
90 cpu->x = pow(cpu->y, cpu->x);
91 else
92 cpu->x_overflow = TRUE;
93 break;
94 case CODE_NO_OPERATION :
95 break;
96 }
97
98 cpu->op = CODE_NO_OPERATION;
99 }
100
perform_binop_chain(int init_priority,int paren_closed)101 void perform_binop_chain(int init_priority, int paren_closed)
102 /* aux function; performs the chain of binary operations from the stack,
103 * stopping the chain at a paren or at a lower-priority operation */
104 {
105 if (paren_closed) {
106 /* if paren_closed == TRUE, it means there were open parens
107 * it have been found in call_right_paren() */
108 if (cpu->number_of_parens > 0){
109 /* handling a weird case of one number in parens, like 2+(3) */
110 cpu->number_of_parens--;
111 return;
112 } else {
113 while ( cpu->number_of_parens == 0 ) {
114 perform_binary_operation();
115 pop_stack();
116 }
117 cpu->number_of_parens--;
118 }
119 } else {
120 while ( (cpu->op != CODE_NO_OPERATION)
121 &&
122 (
123 ( (priority(cpu->op) >= init_priority)
124 && (cpu->number_of_parens == 0) )
125 ||
126 /* "=" effectively closes all parens */
127 init_priority == PRIORITY_CODE_MIN
128 )
129 ) {
130 perform_binary_operation();
131 pop_stack();
132 }
133 }
134 }
135
call_exch_xy(void)136 void call_exch_xy(void)
137 {
138 double tmp;
139
140 if ( (!cpu->rpn_mode) && (cpu->op == CODE_NO_OPERATION) )
141 return;
142
143 if (cpu->last_action == ACTION_INPUT)
144 input_to_x();
145
146 tmp = cpu->x;
147 cpu->x = cpu->y;
148 cpu->y = tmp;
149
150 cpu->last_action = ACTION_ENTER;
151
152 aftermath();
153 }
154
call_stack_up(void)155 void call_stack_up(void)
156 {
157 double tmp1, tmp2;
158 t_stack_element *stack_element;
159
160 /* this operation is not meant for the algebraic mode */
161 if ( !(cpu->rpn_mode)) {
162 error_occured("call_stack_up() called in algebraic mode",
163 FALSE);
164 return;
165 }
166
167 if (cpu->last_action == ACTION_INPUT)
168 input_to_x();
169
170 if (cpu->stack_mode == STACK_MODE_INFINITE)
171 {
172 if (cpu->stack_head != NULL)
173 {
174
175 tmp1 = cpu->stack_head->z;
176 cpu->stack_head->z = cpu->t;
177 cpu->t = cpu->z;
178 cpu->z = cpu->y;
179 cpu->y = cpu->x;
180
181 stack_element = cpu->stack_head;
182
183 while ( stack_element->next != NULL )
184 {
185 tmp2 = tmp1;
186 tmp1 = stack_element->next->z;
187 stack_element->next->z = tmp2;
188 stack_element = stack_element->next;
189 }
190 cpu->x = tmp1;
191 } else {
192 tmp1 = cpu->x; /* for consistency */
193 if (cpu->y == 0.0 && cpu->z == 0.0 && cpu->t == 0.0)
194 goto done_with_stack_scrolling;
195 tmp1 = cpu->y;
196 cpu->y = cpu->x;
197 if (cpu->z == 0.0 && cpu->t == 0.0)
198 goto done_with_stack_scrolling;
199 tmp2 = tmp1;
200 tmp1 = cpu->z;
201 cpu->z = tmp2;
202 if (cpu->t == 0.0)
203 goto done_with_stack_scrolling;
204 tmp2 = tmp1;
205 tmp1 = cpu->t;
206 cpu->t = tmp2;
207 done_with_stack_scrolling:
208 cpu->x = tmp1;
209 }
210 } else {
211 tmp1 = cpu->x;
212 cpu->x = cpu->t;
213 cpu->t = cpu->z;
214 cpu->z = cpu->y;
215 cpu->y = tmp1;
216 }
217
218 cpu->last_action = ACTION_ENTER;
219
220 aftermath();
221 }
222
call_stack_down(void)223 void call_stack_down(void)
224 {
225 double tmp;
226 t_stack_element *stack_element;
227
228 /* this operation is not meant for the algebraic mode */
229 if ( !(cpu->rpn_mode))
230 {
231 error_occured("call_stack_down() called in algebraic mode",
232 FALSE);
233 return;
234 }
235
236 if (cpu->last_action == ACTION_INPUT)
237 input_to_x();
238
239 tmp = cpu->x;
240
241 if (cpu->stack_mode == STACK_MODE_INFINITE)
242 {
243 if (cpu->stack_head != NULL)
244 {
245 cpu->x = cpu->y;
246 cpu->y = cpu->z;
247 cpu->z = cpu->t;
248 cpu->t = cpu->stack_head->z;
249
250 stack_element = cpu->stack_head;
251 while ( stack_element->next != NULL )
252 {
253 stack_element->z = stack_element->next->z;
254 stack_element = stack_element->next;
255 }
256 stack_element->z = tmp;
257 } else {
258 if (cpu->y == 0.0 && cpu->z == 0.0 && cpu->t == 0.0)
259 goto done_with_stack_scrolling;
260 cpu->x = cpu->y;
261 if (cpu->z == 0.0 && cpu->t == 0.0)
262 {
263 cpu->y = tmp;
264 goto done_with_stack_scrolling;
265 }
266 cpu->y = cpu->z;
267 if (cpu->t == 0.0)
268 {
269 cpu->z = tmp;
270 goto done_with_stack_scrolling;
271 }
272 cpu->z = cpu->t;
273 cpu->t = tmp;
274 done_with_stack_scrolling:
275 ;
276 }
277 } else {
278 cpu->x = cpu->y;
279 cpu->y = cpu->z;
280 cpu->z = cpu->t;
281 cpu->t = tmp;
282 }
283
284 cpu->last_action = ACTION_ENTER;
285
286 aftermath();
287 }
288
289
call_eq(void)290 void call_eq(void)
291 {
292 if (cpu->rpn_mode)
293 {
294 error_occured("call_eq() called in RPN mode", FALSE);
295 return;
296 }
297
298 if (cpu->last_action == ACTION_INPUT)
299 input_to_x();
300
301 perform_binop_chain(PRIORITY_CODE_MIN, FALSE);
302 cpu->op = CODE_NO_OPERATION;
303 cpu->number_of_parens = 0;
304
305 cpu->last_action = ACTION_ENTER;
306
307 aftermath();
308 }
309
call_enter(void)310 void call_enter(void)
311 {
312 /* this operation is not meant for the non-RPN mode */
313 if (!(cpu->rpn_mode))
314 {
315 error_occured("call_enter() called in algebraic mode", FALSE);
316 return;
317 }
318
319 if (cpu->enter_mode == ENTER_MODE_TRADITIONAL)
320 {
321 if (cpu->last_action == ACTION_INPUT)
322 input_to_x();
323 push_stack();
324 cpu->op = CODE_ENTER;
325 cpu->number_of_parens = 0;
326 cpu->y = cpu->x;
327 cpu->last_action = ACTION_ENTER_PUSH;
328 } else {
329 if (cpu->last_action == ACTION_INPUT)
330 input_to_x();
331 else {
332 push_stack();
333 cpu->op = CODE_ENTER;
334 cpu->number_of_parens = 0;
335 cpu->y = cpu->x;
336 }
337 cpu->last_action = ACTION_ENTER;
338 }
339
340 aftermath();
341 }
342
call_left_paren(void)343 void call_left_paren(void)
344 {
345 if (cpu->rpn_mode)
346 {
347 error_occured("call_left_paren() called in RPN mode", FALSE);
348 return;
349 }
350
351 if (cpu->last_action == ACTION_BINOP)
352 {
353 cpu->number_of_parens++;
354 aftermath();
355 }
356 }
357
call_right_paren(void)358 void call_right_paren(void)
359 {
360 if (cpu->rpn_mode)
361 {
362 error_occured("call_right_paren() called in RPN mode", FALSE);
363 return;
364 }
365
366 if (cpu->last_action == ACTION_INPUT)
367 {
368 input_to_x();
369 }
370
371 if (!no_open_parens())
372 {
373 if (cpu->op != CODE_NO_OPERATION) {
374 perform_binop_chain(PRIORITY_CODE_MIN, TRUE);
375 }
376 } else {
377 /* acts like "=" to provide reasonable behavior in
378 * the expressions like (2+3)/(4+5) */
379 perform_binop_chain(PRIORITY_CODE_MIN, FALSE);
380 cpu->op = CODE_NO_OPERATION;
381 }
382
383 cpu->last_action = ACTION_ENTER;
384
385 aftermath();
386 }
387
push_stack(void)388 void push_stack(void)
389 /* attention! this function does not move the contents of x to y!
390 * It affects only the higher elements of the stack. To make a full
391 * stack push you need the following sequence:
392 * push_stack();
393 * cpu->op = CODE_foo;
394 * cpu->number_of_parens = 0;
395 * cpu->y = cpu->x;
396 * The reason is that cpu->op is set manually rather than pushed from somewhr*/
397 {
398 t_stack_element *new_element;
399
400 if (cpu->rpn_mode)
401 {
402 if ( (cpu->stack_mode == STACK_MODE_INFINITE)
403 && (cpu->stack_head != NULL || cpu->t != 0.0 ))
404 {
405 new_element = (t_stack_element*)
406 malloc (sizeof(t_stack_element));
407 if(new_element == NULL)
408 error_occured("malloc failed", TRUE);
409 new_element->next = cpu->stack_head;
410 new_element->z = cpu->t;
411 cpu->stack_head = new_element;
412 }
413 cpu->t = cpu->z;
414 cpu->z = cpu->y;
415 } else {
416 if (cpu->op == CODE_NO_OPERATION)
417 return;
418 new_element = (t_stack_element*)
419 malloc (sizeof(t_stack_element));
420 if(new_element == NULL)
421 error_occured("malloc failed", TRUE);
422 new_element->next = cpu->stack_head;
423 new_element->z = cpu->y;
424 new_element->op = cpu->op;
425 new_element->number_of_parens = cpu->number_of_parens;
426 cpu->stack_head = new_element;
427 }
428 }
429
pop_stack(void)430 void pop_stack(void)
431 {
432 t_stack_element *to_remove;
433
434 if (cpu->rpn_mode)
435 {
436 cpu->y = cpu->z;
437 cpu->z = cpu->t;
438 if (cpu->stack_mode == STACK_MODE_INFINITE )
439 {
440 if (cpu->stack_head != NULL)
441 {
442 to_remove = cpu->stack_head;
443 cpu->t = (cpu->stack_head)->z;
444 cpu->stack_head = cpu->stack_head->next;
445 free(to_remove);
446 } else {
447 cpu->t = 0.0;
448 }
449 }
450 } else {
451 if (cpu->stack_head != NULL)
452 {
453 to_remove = cpu->stack_head;
454 cpu->y = (cpu->stack_head)->z;
455 cpu->op = (cpu->stack_head)->op;
456 cpu->number_of_parens =
457 (cpu->stack_head)->number_of_parens;
458 cpu->stack_head = cpu->stack_head->next;
459 free(to_remove);
460 } else {
461 cpu->op = CODE_NO_OPERATION;
462 cpu->y = 0.0;
463 cpu->number_of_parens = 0;
464 }
465 }
466 }
467
no_open_parens(void)468 int no_open_parens(void)
469 /* verifies if there are any open parens, returns TRUE if there are none */
470 {
471 t_stack_element *current;
472
473 if(cpu->number_of_parens > 0)
474 return FALSE;
475
476 current = cpu->stack_head;
477
478 while ( current != NULL )
479 {
480 if (current->number_of_parens > 0)
481 return FALSE;
482 current = current->next;
483 }
484 return TRUE;
485 }
486
call_unary_op(int received_code)487 void call_unary_op(int received_code)
488 {
489 if (cpu->last_action == ACTION_INPUT)
490 input_to_x();
491
492 switch (received_code){
493 case CODE_LOG :
494 if (cpu->x > 0.0){
495 if (fabs(cpu->x - 1.0 ) > cpu->precision)
496 cpu->x = log10(cpu->x);
497 else
498 cpu->x = 0.0;
499 }
500 else
501 cpu->x_overflow = TRUE;
502 break;
503 case CODE_10TOX :
504 if (cpu->x < pow(10,EXP_INPUT_LENGTH) )
505 cpu->x = pow(10, cpu->x);
506 else
507 cpu->x_overflow = TRUE;
508 break;
509 case CODE_LN :
510 if (cpu->x > 0){
511 if (fabs(cpu->x - 1.0 ) > cpu->precision)
512 cpu->x = log(cpu->x);
513 else
514 cpu->x = 0.0;
515 }
516 else
517 cpu->x_overflow = TRUE;
518 break;
519 case CODE_ETOX :
520 if (cpu->x * log10(exp(1.0)) < pow(10,EXP_INPUT_LENGTH))
521 cpu->x = exp(cpu->x);
522 else
523 cpu->x_overflow = TRUE;
524 break;
525 case CODE_SQRT :
526 if (cpu->x >= 0.0)
527 cpu->x = sqrt(cpu->x);
528 else
529 cpu->x_overflow = TRUE;
530 break;
531 case CODE_SQR :
532 cpu->x = cpu->x * cpu->x;
533 break;
534 case CODE_INVX :
535 if (cpu->x != 0.0)
536 cpu->x = 1.0 / cpu->x;
537 else
538 cpu->x_overflow = TRUE;
539 break;
540 case CODE_FACT :
541 if (cpu->x == 0.0)
542 cpu->x = 1.0;
543 else if (!fact_too_large(cpu->x))
544 cpu->x = fact_function(cpu->x);
545 else
546 cpu->x_overflow = TRUE;
547 break;
548 case CODE_SIN:
549 if (cpu->angle_units == CODE_DEG)
550 cpu->x *= PI / 180.0;
551 if (almost_integer(cpu->x / PI, cpu->precision))
552 /* hack to have sin 180 == 0 */
553 cpu->x = 0.0;
554 else
555 cpu->x = sin (cpu->x);
556 break;
557 case CODE_ASIN:
558 if (fabs(cpu->x) <= 1.0) {
559 cpu->x = asin(cpu->x);
560 if (cpu->angle_units == CODE_DEG)
561 cpu->x *= 180.0 / PI;
562 }
563 else
564 cpu->x_overflow = TRUE;
565 break;
566 case CODE_SINH :
567 cpu->x = sinh (cpu->x);
568 break;
569 case CODE_ASINH :
570 cpu->x = asinh (cpu->x);
571 break;
572 case CODE_COS :
573 if (cpu->angle_units == CODE_DEG)
574 cpu->x *= PI / 180.0;
575 if (almost_integer((cpu->x - PI / 2.0) / PI,
576 cpu->precision)
577 || fabs(cpu->x - PI / 2.0) < cpu->precision)
578 /* case x == PI/2 requires special treatment */
579 /* hack to have cos 90 == 0 */
580 cpu->x = 0.0;
581 else
582 cpu->x = cos (cpu->x);
583 break;
584 case CODE_ACOS:
585 if (fabs(cpu->x) <= 1.0) {
586 cpu->x = acos(cpu->x);
587 if (cpu->angle_units == CODE_DEG)
588 cpu->x *= 180.0 / PI;
589 }
590 else
591 cpu->x_overflow = TRUE;
592 break;
593 case CODE_COSH :
594 cpu->x = cosh (cpu->x);
595 break;
596 case CODE_ACOSH :
597 if (cpu->x >= 1.0)
598 cpu->x = acosh(cpu->x);
599 else
600 cpu->x_overflow = TRUE;
601 break;
602 case CODE_TAN:
603 if (cpu->angle_units == CODE_DEG)
604 cpu->x *= PI / 180.0;
605 if (almost_integer((cpu->x - PI / 2.0) / PI,
606 cpu->precision)
607 || fabs(cpu->x - PI / 2.0) < cpu->precision)
608 /* case x == PI/2 requires special treatment */
609 cpu->x_overflow = TRUE;
610 else if (almost_integer(cpu->x / PI, cpu->precision))
611 cpu->x = 0.0;
612 else
613 cpu->x = tan (cpu->x);
614 break;
615 case CODE_ATAN:
616 cpu->x = atan(cpu->x);
617 if (cpu->angle_units == CODE_DEG)
618 cpu->x *= 180.0 / PI;
619 break;
620 case CODE_TANH :
621 cpu->x = tanh (cpu->x);
622 break;
623 case CODE_ATANH :
624 cpu->x = atanh (cpu->x);
625 break;
626 default:
627 error_occured("call_unary_op() unknown unary_op code", FALSE);
628 }
629
630 cpu->last_action = ACTION_ENTER;
631
632 aftermath();
633 }
634
635