Skip to content

Commit

Permalink
[GR-18163] Optimize Integer#pow
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/4251
  • Loading branch information
andrykonchin authored and eregon committed Apr 25, 2024
2 parents 1ee958f + 3681dbb commit 52596ca
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -23,6 +23,7 @@ Compatibility:

Performance:
* Fix inline caching for Regexp creation from Strings (#3492, @andrykonchin, @eregon).
* Optimize `Integer#pow` method for small modulus values (#3544, @andrykonchin).

Changes:

Expand Down
14 changes: 8 additions & 6 deletions src/main/ruby/truffleruby/core/integer.rb
Expand Up @@ -130,13 +130,15 @@ def nobits?(mask)
end

def pow(e, m = undefined)
if Primitive.undefined?(m)
self ** e
else
raise TypeError, '2nd argument not allowed unless a 1st argument is integer' unless Primitive.is_a?(e, Integer)
raise TypeError, '2nd argument not allowed unless all arguments are integers' unless Primitive.is_a?(m, Integer)
raise RangeError, '1st argument cannot be negative when 2nd argument specified' if e.negative?
return self ** e if Primitive.undefined?(m)

raise TypeError, '2nd argument not allowed unless a 1st argument is integer' unless Primitive.is_a?(e, Integer)
raise TypeError, '2nd argument not allowed unless all arguments are integers' unless Primitive.is_a?(m, Integer)
raise RangeError, '1st argument cannot be negative when 2nd argument specified' if e.negative?

if Primitive.integer_fits_into_long(m)
Truffle::IntegerOperations.modular_exponentiation(self, e, m)
else
Primitive.mod_pow(self, e, m)
end
end
Expand Down
27 changes: 27 additions & 0 deletions src/main/ruby/truffleruby/core/truffle/integer_operations.rb
Expand Up @@ -35,5 +35,32 @@ def self.bits_reference_range(n, range)
0
end
end

# Implementation of a**b mod c operation
# See https://en.wikipedia.org/wiki/Modular_exponentiation
# The only difference with the Right-to-left binary method is a special handling of negative modulus -
# a**b mod -c = (a**b mod c) - c
# MRI: similar to int_pow_tmp1/int_pow_tmp2/int_pow_tmp3
def self.modular_exponentiation(base, exponent, modulus)
return 0 if modulus == 1

negative = modulus < 0
modulus = modulus.abs

result = 1
base %= modulus

while exponent > 0
if exponent.odd?
result = (result * base) % modulus
end

base = (base * base) % modulus
exponent >>= 1
end

result -= modulus if negative
result
end
end
end

0 comments on commit 52596ca

Please sign in to comment.