#ifndef lint
static char *rcsid =
   "$Header: divbig.c 1.1 86/05/20 $";
#endif

/*					-[Sat Jan 29 12:22:36 1983 by jkf]-
 * 	divbig.c				$Locker:  $
 * bignum division
 *
 * (c) copyright 1982, Regents of the University of California
 */


#include "global.h"

#define b 0x40000000
#define toint(p) ((int) (p))

divbig(dividend, divisor, quotient, remainder)
lispval dividend, divisor, *quotient, *remainder;
{
	register *ujp, *vip;
	int *alloca(), d, negflag = 0, m, n, carry, rem, qhat, j;
	int borrow, negrem = 0;
	long *utop = sp(), *ubot, *vbot, *qbot;
	register lispval work; lispval export();
	Keepxs();

	/* copy dividend */
	for(work = dividend; work; work = work ->s.CDR)
		stack(work->s.I);
	ubot = sp();
	if(*ubot < 0) {		/* knuth's division alg works only for pos
					bignums				*/
		negflag ^= 1;
		negrem = 1;
		dsmult(utop-1,ubot,-1);
	}
	stack(0);
	ubot = sp();

	
	/*copy divisor */
	for(work = divisor; work; work = work->s.CDR)
		stack(work->s.I);

	vbot = sp();
	stack(0);
	if(*vbot < 0) {
		negflag ^= 1;
		dsmult(ubot-1,vbot,-1);
	}

	/* check validity of data */
	n = ubot - vbot;
	m = utop - ubot - n - 1;
	if (n == 1) {
		/* do destructive division by  a single. */
		rem = dsdiv(utop-1,ubot,*vbot);
		if(negrem)
			rem = -rem;
		if(negflag)
			dsmult(utop-1,ubot,-1);
		if(remainder)
			*remainder = inewint(rem);
		if(quotient)
			*quotient = export(utop,ubot);
		Freexs();
		return;
	}
	if (m < 0) {
		if (remainder)
			*remainder = dividend;
		if(quotient)
			*quotient = inewint(0);
		Freexs();
		return;
	}
	qbot = alloca(toint(utop) + toint(vbot) - 2 * toint(ubot));
d1:
	d = b /(*vbot +1);
	dsmult(utop-1,ubot,d);
	dsmult(ubot-1,vbot,d);

d2:	for(j=0,ujp=ubot; j <= m; j++,ujp++) {

	d3:	
		qhat = calqhat(ujp,vbot);
	d4:
		if((borrow = mlsb(ujp + n, ujp, ubot, -qhat)) < 0) {
			adback(ujp + n, ujp, ubot);
			qhat--;
		}
		qbot[j] = qhat;
	}
d8:	if(remainder) {
		dsdiv(utop-1, utop - n, d);
		if(negrem) dsmult(utop-1,utop-n,-1);
		*remainder = export(utop,utop-n);
	}
	if(quotient) {
		if(negflag)
			dsmult(qbot+m,qbot,-1);
		*quotient = export(qbot + m + 1, qbot);
	}
	Freexs();
}
/*
 * asm code commented out due to optimizer bug
 * also, this file is now shared with the 68k version!
calqhat(ujp,v1p)
register int *ujp, *v1p;
{
asm("	cmpl	(r10),(r11)		# v[1] == u[j] ??");
asm("	beql	2f			");
asm("	# calculate qhat and rhat simultaneously,");
asm("	#  qhat in r0");
asm("	#  rhat in r1");
asm("	emul	(r11),$0x40000000,4(r11),r4 # u[j]b+u[j+1] into r4,r5");
asm("	ediv	(r10),r4,r0,r1		# qhat = ((u[j]b+u[j+1])/v[1]) into r0");
asm("					# (u[j]b+u[j+1] -qhat*v[1]) into r1");
asm("					# called rhat");
asm("1:");
asm("	# check if v[2]*qhat > rhat*b+u[j+2]");
asm("	emul	r0,4(r10),$0,r2		# qhat*v[2] into r3,r2");
asm("	emul	r1,$0x40000000,8(r11),r8 #rhat*b + u[j+2] into r9,r8");
asm("	# give up if r3,r2 <= r9,r8, otherwise iterate");
asm("	subl2	r8,r2			# perform r3,r2 - r9,r8");
asm("	sbwc	r9,r3");
asm("	bleq	3f			# give up if negative or equal");
asm("	decl	r0			# otherwise, qhat = qhat - 1");
asm("	addl2	(r10),r1		# since dec'ed qhat, inc rhat by v[1]");
asm("	jbr	1b");
asm("2:	");
asm("	# get here if v[1]==u[j]");
asm("	# set qhat to b-1");
asm("	# rhat is easily calculated since if we substitute b-1 for qhat in");
asm("	# the formula, then it simplifies to (u[j+1] + v[1])");
asm("	# ");
asm("	addl3	4(r11),(r10),r1		# rhat = u[j+1] + v[1]");
asm("	movl	$0x3fffffff,r0		# qhat = b-1");
asm("	jbr	1b");
asm("3:");
}
mlsb(utop,ubot,vtop,nqhat)
register int *utop, *ubot, *vtop;
register int nqhat;
{
asm("	clrl	r0");
asm("loop2:	addl2	(r11),r0");
asm("	emul	r8,-(r9),r0,r2");
asm("	extzv	$0,$30,r2,(r11)");
asm("	extv	$30,$32,r2,r0");
asm("	acbl	r10,$-4,r11,loop2");
}
adback(utop,ubot,vtop)
register int *utop, *ubot, *vtop;
{
asm("	clrl	r0");
asm("loop3:	addl2	-(r9),r0");
asm("	addl2	(r11),r0");
asm("	extzv	$0,$30,r0,(r11)");
asm("	extv	$30,$2,r0,r0");
asm("	acbl	r10,$-4,r11,loop3");
}
dsdiv(top,bot,div)
register int* bot;
{
asm("	clrl	r0");
asm("loop4:	emul	r0,$0x40000000,(r11),r1");
asm("	ediv	12(ap),r1,(r11),r0");
asm("	acbl	4(ap),$4,r11,loop4");
}
dsmult(top,bot,mult)
register int* top;
{
asm("	clrl	r0");
asm("loop5:	emul	12(ap),(r11),r0,r1");
asm("	extzv	$0,$30,r1,(r11)");
asm("	extv	$30,$32,r1,r0");
asm("	acbl	8(ap),$-4,r11,loop5");
asm("	movl	r1,4(r11)");
}
*/
lispval
export(top,bot)
register long *top, *bot;
{
	register lispval p;
	lispval result;

	top--; /* screwey convention matches original
		  vax assembler convenience */
	while(bot < top)
	{
		if(*bot==0)
			bot++;
		else if(*bot==-1)
			*++bot |= 0xc0000000;
		else break;
	}
	if(bot==top) return(inewint(*bot));
	result = p = newsdot();
	protect(p);
	p->s.I = *top--;
	while(top >= bot) {
		p = p->s.CDR = newdot();
		p->s.I = *top--;
	}
	p->s.CDR = 0;
	np--;
	return(result);
}

