/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
/*****************************************************************************
*
*   $/Plasma20/Sources/Plasma/NucleusLib/pnUtils/Private/pnUtBigNum.cpp
*   
***/

#include "../Pch.h"
#pragma hdrstop


/****************************************************************************
*
*   Constants and macros
*
***/

const unsigned      VAL_BITS = 8 * sizeof(BigNum::Val);
const BigNum::DVal VAL_RANGE = ((BigNum::DVal)1) << VAL_BITS;

#define  LOW(dval)        ((Val)(dval))
#define  HIGH(dval)       ((Val)((dval) / VAL_RANGE))
#define  PACK(low, high)  ((DVal)((high) * VAL_RANGE + (low)))

#define  ALLOC_TEMP(struct, count)  \
    (struct).UseTempAlloc((Val *)_alloca((count) * sizeof(Val)), count)


/****************************************************************************
*
*   BigNum private methods
*
***/

//===========================================================================
void BigNum::SetVal (unsigned index, Val value) {
    ARRAY(Val)::operator[](index) = value;
}

//===========================================================================
void BigNum::SetVal (unsigned index, DVal value, Val * carry) {
    ARRAY(Val)::operator[](index) = LOW(value);
    *carry = HIGH(value);
}

//===========================================================================
void BigNum::Trim (unsigned count) {
    ASSERT(count <= Count());
    while (count && !ARRAY(Val)::operator[](count - 1))
        --count;
    SetCountFewer(count);
}

//===========================================================================
BigNum * BigNum::UseTempAlloc (Val * ptr, unsigned count) {
    m_isTemp = true;
    AttachTemp(ptr, count);
    return this;
}


/****************************************************************************
*
*   BigNum public methods
*
***/

//===========================================================================
BigNum::BigNum () :
    m_isTemp(false)
{
}

//===========================================================================
BigNum::BigNum (const BigNum & a) :
    m_isTemp(false)
{
    Set(a);
}

//===========================================================================
BigNum::BigNum (unsigned a) :
    m_isTemp(false)
{
    Set(a);
}

//===========================================================================
BigNum::BigNum (unsigned bytes, const void * data) :
    m_isTemp(false)
{
    FromData(bytes, data);
}

//===========================================================================
BigNum::BigNum (const wchar str[], Val radix) :
    m_isTemp(false)
{
    FromStr(str, radix);
}

//===========================================================================
BigNum::~BigNum () {
    if (m_isTemp)
        Detach();
}

//===========================================================================
void BigNum::Add (const BigNum & a, Val b) {
    // this = a + b

    const unsigned count = a.Count();
    GrowToCount(count + 1, true);
    unsigned index = 0;
    Val      carry = b;
    for (; index < count; ++index)
        SetVal(index, (DVal)((DVal)a[index] + (DVal)carry), &carry);
    if (carry)
        SetVal(index++, carry);
    Trim(index);

}

//===========================================================================
void BigNum::Add (const BigNum & a, const BigNum & b) {
    // this = a + b

    const unsigned aCount = a.Count();
    const unsigned bCount = b.Count();
    const unsigned count  = aCount + bCount;
    GrowToCount(count + 1, true);
    unsigned index = 0;
    Val      carry = 0;
    for (; index < count; ++index) {
        Val aVal = (index < aCount) ? a[index] : (Val)0;
        Val bVal = (index < bCount) ? b[index] : (Val)0;
        SetVal(index, (DVal)((DVal)aVal + (DVal)bVal + (DVal)carry), &carry);
    }
    if (carry)
        SetVal(index++, carry);
    Trim(index);

}

//===========================================================================
int BigNum::Compare (Val a) const {
    // -1 if (this <  a)
    //  0 if (this == a)
    //  1 if (this >  a)

    // Handle the case where this number has more digits than the comparand
    const unsigned count = Count();
    ASSERT(!count || (*this)[count - 1]);
    if (count > 1)
        return 1;

    // Handle the case where this number has fewer digits than the comparand
    if (!count)
        return a ? -1 : 0;

    // Handle the case where both numbers share the same number of digits
    Val thisVal = (*this)[0];
    return (thisVal > a) ? 1 : (thisVal < a) ? -1 : 0;

}

//===========================================================================
int BigNum::Compare (const BigNum & a) const {
    // -1 if (this <  a)
    //  0 if (this == a)
    //  1 if (this >  a)

    // Handle the case where this number has more digits than the comparand
    const unsigned thisCount = Count();
    const unsigned compCount = a.Count();
    ASSERT(!thisCount || (*this)[thisCount - 1]);
    ASSERT(!compCount || a[compCount - 1]);
    if (thisCount > compCount)
        return 1;

    // Handle the case where this number has fewer digits than the comparand
    if (thisCount < compCount)
        return -1;

    // Handle the case where both numbers share the same number of digits
    for (unsigned index = thisCount; index--; ) {
        Val thisVal = (*this)[index];
        Val compVal = a[index];
        if (thisVal == compVal)
            continue;
        return (thisVal > compVal) ? 1 : -1;
    }
    return 0;

}

