1

I want to compute 1_299_709 ** 1_300_751 % 104_729 in Crystal.

In Python, the pow function allows to pass the modulo as third argument:

❯ python
>>> pow(1_299_709, 1_300_751, 104_729)
90827

In Ruby, the same:

❯ irb
irb(main):001:0> 1_299_709.pow(1_300_751, 104_729)
=> 90827

But in Crystal, there seems not to be such a functionality, and naturally, using ** operators quickly overflows:

❯ crystal eval "1_299_709 ** 1_300_751 % 104_729"
Unhandled exception: Arithmetic overflow (OverflowError)
  from /usr/lib/crystal/int.cr:0:9 in '**'
  from /eval:1:1 in '__crystal_main'
  from /usr/lib/crystal/crystal/main.cr:97:5 in 'main_user_code'
  from /usr/lib/crystal/crystal/main.cr:86:7 in 'main'
  from /usr/lib/crystal/crystal/main.cr:106:3 in 'main'
  from __libc_start_main
  from _start
  from ???

How to compute a modular exponentiation in Crystal?


Edit: To clarify, I'm already using BigInt but that doesn't work. I removed BigInt from my minimal working example for simplicity.

The following Python code contains the actual numbers from my program:

>>> pow(53583115773616729421957814870755484980404298242901134400501331255090818409243, 28948022309329048855892746252171976963317496166410141009864396001977208667916, 115792089237316195423570985008687907853269984665640564039457584007908834671663)
75711134420273723792089656449854389054866833762486990555172221523628676983696

It executes easily and returns the correct result. Same for Ruby:

irb(main):001:0> 53583115773616729421957814870755484980404298242901134400501331255090818409243.pow(2894802230932904885589274625217197696331749616641014100986
4396001977208667916, 115792089237316195423570985008687907853269984665640564039457584007908834671663)
=> 75711134420273723792089656449854389054866833762486990555172221523628676983696

However, Crystal:

a = BigInt.new 53583115773616729421957814870755484980404298242901134400501331255090818409243
e = BigInt.new 28948022309329048855892746252171976963317496166410141009864396001977208667916
p = BigInt.new 115792089237316195423570985008687907853269984665640564039457584007908834671663
y = a ** e % p # overflows with and without BigInt

Is resulting in:

gmp: overflow in mpz type
Program received and didn't handle signal IOT (6)

How to compute such a massive modular exponentiation in Crystal?


Edit: Filed an issue to make sure it's not a bug: crystal-lang/crystal#8612

q9f
  • 11,293
  • 8
  • 57
  • 96

1 Answers1

1

As stated in the Github issue, this can be easily circumvented by binding mpz_powm_sec from gmp:

This is pretty simple: https://carc.in/#/r/89qh

require "big/big_int"

a = BigInt.new "53583115773616729421957814870755484980404298242901134400501331255090818409243"
e = BigInt.new "28948022309329048855892746252171976963317496166410141009864396001977208667916"
p = BigInt.new "115792089237316195423570985008687907853269984665640564039457584007908834671663"

@[Link("gmp")]
lib LibGMP
  fun mpz_powm_sec = __gmpz_powm_sec(rop : MPZ*, base : MPZ*, exp : MPZ*, mod : MPZ*)
end 

#y = a ** e % p
y = BigInt.new
LibGMP.mpz_powm_sec(y, a, e, p)
puts y

# > 75711134420273723792089656449854389054866833762486990555172221523628676983696
q9f
  • 11,293
  • 8
  • 57
  • 96