//-----------------------------------------------------------------------------
//  Package Title  ratpak                                                   
//  File           num.c                                                    
//  Author         Timothy David Corrie Jr. (timc@microsoft.com)            
//  Copyright      (C) 1995-97 Microsoft                                    
//  Date           01-16-95                                                 
//                                                                          
//                                                                          
//  Description                                                             
//                                                                          
//     Contains number routines for add, mul, div, rem and other support       
//  and longs.                                                              
//                                                                          
//  Special Information                                                     
//                                                                          
//                                                                          
//-----------------------------------------------------------------------------

#if defined( DOS )
#include <dosstub.h>
#else
#include <windows.h>
#endif
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <ratpak.h>

//----------------------------------------------------------------------------
//
//    FUNCTION: addnum
//
//    ARGUMENTS: pointer to a number a second number, and the
//               nRadix.
//
//    RETURN: None, changes first pointer.
//
//    DESCRIPTION: Does the number equivalent of *pa += b.
//    Assumes nRadix is the base of both numbers.
//
//    ALGORITHM: Adds each digit from least significant to most
//    significant.
//
//
//----------------------------------------------------------------------------

void _addnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix );

void __inline addnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix )

{
    if ( b->cdigit > 1 || b->mant[0] != 0 )
        {    // If b is zero we are done.
        if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 0 )
            { // pa and b are both nonzero.
            _addnum( pa, b, nRadix );
            }
        else
            { // if pa is zero and b isn't just copy b.
            DUPNUM(*pa,b);
            }
        }
}

void _addnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix )

{
    PNUMBER c=NULL;     // c will contain the result.
    PNUMBER a=NULL;     // a is the dereferenced number pointer from *pa
    MANTTYPE *pcha;     // pcha is a pointer to the mantissa of a.
    MANTTYPE *pchb;     // pchb is a pointer to the mantissa of b.
    MANTTYPE *pchc;     // pchc is a pointer to the mantissa of c.
    long cdigits;       // cdigits is the max count of the digits results
                        // used as a counter.
    long mexp;          // mexp is the exponent of the result.
    MANTTYPE  da;       // da is a single 'digit' after possible padding.
    MANTTYPE  db;       // db is a single 'digit' after possible padding.
    MANTTYPE  cy=0;     // cy is the value of a carry after adding two 'digits'
    long  fcompla = 0;  // fcompla is a flag to signal a is negative.
    long  fcomplb = 0;  // fcomplb is a flag to signal b is negative.

    a=*pa;
    
    
    // Calculate the overlap of the numbers after alignment, this includes
    // necessary padding 0's
    cdigits = max( a->cdigit+a->exp, b->cdigit+b->exp ) -
            min( a->exp, b->exp );

    createnum( c, cdigits + 1 );
    c->exp = min( a->exp, b->exp );
    mexp = c->exp;
    c->cdigit = cdigits;
    pcha = MANT(a);
    pchb = MANT(b);
    pchc = MANT(c);
    
    // Figure out the sign of the numbers
    if ( a->sign != b->sign )
        {
        cy = 1;
        fcompla = ( a->sign == -1 );
        fcomplb = ( b->sign == -1 );
        }
    
    // Loop over all the digits, real and 0 padded. Here we know a and b are
    // aligned 
    for ( ;cdigits > 0; cdigits--, mexp++ )
        {
        
        // Get digit from a, taking padding into account.
        da = ( ( ( mexp >= a->exp ) && ( cdigits + a->exp - c->exp > 
                    (c->cdigit - a->cdigit) ) ) ? 
                    *pcha++ : 0 );
        // Get digit from b, taking padding into account.
        db = ( ( ( mexp >= b->exp ) && ( cdigits + b->exp - c->exp > 
                    (c->cdigit - b->cdigit) ) ) ? 
                    *pchb++ : 0 );
        
        // Handle complementing for a and b digit. Might be a better way, but
        // haven't found it yet.
        if ( fcompla )
            {
            da = (MANTTYPE)(nRadix) - 1 - da;
            }
        if ( fcomplb )
            {
            db = (MANTTYPE)(nRadix) - 1 - db;
            }
        
        // Update carry as necessary
        cy = da + db + cy;
        *pchc++ = (MANTTYPE)(cy % (MANTTYPE)nRadix);
        cy /= (MANTTYPE)nRadix;
        }
    
    // Handle carry from last sum as extra digit
    if ( cy && !(fcompla || fcomplb) )
        {
        *pchc++ = cy;
        c->cdigit++;
        }
    
    // Compute sign of result
    if ( !(fcompla || fcomplb) )
        {
        c->sign = a->sign;
        }
    else
        {
        if ( cy )
            {
            c->sign = 1;
            }
        else
            {
            // In this particular case an overflow or underflow has occoured 
            // and all the digits need to be complemented, at one time an 
            // attempt to handle this above was made, it turned out to be much 
            // slower on average.
            c->sign = -1;
            cy = 1;
            for ( ( cdigits = c->cdigit ), (pchc = MANT(c) ); 
                cdigits > 0; 
                cdigits-- )
                {
                cy = (MANTTYPE)nRadix - (MANTTYPE)1 - *pchc + cy;
                *pchc++ = (MANTTYPE)( cy % (MANTTYPE)nRadix );
                cy /= (MANTTYPE)nRadix;
                }
            }
        }
    
    // Remove leading zeroes, remember digits are in order of
    // increasing significance. i.e. 100 would be 0,0,1
    while ( c->cdigit > 1 && *(--pchc) == 0 )
        {
        c->cdigit--;
        }
    destroynum( *pa );
    *pa=c;
}

