/* Integer Version 2.0, RD, 20.1.93	iadd.c	*/

#include "iint.h"
#include "idigit.h"
#include "imem.h"

void 
IplasI(a, b)
    register pInteger a;
    register const Integer *b;
/* a+=b; */
{
    if (a->sign == b->sign)
    {
	register int neededlength = a->length;
	if (neededlength < b->length)
	{
	    neededlength = b->length;
	}
	neededlength++;
	if (neededlength > a->maxlength)
	{
	    register DigitType *newv;
	    register int oldlength = a->maxlength;
	    a->maxlength = neededlength;
	    newv = newDigitVec(&a->maxlength);
	    a->length =
		DigitVecCadd(newv, a->vec, b->vec, a->length, b->length);
	    delDigitVec(a->vec, oldlength);
	    a->vec = newv;
	    return;
	}
	else
	{
	    a->length =
		DigitVecCadd(a->vec, a->vec, b->vec, a->length, b->length);
	    return;
	}
    }
    else
    {
	/*
	   Vorzeichen verschieden, subtrahiere betragsmaessig kleineres von
	   groesserem
	*/
	if ((b->length > a->length) || ((b->length == a->length)
				  && DigitVecGt(b->vec, a->vec, a->length)))
	{
	    /* |b|>|a| */
	    register int neededlength = b->length;
	    if (neededlength <= a->maxlength)
	    {
		a->length = DigitVecCsub(a->vec, b->vec, a->vec,
					 b->length, a->length);
		a->sign = b->sign;
		return;
	    }
	    else
	    {
		register DigitType *newv;
		register int oldlength = a->maxlength;
		a->maxlength = neededlength;
		newv = newDigitVec(&a->maxlength);
		a->length = DigitVecCsub(newv, b->vec, a->vec,
					 b->length, a->length);
		delDigitVec(a->vec, oldlength);
		a->vec = newv;
		a->sign = b->sign;
		return;
	    }
	}
	else
	{
	    /* |b| <= |a| */
	    register int l = a->length;
	    register DigitType *lp = &(a->vec[l - 1]);
	    DigitVecCsubto(a->vec, b->vec, b->length);
	    while ((l > 0) && (!*lp))
	    {
		l--;
		lp--;
	    }
	    a->length = l;
	    if (!l)
		a->sign = PLUS;
	    return;
	}
    }
}				/* IplasI */

void 
IasIplI(sum, a, b)
    register pInteger sum;
    register const Integer *a, *b;
/* sum=a+b; */
{
    register int neededlength;
    if (sum == a)
    {
	IplasI(sum, b);
	return;
    }
    if (sum == b)
    {
	IplasI(sum, a);
	return;
    }
    if (a->sign == b->sign)
    {				/* Addition */
	neededlength = a->length;
	if (neededlength < b->length)
	    neededlength = b->length;
	neededlength++;
	if (neededlength > sum->maxlength)
	{
	    delDigitVec(sum->vec, sum->maxlength);
	    sum->maxlength = neededlength;
	    sum->vec = newDigitVec(&sum->maxlength);
	}
	sum->length =
	    DigitVecCadd(sum->vec, a->vec, b->vec, a->length, b->length);
	sum->sign = a->sign;
	return;
    }
    else
    {				/* Subtraktion */
	neededlength = a->length;
	if (neededlength < b->length)
	    neededlength = b->length;
	if (neededlength > sum->maxlength)
	{
	    delDigitVec(sum->vec, sum->maxlength);
	    sum->maxlength = neededlength;
	    sum->vec = newDigitVec(&sum->maxlength);
	}
	if ((b->length > a->length) || ((b->length == a->length)
	    && DigitVecGt(b->vec, a->vec, a->length)))
	{
	    /* |b| > |a| */
	    sum->length = DigitVecCsub(sum->vec, b->vec, a->vec,
				       b->length, a->length);
	    sum->sign = b->sign;
	    return;
	}
	else
	{
	    /* |b| <= |a| */
	    sum->length = DigitVecCsub(sum->vec, a->vec, b->vec,
				       a->length, b->length);
	    if (!sum->length)
		sum->sign = PLUS;
	    else
		sum->sign = a->sign;
	    return;
	}
    }
}				/* IasIplI */

void 
ImiasI(a, b)
    register pInteger a;
    register const Integer *b;
