def ldl(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: Coefficient """ 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] + [0 for j in range(deg - 1)] 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(L[i][j], mul(mul(L[i][k], adj(L[j][k])), D[k][k])) L[i][j] = div(L[i][j], D[j][j]) D[i][i] = sub(D[i][i], mul(mul(L[i][j], adj(L[i][j])), D[j][j])) return [L, D]
def ffldl(G): """Compute the ffLDL decomposition tree of G. Args: G: a Gram matrix Format: coefficient Corresponds to algorithm 9 (ffLDL) of Falcon's documentation, except it's in polynomial representation. """ n = len(G[0][0]) L, D = ldl(G) # Coefficients of L, D are elements of R[x]/(x^n - x^(n/2) + 1), in coefficient representation if (n > 2): # A bisection is done on elements of a 2*2 diagonal matrix. d00, d01 = split(D[0][0]) d10, d11 = split(D[1][1]) G0 = [[d00, d01], [adj(d01), d00]] G1 = [[d10, d11], [adj(d11), d10]] return [L[1][0], ffldl(G0), ffldl(G1)] elif (n == 2): # Bottom of the recursion. D[0][0][1] = 0 D[1][1][1] = 0 return [L[1][0], D[0][0], D[1][1]]
def ldl(G): """Compute the LDL decomposition of G. Args: G: a Gram matrix Format: coefficient Corresponds to algorithm 14 (LDL) of Falcon's documentation, except it's in polynomial representation. """ 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] + [0 for j in range(deg - 1)] 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(L[i][j], mul(mul(L[i][k], adj(L[j][k])), D[k][k])) L[i][j] = div(L[i][j], D[j][j]) D[i][i] = sub(D[i][i], mul(mul(L[i][j], adj(L[i][j])), D[j][j])) return [L, D]
def gs_norm(f, g, q): """Compute the squared Gram-Schmidt norm of the NTRU matrix generated by f, g. This matrix is [[g, - f], [G, - F]]. This algorithm is equivalent to line 9 of algorithm 5 (NTRUGen). """ sqnorm_fg = sqnorm([f, g]) ffgg = add(mul(f, adj(f)), mul(g, adj(g))) Ft = div(adj(g), ffgg) Gt = div(adj(f), ffgg) sqnorm_FG = (q**2) * sqnorm([Ft, Gt]) return max(sqnorm_fg, sqnorm_FG)
def ldl(G): """ Compute the LDL decomposition of G. Only works with 2 * 2 matrices. Args: G: a Gram matrix Format: coefficient Corresponds to algorithm 8 (LDL*) of Falcon's documentation, except it's in polynomial representation. """ deg = len(G[0][0]) dim = len(G) assert (dim == 2) assert (dim == len(G[0])) zero = [0] * deg one = [1] + [0] * (deg - 1) D00 = G[0][0][:] L10 = div(G[1][0], G[0][0]) D11 = sub(G[1][1], mul(mul(L10, adj(L10)), G[0][0])) L = [[one, zero], [L10, one]] D = [[D00, zero], [zero, D11]] return [L, D]
def ffldl(G): """ Compute the ffLDL decomposition tree of G. Input: G A Gram matrix Output: T The ffLDL decomposition tree of G Format: Coefficient Similar to algorithm ffLDL of Falcon's documentation, except it's in polynomial representation. """ m = len(G) - 1 d = len(G[0][0]) # LDL decomposition L, D = ldl(G) # General case if (d > 2): rep = [L] for i in range(m + 1): # Split the output d0, d1 = split(D[i][i]) Gi = [[d0, d1], [adj(d1), d0]] # Recursive call on the split parts rep += [ffldl(Gi)] return rep # Bottom case elif (d == 2): D[0][0][1] = 0 D[1][1][1] = 0 return [L, D[0][0], D[1][1]]
def gram(B): """Compute the Gram matrix of B. Args: B: a matrix Format: coefficient """ rows = range(len(B)) ncols = len(B[0]) deg = len(B[0][0]) G = [[[0 for coef in range(deg)] for j in rows] for i in rows] for i in rows: for j in rows: for k in range(ncols): G[i][j] = add(G[i][j], mul(B[i][k], adj(B[j][k]))) return G
def gram(B): """ Compute the Gram matrix of B. Input: B A matrix Output: G The Gram matrix of B: G = B * (B*) Format: Coefficient """ rows = range(len(B)) ncols = len(B[0]) deg = len(B[0][0]) G = [[[0 for coef in range(deg)] for j in rows] for i in rows] for i in rows: for j in rows: for k in range(ncols): G[i][j] = add(G[i][j], mul(B[i][k], adj(B[j][k]))) return G