//----------------------------------------------------------------------------
//
//    FUNCTION: mulnum
//
//    ARGUMENTS: pointer to a number a second number, and the
//               nRadix.
//
//    RETURN: None, changes first pointer.
//
//    DESCRIPTION: Does the number equivalent of *pa *= b.
//    Assumes nRadix is the nRadix of both numbers.  This algorithm is the
//    same one you learned in gradeschool.
//
//----------------------------------------------------------------------------

void _mulnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix );

void __inline mulnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix )

{
    if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
        {    // If b is one we don't multiply exactly.
        if ( (*pa)->cdigit > 1 || (*pa)->mant[0] != 1 || (*pa)->exp != 0 )
            { // pa and b are both nonone.
            _mulnum( pa, b, nRadix );
            }
        else
            { // if pa is one and b isn't just copy b, and adjust the sign.
            long sign = (*pa)->sign;
            DUPNUM(*pa,b);
            (*pa)->sign *= sign;
            }
        }
    else
        {    // But we do have to set the sign.
        (*pa)->sign *= b->sign;
        }
}

void _mulnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix )

{
    PNUMBER c=NULL;         // c will contain the result.
    PNUMBER a=NULL;         // a is the dereferenced number pointer from *pa
    MANTTYPE *pcha;         // pcha is a pointer to the mantissa of a.
    MANTTYPE *pchb;         // pchb is a pointer to the mantissa of b.
    MANTTYPE *pchc;         // pchc is a pointer to the mantissa of c.
    MANTTYPE *pchcoffset;   // pchcoffset, is the anchor location of the next
                            // single digit multiply partial result.
    long iadigit = 0;       // Index of digit being used in the first number.
    long ibdigit = 0;       // Index of digit being used in the second number.
    MANTTYPE  da = 0;       // da is the digit from the fist number.
    TWO_MANTTYPE  cy = 0;   // cy is the carry resulting from the addition of
                            // a multiplied row into the result.
    TWO_MANTTYPE  mcy = 0;  // mcy is the resultant from a single 
                            // multiply, AND the carry of that multiply.
    long  icdigit = 0;      // Index of digit being calculated in final result.

    a=*pa;
    ibdigit = a->cdigit + b->cdigit - 1;
    createnum( c,  ibdigit + 1 );
    c->cdigit = ibdigit;
    c->sign = a->sign * b->sign;

    c->exp = a->exp + b->exp;
    pcha = MANT(a);
    pchcoffset = MANT(c);

    for (  iadigit = a->cdigit; iadigit > 0; iadigit-- )
        {
        da =  *pcha++;
        pchb = MANT(b);
        
        // Shift pchc, and pchcoffset, one for each digit 
        pchc = pchcoffset++;

        for ( ibdigit = b->cdigit; ibdigit > 0; ibdigit-- )
            {
            cy = 0;
            mcy = (TWO_MANTTYPE)da * *pchb;
            if ( mcy )
                {
                icdigit = 0;
                if ( ibdigit == 1 && iadigit == 1 )
                    {
                    c->cdigit++;
                    }
                }
            // If result is nonzero, or while result of carry is nonzero...
            while ( mcy || cy )
                {
                
                // update carry from addition(s) and multiply.
                cy += (TWO_MANTTYPE)pchc[icdigit]+(mcy%(TWO_MANTTYPE)nRadix);
                
                // update result digit from 
                pchc[icdigit++]=(MANTTYPE)(cy%(TWO_MANTTYPE)nRadix);
                
                // update carries from
                mcy /= (TWO_MANTTYPE)nRadix;
                cy /= (TWO_MANTTYPE)nRadix;
                }
            *pchb++;
            *pchc++;
            }
        }
    
    // prevent different kinds of zeros, by stripping leading duplicate zeroes.
    // digits are in order of increasing significance.
    while ( c->cdigit > 1 && MANT(c)[c->cdigit-1] == 0 )
        {
        c->cdigit--;
        }

    destroynum( *pa );
    *pa=c;
}


