vfactor source/vgiants.c

/**************************************************************
 *
 *  vgiants.c
 *
 *  AltiVec-based giant integer package
 *  Derived from original giants.c project at 
 *  Next Software, Inc.
 *
 *  This package is part of ongoing research in the
 *  Advanced Computation Group, Apple Computer.
 *  
 *  c. 1999 Apple Computer, Inc.
 *  All Rights Reserved.
 *
 *
 *************************************************************/
 
/*
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
 
#include "giantsdebug.h"
#if __MWERKS__
#include <altivec.h>
#endif
#include "vgiants.h"
#include "vecarith.h"
#include <Memory.h>
#include <Quickdraw.h>      // for random
#include <stddef.h>
#include <stdlib.h>
#include <math.h>
#include "giantsstack.h"
#include <segload.h>        // for ExitToShell
 
//////////////////////////////////////////////
// globals
//////////////////////////////////////////////
 
static giant    cur_recip = NULL;
static giant    cur_den = NULL;
 
 
 
//////////////////////////////////////////////
 
#if 0
#define BORROW_GIANT()  newgiantbits(DEFAULT_GIANT_BITS)
#define RETURN_GIANT(a) disposegiant(a)
#else
#define BORROW_GIANT()  popg()
#define RETURN_GIANT(a) pushg(1)
#endif
 
#define ADDVECS addvecs
#define SUBVECS subvecs
 
#define max(a, b) (a > b ? a : b)
 
#if GDEBUG 
 
void ASSERT_GVALID(giant g)
{
    GAssert((g->giantSign == -1) || (g->giantSign == 1));
    
    if (((long)g->vectors & 0x0f) != 0) {
        DebugStr("\pvectors not 16-byte aligned.");
    }
    
    if (g->giantDigits > g->giantCapacity) {
        DebugStr("\pgiant digits overflow!");       
    }
    
    if (g->giantDigits != 0) {
        vector unsigned long    veczero = (vector unsigned long)(0);
        
        if (vec_all_eq(g->vectors[g->giantDigits-1], veczero)) {
            DebugStr("\pMSV is zero");
        }
    }
}
#endif
 
static void FATAL_ERROR(long err)
{
#pragma unused(err)
    DebugStr("\pfatal error in vgiants");
    //ExitToShell();
}
 
 
 
 
 
gmatrix
newgmatrix(
    void
)
/* Allocates space for a gmatrix struct, but does not
 * allocate space for the giants. */
{
    return((gmatrix) malloc (4*sizeof(giant)));
}
 
 
GiantStructPtr  newgiantbits(unsigned long capacityBits)
{
    unsigned long   giantCapacity;
    unsigned long   allocSize;
    GiantStructPtr  pg;
    
    if (!capacityBits) capacityBits = 1;
    
    giantCapacity = (capacityBits + BITS_PER_VECTOR-1) / BITS_PER_VECTOR;
    allocSize = giantCapacity * BYTES_PER_VECTOR;
    pg = (GiantStructPtr)NewPtr(sizeof(GiantStruct));
 
    GAssert(pg != nil); 
    
    pg->giantSign = 1;
    pg->giantDigits = 0;
    pg->giantCapacity = giantCapacity;  
    
    pg->vectors = (vector unsigned long *)NewPtr(allocSize);
 
    GAssert(pg->vectors != nil);
    
    return pg;
}
 
GiantStructPtr  newgiant(unsigned long capacityShorts)
{
    return newgiantbits(capacityShorts << 4);
}
 
 
void disposegiant(GiantStructPtr g)
{
    ASSERT_GVALID(g);
    
    DisposePtr((Ptr)g->vectors);    
    DisposePtr((Ptr)g);
}
 
static void reallocgdigits(giant g, unsigned long vectorcount, Boolean save)
{
    long                i;
            
    vector unsigned long *pNewVectors = (vector unsigned long *)NewPtr( vectorcount * BYTES_PER_VECTOR );
 
    GAssert(vectorcount >= g->giantDigits);
 
    GAssert(pNewVectors != nil);
    
    if (pNewVectors != nil) {
        if (save) {
            for (i=0; i<g->giantDigits; i++) {
                pNewVectors[i] = g->vectors[i];
            }
        }
                
        DisposePtr((Ptr)g->vectors);
        g->vectors = pNewVectors;
        g->giantCapacity = vectorcount;
    }
}
 
 
 
static void reallocg(giant g, unsigned long newbits, Boolean save)
{
    unsigned long   vectorcount =  (newbits + 127) / 128;
    
    reallocgdigits(g, vectorcount, save);
}
 
static void EnsureDigits(giant g, unsigned long newdigits, Boolean save)
{
    if (g->giantCapacity < newdigits) {
        reallocgdigits(g, newdigits, save);
    }
}
 
static void EnsureBits(giant g, unsigned long newbits, Boolean save)
{
    if (g->giantCapacity * BITS_PER_VECTOR < newbits) {
        reallocg(g, newbits, save);
    }
}
 
static void EnsureBytes(giant g, unsigned long newbytes, Boolean save)
{
    EnsureBits(g, newbytes << 3, save);
}
 
static void AddMSDLong(giant g, unsigned long digit)
{
    unsigned long *topvector;
 
    if (g->giantCapacity == g->giantDigits) {
        reallocgdigits(g, g->giantDigits+1, true);
    }   
    
    topvector = (unsigned long*)&g->vectors[g->giantDigits];
    
    *topvector++ = 0;
    *topvector++ = 0;
    *topvector++ = 0;
    *topvector = digit;
    
    g->giantDigits++;
}
 
#if 1
static void justg(giant g)
{
    if (g->giantDigits) {
        unsigned long       counter = g->giantDigits;
        unsigned long       *pLongs = (unsigned long*)&g->vectors[g->giantDigits];  // point to one past last
 
        while (counter--) {
            if (*--pLongs) break;
            if (*--pLongs) break;
            if (*--pLongs) break;
            if (*--pLongs) break;
            
            g->giantDigits--;
        }
    
    }
 
}
#else
static void justg(giant g)
{
    if (g->giantDigits) {
        vector unsigned long vecZero = (vector unsigned long)(0);
        
        while (vec_all_eq(vecZero, g->vectors[g->giantDigits-1]) && g->giantDigits) {
            // do nothing
            g->giantDigits--;
        }
    }
}
#endif
 
static unsigned long gtoul(giant g)
{
    GAssert(bitlen(g) <= 32);
    
    if (!g->giantSign) return 0;
    
    return ((unsigned long *)g->vectors)[LONGS_PER_VECTOR-1];
}
 
 
 
 
 
int             cur_run = 0;
double *        sinCos=NULL;
 
void
init_sinCos(
    int         n
)
{
    int         j;
    double  e = TWOPI/n;
 
    if (n<=cur_run)
        return;
    cur_run = n;
    if (sinCos)
        free(sinCos);
    sinCos = (double *)malloc(sizeof(double)*(1+(n>>2)));
    for (j=0;j<=(n>>2);j++)
    {
        sinCos[j] = sin(e*j);
    }
}
 
 
double
s_sin(
    int     n
)
{
    int     seg = n/(cur_run>>2);
 
    switch (seg)
    {
        case 0: return(sinCos[n]);
        case 1: return(sinCos[(cur_run>>1)-n]);
        case 2: return(-sinCos[n-(cur_run>>1)]);
        case 3: return(-sinCos[cur_run-n]);
    }
    return 0;
}
 
 
double
s_cos(
    int     n
)
{
    int     quart = (cur_run>>2);
 
    if (n < quart)
        return(s_sin(n+quart));
    return(-s_sin(n-quart));
}
 
 
 
 
unsigned char GetNthByte(giant g, unsigned long pos)
{
    unsigned long   byteindex;
    
    //GAssert(pos < g->giantDigits * BYTES_PER_VECTOR);
    
    if (pos >= g->giantDigits * BYTES_PER_VECTOR) {
        return 0;
    }
    
    byteindex = pos & ~0xf;
    
    byteindex += BYTES_PER_VECTOR - (pos & 0xf)-1;
    
    return ((unsigned char*)g->vectors)[byteindex];
 
}
 
