<?php
define('INT64', 64);
define('INT128', 128);
define('INT256', 256);
define('INT512', 512);
// BigInt represented as string of characters
// First byte is MSB and last byte is LSB
function BigIntNew($BitWidth): string
{
return str_repeat(chr(0), $BitWidth >> 3);
}
function BigIntNewAs($A): string
{
return str_repeat(chr(0), strlen($A));
}
function BigIntCheck(string $A, string $B): void
{
if (strlen($A) != strlen($B))
throw new Exception('BigInt parameters bit width doesn\'t match.');
}
function BigIntAdd(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
$Carry = 0;
for ($I = strlen($Result) - 1; $I >= 0; $I--)
{
$Sum = ord($A[$I]) + ord($B[$I]) + $Carry;
$Result[$I] = chr($Sum);
if ($Sum > 255) $Carry = 1;
else $Carry = 0;
}
return $Result;
}
function BigIntSub(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
$Carry = 0;
for ($I = strlen($Result) - 1; $I >= 0; $I--)
{
$Sum = ord($A[$I]) - ord($B[$I]) - $Carry;
$Result[$I] = chr($Sum);
if ($Sum < 0) $Carry = 1;
else $Carry = 0;
}
return $Result;
}
function BigIntInc(string $A): string
{
$Result = BigIntNewAs($A);
$Carry = 1;
for ($I = strlen($Result) - 1; $I >= 0; $I--)
{
$Sum = ord($A[$I]) + $Carry;
$Result[$I] = chr($Sum);
if ($Sum > 255) $Carry = 1;
else $Carry = 0;
}
return $Result;
}
function BigIntDec(string $A): string
{
$Result = BigIntNewAs($A);
$Carry = 1;
for ($I = strlen($Result) - 1; $I >= 0; $I--)
{
$Sum = ord($A[$I]) - $Carry;
$Result[$I] = chr($Sum);
if ($Sum < 0) $Carry = 1;
else $Carry = 0;
}
return $Result;
}
function BigIntNeg(string $A): string
{
return BigIntInc(BigIntNot($A));
}
function BigIntIsNeg(string $A): string
{
return ord($A[0]) & 0x80;
}
function BigIntEqual(string $A, string $B): bool
{
return $A == $B; // Simple string comparison
}
function BigIntNotEqual(string $A, string $B): bool
{
return $A != $B; // Simple string comparison
}
function BigIntGreater(string $A, string $B): bool
{
return BigIntIsNeg(BigIntSub($B, $A));
}
function BigIntGreaterOrEqual(string $A, string $B): bool
{
return BigIntIsNeg(BigIntSub($B, $A)) or BigIntEqual($A, $B);
}
function BigIntLesser(string $A, string $B): bool
{
return BigIntIsNeg(BigIntSub($A, $B));
}
function BigIntLesserOrEqual(string $A, string $B): bool
{
return BigIntIsNeg(BigIntSub($A, $B)) or BigIntEqual($A, $B);
}
function BigIntMul(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
while (BigIntGreater($B, BigIntNewAs($A)))
{
$Result = BigIntAdd($Result, $A);
$B = BigIntDec($B);
}
return $Result;
}
function BigIntDiv(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
while (BigIntGreaterOrEqual($A, $B))
{
$A = BigIntSub($A, $B);
$Result = BigIntInc($Result);
}
return $Result;
}
function BigIntMod(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
while (BigIntGreaterOrEqual($A, $B))
{
$A = BigIntSub($A, $B);
$Result = BigIntInc($Result);
}
return $A;
}
function BigIntAnd(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
for ($I = 0; $I < strlen($Result); $I++)
{
$Result[$I] = chr(ord($A[$I]) & ord($B[$I]));
}
return $Result;
}
function BigIntOr(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
for ($I = 0; $I < strlen($Result); $I++)
{
$Result[$I] = chr(ord($A[$I]) | ord($B[$I]));
}
return $Result;
}
function BigIntXor(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = BigIntNewAs($A);
for ($I = 0; $I < strlen($Result); $I++)
{
$Result[$I] = chr(ord($A[$I]) ^ ord($B[$I]));
}
return $Result;
}
function BigIntNot(string $A): string
{
$Result = BigIntNewAs($A);
for ($I = 0; $I < strlen($Result); $I++)
{
$Result[$I] = chr(~ord($A[$I]));
}
return $Result;
}
function BigIntShl1(string $A): string
{
$Result = BigIntNewAs($A);
for ($I = 0; $I < strlen($Result) - 1; $I++)
{
$Result[$I] = chr((ord($A[$I]) << 1) | (ord($A[$I + 1]) >> 7));
}
$Result[strlen($Result) - 1] = chr(ord($A[strlen($Result) - 1]) << 1);
return $Result;
}
function BigIntShl(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = $A;
for ($I = 0; $I < Int128ToInt($B); $I++)
{
$Result = BigIntShl1($Result);
}
return $Result;
}
function BigIntShr1(string $A): string
{
$Result = BigIntNewAs($A);
for ($I = strlen($Result) - 1; $I > 0; $I--)
{
$Result[$I] = chr((ord($A[$I]) >> 1) | ((ord($A[$I - 1]) & 1) << 7));
}
$Result[0] = chr(ord($A[0]) >> 1);
return $Result;
}
function BigIntShr(string $A, string $B): string
{
BigIntCheck($A, $B);
$Result = $A;
for ($I = 0; $I < Int128ToInt($B); $I++)
{
$Result = BigIntShr1($Result);
}
return $Result;
}
function BigIntToHex(string $A): string
{
$Result = '';
for ($I = 0; $I < strlen($A); $I++)
{
$Result .= str_pad(dechex(ord($A[$I])), 2, '0', STR_PAD_LEFT);
}
return $Result;
}
function HexToBigInt(string $A, int $BitWidth): string
{
$Result = BigIntNew($BitWidth);
$I = strlen($A) - 1;
$Index = 15;
while (($I >= 0) && ($Index >= 0))
{
$Result[$Index] = chr(ord($Result[$Index]) | hexdec($A[$I]));
$I--;
if ($I >= 0)
{
$Result[$Index] = chr(ord($Result[$Index]) | (hexdec($A[$I]) << 4));
$I--;
}
$Index--;
}
return $Result;
}
function BigIntToBin(string $A): string
{
$Result = '';
for ($I = 0; $I < strlen($A); $I++)
{
$Result .= str_pad(decbin(ord($A[$I])), 8, '0', STR_PAD_LEFT);
}
return $Result;
}
function BinToBigInt(string $A, int $BitWidth): string
{
$Result = BigIntNew($BitWidth);
$I = strlen($A) - 1;
$Index = strlen($Result) - 1;
while (($I >= 0) && ($Index >= 0))
{
for ($J = 0; $J < 7; $J++)
{
if ($I >= 0)
{
$Result[$Index] = chr(ord($Result[$Index]) | (bindec($A[$I]) << $J));
$I--;
}
}
$Index--;
}
return $Result;
}
function BigIntToInt(string $A): int
{
// 32-bit int support
return ord($A[15]) | (ord($A[14]) << 8) | (ord($A[13]) << 16) | (ord($A[12]) << 24);
}
function IntToBigInt(int $A, int $BitWidth): string
{
$Result = BigIntNew($BitWidth);
// 32-bit int support
$Result[15] = chr($A & 0xff);
$Result[14] = chr(($A >> 8) & 0xff);
$Result[13] = chr(($A >> 16) & 0xff);
$Result[12] = chr(($A >> 24) & 0xff);
return $Result;
}
function BigIntToDec(string $A): string
{
$BitWidth = strlen($A) << 3;
$Result = '';
$Zero = BigIntNewAs($A);
while (BigIntGreater($A, $Zero))
{
$Result = strval(BigIntToInt(BigIntMod($A, IntToBigInt(10, $BitWidth)))).$Result;
$A = BigIntDiv($A, IntToBigInt(10, $BitWidth));
}
return $Result;
}
function DecToBigInt(string $A, int $BitWidth): string
{
$Result = BigIntNew($BitWidth);
$I = 0;
while ($I < strlen($A))
{
$Result = BigIntMul($Result, IntToBigInt(10, $BitWidth));
$Result = BigIntAdd($Result, IntToBigInt(intval($A[$I]), $BitWidth));
$I++;
}
return $Result;
}