0 ) { $hex = dechex( (int) bcmod( $n, '16' ) ) . $hex; $n = bcdiv( $n, '16' ); } } if ( $pad > 0 && strlen( $hex ) < $pad ) { $hex = str_repeat( '0', $pad - strlen( $hex ) ) . $hex; } return $hex; } /** Fixed-width big-endian bytes. */ public static function to_bin( $a, $bytes = 32 ) { return hex2bin( self::to_hex( $a, $bytes * 2 ) ); } // ------------------------------------------------------------ arithmetic public static function add( $a, $b ) { return self::BACKEND_GMP === self::backend() ? gmp_add( $a, $b ) : bcadd( $a, $b ); } public static function sub( $a, $b ) { return self::BACKEND_GMP === self::backend() ? gmp_sub( $a, $b ) : bcsub( $a, $b ); } public static function mul( $a, $b ) { return self::BACKEND_GMP === self::backend() ? gmp_mul( $a, $b ) : bcmul( $a, $b ); } /** Always returns a non-negative residue, matching gmp_mod's sign rule. */ public static function mod( $a, $m ) { if ( self::BACKEND_GMP === self::backend() ) { return gmp_mod( $a, $m ); } $r = bcmod( $a, $m ); return bccomp( $r, '0' ) < 0 ? bcadd( $r, $m ) : $r; } public static function pow_mod( $a, $e, $m ) { return self::BACKEND_GMP === self::backend() ? gmp_powm( $a, $e, $m ) : bcpowmod( $a, $e, $m ); } /** -1, 0 or 1. */ public static function cmp( $a, $b ) { return self::BACKEND_GMP === self::backend() ? gmp_cmp( $a, $b ) : bccomp( $a, $b ); } public static function is_zero( $a ) { return 0 === self::cmp( $a, self::from_int( 0 ) ); } public static function is_odd( $a ) { if ( self::BACKEND_GMP === self::backend() ) { return 1 === gmp_intval( gmp_mod( $a, gmp_init( 2 ) ) ); } return '1' === bcmod( $a, '2' ); } /** * Modular inverse by Fermat's little theorem: a^(m-2) mod m. * * Only correct for prime moduli, which is all this library ever uses (the * field prime p and the group order n are both prime). Saves carrying an * extended-Euclid implementation that BCMath would make painful. */ public static function inv_mod( $a, $m ) { return self::pow_mod( $a, self::sub( $m, self::from_int( 2 ) ), $m ); } /** * The scalar's bits, most-significant first, as a string of '0'/'1'. * * Point multiplication walks these. Deriving them from hex rather than by * repeated division keeps the BCMath path from doing 256 bcdiv calls. */ public static function bits( $a ) { $hex = self::to_hex( $a, 0 ); $bits = ''; $len = strlen( $hex ); for ( $i = 0; $i < $len; $i++ ) { $bits .= str_pad( decbin( hexdec( $hex[ $i ] ) ), 4, '0', STR_PAD_LEFT ); } return ltrim( $bits, '0' ); } }