static unsigned short * GetNthShortPtr(giant g, unsigned long pos)
{
    unsigned long   shortindex;
        
    if (pos >= g->giantDigits * SHORTS_PER_VECTOR) {
        return 0;
    }
    
    shortindex = pos & ~0x7;
    
    shortindex += SHORTS_PER_VECTOR - (pos & 0x7)-1;
    
    return &((unsigned short*)g->vectors)[shortindex];
 
}
 
 
static unsigned long * GetNthLongPtr(giant g, unsigned long pos)
{
    unsigned long   longindex;
        
    if (pos >= g->giantDigits * LONGS_PER_VECTOR) {
        return 0;
    }
    
    longindex = pos & ~0x3;
    
    longindex += LONGS_PER_VECTOR - (pos & 0x3)-1;
    
    return &((unsigned long*)g->vectors)[longindex];
 
}
 
static unsigned long NumLongs(giant g)
{
    unsigned long result = 0;
    unsigned long *longPtr;
    unsigned long i;
    
    if (g->giantDigits) {
        result = g->giantDigits * LONGS_PER_VECTOR;
        
        longPtr = (unsigned long*)&g->vectors[g->giantDigits-1];
        
        for (i=0; i<LONGS_PER_VECTOR; i++) {
            if (*longPtr++ != 0)  break;
            result--;   
        }
    }
    
    return result;
}
 
 
static unsigned long NumBytes(giant g)
{
    unsigned long result = 0;
    unsigned char *bytePtr;
    unsigned long i;
    
    if (g->giantDigits) {
        result = g->giantDigits * BYTES_PER_VECTOR;
        
        bytePtr = (unsigned char*)&g->vectors[g->giantDigits-1];
        
        for (i=0; i<BYTES_PER_VECTOR; i++) {
            if (*bytePtr++ != 0)  break;
            result--;   
        }
    }
    
    return result;
}
 
static unsigned long BytesToDigits(unsigned long bytes)
{
    return (bytes+(BYTES_PER_VECTOR-1))/BYTES_PER_VECTOR;
}
 
 
 
 
static Boolean gshiftleftsubvector(unsigned long bits, giant g)
{
    vector unsigned char    temp;   
    vector unsigned char    shiftFactor, negShiftFactor;
    vector unsigned char    vecZero;
    vector unsigned long    t1, t2;
    long                        i;
    Boolean                 shiftedOut = false;
 
    ASSERT_GVALID(g);
            
    if (bits) {
        vecZero = (vector unsigned char)(0);
 
        *((unsigned char*)&temp) = bits;    
        
        shiftFactor = vec_splat(temp, 0);               // fill vector with shift amount
        negShiftFactor = vec_sub(vecZero, shiftFactor);
        
        // figure out if we need to expand
        t2 = vec_sro(g->vectors[g->giantDigits-1], negShiftFactor);
        t2 = vec_srl(t2, negShiftFactor);
        if (!vec_all_eq((vector unsigned long)vecZero, t2)) {
            shiftedOut = true;
            g->vectors[g->giantDigits] = t2;
        }
 
        for (i=g->giantDigits-1; i>0; i--) {
            t1 = vec_slo(g->vectors[i], shiftFactor);
            t1 = vec_sll(t1, shiftFactor);
            t2 = vec_sro(g->vectors[i-1], negShiftFactor);
            t2 = vec_srl(t2, negShiftFactor);
            g->vectors[i] = vec_or(t1, t2);
        }
        
        t1 = vec_slo(g->vectors[i], shiftFactor);
        g->vectors[i] = vec_sll(t1, shiftFactor);
    }
        
    return shiftedOut;
}
 
void        gshiftleft(unsigned long bits, giant g)
{
    unsigned long   bitlength;
    long            multiples;
 
    if (!bits) return;
    if (!g->giantDigits) return;
 
    bitlength = bitlen(g);
 
    if (bitlength + bits > (g->giantCapacity*BITS_PER_VECTOR)) {
        reallocg(g, bitlength + bits, true);
    }
    
    multiples = bits >> 7;
    
    if (multiples) {
        vector unsigned long    *pSrc;
        vector unsigned long    *pDst;
        unsigned long           digitCount = g->giantDigits;
        
        pSrc = &g->vectors[digitCount-1];
        pDst = &g->vectors[digitCount+multiples-1];
 
        g->giantDigits += multiples;
        
        // move up digits
        while (digitCount--) {
            *pDst-- = *pSrc--;
        }
        
        // fill remaining shifting with zeros
        while (multiples--) {
            *pDst-- = (vector unsigned long)(0);
        }
    }
    
    if (gshiftleftsubvector(bits & 0x7f, g)) {
        g->giantDigits++;
    }
    
    ASSERT_GVALID(g);
}
 
 
static void gshiftrightsubvector(unsigned long bits, giant g)
{
    vector unsigned char    temp;   
    vector unsigned char    shiftFactor, negShiftFactor;
    vector unsigned char    vecZero;
    vector unsigned long    t1, t2;
    long                        i;
    
    if (bits) {
        vecZero = (vector unsigned char)(0);
        
        // set 0th element of shiftFactor to be bits
        *((unsigned char*)&temp) = bits;
            
        shiftFactor = vec_splat(temp, 0);   // fill vector with shift amount
        negShiftFactor = vec_sub(vecZero, shiftFactor);
        
        for (i=0; i<g->giantDigits-1; i++) {
            t1 = vec_sro(g->vectors[i], shiftFactor);
            t1 = vec_srl(t1, shiftFactor);
            t2 = vec_slo(g->vectors[i+1], negShiftFactor);
            t2 = vec_sll(t2, negShiftFactor);
            g->vectors[i] = vec_or(t1, t2);
        }
        
        t1 = vec_sro(g->vectors[i], shiftFactor);
        g->vectors[i] = vec_srl(t1, shiftFactor);
    }
}
 
void        gshiftright(unsigned long bits, giant g)
{
    long        multiples;
 
    if (!bits) return;
    if (!g->giantDigits) return;
 
    multiples = bits >> 7;
    
    if (multiples) {
        if (multiples >= g->giantDigits) {
            g->giantDigits = 0; 
        } else {        
            vector unsigned long    *pSrc;
            vector unsigned long    *pDst;
            unsigned long           newdigits = g->giantDigits-multiples;
            
            g->giantDigits = newdigits;
            
            pSrc = &g->vectors[multiples];
            pDst = g->vectors;
            
            while (newdigits--) {
                *pDst++ = *pSrc++;
            }
        }
    }
 
        
    if (g->giantDigits) {
        gshiftrightsubvector(bits& 0x7f, g);
    }
    
    justg(g);
    
    ASSERT_GVALID(g);   
}
 
 
 
 
#if 1
unsigned long           bitlen(giant g)
{
    unsigned long           length;
    unsigned long           *longPtr;
    unsigned long           i;
    register unsigned long  longdigit;
    ASSERT_GVALID(g);
 
    if (!g->giantDigits) return 0;
    
    length = length = BITS_PER_VECTOR * g->giantDigits;
 
    longPtr = (unsigned long*)&g->vectors[g->giantDigits-1];
    
    for (i=0; i<4; i++) {
        longdigit = longPtr[i];
 
        if (!longdigit) {
            length -= 32;   // bits per long
        } else {
#if __MWERKS__
            register unsigned long leadingzeros=0;  // initialized to eliminate compiler warning
            asm {
                cntlzw  leadingzeros, longdigit
            }
            length -= leadingzeros;         
#else               
            while (!(longdigit & 0x80000000)) {
                longdigit = longdigit << 1;
                length -= 1;
            }
 
#endif      
            break;  
        }
    }
    
    return length;
 
}
#else
unsigned long           bitlen(giant g)
{
    unsigned long           length;
    unsigned long           count;
#if __MWERKS__
    vector unsigned long    vecones = (vector signed long)(-1);
#else   
    vector unsigned long    vecones = (vector unsigned long)(-1);
#endif  
    vector unsigned long    vecmask = (vector unsigned long)(0);
    vector unsigned long    veczero = (vector unsigned long)(0);
    vector unsigned long    digit;
    unsigned long           longindex;
    unsigned long           thelong;
    
    if (!g->giantDigits) return 0;
    
    count = 3;
        
    digit = g->vectors[g->giantDigits-1];
    
    longindex = 0;
 
    length = BITS_PER_VECTOR * g->giantDigits;
 
    vecmask = vec_sld(vecones, vecmask, 12);
    
    while (count-- && vec_all_eq(veczero, vec_and(vecmask, digit))) {
        vecmask = vec_sld(vecones, vecmask, 12);
        length -= 32;
        longindex++;
    }   
        
    thelong = ((unsigned long*)(&g->vectors[g->giantDigits-1]))[longindex];
    
    while (!(thelong & 0x80000000)) {
        thelong = thelong << 1;
        length -= 1;
    }
        
    return length;  
}
#endif
 
