tonelli_shanks_algorithm.sf 1.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. #!/usr/bin/ruby
  2. #
  3. ## https://rosettacode.org/wiki/Tonelli-Shanks_algorithm
  4. #
  5. func tonelli(n, p) {
  6. legendre(n, p) == 1 || die "not a square (mod p)"
  7. var q = p-1
  8. var s = valuation(q, 2)
  9. s == 1 ? return(powmod(n, (p + 1) >> 2, p)) : (q >>= s)
  10. var c = powmod(2 ..^ p -> first {|z| legendre(z, p) == -1 }, q, p)
  11. var r = powmod(n, (q + 1) >> 1, p)
  12. var t = powmod(n, q, p)
  13. var m = s
  14. var t2 = 0
  15. while (!p.divides(t - 1)) {
  16. t2 = ((t * t) % p)
  17. var b
  18. for i in (1 ..^ m) {
  19. if (p.divides(t2 - 1)) {
  20. b = powmod(c, 1 << (m - i - 1), p)
  21. m = i
  22. break
  23. }
  24. t2 = ((t2 * t2) % p)
  25. }
  26. r = ((r * b) % p)
  27. c = ((b * b) % p)
  28. t = ((t * c) % p)
  29. }
  30. return r
  31. }
  32. var tests = [
  33. [10, 13], [56, 101], [1030, 10009], [44402, 100049],
  34. [665820697, 1000000009], [881398088036, 1000000000039],
  35. [41660815127637347468140745042827704103445750172002, 10**50 + 577],
  36. ]
  37. for n,p in tests {
  38. var r = tonelli(n, p)
  39. assert((r*r - n) % p == 0)
  40. say "Roots of #{n} are (#{r}, #{p-r}) mod #{p}"
  41. }