

/*
#include <stdio.h>
template<class T>
inline void Thunk(T& a)
{
  int i;
  for (i=0;i<a.NrItems();i++)
    printf("%d ",a[i]);
  printf("\n");  
}
*/

/* BaseTimesInt : multiply a with one digit in the range 0..(aBase-1)
 */
template<class T>
inline void BaseTimesInt(T& a,PlatDoubleWord aNumber, PlatDoubleWord aBase)
{
//    if (a[a.NrItems()-1] != 0)
//      a.Append(0);

    PlatDoubleWord carry=0;
    LispInt i;
    LispInt nr=a.NrItems();

    typename T::ElementTypePtr aptr = &a[0];
    for (i=0;i<nr;i++)
    {
        PlatDoubleWord word = ((PlatDoubleWord)(*aptr))*aNumber+carry;
        PlatWord digit = (PlatWord)(word % aBase);
        PlatWord newCarry= (PlatWord)(word / aBase);
        *aptr = digit;
        aptr++;
        carry= newCarry;
    }
    if (carry)
    {
      a.Append((typename T::ElementType)carry);
      carry = 0;
    }
    LISPASSERT(carry == 0);
}

template<class T>
inline void BaseDivideInt(T& a,PlatDoubleWord aNumber, PlatDoubleWord aBase, PlatDoubleWord& aCarry)
{
//    if (a[a.NrItems()-1] != 0)

    PlatDoubleWord carry=0;
    LispInt i;
    LispInt nr=a.NrItems();

    typename T::ElementTypePtr aptr = &a[0];
    for (i=nr-1;i>=0;i--)
    {
        PlatDoubleWord word = ((carry*aBase)+((PlatDoubleWord)(aptr[i])));
        PlatWord digit = (PlatWord)(word / aNumber);
        PlatWord newCarry= (PlatWord)(word % aNumber);
        aptr[i] = digit;
        carry= newCarry;
    }
    //carry now is the remainder
    aCarry = carry;
}


/* GrowDigits : add digits to a until it has aDigits digits
 */
template<class T>
inline void GrowDigits(T& a,LispInt aDigits)
{
    LispInt i;

    if (aDigits <= a.NrItems())
        return;
    
    /*
     LispInt nrToAdd = aDigits-a.NrItems();

    for (i=0;i<nrToAdd;i++)
        a.Append(0);
*/
    LispInt origSize = a.NrItems();
    a.GrowTo(aDigits);
    a.SetNrItems(aDigits);
    if (aDigits<=origSize)
        return;
    typename T::ElementType* ptr = &a[origSize];
    for (i=origSize;i<aDigits;i++)
        *ptr++ = 0;
}

/* BaseAdd : destructively add aSource to aTarget, in base aBase.
 */
template<class T>
inline void BaseAdd(T& aTarget, const T& aSource, LispInt aBase)
{
    // Initialize result

    GrowDigits(aTarget,aSource.NrItems());
    aTarget.Append(0);
    
    LispInt nr1 = aTarget.NrItems();
    LispInt nr2 = aSource.NrItems();
    LispInt nr;

    // nr represents min(nr1,nr2), the number of digits to add
    if (nr1>nr2)
        nr=nr2;
    else
        nr=nr1;
    
    PlatDoubleWord carry=0;
    LispInt digit;

   typename T::ElementTypePtr sourcePtr = &aSource[0];
   typename T::ElementTypePtr targetPtr = &aTarget[0];
   for (digit=0;digit<nr;digit++)
    {
        PlatDoubleWord word;
        word = (PlatDoubleWord)targetPtr[digit] +
            (PlatDoubleWord)sourcePtr[digit] + carry;
         PlatDoubleWord newDigit = (word%aBase);
         PlatDoubleWord newCarry = (word/aBase);
         targetPtr[digit] = (typename T::ElementType)newDigit;
         carry          = newCarry;
    }
    while (carry != 0)
    {
        PlatSignedDoubleWord ww = targetPtr[nr];
        ww+=carry;
        targetPtr[nr] = ww%aBase;
        carry = ww/aBase;
        nr++;
    }
}