/* a-=b; */
{
    if (a->sign != b->sign)
    {
	register int neededlength = a->length;
	if (neededlength < b->length)
	{
	    neededlength = b->length;
	}
	neededlength++;
	if (neededlength > a->maxlength)
	{
	    register DigitType *newv;
	    register int oldlength = a->maxlength;
	    a->maxlength = neededlength;
	    newv = newDigitVec(&a->maxlength);
	    a->length = DigitVecCadd(newv, a->vec, b->vec, a->length, b->length);
	    delDigitVec(a->vec, oldlength);
	    a->vec = newv;
	    return;
	}
	else
	{
	    a->length = DigitVecCadd(a->vec, a->vec, b->vec, a->length, b->length);
	    return;
	}
    }
    else
    {
	/*
	   Vorzeichen gleich, subtrahiere betragsmaessig kleineres von
	   groesserem
	*/
	if ((b->length > a->length) || ((b->length == a->length)
				  && DigitVecGt(b->vec, a->vec, a->length)))
	{
	    /* |b|>|a| */
	    register int neededlength = b->length;
	    if (neededlength <= a->maxlength)
	    {
		a->length = DigitVecCsub(a->vec, b->vec, a->vec,
					 b->length, a->length);
		a->sign ^= MINUS;
		return;
	    }
	    else
	    {
		register DigitType *newv;
		register int oldlength = a->maxlength;
		a->maxlength = neededlength;
		newv = newDigitVec(&a->maxlength);
		a->length = DigitVecCsub(newv, b->vec, a->vec,
					 b->length, a->length);
		delDigitVec(a->vec, oldlength);
		a->vec = newv;
		a->sign ^= MINUS;
		return;
	    }
	}
	else
	{
	    /* |b| <= |a| */
	    register int l = a->length;
	    register DigitType *lp = &(a->vec[l - 1]);
	    DigitVecCsubto(a->vec, b->vec, b->length);
	    while ((l > 0) && (!*lp))
	    {
		l--;
		lp--;
	    }
	    a->length = l;
	    if (!l)
		a->sign = PLUS;
	    return;
	}
    }
}				/* ImiasI */

void 
IasImiI(diff, a, b)
    register pInteger diff;
    register const Integer *a, *b;
/* diff=a-b; */
{
    register int neededlength;
    if (diff == a)
    {
	ImiasI(diff, b);
	return;
    }
    if (diff == b)
    {
	ImiasI(diff, a);
	if (diff->length)
	    diff->sign ^= MINUS;
	return;
    }
    if (a->sign != b->sign)
    {				/* Addition */
	neededlength = a->length;
	if (neededlength < b->length)
	    neededlength = b->length;
	neededlength++;
	if (neededlength > diff->maxlength)
	{
	    delDigitVec(diff->vec, diff->maxlength);
	    diff->maxlength = neededlength;
	    diff->vec = newDigitVec(&diff->maxlength);
	}
	diff->length =
	    DigitVecCadd(diff->vec, a->vec, b->vec, a->length, b->length);
	diff->sign = a->sign;
	return;
    }
    else
    {				/* Subtraktion */
	neededlength = a->length;
	if (neededlength < b->length)
	    neededlength = b->length;
	if (neededlength > diff->maxlength)
	{
	    delDigitVec(diff->vec, diff->maxlength);
	    diff->maxlength = neededlength;
	    diff->vec = newDigitVec(&diff->maxlength);
	}
	if ((b->length > a->length) || ((b->length == a->length)
	    && DigitVecGt(b->vec, a->vec, a->length)))
	{
	    /* |b| > |a| */
	    diff->length = DigitVecCsub(diff->vec, b->vec, a->vec,
					b->length, a->length);
	    diff->sign = a->sign ^ MINUS;
	    return;
	}
	else
	{
	    /* |b| <= |a| */
	    diff->length = DigitVecCsub(diff->vec, a->vec, b->vec,
					a->length, b->length);
	    if (!diff->length)
		diff->sign = PLUS;
	    else
		diff->sign = a->sign;
	    return;
	}
    }
}				/* IasImiI */

void 
Iinc(a)
    register Integer *a;