long    bitval(giant g, long pos)
{
#if 1
    return (GetNthByte(g, pos >> 3) & (1 << (pos & 0x07)));
#else
    unsigned char   mask;
    unsigned long   byteindex;
    
    byteindex = (pos >> 7) << 4;
    
    byteindex += BYTES_PER_VECTOR - ((pos & 0x7f) >> 3)-1;
    
    mask = 1 << (pos & 0x07);
    
    return ((unsigned char*)g->vectors)[byteindex] & mask;
#endif
}
 
 
 
void gtog(giant a, giant b)
{
    long i;
 
    ASSERT_GVALID(a);
    ASSERT_GVALID(b);
 
    if (b->giantCapacity < a->giantDigits) {
        reallocgdigits(b, a->giantDigits, false);
    }
 
    GAssert(b->giantCapacity >= a->giantDigits)
    
    
    for (i=0; i<a->giantDigits; i++) {
        b->vectors[i] = a->vectors[i];
    }
    
    b->giantDigits = a->giantDigits;
    b->giantSign = a->giantSign;
}
 
 
long CompareMagnitude(giant a, giant b)
{
    long                vectorindex, longindex;
    unsigned long       *aPtr, *bPtr;
 
    ASSERT_GVALID(a);
    ASSERT_GVALID(b);
    
    if (a->giantDigits > b->giantDigits) return 1;
    if (a->giantDigits < b->giantDigits) return -1;
 
 
    for (vectorindex=a->giantDigits-1; vectorindex >= 0; vectorindex--) {
        aPtr = (unsigned long*)&a->vectors[vectorindex];
        bPtr = (unsigned long*)&b->vectors[vectorindex];
        
        for (longindex = 0; longindex< LONGS_PER_VECTOR; longindex++) {
            if (*aPtr > *bPtr) return 1;
            if (*aPtr < *bPtr) return -1;
            aPtr++;
            bPtr++;     
        }
    }
        
    return 0;
}
 
 
 
static void subg_normal(GiantStructPtr a, GiantStructPtr b, GiantStructPtr result)
{
    unsigned long endDigits = b->giantDigits;
    
    EnsureDigits(result, b->giantDigits, true);
        
    SUBVECS(a->vectors, a->giantDigits, b->vectors, b->giantDigits, result->vectors);
 
    result->giantDigits = endDigits;
            
    justg(result);
}
 
 
void addg(giant a, giant b)
{
    ASSERT_GVALID(a);
    ASSERT_GVALID(b);
 
    if (isZero(a)) {
        return;
    }
    
    if (isZero(b)) {
        gtog(a, b);
        return;
    }
 
    if (a->giantSign == b->giantSign) {
        unsigned long   newdigits;
        
        newdigits = max(a->giantDigits, b->giantDigits)+1;
        EnsureDigits(b, newdigits, true);
        if (!ADDVECS(a->vectors, a->giantDigits, b->vectors, b->giantDigits, b->vectors)) {
            newdigits--;
        }
        b->giantDigits = newdigits;
 
    } else {
        if (b->giantSign > 0) {
            // a is negative, b is positive
            if (CompareMagnitude(b, a) >= 0) {
                subg_normal(a, b, b);
            } else {
                // a neg, b pos, b<a
                subg_normal(b, a, b);
                negg(b);
            }
        } else {
            // a is positive, b is negative
            if (CompareMagnitude(b, a) < 0) {
                subg_normal(b, a, b);
                negg(b);
            } else {
                // a neg, b pos, b>a
                subg_normal(a, b, b);
            }           
        }
    }   
    ASSERT_GVALID(a);
    ASSERT_GVALID(b);
}
 
 
 
// result = b-a
void subg(giant a, giant b)
{
    ASSERT_GVALID(a);
    ASSERT_GVALID(b);
 
    if (isZero(a)) {
        return;
    }
    
    if (isZero(b)) {
        gtog(a, b);
        negg(b);
        return;
    }
 
    if (a->giantSign != b->giantSign) {
        unsigned long   newdigits;
        
        newdigits = max(a->giantDigits, b->giantDigits)+1;
        EnsureDigits(b, newdigits, true);
        if (!ADDVECS(a->vectors, a->giantDigits, b->vectors, b->giantDigits, b->vectors)) {
            newdigits--;
        }
        b->giantDigits = newdigits;
    } else {
        if (b->giantSign > 0) {
            // a is negative, b is positive
            if (CompareMagnitude(b, a) >= 0) {
                subg_normal(a, b, b);
            } else {
                // a neg, b pos, b<a
                subg_normal(b, a, b);
                negg(b);
            }
        } else {
            // a is positive, b is negative
            if (CompareMagnitude(b, a) < 0) {
                subg_normal(b, a, b);
                negg(b);
            } else {
                // a neg, b pos, b>a
                subg_normal(a, b, b);
            }           
        }
    }
    
    ASSERT_GVALID(a);
    ASSERT_GVALID(b);
}
 
 
 
 
 
void grammarmulg(giant a, giant b)
{
    unsigned long   resultdigits = a->giantDigits + b->giantDigits;
    
    if (isZero(b)) return;
 
    if (isZero(a)) {
        gtog(a, b);
        return;
    }
 
    if (a->giantDigits == 1) {
        if (NumLongs(a) == 1) {
            
            smulg(gtoul(a), b);
            b->giantSign *= a->giantSign;
        
        } else {
            EnsureDigits(b, b->giantDigits+1, true);
            
            VecMult1(   a->vectors,
                        b->giantDigits,
                        b->vectors,
                        b->vectors);
                        
            b->giantDigits++;
            
            b->giantSign *= a->giantSign;
            
            justg(b);
        }
    } else  if (b->giantDigits == 1) {
        if (NumLongs(b) == 1) {
            unsigned long   blong = gtoul(b);
            long            bsign = b->giantSign;
            
            gtog(a, b);
            smulg(blong, b);
            b->giantSign *= bsign;
 
        } else {
            EnsureDigits(b, a->giantDigits+1, true);
            
            VecMult1(   b->vectors,
                        a->giantDigits,
                        a->vectors,
                        b->vectors);
                        
            b->giantDigits = a->giantDigits+1;
            
            b->giantSign *= a->giantSign;
            
            justg(b);
        }
    } else {
        GiantStructPtr  temp = BORROW_GIANT();
        
        if (a->giantDigits == 2 && b->giantDigits == 2) {
            EnsureDigits(temp, 4, false);   
 
            mult2by2(   &a->vectors[1],
                        &a->vectors[0],
                        &b->vectors[1],
                        &b->vectors[0],
                        temp->vectors);
                    
            temp->giantDigits = 4;
            
            temp->giantSign = a->giantSign*b->giantSign;
 
            justg(temp);
            
            gtog(temp, b);
 
            RETURN_GIANT(temp);
        
        } else {    
            EnsureDigits(temp, resultdigits, false);    
 
            VecMult(a->vectors,
                    a->giantDigits,
                    b->vectors,
                    b->giantDigits,
                    temp->vectors);
                    
            temp->giantDigits = resultdigits;
            
            temp->giantSign = a->giantSign*b->giantSign;
            justg(temp);
            
            gtog(temp, b);
 
            RETURN_GIANT(temp);
        }
    }
    
    ASSERT_GVALID(b);
    
}
 
void
karatmulg(
        giant   x,
        giant   y)
