initial version of SQIsign
Co-authored-by: Jorge Chavez-Saab <jorgechavezsaab@gmail.com> Co-authored-by: Maria Corte-Real Santos <36373796+mariascrs@users.noreply.github.com> Co-authored-by: Luca De Feo <github@defeo.lu> Co-authored-by: Jonathan Komada Eriksen <jonathan.eriksen97@gmail.com> Co-authored-by: Basil Hess <bhe@zurich.ibm.com> Co-authored-by: Antonin Leroux <18654258+tonioecto@users.noreply.github.com> Co-authored-by: Patrick Longa <plonga@microsoft.com> Co-authored-by: Lorenz Panny <lorenz@yx7.cc> Co-authored-by: Francisco Rodríguez-Henríquez <francisco.rodriguez@tii.ae> Co-authored-by: Sina Schaeffler <108983332+syndrakon@users.noreply.github.com> Co-authored-by: Benjamin Wesolowski <19474926+Calodeon@users.noreply.github.com>
This commit is contained in:
421
src/quaternion/ref/generic/matkermod.c
Normal file
421
src/quaternion/ref/generic/matkermod.c
Normal file
@@ -0,0 +1,421 @@
|
||||
#include "internal.h"
|
||||
|
||||
/***************** Howell form **********************/
|
||||
|
||||
/** @brief Compute u s.t. gcd(u, mod) = 1 and ux = gcd(x, mod)
|
||||
*/
|
||||
static int unit(ibz_t *unit, ibz_t *gcd, const ibz_t *x, const ibz_t *mod) {
|
||||
if (ibz_is_zero(x))
|
||||
return 0;
|
||||
ibz_t stab, nmod, nmod2, thrash, tmp;
|
||||
ibz_init(&stab); ibz_init(&nmod); ibz_init(&nmod2); ibz_init(&thrash); ibz_init(&tmp);
|
||||
ibz_xgcd(gcd, unit, &thrash, x, mod);
|
||||
ibz_div(&nmod, &thrash, mod, gcd);
|
||||
// Stabilizer(unit, nmod)
|
||||
ibz_gcd(&stab, unit, &nmod);
|
||||
ibz_div(&nmod2, &thrash, mod, &stab);
|
||||
ibz_div(&stab, &thrash, unit, &stab);
|
||||
// Split(nmod2, stab)
|
||||
for (int i = ibz_bitsize(&nmod2); i > 0; i >>= 1) {
|
||||
ibz_mul(&stab, &stab, &stab);
|
||||
ibz_mod(&stab, &stab, &nmod2);
|
||||
}
|
||||
ibz_gcd(&stab, &stab, &nmod2);
|
||||
ibz_div(&stab, &thrash, &nmod2, &stab);
|
||||
#ifndef NDEBUG
|
||||
ibz_mul(&thrash, &stab, &nmod);
|
||||
ibz_add(&thrash, unit, &thrash);
|
||||
ibz_gcd(&thrash, &thrash, mod);
|
||||
ibz_gcd(&tmp, unit, &nmod);
|
||||
assert(ibz_cmp(&thrash, &tmp) == 0);
|
||||
#endif
|
||||
// Finish off
|
||||
ibz_mul(&stab, &stab, &nmod);
|
||||
ibz_add(unit, unit, &stab);
|
||||
ibz_mod(unit, unit, mod);
|
||||
#ifndef NDEBUG
|
||||
ibz_gcd(&stab, unit, mod);
|
||||
assert(ibz_is_one(&stab));
|
||||
ibz_mul(&stab, unit, x);
|
||||
ibz_mod(&stab, &stab, mod);
|
||||
assert(ibz_cmp(&stab, gcd) == 0);
|
||||
#endif
|
||||
ibz_finalize(&stab); ibz_finalize(&nmod); ibz_finalize(&nmod2); ibz_finalize(&thrash); ibz_finalize(&tmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @brief Linear combination of two columns
|
||||
*
|
||||
* (mat[][j] | mat[][k]) <- (mat[][j] | mat[][k]) * U (mod)
|
||||
*
|
||||
* only update columns between start (included) and end (excluded)
|
||||
*/
|
||||
static void gen_elem(int rows, int cols, ibz_t mat[rows][cols], int j, int k, int start, int end, const ibz_mat_2x2_t *U, const ibz_t *mod)
|
||||
{
|
||||
ibz_t tmp1, tmp2;
|
||||
ibz_init(&tmp1); ibz_init(&tmp2);
|
||||
for (int i = start; i < end; i++) {
|
||||
ibz_mul(&tmp1, &mat[i][j], &(*U)[0][0]);
|
||||
ibz_mul(&tmp2, &mat[i][k], &(*U)[1][0]);
|
||||
ibz_add(&tmp1, &tmp1, &tmp2);
|
||||
|
||||
ibz_mul(&tmp2, &mat[i][j], &(*U)[0][1]);
|
||||
ibz_mul(&mat[i][k], &mat[i][k], &(*U)[1][1]);
|
||||
ibz_add(&mat[i][k], &tmp2, &mat[i][k]);
|
||||
|
||||
ibz_mod(&mat[i][j], &tmp1, mod);
|
||||
ibz_mod(&mat[i][k], &mat[i][k], mod);
|
||||
}
|
||||
ibz_finalize(&tmp1); ibz_finalize(&tmp2);
|
||||
}
|
||||
|
||||
/** @brief Swap columns j and k of mat
|
||||
*/
|
||||
static inline void swap_col(int rows, int cols, ibz_t mat[rows][cols], int j, int k)
|
||||
{
|
||||
ibz_t tmp;
|
||||
ibz_init(&tmp);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
ibz_copy(&tmp, &mat[i][j]);
|
||||
ibz_copy(&mat[i][j], &mat[i][k]);
|
||||
ibz_copy(&mat[i][k], &tmp);
|
||||
}
|
||||
ibz_finalize(&tmp);
|
||||
}
|
||||
|
||||
/** @brief Check if column is all zeros
|
||||
*/
|
||||
static inline int is_col_zero(int rows, int cols, ibz_t mat[rows][cols], int j)
|
||||
{
|
||||
int is_zero = 1;
|
||||
for (int i = 0; i < rows; i++)
|
||||
is_zero &= ibz_is_zero(&mat[i][j]);
|
||||
return is_zero;
|
||||
}
|
||||
|
||||
/** Check that mat * trans = howell
|
||||
*/
|
||||
static int howell_check_matrices(int rows, int cols, const ibz_t howell[rows][rows+1], const ibz_t trans[rows+1][rows+1], const ibz_t mat[rows][cols], ibz_t *mod)
|
||||
{
|
||||
const int extra = rows + 1 - cols;
|
||||
ibz_t test[rows][rows+1], res[rows][rows+1];
|
||||
ibz_mat_init(rows, rows+1, test);
|
||||
ibz_mat_init(rows, rows+1, res);
|
||||
|
||||
// copy mat to the right of test
|
||||
for (int i = 0; i < rows; i++)
|
||||
for (int j = 0; j < cols; j++)
|
||||
ibz_mod(&test[i][j+extra], &mat[i][j], mod);
|
||||
|
||||
ibz_mat_mulmod(rows, rows+1, rows+1, res, test, trans, mod);
|
||||
|
||||
int ok = 1;
|
||||
for (int i = 0; i < rows; i++)
|
||||
for (int j = 0; j < rows+1; j++)
|
||||
ok &= ibz_cmp(&howell[i][j], &res[i][j]) == 0;
|
||||
|
||||
ibz_mat_finalize(rows, rows+1, res);
|
||||
ibz_mat_finalize(rows, rows+1, test);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void ibz_mat_mulmod(int from, int through, int to, ibz_t res[from][to], const ibz_t A[from][through], const ibz_t B[through][to], const ibz_t *mod)
|
||||
{
|
||||
ibz_t tmp;
|
||||
ibz_init(&tmp);
|
||||
for (int i = 0; i < from; i++) {
|
||||
for (int j = 0; j < to; j++) {
|
||||
ibz_set(&res[i][j], 0);
|
||||
for (int k = 0; k < through; k++) {
|
||||
ibz_mul(&tmp, &A[i][k], &B[k][j]);
|
||||
ibz_add(&res[i][j], &res[i][j], &tmp);
|
||||
ibz_mod(&res[i][j], &res[i][j], mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
ibz_finalize(&tmp);
|
||||
}
|
||||
|
||||
int ibz_mat_howell(int rows, int cols, ibz_t howell[rows][rows+1], ibz_t trans[rows+1][rows+1], const ibz_t mat[rows][cols], ibz_t *mod) {
|
||||
assert(cols <= rows);
|
||||
const int extra = rows + 1 - cols;
|
||||
|
||||
ibz_mat_2x2_t U;
|
||||
ibz_t gcd, u, q;
|
||||
ibz_mat_2x2_init(&U);
|
||||
ibz_init(&gcd); ibz_init(&u); ibz_init(&q);
|
||||
|
||||
// copy mat to the right of howell
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = cols; j < rows+1; j++)
|
||||
ibz_set(&howell[i][j-cols], 0);
|
||||
for (int j = 0; j < cols; j++)
|
||||
ibz_mod(&howell[i][j+extra], &mat[i][j], mod);
|
||||
}
|
||||
// initialize trans to identity
|
||||
if (trans) {
|
||||
for (int i = 0; i < rows+1; i++) {
|
||||
for (int j = 0; j < rows+1; j++)
|
||||
if (j != i) ibz_set(&trans[i][j], 0);
|
||||
ibz_set(&trans[i][i], 1);
|
||||
}
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
|
||||
// Put in upper triangular form
|
||||
for (int i = rows-1; i >= extra-1; i--) {
|
||||
for (int j = extra; j <= i; j++) {
|
||||
if (ibz_is_zero(&howell[i][j]))
|
||||
continue;
|
||||
ibz_xgcd_ann(&gcd, &U[0][0], &U[1][0], &U[0][1], &U[1][1], &howell[i][j], &howell[i][i+1]);
|
||||
gen_elem(rows, rows+1, howell, j, i+1, 0, i, &U, mod);
|
||||
ibz_set(&howell[i][j], 0);
|
||||
ibz_copy(&howell[i][i+1], &gcd);
|
||||
//
|
||||
if (trans) {
|
||||
gen_elem(rows+1, rows+1, trans, j, i+1, 0, rows+1, &U, mod);
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put in reduced Howell form
|
||||
for (int i = rows-1; i >= 0; i--) {
|
||||
/* normalize diagonal coefficient */
|
||||
if (unit(&u, &gcd, &howell[i][i+1], mod)) {
|
||||
for (int k = 0; k < i; k++) {
|
||||
ibz_mul(&howell[k][i+1], &howell[k][i+1], &u);
|
||||
ibz_mod(&howell[k][i+1], &howell[k][i+1], mod);
|
||||
}
|
||||
ibz_copy(&howell[i][i+1], &gcd);
|
||||
//
|
||||
if (trans) {
|
||||
for (int k = 0; k < rows+1; k++) {
|
||||
ibz_mul(&trans[k][i+1], &trans[k][i+1], &u);
|
||||
ibz_mod(&trans[k][i+1], &trans[k][i+1], mod);
|
||||
}
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
}
|
||||
|
||||
/* reduce right of the diagonal */
|
||||
ibz_t *pivot = &howell[i][i+1];
|
||||
if (!ibz_is_zero(pivot)) {
|
||||
for (int j = i+2; j < rows+1; j++) {
|
||||
assert(ibz_cmp(pivot, &ibz_const_zero) > 0);
|
||||
ibz_div(&q, &howell[i][j], &howell[i][j], pivot);
|
||||
// howell[][j] -= q howell[][i+1]
|
||||
for (int k = 0; k < i; k++) {
|
||||
ibz_mul(&u, &q, &howell[k][i+1]);
|
||||
ibz_sub(&howell[k][j], &howell[k][j], &u);
|
||||
ibz_mod(&howell[k][j], &howell[k][j], mod);
|
||||
}
|
||||
// trans[][j] -= q trans[][i+1]
|
||||
if (trans) {
|
||||
for (int k = 0; k < rows+1; k++) {
|
||||
ibz_mul(&u, &q, &trans[k][i+1]);
|
||||
ibz_sub(&trans[k][j], &trans[k][j], &u);
|
||||
ibz_mod(&trans[k][j], &trans[k][j], mod);
|
||||
}
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ensure Howell property */
|
||||
if (i > 0) {
|
||||
ibz_gcd(&gcd, pivot, mod);
|
||||
if (!ibz_is_one(&gcd)) {
|
||||
// Ann(pivot)
|
||||
ibz_div(&u, &gcd, mod, &gcd);
|
||||
for (int k = 0; k < rows; k++) {
|
||||
if (k < i) {
|
||||
ibz_mul(&howell[k][0], &howell[k][i+1], &u);
|
||||
ibz_mod(&howell[k][0], &howell[k][0], mod);
|
||||
} else {
|
||||
ibz_set(&howell[k][0], 0);
|
||||
}
|
||||
}
|
||||
// trans[][0] += u trans[][i+1]
|
||||
if (trans) {
|
||||
for (int k = 0; k < rows+1; k++) {
|
||||
ibz_mul(&q, &u, &trans[k][i+1]);
|
||||
ibz_add(&trans[k][0], &trans[k][0], &q);
|
||||
ibz_mod(&trans[k][0], &trans[k][0], mod);
|
||||
}
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
|
||||
for (int i2 = i-1; i2 >= 0; i2--) {
|
||||
if (ibz_is_zero(&howell[i2][0]))
|
||||
continue;
|
||||
if (ibz_is_zero(&howell[i2][i2+1])) {
|
||||
swap_col(rows, rows+1, howell, 0, i2+1);
|
||||
if (trans) {
|
||||
swap_col(rows+1, rows+1, trans, 0, i2+1);
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ibz_xgcd_ann(&gcd, &U[0][0], &U[1][0], &U[0][1], &U[1][1], &howell[i2][0], &howell[i2][i2+1]);
|
||||
gen_elem(rows, rows+1, howell, 0, i2+1, 0, i2, &U, mod);
|
||||
ibz_set(&howell[i2][0], 0);
|
||||
ibz_copy(&howell[i2][i2+1], &gcd);
|
||||
//
|
||||
if (trans) {
|
||||
gen_elem(rows, rows+1, trans, 0, i2+1, 0, rows+1, &U, mod);
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* put zero columns first */
|
||||
int read, write;
|
||||
for (read = rows, write = rows; read >= 1; read--) {
|
||||
if (!is_col_zero(rows, rows+1, howell, read)) {
|
||||
if (read < write) {
|
||||
swap_col(rows, rows+1, howell, read, write);
|
||||
if (trans) {
|
||||
swap_col(rows+1, rows+1, trans, read, write);
|
||||
assert(howell_check_matrices(rows, cols, howell, trans, mat, mod));
|
||||
}
|
||||
}
|
||||
write--;
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize
|
||||
ibz_mat_2x2_finalize(&U);
|
||||
ibz_finalize(&gcd); ibz_finalize(&u); ibz_finalize(&q);
|
||||
|
||||
return write + 1;
|
||||
}
|
||||
|
||||
void ibz_mat_right_ker_mod(int rows, int cols, ibz_t ker[cols][cols], const ibz_t mat[rows][cols], ibz_t *mod)
|
||||
{
|
||||
assert(cols <= rows);
|
||||
const int extra = rows + 1 - cols;
|
||||
|
||||
ibz_t tmp;
|
||||
ibz_mat_2x2_t U;
|
||||
ibz_t howell[rows][rows+1], trans[rows+1][rows+1], preker[rows+1][rows+1], near_ker[cols][rows+1];
|
||||
|
||||
ibz_init(&tmp);
|
||||
ibz_mat_2x2_init(&U);
|
||||
ibz_mat_init(rows, rows+1, howell);
|
||||
ibz_mat_init(rows+1, rows+1, trans);
|
||||
ibz_mat_init(rows+1, rows+1, preker);
|
||||
ibz_mat_init(cols, rows+1, near_ker);
|
||||
|
||||
// Compute Howell form of mat
|
||||
int zeros = ibz_mat_howell(rows, cols, howell, trans, mat, mod);
|
||||
|
||||
// Compute right kernel of Howell form
|
||||
for (int j = rows, i = rows-1; j >= 0; j--) {
|
||||
while (i >= 0 && ibz_is_zero(&howell[i][j]))
|
||||
i--;
|
||||
if (i < 0) {
|
||||
ibz_set(&preker[j][j], 1);
|
||||
continue;
|
||||
}
|
||||
ibz_t *pivot = &howell[i][j];
|
||||
|
||||
// Ann(pivot)
|
||||
ibz_gcd(&tmp, pivot, mod);
|
||||
if (!ibz_is_one(&tmp))
|
||||
ibz_div(&preker[j][j], &tmp, mod, &tmp);
|
||||
|
||||
for (int j2 = j+1; j2 <= rows; j2++) {
|
||||
// howell[i][j+1..rows] * preker[j+1..rows][j2]
|
||||
for (int k = j+1; k <= rows; k++) {
|
||||
ibz_mul(&tmp, &howell[i][k], &preker[k][j2]);
|
||||
ibz_add(&preker[j][j2], &preker[j][j2], &tmp);
|
||||
}
|
||||
ibz_mod(&preker[j][j2], &preker[j][j2], mod);
|
||||
//
|
||||
ibz_div(&preker[j][j2], &tmp, &preker[j][j2], pivot);
|
||||
assert(ibz_is_zero(&tmp));
|
||||
if (!ibz_is_zero(&preker[j][j2]))
|
||||
ibz_sub(&preker[j][j2], mod, &preker[j][j2]);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check that preker is indeed a kernel of howell
|
||||
ibz_t acc;
|
||||
ibz_init(&acc);
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < rows+1; j++) {
|
||||
ibz_set(&acc, 0);
|
||||
for (int k = 0; k < rows+1; k++) {
|
||||
ibz_mul(&tmp, &howell[i][k], &preker[k][j]);
|
||||
ibz_add(&acc, &acc, &tmp);
|
||||
}
|
||||
ibz_mod(&acc, &acc, mod);
|
||||
assert(ibz_is_zero(&acc));
|
||||
}
|
||||
}
|
||||
ibz_finalize(&acc);
|
||||
#endif
|
||||
|
||||
// Apply (bottom part of) transition matrix to computed kernel
|
||||
for (int i = 0; i < cols; i++) {
|
||||
for (int j = 0; j < rows+1; j++) {
|
||||
for (int k = 0; k < rows+1; k++) {
|
||||
ibz_mul(&tmp, &trans[i+extra][k], &preker[k][j]);
|
||||
ibz_add(&near_ker[i][j], &near_ker[i][j], &tmp);
|
||||
}
|
||||
ibz_mod(&near_ker[i][j], &near_ker[i][j], mod);
|
||||
}
|
||||
}
|
||||
|
||||
// Move zero columns to the start
|
||||
int read, write;
|
||||
for (read = rows, write = rows; read >= 0; read--) {
|
||||
if (!is_col_zero(cols, rows+1, near_ker, read)) {
|
||||
if (read < write) {
|
||||
swap_col(cols, rows+1, near_ker, read, write);
|
||||
}
|
||||
write--;
|
||||
}
|
||||
}
|
||||
|
||||
// Put in upper triangular form
|
||||
const int diag_shift = rows - cols + 1; // i-th diagonal is at (i+diag_shift) column
|
||||
for (int i = cols-1; i >= 0; i--) {
|
||||
for (int j = write+1; j < i+diag_shift; j++) {
|
||||
if (ibz_is_zero(&near_ker[i][j]))
|
||||
continue;
|
||||
ibz_xgcd_ann(&tmp, &U[0][0], &U[1][0], &U[0][1], &U[1][1], &near_ker[i][j], &near_ker[i][i+diag_shift]);
|
||||
gen_elem(cols, rows+1, near_ker, j, i+diag_shift, 0, i+1, &U, mod);
|
||||
ibz_set(&howell[i][j], 0);
|
||||
ibz_copy(&howell[i][i+diag_shift], &tmp);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Check that ker is indeed a kernel of mat
|
||||
ibz_t check[rows][rows+1];
|
||||
ibz_mat_init(rows, rows+1, check);
|
||||
ibz_mat_mulmod(rows, cols, rows+1, check, mat, near_ker, mod);
|
||||
for (int i = 0; i < rows; i++)
|
||||
for (int j = 0; j < rows+1; j++)
|
||||
assert(ibz_is_zero(&check[i][j]));
|
||||
ibz_mat_finalize(rows, rows+1, check);
|
||||
#endif
|
||||
|
||||
// Copy result
|
||||
for (int i = 0; i < cols; i++)
|
||||
for (int j = 0; j < cols; j++)
|
||||
ibz_copy(&ker[i][j], &near_ker[i][j+rows-cols+1]);
|
||||
// Finalize
|
||||
ibz_finalize(&tmp);
|
||||
ibz_mat_2x2_finalize(&U);
|
||||
ibz_mat_finalize(rows, rows+1, howell);
|
||||
ibz_mat_finalize(rows+1, rows+1, trans);
|
||||
ibz_mat_finalize(rows+1, rows+1, preker);
|
||||
ibz_mat_finalize(cols, rows+1, near_ker);
|
||||
}
|
||||
Reference in New Issue
Block a user