You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1385 lines
42 KiB
1385 lines
42 KiB
/*==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/>. |
|
|
|
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); |
|
|
|
}
|
|
|