/* y becomes x*y. */
{
        int     s = x->giantDigits, t = y->giantDigits, w, bits,
                sg = gsign(x)*gsign(y);
        giant   a, b, c, e, f;
 
        if((s <= KARAT_BREAK) || (t <= KARAT_BREAK)) {
                grammarmulg(x,y);
                return;
        }
        w = (s + t + 2)/4; bits = 16*w;
        
        a = BORROW_GIANT(); b = BORROW_GIANT(); c = BORROW_GIANT(); e = BORROW_GIANT(); f = BORROW_GIANT();
        gtog(x,a); if (w <= s) {a->giantDigits = w; justg(a);}
        absg(a);
        gtog(x,b); absg(b);
        gshiftright(bits, b);
 
        gtog(y,c); if (w <= t) {c->giantDigits = w; justg(c);}
        absg(c);
        absg(y);
        gshiftright(bits,y);
        gtog(a,e); addg(b,e);
        gtog(c,f); addg(y,f);
        karatmulg(e,f);  /* f := (a + b)(c + d). */
        karatmulg(c,a); /* a := a c. */
        karatmulg(b,y); /* d := b d. */
        subg(a,f); subg(y,f);
        gshiftleft(bits, y);
        addg(f, y);
        gshiftleft(bits, y);
 
        addg(a, y);
        setsign(y, sg);
        
        RETURN_GIANT(f);
        RETURN_GIANT(e);
        RETURN_GIANT(c);
        RETURN_GIANT(b);
        RETURN_GIANT(a);
 
        return;
}
 
void mulg(giant a, giant b)
{
    if((a->giantDigits <= KARAT_BREAK) || (b->giantDigits <= KARAT_BREAK)) {
        grammarmulg(a,b);
    } else {
        karatmulg(a, b);
    }
}
 
 
#if 1
/* g += i, with i non-negative. */
void        iaddg(unsigned long i,giant g)
{
    if (isZero(g)) {
        itog(i, g);
    } else {    
        ASSERT_GVALID(g);
 
        if (AddULongToVecs(g->vectors, g->giantDigits, i)) {
            AddMSDLong(g, 1);
        }
        
        justg(g);
 
        ASSERT_GVALID(g);
    }
}
#else
/* g += i, with i non-negative. */
void        iaddg(unsigned long i,giant g)
{
    giant       temp=BORROW_GIANT();
    
    ASSERT_GVALID(g);
 
    itog(i, temp);
    addg(temp, g);
 
    ASSERT_GVALID(g);
    
    RETURN_GIANT(temp);
}
#endif
 
 
 
/* Integer <-> giant. */
void itog(long n, giant g)
{
    unsigned long   absn = abs(n);
    
    if (absn) {
        unsigned long   *pLong = (unsigned long *)g->vectors;
        
        *pLong++ = 0;
        *pLong++ = 0;
        *pLong++ = 0;
        *pLong = absn;
 
        g->giantSign = (n < 0 ? -1 : 1);
        g->giantDigits = 1;
        
    } else {
        g->giantDigits = 0;
    }
        
    ASSERT_GVALID(g);   
}
 
 
#if 1
void        squareg(giant g)
{
    GiantStructPtr  temp;
    unsigned long   digits;
 
    if (isZero(g)) return;
 
    temp = BORROW_GIANT();
        
    digits = g->giantDigits * 2;
    
    EnsureDigits(temp, digits, false);  
        
    VecSquare(g->vectors,
            g->giantDigits,
            temp->vectors);
            
    temp->giantDigits = digits;
    
    temp->giantSign = 1;
    
    justg(temp);
    
    gtog(temp, g);
 
    RETURN_GIANT(temp);
    
    ASSERT_GVALID(g);
}   
 
#else
/* g *= g. */
void        squareg(giant g)
{
    ASSERT_GVALID(g);
    mulg(g, g);
 
    ASSERT_GVALID(g);
 
}
#endif
 
void        randomg(giant g, unsigned long numvecs)
{
    long i;
    
    if (numvecs > g->giantCapacity) numvecs = g->giantCapacity;
    
    for (i=0; i<numvecs*SHORTS_PER_VECTOR; i++) {
        ((unsigned short*)g->vectors)[i] = Random();
    }
    
    g->giantSign = (Random() & 1) ? -1 : 1;
    g->giantDigits = numvecs;
    
    justg(g);
 
    ASSERT_GVALID(g);
}
 
 
 
 
void
modg_via_recip(
    giant   d, 
    giant   r,
    giant   n
)
/* This is the fastest mod of the present collection.
 * n := n % d, where r is the precalculated
 * steady-state reciprocal of d. */
 
{
    int     s = (bitlen(r)-1), sign = gsign(n);
    giant   tmp, tmp2;
 
    if (isZero(d) || (gsign(d) < 0))
    {
        FATAL_ERROR(SIGN);
    }
 
    tmp = BORROW_GIANT();
    tmp2 = BORROW_GIANT();
    
    if (isone(d)) {
        itog(0, n);
        goto done;
    }
    
    absg(n);
    while (1) 
    {
        gtog(n, tmp); gshiftright(s-1, tmp);    
        mulg(r, tmp);
        gshiftright(s+1, tmp);
        mulg(d, tmp);
        subg(tmp, n);
        if (gcompg(n,d) >= 0) 
            subg(d,n);
        if (gcompg(n,d) < 0) 
            break;
    }
    if (sign >= 0)
        goto done;
    if (isZero(n))
        goto done; 
    negg(n);
    addg(d,n);
done:
    RETURN_GIANT(tmp);
    RETURN_GIANT(tmp2);
    return;
}
 
void
make_recip(
    giant   d, 
    giant   r
)
/* r becomes the steady-state reciprocal
 * 2^(2b)/d, where b = bit-length of d-1. */
{
    int     b;
    giant   tmp, tmp2;
 
    if (isZero(d) || (gsign(d) < 0))
    {
        FATAL_ERROR(SIGN);
    }
    tmp = BORROW_GIANT();
    tmp2 = BORROW_GIANT();
    itog(1, r); 
    subg(r, d); 
    b = bitlen(d); 
    addg(r, d);
    gshiftleft(b, r); 
    gtog(r, tmp2);
    while (1) 
    {
        gtog(r, tmp);
        squareg(tmp);
        gshiftright(b, tmp);
        mulg(d, tmp);
        gshiftright(b, tmp);
        addg(r, r); 
        subg(tmp, r);
        if (gcompg(r, tmp2) <= 0) 
            break;
        gtog(r, tmp2);
    }
    itog(1, tmp);
    gshiftleft(2*b, tmp);
    gtog(r, tmp2); 
    mulg(d, tmp2);
    subg(tmp2, tmp);
    itog(1, tmp2);
    while (gsign(tmp) < 0) 
    {
        subg(tmp2, r);
        addg(d, tmp);
    }
    RETURN_GIANT(tmp);
    RETURN_GIANT(tmp2);
}
 
 
void
modg(
    giant   d,
    giant   n
)
/* n becomes n%d. n is arbitrary, but the denominator d must be positive! */
{
    if (cur_recip == NULL) {
        cur_recip = newgiantbits(1);
        cur_den = newgiantbits(1);
        gtog(d, cur_den);
        make_recip(d, cur_recip);
    } else if (gcompg(d, cur_den)) {
        gtog(d, cur_den);
        make_recip(d, cur_recip);
    }
    modg_via_recip(d, cur_recip, n);
}
 
void
divg_via_recip(
    giant   d, 
    giant   r, 
    giant   n
)
/* n := n/d, where r is the precalculated
 * steady-state reciprocal of d. */
{
    int     s = 2*(bitlen(r)-1), sign = gsign(n);
    giant   tmp, tmp2;
 
    if (isZero(d) || (gsign(d) < 0))
    {
        FATAL_ERROR(SIGN);
    }
    
    tmp = BORROW_GIANT();
    tmp2 = BORROW_GIANT();
    
    if (isone(d)) {
        // dividing by one!
        goto done;
    }
    
    absg(n);
    itog(0, tmp2);
    while (1) 
    {
        gtog(n, tmp);   
        mulg(r, tmp);
        gshiftright(s, tmp);
        addg(tmp, tmp2);
        mulg(d, tmp);
        subg(tmp, n);
        if (gcompg(n,d) >= 0)
        {
            subg(d,n);
            iaddg(1, tmp2);
        }
        if (gcompg(n,d) < 0) 
            break;
    }
    gtog(tmp2, n);
    setsign(n, gsign(n) * sign);
done:
    RETURN_GIANT(tmp);
    RETURN_GIANT(tmp2);
}
 
 
 