//===========================================================================
void BigNum::Div (const BigNum & a, Val b, Val * remainder) {
    // this = a / b, remainder = a % b

    const unsigned count = a.Count();
    SetCount(count);
    *remainder = 0;
    for (unsigned index = count; index--; ) {
        DVal value = PACK(a[index], *remainder);
        SetVal(index, (Val)(value / b));
        *remainder = (Val)(value % b);
    }
    Trim(count);

}

//===========================================================================
void BigNum::Div (const BigNum & a, const BigNum & b, BigNum * remainder) {
    // this = a / b, remainder = a % b
    // either this or remainder may be nil

    ASSERT(this != remainder);

    // Check for division by zero
    ASSERT(b.Count() && b[b.Count() - 1]);

    // Normalize the operands so that the highest bit is set in the most
    // significant word of the denominator
    const unsigned shift = 8 * sizeof(Val) - MathHighBitPos(b[b.Count() - 1]) - 1;
    BigNum aaBuffer;
    BigNum bbBuffer;
    BigNum * aa = shift ? ALLOC_TEMP(aaBuffer, a.Count() + 1) : (BigNum *)&a;
    BigNum * bb = shift ? ALLOC_TEMP(bbBuffer, b.Count() + 1) : (BigNum *)&b;
    if (shift) {
        aa->Shl(a, shift);
        bb->Shl(b, shift);
    }

    // Perform the division
    DivNormalized(*aa, *bb, remainder);

    // Denormalize the remainder
    if (remainder)
        remainder->Shr(*remainder, shift);

}

//===========================================================================
void BigNum::DivNormalized (const BigNum & a, const BigNum & b, BigNum * remainder) {
    // this = a / b, remainder = a % b
    // either this or remainder may be nil
    // high bit of b must be set

    ASSERT(this != remainder);

    // Check for division by zero
    ASSERT(b.Count() && b[b.Count() - 1]);

    // Verify that the operands are normalized
    ASSERT(MathHighBitPos(b[b.Count() - 1]) == 8 * sizeof(Val) - 1);

    // Handle the case where the denominator is greater than the numerator
    if ((b.Count() > a.Count()) || (b.Compare(a) > 0)) {
        if (remainder && (remainder != &a))
            remainder->Set(a);
        if (this)
            ZeroCount();
        return;
    }

    // Create a distinct buffer for the denominator if necessary
    BigNum   denomTemp;
    BigNum * denom = ((&b != this) && (&b != remainder)) ? (BigNum *)&b : ALLOC_TEMP(denomTemp, b.Count());
    denom->Set(b);

    // Store the numerator into the remainder buffer
    BigNum   numerTemp;
    BigNum * numer = remainder ? remainder : ALLOC_TEMP(numerTemp, a.Count());
    numer->Set(a);

    // Prepare the destination buffer
    const unsigned numerCount = numer->Count();
    const unsigned denomCount = denom->Count();
    if (this)
        this->SetCount(numerCount + 1 - denomCount);

    // Calculate the quotient one word at a time
    DVal t = (DVal)((DVal)(*denom)[denomCount - 1] + (DVal)1);
    for (unsigned quotientIndex = numerCount + 1 - denomCount; quotientIndex--; ) {
        
        // Calculate the approximate value of the next quotient word, 
        // erring on the side of underestimation
        Val low  = (*numer)[quotientIndex + denomCount - 1];
        Val high = (quotientIndex + denomCount < numerCount) ? (*numer)[quotientIndex + denomCount] : (Val)0;
        ASSERT(high < t);
        Val quotient = (Val)(PACK(low, high) / t);

        // Calculate the product of the denominator and this quotient word
        // (using zero for all lower quotient words) and subtract the product
        // from the current numerator
        if (quotient) {
            Val borrow = 0;
            Val carry  = 0;
            unsigned denomIndex;
            for (denomIndex = 0; denomIndex != denomCount; ++denomIndex) {
                DVal product = (DVal)(Mul((*denom)[denomIndex], quotient) + carry);
                carry = HIGH(product);
                numer->SetVal(quotientIndex + denomIndex, (DVal)((DVal)(*numer)[quotientIndex + denomIndex] - (DVal)LOW(product) - (DVal)borrow), &borrow);
                borrow = (Val)((Val)0 - (Val)borrow);
            }
            if (quotientIndex + denomCount != numerCount) {
                numer->SetVal(quotientIndex + denomCount, (DVal)((DVal)(*numer)[quotientIndex + denomIndex] - (DVal)carry - (DVal)borrow), &borrow);
                carry = 0;
            }
            ASSERT(!carry);
            ASSERT(!borrow);
        }

        // Check whether we underestimated the quotient word, and adjust
        // it if necessary
        for (;;) {

            // Test whether the current numerator is still greater than or
            // equal to the denominator
            if ((quotientIndex + denomCount == numerCount) || !(*numer)[quotientIndex + denomCount]) {
                bool numerLess = false;
                for (unsigned denomIndex = denomCount; !numerLess && denomIndex--; ) {
                    Val numerVal = (*numer)[quotientIndex + denomIndex];
                    Val denomVal = (*denom)[denomIndex];
                    numerLess = (numerVal < denomVal);
                    if (numerVal != denomVal)
                        break;
                }
                if (numerLess)
                    break;
            }

            // Increment the quotient by one, and correct the current 
            // numerator for this adjustment by subtracting the denominator
            ++quotient;
            Val borrow = 0;
            for (unsigned denomIndex = 0; denomIndex != denomCount; ++denomIndex) {
                numer->SetVal(quotientIndex + denomIndex, (DVal)((DVal)(*numer)[quotientIndex + denomIndex] - (DVal)(*denom)[denomIndex] - (DVal)borrow), &borrow);
                borrow = (Val)((Val)0 - (Val)borrow);
            }
            if (borrow)
                numer->SetVal(quotientIndex + denomCount, (DVal)((DVal)(*numer)[quotientIndex + denomCount] - (DVal)borrow), &borrow);
            ASSERT(!borrow);

        }
        ASSERT((quotientIndex + denomCount == numerCount) || !(*numer)[quotientIndex + denomCount]);

        // Store the final quotient word
        if (this)
            this->SetVal(quotientIndex, quotient);

    }

    // The final remainder is the remaining portion of the numerator
    if (remainder) {
        ASSERT(remainder == numer);
        remainder->Trim(denomCount);
    }

    // Trim the result
    if (this)
        this->Trim(numerCount + 1 - denomCount);

}

