This repository has been archived on 2024-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
dc/ns.c
2023-09-23 17:26:21 +00:00

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 */