void
divg(
    giant   d,
    giant   n
)
/* n becomes n/d. n is arbitrary, but the denominator d must be positive! */
{
    if (cur_recip == NULL) {
        cur_recip = newgiantbits(1);
        cur_den = newgiantbits(1);
        gtog(d, cur_den);
        make_recip(d, cur_recip);
    } else if (gcompg(d, cur_den)) {
        gtog(d, cur_den);
        make_recip(d, cur_recip);
    }
    divg_via_recip(d, cur_recip, n);
}
 
static long
radixdiv(
    long                base,
    long                divisor,
    giant           thegiant
)
/* Divides giant of arbitrary base by divisor.
 * Returns remainder. Used by idivg and gread. */
{
    long                j = (thegiant->giantDigits*SHORTS_PER_VECTOR)-1;
    unsigned int        num,rem=0;
    unsigned short      *digitpointer;
 
    if (divisor == 0)
    {
        FATAL_ERROR(DIVIDEBYZERO);
    }
 
    while (j>=0)
    {
        digitpointer = GetNthShortPtr(thegiant, j);
        num=rem*base + *digitpointer;       
        *digitpointer = (unsigned short)(num/divisor);
 
        rem = num % divisor;
        --j;
    }
 
    if (divisor < 0) negg(thegiant);
 
    justg(thegiant);
 
    return(rem);
}
 
#if 1
long
idivg(
    long    divisor,
    giant   theg
)
{
    /* theg becomes theg/divisor. Returns remainder. */
    int     n;
    int     base = 1<<(8*sizeof(short));
 
    n = radixdiv(base,divisor,theg);
    return(n);
}
#else
long
idivg(
    long        divisor,
    giant       theg
)
{
    giant   originalg = BORROW_GIANT();
    giant   divisorg = BORROW_GIANT();
    long    remainder;
        
    gtog(theg, originalg);
    
    itog(divisor, divisorg);
    
    divg(divisorg, theg);
 
    mulg(theg, divisorg);
    
    subg(divisorg, originalg);
        
    remainder = gtoi(originalg);
    
    RETURN_GIANT(divisorg);
    RETURN_GIANT(originalg);
    
    return remainder;
}
#endif
 
 
 
 
 
static void
gswap(
    giant   *p,
    giant   *q
)
{
    giant   t;
 
    t = *p;
    *p = *q;
    *q = t;
}
 
 
static void
fix(
    giant   *p,
    giant   *q
)
/* Insure that x > y >= 0. */
{
    if( gsign(*p) < 0 )
        negg(*p);
    if( gsign(*q) < 0 )
        negg(*q);
    if( gcompg(*p,*q) < 0 )
        gswap(p,q);
}
 
 
unsigned long   numtrailzeros(giant g)
{   
    unsigned long trailingbits = 0;
 
    ASSERT_GVALID(g);
    
    if (g->giantDigits != 0) {
        vector unsigned long    veczero = (vector unsigned long)(0);
        long                    vecindex;
        long                    byteindex;
        long                    bitindex;
        unsigned char           lastbyte;
        
        for (vecindex = 0; vecindex < g->giantDigits; vecindex++) {
            if (!vec_all_eq(veczero, g->vectors[vecindex])) {
                break;
            }   
            
            trailingbits += BITS_PER_VECTOR;
        }
    
        // now go by bytes;
        for (byteindex = 0; byteindex < BYTES_PER_VECTOR; byteindex++) {
            lastbyte = GetNthByte(g, byteindex + (vecindex * BYTES_PER_VECTOR));
            if (lastbyte != 0) {
                break;
            } 
            
            trailingbits += 8;
        }
        
        
        for (bitindex=0; bitindex < 8; bitindex++) {
            if (lastbyte & 1) {
                break;
            }
            
            lastbyte = lastbyte >> 1;
            trailingbits++;
        }   
    }
    
    return trailingbits;
}
 
static void
dotproduct(
    giant   a,
    giant   b,
    giant   c,
    giant   d
)
/* Replace last argument with the dot product of two 2-vectors. */
{
    giant s4 = BORROW_GIANT();
 
    gtog(c,s4);
    mulg(a, s4);
    mulg(b,d);
    addg(s4,d);
    
    RETURN_GIANT(s4);
}
 
 
static   void
mulvM(
    gmatrix     A,
    giant       x,
    giant       y
)
/* Multiply vector by Matrix; changes x,y. */
{
    giant s0 = BORROW_GIANT();
    giant s1 = BORROW_GIANT();
    
    gtog(A->ur,s0);
    gtog( A->lr,s1);
    dotproduct(x,y,A->ul,s0);
    dotproduct(x,y,A->ll,s1);
    gtog(s0,x);
    gtog(s1,y);
    
    RETURN_GIANT(s0);
    RETURN_GIANT(s1);
}
 
 
static void
bgcdg(
    giant   a,
    giant   b
)
/* Binary form of the gcd. b becomes the gcd of a,b. */
{
    int     k = isZero(b), m = isZero(a);
    giant   u, v, t;
 
    if (k || m)
    {
        if (m)
        {
            if (k)
                itog(1,b);
            return;
        }
        if (k)
        {
            if (m)
                itog(1,b);
            else
                gtog(a,b);
            return;
        }
    }
 
    u = BORROW_GIANT();
    v = BORROW_GIANT();
    t = BORROW_GIANT();
 
    /* Now neither a nor b is zero. */
    gtog(a, u);
    absg(u);
    gtog(b, v);
    absg(v);
    k = numtrailzeros(u);
    m = numtrailzeros(v);
    if (k>m)
        k = m;
    gshiftright(k,u);
    gshiftright(k,v);
    if (isOdd(u)) {
        gtog(v, t);
        negg(t);
    }
    else
    {
        gtog(u,t);
    }
 
    while (!isZero(t))
    {       
        m = numtrailzeros(t);
        gshiftright(m, t);
        if (gsign(t) > 0)
        {
            gtog(t, u);
            subg(v,t);
        }
        else
        {
            gtog(t, v);
            negg(v);
            addg(u,t);
        }
    }
    gtog(u,b);
    gshiftleft(k, b);
    RETURN_GIANT(v);
    RETURN_GIANT(u);
    RETURN_GIANT(t);
}
 
static void
shgcd(
    register int    x,
    register int    y,
    gmatrix         A
)
/*
 * Do a half gcd on the integers a and b, putting the result in A
 * It is fairly easy to use the 2 by 2 matrix description of the
 * extended Euclidean algorithm to prove that the quantity q*t
 * never overflows.
 */
{
    register int    q, t, start = x;
    int             Aul = 1, Aur = 0, All = 0, Alr = 1;
 
    while   (y != 0 && y > start/y)
    {
        q = x/y;
        t = y;
        y = x%y;
        x = t;
        t = All;
        All = Aul-q*t;
        Aul = t;
        t = Alr;
        Alr = Aur-q*t;
        Aur = t;
    }
    itog(Aul,A->ul);
    itog(Aur,A->ur);
    itog(All,A->ll);
    itog(Alr,A->lr);
}
 
static void
punch(
    giant       q,
    gmatrix     A
)
/* Multiply the matrix A on the left by [0,1,1,-q]. */
{
    giant s0 = BORROW_GIANT();
    
    gtog(A->ll,s0);
    mulg(q,A->ll);
    gswap(&A->ul,&A->ll);
    subg(A->ul,A->ll);
    gtog(s0,A->ul);
    gtog(A->lr,s0);
    mulg(q,A->lr);
    gswap(&A->ur,&A->lr);
    subg(A->ur,A->lr);
    gtog(s0,A->ur);
 
    RETURN_GIANT(s0);
}
 
 
static void
onestep(
    giant       x,
    giant       y,
    gmatrix     A
)
/* Do one step of the euclidean algorithm and modify
 * the matrix A accordingly. */
{
    giant s4 = BORROW_GIANT();
 
    gtog(x,s4);
    gtog(y,x);
    gtog(s4,y);
    divg(x,s4);
    punch(s4,A);
    mulg(x,s4);
    subg(s4,y);
    
    RETURN_GIANT(s4);
}
 