template<class T>
inline void BaseSubtract(T& aResult, T& a2, LispInt offset)
{
    if (IsZero(a2))
        return;
    LISPASSERT(!IsZero(a2));
    // Initialize result
    LispInt nr = a2.NrItems();

    typename T::ElementTypePtr resultPtr = &aResult[0];
    typename T::ElementTypePtr a2ptr = &a2[0];

    while (a2ptr[nr-1] == 0)
        nr--;

    // Subtract on a per-digit basis
    PlatSignedDoubleWord carry=0;
    LispInt digit;

    for (digit=0;digit<nr;digit++)
    {
        PlatSignedDoubleWord word;
        word = ((PlatSignedDoubleWord)resultPtr[digit+offset]) -
            ((PlatSignedDoubleWord)a2ptr[digit]) +
            (PlatSignedDoubleWord)carry;
        carry=0;
        while (word<0)
        {
            word+=WordBase;
            carry--;
        }
        resultPtr[digit+offset] = ((PlatWord)(word%WordBase));
    }

    while (carry != 0)
    {
        LISPASSERT(nr+offset<aResult.NrItems());

        LispInt newCarry = 0;
        PlatSignedDoubleWord ww = resultPtr[nr+offset]+carry;
        while (ww<0)
        {
            ww = ww + WordBase;
            newCarry = newCarry - 1;
        }
        resultPtr[nr+offset]=(typename T::ElementType)ww;
        carry = newCarry;
        offset++;
    }
}

/* BaseIntNumber : convert a number into a different base,
 * using growing arrays.
 */
template<class T>
inline void BaseIntNumber(T& aTarget, LispInt aNumber, PlatWord aBase)
{
    aTarget.SetNrItems(0);
    while (aNumber != 0)
    {
        LispInt digit = aNumber%aBase;
        aTarget.Append(digit);
        aNumber/=aBase;
    }
    if (aTarget.NrItems() == 0)
        aTarget.Append(0);
}

// BaseAddMultiply : multiply x and y, and add result to aTarget
//
template<class T>
inline void BaseAddMultiply(T& aTarget, T& x, T& y, LispInt aBase)
{
    LispInt nrx=x.NrItems();
    LispInt nry=y.NrItems();
    GrowDigits(aTarget,nrx+nry+1);
    LispInt ix,iy;

    typename T::ElementType *targetPtr = &aTarget[0];
    typename T::ElementType *xPtr = &x[0];
    typename T::ElementType *yPtr = &y[0];
    for (ix=0;ix<nrx;ix++)
    {
        PlatDoubleWord carry = 0;
        for (iy=0;iy<nry;iy++)
        {
            PlatDoubleWord word =
                static_cast<PlatDoubleWord>(targetPtr[ix+iy])+
                static_cast<PlatDoubleWord>(xPtr[ix])*
                static_cast<PlatDoubleWord>(yPtr[iy])+carry;
            // This calculates aTarget[ix+iy]+x[ix]*y[iy]+carry;


            targetPtr[ix+iy] = (typename T::ElementType)(word % aBase);
            carry          = word / aBase;
        }
        targetPtr[ix+nry] += (typename T::ElementType)(carry);
    }
}


#ifdef USE_KARATSUBA

const int cKaratsubaCutoff = 8;

/*
#include <stdio.h>

inline void BaseKaratsubaMultiply(ANumber& aTarget, ANumber& x, ANumber& y, LispInt aSize, LispInt aBase)
{
    LispInt halfSize = aSize/2;

    ANumber F0;
    ANumber F1;
    ANumber G0;
    ANumber G1;

    GrowDigits(F0,halfSize);
    GrowDigits(F1,halfSize);
    GrowDigits(G0,halfSize);
    GrowDigits(G1,halfSize);
    LispInt i;
    for (i=0;i<halfSize;i++)
    {
        F0[i] = x[i];
        F1[i] = x[halfSize+i];
        G0[i] = y[i];
        G1[i] = y[halfSize+i];
    }
    
    ANumber F0G0;
    ANumber F1G1;
    ANumber Fsum,Gsum;
    ANumber combined;

    Multiply(F0G0, F0, G0);
    Multiply(F1G1, F1, G1);
    Add(Fsum,F0,F1);
    Add(Gsum,G0,G1);
    Multiply(combined, Fsum, Gsum);
    ANumber s1;
    Subtract(s1,combined,F0G0);
    ANumber s2;
    Subtract(s2,s1,F1G1);

    BaseShiftLeft(F1G1, aSize   *(8*sizeof(PlatWord)));
    BaseShiftLeft(s2  , halfSize*(8*sizeof(PlatWord)));

    ANumber s3;
    Add(s3,F1G1,s2);
    Add(aTarget,s3,F0G0);
}

inline void BaseAddMultiplyK(ANumber& aTarget, ANumber& x, ANumber& y, LispInt aBase)
{
    LispInt nrx=x.NrItems();
    LispInt nry=y.NrItems();
    LispInt i, i2;

    // Too small?  Use O(n^2) multiplication
    if (nrx+nry+1 <= cKaratsubaCutoff)
    {
        BaseAddMultiply(aTarget, x, y, aBase);
        return;
    }

    LispInt max = (nrx > nry) ? nrx : nry;
    LispInt maxtwos;
    for (maxtwos = 1; maxtwos < max; maxtwos *= 2);

    GrowDigits(aTarget,maxtwos);
    GrowDigits(x,maxtwos);
    GrowDigits(y,maxtwos);
    BaseKaratsubaMultiply(aTarget, x, y, maxtwos, aBase);
}
*/


