You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

62 lines
1.5 KiB

  1. # Primitive Root of Unity
  2. def get_primitive_root_of_unity(F, n):
  3. # using the method described by Thomas Pornin in
  4. # https://crypto.stackexchange.com/a/63616
  5. q = F.order()
  6. for k in range(q):
  7. if k==0:
  8. continue
  9. g = F(k)
  10. # g = F.random_element()
  11. if g==0:
  12. continue
  13. w = g ^ ((q-1)/n)
  14. if w^(n/2) != 1:
  15. return g, w
  16. # Roots of Unity
  17. def get_nth_roots_of_unity(n, primitive_w):
  18. w = [0]*n
  19. for i in range(n):
  20. w[i] = primitive_w^i
  21. return w
  22. # fft (Fast Fourier Transform) returns:
  23. # - nth roots of unity
  24. # - Vandermonde matrix for the nth roots of unity
  25. # - Inverse Vandermonde matrix
  26. def fft(F, n):
  27. g, primitive_w = get_primitive_root_of_unity(F, n)
  28. w = get_nth_roots_of_unity(n, primitive_w)
  29. ft = matrix(F, n)
  30. for j in range(n):
  31. row = []
  32. for k in range(n):
  33. row.append(primitive_w^(j*k))
  34. ft.set_row(j, row)
  35. ft_inv = ft^-1
  36. return w, ft, ft_inv
  37. # Fast polynomial multiplicaton using FFT
  38. def poly_mul(fa, fb, F, n):
  39. w, ft, ft_inv = fft(F, n)
  40. # compute evaluation points from polynomials fa & fb at the roots of unity
  41. a_evals = []
  42. b_evals = []
  43. for i in range(n):
  44. a_evals.append(fa(w[i]))
  45. b_evals.append(fb(w[i]))
  46. # multiply elements in a_evals by b_evals
  47. c_evals = map(operator.mul, a_evals, b_evals)
  48. c_evals = vector(c_evals)
  49. # using FFT, convert the c_evals into fc(x)
  50. fc_coef = c_evals*ft_inv
  51. fc2=P(fc_coef.list())
  52. return fc2, c_evals