#define MAXINT 0x80000000L

Ihau(fix)
register int fix;
{
	register count;
	if(fix==MAXINT)
		return(32);
	if(fix < 0)
		fix = -fix;
	for(count = 0; fix; count++)
		fix /= 2;
	return(count);
}
lispval
Lhau()
{
	register count;
	register lispval handy;
	register dum1,dum2;
	lispval Labsval();

	handy = lbot->val;
top:
	switch(TYPE(handy)) {
	case INT:
		count = Ihau(handy->i);
		break;
	case SDOT:
		handy = Labsval();
		for(count = 0; handy->s.CDR!=((lispval) 0); handy = handy->s.CDR)
			count += 30;
		count += Ihau(handy->s.I);
		break;
	default:
		handy = errorh1(Vermisc,"Haulong: bad argument",nil,
			       TRUE,997,handy);
		goto top;
	}
	return(inewint(count));
}
lispval
Lhaipar()
{
	register lispval work;
	register n;
	register int *top = sp() - 1;
	register int *bot;
	int mylen;

	/*chkarg(2);*/
	work = lbot->val;
					/* copy data onto stack */
on1:
	switch(TYPE(work)) {
	case INT:
		stack(work->i);
		break;
	case SDOT:
		for(; work!=((lispval) 0); work = work->s.CDR)
			stack(work->s.I);
		break;
	default:
		work = errorh1(Vermisc,"Haipart: bad first argument",nil,
				TRUE,996,work);
		goto on1;
	}
	bot = sp();
	if(*bot < 0) {
		stack(0);
		dsmult(top,bot,-1);
		bot--;
	}
	for(; *bot==0 && bot < top; bot++);
				/* recalculate haulong internally */
	mylen = (top - bot) * 30 + Ihau(*bot);
				/* get second argument		  */
	work = lbot[1].val;
	while(TYPE(work)!=INT)
		work = errorh1(Vermisc,"Haipart: 2nd arg not int",nil,
				TRUE,995,work);
	n = work->i;
	if(n >= mylen || -n >= mylen)
		goto done;
	if(n==0) return(inewint(0));
	if(n > 0) {
				/* Here we want n most significant bits
				   so chop off mylen - n bits */
		stack(0);
		n = mylen - n;
		for(n; n >= 30; n -= 30)
			top--;
		if(top < bot)
			error("Internal error in haipart #1",FALSE);
		dsdiv(top,bot,1<<n);

	} else {
				/* here we want abs(n) low order bits */
		stack(0);
		bot = top + 1;
		for(; n <= 0; n += 30)
			bot--;
		n = 30 - n;
		*bot &= ~ (-1<<n);
	}
done:
	return(export(top + 1,bot));
}
#define STICKY 1
#define TOEVEN 2
lispval
Ibiglsh(bignum,count,mode)
lispval bignum, count;
{
	register lispval work;
	register n;
	register int *top = sp() - 1;
	register int *bot;
	int mylen, guard = 0, sticky = 0, round = 0;
	lispval export();

				/* get second argument		  */
	work = count;
	while(TYPE(work)!=INT)
		work = errorh1(Vermisc,"Bignum-shift: 2nd arg not int",nil,
				TRUE,995,work);
	n = work->i;
	if(n==0) return(bignum);
	for(; n >= 30; n -= 30) {/* Here we want to multiply by 2^n
				   so start by copying n/30 zeroes
				   onto stack */
		stack(0);
	}

	work = bignum;		/* copy data onto stack */
on1:
	switch(TYPE(work)) {
	case INT:
		stack(work->i);
		break;
	case SDOT:
		for(; work!=((lispval) 0); work = work->s.CDR)
			stack(work->s.I);
		break;
	default:
		work = errorh1(Vermisc,"Bignum-shift: bad bignum argument",nil,
				TRUE,996,work);
		goto on1;
	}
	bot = sp();
	if(n >= 0) {
		stack(0);
		bot--;
		dsmult(top,bot,1<<n);
	} else {
			/* Trimming will only work without leading
			   zeroes without my having to think
			   a lot harder about it, if the inputs
			   are canonical */
		for(n = -n; n > 30; n -= 30) {
			if(guard) sticky |= 1;
			guard = round;
			if(top > bot) {
				round = *top;
				top --;
			} else  {
				round = *top;
				*top >>= 30;
			}
		}
		if(n > 0) {
			if(guard) sticky |= 1;
			guard = round;
			round = dsrsh(top,bot,-n,-1<<n);
		}
		stack(0); /*so that dsadd1 will work;*/
		if (mode==STICKY) {
			if(((*top&1)==0) && (round | guard | sticky))
				dsadd1(top,bot);
		} else if (mode==TOEVEN) {
			int mask;

			if(n==0) n = 30;
			mask = (1<<(n-1));
			if(! (round & mask) ) goto chop;
			mask -= 1;
			if(  ((round&mask)==0)
			  && guard==0
			  && sticky==0
			  && (*top&1)==0 ) goto chop;
			dsadd1(top,bot);
		}
		chop:;
	}
	work = export(top + 1,bot);
	return(work);
}

