546 lines
12 KiB
C
546 lines
12 KiB
C
/* 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 */
|