static void
mulmM(
    gmatrix     A,
    gmatrix     B
)
/* Multiply matrix by Matrix; changes second matrix. */
{
    giant s0 = BORROW_GIANT();
    giant s1 = BORROW_GIANT();
    giant s2 = BORROW_GIANT();
    giant s3 = BORROW_GIANT();
 
    gtog(B->ul,s0);
    gtog(B->ur,s1);
    gtog(B->ll,s2);
    gtog(B->lr,s3);
 
    dotproduct(A->ur,A->ul,B->ll,s0);
    dotproduct(A->ur,A->ul,B->lr,s1);
    dotproduct(A->ll,A->lr,B->ul,s2);
    dotproduct(A->ll,A->lr,B->ur,s3);
 
    gtog(s0,B->ul);
    gtog(s1,B->ur);
    gtog(s2,B->ll);
    gtog(s3,B->lr);
 
    RETURN_GIANT(s0);
    RETURN_GIANT(s1);
    RETURN_GIANT(s2);
    RETURN_GIANT(s3);
}
 
 
 
static void
hgcd(
    int         n,
    giant       xx,
    giant       yy,
    gmatrix     A
)
/* hgcd(n,x,y,A) chops n bits off x and y and computes th
 * 2 by 2 matrix A such that A[x y] is the pair of terms
 * in the remainder sequence starting with x,y that is
 * half the size of x. Note that the argument A is modified
 * but that the arguments xx and yy are left unchanged.
 */
{
    giant       x, y;
 
    if (isZero(yy))
        return;
    
    x = BORROW_GIANT();
    y = BORROW_GIANT();
    gtog(xx,x);
    gtog(yy,y);
    gshiftright(n,x);
    gshiftright(n,y);
    if (bitlen(x) <= INTLIMIT )
    {
        shgcd(gtoi(x),gtoi(y),A);
    }
    else
    {
        GMatrixStruct   B;
        int             m = bitlen(x)/2;
 
        hgcd(m,x,y,A);
        mulvM(A,x,y);
        if (gsign(x) < 0)
        {
            negg(x); negg(A->ul); negg(A->ur);
        }
        if (gsign(y) < 0)
        {
            negg(y); negg(A->ll); negg(A->lr);
        }
        if (gcompg(x,y) < 0)
        {
            gswap(&x,&y);
            gswap(&A->ul,&A->ll);
            gswap(&A->ur,&A->lr);
        }
        if (!isZero(y))
        {
            onestep(x,y,A);
            m /= 2;
            B.ul = BORROW_GIANT();
            B.ur = BORROW_GIANT();
            B.ll = BORROW_GIANT();
            B.lr = BORROW_GIANT();
            itog(1,B.ul);
            itog(0,B.ur);
            itog(0,B.ll);
            itog(1,B.lr);
            hgcd(m,x,y,&B);
            mulmM(&B,A);
            
            RETURN_GIANT(B.ul);
            RETURN_GIANT(B.ur);
            RETURN_GIANT(B.ll);
            RETURN_GIANT(B.lr);
        }
    }
    RETURN_GIANT(x);
    RETURN_GIANT(y);
}
 
 
 
 
static void
ggcd(
    giant   xx,
    giant   yy
)
/* A giant gcd.  Modifies its arguments. */
{
    giant           x = BORROW_GIANT();
    giant           y = BORROW_GIANT();
    GMatrixStruct   A;
 
    gtog(xx,x); gtog(yy,y);
    for(;;)
    {
        fix(&x,&y);
        if (bitlen(y) <= GCDLIMIT )
            break;
        A.ul = BORROW_GIANT();
        A.ur = BORROW_GIANT();
        A.ll = BORROW_GIANT();
        A.lr = BORROW_GIANT();
        itog(1,A.ul);
        itog(0,A.ur);
        itog(0,A.ll);
        itog(1,A.lr);
        hgcd(0,x,y,&A);
        mulvM(&A,x,y);
        RETURN_GIANT(A.ul);
        RETURN_GIANT(A.ur);
        RETURN_GIANT(A.ll);
        RETURN_GIANT(A.lr);
        fix(&x,&y);
        if (bitlen(y) <= GCDLIMIT )
            break;
        modg(y,x);
        gswap(&x,&y);
    }
    bgcdg(x,y);
    gtog(y,yy);
    RETURN_GIANT(x);
    RETURN_GIANT(y);
}
 
void
gcdg(
    giant       x,
    giant       y
)
{
    if (bitlen(y)<= GCDLIMIT)
        bgcdg(x,y);
    else
        ggcd(x,y);
}
 
 
void
cgcdg(
    giant   a,
    giant   v
)
/* Classical Euclid GCD. v becomes gcd(a, v). */
{
    giant   u, r;
 
    absg(v);
    if (isZero(a))
        return;
    
    u = BORROW_GIANT();
    r = BORROW_GIANT();
    
    gtog(a, u);
    absg(u);
    while (!isZero(v))
    {
        gtog(u, r);
        modg(v, r);
        gtog(v, u);
        gtog(r, v);
    }
    gtog(u,v);
    RETURN_GIANT(u);
    RETURN_GIANT(r);
}
 
 
 
 
static void
columnwrite(
    FILE    *filepointer,
    short   *column,
    char    *format,
    short   arg,
    int     newlines
)
/* Used by gwriteln. */
{
    char    outstring[10];
    short   i;
 
    sprintf(outstring,format,arg);
    for (i=0; outstring[i]!=0; ++i)
    {
        if (newlines)
        {
            if (*column >= COLUMNWIDTH)
            {
                fputs("\\\n",filepointer);
                *column = 0;
            }
        }
        fputc(outstring[i],filepointer);
        ++*column;
    }
}
 
 
void
gwrite(
    giant           thegiant,
    FILE            *filepointer,
    int             newlines
)
/* Outputs thegiant to filepointer. Output is terminated by a newline. */
{
    short           column;
    static giant    garbagegiant = NULL;
    unsigned long   *pLongStorage;
    unsigned long   numlongs = (thegiant->giantDigits * 40);    
    unsigned long   *numpointer;
    Boolean         firstdigits=true;
    
    pLongStorage = (unsigned long *)NewPtr(numlongs*sizeof(long));
    
    if (garbagegiant == NULL)
        garbagegiant = newgiantbits(1);
 
    if (isZero(thegiant))
    {
        fputs("0",filepointer);
    }
    else
    {
        numpointer = pLongStorage;
        gtog(thegiant,garbagegiant);
        absg(garbagegiant);
 
        do
        {
            *numpointer = (unsigned short)idivg(10000,garbagegiant);
            ++numpointer;
        }   while (!isZero(garbagegiant));
 
        column = 0;
        numpointer--;
 
        if (gsign(thegiant)<0) {
            columnwrite(filepointer,&column,"-",0, newlines);
        }
        
        do {
            if (firstdigits) {
                columnwrite(filepointer,&column,"%d",*numpointer--,newlines);       
                firstdigits = false;
            } else {
                columnwrite(filepointer,&column,"%04d",*numpointer--,newlines);                 
            }
        } while (numpointer >= pLongStorage);
        
    }
    
    DisposePtr((Ptr)pLongStorage);
}
 
