4 * Copyright (c) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
30 #include "functions.h"
33 static char digits
[] = "0123456789ABCDEF";
35 static double max_fix
[MAXBASES
] = {
36 1.298074214e+33, /* Binary. */
37 2.037035976e+90, /* Octal. */
38 1.000000000e+100, /* Decimal */
39 2.582249878e+120 /* Hexadecimal. */
42 /* Add in the thousand separators characters if required and if we are
43 * currently in the decimal numeric base, use the "right" radix character.
46 /* Add in the thousand separators characters if required */
48 localize_expression(char *dest
, const char *src
, int dest_length
, int *cursor
)
50 GString
*clean
, *output
;
52 int digit_count
= -1, read_cursor
, new_cursor
;
53 gboolean after_radix
= FALSE
;
55 /* Only modify if valid */
56 if (v
->error
|| v
->base
!= DEC
) {
57 STRNCPY(dest
, src
, dest_length
- 1);
67 /* Remove separators if not supported */
68 clean
= g_string_sized_new(strlen(src
));
69 for (c
= src
, read_cursor
= 1; *c
; c
++, read_cursor
++) {
70 if (strncmp(c
, v
->tsep
, strlen(v
->tsep
)) == 0) {
71 c
+= strlen(v
->tsep
) - 1;
72 if (new_cursor
>= read_cursor
) {
78 g_string_append_c(clean
, *c
);
83 STRNCPY(dest
, clean
->str
, dest_length
- 1);
84 g_string_free(clean
, TRUE
);
88 /* Scan expression looking for numbers and inserting separators */
89 output
= g_string_sized_new(dest_length
);
90 for (c
= clean
->str
, read_cursor
= 1; *c
; c
++, read_cursor
++) {
91 /* Insert separators between digits */
92 if (*c
>= '0' && *c
<= '9') {
93 /* Read ahead to find the number of digits */
94 if (digit_count
< 0) {
96 for (d
= c
+ 1; *d
>= '0' && *d
<= '9'; d
++) {
101 g_string_append_c(output
, *c
);
103 /* Insert separator after nth digit */
104 if (!after_radix
&& digit_count
> 1 && digit_count
% v
->tsep_count
== 1) {
105 g_string_append(output
, v
->tsep
);
106 if (new_cursor
> read_cursor
) {
113 /* Ignore digits after the radix */
114 else if (strncmp(c
, v
->radix
, strlen(v
->radix
)) == 0) {
117 c
+= strlen(v
->radix
) - 1;
118 g_string_append(output
, v
->radix
);
120 /* Reset when encountering other characters (e.g. '+') */
124 g_string_append_c(output
, *c
);
128 STRNCPY(dest
, output
->str
, dest_length
- 1);
129 g_string_free(output
, TRUE
);
130 g_string_free(clean
, TRUE
);
132 if (cursor
!= NULL
&& *cursor
!= -1) {
133 *cursor
= new_cursor
;
141 if (chr
>= '0' && chr
<= '9') {
143 } else if (chr
>= 'a' && chr
<= 'f') {
144 return(chr
- 'a' + 10);
145 } else if (chr
>= 'A' && chr
<= 'F') {
146 return(chr
- 'A' + 10);
154 display_clear(int initialise
)
158 do_zero(v
->MPdisp_val
);
159 display_set_number(v
->MPdisp_val
);
161 if (initialise
== TRUE
) {
163 ui_set_hyperbolic_state(FALSE
); /* Also clears v->hyperbolic. */
164 ui_set_inverse_state(FALSE
); /* Also clears v->inverse. */
172 v
->error
= 0; /* Currently no display error. */
173 v
->ltr
.cur_op
= -1; /* No arithmetic operator defined yet. */
174 v
->ltr
.old_cal_value
= -1;
175 do_zero(v
->MPresult
); /* No previous result yet. */
176 do_zero(v
->MPdisp_val
);
177 do_zero(v
->MPlast_input
);
179 v
->ltr
.new_input
= 1; /* Value zero is on calculator display */
185 /* Convert MP number to fixed number string in the given base to the
186 * maximum number of digits specified.
190 make_fixed(char *target
, int target_len
, int *MPnumber
, int base
, int cmax
, int toclear
)
192 char half
[MAXLINE
], *optr
;
193 int MP1base
[MP_SIZE
], MP1
[MP_SIZE
], MP2
[MP_SIZE
], MPval
[MP_SIZE
];
194 int ndig
; /* Total number of digits to generate. */
195 int ddig
; /* Number of digits to left of decimal sep. */
199 mpabs(MPnumber
, MPval
);
201 if (mplt(MPnumber
, MP1
)) {
205 mpcim(&basevals
[base
], MP1base
);
207 mppwr(MP1base
, &v
->accuracy
, MP1
);
208 /* FIXME: string const. if MPstr_to_num can get it */
209 SNPRINTF(half
, MAXLINE
, "0.5");
210 MPstr_to_num(half
, DEC
, MP2
);
211 mpdiv(MP2
, MP1
, MP1
);
212 mpadd(MPval
, MP1
, MPval
);
216 if (mplt(MPval
, MP2
)) {
221 for (ddig
= 0; mpge(MPval
, MP2
); ddig
++) {
222 mpdiv(MPval
, MP1base
, MPval
);
226 ndig
= MIN(ddig
+ v
->accuracy
, --cmax
);
230 for (i
= 0; i
< strlen(v
->radix
); i
++)
231 *optr
++ = v
->radix
[i
];
233 mpmul(MPval
, MP1base
, MPval
);
236 if (dval
> basevals
[base
]-1) {
237 dval
= basevals
[base
]-1;
240 *optr
++ = digits
[dval
];
242 mpaddi(MPval
, &dval
, MPval
);
245 if (toclear
== TRUE
) {
250 /* Strip off trailing zeroes */
251 if (!v
->show_zeroes
) {
252 for (i
= strlen(target
) - 1; i
> 1 && target
[i
] == '0'; i
--) {
256 /* If no fractional part discard radix */
257 if (strlen(target
) >= strlen(v
->radix
) && strcmp(target
+ strlen(target
) - strlen(v
->radix
), v
->radix
) == 0) {
258 target
[strlen(target
) - strlen(v
->radix
)] = '\0';
264 /* Convert engineering or scientific number in the given base. */
267 make_eng_sci(char *target
, int target_len
, int *MPnumber
, int base
)
269 char half
[MAXLINE
], fixed
[MAX_DIGITS
], *optr
;
270 int MP1
[MP_SIZE
], MPatmp
[MP_SIZE
], MPval
[MP_SIZE
];
271 int MP1base
[MP_SIZE
], MP3base
[MP_SIZE
], MP10base
[MP_SIZE
];
273 int MPmant
[MP_SIZE
]; /* Mantissa. */
274 int ddig
; /* Number of digits in exponent. */
275 int eng
= 0; /* Set if this is an engineering number. */
276 int exp
= 0; /* Exponent */
278 if (v
->dtype
== ENG
) {
282 mpabs(MPnumber
, MPval
);
284 if (mplt(MPnumber
, MP1
)) {
287 mpstr(MPval
, MPmant
);
289 mpcim(&basevals
[base
], MP1base
);
291 mppwr(MP1base
, &n
, MP3base
);
294 mppwr(MP1base
, &n
, MP10base
);
298 mpdiv(MP1
, MP10base
, MPatmp
);
301 if (!mpeq(MPmant
, MP1
)) {
302 while (!eng
&& mpge(MPmant
, MP10base
)) {
304 mpmul(MPmant
, MPatmp
, MPmant
);
307 while ((!eng
&& mpge(MPmant
, MP1base
)) ||
308 (eng
&& (mpge(MPmant
, MP3base
) || exp
% 3 != 0))) {
310 mpdiv(MPmant
, MP1base
, MPmant
);
313 while (!eng
&& mplt(MPmant
, MPatmp
)) {
315 mpmul(MPmant
, MP10base
, MPmant
);
320 while (mplt(MPmant
, MP1
) || (eng
&& exp
% 3 != 0)) {
322 mpmul(MPmant
, MP1base
, MPmant
);
326 make_fixed(fixed
, MAX_DIGITS
, MPmant
, base
, MAX_DIGITS
-6, TRUE
);
328 for (i
= 0; i
< len
; i
++) {
341 SNPRINTF(half
, MAXLINE
, "0.5");
342 MPstr_to_num(half
, DEC
, MP1
);
343 mpaddi(MP1
, &exp
, MPval
);
346 for (ddig
= 0; mpge(MPval
, MP1
); ddig
++) {
347 mpdiv(MPval
, MP1base
, MPval
);
355 mpmul(MPval
, MP1base
, MPval
);
357 *optr
++ = digits
[dval
];
359 mpaddi(MPval
, &dval
, MPval
);
367 /* Convert MP number to character string in the given base. */
370 make_number(char *target
, int target_len
, int *MPnumber
, int base
, int ignoreError
)
374 /* NOTE: make_number can currently set v->error when converting to a double.
375 * This is to provide the same look&feel as V3 even though gcalctool
376 * now does internal arithmetic to "infinite" precision.
378 * XXX: Needs to be improved. Shouldn't need to convert to a double in
379 * order to do these tests.
382 mpcmd(MPnumber
, &number
);
384 if (v
->error
&& !ignoreError
) {
385 STRNCPY(target
, _("Error"), target_len
- 1);
388 if ((v
->dtype
== ENG
) ||
390 (v
->dtype
== FIX
&& val
!= 0.0 && (val
> max_fix
[base
]))) {
391 make_eng_sci(target
, target_len
, MPnumber
, base
);
393 make_fixed(target
, target_len
, MPnumber
, base
, MAX_DIGITS
, TRUE
);
398 /* Convert string into an MP number, in the given base
402 MPstr_to_num(char *str
, enum base_type base
, int *MPval
)
405 int MP1
[MP_SIZE
], MP2
[MP_SIZE
], MPbase
[MP_SIZE
];
410 char *lnp
= ui_get_localized_numeric_point();
414 mpcim(&basevals
[(int) base
], MPbase
);
418 /* Remove any initial spaces or tabs. */
419 while (*optr
== ' ' || *optr
== '\t') {
423 /* Check if this is a negative number. */
429 while ((inum
= char_val(*optr
)) >= 0) {
430 mpmul(MPval
, MPbase
, MPval
);
431 mpaddi(MPval
, &inum
, MPval
);
435 if (*optr
== '.' || *optr
== *lnp
) {
437 for (i
= 1; (inum
= char_val(*optr
)) >= 0; i
++) {
438 mppwr(MPbase
, &i
, MP1
);
440 mpdiv(MP2
, MP1
, MP1
);
441 mpadd(MPval
, MP1
, MPval
);
446 while (*optr
== ' ') {
455 while ((inum
= char_val(*++optr
)) >= 0) {
456 exp
= exp
* basevals
[(int) base
] + inum
;
461 if (v
->ltr
.key_exp
) {
462 mppwr(MPbase
, &exp
, MP1
);
463 mpmul(MPval
, MP1
, MPval
);
472 /* Append the latest parenthesis char to the display item. */
480 /* If the character is a Delete, clear the whole line, and exit parenthesis
483 * If the character is a Back Space, remove the last character. If the last
484 * character was a left parenthesis, decrement the parentheses count. If
485 * the parentheses count is zero, exit parenthesis processing.
487 * Otherwise just append the character.
490 n
= strlen(v
->display
);
491 text
= buttons
[key
].symname
;
497 do_zero(v
->MPdisp_val
);
498 display_set_number(v
->MPdisp_val
);
505 if (v
->display
[n
-1] == ')') {
507 } else if (v
->display
[n
-1] == '(') {
509 if (!v
->ltr
.noparens
) {
511 display_set_number(v
->MPdisp_val
);
514 } else if (v
->display
[n
-1] == ')') v
->ltr
.noparens
++;
515 v
->display
[n
-1] = '\0';
518 case KEY_START_BLOCK
:
520 /* If this is the first left parenthesis being displayed and there is no
521 * current arithmetic operand, then the current display is initially cleared
522 * to avoid the confusion of showing something like "0(".
525 if (v
->ltr
.noparens
== 1 && v
->ltr
.cur_op
== -1) {
527 v
->display
[0] = '\0';
538 SNPRINTF(v
->display
+n
, MAXLINE
-n
, "%s", text
);
541 n
= (n
< MAX_DIGITS
) ? 0 : n
- MAX_DIGITS
;
542 ui_set_display(&v
->display
[n
], -1);
546 display_set_number(int *MPval
)
549 make_number(v
->display
, MAXLINE
, MPval
, v
->base
, FALSE
);
550 ui_set_display(v
->display
, -1);
555 display_set_string(char *value
)
557 if(value
!= v
->display
)
558 STRNCPY(value
, v
->display
, MAX_DIGITS
- 1);
559 ui_set_display(v
->display
, -1);
562 /* In arithmetic precedence mode this routine should be called to redraw
566 display_refresh(int cursor
)
568 int i
, MP_reg
[MP_SIZE
];
569 char localized
[MAX_LOCALIZED
], *str
, reg
[3], *t
;
570 struct exprm_state
*e
;
571 char x
[MAX_LOCALIZED
], xx
[MAX_LOCALIZED
], ans
[MAX_LOCALIZED
];
575 display_set_number(v
->MPdisp_val
);
580 if (e
->expression
[0] == '\0') {
582 make_number(x
, MAX_LOCALIZED
, MP_reg
, v
->base
, FALSE
);
585 str
= gc_strdup(e
->expression
);
588 /* Substitute answer register */
589 make_number(ans
, MAX_LOCALIZED
, e
->ans
, v
->base
, TRUE
);
590 localize_expression(localized
, ans
, MAX_LOCALIZED
, &cursor
);
591 str
= str_replace(str
, "Ans", localized
);
593 /* Replace registers with values. */
594 for (i
= 0; i
< 10; i
++) {
595 SNPRINTF(reg
, 3, "R%d", i
);
596 do_rcl_reg(i
, MP_reg
);
597 make_number(xx
, MAX_LOCALIZED
, MP_reg
, v
->base
, FALSE
);
598 t
= str_replace(str
, reg
, xx
);
603 ui_set_display(str
, cursor
);
612 gboolean
display_is_result(void)
614 struct exprm_state
*e
;
618 if (v
->ltr
.old_cal_value
< 0 ||
619 v
->ltr
.old_cal_value
== KEY_CALCULATE
) {
626 if (strcmp(e
->expression
, "Ans") == 0) {