binary-quadratic-forms.sage 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. def is_normal(f):
  2. a, b, c = f
  3. return -a < b <= a
  4. def is_reduced(f):
  5. a, b, c = f
  6. return is_normal(f) and (a < c or (a == c and b >= 0))
  7. # Action of SL₂(ℤ) on a form (x y)ᵗ
  8. # This also will always terminate on the final reduced form in a class
  9. def reduce(f):
  10. a, b, c = f
  11. while not is_reduced((a, b, c)):
  12. if a > c or (a == c and b < 0):
  13. a, b, c = c, -b, a
  14. elif a < c:
  15. if b <= -a:
  16. a, b, c = a, b + 2*a, c + b + a
  17. else:
  18. assert b > a
  19. a, b, c = a, b - 2*a, c - b + a
  20. else:
  21. assert a == c and b >= 0
  22. a, b, c = a, b - 2*a, c - b + a
  23. return a, b, c
  24. def lincong(a, b, m):
  25. # 1
  26. g, d, e = xgcd(a, m)
  27. assert g == gcd(a, m)
  28. assert d*a + e*m == g
  29. # 2
  30. q = floor(b/g)
  31. r = b % g
  32. # 3
  33. if r != 0:
  34. return None
  35. # 4
  36. μ = q*d % m
  37. υ = m/g
  38. return μ, υ
  39. # Composition algo taken from chia class groups document
  40. def compose(f1, f2):
  41. a, b, c = f1
  42. α, β, γ = f2
  43. # 1
  44. g = (b + β)/2
  45. h = -(b - β)/2
  46. w = gcd([a, α, g])
  47. # 2
  48. j = w
  49. s = a/w
  50. t = α/w
  51. u = g/w
  52. # 3
  53. if (vals := lincong(t*u, h*u + s*c, s*t)) is None:
  54. return None
  55. μ, υ = vals
  56. # 4
  57. if (vals := lincong(t*υ, h - t*μ, s)) is None:
  58. return None
  59. λ, _ = vals
  60. # 5
  61. k = μ + υ*λ
  62. l = (k*t - h)/s
  63. m = (t*u*k - h*u - c*s)/(s*t)
  64. # 6
  65. A = s*t
  66. B = j*u - (k*t + l*s)
  67. C = k*l - j*m
  68. # 7
  69. f3 = A, B, C
  70. return reduce(f3)
  71. # Class number = 2
  72. d = -5
  73. D = 4*d
  74. e = (1, 0, 5)
  75. a = (2, 2, 3)
  76. print(compose(e, e))
  77. print(compose(e, a))
  78. print(compose(a, e))
  79. print(compose(a, a))