void
gwriteln(
    giant       theg,
    FILE        *filepointer
)
{
    gwrite(theg, filepointer, 1);
    fputc('\n',filepointer);
}
 
 
void
gread(
    giant           theg,
    FILE            *filepointer
)
{
    char            currentchar;
    int             isneg,size,backslash=false,numdigits=0;
    static giant    basetenthousand = NULL;
    static char     *inbuf = NULL;
    static giant    currentmult;
    
    if (basetenthousand == NULL)
        basetenthousand = newgiantbits(1);
    if (currentmult == NULL)
        currentmult = newgiantbits(1);
    if (inbuf == NULL)
        inbuf = (char*)malloc(MAX_DIGITS);
 
    itog(1, currentmult);
    itog(0, theg);
    
    currentchar = (char)fgetc(filepointer);
    if (currentchar=='-')
    {
        isneg=true;
    }
    else
    {
        isneg=false;
        if (currentchar!='+')
            ungetc(currentchar,filepointer);
    }
 
    do
    {
        currentchar = (char)fgetc(filepointer);
        if ((currentchar>='0') && (currentchar<='9'))
        {
            inbuf[numdigits]=currentchar;
            if(++numdigits==MAX_DIGITS)
                break;
            backslash=false;
        }
        else
        {
            if (currentchar=='\\')
                backslash=true;
        }
    } while(((currentchar!=' ') && (currentchar!='\n') &&
                (currentchar!='\t')) || (backslash) );
    if (numdigits)
    {
        size = 0;
        do
        {
            inbuf[numdigits] = 0;
            numdigits-=4;
            if (numdigits<0)
                numdigits=0;
                
            itog((unsigned short)strtol(&inbuf[numdigits],NULL,10), basetenthousand);
            mulg(currentmult, basetenthousand);
            addg(basetenthousand, theg);
            itog(10000, basetenthousand);
            mulg(basetenthousand, currentmult);
 
        } while (numdigits>0);
 
 
        if (isneg) {
            setsign(theg, -1);
        } else {
            setsign(theg, 1);
        }
    }
}
 
void        gdumphex(giant g)
{
    long            i;
    unsigned char   b;
    
    if (g->giantDigits) {       
        if (g->giantSign == -1) {
            printf("-");
        }
 
        for (i=BYTES_PER_VECTOR*(g->giantDigits)-1; i>=0; i--) {
            b = GetNthByte(g, i);
            if (b == 0) {
                printf("00");
            } else if (b<0x10) {
                printf("0%x", b);
            } else {
                printf("%x", b);
            }
        }
    } else {
        printf("0");
    }
}
 
 
 
void
bdivg(
    giant       v,
    giant       u
)
/* u becomes greatest power of two not exceeding u/v. */
{
    int         diff = bitlen(u) - bitlen(v);
    giant       scratch7;
 
    if (diff<0)
    {
        itog(0,u);
        return;
    }
    scratch7 = BORROW_GIANT();
    gtog(v, scratch7);
    gshiftleft(diff,scratch7);
    if (gcompg(u,scratch7) < 0)
        diff--;
    if (diff<0)
    {
        itog(0,u);
        RETURN_GIANT(scratch7);
        return;
    }
    itog(1,u);
    gshiftleft(diff,u);
 
    RETURN_GIANT(scratch7);
}
 
 
 
static giant    u0=NULL, u1=NULL, v0=NULL, v1=NULL;
 
 
static int
binvaux(
    giant   p,
    giant   x
)
/* Binary inverse method. Returns zero if no inverse exists,
 * in which case x becomes GCD(x,p). */
{
    
    giant scratch7;
 
    if (isone(x))
        return(1);
    if (!v0)
        v0 = newgiantbits(1);
    if (!v1)
        v1 = newgiantbits(1);
    if (!u1)
        u1 = newgiantbits(1);
    if (!u0)
        u0 = newgiantbits(1);
    itog(1, v0);
    gtog(x, v1);
    itog(0,x);
    gtog(p, u1);
 
    scratch7 = BORROW_GIANT();
    while(!isZero(v1))
    {
        gtog(u1, u0);
        bdivg(v1, u0);
        gtog(x, scratch7);
        gtog(v0, x);
        mulg(u0, v0);
        subg(v0,scratch7);
        gtog(scratch7, v0);
 
        gtog(u1, scratch7);
        gtog(v1, u1);
        mulg(u0, v1);
        subg(v1,scratch7);
        gtog(scratch7, v1);
    }
    
    RETURN_GIANT(scratch7);
 
    if (!isone(u1))
    {
        gtog(u1,x);
        if(gsign(x)<0) addg(p, x);
            return(0);
    }
    if(gsign(x)<0)
        addg(p, x);
    return(1);
}
 
int
binvg(
    giant   p,
    giant   x
)
{
    modg(p, x);
    return(binvaux(p,x));
}
 
static int
invaux(
    giant   p,
    giant   x
)
/* Returns zero if no inverse exists, in which case x becomes
 * GCD(x,p). */
{
 
    giant scratch7;
    
    if (isone(x))
        return(1);
    if (!v0)
        v0 = newgiantbits(1);
    if (!v1)
        v1 = newgiantbits(1);
    if (!u1)
        u1 = newgiantbits(1);
    if (!u0)
        u0 = newgiantbits(1);
    itog(1,u1);
    gtog(p, v0);
    gtog(x, v1);
    itog(0,x);
 
    scratch7 = BORROW_GIANT();
    while (!isZero(v1))
    {
        gtog(v0, u0);
        divg(v1, u0);
        gtog(u0, scratch7);
        mulg(v1, scratch7);
        subg(v0, scratch7);
        negg(scratch7);
        gtog(v1, v0);
        gtog(scratch7, v1);
        gtog(u1, scratch7);
        mulg(u0, scratch7);
        subg(x, scratch7);
        negg(scratch7);
        gtog(u1,x);
        gtog(scratch7, u1);
    }
 
    RETURN_GIANT(scratch7);
    
    if (!isone(v0))
    {
        gtog(v0,x);
        return(0);
    }
    if(gsign(x)<0)
        addg(p, x);
    return(1);
}
 
 
int
invg(
    giant   p,
    giant   x
)
{
    modg(p, x);
    return(invaux(p,x));
}
 
void
mersennemod (
    int n,
    giant g
)
/* g := g (mod 2^n - 1) */
{
    int the_sign, s;
    giant scratch3 = BORROW_GIANT();
    giant scratch4 = BORROW_GIANT();
    
    if ((the_sign = gsign(g)) < 0) absg(g);
    while (bitlen(g) > n) {
        gtog(g,scratch3);
        gshiftright(n,scratch3);
        addg(scratch3,g);
        gshiftleft(n,scratch3);
        subg(scratch3,g);
    }
    if(!isZero(g)) {
        if ((s = gsign(g)) < 0) absg(g);
        itog(1,scratch3);
        gshiftleft(n,scratch3);
        itog(1,scratch4);
        subg(scratch4,scratch3);
        if(gcompg(g,scratch3) >= 0) subg(scratch3,g);
        if (s < 0) {
            negg(g);
            addg(scratch3,g);
        }
        if (the_sign < 0) {
            negg(g);
            addg(scratch3,g);
        }
    }
    
    RETURN_GIANT(scratch3);
    RETURN_GIANT(scratch4);
}
 
 
 
int
mersenneinvg(
    int     q,
    giant   x
)
{
    int     k;
 
    if (!u0)
        u0 = newgiantbits(1);
    if (!u1)
        u1 = newgiantbits(1);
    if (!v1)
        v1 = newgiantbits(1);
 
    itog(1, u0);
    itog(0, u1);
    itog(1, v1);
    gshiftleft(q, v1);
    subg(u0, v1);
    mersennemod(q, x);
    while (1)
    {
        k = -1;
        if (isZero(x))
        {
            gtog(v1, x);
            return(0);
        }
        while (bitval(x, ++k) == 0)
            ;
 
        gshiftright(k, x);
        if (k)
        {
            gshiftleft(q-k, u0);
            mersennemod(q, u0);
        }
        if (isone(x))
            break;
        addg(u1, u0);
        mersennemod(q, u0);
        negg(u1);
        addg(u0, u1);
        mersennemod(q, u1);
        if (!gcompg(v1,x))
            return(0);
        addg(v1, x);
        negg(v1);
        addg(x, v1);
        mersennemod(q, v1);
    }
    gtog(u0, x);
    mersennemod(q,x);
    return(1);
}
 
