#include #include #include #include #include #include #define MINBASE 2 #define MAXBASE 255 #define MINPREC 1 #define MAXPREC 1023 #define MINEXP -512 #define MAXEXP 511 typedef unsigned char MANTTYPE; #define BASEBITS 8 #define PRECBITS 10 #define KARATSUBA_THRESHOLD 16 extern const char *__progname; enum nodetype { NT_CONST, NT_VAR, NT_HIST, NT_UNARY, NT_BINARY, NT_FUNCTION } ; enum unaryop { U_PLUS, U_MINUS, U_RECIP } ; enum binaryop { B_PLUS, B_MINUS, B_TIMES, B_DIVIDE, B_ASSIGN } ; enum toktype { TOK_EOF, TOK_EOL, TOK_ERR, TOK_NUMBER, TOK_INUMBER, TOK_VAR, TOK_CHAR, TOK_FLAGS #define TOKF_ECHO 0x00000001 #define TOKF_NOHIST 0x00000002 #define TOKF_NONL 0x00000004 #define TOKF_QUIET 0x00000008 } ; typedef enum binaryop BINARYOP; typedef enum nodetype NODETYPE; typedef enum toktype TOKTYPE; typedef enum unaryop UNARYOP; typedef struct fxn FXN; typedef struct node NODE; typedef struct num NUM; typedef struct numlist NUMLIST; typedef struct var VAR; typedef struct memblk MEMBLK; struct memblk { MEMBLK *flink; MEMBLK *blink; unsigned int len; int line; int printed; } __attribute__((__aligned__)); /* if zero is set, neg must be zero, and exp, digs, mant are ignored - but mant must still be allocated or nil. */ /* note that mant[0] is the most, not least, significant digit */ struct num { unsigned int neg : 1; unsigned int zero : 1; unsigned int base : BASEBITS; unsigned int prec : PRECBITS; unsigned int digs : PRECBITS; signed int exp; unsigned int refcnt; MANTTYPE *mant; } ; struct numlist { NUMLIST *link; int n; NUM *value; } ; struct node { NODETYPE type; union { NUM *c; char *var; int hist; struct { UNARYOP op; NODE *arg; } u; struct { BINARYOP op; NODE *lhs; NODE *rhs; } b; struct { char *name; int inx; int nargs; NODE **args; } f; } u; } ; struct var { VAR *link; char *name; NUM *value; } ; struct fxn { const char *name; union { NUM *(*numfn)(int, NUM **); NUM *(*nodefn)(int, NODE **); } u; unsigned int flags; #define FF_NOEVAL 0x00000001 } ; #define NUMFN(name) { #name, { numfn: C_##name }, 0 } #define NODEFN(name) { #name, { nodefn: C_##name }, FF_NOEVAL } static int exprno; static VAR *vars; static NUMLIST *history; static int untokened; static TOKTYPE toktype; static int toklen; static char *tokbuf; static int tokhave; static NUM *toknum; static int tokinum; static int ungetval; static int ungotten; static int defprec; static int defbase; static int pminexp; static int pmaxexp; static int debugging; static MEMBLK *memblks; static unsigned int warned; #define WARN_FRACDROP 0x00000001 #define WARN_OVERFLOW 0x00000002 #define WARN_LOSSMULT 0x00000004 #define WARN_LOSSADD 0x00000008 static int qflag = 0; static int hflag = 0; static int mflag = 0; static int lflags = 0; static NUM *C_chop(int, NUM **); static NUM *C_round(int, NUM **); static NUM *C_cvt(int, NUM **); static NUM *C_pow(int, NUM **); static NUM *C_abs(int, NUM **); static NUM *C_sqrt(int, NUM **); static NUM *C_BASE(int, NUM **); static NUM *C_PREC(int, NUM **); static NUM *C_EXP(int, NUM **); static NUM *C_MANT(int, NUM **); static NUM *C_ULP(int, NUM **); static NUM *C_int(int, NUM **); static NUM *C_gcd(int, NUM **); static NUM *C_baseline(int, NUM **); static NUM *C_solve(int, NODE **); static NUM *C_const(int, NODE **); static FXN fxns[] = { NUMFN(chop), NUMFN(round), NUMFN(cvt), NUMFN(pow), NUMFN(abs), NUMFN(sqrt), NUMFN(int), NUMFN(BASE), NUMFN(PREC), NUMFN(EXP), NUMFN(MANT), NUMFN(ULP), NUMFN(gcd), NUMFN(baseline), NODEFN(solve), NODEFN(const), { 0 } }; /* forward */ static NODE *parse_top(void); static void init(void) { exprno = 1; vars = 0; history = 0; untokened = 0; tokbuf = 0; tokhave = 0; toknum = 0; ungotten = 0; defprec = 64; defbase = 10; pminexp = -5; pmaxexp = 10; debugging = 0; memblks = 0; } static int get(void) { if (ungotten) { ungotten = 0; return(ungetval); } return(getchar()); } static void unget(int c) { ungotten = 1; ungetval = c; } static void flushnl(void) { int c; do { c = get(); } while ((c != '\n') && (c != EOF)); } static void pwarn(int warnbit, const char *msg) { if (warned & warnbit) return; warned |= warnbit; printf("warning: %s\n",msg); } static void *malloc_blk(unsigned int nb, int lno) { MEMBLK *b; if (nb < 1) return(0); if (! mflag) return(malloc(nb)); b = malloc(sizeof(MEMBLK)+nb); b->len = nb; b->line = lno; b->printed = 0; if (memblks) memblks->blink = b; b->flink = memblks; b->blink = 0; memblks = b; return(b+1); } #define malloc(x) malloc_blk((x),__LINE__) static void free_blk(void *old) { MEMBLK *b; if (! old) return; if (! mflag) { free(old); return; } b = ((MEMBLK *)old) - 1; if (b->flink) b->flink->blink = b->blink; if (b->blink) b->blink->flink = b->flink; else memblks = b->flink; free(b); } #define free(x) free_blk(x) static void *realloc_blk(void *old, unsigned int nb, int lno) { MEMBLK *b; MEMBLK *ob; if (nb < 1) { free_blk(old); return(0); } if (! mflag) return(realloc(old,nb)); if (! old) return(malloc_blk(nb,lno)); ob = ((MEMBLK *)old) - 1; b = realloc(ob,sizeof(MEMBLK)+nb); b->len = nb; b->line = lno; if (b != ob) { if (b->flink) b->flink->blink = b; if (b->blink) b->blink->flink = b; else memblks = b; } return(b+1); } #define realloc(x,n) realloc_blk((x),(n),__LINE__) static char *strdup_blk(const char *s, int lno) { char *rv; rv = malloc_blk(strlen(s)+1,lno); strcpy(rv,s); return(rv); } #define strdup(s) strdup_blk((s),__LINE__) static MANTTYPE *mmalloc_blk(int n, int lno) { return(malloc_blk(n*sizeof(MANTTYPE),lno)); } #define mmalloc(n) mmalloc_blk((n),__LINE__) static MANTTYPE *mrealloc_blk(MANTTYPE *old, int n, int lno) { return(realloc_blk(old,n*sizeof(MANTTYPE),lno)); } #define mrealloc(x,n) mrealloc_blk((x),(n),__LINE__) static void leakcheck(void) { if (memblks) { MEMBLK *b; unsigned int i; for (b=memblks;b;b=b->flink) { if (! b->printed) { printf("line %d: %u %p =",b->line,b->len,(void *)(b+1)); for (i=0;ilen;i++) printf(" %02x",((unsigned char *)(b+1))[i]); printf("\n"); b->printed = 1; } } } } static void mzero(MANTTYPE *m, int n) { if (n > 0) bzero(m,n*sizeof(MANTTYPE)); } static void mcopy(MANTTYPE *f, MANTTYPE *t, int n) { bcopy(f,t,n*sizeof(MANTTYPE)); } static NUM *num_alloc(void) { NUM *rv; rv = malloc(sizeof(NUM)); rv->neg = 0; rv->zero = 0; rv->base = defbase; rv->prec = defprec; rv->digs = 0; rv->exp = 0; rv->refcnt = 1; rv->mant = 0; return(rv); } static void num_free(NUM *n) { free(n->mant); memset(n,'x',sizeof(NUM)); free(n); } static NUM *num_ref(NUM *n) { n->refcnt ++; if (n->refcnt > 100) abort(); return(n); } static void num_deref(NUM *n) { if (n == 0) return; if (n->refcnt == 0) abort(); n->refcnt --; if (n->refcnt > 100) abort(); if (n->refcnt == 0) num_free(n); } static void node_free(NODE *n) { switch (n->type) { case NT_CONST: num_deref(n->u.c); break; case NT_VAR: free(n->u.var); break; case NT_HIST: break; case NT_UNARY: node_free(n->u.u.arg); break; case NT_BINARY: node_free(n->u.b.lhs); node_free(n->u.b.rhs); break; case NT_FUNCTION: { int i; free(n->u.f.name); for (i=0;iu.f.nargs;i++) node_free(n->u.f.args[i]); free(n->u.f.args); } break; } free(n); } static void printdigit(MANTTYPE digit, unsigned int base) { if (base <= 36) putchar("0123456789abcdefghijklmnopqrstuvwxyz"[digit]); else printf(":%lu",(unsigned long int)digit); } static void printnum(NUM *n) { int i; if (n->zero) { printdigit(0,10); return; } if (n->neg) putchar('-'); if (n->base != defbase) { putchar('#'); switch (n->base) { case 2: putchar('b'); break; case 8: putchar('o'); break; case 10: putchar('d'); break; case 16: putchar('x'); break; default: printf("%d#",n->base); break; } } if (n->prec != defprec) { printf("_%d_",n->prec); } if ( ((n->exp >= pminexp) && (n->exp <= pmaxexp)) || ((n->exp > 0) && (n->digs <= n->exp) && ((n->digs*3) > (n->exp*2))) ) { if (n->exp > 0) { if ((n->prec == defprec) && (defprec < n->exp)) { printf("_%d_",defprec); } if (n->digs <= n->exp) { for (i=0;idigs;i++) printdigit(n->mant[i],n->base); for (;iexp;i++) printdigit(0,n->base); } else { for (i=0;iexp;i++) printdigit(n->mant[i],n->base); putchar('.'); for (;idigs;i++) printdigit(n->mant[i],n->base); } } else { putchar('.'); for (i=n->exp;i<0;i++) printdigit(0,n->base); for (i=0;idigs;i++) printdigit(n->mant[i],n->base); } } else { putchar('.'); for (i=0;idigs;i++) printdigit(n->mant[i],n->base); if (n->exp) printf("@%d",n->exp); } } static int roundnum(MANTTYPE *dp, unsigned int base, unsigned int leftdigs, unsigned int rightdigs) { if (base & 1) { MANTTYPE *rp; unsigned int dv; dv = (base - 1) / 2; rp = dp; while ((rightdigs-- > 0) && (*rp++ == dv)) ; if ((rightdigs == -1U) || (*rp < dv)) return(0); } else { if (*dp < base/2) return(0); } while ((leftdigs-- > 0) && (*--dp == base-1)) *dp = 0; if (leftdigs == -1U) { return(1); } else { ++*dp; return(0); } } static NUM *copynum(NUM *n) { NUM *rv; rv = malloc(sizeof(NUM)); *rv = *n; rv->refcnt = 1; if (! n->zero) { rv->mant = mmalloc(rv->digs); mcopy(n->mant,rv->mant,rv->digs); } else { rv->mant = 0; } return(rv); } static NUM *num_modify(NUM *n, int minrefs) { if (n->refcnt > minrefs) { num_deref(n); n = copynum(n); } return(n); } static int num_intval(NUM *n) { int iv; int i; int nd; if (n->zero) return(0); if (n->digs == 0) return(0); if (n->digs > n->exp) { pwarn(WARN_FRACDROP,"discarding fractional part"); nd = n->exp; } else { nd = n->digs; } if (n->exp < 1) return(0); iv = 0; for (i=0;ibase) + n->mant[i]; i = n->exp - nd; if (i > sizeof(int)*CHAR_BIT) { pwarn(WARN_OVERFLOW,"overflow"); i = sizeof(int) * CHAR_BIT; } for (;i>0;i--) iv *= n->base; if (n->neg) iv = - iv; return(iv); } static void setmant1(NUM *n) { n->mant[0] = 1; mzero(&n->mant[1],n->digs-1); } static NODE *node_make(int type, ...) { NODE *n; va_list ap; n = malloc(sizeof(NODE)); n->type = type; va_start(ap,type); switch (type) { default: abort(); break; case NT_CONST: n->u.c = va_arg(ap,NUM *); break; case NT_VAR: n->u.var = va_arg(ap,char *); break; case NT_HIST: n->u.hist = va_arg(ap,int); break; case NT_UNARY: n->u.u.op = va_arg(ap,int); n->u.u.arg = va_arg(ap,NODE *); break; case NT_BINARY: n->u.b.lhs = va_arg(ap,NODE *); n->u.b.op = va_arg(ap,int); n->u.b.rhs = va_arg(ap,NODE *); break; case NT_FUNCTION: { int i; n->u.f.name = va_arg(ap,char *); n->u.f.inx = -1; n->u.f.nargs = va_arg(ap,int); n->u.f.args = malloc(n->u.f.nargs*sizeof(NODE *)); for (i=0;iu.f.nargs;i++) n->u.f.args[i] = va_arg(ap,NODE *); } break; } va_end(ap); return(n); } static NUM *num_for_int_detail(int v, int base, int prec) { NUM *n; int i; int t; n = num_alloc(); n->base = base; n->prec = prec; if (v == 0) { n->zero = 1; return(n); } if (v < 0) { n->neg = 1; v = - v; } for (i=1,t=v;t>=base;i++,t/=base) ; n->digs = i; n->exp = i; n->mant = mmalloc(i); for (i--,t=v;i>=0;i--,t/=base) n->mant[i] = t % base; return(n); } static NUM *num_for_int(int v) { return(num_for_int_detail(v,defbase,defprec)); } #define SETREF(loc,num) do { num_deref(loc); (loc) = num_ref(num); } while (0) static NUM *num_chop(NUM *n, int p) { n = num_ref(n); if (p < n->prec) { n = num_modify(n,1); n->prec = p; } if (p < n->digs) { int i; n = num_modify(n,1); for (i=p-1;(i>=0)&&(n->mant[i]==0);i--) ; if (i < 0) n->zero = 1; else n->digs = i + 1; } return(n); } static NUM *C_chop(int nargs, NUM **args) { NUM *rv; int p; if (nargs != 2) { printf("usage: chop(number,digits)\n"); return(0); } p = num_intval(args[1]); rv = num_chop(args[0],p); return(rv); } static NUM *C_round(int nargs, NUM **args) { NUM *rv; int p; if (nargs != 2) { printf("usage: round(number,digits)\n"); return(0); } rv = num_ref(args[0]); p = num_intval(args[1]); if (p < rv->prec) rv->prec = p; if (p < rv->digs) { int nr; rv = num_modify(rv,2); nr = rv->digs - p; rv->digs = p; if (roundnum(rv->mant+p,rv->base,p,nr)) { if (rv->exp == MAXEXP) { printf("overflow in round(), clipping exponent to @%d\n",MAXEXP); } else { rv->exp ++; } rv->digs = 1; setmant1(rv); } else { for (p=rv->digs-1;(p>=0)&&(rv->mant[p]==0);p--) ; if (p < 0) rv->zero = 1; else rv->digs = p + 1; } } return(rv); } static NUM *C_cvt(int nargs, NUM **args) { NUM *rv; unsigned int b; unsigned int p; unsigned int ob; unsigned int op; MANTTYPE *it; unsigned int nt; unsigned int toff; MANTTYPE *tmp; int i; int oexp; int nexp; if ((nargs < 2) || (nargs > 3)) { printf("usage: cvt(number,base[,precision])\n"); return(0); } b = num_intval(args[1]); p = (nargs > 2) ? num_intval(args[2]) : defprec; if ((b < MINBASE) || (b > MAXBASE)) { printf("invalid base in cvt()\n"); return(0); } if ((p < MINPREC) || (p > MAXPREC)) { printf("invalid precision in cvt()\n"); return(0); } toff = (sizeof(int) * CHAR_BIT) + 1; nt = p + BASEBITS + 2; tmp = mmalloc(nt+toff) + toff; ob = args[0]->base; op = args[0]->digs; it = mmalloc(op); mcopy(args[0]->mant,it,op); i = 0; nexp = 0; while (i < nt) { int a; int j; a = 0; if (op > 0) { for (j=op-1;j>=0;j--) { a += it[j] * b; it[j] = a % ob; a /= ob; } if (it[op-1] == 0) op --; } if (a || i) tmp[i++] = a; else nexp--; } free(it); oexp = args[0]->exp; while (oexp > 0) { unsigned int power; unsigned int pmax; int j; unsigned int a; pmax = UINT_MAX / (b * ob); for (power=1;oexp&&(power=0;j--) { a += tmp[j] * power; tmp[j] = a % b; a /= b; } for (;a;j--) { tmp[j] = a % b; a /= b; } j ++; if (j < 0) { nexp -= j; mcopy(tmp+j,tmp,nt); } } if (oexp < 0) { mcopy(tmp,tmp-toff,nt); tmp -= toff; mzero(tmp+nt,toff); nt += toff; while (oexp < 0) { unsigned int power; unsigned int pmax; int j; unsigned int a; pmax = UINT_MAX / (b * ob); for (power=1;oexp&&(power 0) { mcopy(tmp+i,tmp,nt-i); nexp -= i; for (j-=i;jbase = b; rv->prec = p; for (i=p-1;(i>=0)&&(tmp[i]==0);i--) ; if (i < 0) { rv->zero = 1; } else { rv->digs = i + 1; if (nexp < MINEXP) { printf("exponent underflow in cvt(), returning zero\n"); rv->zero = 1; } else { if (nexp > MAXEXP) { printf("exponent overflow in cvt(), clipping to @%d\n",MAXEXP); nexp = MAXEXP; } rv->exp = nexp; rv->mant = mmalloc(rv->digs); mcopy(tmp,rv->mant,rv->digs); } } free(tmp-toff); return(rv); } static void mixedbase(void) { printf("mixed-base arithmetic unimplemented\n"); } /* * We use Karatsuba's multiplier algorithm. See karatsuba.doc for * details on just what this means. */ #ifdef MULTIPLIER_TRACE static int ktsu_dd = 0; #endif /* * Baseline multiplication. The grounding case for the multiplier's * recursion. We could do this the log-space column-by-column way * instead, but it doesn't make enough difference to care about. */ static void mul_baseline(unsigned int base, MANTTYPE *a, int ad, MANTTYPE *b, int bd, MANTTYPE *p, int pd) { int ax; int bx; int px0; int px; unsigned int prod; #ifdef MULTIPLIER_TRACE int i; ktsu_dd ++; printf("%*smul_baseline: a=[%d]",ktsu_dd*2,"",ad); for (i=0;i=0;bx--) { for (ax=ad-1;ax>=0;ax--) { px = px0 + ax + bx; prod = a[ax] * b[bx]; while (prod) { if (px < 0) abort(); prod += p[px]; p[px] = prod % base; prod /= base; px --; } } } #ifdef MULTIPLIER_TRACE printf("%*sp=[%d]",ktsu_dd*2,"",pd); for (i=0;i