/* BaseAddMultiplyK : multiply x and y, and add result to aTarget
 *					  using Karatsuba multiplication.  This function
 *					  just makes the numbers passed into even ones,
 *					  by adding zeros to the front, then multiplies.
 *
 *					  Needs enough memory to hold 4*(smallest power
 *					  of 2 bigger than operands) digits.
 *
 *					  Code based on
 *
 *			Here's a crude diagram of how the numbers are stored:
 *			(scratch space is where the x & y sums are stored)
 *
 *				 ___________ ___________ _______________________
 *				|           |           |  x & y scratch space  |
 *				|     x     |     y     |  starts on <-- side   |
 *				|___________|___________|____ and works --> ____| 
 *
 */


template<class T>
inline void BaseAddMultiplyK(T& aTarget, T& x, T& y, LispInt aBase)
{
	LispInt nrx=x.NrItems();
	LispInt nry=y.NrItems();
	LispInt i, i2;
	typename T::ElementType *iArray, *iXArray, *iYArray, *iSumArray;

	// Too small?  Use O(n^2) multiplication
	if (nrx+nry+1 <= cKaratsubaCutoff) {
		BaseAddMultiply(aTarget, x, y, aBase);
		return;
	}

    i = (nrx > nry) ? nrx : nry;
    for (i2 = 1; i2 < i; i2 *= 2);

	// Allocate a array of the smallest power of 2 larger than both
	// numbers * 4 elements, clear it to zero, then copy x into the 
	// into the start, y above it, and use the high half for recursion.
	// ToDo: Is this portable ?!?
	iArray = (typename T::ElementType *) PlatAlloc(i2*4*sizeof(T::ElementType));
	PlatMemSet((LispChar *)iArray, 0, i2*4*sizeof(T::ElementType));

	// Split our array into x and y halves
	iXArray = &iArray[0];
	x.CopyToExternalArray(iXArray, true);

	iYArray = &iArray[i2];
	y.CopyToExternalArray(iYArray, true);

	iSumArray = &iArray[i2*2];

	BaseKaratsubaMultiply(aTarget, iXArray, iYArray, iSumArray, i2, aBase);

	PlatFree((LispChar *) iArray);
}


template<class T>
void BaseKaratsubaMultiply(T& aTarget, 
                           typename T::ElementType* x,
                           typename T::ElementType* y,
                           typename T::ElementType* aScratchSpace,
                           LispInt aSize,
                           LispInt aBase)
{
    LispInt iHalfSize = aSize/2;
    LispInt i;

    typename T::ElementType *xlow= &x[0];
    typename T::ElementType *xsum= &aScratchSpace[0];
    typename T::ElementType *xhigh=&x[iHalfSize];

    typename T::ElementType *ylow= &y[0];
    typename T::ElementType *ysum= &aScratchSpace[iHalfSize];
    typename T::ElementType *yhigh=&y[iHalfSize];
    T p1, p2, p3;

    // Too small?  Use O(n^2) multiplication
    if (aSize+1 <= cKaratsubaCutoff)
    {
        T iX(x, iHalfSize), iY(y, iHalfSize);
        BaseAddMultiply(aTarget, iX, iY, aBase);
        return;
    }

    // Compute xsum & ysum and put into the low half of the
    // scratch space, the high half is used for recursion

#if 1
    typename T::ElementType carryx=0;
    typename T::ElementType carryy=0;
#endif
    for (i = 0; i < iHalfSize; i++)
    {
#if 1
        typename T::ElementType word;
        typename T::ElementType newDigit;
        typename T::ElementType newCarry;

        word = (typename T::ElementType)xlow[i] +
               (typename T::ElementType)xhigh[i] + carryx;
        newDigit= (word%aBase);
        newCarry = (word/aBase);
        xsum[i] = newDigit;
        carryx  = newCarry;

        word = (typename T::ElementType)ylow[i] +
               (typename T::ElementType)yhigh[i] + carryy;
        newDigit= (word%aBase);
        newCarry = (word/aBase);
        ysum[i] = newDigit;
        carryy  = newCarry;
#endif

        //xsum[i] = xlow[i] + xhigh[i];
        //ysum[i] = ylow[i] + yhigh[i];
    }
#if 1
    xsum[iHalfSize]+=carryx;
    ysum[iHalfSize]+=carryy;
#endif
    
    // ToDo: make array bigger, so we don't have to zero the
    // scratch space after the first two calls?
    //hier
    BaseKaratsubaMultiply(p1, xlow, ylow, &aScratchSpace[aSize],
                          iHalfSize, aBase);
    PlatMemSet((LispChar *)&aScratchSpace[aSize], 0, aSize*sizeof(typename T::ElementType));
    BaseKaratsubaMultiply(p2, xsum, ysum, &aScratchSpace[aSize],
                          iHalfSize, aBase);
    PlatMemSet((LispChar *)&aScratchSpace[aSize], 0, aSize*sizeof(typename T::ElementType));
    BaseKaratsubaMultiply(p3, xhigh, yhigh, &aScratchSpace[aSize],
                          iHalfSize, aBase);

    // Have a feeling these functions are slowing down the
    // multipication

    BaseSubtract(p2, p1, 0);
    BaseSubtract(p2, p3, 0);
    BaseAdd(aTarget, p1, aBase);
    BaseAdd(aTarget, p3, aBase);
    BaseAdd(aTarget, p2, aBase);
}

