fft5.sage 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import itertools
  2. def find_ext_order(p, n):
  3. N = 1
  4. while True:
  5. pNx_order = p^N - 1
  6. # Does n divide the group order 𝔽_{p^N}^×?
  7. if pNx_order % n == 0:
  8. return N
  9. N += 1
  10. def find_nth_root_unity(K, p, N, n):
  11. # It cannot be a quadratic residue if n is odd
  12. #assert n % 2 == 1
  13. # So there is an nth root of unity in p^N. Now we have to find it.
  14. pNx_order = p^N - 1
  15. ω = K.gens()[0]
  16. ω = ω^(pNx_order/n)
  17. assert ω^n == 1
  18. assert ω^(n - 1) != 1
  19. return ω
  20. def vectorify(X, f, n):
  21. assert f.degree() < n
  22. fT = vector([f[i] for i in range(f.degree() + 1)] +
  23. # Zero padding
  24. [0 for _ in range(n - f.degree() - 1)])
  25. assert len(fT) == n
  26. # Just check decomposed polynomial is in the correct order
  27. assert sum([fT[i]*X^i for i in range(n)]) == f
  28. return fT
  29. def dot(a, b):
  30. assert len(a) == len(b)
  31. return [a_i*b_i for a_i, b_i in zip(a, b)]
  32. # ABC, DEF -> ADBECF
  33. def alternate(list1, list2):
  34. return itertools.chain(*zip(list1, list2))
  35. def calc_dft(n, ω_powers, f):
  36. m = len(f)
  37. if m == 1:
  38. return f
  39. g, h = vector(f[:m/2]), vector(f[m/2:])
  40. r = g + h
  41. s = dot(g - h, ω_powers)
  42. ω_powers = vector(ω_i for ω_i in ω_powers[::2])
  43. rT = calc_dft(n, ω_powers, r)
  44. sT = calc_dft(n, ω_powers, s)
  45. result = list(alternate(rT, sT))
  46. return result
  47. def test1():
  48. p = 199
  49. #n = 16
  50. n = 8
  51. assert p.is_prime()
  52. N = find_ext_order(p, n)
  53. print(f"p = {p}")
  54. print(f"n = {n}")
  55. print(f"N = {N}")
  56. print(f"p^N = {p^N}")
  57. K.<a> = GF(p^N, repr="int")
  58. ω = find_nth_root_unity(K, p, N, n)
  59. print(f"ω = {ω}")
  60. print()
  61. L.<X> = K[]
  62. #f = 9*X^7 + 45*X^6 + 33*X^5 + 7*X^3 + X^2 + 110*X + 4
  63. f = 7*X^3 + X^2 + 110*X + 4
  64. assert f.degree() < n/2
  65. print(f"f = {f}")
  66. print()
  67. ω_powers = vector(ω^i for i in range(n/2))
  68. fT = vectorify(X, f, n)
  69. dft = calc_dft(n, ω_powers, fT)
  70. print()
  71. print(f"DFT(f) = {dft}")
  72. f_evals = [f(X=ω^i) for i in range(n)]
  73. print(f"f(ω^i) = {f_evals}")
  74. def random_test():
  75. p = random_prime(1000)
  76. #n = 16
  77. n = int(2^ZZ.random_element(2, 10))
  78. assert p.is_prime()
  79. N = find_ext_order(p, n)
  80. print(f"p = {p}")
  81. print(f"n = {n}")
  82. print(f"N = {N}")
  83. print(f"p^N = {p^N}")
  84. K.<a> = GF(p^N, repr="int")
  85. ω = find_nth_root_unity(K, p, N, n)
  86. print(f"ω = {ω}")
  87. print()
  88. L.<X> = K[]
  89. #f = 9*X^7 + 45*X^6 + 33*X^5 + 7*X^3 + X^2 + 110*X + 4
  90. f = 0
  91. for i in range(n/2):
  92. f += ZZ.random_element(0, 200) * X^i
  93. assert f.degree() < n/2
  94. print(f"f = {f}")
  95. print()
  96. ω_powers = vector(ω^i for i in range(n/2))
  97. fT = vectorify(X, f, n)
  98. dft = calc_dft(n, ω_powers, fT)
  99. print()
  100. print(f"DFT(f) = {dft}")
  101. f_evals = [f(X=ω^i) for i in range(n)]
  102. print(f"f(ω^i) = {f_evals}")
  103. #test1()
  104. random_test()