void
powermod(
    giant       x,
    int         n,
    giant       g
)
/* x becomes x^n (mod g). */
{
    giant scratch2 = BORROW_GIANT();
    gtog(x, scratch2);
    itog(1, x);
    while (n)
    {
        if (n & 1)
        {
            mulg(scratch2, x);
            modg(g, x);
        }
        n >>= 1;
        if (n)
        {
            squareg(scratch2);
            modg(g, scratch2);
        }
    }
    
    RETURN_GIANT(scratch2);
}
 
 
void
powermodg(
    giant       x,
    giant       n,
    giant       g
)
/* x becomes x^n (mod g). */
{
    int         len, pos;
    giant       scratch2 = BORROW_GIANT();
 
    gtog(x, scratch2);
    itog(1, x);
    len = bitlen(n);
    pos = 0;
    while (1)
    {
        if (bitval(n, pos++))
        {
            mulg(scratch2, x);
            modg(g, x);
        }
        if (pos>=len)
            break;
        squareg(scratch2);
        modg(g, scratch2);
    }
    RETURN_GIANT(scratch2);
}
 
 
void
fermatpowermod(
    giant   x,
    int     n,
    int     q
)
/* x becomes x^n (mod 2^q+1). */
{
    giant scratch2 = BORROW_GIANT();
    
    gtog(x, scratch2);
    itog(1, x);
    while (n)
    {
        if (n & 1)
        {
            mulg(scratch2, x);
            fermatmod(q, x);
        }
        n >>= 1;
        if (n)
        {
            squareg(scratch2);
            fermatmod(q, scratch2);
        }
    }
    RETURN_GIANT(scratch2);
}
 
 
void
fermatpowermodg(
    giant   x,
    giant   n,
    int     q
)
/* x becomes x^n (mod 2^q+1). */
{
    int     len, pos;
    giant   scratch2 = BORROW_GIANT();
 
    gtog(x, scratch2);
    itog(1, x);
    len = bitlen(n);
    pos = 0;
    while (1)
    {
        if (bitval(n, pos++))
        {
            mulg(scratch2, x);
            fermatmod(q, x);
        }
        if (pos>=len)
            break;
        squareg(scratch2);
        fermatmod(q, scratch2);
    }
    RETURN_GIANT(scratch2);
}
 
 
void
mersennepowermod(
    giant   x,
    int     n,
    int     q
)
/* x becomes x^n (mod 2^q-1). */
{
    giant scratch2 = BORROW_GIANT();
 
    gtog(x, scratch2);
    itog(1, x);
    while (n)
    {
        if (n & 1)
        {
            mulg(scratch2, x);
            mersennemod(q, x);
        }
        n >>= 1;
        if (n)
        {
            squareg(scratch2);
            mersennemod(q, scratch2);
        }
    }
    RETURN_GIANT(scratch2);
}
 
 
void
mersennepowermodg(
    giant   x,
    giant   n,
    int     q
)
/* x becomes x^n (mod 2^q-1). */
{
    int     len, pos;
    giant   scratch2 = BORROW_GIANT();
 
    gtog(x, scratch2);
    itog(1, x);
    len = bitlen(n);
    pos = 0;
    while (1)
    {
        if (bitval(n, pos++))
        {
            mulg(scratch2, x);
            mersennemod(q, x);
        }
        if (pos>=len)
            break;
        squareg(scratch2);
        mersennemod(q, scratch2);
    }
    RETURN_GIANT(scratch2);
}
 
 
 
void
fermatmod (
    int             n,
    giant           g
)
/* g := g (mod 2^n + 1). */
{
    int the_sign, s;
    giant scratch3 = BORROW_GIANT();
    
    if ((the_sign = gsign(g)) < 0) absg(g);
    while (bitlen(g) > n) {
        gtog(g,scratch3);
        gshiftright(n,scratch3);
        subg(scratch3,g);
        gshiftleft(n,scratch3);
        subg(scratch3,g);
    }
    if(!isZero(g)) {
        if ((s = gsign(g)) < 0) absg(g);
        itog(1,scratch3);
        gshiftleft(n,scratch3);
        iaddg(1,scratch3);
        if(gcompg(g,scratch3) >= 0) subg(scratch3,g);
        if (s < 0) {
            negg(g);
            addg(scratch3,g);
        }
        if (the_sign < 0) {
            negg(g);
        }
    }
    
    RETURN_GIANT(scratch3);
}
 
 
#if 1
void
smulg(
    unsigned long   i,
    giant           g
)
{
    if (!i) {
        g->giantDigits = 0;
        return;
    }
    
    if (!isZero(g)) {   
        unsigned long topresult = MultVecsByULong(  g->vectors,
                                                    g->giantDigits,
                                                    i);
 
        if (topresult) {
            AddMSDLong(g, topresult);
        }
    }
    
    ASSERT_GVALID(g);
}
#else
void
smulg(
    unsigned short  i,
    giant           g
)
/* g becomes g * i. */
{
    giant   temp = BORROW_GIANT();
    
    itog(i, temp);
    
    mulg(temp, g);
    
    RETURN_GIANT(temp);
 
}
#endif
 
long            icompg(unsigned long i, giant g)
{
    long    result;
 
    giant   temp = BORROW_GIANT();
    
    itog(i, temp);
    result = gcompg(temp, g);   
    
    RETURN_GIANT(temp);
    
    return result;
}
 
 
void        trunctobitlen(long len, giant g)
{
    unsigned long   neededDigits;
    unsigned long   trimbits;
    unsigned char   *pDigit;
    unsigned char   maskByte;
    long            i;
    
    if (bitlen(g) < len) return;
 
    neededDigits = (len + BITS_PER_VECTOR - 1)/BITS_PER_VECTOR;
    
    g->giantDigits = neededDigits;
    
    trimbits = len & 0x7f;
    
    trimbits = BITS_PER_VECTOR - trimbits;
    
    if (trimbits) {
        pDigit = (unsigned char*)&g->vectors[neededDigits-1];
 
        for (i=0; i<trimbits >> 3; i++) {
            pDigit[i] = 0;
        }   
        
        trimbits &= 0x07;
        maskByte = 0xff;
        
        maskByte = maskByte >> trimbits;
        
        pDigit[i] &= maskByte;
        
    }
}
 
 
#if !INLINE_GIANTS
 
/* Returns 1, 0, -1 as a>b, a=b, a<b. */
long gcompg(giant a, giant b)
{
    ASSERT_GVALID(a);
    ASSERT_GVALID(b);
 
    if (a->giantSign > b->giantSign) return 1;
    if (a->giantSign < b->giantSign) return -1;
    
    return a->giantSign * CompareMagnitude(a, b);
}
 
signed long     gtoi (giant g)
{
    GAssert(bitlen(g) <= 32);
    
    if (!g->giantSign) return 0;
    
    return g->giantSign * ((unsigned long *)g->vectors)[LONGS_PER_VECTOR-1];
}
 
/* Returns the giantSign of g: -1, 0, 1. */
long            gsign(giant g)
{
    ASSERT_GVALID(g);
    
    if (!g->giantDigits) return 0;
    
    return g->giantSign;
}
 
void        negg(giant g)
{
    g->giantSign = -g->giantSign;
}
 
void        setsign(giant g, long sign)
{
    if (sign > 0) {
        g->giantSign = 1;
    } else {
        g->giantSign = -1;
    }
}
 
void        absg(giant g)
{
    if (g->giantSign < 0) g->giantSign = 1;
}
 
long isZero(giant g)
{
    ASSERT_GVALID(g);
 
    return (g->giantDigits == 0);
}
 
Boolean isone(giant g)
{
    Boolean                 result = false;
    unsigned long           *pLongPtr;
    unsigned long           accumulator;
    long                    counter;
    
    if (g->giantSign == 1 && g->giantDigits == 1) {
        pLongPtr = (unsigned long*)g->vectors;
        accumulator = 0;
        for (counter=0; counter < LONGS_PER_VECTOR -1 ; counter++) {
            accumulator |= pLongPtr[counter];
        }
        
        // if first three longs are zero
        if (!accumulator) {
            // and last one is one, then it's one
            result = (pLongPtr[counter] == 1);
        }
    }
    
    return result;
 
}
 
Boolean         isOdd(giant g)
{
    return (GetNthByte(g, 0) & 0x01);
}
 
 
#endif //#if INLINE_GIANTS