/** * Multiply two arbitrary precision number * * @param string $a The left operand, as a string. * @param string $b The right operand, as a string. * @access public * @return string Returns the result as a string. */ public function mul($a, $b) { $a = new Math_BigInteger($a); $b = new Math_BigInteger($b); $c = $a->multiply($b); return $c->toString(); }
protected static function make64Int($hi, $lo) { if (PHP_INT_SIZE > 4) { return (int) $hi << 32 | (int) $lo; } $lo = sprintf("%u", $lo); if (function_exists("gmp_mul")) { return gmp_strval(gmp_add(gmp_mul($hi, "4294967296"), $lo)); } if (function_exists("bcmul")) { return bcadd(bcmul($hi, "4294967296"), $lo); } if (class_exists('Math_BigInteger')) { $bi = new Math_BigInteger($hi); return $bi->multiply("4294967296")->add($lo)->toString(); } throw new PListException("either gmp or bc has to be installed, or the Math_BigInteger has to be available!"); }
/** * @param string $ip IPv4 or IPv6 address to convert * @return string 128bit string that can be used with DECIMNAL(39,0) or false */ public static function inet_ptoi($ip) { // make sure it is an ip if (filter_var($ip, FILTER_VALIDATE_IP) === false) { return false; } $parts = unpack('N*', inet_pton($ip)); // fix IPv4 if (strpos($ip, '.') !== false) { $parts = array(1 => 0, 2 => 0, 3 => 0, 4 => $parts[1]); } foreach ($parts as &$part) { // convert any unsigned ints to signed from unpack. // this should be OK as it will be a PHP float not an int if ($part < 0) { $part = 4294967296.0; } } if (function_exists('bcadd')) { // Use BCMath if available $decimal = $parts[4]; $decimal = bcadd($decimal, bcmul($parts[3], '4294967296')); $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616')); $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336')); } else { // Otherwise use the pure PHP BigInteger class $decimal = new Math_BigInteger($parts[4]); $partTree = new Math_BigInteger($parts[3]); $partTwo = new Math_BigInteger($parts[2]); $partOne = new Math_BigInteger($parts[1]); $decimal = $decimal->add($partTree->multiply(new Math_BigInteger('4294967296'))); $decimal = $decimal->add($partTwo->multiply(new Math_BigInteger('18446744073709551616'))); $decimal = $decimal->add($partOne->multiply(new Math_BigInteger('79228162514264337593543950336'))); $decimal = $decimal->toString(); } return $decimal; }
function inet_ptoi($ip) { // make sure it is an ip if (filter_var($ip, FILTER_VALIDATE_IP) === false) { return false; } $parts = unpack('N*', inet_pton($ip)); // fix IPv4 if (strpos($ip, '.') !== false) { $parts = array(1 => 0, 2 => 0, 3 => 0, 4 => $parts[1]); } foreach ($parts as &$part) { // convert any unsigned ints to signed from unpack. // this should be OK as it will be a PHP float not an int if ($part < 0) { $part += 4294967296; } } // use BCMath if the extension exists if (function_exists('bcadd')) { $decimal = $parts[4]; $decimal = bcadd($decimal, bcmul($parts[3], '4294967296')); $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616')); $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336')); } else { $decimal = new Math_BigInteger($parts[4]); $part3 = new Math_BigInteger($parts[3]); $part2 = new Math_BigInteger($parts[2]); $part1 = new Math_BigInteger($parts[1]); $decimal = $decimal->add($part3->multiply(new Math_BigInteger('4294967296'))); $decimal = $decimal->add($part2->multiply(new Math_BigInteger('18446744073709551616'))); $decimal = $decimal->add($part1->multiply(new Math_BigInteger('79228162514264337593543950336'))); $decimal = $decimal->toString(); } return $decimal; }
/** * Calculates the greatest common divisor and Bezout's identity. * * Say you have 693 and 609. The GCD is 21. Bezout's identity states that there exist integers x and y such that * 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which * combination is returned is dependant upon which mode is in use. See * {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bezout's identity - Wikipedia} for more information. * * Here's an example: * <code> * <?php * include 'Math/BigInteger.php'; * * $a = new Math_BigInteger(693); * $b = new Math_BigInteger(609); * * extract($a->extendedGCD($b)); * * echo $gcd->toString() . "\r\n"; // outputs 21 * echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21 * ?> * </code> * * @param Math_BigInteger $n * @return Math_BigInteger * @access public * @internal Calculates the GCD using the binary xGCD algorithim described in * {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes, * the more traditional algorithim requires "relatively costly multiple-precision divisions". */ function extendedGCD($n) { switch (MATH_BIGINTEGER_MODE) { case MATH_BIGINTEGER_MODE_GMP: extract(gmp_gcdext($this->value, $n->value)); return array('gcd' => $this->_normalize(new Math_BigInteger($g)), 'x' => $this->_normalize(new Math_BigInteger($s)), 'y' => $this->_normalize(new Math_BigInteger($t))); case MATH_BIGINTEGER_MODE_BCMATH: // it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works // best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is, // the basic extended euclidean algorithim is what we're using. $u = $this->value; $v = $n->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' => $this->_normalize(new Math_BigInteger($u)), 'x' => $this->_normalize(new Math_BigInteger($a)), 'y' => $this->_normalize(new Math_BigInteger($b))); } $y = $n->copy(); $x = $this->copy(); $g = new Math_BigInteger(); $g->value = array(1); while (!($x->value[0] & 1 || $y->value[0] & 1)) { $x->_rshift(1); $y->_rshift(1); $g->_lshift(1); } $u = $x->copy(); $v = $y->copy(); $a = new Math_BigInteger(); $b = new Math_BigInteger(); $c = new Math_BigInteger(); $d = new Math_BigInteger(); $a->value = $d->value = $g->value = array(1); $b->value = $c->value = array(); while (!empty($u->value)) { while (!($u->value[0] & 1)) { $u->_rshift(1); if (!empty($a->value) && $a->value[0] & 1 || !empty($b->value) && $b->value[0] & 1) { $a = $a->add($y); $b = $b->subtract($x); } $a->_rshift(1); $b->_rshift(1); } while (!($v->value[0] & 1)) { $v->_rshift(1); if (!empty($d->value) && $d->value[0] & 1 || !empty($c->value) && $c->value[0] & 1) { $c = $c->add($y); $d = $d->subtract($x); } $c->_rshift(1); $d->_rshift(1); } if ($u->compare($v) >= 0) { $u = $u->subtract($v); $a = $a->subtract($c); $b = $b->subtract($d); } else { $v = $v->subtract($u); $c = $c->subtract($a); $d = $d->subtract($b); } } return array('gcd' => $this->_normalize($g->multiply($v)), 'x' => $this->_normalize($c), 'y' => $this->_normalize($d)); }
/** * Divides two BigIntegers. * * Returns an array whose first element contains the quotient and whose second element contains the * "common residue". If the remainder would be positive, the "common residue" and the remainder are the * same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder * and the divisor. * * Here's a quick 'n dirty example: * <code> * <?php * include('Math/BigInteger.php'); * * $a = new Math_BigInteger('10'); * $b = new Math_BigInteger('20'); * * list($quotient,$remainder) = $a->divide($b); * * echo $quotient->toString(); // outputs 0 * echo "\r\n"; * echo $remainder->toString(); // outputs 10 * ?> * </code> * * @param Math_BigInteger $y * @return Array * @access public * @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20} * with a slight variation due to the fact that this script, initially, did not support negative numbers. Now, * it does, but I don't want to change that which already works. */ function divide($y) { switch (MATH_BIGINTEGER_MODE) { case MATH_BIGINTEGER_MODE_GMP: $quotient = new Math_BigInteger(); $remainder = new Math_BigInteger(); list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value); if (gmp_sign($remainder->value) < 0) { $remainder->value = gmp_add($remainder->value, gmp_abs($y->value)); } return array($quotient, $remainder); case MATH_BIGINTEGER_MODE_BCMATH: $quotient = new Math_BigInteger(); $remainder = new Math_BigInteger(); $quotient->value = bcdiv($this->value, $y->value); $remainder->value = bcmod($this->value, $y->value); if ($remainder->value[0] == '-') { $remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value); } return array($quotient, $remainder); } $x = $this->_copy(); $y = $y->_copy(); $x_sign = $x->is_negative; $y_sign = $y->is_negative; $x->is_negative = $y->is_negative = false; $diff = $x->compare($y); if (!$diff) { $temp = new Math_BigInteger(); $temp->value = array(1); $temp->is_negative = $x_sign != $y_sign; return array($temp, new Math_BigInteger()); } if ($diff < 0) { // if $x is negative, "add" $y. if ($x_sign) { $x = $y->subtract($x); } return array(new Math_BigInteger(), $x); } // normalize $x and $y as described in HAC 14.23 / 14.24 // (incidently, i haven't been able to find a definitive example showing that this // results in worth-while speedup, but whatever) $msb = $y->value[count($y->value) - 1]; for ($shift = 0; !($msb & 0x2000000); $shift++) { $msb <<= 1; } $x->_lshift($shift); $y->_lshift($shift); $x_max = count($x->value) - 1; $y_max = count($y->value) - 1; $quotient = new Math_BigInteger(); $quotient->value = $this->_array_repeat(0, $x_max - $y_max + 1); // $temp = $y << ($x_max - $y_max-1) in base 2**26 $temp = new Math_BigInteger(); $temp->value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y->value); while ($x->compare($temp) >= 0) { // calculate the "common residue" $quotient->value[$x_max - $y_max]++; $x = $x->subtract($temp); $x_max = count($x->value) - 1; } for ($i = $x_max; $i >= $y_max + 1; $i--) { $x_value = array($x->value[$i], $i > 0 ? $x->value[$i - 1] : 0, $i - 1 > 0 ? $x->value[$i - 2] : 0); $y_value = array($y->value[$y_max], $y_max > 0 ? $y_max - 1 : 0); $q_index = $i - $y_max - 1; if ($x_value[0] == $y_value[0]) { $quotient->value[$q_index] = 0x3ffffff; } else { $quotient->value[$q_index] = floor(($x_value[0] * 0x4000000 + $x_value[1]) / $y_value[0]); } $temp = new Math_BigInteger(); $temp->value = array($y_value[1], $y_value[0]); $lhs = new Math_BigInteger(); $lhs->value = array($quotient->value[$q_index]); $lhs = $lhs->multiply($temp); $rhs = new Math_BigInteger(); $rhs->value = array($x_value[2], $x_value[1], $x_value[0]); while ($lhs->compare($rhs) > 0) { $quotient->value[$q_index]--; $lhs = new Math_BigInteger(); $lhs->value = array($quotient->value[$q_index]); $lhs = $lhs->multiply($temp); } $corrector = new Math_BigInteger(); $temp = new Math_BigInteger(); $corrector->value = $temp->value = $this->_array_repeat(0, $q_index); $temp->value[] = $quotient->value[$q_index]; $temp = $temp->multiply($y); if ($x->compare($temp) < 0) { $corrector->value[] = 1; $x = $x->add($corrector->multiply($y)); $quotient->value[$q_index]--; } $x = $x->subtract($temp); $x_max = count($x->value) - 1; } // unnormalize the remainder $x->_rshift($shift); $quotient->is_negative = $x_sign != $y_sign; // calculate the "common residue", if appropriate if ($x_sign) { $y->_rshift($shift); $x = $y->subtract($x); } return array($quotient->_normalize(), $x); }
/** * DSA verify. * * @param string $message Message. * @param string $hash_alg Hash algorithm. * @param Math_BigInteger $r r. * @param Math_BigInteger $s s. * * @return bool True if verified. */ public function verify($message, $hash_alg, $r, $s) { $hash = new Crypt_Hash($hash_alg); $hash_m = new Math_BigInteger($hash->hash($message), 256); $g = new Math_BigInteger($this->_key->key['g'], 256); $p = new Math_BigInteger($this->_key->key['p'], 256); $q = new Math_BigInteger($this->_key->key['q'], 256); $y = new Math_BigInteger($this->_key->key['y'], 256); $w = $s->modInverse($q); $hash_m_mul = $hash_m->multiply($w); $u1_base = $hash_m_mul->divide($q); $u1 = $u1_base[1]; $r_mul = $r->multiply($w); $u2_base = $r_mul->divide($q); $u2 = $u2_base[1]; $g_pow = $g->modPow($u1, $p); $y_pow = $y->modPow($u2, $p); $g_pow_mul = $g_pow->multiply($y_pow); $g_pow_mul_mod_base = $g_pow_mul->divide($p); $g_pow_mul_mod = $g_pow_mul_mod_base[1]; $v_base = $g_pow_mul_mod->divide($q); $v = $v_base[1]; return $v->compare($r) == 0; }
/** * Create an 64 bit integer using bcmath or gmp * @param int $hi The higher word * @param int $lo The lower word * @return mixed The integer (as int if possible, as string if not possible) * @throws PListException if neither gmp nor bc available */ protected static function make64Int($hi, $lo) { // on x64, we can just use int if (PHP_INT_SIZE > 4) { return (int) $hi << 32 | (int) $lo; } // lower word has to be unsigned since we don't use bitwise or, we use bcadd/gmp_add $lo = sprintf("%u", $lo); // use GMP or bcmath if possible if (function_exists("gmp_mul")) { return gmp_strval(gmp_add(gmp_mul($hi, "4294967296"), $lo)); } if (function_exists("bcmul")) { return bcadd(bcmul($hi, "4294967296"), $lo); } if (class_exists('Math_BigInteger')) { $bi = new \Math_BigInteger($hi); return $bi->multiply(new \Math_BigInteger("4294967296"))->add(new \Math_BigInteger($lo))->toString(); } throw new PListException("either gmp or bc has to be installed, or the Math_BigInteger has to be available!"); }
$result = bcsub($result, $y); $_result = $_result->subtract($_y); echo "\$result = \$result-\$y;\r\n"; echo "{$result}\r\n"; echo $_result->toString(); echo "\r\n\r\n"; $result = bcdiv($x, $y); list($_result, ) = $_x->divide($_y); echo "\$result = \$x/\$y;\r\n"; echo "{$result}\r\n"; echo $_result->toString(); echo "\r\n\r\n"; $result = bcmod($y, $z); list(, $_result) = $_y->divide($_z); echo "\$result = \$x%\$y;\r\n"; echo "{$result}\r\n"; echo $_result->toString(); echo "\r\n\r\n"; $result = bcmul($x, $z); $_result = $_x->multiply($_z); echo "\$result = \$x*\$z;\r\n"; echo "{$result}\r\n"; echo $_result->toString(); echo "\r\n\r\n"; $result = bcpowmod($x, $y, $result); $_result = $_x->modPow($_y, $_result); echo "\$result = (\$x**\$y)%\$result;\r\n"; echo "{$result}\r\n"; echo $_result->toString(); echo "\r\n\r\n"; // modInverse isn't demo'd because no equivalent to it exists in BCMath.
public function extendedGCD($n) { switch (MATH_BIGINTEGER_MODE) { case MATH_BIGINTEGER_MODE_GMP: $_gmp_gcdext = gmp_gcdext($this->value, $n->value); $g = $_gmp_gcdext['g']; $s = $_gmp_gcdext['s']; $t = $_gmp_gcdext['t']; return array('gcd' => $this->_normalize(new Math_BigInteger($g)), 'x' => $this->_normalize(new Math_BigInteger($s)), 'y' => $this->_normalize(new Math_BigInteger($t))); case MATH_BIGINTEGER_MODE_BCMATH: $u = $this->value; $v = $n->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' => $this->_normalize(new Math_BigInteger($u)), 'x' => $this->_normalize(new Math_BigInteger($a)), 'y' => $this->_normalize(new Math_BigInteger($b))); } $y = $n->copy(); $x = $this->copy(); $g = new Math_BigInteger(); $g->value = array(1); while (!($x->value[0] & 1 || $y->value[0] & 1)) { $x->_rshift(1); $y->_rshift(1); $g->_lshift(1); } $u = $x->copy(); $v = $y->copy(); $a = new Math_BigInteger(); $b = new Math_BigInteger(); $c = new Math_BigInteger(); $d = new Math_BigInteger(); $a->value = $d->value = $g->value = array(1); $b->value = $c->value = array(); while (!empty($u->value)) { while (!($u->value[0] & 1)) { $u->_rshift(1); if (!empty($a->value) && $a->value[0] & 1 || !empty($b->value) && $b->value[0] & 1) { $a = $a->add($y); $b = $b->subtract($x); } $a->_rshift(1); $b->_rshift(1); } while (!($v->value[0] & 1)) { $v->_rshift(1); if (!empty($d->value) && $d->value[0] & 1 || !empty($c->value) && $c->value[0] & 1) { $c = $c->add($y); $d = $d->subtract($x); } $c->_rshift(1); $d->_rshift(1); } if (0 <= $u->compare($v)) { $u = $u->subtract($v); $a = $a->subtract($c); $b = $b->subtract($d); } else { $v = $v->subtract($u); $c = $c->subtract($a); $d = $d->subtract($b); } } return array('gcd' => $this->_normalize($g->multiply($v)), 'x' => $this->_normalize($c), 'y' => $this->_normalize($d)); }
/** * Converts human readable representation to a 128bit int * which can be stored in MySQL using DECIMAL(39,0). * * Requires PHP to be compiled with IPv6 support. * This could be made to work without IPv6 support but * I don't think there would be much use for it if PHP * doesn't support IPv6. * * @author Sam Clarke * @link http://www.samclarke.com/2011/07/php-ipv6-to-128bit-int/ * @param string $ip IPv4 or IPv6 address to convert * @return string 128bit string that can be used with DECIMNAL(39,0) or false */ protected static function ipToDecimal($ip) { // make sure it is an ip if (filter_var($ip, FILTER_VALIDATE_IP) === false) { throw new NotValidIpException($ip); } $parts = unpack('N*', inet_pton($ip)); // fix IPv4 if (strpos($ip, '.') !== false) { $parts = array(1 => 0, 2 => 0, 3 => 0, 4 => $parts[1]); } foreach ($parts as &$part) { // convert any unsigned ints to signed from unpack. // this should be OK as it will be a PHP float not an int if ($part < 0) { $part = 4294967296; } } $decimal = new BigInteger($parts[4]); $part3 = new BigInteger($parts[3]); $part2 = new BigInteger($parts[2]); $part1 = new BigInteger($parts[1]); $decimal = $decimal->add($part3->multiply(new BigInteger('4294967296'))); $decimal = $decimal->add($part2->multiply(new BigInteger('18446744073709551616'))); $decimal = $decimal->add($part1->multiply(new BigInteger('79228162514264337593543950336'))); $decimal = $decimal->toString(); return $decimal; }