//===========================================================================
void BigNum::FromData (unsigned bytes, const void * data) {
    ASSERT(data || !bytes);

    // Calculate the number of words required to hold the data
    unsigned count = (bytes + sizeof(Val) - 1) / sizeof(Val);
    SetCount(count);

    // Fill in whole words
    unsigned index  = 0;
    unsigned offset = 0;
    for (; offset + sizeof(Val) <= bytes; ++index, offset += sizeof(Val))
        SetVal(index, *(const Val *)((const byte *)data + offset));

    // Fill in the final partial word
    if (offset < bytes) {
        Val value = 0;
        MemCopy(&value, (const byte *)data + offset, bytes - offset);
        SetVal(index, value);
    }

}

//===========================================================================
void BigNum::FromStr (const wchar str[], Val radix) {
    ASSERT(str);    

    // Decode the prefix
    if (str[0] == L'0') {
        if ((str[1] == L'x') || (str[1] == L'X')) {
            str += 2;
            if (!radix)
                radix = 16;
        }
        else if ((str[1] >= L'0') && (str[1] <= L'9')) {
            str += 1;
            if (!radix)
                radix = 8;
        }
        else if (!radix) {
            ZeroCount();
            return;
        }
    }
    else if (!radix)
        radix = 10;

    // Decode the number
    ZeroCount();
    for (; *str; ++str) {

        // Decode the next character
        Val value;
        if ((*str >= L'0') && (*str <= '9'))
            value = (Val)(*str - L'0');
        else if ((*str >= L'a') && (*str <= L'z'))
            value = (Val)(*str + 10 - L'a');
        else if ((*str >= L'A') && (*str <= L'Z'))
            value = (Val)(*str + 10 - L'A');
        else
            break;
        if (value >= radix)
            break;

        // Apply it to the result
        Mul(*this, radix);
        Add(*this, value);

    }

}

//===========================================================================
void BigNum::Gcd (const BigNum & a, const BigNum & b) {

    // Allocate working copies of a and b
    BigNum aa;
    BigNum bb;
    unsigned maxCount = max(a.Count(), b.Count());
    ALLOC_TEMP(aa, maxCount + 1);
    ALLOC_TEMP(bb, maxCount + 1);
    aa.Set(a);
    bb.Set(b);

    // Find the greatest common denominator using Euclid's algorithm
    Set(bb);
    while (aa.Count()) {
        Set(aa);
        aa.Mod(bb, aa);
        bb.Set(*this);
    }

}

//===========================================================================
const void * BigNum::GetData (unsigned * bytes) const {
    if (bytes)
        *bytes = Bytes();
    return Ptr();
}

//===========================================================================
unsigned BigNum::HighBitPos () const {
    // returns the position of the highest set bit, or -1 if no bits are set

    for (unsigned index = Count(); index--; ) {
        Val val = (*this)[index];
        if (!val)
            continue;
        return index * 8 * sizeof(Val) + MathHighBitPos(val);
    }

    return (unsigned)-1;
}