/* a++; */
{
    if (a->sign == PLUS)
    {
	register int neededlength = a->length + 1;
	DigitType b = 1;
	if (neededlength > a->maxlength)
	{
	    register DigitType *newv;
	    register int oldlength = a->maxlength;
	    a->maxlength = neededlength;
	    newv = newDigitVec(&a->maxlength);
	    a->length = DigitVecCadd(newv, a->vec, &b, a->length, 1);
	    delDigitVec(a->vec, oldlength);
	    a->vec = newv;
	    return;
	}
	else
	{
	    a->length = DigitVecCadd(a->vec, a->vec, &b, a->length, 1);
	    return;
	}
    }
    else
    {
	/* Subtrahiere 1 von |a| */
	register int l = a->length;
	register DigitType *lp = &(a->vec[l - 1]);
	DigitType b = 1;
	DigitVecCsubto(a->vec, &b, 1);
	while ((l > 0) && (!*lp))
	{
	    l--;
	    lp--;
	}
	a->length = l;
	if (!l)
	    a->sign = PLUS;
	return;
    }
}				/* Iinc */

void 
Idec(a)
    register Integer *a;
/* a--; */
{
    if (a->sign == MINUS)
    {
	register int neededlength = a->length + 1;
	DigitType b = 1;
	if (neededlength > a->maxlength)
	{
	    register DigitType *newv;
	    register int oldlength = a->maxlength;
	    a->maxlength = neededlength;
	    newv = newDigitVec(&a->maxlength);
	    a->length = DigitVecCadd(newv, a->vec, &b, a->length, 1);
	    delDigitVec(a->vec, oldlength);
	    a->vec = newv;
	    return;
	}
	else
	{
	    a->length = DigitVecCadd(a->vec, a->vec, &b, a->length, 1);
	    return;
	}
    }
    if (!a->length)
    {
	a->sign = MINUS;
	a->length = 1;
	a->vec[0] = 1;
	return;
    }
    else
    {
	/* Subtrahiere 1 von |a| */
	register int l = a->length;
	register DigitType *lp = &(a->vec[l - 1]);
	DigitType b = 1;
	DigitVecCsubto(a->vec, &b, 1);
	while ((l > 0) && (!*lp))
	{
	    l--;
	    lp--;
	}
	a->length = l;
	return;
    }
}				/* Idec */


BOOLEAN 
IeqI(a, b)
/* return a==b; */
    register const Integer *a, *b;
{
    if ((a->sign == b->sign) && (a->length == b->length) &&
	DigitVecEq(a->vec, b->vec, a->length))
	return TRUE;
    else
	return FALSE;
}

BOOLEAN 
IgtI(a, b)
/* return a>b; */
    register const Integer *a, *b;
{
    if (a->sign == PLUS)
    {
	if (b->sign == MINUS)
	    return TRUE;
	else
	{
	    if ((a->length > b->length) || ((a->length == b->length)
				  && DigitVecGt(a->vec, b->vec, a->length)))
		return TRUE;
	    else
		return FALSE;
	}
    }
    else
    {
	if (b->sign == PLUS)
	    return FALSE;
	else
	{
	    if ((a->length > b->length) || ((a->length == b->length)
				 && !DigitVecGt(b->vec, a->vec, a->length)))
		return FALSE;
	    else
		return TRUE;
	}
    }
}				/* IgtI */

void 
Ineg(a)
    register pInteger a;
{
    if (a->length)
	a->sign ^= MINUS;
}

BOOLEAN 
IneI(a, b)
/* return a!=b; */
    const Integer *a, *b;
{
    return !IeqI(a, b);
}

BOOLEAN 
IgeI(a, b)
/* return a>=b; */
    const Integer *a, *b;
{
    return !IgtI(b, a);
}

BOOLEAN 
IltI(a, b)
/* return a<b; */
    const Integer *a, *b;
{
    return IgtI(b, a);
}

BOOLEAN 
IleI(a, b)
/* return a<=b; */
    const Integer *a, *b;
{
    return !IgtI(a, b);
}

BOOLEAN 
Ige0(a)
    const Integer *a;
{
    return (a->sign == PLUS);
}

BOOLEAN 
Igt0(a)
    const Integer *a;
{
    return ((a->sign == PLUS) && (a->length));
}

BOOLEAN 
Ile0(a)
    const Integer *a;
{
    return (!a->length || (a->sign == MINUS));
}

BOOLEAN 
Ilt0(a)
    const Integer *a;
{
    return (a->sign == MINUS);
}

BOOLEAN 
Ieq0(a)
    const Integer *a;
{
    return (!a->length);
}

BOOLEAN 
Ieq1(a)
    const Integer *a;
{
    if ((*(a->vec) == 1) && (a->length == 1) && (a->sign == PLUS))
	return TRUE;
    else
	return FALSE;
}
