/* ns.c - Number Stack */ #define NS_PRINTLINE_FORMAT NS_PRINT_FORMAT "\n" /* Easy access functions */ #define NS_PEEK(s) s->num[s->top] #define NS_PEEKN(s,n) s->num[s->top + n] #define NS_PEEKDOT(s) s.num[s.top] /* Handling of the stack max size and expanding */ #define NS_EXPAND(s,n) n >= s->max ? !ns_expand(s,n) : 1 #define NS_EXPANDDOT(s,n) n >= s.max ? !ns_expand(&s,n) : 1 #define NS_LTMAX(s,n) s->top + n >= s->max ? !ns_expand(s,s->max+n) : 1 #define NS_LTMAXDOT(s,n) s.top + n >= s.max ? !ns_expand(&s,s.max+n) : 1 /* Debugging and Stack Output */ #define NS_DIV0(fail) do { fprintf(stderr, PROGN ": divide by zero\n"); return fail; } while (0) #define NS_REG_UNDERFLOW(fail, c) do { fprintf(stderr, PROGN ": stack register '%c' (%04d) is empty\n", (char) c, c); return fail; } while (0) #ifndef DC_COMPLY #define NS_OVERFLOW(fail) do { fprintf(stderr, PROGN ": overflow\n"); return fail; } while (0) #define NS_UNDERFLOW(fail) do { fprintf(stderr, PROGN ": underflow\n"); return fail; } while (0) #define NS_REG_OOB(fail, c) fprintf(stderr, PROGN ": register '%c' (%04d) is out of bounds\n", (char) c, c) #else #define NS_OVERFLOW(fail) do { fprintf(stderr, PROGN ": at top of stack\n"); return fail; } while (0) #define NS_UNDERFLOW(fail) do { fprintf(stderr, PROGN ": stack empty\n"); return fail; } while (0) #define NS_REG_OOB(fail, c) __builtin_unreachable() #endif /* !DC_COMPLY */ #define PERROR_RETURN(str,ret) do { perror(str); return ret; } while (1) #define NS_RETURN(ret) do { perror(PROGN); return ret; } while (1) /* #define NS_EXPAND(s,n) \ */ /* if (s->top + n >= s->max) \ */ /* { ns_expand(s,s->max+n); } */ #define NS_OP(fn, op) \ DC_EXPORT int \ fn(ns_t * s) \ { \ if (s->top > 0) \ { \ --s->top; \ op(NS_PEEK(s), \ NS_PEEK(s), NS_PEEKN(s,1)); \ return 0; \ } \ else \ { NS_UNDERFLOW(1); } \ } #define NS_OP_ONE(fn, op) \ DC_EXPORT int \ fn(ns_t * s) \ { \ if (s->top > -1) \ { \ op(NS_PEEK(s), \ NS_PEEK(s)); \ return 0; \ } \ else \ { NS_UNDERFLOW(1); } \ } #define NS_OP_DIV(fn, op) \ DC_EXPORT int fn(ns_t * s) \ { \ if (s->top > 0) \ { \ if (!(mpf_cmp_ui(NS_PEEK(s), 0) || \ mpf_cmp_ui(NS_PEEKN(s,-1), 0))) \ { NS_DIV0(2); } \ else \ { \ --s->top; \ op(NS_PEEK(s), \ NS_PEEK(s), NS_PEEKN(s,1)); \ return 0; \ } \ } \ else \ { NS_UNDERFLOW(1); } \ } typedef struct { ssize_t max; ssize_t top; mpf_t * num; } ns_t; /* number stack */ DC_EXPORT_VAR mp_bitcnt_t g_prec = NS_DEFAULT_PREC; DC_EXPORT_VAR int g_iradix = 10; DC_EXPORT_VAR int g_oradix = 10; /* Data handling */ DC_EXPORT void ns_free(ns_t * s) { ssize_t i; /* fprintf(stderr, "%p: Freeing %ld blocks\n", (void *) s, s->max); */ for (i = 0; i < s->max; ++i) { mpf_clear(s->num[i]); } free(s->num); /* free(s); */ } /* Growth */ DC_EXPORT int ns_expand(ns_t * s, ssize_t newmax) { /* fprintf(stderr, "%p: Expanding to %ld blocks\n", (void *) s, newmax); */ s->num = realloc(s->num, newmax * sizeof(mpf_t)); if (!s->num) { NS_RETURN(1); } else { ssize_t i; for (i = s->max; i < newmax; ++i) { mpf_init(s->num[i]); } s->max = newmax; return 0; } } /* Stack */ #define NS_PUSH(fn, op, type) \ DC_EXPORT int \ fn(ns_t * s, type val) \ { \ if (NS_LTMAX(s,1)) \ { \ ++s->top; \ op(NS_PEEK(s), val); \ return 0; \ } \ NS_OVERFLOW(1); \ } NS_PUSH(ns_push, mpf_set, mpf_t) NS_PUSH(ns_push_ui, mpf_set_ui, unsigned int) DC_EXPORT mpf_t * ns_pop(ns_t * s) { if (s->top > -1) { return &s->num[s->top--]; } else { return NULL; } } DC_EXPORT inline int ns_dup(ns_t * s) { if (NS_LTMAX(s,1)) { return ns_push(s, NS_PEEK(s)); } return 1; } DC_EXPORT inline void ns_clear(ns_t * s) { s->top = -1; } DC_EXPORT inline int ns_reverse(ns_t * s) { if (s->top > 0) { mpf_swap(NS_PEEK(s), NS_PEEKN(s,-1)); return 0; } else { return 1; } } /* nrotate = x - 1 top - n swapped with peek -x */ DC_EXPORT int ns_nrotate(ns_t * s) { mpf_t * val = ns_pop(s); if (!val) { NS_UNDERFLOW(1); } else { signed long v = mpf_get_si(*val); long i = -v; const long l = -v; while (++i < v) { mpf_swap(NS_PEEKN(s,i), NS_PEEKN(s,l)); } } return 0; } /* Registers */ ns_t g_reg[NS_REG_MAX]; void ns_reg_init(void) { size_t i; for (i = 0; i < NS_REG_MAX; ++i) { g_reg[i].top = -1; g_reg[i].max = 0; } } void ns_reg_free(void) { size_t i; for (i = 0; i < NS_REG_MAX; ++i) { ns_free(&g_reg[i]); } } DC_EXPORT int ns_reg_set(ns_t * s, int c) { mpf_t * val = ns_pop(s); if (!val) { NS_REG_UNDERFLOW(1, c); } if (g_reg[c].top > -1) { mpf_set(NS_PEEKDOT(g_reg[c]), *val); } else { ns_push(&g_reg[c], *val); } return 0; } DC_EXPORT int ns_reg_get(ns_t * s, int c) { mpf_t * val = ns_pop(&g_reg[c]); if (!val) { NS_REG_UNDERFLOW(1, c); } if (NS_LTMAX(s,1)) { ns_push(s, *val); } return 0; } DC_EXPORT int ns_reg_push(ns_t * s, int c) { mpf_t * val = ns_pop(s); if (!val) { NS_REG_UNDERFLOW(1, c); } if (NS_LTMAXDOT(g_reg[c],1)) { ns_push(&g_reg[c], *val); } return 0; } DC_EXPORT int ns_reg_pop(ns_t * s, int c) { mpf_t * val = ns_pop(&g_reg[c]); if (!val) { NS_REG_UNDERFLOW(1, c); } if (NS_LTMAX(s,1)) { ns_push(s, *val); } return 0; } DC_EXPORT int ns_reg_push_index(ns_t * s, int c) { mpf_t * val, * ip; ip = ns_pop(s); val = ns_pop(s); if (!ip || !val) { NS_REG_UNDERFLOW(1, c); } else { ssize_t i = mpf_get_si(*ip); if (NS_EXPANDDOT(g_reg[c],i+1)) { mpf_set(g_reg[c].num[i], *val); return 0; } else return 1; } } DC_EXPORT int ns_reg_pop_index(ns_t * s, int c) { mpf_t * ip = ns_pop(s); if (!ip) { NS_REG_UNDERFLOW(1, c); } else { ssize_t i; i = mpf_get_ui(*ip); if (NS_LTMAX(s,1) && NS_EXPANDDOT(g_reg[c],i+1)) { ns_push(s, g_reg[c].num[i]); return 0; } else return 1; } } /* Printing */ DC_EXPORT void ns_printline_all(ns_t * s) { ssize_t i; #ifdef FOR_HUMANS for (i = 0; i <= s->top; ++i) #else for (i = s->top; i >= 0; --i) #endif /* FOR_HUMANS */ { gmp_printf(NS_FORMAT, s->num[i]); } } DC_EXPORT inline void ns_printline_peek(ns_t * s) { if (s->top > -1) { gmp_printf(NS_FORMAT, NS_PEEK(s)); } } DC_EXPORT inline int ns_print_peek(ns_t * s) { if (s->top > -1) { gmp_printf(NS_FORMAT, NS_PEEK(s)); return 0; } else { return 1; } } #undef PRINT /* Numbers */ DC_EXPORT inline int ns_isnum(char c) { switch (c) { case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return 1; default: return 0; } } DC_EXPORT size_t ns_getnum(ns_t * s, char * eval, size_t len, int base) { size_t i = 0; char t; while (ns_isnum(eval[i]) && i < len) { ++i; } if (!i) { return 0; } if (NS_LTMAX(s,1)) { ++s->top; t = eval[i]; eval[i] = '\0'; mpf_set_str(NS_PEEK(s), eval, base); eval[i] = t; return i-1; } else { NS_OVERFLOW(0); } } /* Digits */ #if 0 DC_EXPORT int ns_digit_last(ns_t * s) { return 1; } DC_EXPORT int ns_digit(ns_t * s) { return 1; } #endif /* 0 */ /* Arithmetic */ /* handles negative numbers and is multi-precision on both operands unlike mpf_exp_ui */ DC_EXPORT int ns_exp(ns_t * s) { if (s->top > 0) { if (!(mpf_cmp_ui(NS_PEEK(s), 0) || mpf_cmp_ui(s->num[s->top-1], 0))) { fprintf(stderr, "dc: divide by 0\n"); return 2; } else { mpf_t i; mpf_t mpf; if (mpf_cmp_ui(NS_PEEK(s), 0) == 0) { mpf_set_ui(NS_PEEK(s), 1); return 0; } --s->top; mpf_inits(i, mpf, NULL); mpf_set(mpf, NS_PEEK(s)); mpf_set(i, s->num[s->top + 1]); mpf_ceil(i, i); mpf_sub_ui(i, i, 1); if (mpf_cmp_ui(i, 0) > 0) { for (; mpf_cmp_ui(i, 0); mpf_sub_ui(i, i, 1)) { mpf_mul(NS_PEEK(s), NS_PEEK(s), mpf); } } else { for (; mpf_cmp_ui(i, 0); mpf_add_ui(i, i, 1)) { mpf_div(NS_PEEK(s), NS_PEEK(s), mpf); } } mpf_clears(i, mpf, NULL); return 0; } } NS_UNDERFLOW(1); } NS_OP(ns_add, mpf_add) NS_OP(ns_sub, mpf_sub) NS_OP(ns_mul, mpf_mul) NS_OP_ONE(ns_abs, mpf_abs) NS_OP_ONE(ns_sqrt, mpf_sqrt) NS_OP_ONE(ns_floor, mpf_floor) NS_OP_ONE(ns_ceil, mpf_ceil) NS_OP_DIV(ns_div, mpf_div) /* Params */ DC_EXPORT int ns_pop_prec(ns_t * s) { ssize_t i, f; mpf_t * mpf = ns_pop(s); if (!mpf) { NS_UNDERFLOW(1); } else { size_t prec = mpf_get_ui(*mpf); /* fprintf(stderr, "new prec: %ld\n", prec); */ g_prec = prec; for (i = 0; i < s->max; ++i) { mpf_set_prec(s->num[i], prec); } for (f = NS_REG_OFF; f < NS_REG_MAX; ++f) { for (i = 0; i < g_reg[f].max; ++i) { mpf_set_prec(g_reg[f].num[i], prec); } } return 0; } } DC_EXPORT int ns_push_prec(ns_t * s) { return ns_push_ui(s, g_prec); } DC_EXPORT int ns_pop_iradix(ns_t * s) { mpf_t * mpf = ns_pop(s); if (!mpf) { NS_UNDERFLOW(1); } else { int x = (int) mpf_get_ui(*mpf); if (2 < x && x < INT_MAX - 1) { fprintf(stderr, PROGN ": input base must be a number between 2 and %d (inclusive)\n", INT_MAX - 1); } return 0; } } DC_EXPORT int ns_push_iradix(ns_t * s) { return ns_push_ui(s, g_iradix); } DC_EXPORT int ns_pop_oradix(ns_t * s) { mpf_t * mpf = ns_pop(s); if (!mpf) { NS_UNDERFLOW(1); } else { g_oradix = (int) mpf_get_ui(*mpf); return 0; } } DC_EXPORT int ns_push_oradix(ns_t * s) { return ns_push_ui(s, g_oradix); } /* Test Functions */ #if 0 /* It probably shouldn't return -1 but I'm not worried about this function */ DC_EXPORT int ns_ndup(ns_t * s) { int ret = 0; mpf_t dupn; if (!ns_pop(s)) { NS_UNDERFLOW(-1); } for (mpf_set(dupn, NS_PEEKN(s,1)); !ret && mpf_cmp_ui(dupn,1); mpf_sub_ui(dupn,dupn,1)) { ret = ns_dup(s); } return ret; } #endif /* 0 */ /* Definition Cleanup */ /* #undef NS_OP */ /* #undef NS_OP_ONE */ /* #undef NS_OP_DIV */ /* #undef NS_OVERFLOW */ /* #undef NS_UNDERFLOW */ /* #undef NS_REG_UNDERFLOW */ /* #undef NS_DIV0 */