//===========================================================================
bool BigNum::InverseMod (const BigNum & a, const BigNum & b) {
    // finds value for this such that (a ^ -1) == (this mod b)
    // returns false if a has no inverse modulo b

    // Verify that a and b are nonzero
    ASSERT(a.Count());
    ASSERT(b.Count());

    // Verify that a is less than b
    ASSERT(a.Compare(b) < 0);

    // Verify that either a or b is odd. If both are even then they cannot
    // possibly be relatively prime, so there cannot be a solution.
    if (!(a.IsOdd() || b.IsOdd()))
        return false;

    // Allocate buffers for intermediate values
    BigNum uArray[3];
    BigNum tArray[3];
    BigNum * u = uArray;
    BigNum * t = tArray;

    // Find the inverse using the extended Euclidean algorithm
    u[0].SetOne();
    u[1].SetZero();
    u[2].Set(b);
    t[0].Set(a);
    t[1].Sub(b, 1);
    t[2].Set(a);
    do {
        do {
            if (!u[2].IsOdd()) {
                if (u[0].IsOdd() || u[1].IsOdd()) {
                    u[0].Add(u[0], a);
                    u[1].Add(u[1], b);
                }
                u[0].Shr(u[0], 1);
                u[1].Shr(u[1], 1);
                u[2].Shr(u[2], 1);
            }
            if (!t[2].IsOdd() || (u[2].Compare(t[2]) < 0))
                SWAP(u, t);
        } while (!u[2].IsOdd());

        while ((u[0].Compare(t[0]) < 0) || (u[1].Compare(t[1]) < 0)) {
            u[0].Add(u[0], a);
            u[1].Add(u[1], b);
        }

        u[0].Sub(u[0], t[0]);
        u[1].Sub(u[1], t[1]);
        u[2].Sub(u[2], t[2]);
    } while (t[2].Count());

    while ((u[0].Compare(a) >= 0) && (u[1].Compare(b) >= 0)) {
        u[0].Sub(u[0], a);
        u[1].Sub(u[1], b);
    }

    // If the greatest common denominator is not one then there is no
    // solution
    if (u[2].Compare(1))
        return false;

    // Return the solution
    Sub(b, u[1]);
    return true;

}

//===========================================================================
bool BigNum::IsMultiple (Val a) const {
    // returns true if (this % a) == 0

    DVal remainder = 0;
    for (unsigned index = Count(); index--; )
        remainder = (DVal)(PACK((*this)[index], remainder) % a);

    return !remainder;
}

//===========================================================================
bool BigNum::IsOdd () const {
    // returns true if this is an odd number

    return Count() ? (*this)[0] & 1 : false;
}

//===========================================================================
bool BigNum::IsPrime () const {
    // returns true if there is a strong likelihood that this is prime

    // Verify that the number is odd, or is exactly equal to two
    if (!Count() || (!((*this)[0] & 1) && ((Count() > 1) || ((*this)[0] > 2))))
        return false;

    // Verify that the number is not evenly divisible by a small prime
    static const Val smallPrimes[] = {3, 5, 7, 11};
    unsigned loop;
    for (loop = 0; loop != arrsize(smallPrimes); ++loop)
        if (IsMultiple(smallPrimes[loop]))
            return false;
    if (Compare(smallPrimes[arrsize(smallPrimes)-1]) <= 0)
        return true;

    // Rabin-Miller Test

    // Calculate b, where b is the number of times 2 divides (this - 1)
    BigNum this_1;
    ALLOC_TEMP(this_1, Count());
    this_1.Sub(*this, 1);
    const unsigned b = this_1.LowBitPos();

    // Calculate m, such that this == 1 + 2 ^ b * m
    BigNum m;
    ALLOC_TEMP(m, Count());
    m.Shr(this_1, b);

    // For a number of witnesses, test whether the witness demonstrates this
    // number to be composite via Fermat's Little Theorem, or has a 
    // nontrivial square root mod n
    static const Val witnesses[] = {3, 5, 7};
    BigNum z;
    ALLOC_TEMP(z, 2 * (Count() + 1));
    for (loop = 0; loop != arrsize(witnesses); ++loop) {

        // Initialize z to (witness ^ m % this)
        z.PowMod(witnesses[loop], m, *this);

        // This passes the test if (z == 1)
        if (!z.Compare(1))
            continue;

        for (unsigned j = 0; z.Compare(this_1); ) {

            // This fails the test if we reach b iterations.
            ++j;
            if (j == b)
                return false;

            // Square z. This fails the test if z mod this equals 1.
            z.MulMod(z, z, *this);
            if (!z.Compare(1))
                return false;

        }      

    }

    return true;
}

//===========================================================================
unsigned BigNum::LowBitPos () const {
    // returns the position of the lowest set bit, or -1 if no bits are set

    for (unsigned index = 0, count = Count(); index < count; ++index) {
        Val val = (*this)[index];
        if (!val)
            continue;
        for (unsigned bit = 0; ; ++bit)
            if (val & (1 << bit))
                return index * 8 * sizeof(Val) + bit;
    }

    return (unsigned)-1;
}

//===========================================================================
void BigNum::Mod (const BigNum & a, const BigNum & b) {
    // this = a % b

    ((BigNum *)nil)->Div(a, b, this);
}

//===========================================================================
void BigNum::ModNormalized (const BigNum & a, const BigNum & b) {
    // this = a % b
    // high bit of b must be set

    ((BigNum *)nil)->DivNormalized(a, b, this);
}

//===========================================================================
BigNum::DVal BigNum::Mul (BigNum::Val a, BigNum::Val b) {
    // returns a * b

    return (DVal)a * (DVal)b;
}

