Crypt-PQClean-Sign
view release on metacpan or search on metacpan
pqclean/crypto_sign/falcon-1024/clean/fft.c view on Meta::CPAN
* set of values f(w_j). Since f is real, conj(f(w_j)) = f(conj(w_j)),
* thus f(w_{N-1-j}) = conj(f(w_j)). We thus store only half the values,
* for j = 0 to N/2-1; the other half can be recomputed easily when (if)
* needed. A consequence is that FFT representation has the same size
* as normal representation: N/2 complex numbers use N real numbers (each
* complex number is the combination of a real and an imaginary part).
*
* We use a specific ordering which makes computations easier. Let rev()
* be the bit-reversal function over log(N) bits. For j in 0..N/2-1, we
* store the real and imaginary parts of f(w_j) in slots:
*
* Re(f(w_j)) -> slot rev(j)/2
* Im(f(w_j)) -> slot rev(j)/2+N/2
*
* (Note that rev(j) is even for j < N/2.)
*/
/* see inner.h */
void
PQCLEAN_FALCON1024_CLEAN_FFT(fpr *f, unsigned logn) {
/*
* FFT algorithm in bit-reversal order uses the following
* iterative algorithm:
*
* t = N
* for m = 1; m < N; m *= 2:
* ht = t/2
* for i1 = 0; i1 < m; i1 ++:
* j1 = i1 * t
* s = GM[m + i1]
* for j = j1; j < (j1 + ht); j ++:
* x = f[j]
* y = s * f[j + ht]
* f[j] = x + y
* f[j + ht] = x - y
* t = ht
*
* GM[k] contains w^rev(k) for primitive root w = exp(i*pi/N).
*
* In the description above, f[] is supposed to contain complex
* numbers. In our in-memory representation, the real and
* imaginary parts of f[k] are in array slots k and k+N/2.
*
* We only keep the first half of the complex numbers. We can
* see that after the first iteration, the first and second halves
* of the array of complex numbers have separate lives, so we
* simply ignore the second part.
*/
unsigned u;
size_t t, n, hn, m;
/*
* First iteration: compute f[j] + i * f[j+N/2] for all j < N/2
* (because GM[1] = w^rev(1) = w^(N/2) = i).
* In our chosen representation, this is a no-op: everything is
* already where it should be.
*/
/*
* Subsequent iterations are truncated to use only the first
* half of values.
*/
n = (size_t)1 << logn;
hn = n >> 1;
t = hn;
for (u = 1, m = 2; u < logn; u ++, m <<= 1) {
size_t ht, hm, i1, j1;
ht = t >> 1;
hm = m >> 1;
for (i1 = 0, j1 = 0; i1 < hm; i1 ++, j1 += t) {
size_t j, j2;
j2 = j1 + ht;
fpr s_re, s_im;
s_re = fpr_gm_tab[((m + i1) << 1) + 0];
s_im = fpr_gm_tab[((m + i1) << 1) + 1];
for (j = j1; j < j2; j ++) {
fpr x_re, x_im, y_re, y_im;
x_re = f[j];
x_im = f[j + hn];
y_re = f[j + ht];
y_im = f[j + ht + hn];
FPC_MUL(y_re, y_im, y_re, y_im, s_re, s_im);
FPC_ADD(f[j], f[j + hn],
x_re, x_im, y_re, y_im);
FPC_SUB(f[j + ht], f[j + ht + hn],
x_re, x_im, y_re, y_im);
}
}
t = ht;
}
}
/* see inner.h */
void
PQCLEAN_FALCON1024_CLEAN_iFFT(fpr *f, unsigned logn) {
/*
* Inverse FFT algorithm in bit-reversal order uses the following
* iterative algorithm:
*
* t = 1
* for m = N; m > 1; m /= 2:
* hm = m/2
* dt = t*2
* for i1 = 0; i1 < hm; i1 ++:
* j1 = i1 * dt
* s = iGM[hm + i1]
* for j = j1; j < (j1 + t); j ++:
* x = f[j]
* y = f[j + t]
* f[j] = x + y
* f[j + t] = s * (x - y)
* t = dt
* for i1 = 0; i1 < N; i1 ++:
* f[i1] = f[i1] / N
*
* iGM[k] contains (1/w)^rev(k) for primitive root w = exp(i*pi/N)
* (actually, iGM[k] = 1/GM[k] = conj(GM[k])).
*
* In the main loop (not counting the final division loop), in
* all iterations except the last, the first and second half of f[]
* (as an array of complex numbers) are separate. In our chosen
* representation, we do not keep the second half.
*
* The last iteration recombines the recomputed half with the
* implicit half, and should yield only real numbers since the
* target polynomial is real; moreover, s = i at that step.
* Thus, when considering x and y:
* y = conj(x) since the final f[j] must be real
* Therefore, f[j] is filled with 2*Re(x), and f[j + t] is
* filled with 2*Im(x).
* But we already have Re(x) and Im(x) in array slots j and j+t
* in our chosen representation. That last iteration is thus a
* simple doubling of the values in all the array.
*
* We make the last iteration a no-op by tweaking the final
* division into a division by N/2, not N.
*/
size_t u, n, hn, t, m;
n = (size_t)1 << logn;
t = 1;
m = n;
hn = n >> 1;
for (u = logn; u > 1; u --) {
size_t hm, dt, i1, j1;
hm = m >> 1;
dt = t << 1;
for (i1 = 0, j1 = 0; j1 < hn; i1 ++, j1 += dt) {
size_t j, j2;
j2 = j1 + t;
fpr s_re, s_im;
s_re = fpr_gm_tab[((hm + i1) << 1) + 0];
s_im = fpr_neg(fpr_gm_tab[((hm + i1) << 1) + 1]);
for (j = j1; j < j2; j ++) {
fpr x_re, x_im, y_re, y_im;
x_re = f[j];
x_im = f[j + hn];
y_re = f[j + t];
y_im = f[j + t + hn];
FPC_ADD(f[j], f[j + hn],
x_re, x_im, y_re, y_im);
FPC_SUB(x_re, x_im, x_re, x_im, y_re, y_im);
FPC_MUL(f[j + t], f[j + t + hn],
x_re, x_im, s_re, s_im);
}
}
t = dt;
m = hm;
}
/*
* Last iteration is a no-op, provided that we divide by N/2
* instead of N. We need to make a special case for logn = 0.
*/
if (logn > 0) {
fpr ni;
( run in 0.857 second using v1.01-cache-2.11-cpan-f03e8824b8d )