| /* |
| Name: imcalc.c |
| Purpose: Simple RPN calculator based on IMath library. |
| Author: M. J. Fromberger |
| |
| This is a very simplistic RPN calculator that will let you test the features |
| of the IMath built-in functions. |
| |
| Copyright (C) 2002-2008 Michael J. Fromberger, All Rights Reserved. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal |
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the Software is |
| furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in |
| all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| SOFTWARE. |
| */ |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <strings.h> /* for strcasecmp */ |
| |
| #include <getopt.h> |
| #include <unistd.h> |
| |
| #include "imath.h" |
| #include "imrat.h" |
| #include "iprime.h" |
| |
| /* A cstate_t represents a stack of operands; numeric operands are pushed on |
| the stack, and commands cause them to be consumed in various ways. |
| */ |
| typedef struct { |
| /* Operand stack */ |
| mp_int *elts; |
| mp_size alloc; /* number of slots available */ |
| mp_size used; /* number of slots free */ |
| |
| /* Named variables */ |
| mp_int *mem; /* named memory slots */ |
| char **names; /* names of memory slots */ |
| mp_size mslots; /* number of memory slots */ |
| mp_size mused; /* number of used memories */ |
| |
| /* I/O components */ |
| FILE *ifp; /* input file handle */ |
| char *ibuf; /* input scratch buffer */ |
| int buflen; /* size of scratch buffer */ |
| } cstate_t; |
| |
| static mp_result state_init(cstate_t *sp, mp_size n_elts); |
| static void state_clear(cstate_t *sp); |
| static void stack_flush(cstate_t *sp); |
| static mp_result stack_push(cstate_t *sp, mp_int elt); |
| static mp_result stack_pop(cstate_t *sp); |
| static mp_result mem_insert(cstate_t *sp, const char *name, mp_int value); |
| static mp_result mem_recall(cstate_t *sp, const char *name, mp_int value); |
| static mp_result mem_clear(cstate_t *sp); |
| |
| typedef mp_result (*op_func)(cstate_t *); |
| |
| static mp_result cf_abs(cstate_t *sp); |
| static mp_result cf_neg(cstate_t *sp); |
| static mp_result cf_add(cstate_t *sp); |
| static mp_result cf_sub(cstate_t *sp); |
| static mp_result cf_mul(cstate_t *sp); |
| static mp_result cf_divmod(cstate_t *sp); |
| static mp_result cf_div(cstate_t *sp); |
| static mp_result cf_mod(cstate_t *sp); |
| static mp_result cf_expt(cstate_t *sp); |
| static mp_result cf_exptmod(cstate_t *sp); |
| static mp_result cf_square(cstate_t *sp); |
| static mp_result cf_invmod(cstate_t *sp); |
| static mp_result cf_gcd(cstate_t *sp); |
| static mp_result cf_xgcd(cstate_t *sp); |
| static mp_result cf_sqrt(cstate_t *sp); |
| static mp_result cf_root(cstate_t *sp); |
| static mp_result cf_cmplt(cstate_t *sp); |
| static mp_result cf_cmpgt(cstate_t *sp); |
| static mp_result cf_cmple(cstate_t *sp); |
| static mp_result cf_cmpge(cstate_t *sp); |
| static mp_result cf_cmpeq(cstate_t *sp); |
| static mp_result cf_cmpne(cstate_t *sp); |
| static mp_result cf_inc(cstate_t *sp); |
| static mp_result cf_dec(cstate_t *sp); |
| static mp_result cf_fact(cstate_t *sp); |
| static mp_result cf_pprint(cstate_t *sp); |
| static mp_result cf_print(cstate_t *sp); |
| static mp_result cf_pstack(cstate_t *sp); |
| static mp_result cf_clstk(cstate_t *sp); |
| static mp_result cf_pop(cstate_t *sp); |
| static mp_result cf_dup(cstate_t *sp); |
| static mp_result cf_copy(cstate_t *sp); |
| static mp_result cf_swap(cstate_t *sp); |
| static mp_result cf_rot(cstate_t *sp); |
| static mp_result cf_pick(cstate_t *sp); |
| static mp_result cf_setr(cstate_t *sp); |
| static mp_result cf_setbin(cstate_t *sp); |
| static mp_result cf_help(cstate_t *sp); |
| static mp_result cf_store(cstate_t *sp); |
| static mp_result cf_recall(cstate_t *sp); |
| static mp_result cf_cmem(cstate_t *sp); |
| static mp_result cf_pmem(cstate_t *sp); |
| static mp_result cf_qrecall(cstate_t *sp); |
| |
| typedef struct { |
| char *name; /* The name of the operator. */ |
| int stack_size; /* Number of stack arguments required. */ |
| op_func handler; /* Function implementing operation. */ |
| char *descript; /* Human-readable description. */ |
| } calcop_t; |
| |
| static calcop_t g_ops[] = { |
| {"abs", 1, cf_abs, "x -- |x|"}, |
| {"neg", 1, cf_neg, "x -- (-x)"}, |
| {"+", 2, cf_add, "x y -- (x+y)"}, |
| {"add", 2, cf_add, "x y -- (x+y)"}, |
| {"-", 2, cf_sub, "x y -- (x-y)"}, |
| {"sub", 2, cf_sub, "x y -- (x-y)"}, |
| {"*", 2, cf_mul, "x y -- (x*y)"}, |
| {"mul", 2, cf_mul, "x y -- (x*y)"}, |
| {"/", 2, cf_divmod, "x y -- q r ; x = yq + r, 0 <= r < y"}, |
| {"//", 2, cf_div, "x y -- (x div y)"}, |
| {"div", 2, cf_div, "x y -- (x div y)"}, |
| {"%", 2, cf_mod, "x y -- (x mod y)"}, |
| {"mod", 2, cf_mod, "x y -- (x mod y)"}, |
| {"^", 2, cf_expt, "x y -- (x^y)"}, |
| {"expt", 2, cf_expt, "x y -- (x^y)"}, |
| {"^^", 3, cf_exptmod, "x y m -- (x^y mod m)"}, |
| {"emod", 3, cf_exptmod, "x y m -- (x^y mod m)"}, |
| {"sqr", 1, cf_square, "x -- (x*x)"}, |
| {"inv", 2, cf_invmod, "x m -- (1/x mod m)"}, |
| {"gcd", 2, cf_gcd, "x y -- gcd(x, y)"}, |
| {"xgcd", 2, cf_xgcd, "x y -- g u v ; g = ux + vy"}, |
| {"sqrt", 1, cf_sqrt, "x -- floor(sqrt(x))"}, |
| {"root", 2, cf_root, "x y -- floor(x^{1/y}) ; y > 0"}, |
| {"<", 2, cf_cmplt, "x y -- (x<y)"}, |
| {">", 2, cf_cmpgt, "x y -- (x>y)"}, |
| {"<=", 2, cf_cmple, "x y -- (x<=y)"}, |
| {">=", 2, cf_cmpge, "x y -- (x>=y)"}, |
| {"=", 2, cf_cmpeq, "x y -- (x=y)"}, |
| {"<>", 2, cf_cmpne, "x y -- (x<>y)"}, |
| {"inc", 1, cf_inc, "x -- (x+1)"}, |
| {"dec", 1, cf_dec, "x -- (x-1)"}, |
| {"!", 1, cf_fact, "x -- x!"}, |
| {"fact", 1, cf_fact, "x -- x!"}, |
| |
| {".", 1, cf_pprint, "x -- ; print x in current output mode"}, |
| {";", 1, cf_print, "x -- x ; print x in current output mode"}, |
| {"?", 0, cf_pstack, "-- ; print stack"}, |
| {"cls", 0, cf_clstk, "... -- ; clear stack"}, |
| {"$", 1, cf_pop, "x --"}, |
| {"drop", 1, cf_pop, "x --"}, |
| {"dup", 1, cf_dup, "x -- x x"}, |
| {"copy", 2, cf_copy, "vn ... v1 v0 n -- vn ... v0 vn ... v0"}, |
| {"swap", 2, cf_swap, "x y -- y x"}, |
| {"rot", 3, cf_rot, "a b c -- b c a"}, |
| {"pick", 2, cf_pick, "... v2 v1 v0 n -- ... v2 v1 v0 vn"}, |
| |
| {">>", 1, cf_store, "x -- ; save in named variable"}, |
| {"<<", 0, cf_recall, "-- x ; recall from named variable"}, |
| {"clm", 0, cf_cmem, "-- ; clear memory"}, |
| {"??", 0, cf_pmem, "-- ; print memory"}, |
| |
| {"out", 1, cf_setr, "r -- ; set output radix to r"}, |
| {"bin", 0, cf_setbin, "-- ; set output format to binary"}, |
| {"help", 0, cf_help, "-- ; print help message"}, |
| |
| /* This is the end-marker, but it is also used to catch implicit |
| variable lookups from memory. |
| */ |
| {NULL, 0, cf_qrecall, "-- x ; recall from named variable"}, |
| }; |
| |
| #define BUFFER_SIZE 16384 /* max. length of input values, in chars */ |
| |
| /* Token types from the primitive lexical analyzer */ |
| typedef enum { t_eof, t_symbol, t_number, t_error } token_t; |
| |
| static token_t next_token(FILE *ifp, char *buf, int size); |
| static mp_result read_number(char *buf, mp_int *out); |
| static int find_command(cstate_t *ops); |
| static void print_value(mp_int v); |
| static mp_result run_file(FILE *ifp, cstate_t *op_state); |
| |
| /* Error code used internally to signal input problems. */ |
| static mp_result MP_INPUT; |
| |
| static int g_output_radix = 10; /* output radix */ |
| static FILE *g_output_file = NULL; |
| |
| int main(int argc, char *argv[]) { |
| extern char *optarg; |
| extern int optind; |
| |
| int opt, errs = 0; |
| FILE *ifp; |
| |
| cstate_t op_state; |
| mp_result res; |
| |
| MP_INPUT = MP_MINERR - 1; |
| |
| g_output_file = stdout; |
| while ((opt = getopt(argc, argv, "ho:")) != EOF) { |
| switch (opt) { |
| case 'h': |
| fprintf( |
| stderr, |
| "Usage: imcalc [-h] [-o <output>] input*\n\n" |
| "Options:\n" |
| " -h : display this help message.\n" |
| " -o <output> : send output to file.\n\n" |
| |
| "If no input files are given, the standard input is read. The\n" |
| "special file name \"-\" is interpreted to mean the standard " |
| "input.\n" |
| "Output goes to standard output unless \"-o\" is used.\n\n"); |
| return 0; |
| |
| case 'o': |
| if ((g_output_file = fopen(optarg, "wt")) == NULL) { |
| fprintf(stderr, "Unable to open \"%s\" for writing: %s\n", optarg, |
| strerror(errno)); |
| return 1; |
| } |
| break; |
| |
| default: |
| fprintf(stderr, |
| "Usage: imcalc [-h] [-o <output>] input*\n" |
| " [use \"imcalc -h\" to get help]\n\n"); |
| return 1; |
| } |
| } |
| |
| if ((res = state_init(&op_state, 1)) != MP_OK) { |
| fprintf(stderr, "Error: state_init: %s\n", mp_error_string(res)); |
| return 1; |
| } |
| |
| if (optind < argc) { |
| int ix; |
| |
| for (ix = optind; ix < argc; ++ix) { |
| if (strcmp(argv[ix], "-") == 0) |
| ifp = stdin; |
| else if ((ifp = fopen(argv[optind], "rt")) == NULL) { |
| fprintf(stderr, "Unable to open \"%s\" for reading: %s\n", argv[optind], |
| strerror(errno)); |
| return 1; |
| } |
| |
| if (run_file(ifp, &op_state) != MP_OK) ++errs; |
| } |
| |
| state_clear(&op_state); |
| return errs > 0; |
| } else { |
| int rv = 1 - (run_file(stdin, &op_state) == MP_OK); |
| state_clear(&op_state); |
| return rv; |
| } |
| } |
| |
| static token_t next_token(FILE *ifp, char *buf, int size) { |
| int ch, pos = 0; |
| token_t res; |
| |
| assert(buf != NULL && size > 0); |
| |
| while ((ch = fgetc(ifp)) != EOF && isspace(ch)) /* empty */ |
| ; |
| |
| if (ch == EOF) { |
| buf[0] = '\0'; |
| return t_eof; |
| } |
| |
| if (ch == '-') { |
| int next = fgetc(ifp); |
| if (next == EOF || !isdigit(next)) |
| res = t_symbol; |
| else |
| res = t_number; |
| ungetc(next, ifp); |
| } else if (isdigit(ch) || ch == '#') |
| res = t_number; |
| else |
| res = t_symbol; |
| |
| buf[pos++] = ch; |
| while ((ch = fgetc(ifp)) != EOF) { |
| if ((res == t_number && ispunct(ch) && ch != '-') || |
| (res == t_symbol && isdigit(ch)) || isspace(ch)) { |
| ungetc(ch, ifp); |
| break; |
| } else if (pos + 1 >= size) { |
| res = t_error; |
| break; |
| } |
| buf[pos++] = ch; |
| } |
| |
| buf[pos] = '\0'; |
| return res; |
| } |
| |
| static mp_result read_number(char *buf, mp_int *out) { |
| int radix = 10, pos = 0; |
| mp_result res; |
| mp_int value; |
| |
| assert(buf != NULL && out != NULL); |
| |
| if (buf[pos] == '#') { |
| switch (buf[1]) { |
| case 'b': |
| case 'B': |
| radix = 2; |
| break; |
| case 'd': |
| case 'D': |
| radix = 10; |
| break; |
| case 'o': |
| case 'O': |
| radix = 8; |
| break; |
| case 'x': |
| case 'X': |
| radix = 16; |
| break; |
| default: |
| return MP_BADARG; |
| } |
| |
| pos += 2; |
| } |
| |
| if ((value = mp_int_alloc()) == NULL) { |
| *out = NULL; |
| return MP_MEMORY; |
| } |
| |
| if ((res = mp_int_read_string(value, radix, buf + pos)) != MP_OK) { |
| mp_int_free(value); |
| *out = NULL; |
| return res; |
| } |
| |
| *out = value; |
| return res; |
| } |
| |
| static int find_command(cstate_t *op) { |
| int ix, jx; |
| char *buf = op->ibuf; |
| |
| /* First, try to find the command by name */ |
| for (ix = 0; g_ops[ix].name != NULL; ++ix) { |
| if (strcasecmp(buf, g_ops[ix].name) == 0) return ix; |
| } |
| |
| /* If we don't find the command, try a variable lookup */ |
| for (jx = 0; (mp_size)jx < op->mused; ++jx) { |
| if (strcmp(buf, op->names[jx]) == 0) return ix; /* sentinel */ |
| } |
| |
| /* If variable lookup fails, report command not found */ |
| return -1; |
| } |
| |
| static void print_value(mp_int v) { |
| if (g_output_radix == 0) { |
| mp_result len = mp_int_binary_len(v); |
| unsigned char *buf = malloc(len); |
| int ix; |
| |
| if (buf != NULL) { |
| mp_int_to_binary(v, buf, len); |
| for (ix = 0; ix < len - 1; ++ix) { |
| fprintf(g_output_file, "%02x.", buf[ix]); |
| } |
| fprintf(g_output_file, "%02x\n", buf[ix]); |
| free(buf); |
| } else { |
| fprintf(g_output_file, "<insufficient memory to print>\n"); |
| } |
| } else { |
| mp_result len = mp_int_string_len(v, g_output_radix); |
| char *buf = malloc(len); |
| |
| if (buf != NULL) { |
| mp_int_to_string(v, g_output_radix, buf, len); |
| fputs(buf, g_output_file); |
| fputc('\n', g_output_file); |
| free(buf); |
| } else { |
| fprintf(g_output_file, "<insufficient memory to print>\n"); |
| } |
| } |
| } |
| |
| static mp_result run_file(FILE *ifp, cstate_t *op_state) { |
| mp_result res = MP_OK; |
| token_t next; |
| |
| op_state->ifp = ifp; |
| while ((next = next_token(ifp, op_state->ibuf, op_state->buflen)) != t_eof) { |
| mp_int value = NULL; |
| int cpos; |
| |
| switch (next) { |
| case t_number: |
| if ((res = read_number(op_state->ibuf, &value)) != MP_OK) |
| fprintf(stderr, "error: invalid number syntax: %s\n", op_state->ibuf); |
| else if ((res = stack_push(op_state, value)) != MP_OK) |
| goto EXIT; |
| break; |
| case t_symbol: |
| if ((cpos = find_command(op_state)) < 0) { |
| fprintf(stderr, "error: command not understood: %s\n", |
| op_state->ibuf); |
| } else if (op_state->used < (mp_size)g_ops[cpos].stack_size) { |
| fprintf(stderr, "error: not enough arguments (have %d, want %d)\n", |
| op_state->used, g_ops[cpos].stack_size); |
| } else if ((res = (g_ops[cpos].handler)(op_state)) != MP_OK) { |
| if (res == MP_INPUT) { |
| fprintf(stderr, "error: incorrect input format\n"); |
| } else { |
| fprintf(stderr, "error: %s\n", mp_error_string(res)); |
| } |
| } |
| break; |
| default: |
| fprintf(stderr, "error: invalid input token: %s\n", op_state->ibuf); |
| res = MP_BADARG; |
| goto EXIT; |
| } |
| } |
| |
| EXIT: |
| return res; |
| } |
| |
| static mp_result state_init(cstate_t *sp, mp_size n_elts) { |
| int ix; |
| |
| assert(sp != NULL && n_elts > 0); |
| |
| if ((sp->elts = malloc(n_elts * sizeof(*(sp->elts)))) == NULL) |
| return MP_MEMORY; |
| if ((sp->mem = malloc(n_elts * sizeof(*(sp->mem)))) == NULL) { |
| free(sp->elts); |
| return MP_MEMORY; |
| } |
| if ((sp->names = malloc(n_elts * sizeof(*(sp->names)))) == NULL) { |
| free(sp->mem); |
| free(sp->elts); |
| return MP_MEMORY; |
| } |
| if ((sp->ibuf = malloc(BUFFER_SIZE * sizeof(char))) == NULL) { |
| free(sp->names); |
| free(sp->mem); |
| free(sp->elts); |
| return MP_MEMORY; |
| } |
| |
| for (ix = 0; (mp_size)ix < n_elts; ++ix) { |
| sp->elts[ix] = NULL; |
| sp->mem[ix] = NULL; |
| sp->names[ix] = NULL; |
| } |
| |
| sp->alloc = n_elts; |
| sp->used = 0; |
| sp->mslots = n_elts; |
| sp->mused = 0; |
| sp->buflen = BUFFER_SIZE; |
| |
| return MP_OK; |
| } |
| |
| static void state_clear(cstate_t *sp) { |
| assert(sp != NULL); |
| |
| if (sp->elts != NULL) { |
| int ix; |
| |
| for (ix = 0; (mp_size)ix < sp->used; ++ix) { |
| mp_int_clear(sp->elts[ix]); |
| sp->elts[ix] = NULL; |
| } |
| |
| free(sp->elts); |
| sp->elts = NULL; |
| sp->alloc = 0; |
| sp->used = 0; |
| } |
| if (sp->mem != NULL) { |
| int ix; |
| |
| for (ix = 0; (mp_size)ix < sp->mused; ++ix) { |
| mp_int_free(sp->mem[ix]); |
| sp->mem[ix] = NULL; |
| free(sp->names[ix]); |
| sp->names[ix] = NULL; |
| } |
| |
| free(sp->mem); |
| sp->mem = NULL; |
| free(sp->names); |
| sp->names = NULL; |
| |
| sp->mslots = 0; |
| sp->mused = 0; |
| } |
| if (sp->ibuf != NULL) { |
| free(sp->ibuf); |
| sp->buflen = 0; |
| } |
| if (sp->ifp != NULL) { |
| fclose(sp->ifp); |
| sp->ifp = NULL; |
| } |
| } |
| |
| static void stack_flush(cstate_t *sp) { |
| int ix; |
| |
| assert(sp != NULL && sp->elts != NULL); |
| |
| for (ix = 0; (mp_size)ix < sp->used; ++ix) { |
| mp_int_clear(sp->elts[ix]); |
| sp->elts[ix] = NULL; |
| } |
| |
| sp->used = 0; |
| } |
| |
| static mp_result stack_push(cstate_t *sp, mp_int elt) { |
| if (sp->used >= sp->alloc) { |
| mp_size nsize = 2 * sp->alloc; |
| mp_int *tmp; |
| int ix; |
| |
| if ((tmp = malloc(nsize * sizeof(*(sp->elts)))) == NULL) return MP_MEMORY; |
| |
| for (ix = 0; (mp_size)ix < sp->used; ++ix) { |
| tmp[ix] = sp->elts[ix]; |
| } |
| |
| free(sp->elts); |
| sp->elts = tmp; |
| sp->alloc = nsize; |
| } |
| |
| sp->elts[sp->used++] = elt; |
| return MP_OK; |
| } |
| |
| static mp_result stack_pop(cstate_t *sp) { |
| assert(sp != NULL && sp->elts != NULL); |
| |
| if (sp->used == 0) return MP_UNDEF; |
| |
| sp->used -= 1; |
| mp_int_clear(sp->elts[sp->used]); |
| sp->elts[sp->used] = NULL; |
| |
| return MP_OK; |
| } |
| |
| static mp_result mem_insert(cstate_t *sp, const char *name, mp_int value) { |
| int ix; |
| |
| for (ix = 0; (mp_size)ix < sp->mused; ++ix) { |
| if (strcmp(name, sp->names[ix]) == 0) break; |
| } |
| |
| /* Two cases: |
| ix < sp->mused ==> replacing existing entry. |
| otherwise ==> adding new entry, may need to grow dictionary. |
| */ |
| if ((mp_size)ix < sp->mused) { |
| mp_int_free(sp->mem[ix]); /* fall through to the end */ |
| } else { |
| if (sp->mused >= sp->mslots) { |
| mp_size nsize = 2 * sp->mslots; |
| mp_int *tz; |
| char **tc; |
| int jx; |
| |
| if ((tz = malloc(nsize * sizeof(*(sp->mem)))) == NULL) return MP_MEMORY; |
| if ((tc = malloc(nsize * sizeof(*(sp->names)))) == NULL) { |
| free(tz); |
| return MP_MEMORY; |
| } |
| |
| for (jx = 0; (mp_size)jx < sp->mused; ++jx) { |
| tz[jx] = sp->mem[jx]; |
| tc[jx] = sp->names[jx]; |
| } |
| |
| free(sp->mem); |
| sp->mem = tz; |
| free(sp->names); |
| sp->names = tc; |
| |
| sp->mslots = nsize; |
| } |
| |
| sp->mused += 1; |
| sp->names[ix] = malloc(1 + strlen(name)); |
| strcpy(sp->names[ix], name); |
| } |
| |
| sp->mem[ix] = mp_int_alloc(); |
| return mp_int_copy(value, sp->mem[ix]); |
| } |
| |
| static mp_result mem_recall(cstate_t *sp, const char *name, mp_int value) { |
| int ix; |
| |
| for (ix = 0; (mp_size)ix < sp->mused; ++ix) { |
| if (strcmp(name, sp->names[ix]) == 0) { |
| return mp_int_copy(sp->mem[ix], value); |
| } |
| } |
| |
| return MP_UNDEF; /* not found */ |
| } |
| |
| static mp_result mem_clear(cstate_t *sp) { |
| int ix; |
| |
| for (ix = 0; (mp_size)ix < sp->mused; ++ix) { |
| mp_int_free(sp->mem[ix]); |
| free(sp->names[ix]); |
| } |
| sp->mused = 0; |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_abs(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 1]; |
| |
| return mp_int_abs(a, a); |
| } |
| |
| static mp_result cf_neg(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 1]; |
| |
| return mp_int_neg(a, a); |
| } |
| |
| static mp_result cf_add(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res = mp_int_add(a, b, a); |
| |
| if (res == MP_OK) stack_pop(sp); |
| |
| return res; |
| } |
| |
| static mp_result cf_sub(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res = mp_int_sub(a, b, a); |
| |
| if (res == MP_OK) stack_pop(sp); |
| |
| return res; |
| } |
| |
| static mp_result cf_mul(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res = mp_int_mul(a, b, a); |
| |
| if (res == MP_OK) stack_pop(sp); |
| |
| return res; |
| } |
| |
| static mp_result cf_divmod(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| |
| return mp_int_div(a, b, a, b); |
| } |
| |
| static mp_result cf_div(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res = mp_int_div(a, b, a, NULL); |
| |
| if (res == MP_OK) stack_pop(sp); |
| |
| return res; |
| } |
| |
| static mp_result cf_mod(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res = mp_int_mod(a, b, a); |
| |
| if (res == MP_OK) stack_pop(sp); |
| |
| return res; |
| } |
| |
| static mp_result cf_expt(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res; |
| mp_small bval; |
| |
| if ((res = mp_int_to_int(b, &bval)) != MP_OK) return res; |
| |
| stack_pop(sp); |
| return mp_int_expt(a, bval, a); |
| } |
| |
| static mp_result cf_exptmod(cstate_t *sp) { |
| mp_int m = sp->elts[sp->used - 1]; |
| mp_int b = sp->elts[sp->used - 2]; |
| mp_int a = sp->elts[sp->used - 3]; |
| mp_result res = mp_int_exptmod(a, b, m, a); |
| |
| if (res == MP_OK) { |
| stack_pop(sp); |
| stack_pop(sp); |
| } |
| |
| return res; |
| } |
| |
| static mp_result cf_square(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 1]; |
| |
| return mp_int_sqr(a, a); |
| } |
| |
| static mp_result cf_invmod(cstate_t *sp) { |
| mp_int m = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res = mp_int_invmod(a, m, a); |
| |
| stack_pop(sp); |
| |
| return res; |
| } |
| |
| static mp_result cf_gcd(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res = mp_int_gcd(a, b, a); |
| |
| if (res == MP_OK) stack_pop(sp); |
| |
| return res; |
| } |
| |
| static mp_result cf_xgcd(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_int t; |
| mp_result res; |
| |
| if ((t = mp_int_alloc()) == NULL) return MP_MEMORY; |
| if ((res = mp_int_egcd(a, b, a, b, t)) != MP_OK) { |
| mp_int_free(t); |
| return res; |
| } |
| |
| if ((res = stack_push(sp, t)) != MP_OK) mp_int_free(t); |
| |
| return res; |
| } |
| |
| static mp_result cf_sqrt(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 1]; |
| |
| return mp_int_sqrt(a, a); |
| } |
| |
| static mp_result cf_root(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_int bp = sp->elts[sp->used - 1]; |
| mp_small b; |
| mp_result res; |
| |
| if ((res = mp_int_to_int(bp, &b)) != MP_OK) return res; |
| |
| stack_pop(sp); |
| return mp_int_root(a, b, a); |
| } |
| |
| static mp_result cf_cmplt(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res; |
| |
| res = mp_int_set_value(a, (mp_int_compare(a, b) < 0)); |
| stack_pop(sp); |
| return res; |
| } |
| |
| static mp_result cf_cmpgt(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res; |
| |
| res = mp_int_set_value(a, (mp_int_compare(a, b) > 0)); |
| stack_pop(sp); |
| return res; |
| } |
| |
| static mp_result cf_cmple(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res; |
| |
| res = mp_int_set_value(a, (mp_int_compare(a, b) <= 0)); |
| stack_pop(sp); |
| return res; |
| } |
| |
| static mp_result cf_cmpge(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res; |
| |
| res = mp_int_set_value(a, (mp_int_compare(a, b) >= 0)); |
| stack_pop(sp); |
| return res; |
| } |
| |
| static mp_result cf_cmpeq(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res; |
| |
| res = mp_int_set_value(a, (mp_int_compare(a, b) == 0)); |
| stack_pop(sp); |
| return res; |
| } |
| |
| static mp_result cf_cmpne(cstate_t *sp) { |
| mp_int b = sp->elts[sp->used - 1]; |
| mp_int a = sp->elts[sp->used - 2]; |
| mp_result res; |
| |
| res = mp_int_set_value(a, (mp_int_compare(a, b) != 0)); |
| stack_pop(sp); |
| return res; |
| } |
| |
| static mp_result cf_inc(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 1]; |
| |
| return mp_int_add_value(a, 1, a); |
| } |
| |
| static mp_result cf_dec(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 1]; |
| |
| return mp_int_sub_value(a, 1, a); |
| } |
| |
| static mp_result cf_fact(cstate_t *sp) { |
| mpz_t tmp; |
| mp_int x = sp->elts[sp->used - 1]; |
| mp_result res = MP_OK; |
| |
| if (mp_int_compare_zero(x) < 0) return MP_UNDEF; |
| |
| (void)mp_int_init_value(&tmp, 1); |
| |
| while (mp_int_compare_value(x, 1) > 0) { |
| if ((res = mp_int_mul(&tmp, x, &tmp)) != MP_OK) goto CLEANUP; |
| if ((res = mp_int_sub_value(x, 1, x)) != MP_OK) goto CLEANUP; |
| } |
| |
| res = mp_int_copy(&tmp, x); |
| |
| CLEANUP: |
| mp_int_clear(&tmp); |
| return res; |
| } |
| |
| static mp_result cf_pprint(cstate_t *sp) { |
| print_value(sp->elts[sp->used - 1]); |
| stack_pop(sp); |
| return MP_OK; |
| } |
| |
| static mp_result cf_print(cstate_t *sp) { |
| print_value(sp->elts[sp->used - 1]); |
| return MP_OK; |
| } |
| |
| static mp_result cf_pstack(cstate_t *sp) { |
| int ix; |
| |
| if (sp->used == 0) { |
| fprintf(g_output_file, "<stack empty>\n"); |
| } else { |
| for (ix = 0; (mp_size)ix < sp->used; ++ix) { |
| fprintf(g_output_file, "%2d: ", ix); |
| print_value(sp->elts[sp->used - 1 - ix]); |
| } |
| } |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_clstk(cstate_t *sp) { |
| stack_flush(sp); |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_pop(cstate_t *sp) { return stack_pop(sp); } |
| |
| static mp_result cf_dup(cstate_t *sp) { |
| mp_int cp = mp_int_alloc(); |
| mp_result res; |
| |
| if (cp == NULL) return MP_MEMORY; |
| |
| if ((res = mp_int_copy(sp->elts[sp->used - 1], cp)) != MP_OK) { |
| mp_int_free(cp); |
| return res; |
| } |
| |
| if ((res = stack_push(sp, cp)) != MP_OK) mp_int_free(cp); |
| |
| return res; |
| } |
| |
| static mp_result cf_copy(cstate_t *sp) { |
| mp_int n = sp->elts[sp->used - 1]; |
| mp_result res; |
| mp_small ncopy; |
| int ix; |
| |
| if ((res = mp_int_to_int(n, &ncopy)) != MP_OK) return res; |
| |
| if (ncopy < 1 || ncopy >= sp->used) return MP_RANGE; |
| |
| stack_pop(sp); |
| |
| for (ix = 0; ix < ncopy; ++ix) { |
| mp_int old = sp->elts[sp->used - ncopy]; |
| mp_int new = mp_int_alloc(); |
| |
| if (new == NULL) return MP_MEMORY; |
| |
| if ((res = mp_int_copy(old, new)) != MP_OK) { |
| mp_int_free(new); |
| return res; |
| } |
| if ((res = stack_push(sp, new)) != MP_OK) return res; |
| } |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_swap(cstate_t *sp) { |
| mp_int t = sp->elts[sp->used - 1]; |
| |
| sp->elts[sp->used - 1] = sp->elts[sp->used - 2]; |
| sp->elts[sp->used - 2] = t; |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_rot(cstate_t *sp) { |
| mp_int t = sp->elts[sp->used - 3]; |
| |
| sp->elts[sp->used - 3] = sp->elts[sp->used - 2]; |
| sp->elts[sp->used - 2] = sp->elts[sp->used - 1]; |
| sp->elts[sp->used - 1] = t; |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_pick(cstate_t *sp) { |
| mp_int n = sp->elts[sp->used - 1]; |
| mp_result res; |
| mp_small pos = 0; |
| |
| if ((res = mp_int_to_int(n, &pos)) != MP_OK) return res; |
| |
| if (pos < 0 || pos >= sp->used - 1) return MP_RANGE; |
| |
| return mp_int_copy(sp->elts[sp->used - 2 - pos], n); |
| } |
| |
| static mp_result cf_setr(cstate_t *sp) { |
| mp_int a = sp->elts[sp->used - 1]; |
| mp_result res; |
| mp_small rdx = 0; |
| |
| if ((res = mp_int_to_int(a, &rdx)) != MP_OK) return res; |
| |
| if (rdx < MP_MIN_RADIX || rdx > MP_MAX_RADIX) return MP_RANGE; |
| |
| g_output_radix = rdx; |
| stack_pop(sp); |
| return MP_OK; |
| } |
| |
| static mp_result cf_setbin(cstate_t *sp) { |
| g_output_radix = 0; |
| return MP_OK; |
| } |
| |
| static mp_result cf_help(cstate_t *sp) { |
| int ix, maxlen = 10; /* minimum width */ |
| |
| for (ix = 0; g_ops[ix].name != NULL; ++ix) { |
| int len = strlen(g_ops[ix].name); |
| |
| if (len > maxlen) maxlen = len; |
| } |
| |
| fprintf(stderr, "Operators understood:\n"); |
| for (ix = 0; g_ops[ix].name != NULL; ++ix) { |
| int len = strlen(g_ops[ix].name); |
| |
| fputs(g_ops[ix].name, stderr); |
| while (len++ <= maxlen) fputc(' ', stderr); |
| |
| fprintf(stderr, "%s\n", g_ops[ix].descript); |
| } |
| fputc('\n', stderr); |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_store(cstate_t *sp) { |
| mp_result res; |
| |
| if (next_token(sp->ifp, sp->ibuf, sp->buflen) != t_symbol) return MP_INPUT; |
| |
| if ((res = mem_insert(sp, sp->ibuf, sp->elts[sp->used - 1])) != MP_OK) |
| return res; |
| |
| return stack_pop(sp); |
| } |
| |
| static mp_result cf_recall(cstate_t *sp) { |
| mp_result res; |
| mp_int val; |
| |
| if (next_token(sp->ifp, sp->ibuf, sp->buflen) != t_symbol) return MP_INPUT; |
| |
| if ((val = mp_int_alloc()) == NULL) return MP_MEMORY; |
| if ((res = mem_recall(sp, sp->ibuf, val)) != MP_OK) { |
| mp_int_free(val); |
| return res; |
| } |
| |
| return stack_push(sp, val); |
| } |
| |
| static mp_result cf_cmem(cstate_t *sp) { return mem_clear(sp); } |
| |
| static mp_result cf_pmem(cstate_t *sp) { |
| int ix, max_len = 0; |
| |
| if (sp->mused == 0) { |
| fprintf(g_output_file, "<memory empty>\n"); |
| return MP_OK; |
| } |
| |
| for (ix = 0; (mp_size)ix < sp->mused; ++ix) { |
| int ln = strlen(sp->names[ix]); |
| |
| if (ln > max_len) max_len = ln; |
| } |
| |
| max_len += 1; /* allow for a padding space */ |
| |
| for (ix = 0; (mp_size)ix < sp->mused; ++ix) { |
| int ln = strlen(sp->names[ix]); |
| |
| fprintf(g_output_file, "%s:", sp->names[ix]); |
| |
| while (ln++ < max_len) fputc(' ', g_output_file); |
| |
| print_value(sp->mem[ix]); |
| } |
| |
| return MP_OK; |
| } |
| |
| static mp_result cf_qrecall(cstate_t *sp) { |
| mp_result res; |
| mp_int val; |
| |
| if ((val = mp_int_alloc()) == NULL) return MP_MEMORY; |
| |
| if ((res = mem_recall(sp, sp->ibuf, val)) != MP_OK) { |
| mp_int_free(val); |
| return res; |
| } |
| |
| return stack_push(sp, val); |
| } |
| |
| /* Here there be dragons */ |