//===========================================================================
void BigNum::Mul (const BigNum & a, Val b) {
    // this = a * b

    const unsigned count = a.Count();
    GrowToCount(count + 1, true);
    unsigned index = 0;
    Val      carry = 0;
    for (; index < count; ++index)
        SetVal(index, (DVal)(Mul(a[index], b) + carry), &carry);
    if (carry)
        SetVal(index++, carry);
    Trim(index);

}

//===========================================================================
void BigNum::Mul (const BigNum & a, const BigNum & b) {
    // this = a * b

    const unsigned aCount = a.Count();
    const unsigned bCount = b.Count();
    const unsigned count  = aCount + bCount;
    SetCount(count);
    if (!count)
        return;

    // We perform the multiplication from left to right, so that we don't
    // overwrite any operand words before they're used in the case that
    // the destination is not distinct from either of the operands
    SetVal(count - 1, 0);
    for (unsigned index = count - 1; index--; ) {
        
        // Iterate every combination of aIndex + bIndex that adds up to 
        // index, and sum the products of those words
        DVal value = 0;
        const unsigned aStart = (index < bCount) ? 0 : (index + 1 - bCount);
        const unsigned aTerm  = min(index + 1, aCount);
        for (unsigned aIndex = aStart; aIndex != aTerm; ++aIndex) {

            // Accumulate the product of this pair of words
            value = (DVal)(Mul(a[aIndex], b[index - aIndex]) + value);

            // If the product exceeds the word size, apply carry
            Val carry = HIGH(value);
            for (unsigned carryIndex = index + 1; carry; ++carryIndex)
                SetVal(carryIndex, (DVal)((DVal)(*this)[carryIndex] + (DVal)carry), &carry);
            value = LOW(value);

        }

        // Store the sum of products as the final value for index
        SetVal(index, LOW(value));

    }

    Trim(count);

}

//===========================================================================
void BigNum::MulMod (const BigNum & a, const BigNum & b, const BigNum & c) {
    // this = a * b % c

    if (this != &c) {
        Mul(a, b);
        Mod(*this, c);
    }
    else {
        BigNum temp;
        ALLOC_TEMP(temp, a.Count() + b.Count());
        temp.Mul(a, b);
        Mod(temp, c);
    }

}

//===========================================================================
void BigNum::PowMod (Val a, const BigNum & b, const BigNum & c) {
    // this = a ^ b % c

    // Verify that b is distinct from this
    BigNum bbBuffer;
    const BigNum & bb = (&b != this) ? b : bbBuffer;
    if (&bb != &b) {
        ALLOC_TEMP(bbBuffer, b.Count());
        bbBuffer.Set(b);
    }

    // Generate a table which may allow us to process two bits at once
    Val aMult[4] = {
        1,
        a,
        (Val)(a * a),
        (Val)(a * a * a)
    };
    bool overflow = (aMult[2] < a) || (aMult[3] < a) || (c.Compare(aMult[3]) <= 0);

    // Normalize the denominator so that the high bit is set. The result
    // will be built shifted an equivalent amount.
    const unsigned shift = 8 * sizeof(Val) - MathHighBitPos(c[c.Count() - 1]) - 1;
    BigNum cc;
    ALLOC_TEMP(cc, c.Count() + 1);
    cc.Shl(c, shift);

    // Perform the exponentiation from left to right two bits at a time
    if (!overflow) {
        SetBits(shift, 1);
        bool anySet = false;
        for (unsigned index = bb.Count(); index--; )
            for (unsigned bit = 8 * sizeof(Val); bit; ) {
                bit -= 2;

                if (anySet) {
                    Square(*this);
                    Shr(*this, shift);
                    ModNormalized(*this, cc);
                    Square(*this);
                    Shr(*this, shift);
                    ModNormalized(*this, cc);
                }

                unsigned entry = (bb[index] >> bit) & 3;
                if (entry) {
                    Mul(*this, aMult[entry]);
                    ModNormalized(*this, cc);
                    anySet = true;
                }

            }
    }

    // Perform the exponentiation from left to right a single bit at a time
    else {
        SetBits(shift, 1);
        bool anySet = false;
        for (unsigned index = bb.Count(); index--; )
            for (unsigned bit = 8 * sizeof(Val); bit--; ) {

                if (anySet) {
                    Square(*this);
                    ModNormalized(*this, cc);
                }

                if (bb[index] & (1 << bit)) {
                    Mul(*this, a);
                    ModNormalized(*this, cc);
                    anySet = true;
                }

            }
    }

    // Denormalize the result
    Shr(*this, shift);

}