//----------------------------------------------------------------------------
//
//    FUNCTION: remnum
//
//    ARGUMENTS: pointer to a number a second number, and the
//               nRadix.
//
//    RETURN: None, changes first pointer.
//
//    DESCRIPTION: Does the number equivalent of *pa %= b.
//            Repeatedly subtracts off powers of 2 of b until *pa < b.
//
//
//----------------------------------------------------------------------------

void remnum( PNUMBER *pa, PNUMBER b, long nRadix )

{
    PNUMBER tmp = NULL;     // tmp is the working remainder.
    PNUMBER lasttmp = NULL; // lasttmp is the last remainder which worked.
    
    // Once *pa is less than b, *pa is the remainder.
    while ( !lessnum( *pa, b ) && !fhalt )
        {
        DUPNUM( tmp, b );
        if ( lessnum( tmp, *pa ) )
            {
            // Start off close to the right answer for subtraction.
            tmp->exp = (*pa)->cdigit+(*pa)->exp - tmp->cdigit;
            if ( MSD(*pa) <= MSD(tmp) )
                {
                // Don't take the chance that the numbers are equal.
                tmp->exp--;
                }
            }

        destroynum( lasttmp );
        lasttmp=longtonum( 0, nRadix );

        while ( lessnum( tmp, *pa ) ) 
            {
            DUPNUM( lasttmp, tmp );
            addnum( &tmp, tmp, nRadix );
            }

        if ( lessnum( *pa, tmp ) )
            {    
            // too far, back up...
            destroynum( tmp );
            tmp=lasttmp;
            lasttmp=NULL;
            }
        
        // Subtract the working remainder from the remainder holder.
        tmp->sign = -1*(*pa)->sign;
        addnum( pa, tmp, nRadix ); 

        destroynum( tmp );
        destroynum( lasttmp );

        } 
}


//---------------------------------------------------------------------------
//
//    FUNCTION: divnum
//
//    ARGUMENTS: pointer to a number a second number, and the
//               nRadix.
//
//    RETURN: None, changes first pointer.
//
//    DESCRIPTION: Does the number equivalent of *pa /= b.
//    Assumes nRadix is the nRadix of both numbers.
//
//---------------------------------------------------------------------------

