/*$this->normalize*/($a), 's' => /*$this->normalize*/($s), 't' => /*$this->normalize*/($t) ]; } // gmp_hamdist ( GMP $a , GMP $b ) : int // Hamming distance function gmp_hamdist($a, $b) { bcscale(0); throw new Exception("gmp_hamdist() NOT IMPLEMENTED"); } // gmp_import ( string $data [, int $word_size = 1 [, int $options = GMP_MSW_FIRST | GMP_NATIVE_ENDIAN ]] ) : GMP // Import from a binary string function gmp_import($data, $word_size=1, $options=GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) { bcscale(0); if ($word_size != 1) throw new Exception("Word size != 1 not implemented"); if ($options != GMP_MSW_FIRST | GMP_NATIVE_ENDIAN) throw new Exception("Different options not implemented"); return gmp_init(hex2bin(gmp_strval(gmp_init($data), 16))); } // gmp_init ( mixed $number [, int $base = 0 ] ) : GMP // Create GMP number function gmp_init($number, $base=0) { bcscale(0); if ($base == 0) { // If base is 0 (default value), the actual base is determined from the leading characters: // if the first two characters are 0x or 0X, hexadecimal is assumed, // otherwise if the first character is "0", octal is assumed, // otherwise decimal is assumed. if (strtoupper(substr($number, 0, 2)) == '0X') { $base = 16; } else if (strtoupper(substr($number, 0, 1)) == '0') { $base = 8; } else { $base = 10; } } if ($base == 10) { return $number; } else { return base_convert_bigint($number, $base, 10); } } // gmp_intval ( GMP $gmpnumber ) : int // Convert GMP number to integer function gmp_intval($gmpnumber) { bcscale(0); return (int)$gmpnumber; } // gmp_invert ( GMP $a , GMP $b ) : GMP // Inverse by modulo function gmp_invert($a, $b) { bcscale(0); // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L246 while (bccomp($a, '0')==-1) { $a=bcadd($b, $a); } while (bccomp($b, $a)==-1) { $a=bcmod($a, $b); } $c=$a; $d=$b; $uc=1; $vc=0; $ud=0; $vd=1; while (bccomp($c, '0')!=0) { $temp1=$c; $q=bcdiv($d, $c, 0); $c=bcmod($d, $c); $d=$temp1; $temp2=$uc; $temp3=$vc; $uc=bcsub($ud, bcmul($q, $uc)); $vc=bcsub($vd, bcmul($q, $vc)); $ud=$temp2; $vd=$temp3; } $result=''; if (bccomp($d, '1')==0) { if (bccomp($ud, '0')==1) { $result=$ud; } else { $result=bcadd($ud, $b); } } else { throw new ErrorException("ERROR: $a and $b are NOT relatively prime."); } return $result; } // gmp_jacobi ( GMP $a , GMP $p ) : int // Jacobi symbol function gmp_jacobi($a, $p) { bcscale(0); // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L136 if ($p>=3 && $p%2==1) { $a = bcmod($a, $p); if ($a == '0') return '0'; if ($a == '1') return '1'; $a1 = $a; $e = 0; while (bcmod($a1, '2') == '0') { $a1 = bcdiv($a1, '2'); $e = bcadd($e, '1'); } $s = (bcmod($e, '2')=='0' || bcmod($p, '8')=='1' || bcmod($p, '8')=='7') ? '1' : '-1'; if ($a1 == '1') return $s; if (bcmod($p, '4')=='3' && bcmod($a1, '4')=='3') $s = -$s; return bcmul($s, (string)gmp_jacobi(bcmod($p, $a1), $a1)); } else { return false; } } // gmp_kronecker ( mixed $a , mixed $b ) : int // Kronecker symbol function gmp_kronecker($a, $b) { bcscale(0); throw new Exception("gmp_kronecker() NOT IMPLEMENTED"); } // gmp_lcm ( mixed $a , mixed $b ) : GMP // Calculate LCM function gmp_lcm($a, $b) { bcscale(0); if ((bccomp($a,'0')==0) && (bccomp($b,'0')==0)) { return '0'; } else { return gmp_div(gmp_abs(gmp_mul($a,$b)), gmp_gcd($a,$b)); } } // gmp_legendre ( GMP $a , GMP $p ) : int // Legendre symbol function gmp_legendre($a, $p) { bcscale(0); throw new Exception("gmp_legendre() NOT IMPLEMENTED"); } // gmp_mod ( GMP $n , GMP $d ) : GMP // Modulo operation function gmp_mod($n, $d) { bcscale(0); // bcmod ( string $dividend , string $divisor [, int $scale = 0 ] ) : string return bcmod($n, $d); } // gmp_mul ( GMP $a , GMP $b ) : GMP // Multiply numbers function gmp_mul($a, $b) { bcscale(0); // bcmul ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string return bcmul($a, $b); } // gmp_neg ( GMP $a ) : GMP // Negate number function gmp_neg($a) { bcscale(0); return bcmul($a, "-1"); } // gmp_nextprime ( int $a ) : GMP // Find next prime number function gmp_nextprime($a) { bcscale(0); // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L692 if (bccomp($a, '2') == '-1') { return '2'; } $result = gmp_or(bcadd($a, '1'), '1'); while (!gmp_prob_prime($result)) { $result = bcadd($result, '2'); } return $result; } // gmp_or ( GMP $a , GMP $b ) : GMP // Bitwise OR function gmp_or($a, $b) { bcscale(0); // Convert $a and $b to a binary string $ab = bc_dec2bin($a); $bb = bc_dec2bin($b); $length = max(strlen($ab), strlen($bb)); $ab = str_pad($ab, $length, "0", STR_PAD_LEFT); $bb = str_pad($bb, $length, "0", STR_PAD_LEFT); // Do the bitwise binary operation $cb = ''; for ($i=0; $i<$length; $i++) { $cb .= (($ab[$i] == 1) or ($bb[$i] == 1)) ? '1' : '0'; } // Convert back to a decimal number return bc_bin2dec($cb); } // gmp_perfect_power ( mixed $a ) : bool // Perfect power check function gmp_perfect_power($a) { bcscale(0); throw new Exception("gmp_perfect_power() NOT IMPLEMENTED"); } // gmp_perfect_square ( GMP $a ) : bool // Perfect square check function gmp_perfect_square($a) { bcscale(0); throw new Exception("gmp_perfect_square() NOT IMPLEMENTED"); } // gmp_popcount ( GMP $a ) : int // Population count function gmp_popcount($a) { bcscale(0); $ab = bc_dec2bin($a); return substr_count($ab, '1'); } // gmp_pow ( GMP $base , int $exp ) : GMP // Raise number into power function gmp_pow($base, $exp) { bcscale(0); // bcpow ( string $base , string $exponent [, int $scale = 0 ] ) : string return bcpow($base, $exp); } // gmp_powm ( GMP $base , GMP $exp , GMP $mod ) : GMP // Raise number into power with modulo function gmp_powm($base, $exp, $mod) { bcscale(0); // bcpowmod ( string $base , string $exponent , string $modulus [, int $scale = 0 ] ) : string return bcpowmod($base, $exp, $mod); } // gmp_prob_prime ( GMP $a [, int $reps = 10 ] ) : int // Check if number is "probably prime" function gmp_prob_prime($a, $reps=10) { bcscale(0); // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/NumberTheory.php#L655 $t = 40; $k = 0; $m = bcsub($reps, '1'); while (bcmod($m, '2') == '0') { $k = bcadd($k, '1'); $m = bcdiv($m, '2'); } for ($i=0; $i<$t; $i++) { $a = bcrand('1', bcsub($reps, '1')); if ($m < 0) { return new ErrorException("Negative exponents ($m) not allowed"); } else { $b0 = bcpowmod($a, $m, $reps); } if ($b0!=1 && $b0!=bcsub($reps, '1')) { $j = 1; while ($j<=$k-1 && $b0!=bcsub($reps, '1')) { $b0 = bcpowmod($b0, '2', $reps); if ($b0 == 1) { return false; } $j++; } if ($b0 != bcsub($reps, '1')) { return false; } } } return true; } // gmp_random_bits ( int $bits ) : GMP // Random number function gmp_random_bits($bits) { bcscale(0); $min = 0; $max = bcsub(bcpow('2', $bits), '1'); return bcrand($min, $max); } // gmp_random_range ( GMP $min , GMP $max ) : GMP // Random number function gmp_random_range($min, $max) { bcscale(0); return bcrand($min, $max); } // gmp_random_seed ( mixed $seed ) : void // Sets the RNG seed function gmp_random_seed($seed) { bcscale(0); bcrand_seed($seed); } // gmp_random ([ int $limiter = 20 ] ) : GMP // Random number (deprecated) function gmp_random($limiter=20) { bcscale(0); throw new Exception("gmp_random() is deprecated! Please use gmp_random_bits() or gmp_random_range() instead."); } // gmp_root ( GMP $a , int $nth ) : GMP // Take the integer part of nth root function gmp_root($a, $nth) { bcscale(0); throw new Exception("gmp_root() NOT IMPLEMENTED"); } // gmp_rootrem ( GMP $a , int $nth ) : array // Take the integer part and remainder of nth root function gmp_rootrem($a, $nth) { bcscale(0); throw new Exception("gmp_rootrem() NOT IMPLEMENTED"); } // gmp_scan0 ( GMP $a , int $start ) : int // Scan for 0 function gmp_scan0($a, $start) { bcscale(0); $ab = bc_dec2bin($a); if ($start < 0) throw new Exception("Starting index must be greater than or equal to zero"); if ($start >= strlen($ab)) return $start; for ($i=$start; $i= strlen($ab)) return -1; for ($i=$start; $i= strlen($ab)) { $ab = str_pad($ab, $index+1, '0', STR_PAD_LEFT); } $ab[strlen($ab)-1-$index] = $bit_on ? '1' : '0'; $a = bc_bin2dec($ab); } // gmp_sign ( GMP $a ) : int // Sign of number function gmp_sign($a) { bcscale(0); return bccomp($a, "0"); } // gmp_sqrt ( GMP $a ) : GMP // Calculate square root function gmp_sqrt($a) { bcscale(0); // bcsqrt ( string $operand [, int $scale = 0 ] ) : string return bcsqrt($a); } // gmp_sqrtrem ( GMP $a ) : array // Square root with remainder function gmp_sqrtrem($a) { bcscale(0); throw new Exception("gmp_sqrtrem() NOT IMPLEMENTED"); } // gmp_strval ( GMP $gmpnumber [, int $base = 10 ] ) : string // Convert GMP number to string function gmp_strval($gmpnumber, $base=10) { bcscale(0); if ($base == 10) { return $gmpnumber; } else { return base_convert_bigint($gmpnumber, 10, $base); } } // gmp_sub ( GMP $a , GMP $b ) : GMP // Subtract numbers function gmp_sub($a, $b) { bcscale(0); // bcsub ( string $left_operand , string $right_operand [, int $scale = 0 ] ) : string return bcsub($a, $b); } // gmp_testbit ( GMP $a , int $index ) : bool // Tests if a bit is set function gmp_testbit($a, $index) { bcscale(0); $ab = bc_dec2bin($a); if ($index < 0) throw new Exception("Invalid index"); if ($index >= strlen($ab)) return ('0' == '1'); return $ab[strlen($ab)-1-$index] == '1'; } // gmp_xor ( GMP $a , GMP $b ) : GMP // Bitwise XOR function gmp_xor($a, $b) { bcscale(0); // Convert $a and $b to a binary string $ab = bc_dec2bin($a); $bb = bc_dec2bin($b); $length = max(strlen($ab), strlen($bb)); $ab = str_pad($ab, $length, "0", STR_PAD_LEFT); $bb = str_pad($bb, $length, "0", STR_PAD_LEFT); // Do the bitwise binary operation $cb = ''; for ($i=0; $i<$length; $i++) { $cb .= (($ab[$i] == 1) xor ($bb[$i] == 1)) ? '1' : '0'; } // Convert back to a decimal number return bc_bin2dec($cb); } } // ----------------- Helper functions ----------------- function base_convert_bigint($numstring, $frombase, $tobase) { $numstring = "".$numstring; $frombase_str = ''; for ($i=0; $i<$frombase; $i++) { $frombase_str .= strtoupper(base_convert((string)$i, 10, 36)); } $tobase_str = ''; for ($i=0; $i<$tobase; $i++) { $tobase_str .= strtoupper(base_convert((string)$i, 10, 36)); } $length = strlen($numstring); $result = ''; $number = array(); for ($i = 0; $i < $length; $i++) { $number[$i] = stripos($frombase_str, $numstring[$i]); } do { // Loop until whole number is converted $divide = 0; $newlen = 0; for ($i = 0; $i < $length; $i++) { // Perform division manually (which is why this works with big numbers) $divide = $divide * $frombase + $number[$i]; if ($divide >= $tobase) { $number[$newlen++] = (int)($divide / $tobase); $divide = $divide % $tobase; } else if ($newlen > 0) { $number[$newlen++] = 0; } } $length = $newlen; $result = $tobase_str[$divide] . $result; // Divide is basically $numstring % $tobase (i.e. the new character) } while ($newlen != 0); return $result; } function bc_dec2bin($decimal_i) { // https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/ bcscale(0); $binary_i = ''; do { $binary_i = bcmod($decimal_i,'2') . $binary_i; $decimal_i = bcdiv($decimal_i,'2'); } while (bccomp($decimal_i,'0')); return $binary_i; } function bc_bin2dec($binary_i) { // https://www.exploringbinary.com/base-conversion-in-php-using-bcmath/ bcscale(0); $decimal_i = '0'; for ($i = 0; $i < strlen($binary_i); $i++) { $decimal_i = bcmul($decimal_i,'2'); $decimal_i = bcadd($decimal_i,$binary_i[$i]); } return $decimal_i; } // ----------------- New functions ----------------- // Newly added: gmp_not / bcnot function bcnot($a) { bcscale(0); // Convert $a to a binary string $ab = bc_dec2bin($a); $length = strlen($ab); // Do the bitwise binary operation $cb = ''; for ($i=0; $i<$length; $i++) { $cb .= ($ab[$i] == 1) ? '0' : '1'; } // Convert back to a decimal number return bc_bin2dec($cb); } function gmp_not($a) { bcscale(0); return bcnot($a); } // Newly added: bcshiftl / gmp_shiftl function bcshiftl($num, $bits) { bcscale(0); return bcmul($num, bcpow('2', $bits)); } function gmp_shiftl($num, $bits) { bcscale(0); return bcshiftl($num, $bits); } // Newly added: bcshiftr / gmp_shiftr function bcshiftr($num, $bits) { bcscale(0); return bcdiv($num, bcpow('2', $bits)); } function gmp_shiftr($num, $bits) { bcscale(0); return bcshiftr($num, $bits); } // Newly added: bcfact (used by gmp_fact) function bcfact($a) { bcscale(0); // Source: https://www.php.net/manual/de/book.bc.php#116510 if (!filter_var($a, FILTER_VALIDATE_INT) || $a <= 0) { throw new InvalidArgumentException(sprintf('Argument must be natural number, "%s" given.', $a)); } for ($result = '1'; $a > 0; $a--) { $result = bcmul($result, $a); } return $result; } // Newly added (used by gmp_prob_prime, gmp_random_range and gmp_random_bits) function bcrand($min, $max = false) { bcscale(0); // Source: https://github.com/CityOfZion/neo-php/blob/master/src/Crypto/BCMathUtils.php#L7 // Fixed: https://github.com/CityOfZion/neo-php/issues/16 if (!$max) { $max = $min; $min = 0; } return bcadd(bcmul(bcdiv((string)mt_rand(), (string)mt_getrandmax(), strlen($max)), bcsub(bcadd($max, '1'), $min)), $min); } // Newly added (used by gmp_random_seed) function bcrand_seed($seed) { bcscale(0); mt_srand($seed); } }