1134 lines
28 KiB
C
1134 lines
28 KiB
C
/*
|
|
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 */
|