cipolla_algorithm.sf 1.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. #!/usr/bin/ruby
  2. # Cipolla's algorithm, for solving a congruence of the form:
  3. # x^2 == n (mod p)
  4. # where p is an odd prime.
  5. # See also:
  6. # https://en.wikipedia.org/wiki/Cipolla's_algorithm
  7. # https://rosettacode.org/wiki/Cipolla%27s_algorithm
  8. func cipolla(n, p) {
  9. legendre(n, p) == 1 || return nil
  10. var (a = 0, ω2 = 0)
  11. loop {
  12. ω2 = ((a*a - n) % p)
  13. if (kronecker(ω2, p) == -1) {
  14. break
  15. }
  16. ++a
  17. }
  18. struct point { x, y }
  19. func mul(a, b) {
  20. point((a.x*b.x + a.y*b.y*ω2) % p, (a.x*b.y + b.x*a.y) % p)
  21. }
  22. var r = point(1, 0)
  23. var s = point(a, 1)
  24. for (var n = ((p+1) >> 1); n > 0; n >>= 1) {
  25. r = mul(r, s) if n.is_odd
  26. s = mul(s, s)
  27. }
  28. r.y == 0 ? r.x : nil
  29. }
  30. var tests = [
  31. [10, 13],
  32. [56, 101],
  33. [8218, 10007],
  34. [8219, 10007],
  35. [331575, 1000003],
  36. [665165880, 1000000007],
  37. [881398088036 1000000000039],
  38. [34035243914635549601583369544560650254325084643201, 10**50 + 151],
  39. ]
  40. for n,p in tests {
  41. var r = cipolla(n, p)
  42. if (defined(r)) {
  43. say "Roots of #{n} are (#{r} #{p-r}) mod #{p}"
  44. } else {
  45. say "No solution for (#{n}, #{p})"
  46. }
  47. }