//===========================================================================
void BigNum::PowMod (const BigNum & a, const BigNum & b, const BigNum & c) {
    // this = a ^ b % c

    // Verify that a and b are distinct from this
    BigNum distinctTemp;
    const BigNum & aa = (&a != this) ? a : distinctTemp;
    const BigNum & bb = (&b != this) ? b : distinctTemp;
    if ((&aa != &a) || (&bb != &b)) {
        ALLOC_TEMP(distinctTemp, Count());
        distinctTemp.Set(*this);
    }

    // Generate a table which will allow us to process two bits at once
    BigNum a2;
    BigNum a3;
    ALLOC_TEMP(a2, 2 * aa.Count() + 1);
    ALLOC_TEMP(a3, 3 * aa.Count() + 1);
    a2.Mul(aa, aa);
    a2.Mod(a2, c);
    a3.Mul(aa, a2);
    a3.Mod(a3, c);
    const BigNum * aMult[] = {
        nil,
        &aa,
        &a2,
        &a3
    };

    // Normalize the denominator so that the high bit is set. The result
    // will be built shifted an equivalent amount.
    const unsigned shift = 8 * sizeof(Val) - MathHighBitPos(c[c.Count() - 1]) - 1;
    BigNum cc;
    ALLOC_TEMP(cc, c.Count() + 1);
    cc.Shl(c, shift);

    // Perform the exponentiation from left to right two bits at a time
    SetBits(shift, 1);
    bool anySet = false;
    for (unsigned index = bb.Count(); index--; )
        for (unsigned bit = 8 * sizeof(Val); bit; ) {
            bit -= 2;

            if (anySet) {
                Square(*this);
                Shr(*this, shift);
                ModNormalized(*this, cc);
                Square(*this);
                Shr(*this, shift);
                ModNormalized(*this, cc);
            }

            unsigned entry = (bb[index] >> bit) & 3;
            if (entry) {
                Mul(*this, *aMult[entry]);
                ModNormalized(*this, cc);
                anySet = true;
            }

        }

    // Denormalize the result
    Shr(*this, shift);

}

//===========================================================================
void BigNum::Rand (const BigNum & a, BigNum * seed) {
    // this = random number less than a

    ASSERT(seed != &a);
    ASSERT(seed != this);

    // Verify that a is distinct from this
    BigNum distinctTemp;
    const BigNum & aa = (&a != this) ? a : distinctTemp;
    if (&aa != &a) {
        ALLOC_TEMP(distinctTemp, a.Count());
        distinctTemp.Set(a);
    }

    // Count the number of bits in a
    unsigned bits = aa.HighBitPos() + 1;

    for (;;) {

        // Generate a random number with the same number of bits as a
        Rand(bits, seed);

        // Check whether the number is less than a
        if (Compare(aa) < 0)
            break;

    }        

}

//===========================================================================
void BigNum::Rand (unsigned bits, BigNum * seed) {
    // this = random number with bits or fewer bits

    ASSERT(seed != this);

    // Prepare the output buffer
    const unsigned count = (bits + 8 * sizeof(Val) - 1) / (8 * sizeof(Val));
    SetCount(count);
    if (!count)
        return;

    // Prepare the seed
    unsigned seedCount = seed->Count();
    if (!seedCount)
        seed->SetCount(++seedCount);
    unsigned seedIndex = 0;

    // Produce a random number with the correct number of words
    for (unsigned index = 0; index < count; ++index) {

        // Read the next word of the seed
        dword randValue = (*seed)[seedIndex] ^ ((index == seedIndex) ? 0x075bd924 : 0);

        // Produce one word of randomness, 16 bits at a time
        Val value = 0;
        for (unsigned bit = 0; bit < 8 * sizeof(Val); bit += 16) {
            const dword A = 0xbc8f;
            const dword Q = 0xadc8;
            const dword R = 0x0d47;

            dword div  = randValue / Q;
            randValue  = A * (randValue - Q * div) - R * div;
            randValue &= 0x7fffffff;

            value |= (randValue & 0xffff) << bit;
        }

        // Store the random word
        SetVal(index, value);

        // Update the seed and move to the seed next word
        seed->SetVal(seedIndex, (Val)randValue);
        if (++seedIndex >= seedCount)
            seedIndex = 0;

    }

    // Mask the final word to contain the correct number of bits
    Val mask = (Val)((Val)-1 >> (count * 8 * sizeof(Val) - bits));
    SetVal(count - 1, (Val)((*this)[count - 1] & mask));

    // Trim the result
    Trim(count);

    // Rotate the seed so the next unused seed word will be the first seed
    // word used in the next random operation
    if (seedIndex) {
        BigNum saved;
        ALLOC_TEMP(saved, seedCount);
        saved.Set(*seed);

        for (unsigned index = 0; index < seedCount; ++index)
            (*seed)[index] = saved[(index + seedIndex) % seedCount];
    }

}

