%{
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include <mp.h>
#include <thread.h>
#undef long
#include <unistd.h>

int inbase = 10, outbase, divmode, sep, heads, fail, prompt, eof;
enum { MAXARGS = 16 };

typedef struct Num Num;
struct Num {
	mpint i;
	int b;
	Ref r;
};
enum { STRONG = 0x100 };

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->r.ref = 1;
	r->i.p = emalloc(sizeof(mpdigit));
	mpassign(mpzero, r);
	return r;
}

Num *
numincref(Num *n)
{
	incref(&n->r);
	return n;
}

Num *
numdecref(Num *n)
{
	if(n == nil) return nil;
	if(decref(&n->r) == 0){
		free(n->i.p);
		free(n);
		return nil;
	}
	return n;
}

Num *
nummod(Num *n)
{
	Num *m;

	if(n == nil) return nil;
	if(n->r.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;
}

typedef struct Symbol Symbol;
struct Symbol {
	enum {
		SYMNONE,
		SYMVAR,
		SYMFUNC,
	} t;
	Num *val;
	int nargs;
	Num *(*func)(int unused, 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);
	}
}

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;
	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->i.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' && b <= 16)
			*--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);
}

%}

%token LOEXP LOLSH LORSH LOEQ LONE LOLE LOGE LOLAND LOLOR

%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;

	extern Num *numbin(int op, Num *a, Num *b);
	extern void yyerror(char *msg);
	extern int yylex(void);
%}

%%

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 != 4 && 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 != 4 && outbase != 8 && outbase != 10 && outbase != 16 && outbase != 32 && outbase != 64){
			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;
	}
	| 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->i.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;

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->i.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->i.sign < 0){
			if(b->i.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->i.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->i.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;
}
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 unused, 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 unused, Num **a)
{
	Num *r;
	
	r = nummod(a[0]);
	r->b = STRONG | 16;
	return r;
}

Num *
fndec(int unused, Num **a)
{
	Num *r;
	
	r = nummod(a[0]);
	r->b = STRONG | 10;
	return r;
}

Num *
fnoct(int unused, Num **a)
{
	Num *r;
	
	r = nummod(a[0]);
	r->b = STRONG | 8;
	return r;
}

Num *
fnbin(int unused, Num **a)
{
	Num *r;
	
	r = nummod(a[0]);
	r->b = STRONG | 2;
	return r;
}

Num *
fnpb(int unused, 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 != 4 && b != 8 && b != 10 && b != 16 && b != 32 && b != 64){
		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 unused, Num **a)
{
	Num *r;
	
	r = nummod(a[0]);
	r->i.sign = 1;
	return r;
}

Num *
fnround(int unused, 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]->i.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 unused, 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 unused, 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 unused, 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 unused, 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 unused, Num **a)
{
	if(a[0]->i.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 unused, Num **a)
{
	a[0] = nummod(a[0]);
	if(a[0]->i.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 unused, Num **a)
{
	int n, i;
	mpdigit d;

	a[0] = nummod(a[0]);
	if(a[0]->i.sign < 0){
		numdecref(a[0]);
		return error("invalid argument");
	}
	n = 0;
	for(i = 0; i < a[0]->i.top; i++){
		d = a[0]->i.p[i];
		for(; d != 0; d &= d-1)
			n++;
	}
	itomp(n, a[0]);
	a[0]->b = 0;
	return a[0];
}

Num *
fngcd(int unused, 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 unused, Num **a)
{
	Num *n;

	n = numalloc();
	n->b = a[0]->b;
	mpnrand(a[0], genrandom, n);
	numdecref(a[0]);
	return n;
}

Num *
fnminv(int unused, 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 unused, 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]->i.top; i++){
		v = a[0]->i.p[i];
		m = -1;
		for(j = sizeof(mpdigit) * 8; j >>= 1; ){
			m ^= m << j;
			v = v >> j & m | v << j & ~m;
		}
		a[0]->i.p[i] = v;
	}
	for(i = 0; i < a[0]->i.top / 2; i++){
		v = a[0]->i.p[i];
		a[0]->i.p[i] = a[0]->i.p[a[0]->i.top - 1 - i];
		a[0]->i.p[a[0]->i.top - 1 - i] = v;
	}
	mpleft(a[0], n - a[0]->i.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]->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;
}

int
main(int argc, char **argv)
{
	Keyword *kw;
	
	fmtinstall('B', mpfmt);
	
	for(kw = ops; kw->name[0] != 0; kw++)
		if(optab[(int)kw->name[0]] == nil)
			optab[(int)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 = isatty(fileno(stdin));
	
	in = Bfdopen(0, OREAD);
	if(in == nil) sysfatal("Bfdopen: %r");
	yyparse();
	extern int yynerrs;
	return yynerrs ? 1 : 0;
}