/*From drb  Mon Jul 27 01:25:56 1981
To: sklower

The idea is that the answer/2
is equal to the exact answer/2 rounded towards - infinity.  The final bit
of the answer is the "or" of the true final bit, together with all true
bits after the binary point.  In other words, the 1's bit of the answer
is almost always 1.  THE FINAL BIT OF THE ANSWER IS 0 IFF n*2^i = THE
ANSWER RETURNED EXACTLY, WITH A 0 FINAL BIT.


To try again, more succintly:  the answer is correct to within 1, and
the 1's bit of the answer will be 0 only if the answer is exactly
correct. */

lispval
Lsbiglsh()
{
	register struct argent *mylbot = lbot;
	chkarg(2,"sticky-bignum-leftshift");
	return(Ibiglsh(lbot->val,lbot[1].val,STICKY));
}
lispval
Lbiglsh()
{
	register struct argent *mylbot = lbot;
	chkarg(2,"bignum-leftshift");
	return(Ibiglsh(lbot->val,lbot[1].val,TOEVEN));
}
lispval
HackHex() /* this is a one minute function so drb and kls can debug biglsh */
/* (HackHex i) returns a string which is the result of printing i in hex */
{
	register struct argent *mylbot = lbot;
	char buf[32];
	sprintf(buf,"%lx",lbot->val->i);
	return((lispval)inewstr(buf));
}