//===========================================================================
void BigNum::RandPrime (unsigned bits, BigNum * seed) {

    // Calculate the required number of words to hold the generated number
    unsigned count = (bits + 8 * sizeof(Val) - 1) / (8 * sizeof(Val));

    // For large bit counts, calculate the prime number as 2 * q * n + 1,
    // where q is a random prime with fewer bits, and n is a random number
    // chosen as follows:
    // n >= (2 ^ (bits - 1) - 1) / (2 * q)
    // n <  (2 ^ bits - 1) / (2 * q)
    if (bits > 128) {

        // Choose a prime number q, and multiply it by 2
        BigNum q_2;
        ALLOC_TEMP(q_2, count / 2 + 2);
        q_2.RandPrime(bits / 2, seed);
        q_2.Mul(q_2, 2);

        // Calculate the lower bound
        BigNum lowerBound;
        ALLOC_TEMP(lowerBound, count + 1);
        lowerBound.SetBits(0, bits - 1);
        lowerBound.Div(lowerBound, q_2, nil);

        // Calculate the upper bound
        BigNum upperBound;
        ALLOC_TEMP(upperBound, count + 1);
        upperBound.SetBits(0, bits);
        upperBound.Div(upperBound, q_2, nil);

        // Calculate the number of bits in the upper bound
        unsigned upperBoundBits = upperBound.HighBitPos() + 1;

        for (;;) {

            // Choose a random number between the lower and upper bounds
            Rand(upperBoundBits, seed);
            if (Compare(upperBound) >= 0)
                continue;
            if (Compare(lowerBound) < 0)
                continue;

            // Calculate 2 * q * n + 1
            Mul(*this, q_2);
            Add(*this, 1);

            // Test whether the result is prime
            if (IsPrime())
                break;

        }

    }

    // For small bit counts, choose a random number with the requested
    // number of bits, then keep incrementing it until we find a prime
    else {

        // Define the upper bound for a number with the requested number 
        // of bits
        BigNum bound;
        ALLOC_TEMP(bound, count + 1);
        bound.SetBits(bits, 1);

        for (;;) {

            // Choose a random number with (bits - 1) bits
            Rand(bits - 1, seed);

            // Subtract it from the upper bound to generate a number with 
            // the high bit set
            Sub(bound, *this);

            // Keep incrementing the number until we find a prime
            if (!IsOdd())
                Add(*this, 1);
            while (!IsPrime())
                Add(*this, 2);

            // If the number reached or exceeded the upper bound, try again
            if (Compare(bound) < 0)
                break;

        }

    }

}

//===========================================================================
void BigNum::Set (const BigNum & a) {
    // this = a

    if (&a == this)
        return;

    const unsigned count = a.Count();
    SetCount(count);
    for (unsigned index = count; index--; )
        SetVal(index, a[index]);

}

//===========================================================================
void BigNum::Set (unsigned a) {
    // this = a
        
    ZeroCount();
    if (a)
        for (unsigned index = 0; ; ++index) {
            SetCount(index + 1);
            SetVal(index, LOW(a));
            if (a < VAL_RANGE)
                break;
            a = (unsigned)(a / VAL_RANGE);
        }

}

//===========================================================================
void BigNum::SetBits (unsigned setBitsOffset, unsigned setBitsCount) {
    // this = binary [1...][0...], where 'setBitsOffset' is the number of
    // zero bits and 'setBitsCount' is the number of one bits

    if (!setBitsCount) {
        ZeroCount();
        return;
    }

    const unsigned setBitsTerm  = setBitsOffset + setBitsCount - 1;
    const unsigned bitsPerWord  = 8 * sizeof(Val);
    const unsigned firstSetWord = setBitsOffset / bitsPerWord;
    const unsigned lastSetWord  = (setBitsOffset + setBitsCount - 1) / bitsPerWord;
    Val firstSetMask = (Val)((Val)-1 << (setBitsOffset % bitsPerWord));
    Val lastSetMask  = (Val)((Val)-1 >> (bitsPerWord - setBitsTerm % bitsPerWord - 1));
    if (firstSetWord == lastSetWord)
        firstSetMask = lastSetMask = (Val)(firstSetMask & lastSetMask);

    SetCount(lastSetWord + 1);
    unsigned index = 0;
    for (; index < firstSetWord; ++index)
        SetVal(index, 0);
    SetVal(index++, firstSetMask);
    if (firstSetWord == lastSetWord)
        return;
    for (; index < lastSetWord; ++index)
        SetVal(index, (Val)-1);
    SetVal(index, lastSetMask);

}

//===========================================================================
void BigNum::SetOne () {
    // this = 1

    SetCount(1);
    SetVal(0, 1);
}

//===========================================================================
void BigNum::SetZero () {
    // this = 0

    ZeroCount();
}

//===========================================================================
void BigNum::Shl (const BigNum & a, unsigned b) {
    // this = a << b

    ASSERT(b < 8 * sizeof(Val));
    if (!b) {
        Set(a);
        return;
    }
    const unsigned bInv = 8 * sizeof(Val) - b;

    const unsigned count = a.Count();
    SetCount(count + 1);
    Val curr = 0;
    for (unsigned index = count; index >= 1; --index) {
        Val next = a[index - 1];
        SetVal(index, (Val)((next >> bInv) | (curr << b)));
        curr = next;
    }
    SetVal(0, (Val)(curr << b));
    Trim(count + 1);

}

