Longfellow ZK 0290cb32

Loading...
Searching...
No Matches
rfft.h
1// Copyright 2025 Google LLC.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef PRIVACY_PROOFS_ZK_LIB_ALGEBRA_RFFT_H_
16#define PRIVACY_PROOFS_ZK_LIB_ALGEBRA_RFFT_H_
17
18#include <stddef.h>
19#include <stdint.h>
20
21#include "algebra/permutations.h"
22#include "algebra/twiddle.h"
23#include "util/panic.h"
24
25 namespace proofs {
26
27// Real FFT and its inverse.
28//
29// The FFT F[j] of a real input R[k] is complex and
30// conjugate-symmetric: F[j] = conj(F[n - j]).
31//
32// Following the FFTW conventions, to avoid doubling the
33// storage, we store F[j] as a "half-complex" array HC[j] of elements
34// in the base field.
35//
36// HC[j] = (2j <= n) ? real(F[j]) : imag(F[n - j])
37//
38// Thus we have two kinds of transforms: R2HC (real to
39// half-complex) and HC2R (half-complex to real).
40//
41// Again following the FFTW conventions, we say that
42// the R2HC transform is "forward" (minus sign in the exponent)
43// and the HC2R sign is "backward" (plus sign in the exponent).
44// See fft.h for a definition of forward and backward.
45
46 template <class FieldExt>
47 class RFFT {
48 using Field = typename FieldExt::BaseField;
49 using RElt = typename Field::Elt;
50 using CElt = typename FieldExt::Elt;
51
52 // The machinery in this file only works if the root is
53 // on the unit circle, because we multiply by the conjugate
54 // instead of by the inverse.
55 static void validate_root(const CElt& omega, const FieldExt& C) {
56 check(C.mulf(omega, C.conjf(omega)) == C.one(),
57 "root of unity not on the unit circle");
58 }
59
60 static void r2hcI(RElt* A, size_t s, const Field& R) {
61 RElt t = A[s];
62 A[s] = A[0];
63 R.add(A[0], t);
64 R.sub(A[s], t);
65 }
66 static void r2hcII(RElt* A, size_t s, const CElt& tw, const Field& R) {
67 R.mul(A[s], R.negf(tw.im));
68 }
69
70 static void hc2hcf(RElt* Ar, RElt* Ai, size_t s, const CElt& tw,
71 const Field& R) {
72 RElt xr, xi;
73 cmulj(&xr, &xi, Ar[s], Ai[s], tw.re, tw.im, R);
74 RElt ar0 = Ar[0];
75 RElt ai0 = Ai[0];
76 Ar[0] = R.addf(ar0, xr);
77 Ai[0] = R.subf(ar0, xr);
78 Ar[s] = R.subf(xi, ai0);
79 Ai[s] = R.addf(xi, ai0);
80 }
81
82 static void hc2rI(RElt* A, size_t s, const Field& R) {
83 RElt t = A[s];
84 A[s] = A[0];
85 R.add(A[0], t);
86 R.sub(A[s], t);
87 }
88
89 static void hc2rIII(RElt* A, size_t s, const CElt& tw, const Field& R) {
90 R.add(A[0], A[0]);
91 R.add(A[s], A[s]);
92 R.mul(A[s], R.negf(tw.im));
93 }
94
95 static void hc2hcb(RElt* Ar, RElt* Ai, size_t s, const CElt& tw,
96 const Field& R) {
97 RElt ar0 = Ar[0];
98 RElt ai0 = Ai[0];
99 RElt ar1 = Ar[s];
100 RElt ai1 = Ai[s];
101 Ar[0] = R.addf(ar0, ai0);
102 Ai[0] = R.subf(ai1, ar1);
103 RElt xr = R.subf(ar0, ai0);
104 RElt xi = R.addf(ai1, ar1);
105 cmul(&Ar[s], &Ai[s], xr, xi, tw.re, tw.im, R);
106 }
107
108 public:
109 // Forward real to half-complex in-place transform.
110 // N (the length of A) must be a power of 2
111 static void r2hc(RElt A[/*n*/], size_t n, const CElt& omega,
112 uint64_t omega_order, const FieldExt& C) {
113 validate_root(omega, C);
114
115 if (n <= 1) {
116 return;
117 }
118
119 const Field& R = C.base_field();
120 CElt omega_n = Twiddle<FieldExt>::reroot(omega, omega_order, n, C);
121 Twiddle<FieldExt> roots(n, omega_n, C);
122
123 Permutations<RElt>::bitrev(A, n);
124
125 // m=1 iteration
126 for (size_t k = 0; k < n; k += 2) {
127 r2hcI(&A[k], 1, R);
128 }
129
130 // m>1 iterations
131 for (size_t m = 2; m < n; m = 2 * m) {
132 size_t ws = n / (2 * m);
133 for (size_t k = 0; k < n; k += 2 * m) {
134 size_t j;
135 r2hcI(&A[k], m, R); // j==0
136
137 for (j = 1; j + j < m; ++j) {
138 hc2hcf(&A[k + j], &A[k + m - j], m, roots.w_[j * ws], R);
139 }
140
141 r2hcII(&A[k + j], m, roots.w_[j * ws], R); // j==m/2
142 }
143 }
144 }
145
146 // Backward half-complex to real in-place transform.
147 static void hc2r(RElt A[/*n*/], size_t n, const CElt& omega,
148 uint64_t omega_order, const FieldExt& C) {
149 validate_root(omega, C);
150
151 if (n <= 1) {
152 return;
153 }
154
155 const Field& R = C.base_field();
156 CElt omega_n = Twiddle<FieldExt>::reroot(omega, omega_order, n, C);
157 Twiddle<FieldExt> roots(n, omega_n, C);
158
159 // m>1 iterations
160 for (size_t m = n; (m /= 2) >= 2;) {
161 size_t ws = n / (2 * m);
162 for (size_t k = 0; k < n; k += 2 * m) {
163 size_t j;
164 hc2rI(&A[k], m, R); // j==0
165
166 for (j = 1; j + j < m; ++j) {
167 hc2hcb(&A[k + j], &A[k + m - j], m, roots.w_[j * ws], R);
168 }
169
170 hc2rIII(&A[k + j], m, roots.w_[j * ws], R); // j==m/2
171 }
172 }
173
174 // m=1 iteration
175 for (size_t k = 0; k < n; k += 2) {
176 hc2rI(&A[k], 1, R);
177 }
178
179 Permutations<RElt>::bitrev(A, n);
180 }
181
182 // X = A * B
183 static void cmul(RElt* xr, RElt* xi, const RElt& ar, const RElt& ai,
184 const RElt& br, const RElt& bi, const Field& R) {
185 // Karatsuba 3 mul + 5 add
186 auto p0 = R.mulf(ar, br);
187 auto p1 = R.mulf(ai, bi);
188 auto a01 = R.addf(ar, ai);
189 auto b01 = R.addf(br, bi);
190 *xr = R.subf(p0, p1);
191 R.mul(a01, b01);
192 R.sub(a01, p0);
193 R.sub(a01, p1);
194 *xi = a01;
195 }
196
197 // X = A * conj(B)
198 static void cmulj(RElt* xr, RElt* xi, const RElt& ar, const RElt& ai,
199 const RElt& br, const RElt& bi, const Field& R) {
200 // Karatsuba 3 mul + 5 add
201 auto p0 = R.mulf(ar, br);
202 auto p1 = R.mulf(ai, bi);
203 auto a01 = R.addf(ar, ai);
204 auto b01 = R.subf(br, bi);
205 *xr = R.addf(p0, p1);
206 R.mul(a01, b01);
207 R.sub(a01, p0);
208 R.add(a01, p1);
209 *xi = a01;
210 }
211};
212
213} // namespace proofs
214
215#endif // PRIVACY_PROOFS_ZK_LIB_ALGEBRA_RFFT_H_
proofs::RFFT
Definition rfft.h:47
proofs::Twiddle
Definition twiddle.h:27
proofs::GF2_128::Elt
Definition gf2_128.h:63