ref: 14cac15cc44e62351d644e8fb31dc600fa25ee14
parent: 5b8ab1a707f43bb50a0223fd710b7e17cd9a802c
author: qwx <qwx@sciops.net>
date: Wed Jan 7 05:32:08 EST 2026
pc: add #, output mask allows fixing width of output for eg. negative numbers. needs manpage
--- /dev/null
+++ b/sys/src/cmd/pc.y
@@ -1,0 +1,1100 @@
+%{+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mp.h>
+#include <thread.h>
+#include <libsec.h>
+
+int inbase = 10, outbase, divmode, sep, heads, fail, prompt, eof;
+enum { MAXARGS = 16 };+
+typedef struct Num Num;
+struct Num {+ mpint;
+ int b;
+ Ref;
+};
+enum { STRONG = 0x100 };+Num *outmask;
+
+void *
+emalloc(int n)
+{+ void *v;
+
+ v = malloc(n);
+ if(v == nil)
+ sysfatal("malloc: %r");+ memset(v, 0, n);
+ setmalloctag(v, getcallerpc(&n));
+ return v;
+}
+
+void *
+error(char *fmt, ...)
+{+ va_list va;
+ Fmt f;
+ char buf[256];
+
+ fmtfdinit(&f, 2, buf, sizeof(buf));
+ va_start(va, fmt);
+ fmtvprint(&f, fmt, va);
+ fmtrune(&f, '\n');
+ fmtfdflush(&f);
+ va_end(va);
+ fail++;
+ return nil;
+}
+
+Num *
+numalloc(void)
+{+ Num *r;
+
+ r = emalloc(sizeof(Num));
+ r->ref = 1;
+ r->p = emalloc(sizeof(mpdigit));
+ mpassign(mpzero, r);
+ return r;
+}
+
+Num *
+numincref(Num *n)
+{+ incref(n);
+ return n;
+}
+
+Num *
+numdecref(Num *n)
+{+ if(n == nil) return nil;
+ if(decref(n) == 0){+ free(n->p);
+ free(n);
+ return nil;
+ }
+ return n;
+}
+
+Num *
+nummod(Num *n)
+{+ Num *m;
+
+ if(n == nil) return nil;
+ if(n->ref == 1) return n;
+ m = numalloc();
+ mpassign(n, m);
+ m->b = n->b;
+ numdecref(n);
+ return m;
+}
+
+int
+basemax(int a, int b)
+{+ if(a == STRONG+10 && b >= STRONG) return b;
+ if(b == STRONG+10 && a >= STRONG) return a;
+ if(a == 10) return b;
+ if(b == 10) return a;
+ if(a < b) return b;
+ return a;
+}
+
+%}
+%token LOEXP LOLSH LORSH LOEQ LONE LOLE LOGE LOLAND LOLOR
+%{+
+Num *
+numbin(int op, Num *a, Num *b)
+{+ mpint *r;
+
+ if(fail || a == nil || b == nil) return nil;
+ a = nummod(a);
+ a->b = basemax(a->b, b->b);
+ switch(op){+ case '+': mpadd(a, b, a); break;
+ case '-': mpsub(a, b, a); break;
+ case '*': mpmul(a, b, a); break;
+ case '/':
+ if(mpcmp(b, mpzero) == 0){+ numdecref(a);
+ numdecref(b);
+ return error("division by zero");+ }
+ r = mpnew(0);
+ mpdiv(a, b, a, r);
+ if(!divmode && r->sign < 0)
+ if(b->sign > 0)
+ mpsub(a, mpone, a);
+ else
+ mpadd(a, mpone, a);
+ mpfree(r);
+ break;
+ case '%':
+ if(mpcmp(b, mpzero) == 0){+ numdecref(a);
+ numdecref(b);
+ return error("division by zero");+ }
+ mpdiv(a, b, nil, a);
+ if(!divmode && a->sign < 0)
+ if(b->sign > 0)
+ mpadd(a, b, a);
+ else
+ mpsub(a, b, a);
+ break;
+ case '&': mpand(a, b, a); break;
+ case '|': mpor(a, b, a); break;
+ case '^': mpxor(a, b, a); break;
+ case LOEXP:
+ if(mpcmp(b, mpzero) < 0){+ numdecref(a);
+ numdecref(b);
+ return error("negative exponent");+ }
+ mpexp(a, b, nil, a);
+ break;
+ case LOLSH:
+ if(mpsignif(b) >= 31){+ if(b->sign > 0)
+ error("left shift overflow");+ itomp(-(mpcmp(a, mpzero) < 0), a);
+ }else
+ mpasr(a, -mptoi(b), a);
+ break;
+ case LORSH:
+ if(mpsignif(b) >= 31){+ if(b->sign < 0)
+ error("right shift overflow");+ itomp(-(mpcmp(a, mpzero) < 0), a);
+ }else
+ mpasr(a, mptoi(b), a);
+ break;
+ case '<': itomp(mpcmp(a, b) < 0, a); a->b = 0; break;
+ case '>': itomp(mpcmp(a, b) > 0, a); a->b = 0; break;
+ case LOLE: itomp(mpcmp(a, b) <= 0, a); a->b = 0; break;
+ case LOGE: itomp(mpcmp(a, b) >= 0, a); a->b = 0; break;
+ case LOEQ: itomp(mpcmp(a, b) == 0, a); a->b = 0; break;
+ case LONE: itomp(mpcmp(a, b) != 0, a); a->b = 0; break;
+ case LOLAND:
+ a->b = b->b;
+ if(mpcmp(a, mpzero) == 0)
+ mpassign(mpzero, a);
+ else
+ mpassign(b, a);
+ break;
+ case LOLOR:
+ a->b = b->b;
+ if(mpcmp(a, mpzero) != 0)
+ mpassign(mpone, a);
+ else
+ mpassign(b, a);
+ break;
+ case '$':
+ a->b = b->b;
+ mpxtend(b, mptoi(a), a);
+ break;
+ }
+ numdecref(b);
+ return a;
+}
+
+typedef struct Symbol Symbol;
+struct Symbol {+ enum {+ SYMNONE,
+ SYMVAR,
+ SYMFUNC,
+ } t;
+ Num *val;
+ int nargs;
+ Num *(*func)(int, Num **);
+ char *name;
+ Symbol *next;
+};
+Symbol *symtab[64];
+
+Symbol *
+getsym(char *n, int mk)
+{+ Symbol **p;
+ for(p = &symtab[*n&63]; *p != nil; p = &(*p)->next)
+ if(strcmp((*p)->name, n) == 0)
+ return *p;
+ if(!mk) return nil;
+ *p = emalloc(sizeof(Symbol));
+ (*p)->name = strdup(n);
+ return *p;
+}
+
+static void
+printhead(int n, int s, int sp, char *t)
+{+ char *q;
+ int i, j, k;
+
+ for(i = 1; i < n; i *= 10)
+ ;
+ while(i /= 10, i != 0){+ q = t;
+ *--q = 0;
+ for(j = 0, k = 0; j < n; j += s, k++){+ if(k == sep && sep != 0){+ *--q = ' ';
+ k = 0;
+ }
+ if(j >= i || j == 0 && i == 1)
+ *--q = '0' + j / i % 10;
+ else
+ *--q = ' ';
+ }
+ for(j = 0; j < sp; j++)
+ *--q = ' ';
+ print("%s\n", q);+ }
+}
+
+Num *
+nummask(Num *n)
+{+ if(outmask == nil)
+ return n;
+ numincref(n);
+ numincref(outmask);
+ n = numbin('&', n, outmask);+ return n;
+}
+
+void
+numprint(Num *n)
+{+ int b;
+ int l, i, st, sp;
+ char *s, *t, *p, *q;
+
+ if(n == nil) return;
+ if(n->b >= STRONG || n->b != 0 && outbase == 0)
+ b = n->b & ~STRONG;
+ else if(outbase == 0)
+ b = 10;
+ else
+ b = outbase;
+ n = nummask(n);
+ s = mptoa(n, b, nil, 0);
+ l = strlen(s);
+ t = emalloc(l * 2 + 4);
+ q = t + l * 2 + 4;
+ if(heads){+ switch(b){+ case 16: st = 4; sp = 2; break;
+ case 8: st = 3; sp = 1; break;
+ case 2: st = 1; sp = 2; break;
+ default: st = 0; sp = 0;
+ }
+ if(n->sign < 0)
+ sp++;
+ if(st != 0)
+ printhead(mpsignif(n), st, sp, q);
+ }
+ *--q = 0;
+ for(p = s + l - 1, i = 0; p >= s && *p != '-'; p--, i++){+ if(sep != 0 && i == sep){+ *--q = '_';
+ i = 0;
+ }
+ if(*p >= 'A')
+ *--q = *p + ('a' - 'A');+ else
+ *--q = *p;
+ }
+ if(mpcmp(n, mpzero) != 0)
+ switch(b){+ case 16: *--q = 'x'; *--q = '0'; break;
+ case 10: if(outbase != 0 && outbase != 10 || inbase != 10) {*--q = 'd'; *--q = '0';} break;+ case 8: *--q = '0'; break;
+ case 2: *--q = 'b'; *--q = '0'; break;
+ }
+ if(p >= s)
+ *--q = '-';
+ print("%s\n", q);+ free(s);
+ free(t);
+}
+
+void
+numdecrefs(int n, Num **x)
+{+ int i;
+
+ for(i = 0; i < n; i++)
+ numdecref(x[i]);
+}
+
+Num *
+fncall(Symbol *s, int n, Num **x)
+{+ int i;
+
+ if(s->t != SYMFUNC){+ numdecrefs(n, x);
+ return error("%s: not a function", s->name);+ }
+ else if(s->nargs >= 0 && s->nargs != n){+ numdecrefs(n, x);
+ return error("%s: wrong number of arguments", s->name);+ }
+ for(i = 0; i < n; i++)
+ if(x[i] == nil)
+ return nil;
+ return s->func(n, x);
+}
+
+Num *
+hexfix(Symbol *s)
+{+ char *b, *p, *q;
+
+ if(inbase != 16) return nil;
+ if(s->val != nil) return numincref(s->val);
+ if(strspn(s->name, "0123456789ABCDEFabcdef_") != strlen(s->name)) return nil;
+ b = strdup(s->name);
+ for(p = b, q = b; *p != 0; p++)
+ if(*p != '_')
+ *q++ = *p;
+ *q = 0;
+ s->val = numalloc();
+ strtomp(b, nil, 16, s->val);
+ s->val->b = 16;
+ free(b);
+ return numincref(s->val);
+}
+
+%}
+
+%union {+ Num *n;
+ Symbol *sym;
+ struct {+ Num *x[MAXARGS];
+ int n;
+ } args;
+}
+
+%token <n> LNUM
+%token <sym> LSYMB
+
+%type <n> expr
+%type <args> elist elist1
+
+%right '='
+%right '?'
+%left LOLOR
+%left LOLAND
+%left '|'
+%left '^'
+%left '&'
+%left LOEQ LONE
+%left '<' '>' LOLE LOGE
+%left LOLSH LORSH
+%left '+' '-'
+%left unary
+%left '*' '/' '%'
+%right LOEXP
+%right '$'
+
+%{+ int save;
+ Num *last;
+ Num *lastp;
+%}
+
+%%
+
+input: | input line '\n' {+ if(!fail && last != nil) {+ numprint(last);
+ numdecref(lastp);
+ lastp = last;
+ }
+ fail = 0;
+ last = nil;
+ }
+
+line: stat
+ | line ';' stat
+
+stat: { last = nil; }+ | expr { last = $1; }+ | '_' { save = inbase; inbase = 10; } expr {+ inbase = save;
+ if(mpcmp($3, mpzero) < 0)
+ error("no.");+ if(!fail)
+ sep = mptoi($3);
+ numdecref($3);
+ numdecref(last);
+ last = nil;
+ }
+ | '<' { save = inbase; inbase = 10; } expr {+ inbase = save;
+ if(!fail)
+ inbase = mptoi($3);
+ if(inbase != 2 && inbase != 8 && inbase != 10 && inbase != 16){+ error("no.");+ inbase = save;
+ }
+ numdecref($3);
+ numdecref(last);
+ last = nil;
+ }
+ | '>' { save = inbase; inbase = 10; } expr {+ inbase = save;
+ save = outbase;
+ if(!fail)
+ outbase = mptoi($3);
+ if(outbase != 0 && outbase != 2 && outbase != 8 && outbase != 10 && outbase != 16){+ error("no.");+ outbase = save;
+ }
+ numdecref($3);
+ numdecref(last);
+ last = nil;
+ }
+ | '/' { save = inbase; inbase = 10; } expr {+ inbase = save;
+ save = divmode;
+ if(!fail)
+ divmode = mptoi($3);
+ if(divmode != 0 && divmode != 1){+ error("no.");+ divmode = save;
+ }
+ numdecref($3);
+ numdecref(last);
+ last = nil;
+ }
+ | '\'' { save = inbase; inbase = 10; } expr {+ inbase = save;
+ save = heads;
+ if(!fail)
+ heads = mptoi($3);
+ if(heads != 0 && heads != 1){+ error("no.");+ heads = save;
+ }
+ numdecref($3);
+ numdecref(last);
+ last = nil;
+ }
+ | '#' { save = inbase; inbase = 10; } expr {+ int mask = -1;
+ inbase = save;
+ if(!fail)
+ mask = mptoi($3);
+ if(mask > 0){+ if(outmask == nil)
+ outmask = numalloc();
+ mpassign(mpone, outmask);
+ mpasr(outmask, -mask, outmask);
+ mpsub(outmask, mpone, outmask);
+ }else if(mask == 0)
+ outmask = numdecref(outmask);
+ else
+ error("no.");+ numdecref($3);
+ numdecref(last);
+ last = nil;
+ }
+ | error
+
+expr: LNUM
+ | '(' expr ')' { $$ = $2; }+ | expr '+' expr { $$ = numbin('+', $1, $3); }+ | expr '-' expr { $$ = numbin('-', $1, $3); }+ | expr '*' expr { $$ = numbin('*', $1, $3); }+ | expr '/' expr { $$ = numbin('/', $1, $3); }+ | expr '%' expr { $$ = numbin('%', $1, $3); }+ | expr '&' expr { $$ = numbin('&', $1, $3); }+ | expr '|' expr { $$ = numbin('|', $1, $3); }+ | expr '^' expr { $$ = numbin('^', $1, $3); } + | expr LOEXP expr { $$ = numbin(LOEXP, $1, $3); }+ | expr LOLSH expr { $$ = numbin(LOLSH, $1, $3); }+ | expr LORSH expr { $$ = numbin(LORSH, $1, $3); }+ | expr LOEQ expr { $$ = numbin(LOEQ, $1, $3); }+ | expr LONE expr { $$ = numbin(LONE, $1, $3); }+ | expr '<' expr { $$ = numbin('<', $1, $3); }+ | expr '>' expr { $$ = numbin('>', $1, $3); }+ | expr LOLE expr { $$ = numbin(LOLE, $1, $3); }+ | expr LOGE expr { $$ = numbin(LOGE, $1, $3); }+ | expr LOLAND expr { $$ = numbin(LOLAND, $1, $3); }+ | expr LOLOR expr { $$ = numbin(LOLOR, $1, $3); }+ | '+' expr %prec unary { $$ = $2; }+ | '-' expr %prec unary { $$ = nummod($2); if($$ != nil) mpsub(mpzero, $$, $$); }+ | '~' expr %prec unary { $$ = nummod($2); if($$ != nil) mpnot($$, $$); }+ | '!' expr %prec unary { $$ = nummod($2); if($$ != nil) {itomp(mpcmp($$, mpzero) == 0, $$); $$->b = 0; } }+ | '$' expr { $$ = nummod($2); if($$ != nil) if($2->sign > 0) mpxtend($2, mpsignif($2), $$); else mpassign($2, $$); }+ | expr '?' expr ':' expr %prec '?' {+ if($1 == nil || mpcmp($1, mpzero) != 0){+ $$ = $3;
+ numdecref($5);
+ }else{+ $$ = $5;
+ numdecref($3);
+ }
+ numdecref($1);
+ }
+ | LSYMB '(' elist ')' { $$ = fncall($1, $3.n, $3.x); }+ | LSYMB {+ Num *n;
+ $$ = nil;
+ switch($1->t){+ case SYMVAR: $$ = numincref($1->val); break;
+ case SYMNONE:
+ n = hexfix($1);
+ if(n != nil) $$ = n;
+ else error("%s undefined", $1->name);+ break;
+ case SYMFUNC: error("%s is a function", $1->name); break;+ default: error("%s invalid here", $1->name);+ }
+ }
+ | LSYMB '=' expr {+ if($1->t != SYMNONE && $1->t != SYMVAR)
+ error("%s redefined", $1->name);+ else if(!fail){+ $1->t = SYMVAR;
+ numdecref($1->val);
+ $1->val = numincref($3);
+ }
+ $$ = $3;
+ }
+ | '@' {+ $$ = lastp;
+ if($$ == nil) error("no last result");+ else numincref($$);
+ }
+ | expr '$' expr { $$ = numbin('$', $1, $3); }+
+elist: { $$.n = 0; } | elist1+elist1: expr { $$.x[0] = $1; $$.n = 1; }+ | elist1 ',' expr {+ $$ = $1;
+ if($$.n >= MAXARGS)
+ error("too many arguments");+ else
+ $$.x[$$.n++] = $3;
+ }
+
+%%
+
+typedef struct Keyword Keyword;
+struct Keyword {+ char *name;
+ int tok;
+};
+
+Keyword ops[] = {+ "**", LOEXP,
+ "<<", LOLSH,
+ "<=", LOLE,
+ ">>", LORSH,
+ ">=", LOGE,
+ "==", LOEQ,
+ "&&", LOLAND,
+ "||", LOLOR,
+ "", 0,
+};
+
+Keyword *optab[128];
+
+
+Biobuf *in;
+int prompted;
+
+int
+yylex(void)
+{+ int c, b;
+ char buf[512], *p;
+ Keyword *kw;
+
+ if(prompt && !prompted && !eof) {print("; "); prompted = 1;}+ do
+ c = Bgetc(in);
+ while(c != '\n' && isspace(c));
+ if(c < 0 && !eof){+ eof = 1;
+ c = '\n';
+ }
+ if(c == '\n')
+ prompted = 0;
+ if(isdigit(c)){+ for(p = buf, *p++ = c; c = Bgetc(in), isalnum(c) || c == '_'; )
+ if(p < buf + sizeof(buf) - 1 && c != '_')
+ *p++ = c;
+ *p = 0;
+ if(c >= 0) Bungetc(in);
+ b = inbase;
+ p = buf;
+ if(*p == '0'){+ p++;
+ switch(*p++){+ case 0: p -= 2; break;
+ case 'b': case 'B': b = 2; break;
+ case 'd': case 'D': b = 10; break;
+ case 'x': case 'X': b = 16; break;
+ default: p--; b = 8; break;
+ }
+ }
+ yylval.n = numalloc();
+ strtomp(p, &p, b, yylval.n);
+ if(*p != 0) error("not a number: %s", buf);+ yylval.n->b = b;
+ return LNUM;
+ }
+ if(isalpha(c) || c >= 0x80 || c == '_'){+ for(p = buf, *p++ = c; c = Bgetc(in), isalnum(c) || c >= 0x80 || c == '_'; )
+ if(p < buf + sizeof(buf) - 1)
+ *p++ = c;
+ *p = 0;
+ Bungetc(in);
+ if(buf[0] == '_' && buf[1] == 0) return '_';
+ yylval.sym = getsym(buf, 1);
+ return LSYMB;
+ }
+ if(c >= 0 && c < 128 && (kw = optab[c], kw != nil)){+ b = Bgetc(in);
+ for(; kw->name[0] == c; kw++)
+ if(kw->name[0] == b)
+ return kw->tok;
+ if(c >= 0) Bungetc(in);
+ }
+ return c;
+}
+
+void
+yyerror(char *msg)
+{+ error("%s", msg);+}
+
+void
+regfunc(char *n, Num *(*f)(int, Num **), int nargs)
+{+ Symbol *s;
+
+ s = getsym(n, 1);
+ s->t = SYMFUNC;
+ s->func = f;
+ s->nargs = nargs;
+}
+
+int
+toint(Num *n, int *p, int mustpos)
+{+ if(mpsignif(n) > 31 || mustpos && mpcmp(n, mpzero) < 0){+ error("invalid argument");+ return -1;
+ }
+ if(p != nil)
+ *p = mptoi(n);
+ return 0;
+}
+
+Num *
+fnhex(int, Num **a)
+{+ Num *r;
+
+ r = nummod(a[0]);
+ r->b = STRONG | 16;
+ return r;
+}
+
+Num *
+fndec(int, Num **a)
+{+ Num *r;
+
+ r = nummod(a[0]);
+ r->b = STRONG | 10;
+ return r;
+}
+
+Num *
+fnoct(int, Num **a)
+{+ Num *r;
+
+ r = nummod(a[0]);
+ r->b = STRONG | 8;
+ return r;
+}
+
+Num *
+fnbin(int, Num **a)
+{+ Num *r;
+
+ r = nummod(a[0]);
+ r->b = STRONG | 2;
+ return r;
+}
+
+Num *
+fnpb(int, Num **a)
+{+ Num *r;
+ int b;
+
+ if(toint(a[1], &b, 1)){+ out:
+ numdecref(a[0]);
+ numdecref(a[1]);
+ return nil;
+ }
+ if(b != 0 && b != 2 && b != 8 && b != 10 && b != 16){+ error("unsupported base");+ goto out;
+ }
+ r = nummod(a[0]);
+ if(b == 0)
+ r->b = 0;
+ else
+ r->b = STRONG | b;
+ return r;
+}
+
+Num *
+fnabs(int, Num **a)
+{+ Num *r;
+
+ r = nummod(a[0]);
+ r->sign = 1;
+ return r;
+}
+
+Num *
+fnround(int, Num **a)
+{+ mpint *q, *r;
+ int i;
+
+ if(mpcmp(a[1], mpzero) <= 0){+ numdecref(a[0]);
+ numdecref(a[1]);
+ return error("invalid argument");+ }
+ q = mpnew(0);
+ r = mpnew(0);
+ a[0] = nummod(a[0]);
+ mpdiv(a[0], a[1], q, r);
+ if(r->sign < 0) mpadd(r, a[1], r);
+ mpleft(r, 1, r);
+ i = mpcmp(r, a[1]);
+ mpright(r, 1, r);
+ if(i > 0 || i == 0 && (a[0]->sign < 0) ^ (q->top != 0 && (q->p[0] & 1) != 0))
+ mpsub(r, a[1], r);
+ mpsub(a[0], r, a[0]);
+ mpfree(q);
+ mpfree(r);
+ numdecref(a[1]);
+ return a[0];
+}
+
+Num *
+fnfloor(int, Num **a)
+{+ mpint *r;
+
+ if(mpcmp(a[1], mpzero) <= 0){+ numdecref(a[0]);
+ numdecref(a[1]);
+ return error("invalid argument");+ }
+ r = mpnew(0);
+ a[0] = nummod(a[0]);
+ mpdiv(a[0], a[1], nil, r);
+ if(r->sign < 0) mpadd(r, a[1], r);
+ mpsub(a[0], r, a[0]);
+ mpfree(r);
+ numdecref(a[1]);
+ return a[0];
+}
+
+Num *
+fnceil(int, Num **a)
+{+ mpint *r;
+
+ if(mpcmp(a[1], mpzero) <= 0){+ numdecref(a[0]);
+ numdecref(a[1]);
+ return error("invalid argument");+ }
+ r = mpnew(0);
+ a[0] = nummod(a[0]);
+ mpdiv(a[0], a[1], nil, r);
+ if(r->sign < 0) mpadd(r, a[1], r);
+ if(mpcmp(r, mpzero) != 0){+ mpsub(a[0], r, a[0]);
+ mpadd(a[0], a[1], a[0]);
+ }
+ mpfree(r);
+ numdecref(a[1]);
+ return a[0];
+}
+
+Num *
+fntrunc(int, Num **a)
+{+ int i;
+
+ if(toint(a[1], &i, 1)){+ numdecref(a[0]);
+ numdecref(a[1]);
+ return nil;
+ }
+ a[0] = nummod(a[0]);
+ mptrunc(a[0], i, a[0]);
+ return a[0];
+}
+
+Num *
+fnxtend(int, Num **a)
+{+ int i;
+
+ if(toint(a[1], &i, 1)) return nil;
+ a[0] = nummod(a[0]);
+ mpxtend(a[0], i, a[0]);
+ return a[0];
+}
+
+Num *
+fnclog(int n, Num **a)
+{+ int r;
+
+ if(n != 1 && n != 2){+ numdecrefs(n, a);
+ return error("clog: wrong number of arguments");+ }
+ if(mpcmp(a[0], mpzero) <= 0 || n == 2 && mpcmp(a[1], mpone) <= 0){+ numdecref(a[0]);
+ return error("invalid argument");+ }
+ if(n == 1 || mpcmp(a[1], mptwo) == 0){+ a[0] = nummod(a[0]);
+ mpsub(a[0], mpone, a[0]);
+ itomp(mpsignif(a[0]), a[0]);
+ a[0]->b = 0;
+ if(n == 2) numdecref(a[1]);
+ return a[0];
+ }
+ a[0] = nummod(a[0]);
+ for(r = 0; mpcmp(a[0], mpone) > 0; r++){+ mpadd(a[0], a[1], a[0]);
+ mpsub(a[0], mpone, a[0]);
+ mpdiv(a[0], a[1], a[0], nil);
+ }
+ itomp(r, a[0]);
+ a[0]->b = 0;
+ numdecref(a[1]);
+ return a[0];
+}
+
+Num *
+fnubits(int, Num **a)
+{+ if(a[0]->sign < 0){+ numdecref(a[0]);
+ return error("invalid argument");+ }
+ a[0] = nummod(a[0]);
+ itomp(mpsignif(a[0]), a[0]);
+ a[0]->b = 0;
+ return a[0];
+}
+
+Num *
+fnsbits(int, Num **a)
+{+ a[0] = nummod(a[0]);
+ if(a[0]->sign < 0) mpadd(a[0], mpone, a[0]);
+ itomp(mpsignif(a[0]) + 1, a[0]);
+ a[0]->b = 0;
+ return a[0];
+}
+
+Num *
+fnnsa(int, Num **a)
+{+ int n, i;
+ mpdigit d;
+
+ a[0] = nummod(a[0]);
+ if(a[0]->sign < 0){+ numdecref(a[0]);
+ return error("invalid argument");+ }
+ n = 0;
+ for(i = 0; i < a[0]->top; i++){+ d = a[0]->p[i];
+ for(; d != 0; d &= d-1)
+ n++;
+ }
+ itomp(n, a[0]);
+ a[0]->b = 0;
+ return a[0];
+}
+
+Num *
+fngcd(int, Num **a)
+{+ a[0] = nummod(a[0]);
+ a[0]->b = basemax(a[0]->b, a[1]->b);
+ mpextendedgcd(a[0], a[1], a[0], nil, nil);
+ return a[0];
+}
+
+Num *
+fnrand(int, Num **a)
+{+ Num *n;
+
+ n = numalloc();
+ n->b = a[0]->b;
+ mpnrand(a[0], genrandom, n);
+ numdecref(a[0]);
+ return n;
+}
+
+Num *
+fnminv(int, Num **a)
+{+ mpint *x;
+
+ a[0] = nummod(a[0]);
+ x = mpnew(0);
+ mpextendedgcd(a[0], a[1], x, a[0], nil);
+ if(mpcmp(x, mpone) != 0)
+ error("no modular inverse");+ else
+ mpmod(a[0], a[1], a[0]);
+ mpfree(x);
+ numdecref(a[1]);
+ return a[0];
+}
+
+Num *
+fnrev(int, Num **a)
+{+ mpdigit v, m;
+ int i, j, n;
+
+ if(toint(a[1], &n, 1)){+ numdecref(a[0]);
+ numdecref(a[1]);
+ return nil;
+ }
+ a[0] = nummod(a[0]);
+ mptrunc(a[0], n, a[0]);
+ for(i = 0; i < a[0]->top; i++){+ v = a[0]->p[i];
+ m = -1;
+ for(j = sizeof(mpdigit) * 8; j >>= 1; ){+ m ^= m << j;
+ v = v >> j & m | v << j & ~m;
+ }
+ a[0]->p[i] = v;
+ }
+ for(i = 0; i < a[0]->top / 2; i++){+ v = a[0]->p[i];
+ a[0]->p[i] = a[0]->p[a[0]->top - 1 - i];
+ a[0]->p[a[0]->top - 1 - i] = v;
+ }
+ mpleft(a[0], n - a[0]->top * sizeof(mpdigit) * 8, a[0]);
+ numdecref(a[1]);
+ return a[0];
+}
+
+Num *
+fncat(int n, Num **a)
+{+ int i, w;
+ Num *r;
+
+ if(n % 2 != 0){+ error("cat: odd number of arguments");+ i = 0;
+ fail:
+ for(; i < n; i++)
+ numdecref(a[i]);
+ return nil;
+ }
+ r = numalloc();
+ for(i = 0; i < n; i += 2){+ if(toint(a[i+1], &w, 1)) goto fail;
+ mpleft(r, w, r);
+ if(a[i]->sign < 0 || mpsignif(a[i]) > w){+ a[i] = nummod(a[i]);
+ mptrunc(a[i], w, a[i]);
+ }
+ r->b = basemax(r->b, a[i]->b);
+ mpor(r, a[i], r);
+ numdecref(a[i]);
+ numdecref(a[i+1]);
+ }
+ return r;
+}
+
+void
+main(int, char **)
+{+ char buf[32];
+ Keyword *kw;
+
+ fmtinstall('B', mpfmt);+
+ for(kw = ops; kw->name[0] != 0; kw++)
+ if(optab[kw->name[0]] == nil)
+ optab[kw->name[0]] = kw;
+
+ regfunc("hex", fnhex, 1);+ regfunc("dec", fndec, 1);+ regfunc("oct", fnoct, 1);+ regfunc("bin", fnbin, 1);+ regfunc("pb", fnpb, 2);+ regfunc("abs", fnabs, 1);+ regfunc("round", fnround, 2);+ regfunc("floor", fnfloor, 2);+ regfunc("ceil", fnceil, 2);+ regfunc("trunc", fntrunc, 2);+ regfunc("xtend", fnxtend, 2);+ regfunc("clog", fnclog, -1);+ regfunc("ubits", fnubits, 1);+ regfunc("sbits", fnsbits, 1);+ regfunc("nsa", fnnsa, 1);+ regfunc("gcd", fngcd, 2);+ regfunc("minv", fnminv, 2);+ regfunc("rand", fnrand, 1);+ regfunc("rev", fnrev, 2);+ regfunc("cat", fncat, -1);+
+ prompt = fd2path(0, buf, sizeof buf) >= 0 && strstr(buf, "/dev/cons") != nil;
+ in = Bfdopen(0, OREAD);
+ if(in == nil)
+ sysfatal("Bfdopen: %r");+ extern void yyparse(void);
+ yyparse();
+ extern int yynerrs;
+ exits(yynerrs ? "error" : nil);
+}
--
⑨