void _divnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix );

void __inline divnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix )

{
    if ( b->cdigit > 1 || b->mant[0] != 1 || b->exp != 0 )
        {    
    	// b is not one
        _divnum( pa, b, nRadix );
        }
    else
        {    // But we do have to set the sign.
        (*pa)->sign *= b->sign;
        }
}

void _divnum( PNUMBER *pa, PNUMBER b, unsigned long nRadix )

{
    PNUMBER a = NULL;
    PNUMBER c = NULL;
    PNUMBER tmp = NULL;
    PNUMBER rem = NULL;
    PLINKEDLIST pll = NULL;
    PLINKEDLIST pllrover = NULL;
    long digit;
    long cdigits;
    BOOL bret;
    MANTTYPE *ptrc;
    long thismax = maxout+2;

    a=*pa;
    if ( thismax < a->cdigit )
        {
        thismax = a->cdigit;
        }

    if ( thismax < b->cdigit )
        {
        thismax = b->cdigit;
        }

    createnum( c, thismax + 1 );
    c->exp = (a->cdigit+a->exp) - (b->cdigit+b->exp) + 1;
    c->sign = a->sign * b->sign;

    ptrc = MANT(c) + thismax;
    cdigits = 0;
    DUPNUM( rem, a );
    DUPNUM( tmp, b );
    tmp->sign = a->sign;
    rem->exp = b->cdigit + b->exp - rem->cdigit;
    
    // Build a table of multiplications of the divisor, this is quicker for 
    // more than nRadix 'digits'
    pll = (PLINKEDLIST)zmalloc( sizeof( LINKEDLIST ) );
    pll->pnum = longtonum( 0L, nRadix );
    pll->llprev = NULL;
    for ( cdigits = 1; cdigits < (long)nRadix; cdigits++ )
        {
        pllrover = (PLINKEDLIST)zmalloc( sizeof( LINKEDLIST ) );
        pllrover->pnum=NULL;
        DUPNUM( pllrover->pnum, pll->pnum );
        addnum( &(pllrover->pnum), tmp, nRadix );
        pllrover->llprev = pll;
        pll = pllrover;
        }
    destroynum( tmp );
    cdigits = 0;
    while ( cdigits++ < thismax && !zernum(rem) )
        {
        pllrover = pll;
        digit = nRadix - 1;
        do    {
            bret = lessnum( rem, pllrover->pnum );
            } while ( bret && --digit && ( pllrover = pllrover->llprev ) );
        if ( digit )
            {
            pllrover->pnum->sign *= -1;
            addnum( &rem, pllrover->pnum, nRadix );
            pllrover->pnum->sign *= -1;
            }
        rem->exp++;
        *ptrc-- = (MANTTYPE)digit;
        }
    cdigits--;
    if ( MANT(c) != ++ptrc )
        {
        memcpy( MANT(c), ptrc, (int)(cdigits*sizeof(MANTTYPE)) );
        }
    
    // Cleanup table structure
    pllrover = pll;
    do    {
        pll = pllrover->llprev;
        destroynum( pllrover->pnum );
        zfree( pllrover );
        } while ( pllrover = pll );

    if ( !cdigits )
        {
        c->cdigit = 1;
        c->exp = 0;
        }
    else
        {
        c->cdigit = cdigits;
        c->exp -= cdigits;
        while ( c->cdigit > 1 && MANT(c)[c->cdigit-1] == 0 )
            {
            c->cdigit--;
            }
        }
    destroynum( rem );

    destroynum( *pa );
    *pa=c;
}


//---------------------------------------------------------------------------
//
//    FUNCTION: equnum
//
//    ARGUMENTS: two numbers.
//
//    RETURN: Boolean
//
//    DESCRIPTION: Does the number equivalent of ( a == b )
//    Only assumes that a and b are the same nRadix.
//
//---------------------------------------------------------------------------

