blob: 9908eaab59401bacea9115d130bc0465760b5ce4 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "CommonDataStructuresPch.h"
#include "DataStructures/BigInt.h"
#include "Common/NumberUtilitiesBase.h"
#include "Common/NumberUtilities.h"
namespace Js
{
BigInt & BigInt::operator= (BigInt &bi)
{
AssertMsg(false, "can't assign BigInts");
return *this;
}
#if DBG
void BigInt::AssertValid(bool fCheckVal)
{
Assert(m_cluMax >= kcluMaxInit);
Assert(m_prglu != 0);
Assert(m_clu >= 0 && m_clu <= m_cluMax);
Assert(!fCheckVal || 0 == m_clu || 0 != m_prglu[m_clu - 1]);
Assert((m_prglu == m_rgluInit) == (m_cluMax == kcluMaxInit));
}
#endif
BigInt::BigInt(void)
{
m_cluMax = kcluMaxInit;
m_clu = 0;
m_prglu = m_rgluInit;
AssertBi(this);
}
BigInt::~BigInt(void)
{
if (m_prglu != m_rgluInit)
free(m_prglu);
}
int32 BigInt::Clu(void)
{
return m_clu;
}
uint32 BigInt::Lu(int32 ilu)
{
AssertBi(this);
Assert(ilu < m_clu);
return m_prglu[ilu];
}
bool BigInt::FResize(int32 clu)
{
AssertBiNoVal(this);
uint32 *prglu;
if (clu <= m_cluMax)
return true;
clu += clu;
if (m_prglu == m_rgluInit)
{
if ((INT_MAX / sizeof(uint32) < clu) || (NULL == (prglu = (uint32 *)malloc(clu * sizeof(uint32)))))
return false;
if (0 < m_clu)
js_memcpy_s(prglu, clu * sizeof(uint32), m_prglu, m_clu * sizeof(uint32));
}
else if (NULL == (prglu = (uint32 *)realloc(m_prglu, clu * sizeof(uint32))))
return false;
m_prglu = prglu;
m_cluMax = clu;
AssertBiNoVal(this);
return true;
}
bool BigInt::FInitFromRglu(uint32 *prglu, int32 clu)
{
AssertBi(this);
Assert(clu >= 0);
Assert(prglu != 0);
if (clu > m_cluMax && !FResize(clu))
return false;
m_clu = clu;
if (clu > 0)
js_memcpy_s(m_prglu, m_clu * sizeof(uint32), prglu, clu * sizeof(uint32));
AssertBi(this);
return true;
}
bool BigInt::FInitFromBigint(BigInt *pbiSrc)
{
AssertBi(this);
AssertBi(pbiSrc);
Assert(this != pbiSrc);
return FInitFromRglu(pbiSrc->m_prglu, pbiSrc->m_clu);
}
template <typename EncodedChar>
bool BigInt::FInitFromDigits(const EncodedChar *prgch, int32 cch, int32 *pcchDig)
{
AssertBi(this);
Assert(cch >= 0);
Assert(prgch != 0);
Assert(pcchDig != 0);
uint32 luAdd;
uint32 luMul;
int32 clu = (cch + 8) / 9;
const EncodedChar *pchLim = prgch + cch;
if (clu > m_cluMax && !FResize(clu))
return false;
m_clu = 0;
luAdd = 0;
luMul = 1;
for (*pcchDig = cch; prgch < pchLim; prgch++)
{
if (*prgch == '.')
{
(*pcchDig)--;
continue;
}
Assert(NumberUtilities::IsDigit(*prgch));
if (luMul == 1000000000)
{
AssertVerify(FMulAdd(luMul, luAdd));
luMul = 1;
luAdd = 0;
}
luMul *= 10;
luAdd = luAdd * 10 + *prgch - '0';
}
Assert(1 < luMul);
AssertVerify(FMulAdd(luMul, luAdd));
AssertBi(this);
return true;
}
bool BigInt::FMulAdd(uint32 luMul, uint32 luAdd)
{
AssertBi(this);
Assert(luMul != 0);
uint32 luT;
uint32 *plu = m_prglu;
uint32 *pluLim = plu + m_clu;
for (; plu < pluLim; plu++)
{
*plu = NumberUtilities::MulLu(*plu, luMul, &luT);
if (luAdd)
luT += NumberUtilities::AddLu(plu, luAdd);
luAdd = luT;
}
if (0 == luAdd)
goto LDone;
if (m_clu >= m_cluMax && !FResize(m_clu + 1))
return false;
m_prglu[m_clu++] = luAdd;
LDone:
AssertBi(this);
return true;
}
bool BigInt::FMulPow5(int32 c5)
{
AssertBi(this);
Assert(c5 >= 0);
const uint32 k5to13 = 1220703125;
int32 clu = (c5 + 12) / 13;
uint32 luT;
if (0 == m_clu || 0 == c5)
return true;
if (m_clu + clu > m_cluMax && !FResize(m_clu + clu))
return false;
for (; c5 >= 13; c5 -= 13)
AssertVerify(FMulAdd(k5to13, 0));
if (c5 > 0)
{
for (luT = 5; --c5 > 0; )
luT *= 5;
AssertVerify(FMulAdd(luT, 0));
}
AssertBi(this);
return true;
}
bool BigInt::FShiftLeft(int32 cbit)
{
AssertBi(this);
Assert(cbit >= 0);
int32 ilu;
int32 clu;
uint32 luExtra;
if (0 == cbit || 0 == m_clu)
return true;
clu = cbit >> 5;
cbit &= 0x001F;
if (cbit > 0)
{
ilu = m_clu - 1;
luExtra = m_prglu[ilu] >> (32 - cbit);
for (; ; ilu--)
{
m_prglu[ilu] <<= cbit;
if (0 == ilu)
break;
m_prglu[ilu] |= m_prglu[ilu - 1] >> (32 - cbit);
}
}
else
luExtra = 0;
if (clu > 0 || 0 != luExtra)
{
// Make sure there's enough room.
ilu = m_clu + (0 != luExtra) + clu;
if (ilu > m_cluMax && !FResize(ilu))
return false;
if (clu > 0)
{
// Shift the uint32s.
memmove(m_prglu + clu, m_prglu, m_clu * sizeof(uint32));
memset(m_prglu, 0, clu * sizeof(uint32));
m_clu += clu;
}
// Throw on the extra one.
if (0 != luExtra)
m_prglu[m_clu++] = luExtra;
}
AssertBi(this);
return true;
}
void BigInt::ShiftLusRight(int32 clu)
{
AssertBi(this);
Assert(clu >= 0);
if (clu >= m_clu)
{
m_clu = 0;
AssertBi(this);
return;
}
if (clu > 0)
{
memmove(m_prglu, m_prglu + clu, (m_clu - clu) * sizeof(uint32));
m_clu -= clu;
}
AssertBi(this);
}
void BigInt::ShiftRight(int32 cbit)
{
AssertBi(this);
Assert(cbit >= 0);
int32 ilu;
int32 clu = cbit >> 5;
cbit &= 0x001F;
if (clu > 0)
ShiftLusRight(clu);
if (cbit == 0 || m_clu == 0)
{
AssertBi(this);
return;
}
for (ilu = 0; ; )
{
m_prglu[ilu] >>= cbit;
if (++ilu >= m_clu)
{
// Last one.
if (0 == m_prglu[ilu - 1])
m_clu--;
break;
}
m_prglu[ilu - 1] |= m_prglu[ilu] << (32 - cbit);
}
AssertBi(this);
}
int BigInt::Compare(BigInt *pbi)
{
AssertBi(this);
AssertBi(pbi);
int32 ilu;
if (m_clu > pbi->m_clu)
return 1;
if (m_clu < pbi->m_clu)
return -1;
if (0 == m_clu)
return 0;
#pragma prefast(suppress:__WARNING_LOOP_ONLY_EXECUTED_ONCE,"noise")
for (ilu = m_clu - 1; m_prglu[ilu] == pbi->m_prglu[ilu]; ilu--)
{
if (0 == ilu)
return 0;
}
Assert(ilu >= 0 && ilu < m_clu);
Assert(m_prglu[ilu] != pbi->m_prglu[ilu]);
return (m_prglu[ilu] > pbi->m_prglu[ilu]) ? 1 : -1;
}
bool BigInt::FAdd(BigInt *pbi)
{
AssertBi(this);
AssertBi(pbi);
Assert(this != pbi);
int32 cluMax, cluMin;
int32 ilu;
int wCarry;
if ((cluMax = m_clu) < (cluMin = pbi->m_clu))
{
cluMax = pbi->m_clu;
cluMin = m_clu;
if (cluMax > m_cluMax && !FResize(cluMax + 1))
return false;
}
wCarry = 0;
for (ilu = 0; ilu < cluMin; ilu++)
{
if (0 != wCarry)
wCarry = NumberUtilities::AddLu(&m_prglu[ilu], wCarry);
wCarry += NumberUtilities::AddLu(&m_prglu[ilu], pbi->m_prglu[ilu]);
}
if (m_clu < pbi->m_clu)
{
for (; ilu < cluMax; ilu++)
{
m_prglu[ilu] = pbi->m_prglu[ilu];
if (0 != wCarry)
wCarry = NumberUtilities::AddLu(&m_prglu[ilu], wCarry);
}
m_clu = cluMax;
}
else
{
for (; 0 != wCarry && ilu < cluMax; ilu++)
wCarry = NumberUtilities::AddLu(&m_prglu[ilu], wCarry);
}
if (0 != wCarry)
{
if (m_clu >= m_cluMax && !FResize(m_clu + 1))
return false;
m_prglu[m_clu++] = wCarry;
}
AssertBi(this);
return true;
}
void BigInt::Subtract(BigInt *pbi)
{
AssertBi(this);
AssertBi(pbi);
Assert(this != pbi);
int32 ilu;
int wCarry;
uint32 luT;
if (m_clu < pbi->m_clu)
goto LNegative;
wCarry = 1;
for (ilu = 0; (ilu < pbi->m_clu) && (ilu < pbi->m_cluMax); ilu++)
{
Assert(wCarry == 0 || wCarry == 1);
luT = pbi->m_prglu[ilu];
// NOTE: We should really do:
// wCarry = AddLu(&m_prglu[ilu], wCarry);
// wCarry += AddLu(&m_prglu[ilu], ~luT);
// The only case where this is different than
// wCarry = AddLu(&m_prglu[ilu], ~luT + wCarry);
// is when luT == 0 and 1 == wCarry, in which case we don't
// need to add anything and wCarry should still be 1, so we can
// just skip the operations.
if (0 != luT || 0 == wCarry)
wCarry = NumberUtilities::AddLu(&m_prglu[ilu], ~luT + wCarry);
}
while ((0 == wCarry) && (ilu < m_clu) && (ilu < m_cluMax))
wCarry = NumberUtilities::AddLu(&m_prglu[ilu], 0xFFFFFFFF);
if (0 == wCarry)
{
LNegative:
// pbi was bigger than this.
AssertMsg(false, "Who's subtracting to negative?");
m_clu = 0;
}
else if (ilu == m_clu)
{
// Trim off zeros.
while (--ilu >= 0 && 0 == m_prglu[ilu])
;
m_clu = ilu + 1;
}
AssertBi(this);
}
int BigInt::DivRem(BigInt *pbi)
{
AssertBi(this);
AssertBi(pbi);
Assert(this != pbi);
int32 ilu, clu;
int wCarry;
int wQuo;
int wT;
uint32 luT, luHi, luLo;
clu = pbi->m_clu;
Assert(m_clu <= clu);
if ((m_clu < clu) || (clu <= 0))
return 0;
// Get a lower bound on the quotient.
wQuo = (int)(m_prglu[clu - 1] / (pbi->m_prglu[clu - 1] + 1));
Assert(wQuo >= 0 && wQuo <= 9);
// Handle 0 and 1 as special cases.
switch (wQuo)
{
case 0:
break;
case 1:
Subtract(pbi);
break;
default:
luHi = 0;
wCarry = 1;
for (ilu = 0; ilu < clu; ilu++)
{
Assert(wCarry == 0 || wCarry == 1);
// Compute the product.
luLo = NumberUtilities::MulLu(wQuo, pbi->m_prglu[ilu], &luT);
luHi = luT + NumberUtilities::AddLu(&luLo, luHi);
// Subtract the product. See note in BigInt::Subtract.
if (0 != luLo || 0 == wCarry)
wCarry = NumberUtilities::AddLu(&m_prglu[ilu], ~luLo + wCarry);
}
Assert(1 == wCarry);
Assert(ilu == clu);
// Trim off zeros.
while (--ilu >= 0 && 0 == m_prglu[ilu])
;
m_clu = ilu + 1;
}
if (wQuo < 9 && (wT = Compare(pbi)) >= 0)
{
// Quotient was off too small (by one).
wQuo++;
if (wT == 0)
m_clu = 0;
else
Subtract(pbi);
}
Assert(Compare(pbi) < 0);
return wQuo;
}
double BigInt::GetDbl(void)
{
double dbl;
uint32 luHi, luLo;
uint32 lu1, lu2, lu3;
int32 ilu;
int cbit;
switch (m_clu)
{
case 0:
return 0;
case 1:
return m_prglu[0];
case 2:
dbl = m_prglu[1];
NumberUtilities::LuHiDbl(dbl) += 0x02000000;
return dbl + m_prglu[0];
}
Assert(3 <= m_clu);
if (m_clu > 32)
{
// Result is infinite.
NumberUtilities::LuHiDbl(dbl) = 0x7FF00000;
NumberUtilities::LuLoDbl(dbl) = 0;
return dbl;
}
lu1 = m_prglu[m_clu - 1];
lu2 = m_prglu[m_clu - 2];
lu3 = m_prglu[m_clu - 3];
Assert(0 != lu1);
cbit = 31 - NumberUtilities::CbitZeroLeft(lu1);
if (cbit == 0)
{
luHi = lu2;
luLo = lu3;
}
else
{
luHi = (lu1 << (32 - cbit)) | (lu2 >> cbit);
// Or 1 if there are any remaining nonzero bits in lu3, so we take
// them into account when rounding.
luLo = (lu2 << (32 - cbit)) | (lu3 >> cbit) | (0 != (lu3 << (32 - cbit)));
}
// Set the mantissa bits.
NumberUtilities::LuHiDbl(dbl) = luHi >> 12;
NumberUtilities::LuLoDbl(dbl) = (luHi << 20) | (luLo >> 12);
// Set the exponent field.
NumberUtilities::LuHiDbl(dbl) |= (0x03FF + cbit + (m_clu - 1) * 0x0020) << 20;
// Do IEEE rounding.
if (luLo & 0x0800)
{
if ((luLo & 0x07FF) || (NumberUtilities::LuLoDbl(dbl) & 1))
{
if (0 == ++NumberUtilities::LuLoDbl(dbl))
++NumberUtilities::LuHiDbl(dbl);
}
else
{
// If there are any non-zero bits in m_prglu from 0 to m_clu - 4, round up.
for (ilu = m_clu - 4; ilu >= 0; ilu--)
{
if (0 != m_prglu[ilu])
{
if (0 == ++NumberUtilities::LuLoDbl(dbl))
++NumberUtilities::LuHiDbl(dbl);
break;
}
}
}
}
return dbl;
}
template bool BigInt::FInitFromDigits<char16>(const char16 *prgch, int32 cch, int32 *pcchDig);
template bool BigInt::FInitFromDigits<utf8char_t>(const utf8char_t *prgch, int32 cch, int32 *pcchDig);
}