3 Purpose: Simple RPN calculator based on IMath library.
4 Author: M. J. Fromberger
6 This is a very simplistic RPN calculator that will let you test the features
7 of the IMath built-in functions.
9 Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved.
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
18 The above copyright notice and this permission notice shall be included in
19 all copies or substantial portions of the Software.
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
37 #include <strings.h> /* for strcasecmp */
46 /* A cstate_t represents a stack of operands; numeric operands are pushed on
47 the stack, and commands cause them to be consumed in various ways.
52 mp_size alloc
; /* number of slots available */
53 mp_size used
; /* number of slots free */
56 mp_int
*mem
; /* named memory slots */
57 char **names
; /* names of memory slots */
58 mp_size mslots
; /* number of memory slots */
59 mp_size mused
; /* number of used memories */
62 FILE *ifp
; /* input file handle */
63 char *ibuf
; /* input scratch buffer */
64 int buflen
; /* size of scratch buffer */
67 static mp_result
state_init(cstate_t
*sp
, mp_size n_elts
);
68 static void state_clear(cstate_t
*sp
);
69 static void stack_flush(cstate_t
*sp
);
70 static mp_result
stack_push(cstate_t
*sp
, mp_int elt
);
71 static mp_result
stack_pop(cstate_t
*sp
);
72 static mp_result
mem_insert(cstate_t
*sp
, const char *name
, mp_int value
);
73 static mp_result
mem_recall(cstate_t
*sp
, const char *name
, mp_int value
);
74 static mp_result
mem_clear(cstate_t
*sp
);
76 typedef mp_result (*op_func
)(cstate_t
*);
78 static mp_result
cf_abs(cstate_t
*sp
);
79 static mp_result
cf_neg(cstate_t
*sp
);
80 static mp_result
cf_add(cstate_t
*sp
);
81 static mp_result
cf_sub(cstate_t
*sp
);
82 static mp_result
cf_mul(cstate_t
*sp
);
83 static mp_result
cf_divmod(cstate_t
*sp
);
84 static mp_result
cf_div(cstate_t
*sp
);
85 static mp_result
cf_mod(cstate_t
*sp
);
86 static mp_result
cf_expt(cstate_t
*sp
);
87 static mp_result
cf_exptmod(cstate_t
*sp
);
88 static mp_result
cf_square(cstate_t
*sp
);
89 static mp_result
cf_invmod(cstate_t
*sp
);
90 static mp_result
cf_gcd(cstate_t
*sp
);
91 static mp_result
cf_xgcd(cstate_t
*sp
);
92 static mp_result
cf_sqrt(cstate_t
*sp
);
93 static mp_result
cf_root(cstate_t
*sp
);
94 static mp_result
cf_cmplt(cstate_t
*sp
);
95 static mp_result
cf_cmpgt(cstate_t
*sp
);
96 static mp_result
cf_cmple(cstate_t
*sp
);
97 static mp_result
cf_cmpge(cstate_t
*sp
);
98 static mp_result
cf_cmpeq(cstate_t
*sp
);
99 static mp_result
cf_cmpne(cstate_t
*sp
);
100 static mp_result
cf_inc(cstate_t
*sp
);
101 static mp_result
cf_dec(cstate_t
*sp
);
102 static mp_result
cf_fact(cstate_t
*sp
);
103 static mp_result
cf_pprint(cstate_t
*sp
);
104 static mp_result
cf_print(cstate_t
*sp
);
105 static mp_result
cf_pstack(cstate_t
*sp
);
106 static mp_result
cf_clstk(cstate_t
*sp
);
107 static mp_result
cf_pop(cstate_t
*sp
);
108 static mp_result
cf_dup(cstate_t
*sp
);
109 static mp_result
cf_copy(cstate_t
*sp
);
110 static mp_result
cf_swap(cstate_t
*sp
);
111 static mp_result
cf_rot(cstate_t
*sp
);
112 static mp_result
cf_pick(cstate_t
*sp
);
113 static mp_result
cf_setr(cstate_t
*sp
);
114 static mp_result
cf_setbin(cstate_t
*sp
);
115 static mp_result
cf_help(cstate_t
*sp
);
116 static mp_result
cf_store(cstate_t
*sp
);
117 static mp_result
cf_recall(cstate_t
*sp
);
118 static mp_result
cf_cmem(cstate_t
*sp
);
119 static mp_result
cf_pmem(cstate_t
*sp
);
120 static mp_result
cf_qrecall(cstate_t
*sp
);
123 char *name
; /* The name of the operator. */
124 int stack_size
; /* Number of stack arguments required. */
125 op_func handler
; /* Function implementing operation. */
126 char *descript
; /* Human-readable description. */
129 static calcop_t g_ops
[] = {
130 {"abs", 1, cf_abs
, "x -- |x|"},
131 {"neg", 1, cf_neg
, "x -- (-x)"},
132 {"+", 2, cf_add
, "x y -- (x+y)"},
133 {"add", 2, cf_add
, "x y -- (x+y)"},
134 {"-", 2, cf_sub
, "x y -- (x-y)"},
135 {"sub", 2, cf_sub
, "x y -- (x-y)"},
136 {"*", 2, cf_mul
, "x y -- (x*y)"},
137 {"mul", 2, cf_mul
, "x y -- (x*y)"},
138 {"/", 2, cf_divmod
, "x y -- q r ; x = yq + r, 0 <= r < y"},
139 {"//", 2, cf_div
, "x y -- (x div y)"},
140 {"div", 2, cf_div
, "x y -- (x div y)"},
141 {"%", 2, cf_mod
, "x y -- (x mod y)"},
142 {"mod", 2, cf_mod
, "x y -- (x mod y)"},
143 {"^", 2, cf_expt
, "x y -- (x^y)"},
144 {"expt", 2, cf_expt
, "x y -- (x^y)"},
145 {"^^", 3, cf_exptmod
, "x y m -- (x^y mod m)"},
146 {"emod", 3, cf_exptmod
, "x y m -- (x^y mod m)"},
147 {"sqr", 1, cf_square
, "x -- (x*x)"},
148 {"inv", 2, cf_invmod
, "x m -- (1/x mod m)"},
149 {"gcd", 2, cf_gcd
, "x y -- gcd(x, y)"},
150 {"xgcd", 2, cf_xgcd
, "x y -- g u v ; g = ux + vy"},
151 {"sqrt", 1, cf_sqrt
, "x -- floor(sqrt(x))"},
152 {"root", 2, cf_root
, "x y -- floor(x^{1/y}) ; y > 0"},
153 {"<", 2, cf_cmplt
, "x y -- (x<y)"},
154 {">", 2, cf_cmpgt
, "x y -- (x>y)"},
155 {"<=", 2, cf_cmple
, "x y -- (x<=y)"},
156 {">=", 2, cf_cmpge
, "x y -- (x>=y)"},
157 {"=", 2, cf_cmpeq
, "x y -- (x=y)"},
158 {"<>", 2, cf_cmpne
, "x y -- (x<>y)"},
159 {"inc", 1, cf_inc
, "x -- (x+1)"},
160 {"dec", 1, cf_dec
, "x -- (x-1)"},
161 {"!", 1, cf_fact
, "x -- x!"},
162 {"fact", 1, cf_fact
, "x -- x!"},
164 {".", 1, cf_pprint
, "x -- ; print x in current output mode"},
165 {";", 1, cf_print
, "x -- x ; print x in current output mode"},
166 {"?", 0, cf_pstack
, "-- ; print stack"},
167 {"cls", 0, cf_clstk
, "... -- ; clear stack"},
168 {"$", 1, cf_pop
, "x --"},
169 {"drop", 1, cf_pop
, "x --"},
170 {"dup", 1, cf_dup
, "x -- x x"},
171 {"copy", 2, cf_copy
, "vn ... v1 v0 n -- vn ... v0 vn ... v0"},
172 {"swap", 2, cf_swap
, "x y -- y x"},
173 {"rot", 3, cf_rot
, "a b c -- b c a"},
174 {"pick", 2, cf_pick
, "... v2 v1 v0 n -- ... v2 v1 v0 vn"},
176 {">>", 1, cf_store
, "x -- ; save in named variable"},
177 {"<<", 0, cf_recall
, "-- x ; recall from named variable"},
178 {"clm", 0, cf_cmem
, "-- ; clear memory"},
179 {"??", 0, cf_pmem
, "-- ; print memory"},
181 {"out", 1, cf_setr
, "r -- ; set output radix to r"},
182 {"bin", 0, cf_setbin
, "-- ; set output format to binary"},
183 {"help", 0, cf_help
, "-- ; print help message"},
185 /* This is the end-marker, but it is also used to catch implicit
186 variable lookups from memory.
188 {NULL
, 0, cf_qrecall
, "-- x ; recall from named variable"},
191 #define BUFFER_SIZE 16384 /* max. length of input values, in chars */
193 /* Token types from the primitive lexical analyzer */
194 typedef enum { t_eof
, t_symbol
, t_number
, t_error
} token_t
;
196 static token_t
next_token(FILE *ifp
, char *buf
, int size
);
197 static mp_result
read_number(char *buf
, mp_int
*out
);
198 static int find_command(cstate_t
*ops
);
199 static void print_value(mp_int v
);
200 static mp_result
run_file(FILE *ifp
, cstate_t
*op_state
);
202 /* Error code used internally to signal input problems. */
203 static mp_result MP_INPUT
;
205 static int g_output_radix
= 10; /* output radix */
206 static FILE *g_output_file
= NULL
;
208 int main(int argc
, char *argv
[]) {
218 MP_INPUT
= MP_MINERR
- 1;
220 g_output_file
= stdout
;
221 while ((opt
= getopt(argc
, argv
, "ho:")) != EOF
) {
226 "Usage: imcalc [-h] [-o <output>] input*\n\n"
228 " -h : display this help message.\n"
229 " -o <output> : send output to file.\n\n"
231 "If no input files are given, the standard input is read. The\n"
232 "special file name \"-\" is interpreted to mean the standard "
234 "Output goes to standard output unless \"-o\" is used.\n\n");
238 if ((g_output_file
= fopen(optarg
, "wt")) == NULL
) {
239 fprintf(stderr
, "Unable to open \"%s\" for writing: %s\n", optarg
,
247 "Usage: imcalc [-h] [-o <output>] input*\n"
248 " [use \"imcalc -h\" to get help]\n\n");
253 if ((res
= state_init(&op_state
, 1)) != MP_OK
) {
254 fprintf(stderr
, "Error: state_init: %s\n", mp_error_string(res
));
261 for (ix
= optind
; ix
< argc
; ++ix
) {
262 if (strcmp(argv
[ix
], "-") == 0)
264 else if ((ifp
= fopen(argv
[optind
], "rt")) == NULL
) {
265 fprintf(stderr
, "Unable to open \"%s\" for reading: %s\n", argv
[optind
],
270 if (run_file(ifp
, &op_state
) != MP_OK
) ++errs
;
273 state_clear(&op_state
);
276 int rv
= 1 - (run_file(stdin
, &op_state
) == MP_OK
);
277 state_clear(&op_state
);
282 static token_t
next_token(FILE *ifp
, char *buf
, int size
) {
286 assert(buf
!= NULL
&& size
> 0);
288 while ((ch
= fgetc(ifp
)) != EOF
&& isspace(ch
)) /* empty */
297 int next
= fgetc(ifp
);
298 if (next
== EOF
|| !isdigit(next
))
303 } else if (isdigit(ch
) || ch
== '#')
309 while ((ch
= fgetc(ifp
)) != EOF
) {
310 if ((res
== t_number
&& ispunct(ch
) && ch
!= '-') ||
311 (res
== t_symbol
&& isdigit(ch
)) || isspace(ch
)) {
314 } else if (pos
+ 1 >= size
) {
325 static mp_result
read_number(char *buf
, mp_int
*out
) {
326 int radix
= 10, pos
= 0;
330 assert(buf
!= NULL
&& out
!= NULL
);
332 if (buf
[pos
] == '#') {
357 if ((value
= mp_int_alloc()) == NULL
) {
362 if ((res
= mp_int_read_string(value
, radix
, buf
+ pos
)) != MP_OK
) {
372 static int find_command(cstate_t
*op
) {
374 char *buf
= op
->ibuf
;
376 /* First, try to find the command by name */
377 for (ix
= 0; g_ops
[ix
].name
!= NULL
; ++ix
) {
378 if (strcasecmp(buf
, g_ops
[ix
].name
) == 0) return ix
;
381 /* If we don't find the command, try a variable lookup */
382 for (jx
= 0; (mp_size
)jx
< op
->mused
; ++jx
) {
383 if (strcmp(buf
, op
->names
[jx
]) == 0) return ix
; /* sentinel */
386 /* If variable lookup fails, report command not found */
390 static void print_value(mp_int v
) {
391 if (g_output_radix
== 0) {
392 mp_result len
= mp_int_binary_len(v
);
393 unsigned char *buf
= malloc(len
);
397 mp_int_to_binary(v
, buf
, len
);
398 for (ix
= 0; ix
< len
- 1; ++ix
) {
399 fprintf(g_output_file
, "%02x.", buf
[ix
]);
401 fprintf(g_output_file
, "%02x\n", buf
[ix
]);
404 fprintf(g_output_file
, "<insufficient memory to print>\n");
407 mp_result len
= mp_int_string_len(v
, g_output_radix
);
408 char *buf
= malloc(len
);
411 mp_int_to_string(v
, g_output_radix
, buf
, len
);
412 fputs(buf
, g_output_file
);
413 fputc('\n', g_output_file
);
416 fprintf(g_output_file
, "<insufficient memory to print>\n");
421 static mp_result
run_file(FILE *ifp
, cstate_t
*op_state
) {
422 mp_result res
= MP_OK
;
426 while ((next
= next_token(ifp
, op_state
->ibuf
, op_state
->buflen
)) != t_eof
) {
432 if ((res
= read_number(op_state
->ibuf
, &value
)) != MP_OK
)
433 fprintf(stderr
, "error: invalid number syntax: %s\n", op_state
->ibuf
);
434 else if ((res
= stack_push(op_state
, value
)) != MP_OK
)
438 if ((cpos
= find_command(op_state
)) < 0) {
439 fprintf(stderr
, "error: command not understood: %s\n",
441 } else if (op_state
->used
< (mp_size
)g_ops
[cpos
].stack_size
) {
442 fprintf(stderr
, "error: not enough arguments (have %d, want %d)\n",
443 op_state
->used
, g_ops
[cpos
].stack_size
);
444 } else if ((res
= (g_ops
[cpos
].handler
)(op_state
)) != MP_OK
) {
445 if (res
== MP_INPUT
) {
446 fprintf(stderr
, "error: incorrect input format\n");
448 fprintf(stderr
, "error: %s\n", mp_error_string(res
));
453 fprintf(stderr
, "error: invalid input token: %s\n", op_state
->ibuf
);
463 static mp_result
state_init(cstate_t
*sp
, mp_size n_elts
) {
466 assert(sp
!= NULL
&& n_elts
> 0);
468 if ((sp
->elts
= malloc(n_elts
* sizeof(*(sp
->elts
)))) == NULL
)
470 if ((sp
->mem
= malloc(n_elts
* sizeof(*(sp
->mem
)))) == NULL
) {
474 if ((sp
->names
= malloc(n_elts
* sizeof(*(sp
->names
)))) == NULL
) {
479 if ((sp
->ibuf
= malloc(BUFFER_SIZE
* sizeof(char))) == NULL
) {
486 for (ix
= 0; (mp_size
)ix
< n_elts
; ++ix
) {
489 sp
->names
[ix
] = NULL
;
496 sp
->buflen
= BUFFER_SIZE
;
501 static void state_clear(cstate_t
*sp
) {
504 if (sp
->elts
!= NULL
) {
507 for (ix
= 0; (mp_size
)ix
< sp
->used
; ++ix
) {
508 mp_int_clear(sp
->elts
[ix
]);
517 if (sp
->mem
!= NULL
) {
520 for (ix
= 0; (mp_size
)ix
< sp
->mused
; ++ix
) {
521 mp_int_free(sp
->mem
[ix
]);
524 sp
->names
[ix
] = NULL
;
535 if (sp
->ibuf
!= NULL
) {
539 if (sp
->ifp
!= NULL
) {
545 static void stack_flush(cstate_t
*sp
) {
548 assert(sp
!= NULL
&& sp
->elts
!= NULL
);
550 for (ix
= 0; (mp_size
)ix
< sp
->used
; ++ix
) {
551 mp_int_clear(sp
->elts
[ix
]);
558 static mp_result
stack_push(cstate_t
*sp
, mp_int elt
) {
559 if (sp
->used
>= sp
->alloc
) {
560 mp_size nsize
= 2 * sp
->alloc
;
564 if ((tmp
= malloc(nsize
* sizeof(*(sp
->elts
)))) == NULL
) return MP_MEMORY
;
566 for (ix
= 0; (mp_size
)ix
< sp
->used
; ++ix
) {
567 tmp
[ix
] = sp
->elts
[ix
];
575 sp
->elts
[sp
->used
++] = elt
;
579 static mp_result
stack_pop(cstate_t
*sp
) {
580 assert(sp
!= NULL
&& sp
->elts
!= NULL
);
582 if (sp
->used
== 0) return MP_UNDEF
;
585 mp_int_clear(sp
->elts
[sp
->used
]);
586 sp
->elts
[sp
->used
] = NULL
;
591 static mp_result
mem_insert(cstate_t
*sp
, const char *name
, mp_int value
) {
594 for (ix
= 0; (mp_size
)ix
< sp
->mused
; ++ix
) {
595 if (strcmp(name
, sp
->names
[ix
]) == 0) break;
599 ix < sp->mused ==> replacing existing entry.
600 otherwise ==> adding new entry, may need to grow dictionary.
602 if ((mp_size
)ix
< sp
->mused
) {
603 mp_int_free(sp
->mem
[ix
]); /* fall through to the end */
605 if (sp
->mused
>= sp
->mslots
) {
606 mp_size nsize
= 2 * sp
->mslots
;
611 if ((tz
= malloc(nsize
* sizeof(*(sp
->mem
)))) == NULL
) return MP_MEMORY
;
612 if ((tc
= malloc(nsize
* sizeof(*(sp
->names
)))) == NULL
) {
617 for (jx
= 0; (mp_size
)jx
< sp
->mused
; ++jx
) {
618 tz
[jx
] = sp
->mem
[jx
];
619 tc
[jx
] = sp
->names
[jx
];
631 sp
->names
[ix
] = malloc(1 + strlen(name
));
632 strcpy(sp
->names
[ix
], name
);
635 sp
->mem
[ix
] = mp_int_alloc();
636 return mp_int_copy(value
, sp
->mem
[ix
]);
639 static mp_result
mem_recall(cstate_t
*sp
, const char *name
, mp_int value
) {
642 for (ix
= 0; (mp_size
)ix
< sp
->mused
; ++ix
) {
643 if (strcmp(name
, sp
->names
[ix
]) == 0) {
644 return mp_int_copy(sp
->mem
[ix
], value
);
648 return MP_UNDEF
; /* not found */
651 static mp_result
mem_clear(cstate_t
*sp
) {
654 for (ix
= 0; (mp_size
)ix
< sp
->mused
; ++ix
) {
655 mp_int_free(sp
->mem
[ix
]);
663 static mp_result
cf_abs(cstate_t
*sp
) {
664 mp_int a
= sp
->elts
[sp
->used
- 1];
666 return mp_int_abs(a
, a
);
669 static mp_result
cf_neg(cstate_t
*sp
) {
670 mp_int a
= sp
->elts
[sp
->used
- 1];
672 return mp_int_neg(a
, a
);
675 static mp_result
cf_add(cstate_t
*sp
) {
676 mp_int b
= sp
->elts
[sp
->used
- 1];
677 mp_int a
= sp
->elts
[sp
->used
- 2];
678 mp_result res
= mp_int_add(a
, b
, a
);
680 if (res
== MP_OK
) stack_pop(sp
);
685 static mp_result
cf_sub(cstate_t
*sp
) {
686 mp_int b
= sp
->elts
[sp
->used
- 1];
687 mp_int a
= sp
->elts
[sp
->used
- 2];
688 mp_result res
= mp_int_sub(a
, b
, a
);
690 if (res
== MP_OK
) stack_pop(sp
);
695 static mp_result
cf_mul(cstate_t
*sp
) {
696 mp_int b
= sp
->elts
[sp
->used
- 1];
697 mp_int a
= sp
->elts
[sp
->used
- 2];
698 mp_result res
= mp_int_mul(a
, b
, a
);
700 if (res
== MP_OK
) stack_pop(sp
);
705 static mp_result
cf_divmod(cstate_t
*sp
) {
706 mp_int b
= sp
->elts
[sp
->used
- 1];
707 mp_int a
= sp
->elts
[sp
->used
- 2];
709 return mp_int_div(a
, b
, a
, b
);
712 static mp_result
cf_div(cstate_t
*sp
) {
713 mp_int b
= sp
->elts
[sp
->used
- 1];
714 mp_int a
= sp
->elts
[sp
->used
- 2];
715 mp_result res
= mp_int_div(a
, b
, a
, NULL
);
717 if (res
== MP_OK
) stack_pop(sp
);
722 static mp_result
cf_mod(cstate_t
*sp
) {
723 mp_int b
= sp
->elts
[sp
->used
- 1];
724 mp_int a
= sp
->elts
[sp
->used
- 2];
725 mp_result res
= mp_int_mod(a
, b
, a
);
727 if (res
== MP_OK
) stack_pop(sp
);
732 static mp_result
cf_expt(cstate_t
*sp
) {
733 mp_int b
= sp
->elts
[sp
->used
- 1];
734 mp_int a
= sp
->elts
[sp
->used
- 2];
738 if ((res
= mp_int_to_int(b
, &bval
)) != MP_OK
) return res
;
741 return mp_int_expt(a
, bval
, a
);
744 static mp_result
cf_exptmod(cstate_t
*sp
) {
745 mp_int m
= sp
->elts
[sp
->used
- 1];
746 mp_int b
= sp
->elts
[sp
->used
- 2];
747 mp_int a
= sp
->elts
[sp
->used
- 3];
748 mp_result res
= mp_int_exptmod(a
, b
, m
, a
);
758 static mp_result
cf_square(cstate_t
*sp
) {
759 mp_int a
= sp
->elts
[sp
->used
- 1];
761 return mp_int_sqr(a
, a
);
764 static mp_result
cf_invmod(cstate_t
*sp
) {
765 mp_int m
= sp
->elts
[sp
->used
- 1];
766 mp_int a
= sp
->elts
[sp
->used
- 2];
767 mp_result res
= mp_int_invmod(a
, m
, a
);
774 static mp_result
cf_gcd(cstate_t
*sp
) {
775 mp_int b
= sp
->elts
[sp
->used
- 1];
776 mp_int a
= sp
->elts
[sp
->used
- 2];
777 mp_result res
= mp_int_gcd(a
, b
, a
);
779 if (res
== MP_OK
) stack_pop(sp
);
784 static mp_result
cf_xgcd(cstate_t
*sp
) {
785 mp_int b
= sp
->elts
[sp
->used
- 1];
786 mp_int a
= sp
->elts
[sp
->used
- 2];
790 if ((t
= mp_int_alloc()) == NULL
) return MP_MEMORY
;
791 if ((res
= mp_int_egcd(a
, b
, a
, b
, t
)) != MP_OK
) {
796 if ((res
= stack_push(sp
, t
)) != MP_OK
) mp_int_free(t
);
801 static mp_result
cf_sqrt(cstate_t
*sp
) {
802 mp_int a
= sp
->elts
[sp
->used
- 1];
804 return mp_int_sqrt(a
, a
);
807 static mp_result
cf_root(cstate_t
*sp
) {
808 mp_int a
= sp
->elts
[sp
->used
- 2];
809 mp_int bp
= sp
->elts
[sp
->used
- 1];
813 if ((res
= mp_int_to_int(bp
, &b
)) != MP_OK
) return res
;
816 return mp_int_root(a
, b
, a
);
819 static mp_result
cf_cmplt(cstate_t
*sp
) {
820 mp_int b
= sp
->elts
[sp
->used
- 1];
821 mp_int a
= sp
->elts
[sp
->used
- 2];
824 res
= mp_int_set_value(a
, (mp_int_compare(a
, b
) < 0));
829 static mp_result
cf_cmpgt(cstate_t
*sp
) {
830 mp_int b
= sp
->elts
[sp
->used
- 1];
831 mp_int a
= sp
->elts
[sp
->used
- 2];
834 res
= mp_int_set_value(a
, (mp_int_compare(a
, b
) > 0));
839 static mp_result
cf_cmple(cstate_t
*sp
) {
840 mp_int b
= sp
->elts
[sp
->used
- 1];
841 mp_int a
= sp
->elts
[sp
->used
- 2];
844 res
= mp_int_set_value(a
, (mp_int_compare(a
, b
) <= 0));
849 static mp_result
cf_cmpge(cstate_t
*sp
) {
850 mp_int b
= sp
->elts
[sp
->used
- 1];
851 mp_int a
= sp
->elts
[sp
->used
- 2];
854 res
= mp_int_set_value(a
, (mp_int_compare(a
, b
) >= 0));
859 static mp_result
cf_cmpeq(cstate_t
*sp
) {
860 mp_int b
= sp
->elts
[sp
->used
- 1];
861 mp_int a
= sp
->elts
[sp
->used
- 2];
864 res
= mp_int_set_value(a
, (mp_int_compare(a
, b
) == 0));
869 static mp_result
cf_cmpne(cstate_t
*sp
) {
870 mp_int b
= sp
->elts
[sp
->used
- 1];
871 mp_int a
= sp
->elts
[sp
->used
- 2];
874 res
= mp_int_set_value(a
, (mp_int_compare(a
, b
) != 0));
879 static mp_result
cf_inc(cstate_t
*sp
) {
880 mp_int a
= sp
->elts
[sp
->used
- 1];
882 return mp_int_add_value(a
, 1, a
);
885 static mp_result
cf_dec(cstate_t
*sp
) {
886 mp_int a
= sp
->elts
[sp
->used
- 1];
888 return mp_int_sub_value(a
, 1, a
);
891 static mp_result
cf_fact(cstate_t
*sp
) {
893 mp_int x
= sp
->elts
[sp
->used
- 1];
894 mp_result res
= MP_OK
;
896 if (mp_int_compare_zero(x
) < 0) return MP_UNDEF
;
898 (void)mp_int_init_value(&tmp
, 1);
900 while (mp_int_compare_value(x
, 1) > 0) {
901 if ((res
= mp_int_mul(&tmp
, x
, &tmp
)) != MP_OK
) goto CLEANUP
;
902 if ((res
= mp_int_sub_value(x
, 1, x
)) != MP_OK
) goto CLEANUP
;
905 res
= mp_int_copy(&tmp
, x
);
912 static mp_result
cf_pprint(cstate_t
*sp
) {
913 print_value(sp
->elts
[sp
->used
- 1]);
918 static mp_result
cf_print(cstate_t
*sp
) {
919 print_value(sp
->elts
[sp
->used
- 1]);
923 static mp_result
cf_pstack(cstate_t
*sp
) {
927 fprintf(g_output_file
, "<stack empty>\n");
929 for (ix
= 0; (mp_size
)ix
< sp
->used
; ++ix
) {
930 fprintf(g_output_file
, "%2d: ", ix
);
931 print_value(sp
->elts
[sp
->used
- 1 - ix
]);
938 static mp_result
cf_clstk(cstate_t
*sp
) {
944 static mp_result
cf_pop(cstate_t
*sp
) { return stack_pop(sp
); }
946 static mp_result
cf_dup(cstate_t
*sp
) {
947 mp_int cp
= mp_int_alloc();
950 if (cp
== NULL
) return MP_MEMORY
;
952 if ((res
= mp_int_copy(sp
->elts
[sp
->used
- 1], cp
)) != MP_OK
) {
957 if ((res
= stack_push(sp
, cp
)) != MP_OK
) mp_int_free(cp
);
962 static mp_result
cf_copy(cstate_t
*sp
) {
963 mp_int n
= sp
->elts
[sp
->used
- 1];
968 if ((res
= mp_int_to_int(n
, &ncopy
)) != MP_OK
) return res
;
970 if (ncopy
< 1 || ncopy
>= sp
->used
) return MP_RANGE
;
974 for (ix
= 0; ix
< ncopy
; ++ix
) {
975 mp_int old
= sp
->elts
[sp
->used
- ncopy
];
976 mp_int
new = mp_int_alloc();
978 if (new == NULL
) return MP_MEMORY
;
980 if ((res
= mp_int_copy(old
, new)) != MP_OK
) {
984 if ((res
= stack_push(sp
, new)) != MP_OK
) return res
;
990 static mp_result
cf_swap(cstate_t
*sp
) {
991 mp_int t
= sp
->elts
[sp
->used
- 1];
993 sp
->elts
[sp
->used
- 1] = sp
->elts
[sp
->used
- 2];
994 sp
->elts
[sp
->used
- 2] = t
;
999 static mp_result
cf_rot(cstate_t
*sp
) {
1000 mp_int t
= sp
->elts
[sp
->used
- 3];
1002 sp
->elts
[sp
->used
- 3] = sp
->elts
[sp
->used
- 2];
1003 sp
->elts
[sp
->used
- 2] = sp
->elts
[sp
->used
- 1];
1004 sp
->elts
[sp
->used
- 1] = t
;
1009 static mp_result
cf_pick(cstate_t
*sp
) {
1010 mp_int n
= sp
->elts
[sp
->used
- 1];
1014 if ((res
= mp_int_to_int(n
, &pos
)) != MP_OK
) return res
;
1016 if (pos
< 0 || pos
>= sp
->used
- 1) return MP_RANGE
;
1018 return mp_int_copy(sp
->elts
[sp
->used
- 2 - pos
], n
);
1021 static mp_result
cf_setr(cstate_t
*sp
) {
1022 mp_int a
= sp
->elts
[sp
->used
- 1];
1026 if ((res
= mp_int_to_int(a
, &rdx
)) != MP_OK
) return res
;
1028 if (rdx
< MP_MIN_RADIX
|| rdx
> MP_MAX_RADIX
) return MP_RANGE
;
1030 g_output_radix
= rdx
;
1035 static mp_result
cf_setbin(cstate_t
*sp
) {
1040 static mp_result
cf_help(cstate_t
*sp
) {
1041 int ix
, maxlen
= 10; /* minimum width */
1043 for (ix
= 0; g_ops
[ix
].name
!= NULL
; ++ix
) {
1044 int len
= strlen(g_ops
[ix
].name
);
1046 if (len
> maxlen
) maxlen
= len
;
1049 fprintf(stderr
, "Operators understood:\n");
1050 for (ix
= 0; g_ops
[ix
].name
!= NULL
; ++ix
) {
1051 int len
= strlen(g_ops
[ix
].name
);
1053 fputs(g_ops
[ix
].name
, stderr
);
1054 while (len
++ <= maxlen
) fputc(' ', stderr
);
1056 fprintf(stderr
, "%s\n", g_ops
[ix
].descript
);
1058 fputc('\n', stderr
);
1063 static mp_result
cf_store(cstate_t
*sp
) {
1066 if (next_token(sp
->ifp
, sp
->ibuf
, sp
->buflen
) != t_symbol
) return MP_INPUT
;
1068 if ((res
= mem_insert(sp
, sp
->ibuf
, sp
->elts
[sp
->used
- 1])) != MP_OK
)
1071 return stack_pop(sp
);
1074 static mp_result
cf_recall(cstate_t
*sp
) {
1078 if (next_token(sp
->ifp
, sp
->ibuf
, sp
->buflen
) != t_symbol
) return MP_INPUT
;
1080 if ((val
= mp_int_alloc()) == NULL
) return MP_MEMORY
;
1081 if ((res
= mem_recall(sp
, sp
->ibuf
, val
)) != MP_OK
) {
1086 return stack_push(sp
, val
);
1089 static mp_result
cf_cmem(cstate_t
*sp
) { return mem_clear(sp
); }
1091 static mp_result
cf_pmem(cstate_t
*sp
) {
1092 int ix
, max_len
= 0;
1094 if (sp
->mused
== 0) {
1095 fprintf(g_output_file
, "<memory empty>\n");
1099 for (ix
= 0; (mp_size
)ix
< sp
->mused
; ++ix
) {
1100 int ln
= strlen(sp
->names
[ix
]);
1102 if (ln
> max_len
) max_len
= ln
;
1105 max_len
+= 1; /* allow for a padding space */
1107 for (ix
= 0; (mp_size
)ix
< sp
->mused
; ++ix
) {
1108 int ln
= strlen(sp
->names
[ix
]);
1110 fprintf(g_output_file
, "%s:", sp
->names
[ix
]);
1112 while (ln
++ < max_len
) fputc(' ', g_output_file
);
1114 print_value(sp
->mem
[ix
]);
1120 static mp_result
cf_qrecall(cstate_t
*sp
) {
1124 if ((val
= mp_int_alloc()) == NULL
) return MP_MEMORY
;
1126 if ((res
= mem_recall(sp
, sp
->ibuf
, val
)) != MP_OK
) {
1131 return stack_push(sp
, val
);
1134 /* Here there be dragons */