BOOL equnum( PNUMBER a, PNUMBER b )

{
    long diff;
    MANTTYPE *pa;
    MANTTYPE *pb;
    long cdigits;
    long ccdigits;
    MANTTYPE  da;
    MANTTYPE  db;

    diff = ( a->cdigit + a->exp ) - ( b->cdigit + b->exp );
    if ( diff < 0 )
        {
        // If the exponents are different, these are different numbers.
        return( FALSE );
        }
    else
        {
        if ( diff > 0 )
            {
            // If the exponents are different, these are different numbers.
            return( FALSE );
            }
        else
            {
            // OK the exponents match.
            pa = MANT(a);
            pb = MANT(b);
            pa += a->cdigit - 1;
            pb += b->cdigit - 1;
            cdigits = max( a->cdigit, b->cdigit );
            ccdigits = cdigits;
            
            // Loop over all digits until we run out of digits or there is a
            // difference in the digits.
            for ( ;cdigits > 0; cdigits-- )
                {
                da = ( (cdigits > (ccdigits - a->cdigit) ) ? 
                    *pa-- : 0 );
                db = ( (cdigits > (ccdigits - b->cdigit) ) ? 
                    *pb-- : 0 );
                if ( da != db )
                    {
                    return( FALSE );
                    }
                }
            
            // In this case, they are equal.
            return( TRUE );
            }
        }
}

//---------------------------------------------------------------------------
//
//    FUNCTION: lessnum
//
//    ARGUMENTS: two numbers.
//
//    RETURN: Boolean
//
//    DESCRIPTION: Does the number equivalent of ( abs(a) < abs(b) )
//    Only assumes that a and b are the same nRadix, WARNING THIS IS AN.
//    UNSIGNED COMPARE!
//
//---------------------------------------------------------------------------

BOOL lessnum( PNUMBER a, PNUMBER b )

{
    long diff;
    MANTTYPE *pa;
    MANTTYPE *pb;
    long cdigits;
    long ccdigits;
    MANTTYPE  da;
    MANTTYPE  db;


    diff = ( a->cdigit + a->exp ) - ( b->cdigit + b->exp );
    if ( diff < 0 )
        {
        // The exponent of a is less than b
        return( TRUE );
        }
    else
        {
        if ( diff > 0 )
            {
            return( FALSE );
            }
        else
            {
            pa = MANT(a);
            pb = MANT(b);
            pa += a->cdigit - 1;
            pb += b->cdigit - 1;
            cdigits = max( a->cdigit, b->cdigit );
            ccdigits = cdigits;
            for ( ;cdigits > 0; cdigits-- )
                {
                da = ( (cdigits > (ccdigits - a->cdigit) ) ? 
                    *pa-- : 0 );
                db = ( (cdigits > (ccdigits - b->cdigit) ) ? 
                    *pb-- : 0 );
                diff = da-db;
                if ( diff )
                    {
                    return( diff < 0 );
                    }
                }
            // In this case, they are equal.
            return( FALSE );
            }
        }
}

//----------------------------------------------------------------------------
//
//    FUNCTION: zernum
//
//    ARGUMENTS: number
//
//    RETURN: Boolean
//
//    DESCRIPTION: Does the number equivalent of ( !a )
//
//----------------------------------------------------------------------------

BOOL zernum( PNUMBER a )

{
    long length;
    MANTTYPE *pcha;
    length = a->cdigit;
    pcha = MANT( a );
    
    // loop over all the digits until you find a nonzero or until you run 
    // out of digits
    while ( length-- > 0 )
        {
        if ( *pcha++ )
            {
            // One of the digits isn't zero, therefore the number isn't zero
            return( FALSE );
            }
        }
    // All of the digits are zero, therefore the number is zero
    return( TRUE );
}
