binary-quadratic-forms.sage 1.6 KB

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