init
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
||||
|
||||
## MIT LICENSE
|
||||
|
||||
Copyright (C) 2018 Simplito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
|
||||
# BigInteger wrapper library for PHP
|
||||
|
||||
## Information
|
||||
|
||||
This library is a common interface for php_gmp and php_bcmath modules. It automatically detects supported modules and uses the best of them (gmp>bcmath). Gmp is a lot faster, but is also missing on many hosting services -- that is why this wrapper has been created. It is used for example in encryption functions of the [PrivMX WebMail](https://privmx.com) software.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install this library via Composer:
|
||||
```
|
||||
composer require simplito/bigint-wrapper-php
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
If you want to force using a specific implementation, then define constant S_MATH_BIGINTEGER_MODE - set it to "gmp" or "bcmath". If you do not do this, mode of operation and the constant will be set automatically.
|
||||
|
||||
If there are no gmp and bcmath modules, an exception will be thrown. If you want to prevent this, then simply define S_MATH_BIGINTEGER_QUIET constant.
|
||||
|
||||
All functions of this library are implemented as members of class BigInteger, which is located under BI namespace. Instances of BigInteger are immutable - member functions usually return new instances of the BigInteger class.
|
||||
|
||||
|
||||
### ConvertibleToBi - a placeholder type
|
||||
|
||||
To make the below documentation more readable we use the "ConvertibleToBi" type symbol, which in reality can be one of the following types:
|
||||
- an instance of the BigInteger class
|
||||
- an integer
|
||||
- a decimal string
|
||||
- a gmp resource or class (only when you are in gmp mode)
|
||||
|
||||
If you have a non-decimal string and want to use it -- first you have to convert it to BigInteger class using:
|
||||
```
|
||||
new BigInteger($myNonDecimalString, $baseOfMyNonDecimalString)
|
||||
```
|
||||
|
||||
### BI\BigInteger class members
|
||||
|
||||
#### construct(ConvertibleToBi $value = 0, int $base = 10)
|
||||
Creates a new instance of BigInteger. If you pass an invalid value, an exception will be thrown. If $base === true then passed $value will be used without any check and conversion. Supported bases: 2, 10, 16, 256.
|
||||
- **GMP implementation:** gmp_init + bin2hex for 256 base
|
||||
- **Bcmath implementation:** custom(bcadd + bcmul)
|
||||
|
||||
#### static BigInteger|false createSafe(ConvertibleToBi $value = 0, int $base = 10)
|
||||
Creates a new BigInteger instance in the same way as constructor, but if there is an error, false will be returned instead of throwing an exception.
|
||||
|
||||
#### BigInteger add(ConvertibleToBi $x)
|
||||
Adds numbers
|
||||
- **GMP implementation:** gmp_add
|
||||
- **Bcmath implementation:** bcadd
|
||||
|
||||
#### BigInteger sub(ConvertibleToBi $x)
|
||||
Subtracts numbers
|
||||
- **GMP implementation:** gmp_sub
|
||||
- **Bcmath implementation:** bcsub
|
||||
|
||||
#### BigInteger mul(ConvertibleToBi $x)
|
||||
Multiplies numbers
|
||||
- **GMP implementation:** gmp_mul
|
||||
- **Bcmath implementation:** bcmul
|
||||
|
||||
#### BigInteger div(ConvertibleToBi $x)
|
||||
Divides numbers
|
||||
- **GMP implementation:** gmp_div_q
|
||||
- **Bcmath implementation:** bcdiv
|
||||
|
||||
#### BigInteger divR(ConvertibleToBi $x)
|
||||
Returns a remainder of the division of numbers. The remainder has the sign of the divided number.
|
||||
- **GMP implementation:** gmp_div_r
|
||||
- **Bcmath implementation:** bcmod
|
||||
|
||||
#### array(BigInteger, BigInteger) divQR(ConvertibleToBi $x)
|
||||
Divides numbers and returns quotient and remainder. Returns an array(), with the first element being quotient, and the second being remainder.
|
||||
- **GMP implementation:** gmp_div_qr
|
||||
- **Bcmath implementation:** div + divR
|
||||
|
||||
#### BigInteger mod(ConvertibleToBi $x)
|
||||
The "division modulo" operation. The result is always non-negative, the sign of divider is ignored.
|
||||
- **GMP implementation:** gmp_mod
|
||||
- **Bcmath implementation:** custom (bcmod + bcadd)
|
||||
|
||||
#### BigInteger gcd(ConvertibleToBi $x)
|
||||
Calculates greatest common divisor
|
||||
- **GMP implementation:** gmp_gcd
|
||||
- **Bcmath implementation:** custom (bccomp + bcdiv + bcsub + bcmul)
|
||||
|
||||
#### BigInteger|false modInverse(ConvertibleToBi $x)
|
||||
Inverses by modulo, returns false if inversion does not exist.
|
||||
- **GMP implementation:** gmp_invert
|
||||
- **Bcmath implementation:** custom (gcd)
|
||||
|
||||
#### BigInteger pow(ConvertibleToBi $x)
|
||||
The power function.
|
||||
- **GMP implementation:** gmp_pow
|
||||
- **Bcmath implementation:** bcpow
|
||||
|
||||
#### BigInteger powMod(ConvertibleToBi $x, ConvertibleToBi $n)
|
||||
The modular power function.
|
||||
- **GMP implementation:** gmp_powm
|
||||
- **Bcmath implementation:** bcpowmod
|
||||
|
||||
#### BigInteger abs()
|
||||
Returns absolute value.
|
||||
- **GMP implementation:** gmp_abs
|
||||
- **Bcmath implementation:** check first character
|
||||
|
||||
#### BigInteger neg()
|
||||
Negates the number
|
||||
- **GMP implementation:** gmp_neg
|
||||
- **Bcmath implementation:** check first character
|
||||
|
||||
#### BigInteger binaryAnd(ConvertibleToBi $x)
|
||||
Bitwise AND.
|
||||
- **GMP implementation:** gmp_and
|
||||
- **Bcmath implementation:** custom (toBytes + php string and)
|
||||
|
||||
#### BigInteger binaryOr(ConvertibleToBi $x)
|
||||
Bitwise OR
|
||||
- **GMP implementation:** gmp_or
|
||||
- **Bcmath implementation:** custom (toBytes + php string or)
|
||||
|
||||
#### BigInteger binaryXor(ConvertibleToBi $x)
|
||||
Bitwise XOR
|
||||
- **GMP implementation:** gmp_xor
|
||||
- **Bcmath implementation:** custom (toBytes + php string xor)
|
||||
|
||||
#### BigInteger setbit($index, $bitOn = true)
|
||||
Sets bit at given index
|
||||
- **GMP implementation:** gmp_setbit
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### bool testbit($index)
|
||||
Tests if a bit at given index is set
|
||||
- **GMP implementation:** gmp_testbit
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### int scan0($start)
|
||||
Scans for 0, and returns index of first found bit
|
||||
- **GMP implementation:** gmp_scan0
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### int scan1($start)
|
||||
Scans for 1, and returns index of first found bit
|
||||
- **GMP implementation:** gmp_scan1
|
||||
- **Bcmath implementation:** custom (toBits)
|
||||
|
||||
#### int cmp(ConvertibleToBi $x)
|
||||
Compares numbers, returns <0, 0, >0
|
||||
- **GMP implementation:** gmp_cmp
|
||||
- **Bcmath implementation:** bccomp
|
||||
|
||||
#### bool equals(ConvertibleToBi $x)
|
||||
Checks if numbers are equal
|
||||
- **GMP implementation:** gmp_cmp
|
||||
- **Bcmath implementation:** bccomp
|
||||
|
||||
#### int sign()
|
||||
Sign of number, returns -1, 0, 1
|
||||
- **GMP implementation:** gmp_sign
|
||||
- **Bcmath implementation:** check first character
|
||||
|
||||
#### int toNumber()
|
||||
Converts to number (use only with small 32/64bit numbers)
|
||||
- **GMP implementation:** gmp_intval
|
||||
- **Bcmath implementation:** intval
|
||||
|
||||
#### string toDec()
|
||||
Converts to decimal string
|
||||
- **GMP implementation:** gmp_strval
|
||||
- **Bcmath implementation:** just the value
|
||||
|
||||
#### string toHex()
|
||||
Converts to hex string
|
||||
- **GMP implementation:** gmp_strval
|
||||
- **Bcmath implementation:** toBytes + bin2hex
|
||||
|
||||
#### string toBytes
|
||||
Converts to binary string
|
||||
- **GMP implementation:** gmp_strval + hex2bin
|
||||
- **Bcmath implementation:** custom (bcmod + bcdiv + bccomp)
|
||||
|
||||
#### string toBits()
|
||||
Converts to bits string (0 and 1 characters)
|
||||
- **GMP implementation:** gmp_strval
|
||||
- **Bcmath implementation:** toBytes + decbin
|
||||
|
||||
#### string toString(int $base = 10)
|
||||
Converts to string using given base (supported bases 2-62, 256)
|
||||
- **GMP implementation:** all above toX functions, and for non standard gmp_strval
|
||||
- **Bcmath implementation:** all above toX functions, and for non standard bcmod + bcdiv + bccomp
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "simplito/bigint-wrapper-php",
|
||||
"description": "Common interface for php_gmp and php_bcmath modules",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BI\\": "lib/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
|
||||
namespace BI;
|
||||
|
||||
if (!defined("S_MATH_BIGINTEGER_MODE")) {
|
||||
if (extension_loaded("gmp")) {
|
||||
define("S_MATH_BIGINTEGER_MODE", "gmp");
|
||||
}
|
||||
else if (extension_loaded("bcmath")) {
|
||||
define("S_MATH_BIGINTEGER_MODE", "bcmath");
|
||||
}
|
||||
else {
|
||||
if (!defined("S_MATH_BIGINTEGER_QUIET")) {
|
||||
throw new \Exception("Cannot use BigInteger. Neither gmp nor bcmath module is loaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (S_MATH_BIGINTEGER_MODE == "gmp") {
|
||||
|
||||
if (!extension_loaded("gmp")) {
|
||||
throw new \Exception("Extension gmp not loaded");
|
||||
}
|
||||
|
||||
class BigInteger {
|
||||
|
||||
public $value;
|
||||
|
||||
public function __construct($value = 0, $base = 10) {
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
define('S_MATH_BIGINTEGER_MODE', "bcmath");
|
||||
|
||||
require(__DIR__ . "/../lib/BigInteger.php");
|
||||
|
||||
use BI\BigInteger;
|
||||
|
||||
$a = new BigInteger("4547395333333333333333333333333333333333333343587493875493579375498759837593574935739857");
|
||||
$b = new BigInteger("-4547395333333333333333333333333333333333333343587493875493579375498759837593574935739857");
|
||||
$c = new BigInteger("0");
|
||||
$hex = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
|
||||
$bytes = hex2bin($hex);
|
||||
|
||||
function test($v, $b) {
|
||||
$start = microtime(true);
|
||||
$count = 10000;
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$v->binaryAnd($b);
|
||||
//$v->toBytes();
|
||||
//BigInteger::getBC($v, 256);
|
||||
}
|
||||
$end = microtime(true);
|
||||
error_log($end - $start);
|
||||
}
|
||||
|
||||
test($a, $a);
|
||||
test($b, $b);
|
||||
//test($bytes, $b);
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
|
||||
use BI\BigInteger;
|
||||
|
||||
function test($a, $b, $message = "") {
|
||||
error_log(($a === $b ? "PASS" : "FAIL get: " . $a . ", expected: " . $b) . " " . $message);
|
||||
}
|
||||
|
||||
function testB($a, $b, $message = "") {
|
||||
error_log(($a->toString() === $b ? "PASS" : "FAIL get: " . $a . ", expected: " . $b) . " " . $message);
|
||||
}
|
||||
|
||||
function testSerialization($b, $msg = "") {
|
||||
test($b->toBits(), "1010000", $msg . " toBits");
|
||||
test($b->toBytes(), hex2bin("50"), $msg . " toBytes");
|
||||
test($b->toHex(), "50", $msg . " toHex");
|
||||
test($b->toDec(), "80", $msg . " toDec");
|
||||
test($b->toNumber(), 80, $msg . " toNumber");
|
||||
test($b->toBase(58), "1M", $msg . " to58");
|
||||
}
|
||||
|
||||
function testCreate() {
|
||||
error_log("=============\nTest serialization\n=============");
|
||||
testSerialization(new BigInteger("1010000", 2), "bits");
|
||||
testSerialization(new BigInteger(hex2bin("50"), 256), "bytes");
|
||||
testSerialization(new BigInteger("50", 16), "hex");
|
||||
testSerialization(new BigInteger("80", 10), "dec");
|
||||
testSerialization(new BigInteger("80"), "dec2");
|
||||
testSerialization(new BigInteger(80), "number");
|
||||
}
|
||||
|
||||
function testCreateSafeSingle($value, $base, $msg) {
|
||||
try {
|
||||
$z = new BigInteger($value, $base);
|
||||
error_log("FAIL exception during create " . $msg);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
error_log("PASS exception during create " . $msg);
|
||||
}
|
||||
test(BigInteger::createSafe($value, $base), false, "createSafe " . $msg);
|
||||
}
|
||||
|
||||
function testCreateSafe() {
|
||||
error_log("=============\nTest create safe\n=============");
|
||||
testCreateSafeSingle("zz", 2, "bin");
|
||||
testCreateSafeSingle("zz", 10, "dec");
|
||||
testCreateSafeSingle("zz", 16, "hex");
|
||||
}
|
||||
|
||||
function testSpaces() {
|
||||
error_log("=============\nTest spaces\n=============");
|
||||
test((new BigInteger("11 0 1", 2))->toBits(), "1101", "bin");
|
||||
test((new BigInteger("6 2 0 6", 10))->toDec(), "6206", "dec");
|
||||
test((new BigInteger("f3 5 12 ac 0", 16))->toHex(), "f3512ac0", "hex");
|
||||
}
|
||||
|
||||
function testOp() {
|
||||
error_log("=============\nTest op\n=============");
|
||||
testB((new BigInteger(20))->add(34), "54", "add");
|
||||
testB((new BigInteger(20))->sub(14), "6", "sub");
|
||||
testB((new BigInteger(20))->mul(12), "240", "mul");
|
||||
testB((new BigInteger(20))->div(4), "5", "div");
|
||||
testB((new BigInteger(20))->divR(7), "6", "divR");
|
||||
$qr = (new BigInteger(20))->divQR(6);
|
||||
testB($qr[0], "3", "divQR[0]");
|
||||
testB($qr[1], "2", "divQR[1]");
|
||||
testB((new BigInteger(20))->mod(3), "2", "mod");
|
||||
testB((new BigInteger(54))->gcd(81), "27", "gcd");
|
||||
testB((new BigInteger(3))->modInverse(10), "7", "modInverse");
|
||||
testB((new BigInteger(3))->pow(4), "81", "pow");
|
||||
testB((new BigInteger(3))->powMod(4, 10), "1", "powMod");
|
||||
testB((new BigInteger(20))->abs(), "20", "abs");
|
||||
testB((new BigInteger(20))->neg(), "-20", "neg");
|
||||
testB((new BigInteger(20))->binaryAnd(18), "16", "binaryAnd");
|
||||
testB((new BigInteger(20))->binaryOr(18), "22", "binaryOr");
|
||||
testB((new BigInteger(20))->binaryXor(18), "6", "binaryXor");
|
||||
testB((new BigInteger(20))->setbit(3), "28", "setbit");
|
||||
test((new BigInteger(20))->testbit(4), true, "testbit true");
|
||||
test((new BigInteger(20))->testbit(3), false, "testbit false");
|
||||
test((new BigInteger(5))->testbit(0), true, "testbit 0 true");
|
||||
test((new BigInteger(6))->testbit(0), false, "testbit 0 false");
|
||||
test((new BigInteger(6))->testbit(1), true, "testbit 1 true");
|
||||
test((new BigInteger(5))->testbit(1), false, "testbit 1 false");
|
||||
test((new BigInteger(132))->testbit(7), true, "testbit 7 true");
|
||||
test((new BigInteger(81))->testbit(7), false, "testbit 7 false");
|
||||
test((new BigInteger(258))->testbit(8), true, "testbit 8 true");
|
||||
test((new BigInteger(253))->testbit(8), false, "testbit 8 false");
|
||||
test((new BigInteger(20))->scan0(2), 3, "scan0");
|
||||
test((new BigInteger(20))->scan1(3), 4, "scan1");
|
||||
test((new BigInteger(20))->cmp(22), -1, "cmp -1");
|
||||
test((new BigInteger(20))->cmp(20), 0, "cmp 0");
|
||||
test((new BigInteger(20))->cmp(18), 1, "cmp 1");
|
||||
test((new BigInteger(20))->equals(20), true, "equals true");
|
||||
test((new BigInteger(20))->equals(21), false, "equals false");
|
||||
test((new BigInteger(-20))->sign(), -1, "sign -1");
|
||||
test((new BigInteger(0))->sign(), 0, "sign 0");
|
||||
test((new BigInteger(20))->sign(), 1, "sign 1");
|
||||
testB(new BigInteger("-20"), "-20", "minus");
|
||||
testB(new BigInteger("-14", 16), "-20", "minus");
|
||||
testB(new BigInteger("-10100", 2), "-20", "minus");
|
||||
}
|
||||
|
||||
function testBig() {
|
||||
error_log("=============\nTest big\n=============");
|
||||
$bits = "1001010111010010100001000101110110100001000101101000110101010101001";
|
||||
$hex = "eeaf0ab9adb38dd69c33f80afa8fc5e86072618775ff3c0b9ea2314c9c256576d674df7496ea81d3383b4813d692c6e0e0d5d8e250b98be48e495c1d6089dad15dc7d7b46154d6b6ce8ef4ad69b15d4982559b297bcf1885c529f566660e57ec68edbc3c05726cc02fd4cbf4976eaa9afd5138fe8376435b9fc61d2fc0eb06e3";
|
||||
$dec = "436529472098746319073192837123683467019263172846";
|
||||
$bytes = hex2bin($hex);
|
||||
test((new BigInteger($bits, 2))->toBits(), $bits, "init big from binary");
|
||||
test((new BigInteger($dec, 10))->toDec(), $dec, "init big from dec");
|
||||
test((new BigInteger($hex, 16))->toHex(), $hex, "init big from hex");
|
||||
test((new BigInteger($bytes, 256))->toBytes(), $bytes, "init big from buffer");
|
||||
}
|
||||
|
||||
testCreate();
|
||||
testCreateSafe();
|
||||
testSpaces();
|
||||
testOp();
|
||||
testBig();
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
define('S_MATH_BIGINTEGER_MODE', "bcmath");
|
||||
|
||||
require(__DIR__ . "/../lib/BigInteger.php");
|
||||
require(__DIR__ . "/test.php");
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
define('S_MATH_BIGINTEGER_MODE', "gmp");
|
||||
|
||||
require(__DIR__ . "/../lib/BigInteger.php");
|
||||
require(__DIR__ . "/test.php");
|
||||
Reference in New Issue
Block a user