value = $base === true ? $value : BigInteger::getGmp($value, $base); } public static function createSafe($value = 0, $base = 10) { try { return new BigInteger($value, $base); } catch (\Exception $e) { return false; } } public static function isGmp($var) { if (is_resource($var)) { return get_resource_type($var) == "GMP integer"; } if (class_exists("GMP") && $var instanceof \GMP) { return true; } return false; } public static function getGmp($value = 0, $base = 10) { if ($value instanceof BigInteger) { return $value->value; } if (BigInteger::isGmp($value)) { return $value; } $type = gettype($value); if ($type == "integer") { $gmp = gmp_init($value); if ($gmp === false) { throw new \Exception("Cannot initialize"); } return $gmp; } if ($type == "string") { if ($base != 2 && $base != 10 && $base != 16 && $base != 256) { throw new \Exception("Unsupported BigInteger base"); } if ($base == 256) { $value = bin2hex($value); $base = 16; } $level = error_reporting(); error_reporting(0); $gmp = gmp_init($value, $base); error_reporting($level); if ($gmp === false) { throw new \Exception("Cannot initialize"); } return $gmp; } throw new \Exception("Unsupported value, only string and integer are allowed, receive " . $type . ($type == "object" ? ", class: " . get_class($value) : "")); } public function toDec() { return gmp_strval($this->value, 10); } public function toHex() { $hex = gmp_strval($this->value, 16); return strlen($hex) % 2 == 1 ? "0". $hex : $hex; } public function toBytes() { return hex2bin($this->toHex()); } public function toBase($base) { if ($base < 2 || $base > 62) { throw new \Exception("Invalid base"); } return gmp_strval($this->value, $base); } public function toBits() { return gmp_strval($this->value, 2); } public function toString($base = 10) { if ($base == 2) { return $this->toBits(); } if ($base == 10) { return $this->toDec(); } if ($base == 16) { return $this->toHex(); } if ($base == 256) { return $this->toBytes(); } return $this->toBase($base); } public function __toString() { return $this->toString(); } public function toNumber() { return gmp_intval($this->value); } public function add($x) { return new BigInteger(gmp_add($this->value, BigInteger::getGmp($x)), true); } public function sub($x) { return new BigInteger(gmp_sub($this->value, BigInteger::getGmp($x)), true); } public function mul($x) { return new BigInteger(gmp_mul($this->value, BigInteger::getGmp($x)), true); } public function div($x) { return new BigInteger(gmp_div_q($this->value, BigInteger::getGmp($x)), true); } public function divR($x) { return new BigInteger(gmp_div_r($this->value, BigInteger::getGmp($x)), true); } public function divQR($x) { $res = gmp_div_qr($this->value, BigInteger::getGmp($x)); return array(new BigInteger($res[0], true), new BigInteger($res[1], true)); } public function mod($x) { return new BigInteger(gmp_mod($this->value, BigInteger::getGmp($x)), true); } public function gcd($x) { return new BigInteger(gmp_gcd($this->value, BigInteger::getGmp($x)), true); } public function modInverse($x) { $res = gmp_invert($this->value, BigInteger::getGmp($x)); return $res === false ? false : new BigInteger($res, true); } public function pow($x) { return new BigInteger(gmp_pow($this->value, (new BigInteger($x))->toNumber()), true); } public function powMod($x, $n) { return new BigInteger(gmp_powm($this->value, BigInteger::getGmp($x), BigInteger::getGmp($n)), true); } public function abs() { return new BigInteger(gmp_abs($this->value), true); } public function neg() { return new BigInteger(gmp_neg($this->value), true); } public function binaryAnd($x) { return new BigInteger(gmp_and($this->value, BigInteger::getGmp($x)), true); } public function binaryOr($x) { return new BigInteger(gmp_or($this->value, BigInteger::getGmp($x)), true); } public function binaryXor($x) { return new BigInteger(gmp_xor($this->value, BigInteger::getGmp($x)), true); } public function setbit($index, $bitOn = true) { $cpy = gmp_init(gmp_strval($this->value, 16), 16); gmp_setbit($cpy, $index, $bitOn); return new BigInteger($cpy, true); } public function testbit($index) { return gmp_testbit($this->value, $index); } public function scan0($start) { return gmp_scan0($this->value, $start); } public function scan1($start) { return gmp_scan1($this->value, $start); } public function cmp($x) { return gmp_cmp($this->value, BigInteger::getGmp($x)); } public function equals($x) { return $this->cmp($x) === 0; } public function sign() { return gmp_sign($this->value); } } } else if (S_MATH_BIGINTEGER_MODE == "bcmath") { if (!extension_loaded("bcmath")) { throw new \Exception("Extension bcmath not loaded"); } class BigInteger{ public static $chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv"; public $value; public function __construct($value = 0, $base = 10) { $this->value = $base === true ? $value : BigInteger::getBC($value, $base); } public static function createSafe($value = 0, $base = 10) { try { return new BigInteger($value, $base); } catch (\Exception $e) { return false; } } public static function checkBinary($str) { $len = strlen($str); for ($i = 0; $i < $len; $i++) { $c = ord($str[$i]); if (($i != 0 || $c != 45) && ($c < 48 || $c > 49)) { return false; } } return true; } public static function checkDecimal($str) { $len = strlen($str); for ($i = 0; $i < $len; $i++) { $c = ord($str[$i]); if (($i != 0 || $c != 45) && ($c < 48 || $c > 57)) { return false; } } return true; } public static function checkHex($str) { $len = strlen($str); for ($i = 0; $i < $len; $i++) { $c = ord($str[$i]); if (($i != 0 || $c != 45) && ($c < 48 || $c > 57) && ($c < 65 || $c > 70) && ($c < 97 || $c > 102)) { return false; } } return true; } public static function getBC($value = 0, $base = 10) { if ($value instanceof BigInteger) { return $value->value; } $type = gettype($value); if ($type == "integer") { return strval($value); } if ($type == "string") { if ($base == 2) { $value = str_replace(" ", "", $value); if (!BigInteger::checkBinary($value)) { throw new \Exception("Invalid characters"); } $minus = $value[0] == "-"; if ($minus) { $value = substr($value, 1); } $len = strlen($value); $m = 1; $res = "0"; for ($i = $len - 1; $i >= 0; $i -= 8) { $h = $i - 7 < 0 ? substr($value, 0, $i + 1) : substr($value, $i - 7, 8); $res = bcadd($res, bcmul(bindec($h), $m, 0), 0); $m = bcmul($m, "256", 0); } return ($minus ? "-" : "") . $res; } if ($base == 10) { $value = str_replace(" ", "", $value); if (!BigInteger::checkDecimal($value)) { throw new \Exception("Invalid characters"); } return $value; } if ($base == 16) { $value = str_replace(" ", "", $value); if (!BigInteger::checkHex($value)) { throw new \Exception("Invalid characters"); } $minus = $value[0] == "-"; if ($minus) { $value = substr($value, 1); } $len = strlen($value); $m = 1; $res = "0"; for ($i = $len - 1; $i >= 0; $i -= 2) { $h = $i == 0 ? "0" . substr($value, 0, 1) : substr($value, $i - 1, 2); $res = bcadd($res, bcmul(hexdec($h), $m, 0), 0); $m = bcmul($m, "256", 0); } return ($minus ? "-" : "") . $res; } if ($base == 256) { $len = strlen($value); $m = 1; $res = "0"; for ($i = $len - 1; $i >= 0; $i -= 6) { $h = $i - 5 < 0 ? substr($value, 0, $i + 1) : substr($value, $i - 5, 6); $res = bcadd($res, bcmul(base_convert(bin2hex($h), 16, 10), $m, 0), 0); $m = bcmul($m, "281474976710656", 0); } return $res; } throw new \Exception("Unsupported BigInteger base"); } throw new \Exception("Unsupported value, only string and integer are allowed, receive " . $type . ($type == "object" ? ", class: " . get_class($value) : "")); } public function toDec() { return $this->value; } public function toHex() { return bin2hex($this->toBytes()); } public function toBytes() { $value = ""; $current = $this->value; if ($current[0] == "-") { $current = substr($current, 1); } while (bccomp($current, "0", 0) > 0) { $temp = bcmod($current, "281474976710656"); $value = hex2bin(str_pad(base_convert($temp, 10, 16), 12, "0", STR_PAD_LEFT)) . $value; $current = bcdiv($current, "281474976710656", 0); } return ltrim($value, chr(0)); } public function toBase($base) { if ($base < 2 || $base > 62) { throw new \Exception("Invalid base"); } $value = ''; $current = $this->value; $base = BigInteger::getBC($base); if ($current[0] == '-') { $current = substr($current, 1); } while (bccomp($current, '0', 0) > 0) { $v = bcmod($current, $base); $value = BigInteger::$chars[$v] . $value; $current = bcdiv($current, $base, 0); } return $value; } public function toBits() { $bytes = $this->toBytes(); $res = ""; $len = strlen($bytes); for ($i = 0; $i < $len; $i++) { $b = decbin(ord($bytes[$i])); $res .= strlen($b) != 8 ? str_pad($b, 8, "0", STR_PAD_LEFT) : $b; } $res = ltrim($res, "0"); return strlen($res) == 0 ? "0" : $res; } public function toString($base = 10) { if ($base == 2) { return $this->toBits(); } if ($base == 10) { return $this->toDec(); } if ($base == 16) { return $this->toHex(); } if ($base == 256) { return $this->toBytes(); } return $this->toBase($base); } public function __toString() { return $this->toString(); } public function toNumber() { return intval($this->value); } public function add($x) { return new BigInteger(bcadd($this->value, BigInteger::getBC($x), 0), true); } public function sub($x) { return new BigInteger(bcsub($this->value, BigInteger::getBC($x), 0), true); } public function mul($x) { return new BigInteger(bcmul($this->value, BigInteger::getBC($x), 0), true); } public function div($x) { return new BigInteger(bcdiv($this->value, BigInteger::getBC($x), 0), true); } public function divR($x) { return new BigInteger(bcmod($this->value, BigInteger::getBC($x)), true); } public function divQR($x) { return array( $this->div($x), $this->divR($x) ); } public function mod($x) { $xv = BigInteger::getBC($x); $mod = bcmod($this->value, $xv); if ($mod[0] == "-") { $mod = bcadd($mod, $xv[0] == "-" ? substr($xv, 1) : $xv, 0); } return new BigInteger($mod, true); } public function extendedGcd($n) { $u = $this->value; $v = (new BigInteger($n))->abs()->value; $a = "1"; $b = "0"; $c = "0"; $d = "1"; while (bccomp($v, "0", 0) != 0) { $q = bcdiv($u, $v, 0); $temp = $u; $u = $v; $v = bcsub($temp, bcmul($v, $q, 0), 0); $temp = $a; $a = $c; $c = bcsub($temp, bcmul($a, $q, 0), 0); $temp = $b; $b = $d; $d = bcsub($temp, bcmul($b, $q, 0), 0); } return array( "gcd" => new BigInteger($u, true), "x" => new BigInteger($a, true), "y" => new BigInteger($b, true) ); } public function gcd($x) { return $this->extendedGcd($x)["gcd"]; } public function modInverse($n) { $n = (new BigInteger($n))->abs(); if ($this->sign() < 0) { $temp = $this->abs(); $temp = $temp->modInverse($n); return $n->sub($temp); } extract($this->extendedGcd($n)); if (!$gcd->equals(1)) { return false; } $x = $x->sign() < 0 ? $x->add($n) : $x; return $this->sign() < 0 ? $n->sub($x) : $x; } public function pow($x) { return new BigInteger(bcpow($this->value, BigInteger::getBC($x), 0), true); } public function powMod($x, $n) { return new BigInteger(bcpowmod($this->value, BigInteger::getBC($x), BigInteger::getBC($n), 0), true); } public function abs() { return new BigInteger($this->value[0] == "-" ? substr($this->value, 1) : $this->value, true); } public function neg() { return new BigInteger($this->value[0] == "-" ? substr($this->value, 1) : "-" . $this->value, true); } public function binaryAnd($x) { $left = $this->toBytes(); $right = (new BigInteger($x))->toBytes(); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return new BigInteger($left & $right, 256); } public function binaryOr($x) { $left = $this->toBytes(); $right = (new BigInteger($x))->toBytes(); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return new BigInteger($left | $right, 256); } public function binaryXor($x) { $left = $this->toBytes(); $right = (new BigInteger($x))->toBytes(); $length = max(strlen($left), strlen($right)); $left = str_pad($left, $length, chr(0), STR_PAD_LEFT); $right = str_pad($right, $length, chr(0), STR_PAD_LEFT); return new BigInteger($left ^ $right, 256); } public function setbit($index, $bitOn = true) { $bits = $this->toBits(); $bits[strlen($bits) - $index - 1] = $bitOn ? "1" : "0"; return new BigInteger($bits, 2); } public function testbit($index) { $bytes = $this->toBytes(); $bytesIndex = intval($index / 8); $len = strlen($bytes); $b = $bytesIndex >= $len ? 0 : ord($bytes[$len - $bytesIndex - 1]); $v = 1 << ($index % 8); return ($b & $v) === $v; } public function scan0($start) { $bits = $this->toBits(); $len = strlen($bits); if ($start < 0 || $start >= $len) { return -1; } $pos = strrpos($bits, "0", -1 - $start); return $pos === false ? -1 : $len - $pos - 1; } public function scan1($start) { $bits = $this->toBits(); $len = strlen($bits); if ($start < 0 || $start >= $len) { return -1; } $pos = strrpos($bits, "1", -1 - $start); return $pos === false ? -1 : $len - $pos - 1; } public function cmp($x) { return bccomp($this->value, BigInteger::getBC($x)); } public function equals($x) { return $this->value === BigInteger::getBC($x); } public function sign() { return $this->value[0] === "-" ? -1 : ($this->value === "0" ? 0 : 1); } } } else { if (!defined("S_MATH_BIGINTEGER_QUIET")) { throw new \Exception("Unsupported S_MATH_BIGINTEGER_MODE " . S_MATH_BIGINTEGER_MODE); } }