Exemple #1
0
 /**
  * Performs modular exponentiation.
  *
  * Here's a quick 'n dirty example:
  * <code>
  * <?php
  *    include('Math/BigInteger.php');
  *
  *    $a = new Math_BigInteger('10');
  *    $b = new Math_BigInteger('20');
  *    $c = new Math_BigInteger('30');
  *
  *    $c = $a->modPow($b, $c);
  *
  *    echo $c->toString(); // outputs 10
  * ?>
  * </code>
  *
  * @param Math_BigInteger $e
  * @param Math_BigInteger $n
  * @return Math_BigInteger
  * @access public
  * @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
  *    and although the approach involving repeated squaring does vastly better, it, too, is impractical
  *    for our purposes.  The reason being that division - by far the most complicated and time-consuming
  *    of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
  *
  *    Modular reductions resolve this issue.  Although an individual modular reduction takes more time
  *    then an individual division, when performed in succession (with the same modulo), they're a lot faster.
  *
  *    The two most commonly used modular reductions are Barrett and Montgomery reduction.  Montgomery reduction,
  *    although faster, only works when the gcd of the modulo and of the base being used is 1.  In RSA, when the
  *    base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
  *    the product of two odd numbers is odd), but what about when RSA isn't used?
  *
  *    In contrast, Barrett reduction has no such constraint.  As such, some bigint implementations perform a
  *    Barrett reduction after every operation in the modpow function.  Others perform Barrett reductions when the
  *    modulo is even and Montgomery reductions when the modulo is odd.  BigInteger.java's modPow method, however,
  *    uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
  *    the other, a power of two - and recombine them, later.  This is the method that this modPow function uses.
  *    {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
  */
 function modPow($e, $n)
 {
     $n = $n->abs();
     if ($e->compare(new Math_BigInteger()) < 0) {
         $e = $e->abs();
         $temp = $this->modInverse($n);
         if ($temp === false) {
             return false;
         }
         return $temp->modPow($e, $n);
     }
     switch (MATH_BIGINTEGER_MODE) {
         case MATH_BIGINTEGER_MODE_GMP:
             $temp = new Math_BigInteger();
             $temp->value = gmp_powm($this->value, $e->value, $n->value);
             return $temp;
         case MATH_BIGINTEGER_MODE_BCMATH:
             // even though the last parameter is optional, according to php.net, it's not optional in
             // PHP_Compat 1.5.0 when running PHP 4.
             $temp = new Math_BigInteger();
             $temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
             return $temp;
     }
     if (empty($e->value)) {
         $temp = new Math_BigInteger();
         $temp->value = array(1);
         return $temp;
     }
     if ($e->value == array(1)) {
         list(, $temp) = $this->divide($n);
         return $temp;
     }
     if ($e->value == array(2)) {
         $temp = $this->_square();
         list(, $temp) = $temp->divide($n);
         return $temp;
     }
     // is the modulo odd?
     if ($n->value[0] & 1) {
         return $this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY);
     }
     // if it's not, it's even
     // find the lowest set bit (eg. the max pow of 2 that divides $n)
     for ($i = 0; $i < count($n->value); $i++) {
         if ($n->value[$i]) {
             $temp = decbin($n->value[$i]);
             $j = strlen($temp) - strrpos($temp, '1') - 1;
             $j += 26 * $i;
             break;
         }
     }
     // at this point, 2^$j * $n/(2^$j) == $n
     $mod1 = $n->_copy();
     $mod1->_rshift($j);
     $mod2 = new Math_BigInteger();
     $mod2->value = array(1);
     $mod2->_lshift($j);
     $part1 = $mod1->value != array(1) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger();
     $part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2);
     $y1 = $mod2->modInverse($mod1);
     $y2 = $mod1->modInverse($mod2);
     $result = $part1->multiply($mod2);
     $result = $result->multiply($y1);
     $temp = $part2->multiply($mod1);
     $temp = $temp->multiply($y2);
     $result = $result->add($temp);
     list(, $result) = $result->divide($n);
     return $result;
 }
Exemple #2
0
 /**
  * Logical Left Shift
  *
  * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
  *
  * @param int $shift
  * @return Math_BigInteger
  * @access public
  * @internal The only version that yields any speed increases is the internal version.
  */
 function bitwise_leftShift($shift)
 {
     $temp = new Math_BigInteger();
     switch (MATH_BIGINTEGER_MODE) {
         case MATH_BIGINTEGER_MODE_GMP:
             static $two;
             if (!isset($two)) {
                 $two = gmp_init('2');
             }
             $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
             break;
         case MATH_BIGINTEGER_MODE_BCMATH:
             $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
             break;
         default:
             // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
             // and I don't want to do that...
             $temp->value = $this->value;
             $temp->_lshift($shift);
     }
     return $this->_normalize($temp);
 }
Exemple #3
0
 public function bitwise_leftShift($shift)
 {
     $temp = new Math_BigInteger();
     switch (MATH_BIGINTEGER_MODE) {
         case MATH_BIGINTEGER_MODE_GMP:
             static $two;
             if (!isset($two)) {
                 $two = gmp_init('2');
             }
             $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
             break;
         case MATH_BIGINTEGER_MODE_BCMATH:
             $temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
             break;
         default:
             $temp->value = $this->value;
             $temp->_lshift($shift);
     }
     return $this->_normalize($temp);
 }