#endif

/* BaseMultiply : multiply x and y, and put result in aTarget
 */

/*
 #ifdef USE_KARATSUBA
inline void BaseMultiply(ANumber& aTarget, ANumber& x, ANumber& y, LispInt aBase)
{
    aTarget.SetNrItems(1);
    aTarget[0] = 0;
    BaseAddMultiplyK(aTarget, x, y, aBase);
}
#endif
*/

template<class T>
inline void BaseMultiply(T& aTarget, T& x, T& y, LispInt aBase)
{
    aTarget.SetNrItems(1);
    aTarget[0] = 0;
#ifdef USE_KARATSUBA
    BaseAddMultiplyK(aTarget, x, y, aBase);
#else
    BaseAddMultiply(aTarget, x, y, aBase);
#endif
}



template<class T>
inline LispBoolean IsZero(T& a)
{
    LispInt i;
    for (i=0;i<a.NrItems();i++)
        if (a[i] != 0)
            return LispFalse;
    return LispTrue;
}



template<class T>
inline void BaseDivide(T& aQuotient, T& aRemainder, T& a1, T& a2, PlatDoubleWord base)
{
    // Find the values n and m as described in Knuth II:
    LispInt n,m;
    n=a2.NrItems();
    LISPASSERT(n>0);
    LISPASSERT(a2[n-1] != 0);
    
    //a1.NrItems() = m+n => m = a1.NrItems()-n
    m = a1.NrItems()-n;
    LISPASSERT(m>=0);

    aQuotient.GrowTo(m+1);
    
    //D1:
    //this calculates d = base/(a2[n-1]+1);
    PlatDoubleWord d = base/(static_cast<PlatDoubleWord>(a2[n-1])+1);


    BaseTimesInt(a1, d, base);
    BaseTimesInt(a2, d, base);
    a1.Append(0);
    a2.Append(0);
    
    //D2:
    LispInt j = m;

    while (j>=0)
    {
        //D3:
        PlatDoubleWord q = (a1[j+n]*base+a1[j+n-1])/a2[n-1];
        PlatDoubleWord r = (a1[j+n]*base+a1[j+n-1])%a2[n-1];

    REDO:
        if (q == base || q*a2[n-2] > base*r+a1[j+n-2])
        {
            q = q - 1;
            r = r + a2[n-1];
            if (r < base)
                goto REDO;
        }

        //D4:
        ANumber sub(Precision(aQuotient));
        sub.CopyFrom(a2);
        BaseTimesInt(sub, q, base);
        sub.Append(0);

        PlatSignedDoubleWord carry;
        LispInt digit;
        {//Subtract the two
            //TODO this can be generalized!!!!
            //
            // Beware though: this is not a normal subtraction. Only a
            // certain set of digits ends up being subtracted.

            // First check if qv isn't too big...
            carry = 0;
            for (digit=0;digit<=n;digit++)
            {
                PlatSignedDoubleWord word;
                word = ((PlatSignedDoubleWord)a1[digit+j]) -
                    ((PlatSignedDoubleWord)sub[digit]) +
                    (PlatSignedDoubleWord)carry;
                carry=0;
                while (word<0)
                {
                    word+=base;
                    carry--;
                }
            }
            if (carry)
            {
                q--;
                sub.CopyFrom(a2);
                BaseTimesInt(sub, q, base);
                sub.Append(0);
            }
            
            carry = 0;
            for (digit=0;digit<=n;digit++)
            {
                PlatSignedDoubleWord word;
                word = ((PlatSignedDoubleWord)a1[digit+j]) -
                    ((PlatSignedDoubleWord)sub[digit]) +
                    (PlatSignedDoubleWord)carry;
                carry=0;
                while (word<0)
                {
                    word+=base;
                    carry--;
                }
                a1[digit+j] = ((PlatWord)(word%base));
            }
        }
        LISPASSERT(carry == 0);
            
        //D5:
        aQuotient[j] = (typename T::ElementType)q;
        //D7:
        j--;
        
    }

    //D8:
    a1.SetNrItems(n);
    PlatDoubleWord carry;
    BaseDivideInt(a1, d, base,carry);
    aRemainder.CopyFrom(a1);
}


