def ldl_fft(G): """ Compute the LDL decomposition of G. Input: G A self-adjoint matrix (i.e. G is equal to its conjugate transpose) Output: L, D The LDL decomposition of G, that is G = L * D * (L*), where: - L is lower triangular with a diagonal of 1's - D is diagonal Format: FFT """ deg = len(G[0][0]) dim = len(G) L = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)] D = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)] for i in range(dim): L[i][i] = [1 for j in range(deg)] D[i][i] = G[i][i] for j in range(i): L[i][j] = G[i][j] for k in range(j): L[i][j] = sub_fft( L[i][j], mul_fft(mul_fft(L[i][k], adj_fft(L[j][k])), D[k][k])) L[i][j] = div_fft(L[i][j], D[j][j]) D[i][i] = sub_fft( D[i][i], mul_fft(mul_fft(L[i][j], adj_fft(L[i][j])), D[j][j])) return [L, D]
def ldl_fft(G): """Compute the LDL decomposition of G. Args: G: a Gram matrix Format: FFT Corresponds to algorithm 14 (LDL) of Falcon's documentation. """ deg = len(G[0][0]) dim = len(G) L = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)] D = [[[0 for k in range(deg)] for j in range(dim)] for i in range(dim)] for i in range(dim): L[i][i] = [1 for j in range(deg)] D[i][i] = G[i][i] for j in range(i): L[i][j] = G[i][j] for k in range(j): L[i][j] = sub_fft( L[i][j], mul_fft(mul_fft(L[i][k], adj_fft(L[j][k])), D[k][k])) L[i][j] = div_fft(L[i][j], D[j][j]) D[i][i] = sub_fft( D[i][i], mul_fft(mul_fft(L[i][j], adj_fft(L[i][j])), D[j][j])) return [L, D]
def ldl_fft(G): """ Compute the LDL decomposition of G. Only works with 2 * 2 matrices. Args: G: a Gram matrix Format: FFT Corresponds to algorithm 8 (LDL*) of Falcon's documentation. """ deg = len(G[0][0]) dim = len(G) assert (dim == 2) assert (dim == len(G[0])) zero = [0] * deg one = [1] * deg D00 = G[0][0][:] L10 = div_fft(G[1][0], G[0][0]) D11 = sub_fft(G[1][1], mul_fft(mul_fft(L10, adj_fft(L10)), G[0][0])) L = [[one, zero], [L10, one]] D = [[D00, zero], [zero, D11]] return [L, D]
def reduce(f, g, F, G): """ Reduce (F, G) relatively to (f, g). This is done via Babai's reduction. (F, G) <-- (F, G) - k * (f, g), where k = round((F f* + G g*) / (f f* + g g*)). Corresponds to algorithm 7 (Reduce) of Falcon's documentation. """ n = len(f) size = max(53, bitsize(min(f)), bitsize(max(f)), bitsize(min(g)), bitsize(max(g))) f_adjust = [elt >> (size - 53) for elt in f] g_adjust = [elt >> (size - 53) for elt in g] fa_fft = fft(f_adjust) ga_fft = fft(g_adjust) while (1): # Because we work in finite precision to reduce very large polynomials, # we may need to perform the reduction several times. Size = max(53, bitsize(min(F)), bitsize(max(F)), bitsize(min(G)), bitsize(max(G))) if Size < size: break F_adjust = [elt >> (Size - 53) for elt in F] G_adjust = [elt >> (Size - 53) for elt in G] Fa_fft = fft(F_adjust) Ga_fft = fft(G_adjust) den_fft = add_fft(mul_fft(fa_fft, adj_fft(fa_fft)), mul_fft(ga_fft, adj_fft(ga_fft))) num_fft = add_fft(mul_fft(Fa_fft, adj_fft(fa_fft)), mul_fft(Ga_fft, adj_fft(ga_fft))) k_fft = div_fft(num_fft, den_fft) k = ifft(k_fft) k = [int(round(elt)) for elt in k] if all(elt == 0 for elt in k): break # The two next lines are the costliest operations in ntru_gen # (more than 75% of the total cost in dimension n = 1024). # There are at least two ways to make them faster: # - replace Karatsuba with Toom-Cook # - mutualized Karatsuba, see ia.cr/2020/268 # For simplicity reasons, we didn't implement these optimisations here. fk = karamul(f, k) gk = karamul(g, k) for i in range(n): F[i] -= fk[i] << (Size - size) G[i] -= gk[i] << (Size - size) return F, G
def reduce(f, g, F, G): """ Reduce (F, G) relatively to (f, g). This is done via Babai's reduction. (F, G) <-- (F, G) - k * (f, g), where k = round((F f* + G g*) / (f f* + g g*)). Similar to algorithm Reduce of Falcon's documentation. Input: f, g, F, G Four polynomials mod (x ** n + 1) Output: None The inputs are reduced as detailed above. Format: Coefficient """ n = len(f) size = max(53, bitsize(min(f)), bitsize(max(f)), bitsize(min(g)), bitsize(max(g))) f_adjust = [elt >> (size - 53) for elt in f] g_adjust = [elt >> (size - 53) for elt in g] fa_fft = fft(f_adjust) ga_fft = fft(g_adjust) while(1): # Because we are working in finite precision to reduce very large polynomials, # we may need to perform the reduction several times. Size = max(53, bitsize(min(F)), bitsize(max(F)), bitsize(min(G)), bitsize(max(G))) if Size < size: break F_adjust = [elt >> (Size - 53) for elt in F] G_adjust = [elt >> (Size - 53) for elt in G] Fa_fft = fft(F_adjust) Ga_fft = fft(G_adjust) den_fft = add_fft(mul_fft(fa_fft, adj_fft(fa_fft)), mul_fft(ga_fft, adj_fft(ga_fft))) num_fft = add_fft(mul_fft(Fa_fft, adj_fft(fa_fft)), mul_fft(Ga_fft, adj_fft(ga_fft))) k_fft = div_fft(num_fft, den_fft) k = ifft(k_fft) k = [int(round(elt)) for elt in k] if all(elt == 0 for elt in k): break fk = karamul(f, k) gk = karamul(g, k) for i in range(n): F[i] -= fk[i] << (Size - size) G[i] -= gk[i] << (Size - size) return F, G