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