//===========================================================================
void BigNum::Shr (const BigNum & a, unsigned b) {
    // this = a >> b

    ASSERT(b < 8 * sizeof(Val));
    if (!b) {
        Set(a);
        return;
    }
    const unsigned bInv = 8 * sizeof(Val) - b;

    const unsigned count = a.Count();
    SetCount(count);
    if (!count)
        return;

    Val curr = a[0];
    for (unsigned index = 0; index + 1 < count; ++index) {
        Val next = a[index + 1];
        SetVal(index, (Val)((next << bInv) | (curr >> b)));
        curr = next;
    }
    SetVal(count - 1, (Val)(curr >> b));
    Trim(count);

}

//===========================================================================
void BigNum::Square (const BigNum & a) {
    // this = a * a

    const unsigned aCount = a.Count();
    const unsigned count  = 2 * aCount;
    SetCount(count);
    if (!count)
        return;

    // We perform the multiplication from left to right, so that we don't
    // overwrite any operand words before they're used in the case that
    // the destination is not distinct from the operand
    SetVal(count - 1, 0);
    for (unsigned index = count - 1; index--; ) {
        
        // Iterate every combination of source indices that adds up to 
        // index, and sum the products of those words
        DVal     value  = 0;
        unsigned aIndex = (index < aCount) ? 0 : (index + 1 - aCount);
        unsigned bIndex;
        for (; (int)((bIndex = index - aIndex) - aIndex) >= 0; ++aIndex) {

            // Calculate the product of this pair of words
            DVal product = Mul(a[aIndex], a[bIndex]);

            // Add the product to the sum. If it exceeds the word size,
            // apply carry.
            value = (DVal)(value + product);
            Val carry = HIGH(value);
            unsigned carryIndex;
            for (carryIndex = index + 1; carry; ++carryIndex)
                SetVal(carryIndex, (DVal)((DVal)(*this)[carryIndex] + (DVal)carry), &carry);
            value = LOW(value);

            // If this pair of words should be multiplied twice, add the
            // product again.
            if (aIndex == bIndex)
                continue;
            value = (DVal)(value + product);
            carry = HIGH(value);
            for (carryIndex = index + 1; carry; ++carryIndex)
                SetVal(carryIndex, (DVal)((DVal)(*this)[carryIndex] + (DVal)carry), &carry);
            value = LOW(value);

        }

        // Store the sum of products as the final value for index
        SetVal(index, LOW(value));

    }

    Trim(count);

}

//===========================================================================
void BigNum::Sub (const BigNum & a, Val b) {
    // this = a - b

    const unsigned count = a.Count();
    SetCount(count);
    Val borrow = b;
    unsigned index;
    for (index = 0; index < count; ++index) {
        SetVal(index, (DVal)((DVal)a[index] - (DVal)borrow), &borrow);
        borrow = (Val)((Val)0 - (Val)borrow);
    }
    ASSERT(!borrow);
    Trim(index);

}

//===========================================================================
void BigNum::Sub (const BigNum & a, const BigNum & b) {
    // this = a - b

    const unsigned count  = a.Count();
    const unsigned bCount = b.Count();
    GrowToCount(count, true);
    Val borrow = 0;
    unsigned index;
    for (index = 0; index < count; ++index) {
        Val bVal = (index < bCount) ? b[index] : (Val)0;
        SetVal(index, (DVal)((DVal)a[index] - (DVal)bVal - (DVal)borrow), &borrow);
        borrow = (Val)((Val)0 - (Val)borrow);
    }
    ASSERT(!borrow);
    Trim(index);

}

//===========================================================================
void BigNum::ToStr (BigNum * buffer, Val radix) const {
    ASSERT(this != buffer);

    // Calculate the number of characters in the prefix
    unsigned prefixChars;
    if (radix == 16)
        prefixChars = 2;
    else if (radix == 8)
        prefixChars = 1;
    else
        prefixChars = 0;

    // Preallocate space for the output string
    unsigned charsPerVal = 0;
    for (Val testVal = (Val)-1; testVal; testVal = (Val)(testVal / radix))
        ++charsPerVal;
    const unsigned charsTotal = max(1, Count()) * charsPerVal + prefixChars + 1;
    buffer->SetCount((charsTotal * sizeof(wchar) + sizeof(Val) - 1) / sizeof(Val));

    // Build the prefix
    wchar * prefix = (wchar *)buffer->Ptr();
    if (prefixChars) {
        prefix[0] = L'0';
        if (radix == 16)
            prefix[1] = L'x';
        else
            ASSERT(prefixChars == 1);
    }

    // Build the number starting with the least significant digit
    wchar * start = prefix + prefixChars;
    wchar * curr  = start;
    BigNum work;
    ALLOC_TEMP(work, Count());
    work.Set(*this);
    do {

        // Extract the next value
        Val remainder;
        work.Div(work, radix, &remainder);

        // Encode it as a character in the output string
        if (remainder >= 10)
            *curr++ = (wchar)(L'a' + (unsigned)remainder - 10);
        else
            *curr++ = (wchar)(L'0' + (unsigned)remainder);

    } while (work.Count());
    *curr = 0;

    // Reverse the order of the output string
    for (wchar * left = start, * right = curr - 1; left < right; ++left, --right)
        SWAP(*left, *right);

}
