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:
SQIsign team
2023-06-01 00:00:00 +00:00
committed by Lorenz Panny
commit 28ff420dd0
285 changed files with 70301 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
set(SOURCE_FILES_QUATERNION_GENERIC_REF
algebra.c
ideal.c
dim4.c
dim2.c
integers.c
lattice.c
finit.c
printer.c
lll.c
matkermod.c
)
add_library(${LIB_QUATERNION} ${SOURCE_FILES_QUATERNION_GENERIC_REF})
target_include_directories(${LIB_QUATERNION} PRIVATE common ${INC_PUBLIC} ${INC_COMMON} ${INC_INTBIG} ${INC_QUATERNION})
target_compile_options(${LIB_QUATERNION} PRIVATE ${C_OPT_FLAGS})
add_subdirectory(test)

View File

@@ -0,0 +1,287 @@
#include <quaternion.h>
#include "internal.h"
//Internal helper functions
//in internal.h:
//static inline void quat_alg_init_set_ui(quat_alg_t *alg, unsigned int p) {
// ibz_t bp;
// ibz_init(&bp);
// ibz_set(&bp, p);
// quat_alg_init_set(alg, &bp);
// ibz_finalize(&bp);
//}
void quat_alg_coord_add(quat_alg_coord_t *res, const quat_alg_coord_t *a, const quat_alg_coord_t *b){
ibz_add(&((*res)[0]),&((*a)[0]),&((*b)[0]));
ibz_add(&((*res)[1]),&((*a)[1]),&((*b)[1]));
ibz_add(&((*res)[2]),&((*a)[2]),&((*b)[2]));
ibz_add(&((*res)[3]),&((*a)[3]),&((*b)[3]));
}
void quat_alg_coord_sub(quat_alg_coord_t *res, const quat_alg_coord_t *a, const quat_alg_coord_t *b){
ibz_sub(&((*res)[0]),&((*a)[0]),&((*b)[0]));
ibz_sub(&((*res)[1]),&((*a)[1]),&((*b)[1]));
ibz_sub(&((*res)[2]),&((*a)[2]),&((*b)[2]));
ibz_sub(&((*res)[3]),&((*a)[3]),&((*b)[3]));
}
void quat_alg_equal_denom(quat_alg_elem_t *res_a, quat_alg_elem_t *res_b, const quat_alg_elem_t *a, const quat_alg_elem_t *b){
ibz_t gcd, r;
ibz_init(&gcd);
ibz_init(&r);
ibz_gcd(&gcd, &(a->denom), &(b->denom));
//temporarily set res_a.denom to a.denom/gcd, and res_b.denom to b.denom/gcd
ibz_div(&(res_a->denom), &r, &(a->denom), &gcd);
ibz_div(&(res_b->denom), &r, &(b->denom), &gcd);
for (int i = 0; i<4;i++){
//multiply coordiates by reduced denominators from the other element
ibz_mul(&(res_a->coord[i]), &(a->coord[i]), &(res_b->denom));
ibz_mul(&(res_b->coord[i]), &(b->coord[i]), &(res_a->denom));
}
// multiply both reduced denominators
ibz_mul(&(res_a->denom), &(res_a->denom), &(res_b->denom));
// multiply them by the gcd to get the new common denominator
ibz_mul(&(res_b->denom), &(res_a->denom), &gcd);
ibz_mul(&(res_a->denom), &(res_a->denom), &gcd);
ibz_finalize(&gcd);
ibz_finalize(&r);
}
//Public Functions
void quat_alg_add(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b){
quat_alg_elem_t res_a, res_b;
quat_alg_elem_init(&res_a);
quat_alg_elem_init(&res_b);
// put both on the same denominator
quat_alg_equal_denom(&res_a,&res_b,a,b);
//then add
ibz_copy(&(res->denom), &(res_a.denom));
quat_alg_coord_add(&(res->coord),&(res_a.coord),&(res_b.coord));
quat_alg_elem_finalize(&res_a);
quat_alg_elem_finalize(&res_b);
}
void quat_alg_sub(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b){
quat_alg_elem_t res_a, res_b;
quat_alg_elem_init(&res_a);
quat_alg_elem_init(&res_b);
// put both on the same denominator
quat_alg_equal_denom(&res_a,&res_b,a,b);
//then substract
ibz_copy(&res->denom, &res_a.denom);
quat_alg_coord_sub(&res->coord,&res_a.coord,&res_b.coord);
quat_alg_elem_finalize(&res_a);
quat_alg_elem_finalize(&res_b);
}
void quat_alg_mul(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b, const quat_alg_t *alg){
ibz_t prod;
quat_alg_coord_t sum;
ibz_init(&prod);
quat_alg_coord_init(&sum);
ibz_set(&(sum[0]), 0);
ibz_set(&(sum[1]), 0);
ibz_set(&(sum[2]), 0);
ibz_set(&(sum[3]), 0);
//denominator: product of denominators
ibz_mul(&(res->denom), &(a->denom),&(b->denom));
// compute 1 coordinate
ibz_mul(&prod, &(a->coord[2]),&(b->coord[2]));
ibz_sub(&(sum[0]),&(sum[0]),&prod);
ibz_mul(&prod, &(a->coord[3]),&(b->coord[3]));
ibz_sub(&(sum[0]),&(sum[0]),&prod);
ibz_mul(&(sum[0]), &(sum[0]),&(alg->p));
ibz_mul(&prod, &(a->coord[0]),&(b->coord[0]));
ibz_add(&(sum[0]),&(sum[0]),&prod);
ibz_mul(&prod, &(a->coord[1]),&(b->coord[1]));
ibz_sub(&(sum[0]),&(sum[0]),&prod);
// compute i coordiante
ibz_mul(&prod, &(a->coord[2]),&(b->coord[3]));
ibz_add(&(sum[1]),&(sum[1]),&prod);
ibz_mul(&prod, &(a->coord[3]),&(b->coord[2]));
ibz_sub(&(sum[1]),&(sum[1]),&prod);
ibz_mul(&(sum[1]), &(sum[1]),&(alg->p));
ibz_mul(&prod, &(a->coord[0]),&(b->coord[1]));
ibz_add(&(sum[1]),&(sum[1]),&prod);
ibz_mul(&prod, &(a->coord[1]),&(b->coord[0]));
ibz_add(&(sum[1]),&(sum[1]),&prod);
// compute j coordiante
ibz_mul(&prod, &(a->coord[0]),&(b->coord[2]));
ibz_add(&(sum[2]),&(sum[2]),&prod);
ibz_mul(&prod, &(a->coord[2]),&(b->coord[0]));
ibz_add(&(sum[2]),&(sum[2]),&prod);
ibz_mul(&prod, &(a->coord[1]),&(b->coord[3]));
ibz_sub(&(sum[2]),&(sum[2]),&prod);
ibz_mul(&prod, &(a->coord[3]),&(b->coord[1]));
ibz_add(&(sum[2]),&(sum[2]),&prod);
// compute ij coordiante
ibz_mul(&prod, &(a->coord[0]),&(b->coord[3]));
ibz_add(&(sum[3]),&(sum[3]),&prod);
ibz_mul(&prod, &(a->coord[3]),&(b->coord[0]));
ibz_add(&(sum[3]),&(sum[3]),&prod);
ibz_mul(&prod, &(a->coord[2]),&(b->coord[1]));
ibz_sub(&(sum[3]),&(sum[3]),&prod);
ibz_mul(&prod, &(a->coord[1]),&(b->coord[2]));
ibz_add(&(sum[3]),&(sum[3]),&prod);
ibz_copy(&(res->coord[0]),&(sum[0]));
ibz_copy(&(res->coord[1]),&(sum[1]));
ibz_copy(&(res->coord[2]),&(sum[2]));
ibz_copy(&(res->coord[3]),&(sum[3]));
ibz_finalize(&prod);
quat_alg_coord_finalize(&sum);
}
void quat_alg_rightmul_mat(ibz_mat_4x4_t *mulmat, const quat_alg_elem_t *a, const quat_alg_t *alg) {
quat_alg_elem_t e, res;
quat_alg_elem_init(&e);
quat_alg_elem_init(&res);
for (int i = 0; i < 4; i++) {
// i-th standard basis vector
if (i)
ibz_set(&e.coord[i-1], 0);
ibz_set(&e.coord[i], 1);
quat_alg_mul(&res, &e, a, alg);
for (int j = 0; j < 4; j++)
ibz_copy(&(*mulmat)[j][i], &res.coord[j]);
}
quat_alg_elem_finalize(&e);
quat_alg_elem_finalize(&res);
}
void quat_alg_norm(ibq_t *res, const quat_alg_elem_t *a, const quat_alg_t *alg){
quat_alg_elem_t conj, norm;
quat_alg_elem_init(&conj);
quat_alg_elem_init(&norm);
quat_alg_conj(&conj,a);
quat_alg_mul(&norm,a,&conj,alg);
ibq_set(res,&(norm.coord[0]),&(norm.denom));
quat_alg_elem_finalize(&conj);
quat_alg_elem_finalize(&norm);
}
void quat_alg_trace(ibq_t *res, const quat_alg_elem_t *a){
quat_alg_elem_t trace;
quat_alg_elem_init(&trace);
ibz_copy(&(trace.denom), &(a->denom));
ibz_add(&(trace.coord[0]),&(a->coord[0]),&(a->coord[0]));
ibz_sub(&(trace.coord[1]),&(a->coord[1]),&(a->coord[1]));
ibz_sub(&(trace.coord[2]),&(a->coord[2]),&(a->coord[2]));
ibz_sub(&(trace.coord[3]),&(a->coord[3]),&(a->coord[3]));
ibq_set(res,&(trace.coord[0]),&(trace.denom));
quat_alg_elem_finalize(&trace);
}
void quat_alg_scalar(quat_alg_elem_t *elem, const ibz_t *numerator, const ibz_t *denominator){
ibz_copy(&(elem->denom),denominator);
ibz_copy(&(elem->coord[0]),numerator);
ibz_set(&(elem->coord[1]),0);
ibz_set(&(elem->coord[2]),0);
ibz_set(&(elem->coord[3]),0);
}
void quat_alg_conj(quat_alg_elem_t *conj, const quat_alg_elem_t *x){
ibz_copy(&(conj->denom), &(x->denom));
ibz_copy(&(conj->coord[0]),&(x->coord[0]));
ibz_neg(&(conj->coord[1]),&(x->coord[1]));
ibz_neg(&(conj->coord[2]),&(x->coord[2]));
ibz_neg(&(conj->coord[3]),&(x->coord[3]));
}
//defined in header
//int quat_alg_is_primitive(const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg) {
// quat_alg_coord_t *coord;
// quat_alg_coord_init(coord);
// int ok = quat_lattice_contains(coord, order, x);
//assert(ok);
//unused(ok);
// ibz_t *cnt;
// ibz_init(cnt);
// ibz_content(cnt, coord);
// ok = ibz_is_one(cnt);
// ibz_finalize(cnt);
// quat_alg_coord_finalize(coord);
// return ok;
//}
//
//static inline void quat_alg_make_primitive(quat_alg_coord_t *primitive_x, ibz_t *content, const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg){
// int ok = quat_lattice_contains(primitive_x, order, x);
//assert(ok);
//unused(ok);
// ibz_content(content, primitive_x);
// for(int i = 0; i <4; i++){
// ibz_div(primitive_x[i], NULL, primitive_x[i], content);
// }
//}
void quat_alg_normalize(quat_alg_elem_t *x){
ibz_t gcd,r, zero;
ibz_init(&gcd);
ibz_init(&r);
ibz_init(&zero);
ibz_content(&gcd,&(x->coord));
ibz_gcd(&gcd,&gcd,&(x->denom));
ibz_div(&(x->denom),&r,&(x->denom),&gcd);
for (int i = 0; i < 4; i++){
ibz_div(&(x->coord[i]),&r,&(x->coord[i]),&gcd);
}
ibz_set(&zero,0);
if (0<ibz_cmp(&zero,&(x->denom))){
for (int i = 0; i < 4; i++){
ibz_neg(&(x->coord[i]),&(x->coord[i]));
}
ibz_neg(&(x->denom),&(x->denom));
}
ibz_finalize(&gcd);
ibz_finalize(&r);
ibz_finalize(&zero);
}
int quat_alg_elem_is_zero(const quat_alg_elem_t *x){
int res = quat_alg_coord_is_zero(&(x->coord));
return(res);
}
int quat_alg_coord_is_zero(const quat_alg_coord_t *x){
int res = 1;
for (int i = 0; i < 4; i++){
res &= ibz_is_zero(&((*x)[i]));
}
return(res);
}
// helper functions for lattices
void quat_alg_elem_copy_ibz(quat_alg_elem_t *elem, const ibz_t *denom, const ibz_t *coord0,const ibz_t *coord1,const ibz_t *coord2,const ibz_t *coord3){
ibz_copy(&(elem->coord[0]), coord0);
ibz_copy(&(elem->coord[1]), coord1);
ibz_copy(&(elem->coord[2]), coord2);
ibz_copy(&(elem->coord[3]), coord3);
ibz_copy(&(elem->denom),denom);
}
void quat_alg_elem_set(quat_alg_elem_t *elem, int64_t denom, int64_t coord0, int64_t coord1, int64_t coord2, int64_t coord3){
ibz_set(&(elem->coord[0]), coord0);
ibz_set(&(elem->coord[1]), coord1);
ibz_set(&(elem->coord[2]), coord2);
ibz_set(&(elem->coord[3]), coord3);
ibz_set(&(elem->denom),denom);
}
void quat_alg_elem_mul_by_scalar(quat_alg_elem_t *res, const ibz_t *scalar, const quat_alg_elem_t *elem){
for(int i = 0; i < 4; i++){
ibz_mul(&(res->coord[i]), &(elem->coord[i]),scalar);
}
ibz_copy(&(res->denom),&(elem->denom));
}

View File

@@ -0,0 +1,553 @@
#include <quaternion.h>
#include "internal.h"
//internal helpers, also for other files
void ibz_vec_2_set(ibz_vec_2_t *vec, int a0, int a1){
ibz_set(&((*vec)[0]),a0);
ibz_set(&((*vec)[1]),a1);
}
void ibz_mat_2x2_set(ibz_mat_2x2_t *mat, int a00, int a01, int a10, int a11){
ibz_set(&((*mat)[0][0]),a00);
ibz_set(&((*mat)[0][1]),a01);
ibz_set(&((*mat)[1][0]),a10);
ibz_set(&((*mat)[1][1]),a11);
}
void ibz_mat_2x2_det_from_ibz(ibz_t *det, const ibz_t *a11, const ibz_t *a12, const ibz_t *a21, const ibz_t *a22){
ibz_t prod;
ibz_init(&prod);
ibz_mul(&prod,a12,a21);
ibz_mul(det,a11,a22);
ibz_sub(det,det,&prod);
ibz_finalize(&prod);
}
void ibz_mat_2x2_eval(ibz_vec_2_t *res, const ibz_mat_2x2_t *mat, const ibz_vec_2_t *vec){
ibz_t prod;
ibz_vec_2_t matvec;
ibz_init(&prod);
ibz_vec_2_init(&matvec);
ibz_mul(&prod,&((*mat)[0][0]),&((*vec)[0]));
ibz_copy(&(matvec[0]), &prod);
ibz_mul(&prod,&((*mat)[0][1]),&((*vec)[1]));
ibz_add(&(matvec[0]),&(matvec[0]), &prod);
ibz_mul(&prod,&((*mat)[1][0]),&((*vec)[0]));
ibz_copy(&(matvec[1]), &prod);
ibz_mul(&prod,&((*mat)[1][1]),&((*vec)[1]));
ibz_add(&(matvec[1]),&(matvec[1]), &prod);
ibz_copy(&((*res)[0]), &(matvec[0]));
ibz_copy(&((*res)[1]), &(matvec[1]));
ibz_finalize(&prod);
ibz_vec_2_finalize(&matvec);
}
// modular 2x2 operations
void ibz_2x2_mul_mod(ibz_mat_2x2_t *prod, const ibz_mat_2x2_t *mat_a, const ibz_mat_2x2_t *mat_b, const ibz_t *m){
ibz_t mul;
ibz_mat_2x2_t sums;
ibz_init(&mul);
ibz_mat_2x2_init(&sums);
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
ibz_set(&(sums[i][j]),0);
}
}
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
for(int k = 0; k < 2; k++){
ibz_mul(&mul,&((*mat_a)[i][k]), &((*mat_b)[k][j]));
ibz_add(&(sums[i][j]),&(sums[i][j]), &mul);
ibz_mod(&(sums[i][j]),&(sums[i][j]), m);
}
}
}
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
ibz_copy(&((*prod)[i][j]),&(sums[i][j]));
}
}
ibz_finalize(&mul);
ibz_mat_2x2_finalize(&sums);
}
int ibz_2x2_inv_mod(ibz_mat_2x2_t *inv, const ibz_mat_2x2_t *mat, const ibz_t *m){
ibz_t det, prod;
ibz_init(&det);
ibz_init(&prod);
ibz_mul(&det,&((*mat)[0][0]),&((*mat)[1][1]));
ibz_mod(&det,&det,m);
ibz_mul(&prod,&((*mat)[0][1]),&((*mat)[1][0]));
ibz_sub(&det,&det,&prod);
ibz_mod(&det,&det,m);
int res = ibz_invmod(&det,&det,m);
if(res){
ibz_copy(&prod,&((*mat)[0][0]));
ibz_copy(&((*inv)[0][0]), &((*mat)[1][1]));
ibz_copy(&((*inv)[1][1]), &prod);
ibz_neg(&((*inv)[1][0]), &((*mat)[1][0]));
ibz_neg(&((*inv)[0][1]), &((*mat)[0][1]));
for(int i = 0; i<2;i++){
for(int j = 0; j<2;j++){
ibz_mul(&((*inv)[i][j]),&((*inv)[i][j]),&det);
ibz_mod(&((*inv)[i][j]),&((*inv)[i][j]),m);
}
}
}
ibz_finalize(&det);
ibz_finalize(&prod);
return(res);
}
//helper for cvp
int quat_dim2_lattice_contains(ibz_mat_2x2_t *basis, ibz_t *coord1, ibz_t *coord2){
int res = 1;
ibz_t prod, sum, det, r;
ibz_init(&det);
ibz_init(&r);
ibz_init(&sum);
ibz_init(&prod);
// compute det, then both coordinates (inverse*det)*vec, where vec is (coord1, coord2) and check wthether det divides both results
ibz_mat_2x2_det_from_ibz(&det, &((*basis)[0][0]), &((*basis)[0][1]), &((*basis)[1][0]), &((*basis)[1][1]));
ibz_mul(&sum,coord1,&((*basis)[1][1]));
ibz_mul(&prod,coord2,&((*basis)[0][1]));
ibz_sub(&sum,&sum,&prod);
ibz_div(&prod,&r,&sum,&det);
res = res && ibz_is_zero(&r);
ibz_mul(&sum,coord2,&((*basis)[0][0]));
ibz_mul(&prod,coord1,&((*basis)[1][0]));
ibz_sub(&sum,&sum,&prod);
ibz_div(&prod,&r,&sum,&det);
res = res && ibz_is_zero(&r);
ibz_finalize(&det);
ibz_finalize(&r);
ibz_finalize(&sum);
ibz_finalize(&prod);
return(res);
}
void quat_dim2_lattice_norm(ibz_t *norm, const ibz_t *coord1, const ibz_t *coord2, const ibz_t *norm_q){
ibz_t prod, sum;
ibz_init(&prod);
ibz_init(&sum);
ibz_mul(&sum,coord1,coord1);
ibz_mul(&prod,coord2,coord2);
ibz_mul(&prod,&prod,norm_q);
ibz_add(norm,&sum,&prod);
ibz_finalize(&prod);
ibz_finalize(&sum);
}
void quat_dim2_lattice_bilinear(ibz_t *res, const ibz_t *v11, const ibz_t *v12,const ibz_t *v21, const ibz_t *v22, const ibz_t *norm_q){
ibz_t prod, sum;
ibz_init(&prod);
ibz_init(&sum);
ibz_mul(&sum,v11,v21);
ibz_mul(&prod,v12,v22);
ibz_mul(&prod,&prod,norm_q);
ibz_add(res,&sum,&prod);
ibz_finalize(&prod);
ibz_finalize(&sum);
}
// algo 3.1.14 Cohen (exact solution for shortest vector in dimension 2, than take a second, orthogonal vector)
void quat_dim2_lattice_short_basis(ibz_mat_2x2_t *reduced, const ibz_mat_2x2_t *basis, const ibz_t *norm_q){
ibz_vec_2_t a,b,t;
ibz_t prod,sum, norm_a, norm_b, r, norm_t, n;
ibz_vec_2_init(&a);
ibz_vec_2_init(&b);
ibz_vec_2_init(&t);
ibz_init(&prod);
ibz_init(&sum);
ibz_init(&r);
ibz_init(&n);
ibz_init(&norm_t);
ibz_init(&norm_a);
ibz_init(&norm_b);
// init a,b
ibz_copy(&(a[0]),&((*basis)[0][0]));
ibz_copy(&(a[1]),&((*basis)[1][0]));
ibz_copy(&(b[0]),&((*basis)[0][1]));
ibz_copy(&(b[1]),&((*basis)[1][1]));
// compute initial norms
quat_dim2_lattice_norm(&norm_a,&(a[0]),&(a[1]), norm_q);
quat_dim2_lattice_norm(&norm_b,&(b[0]),&(b[1]), norm_q);
// exchange if needed
if(ibz_cmp(&norm_a,&norm_b)<0){
ibz_copy(&sum,&(a[0]));
ibz_copy(&(a[0]),&(b[0]));
ibz_copy(&(b[0]),&sum);
ibz_copy(&sum,&(a[1]));
ibz_copy(&(a[1]),&(b[1]));
ibz_copy(&(b[1]),&sum);
ibz_copy(&sum,&norm_a);
ibz_copy(&norm_a,&norm_b);
ibz_copy(&norm_b,&sum);
}
int test = 1;
while(test){
//compute n
quat_dim2_lattice_bilinear(&n,&(a[0]),&(a[1]),&(b[0]),&(b[1]),norm_q);
// set r
ibz_rounded_div(&r,&n,&norm_b);
// compute t_norm
ibz_set(&prod,2);
ibz_mul(&prod,&prod,&n);
ibz_mul(&prod,&prod,&r);
ibz_sub(&sum,&norm_a,&prod);
ibz_mul(&prod,&r,&r);
ibz_mul(&prod,&prod,&norm_b);
ibz_add(&norm_t,&sum,&prod);
// test:
if(ibz_cmp(&norm_b,&norm_t)>0){
// compute t, a, b
ibz_copy(&norm_a,&norm_b);
ibz_copy(&norm_b,&norm_t);
// t is a -rb, a is b, b is t
ibz_mul(&prod,&r,&(b[0]));
ibz_sub(&(t[0]),&(a[0]),&prod);
ibz_mul(&prod,&r,&(b[1]));
ibz_sub(&(t[1]),&(a[1]),&prod);
ibz_copy(&(a[0]),&(b[0]));
ibz_copy(&(a[1]),&(b[1]));
ibz_copy(&(b[0]),&(t[0]));
ibz_copy(&(b[1]),&(t[1]));
} else {
test = 0;
}
}
// output : now b is short: need to get 2nd short vector: idea: take shortest among t and a
if(ibz_cmp(&norm_t,&norm_a)<0){
ibz_mul(&prod,&r,&(b[0]));
ibz_sub(&(a[0]),&(a[0]),&prod);
ibz_mul(&prod,&r,&(b[1]));
ibz_sub(&(a[1]),&(a[1]),&prod);
}
ibz_copy(&((*reduced)[0][0]),&(b[0]));
ibz_copy(&((*reduced)[1][0]),&(b[1]));
ibz_copy(&((*reduced)[0][1]),&(a[0]));
ibz_copy(&((*reduced)[1][1]),&(a[1]));
ibz_finalize(&prod);
ibz_finalize(&sum);
ibz_finalize(&norm_a);
ibz_finalize(&norm_b);
ibz_finalize(&norm_t);
ibz_vec_2_finalize(&a);
ibz_vec_2_finalize(&b);
ibz_vec_2_finalize(&t);
ibz_finalize(&r);
ibz_finalize(&n);
}
// compute the rounded value of <a*,t>/<a*,a*>, where a* is a orthogonalised with respect to b
void quat_dim2_lattice_get_coefficient_with_orthogonalisation(ibz_t *res, const ibz_t *a0,const ibz_t *a1,const ibz_t *b0,const ibz_t *b1,const ibz_t *t0,const ibz_t *t1,const ibz_t *norm_q){
ibz_t norm_b, bilinear, astar1,astar0, prod, norm_astar;
ibz_init(&norm_b);
ibz_init(&norm_astar);
ibz_init(&bilinear);
ibz_init(&prod);
ibz_init(&astar0);
ibz_init(&astar1);
quat_dim2_lattice_norm(&norm_b,b0,b1,norm_q);
quat_dim2_lattice_bilinear(&bilinear,a0,a1,b0,b1,norm_q);
ibz_mul(&astar0,a0,&norm_b);
ibz_mul(&prod,b0,&bilinear);
ibz_sub(&astar0,&astar0,&prod);
ibz_mul(&astar1,a1,&norm_b);
ibz_mul(&prod,b1,&bilinear);
ibz_sub(&astar1,&astar1,&prod);
quat_dim2_lattice_norm(&norm_astar,&astar0,&astar1,norm_q);
quat_dim2_lattice_bilinear(&bilinear,&astar0,&astar1,t0,t1,norm_q);
ibz_mul(&bilinear,&bilinear,&norm_b);
ibz_rounded_div(res,&bilinear,&norm_astar);
ibz_finalize(&norm_b);
ibz_finalize(&norm_astar);
ibz_finalize(&bilinear);
ibz_finalize(&prod);
ibz_finalize(&astar0);
ibz_finalize(&astar1);
}
// find a closest vector to target in lattice, using a reduced basis given as argument basis.
//nearest plane algo as in https://cims.nyu.edu/~regev/teaching/lattices_fall_2004/ln/cvp.pdf, but without basis: basicallly just a projection
void quat_dim2_lattice_closest_vector(ibz_vec_2_t *target_minus_closest, ibz_vec_2_t *closest_coords_in_basis, const ibz_mat_2x2_t *reduced_basis, const ibz_vec_2_t *target, const ibz_t *norm_q){
ibz_vec_2_t coords, work;
ibz_t prod, sum, norm_a, norm_b, r;
ibz_vec_2_init(&coords);
ibz_vec_2_init(&work);
ibz_init(&prod);
ibz_init(&sum);
ibz_init(&norm_a);
ibz_init(&norm_b);
ibz_init(&r);
// init work
ibz_copy(&(work[0]),&((*target)[0]));
ibz_copy(&(work[1]),&((*target)[1]));
// norm a,b
quat_dim2_lattice_norm(&norm_a,&((*reduced_basis)[0][0]),&((*reduced_basis)[1][0]), norm_q);
quat_dim2_lattice_norm(&norm_b,&((*reduced_basis)[0][1]),&((*reduced_basis)[1][1]), norm_q);
// use 2nd basis vector (the larger one), and orthogonalise it with respect to the first one
quat_dim2_lattice_get_coefficient_with_orthogonalisation(&(coords[1]),&((*reduced_basis)[0][1]),&((*reduced_basis)[1][1]), &((*reduced_basis)[0][0]),&((*reduced_basis)[1][0]),&(work[0]),&(work[1]),norm_q);
// sustract projection from vector
ibz_mul(&prod,&((*reduced_basis)[0][1]),&(coords[1]));
ibz_sub(&(work[0]),&(work[0]),&prod);
ibz_mul(&prod,&((*reduced_basis)[1][1]),&(coords[1]));
ibz_sub(&(work[1]),&(work[1]),&prod );
// use 1st basis vector (the smaller one)
quat_dim2_lattice_bilinear(&(coords[0]),&(work[0]),&(work[1]),&((*reduced_basis)[0][0]),&((*reduced_basis)[1][0]),norm_q);
ibz_rounded_div(&(coords[0]),&(coords[0]),&norm_a);
// substract projection from vector
ibz_mul(&prod,&((*reduced_basis)[0][0]),&(coords[0]));
ibz_sub(&(work[0]),&(work[0]),&prod);
ibz_mul(&prod,&((*reduced_basis)[1][0]),&(coords[0]));
ibz_sub(&(work[1]),&(work[1]),&prod );
// copy results to output
ibz_copy(&((*target_minus_closest)[0]),&(work[0]));
ibz_copy(&((*target_minus_closest)[1]),&(work[1]));
ibz_copy(&((*closest_coords_in_basis)[0]),&(coords[0]));
ibz_copy(&((*closest_coords_in_basis)[1]),&(coords[1]));
ibz_vec_2_finalize(&coords);
ibz_vec_2_finalize(&work);
ibz_finalize(&prod);
ibz_finalize(&sum);
ibz_finalize(&norm_a);
ibz_finalize(&norm_b);
ibz_finalize(&r);
}
// give a,b,c such that ax^2 + bxy + cy^2 = N(Bz), where B is the basis, z the vector x,y and N the quadratic form (coord1^2 + q coord2^2)
void quat_dim2_lattice_get_qf_on_lattice(ibz_t *qf_a, ibz_t *qf_b,ibz_t *qf_c, const ibz_mat_2x2_t *basis ,const ibz_t *norm_q){
ibz_t a, b, c;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
quat_dim2_lattice_bilinear(&b,&((*basis)[0][0]), &((*basis)[1][0]), &((*basis)[0][1]), &((*basis)[1][1]),norm_q);
ibz_set(&a,2);
ibz_mul(&b,&b,&a);
quat_dim2_lattice_norm(&a,&((*basis)[0][0]), &((*basis)[1][0]),norm_q);
quat_dim2_lattice_norm(&c, &((*basis)[0][1]), &((*basis)[1][1]),norm_q);
ibz_copy(qf_a,&a);
ibz_copy(qf_b,&b);
ibz_copy(qf_c,&c);
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
}
int quat_dim2_lattice_test_cvp_condition(quat_alg_elem_t* elem, const ibz_vec_2_t* vec, const void* params){
ibz_t *p = ((ibz_t *) params);
ibz_t sum, two;
ibz_init(&sum);
ibz_init(&two);
ibz_set(&two,2);
ibz_add(&sum,&((*vec)[0]),&((*vec)[1]));
ibz_mod(&sum,&sum,p);
int res = (0 ==ibz_cmp(&sum,&two));
if(res){
quat_alg_elem_copy_ibz(elem,&two,&((*vec)[0]),&((*vec)[1]),&((*vec)[0]),&((*vec)[1]));
}
ibz_finalize(&sum);
ibz_finalize(&two);
return(res);
}
int quat_dim2_lattice_bound_and_condition(quat_alg_elem_t *res, const ibz_t *x, const ibz_t *y, int (*condition)(quat_alg_elem_t* , const ibz_vec_2_t*, const void*), const void* params, const ibz_vec_2_t* target_minus_closest, const ibz_mat_2x2_t *lat_basis, const ibz_t* norm_q, const ibz_t* norm_bound){
ibz_vec_2_t sum, proposal;
ibz_t norm;
int ok = 0;
ibz_init(&norm);
ibz_vec_2_init(&sum);
ibz_vec_2_init(&proposal);
// put x,y from lattice basis in canonical basis
ibz_copy(&(proposal[0]), x);
ibz_copy(&(proposal[1]), y);
ibz_mat_2x2_eval(&proposal,lat_basis,&proposal);
//compute norm of target -closest-proposal
ibz_sub(&(sum[0]),&((*target_minus_closest)[0]),&((proposal)[0]));
ibz_sub(&(sum[1]),&((*target_minus_closest)[1]),&((proposal)[1]));
quat_dim2_lattice_norm(&norm,&(sum[0]),&(sum[1]),norm_q);
// test
if( ibz_cmp(&norm,norm_bound) <= 0){
ok = condition(res,&sum,params);
}
ibz_finalize(&norm);
ibz_vec_2_finalize(&sum);
ibz_vec_2_finalize(&proposal);
return(ok);
}
int quat_dim2_lattice_qf_value_bound_generation(ibz_t *res, const ibz_t *num_a, const ibz_t *denom_a, const ibz_t *num_b, const ibz_t *denom_b){
int ok = 1;
ibz_t sqrt_num, sqrt_denom, one, zero, common_denom, r;
ibz_init(&sqrt_num);
ibz_init(&sqrt_denom);
ibz_init(&one);
ibz_init(&zero);
ibz_init(&common_denom);
ibz_init(&r);
ibz_set(&zero,0);
ibz_set(&one,1);
ibz_mul(&r,num_a,denom_a);
if((ibz_cmp(denom_a,&zero)<0)||ibz_is_zero(denom_a)||ibz_is_zero(denom_b)){
ok = 0;
}
if(ok){
ibz_sqrt_floor(&sqrt_num,num_a);
ibz_add(&sqrt_num,&sqrt_num, &one);
ibz_sqrt_floor(&sqrt_denom,denom_a);
ibz_mul(&common_denom,denom_b,&sqrt_denom);
ibz_mul(&sqrt_num,&sqrt_num,denom_b);
ibz_mul(&r,&sqrt_denom,num_b);
ibz_add(&sqrt_num,&sqrt_num,&r);
ibz_div(res,&r,&sqrt_num,&common_denom);
ibz_add(res,res,&one);
}
ibz_finalize(&sqrt_num);
ibz_finalize(&sqrt_denom);
ibz_finalize(&one);
ibz_finalize(&zero);
ibz_finalize(&common_denom);
ibz_finalize(&r);
return(ok);
}
//Uses algorithm 2.7.5 (Fincke-Pohst) from Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
//Slightly adapted to work without rational numbers and their square roots
//Therefore needing a test to make sure the bounds are respected
int quat_dim2_lattice_qf_enumerate_short_vec(quat_alg_elem_t *res,int (*condition)(quat_alg_elem_t*, const ibz_vec_2_t*, const void*), const void *params, const ibz_vec_2_t *target_minus_closest, const ibz_mat_2x2_t *lat_basis, const ibz_t *norm_q,const ibz_t *norm_bound, const int max_tries){
int ok = 1;
int found = 0;
int tries = 0;
int stop = 0;
ibz_t bound_y, bound_x, x, y, disc, prod, var, one, four_a2, four_a2_norm_bound, four_a2_c_minus_b2, four_a3,two_a, zero, y2, qf_a, qf_b, qf_c, norm_bound_for_enumeration;
ibz_init(&bound_y);
ibz_init(&bound_x);
ibz_init(&y);
ibz_init(&y2);
ibz_init(&x);
ibz_init(&qf_a);
ibz_init(&qf_b);
ibz_init(&qf_c);
ibz_init(&var);
ibz_init(&one);
ibz_init(&zero);
ibz_init(&disc);
ibz_init(&prod);
ibz_init(&four_a2);
ibz_init(&two_a);
ibz_init(&norm_bound_for_enumeration);
ibz_init(&four_a2_norm_bound);
ibz_init(&four_a2_c_minus_b2);
ibz_init(&four_a3);
ibz_set(&one,1);
ibz_set(&zero,0);
// substract norm of distance from bound to not enumerate too large vectors at beginning
// this is an heuristic which can fail, so in case it is really bad, we do not do it
quat_dim2_lattice_norm(&norm_bound_for_enumeration,&((*target_minus_closest)[0]),&((*target_minus_closest)[1]),norm_q);
ibz_sub(&norm_bound_for_enumeration,norm_bound,&norm_bound_for_enumeration);
if(ibz_cmp(&norm_bound_for_enumeration,&zero)<=0){
ibz_copy(&norm_bound_for_enumeration, norm_bound);
}
//Set qf_a, qf_b, qf_c such that for x,y, a vector represented in lat_basis, ax^2 + bxy + cy^2 is the norm defined by norm_q
quat_dim2_lattice_get_qf_on_lattice(&qf_a,&qf_b,&qf_c,lat_basis,norm_q);
//discriminant computation
ibz_mul(&disc,&qf_a,&qf_c);
ibz_set(&var,4);
ibz_mul(&disc,&disc,&var);
ibz_mul(&prod,&qf_b,&qf_b);
ibz_sub(&disc,&disc,&prod);
// only continue if disc is not zero nor negative
if (ibz_cmp(&disc,&zero)<=0){
ok = 0;
}
if(ok){
// precomputations
ibz_set(&var,2);
ibz_mul(&two_a,&var,&qf_a);//2a init
ibz_mul(&four_a2,&two_a,&two_a);//4a^2 init
ibz_mul(&four_a2_norm_bound,&four_a2,&norm_bound_for_enumeration);//4a^2*norm_bound init
ibz_mul(&four_a3,&four_a2,&qf_a);//4a^3 init
ibz_copy(&four_a2_c_minus_b2,&prod);//equals b^2 now, since prod was not reused since
ibz_mul(&prod,&four_a2,&qf_c);
ibz_sub(&four_a2_c_minus_b2,&prod,&four_a2_c_minus_b2);//now has correct value
// y bound generation
quat_dim2_lattice_qf_value_bound_generation(&bound_y,&four_a2_norm_bound,&four_a2_c_minus_b2,&zero,&one);
ibz_neg(&y,&bound_y);
ibz_sub(&y,&y,&one); // y is set
while((!found) && (!stop) && (ibz_cmp(&y,&bound_y)<0) && (tries <max_tries)){
ibz_add(&y,&y,&one);
ibz_mul(&y2,&y,&y);
ibz_mul(&prod,&qf_b,&y);
ibz_neg(&var,&prod);//var = -by
ibz_mul(&prod,&y2,&four_a2_c_minus_b2);
ibz_add(&prod,&prod,&four_a2_norm_bound);//prod = 4a^2*norm_bound + (4ca^2 - b^2)y^2
quat_dim2_lattice_qf_value_bound_generation(&bound_x,&prod, &four_a3,&var,&two_a);
ibz_neg(&var,&var);
quat_dim2_lattice_qf_value_bound_generation(&x,&prod, &four_a3,&var,&two_a);
ibz_neg(&x,&x);
ibz_sub(&x,&x,&one);// x is set
while((!found) && (!stop) && (ibz_cmp(&x,&bound_x)<0) && (tries <max_tries)){
tries +=1;
ibz_add(&x,&x,&one);
found = quat_dim2_lattice_bound_and_condition(res,&x, &y,condition, params, target_minus_closest, lat_basis, norm_q, norm_bound);
stop = (ibz_is_zero(&x) && ibz_is_zero(&y));
}
}
}
ibz_finalize(&norm_bound_for_enumeration);
ibz_finalize(&bound_y);
ibz_finalize(&bound_x);
ibz_finalize(&y);
ibz_finalize(&y2);
ibz_finalize(&x);
ibz_finalize(&qf_a);
ibz_finalize(&qf_b);
ibz_finalize(&qf_c);
ibz_finalize(&var);
ibz_finalize(&one);
ibz_finalize(&zero);
ibz_finalize(&disc);
ibz_finalize(&prod);
ibz_finalize(&four_a2);
ibz_finalize(&four_a2_norm_bound);
ibz_finalize(&four_a2_c_minus_b2);
ibz_finalize(&four_a3);
ibz_finalize(&two_a);
return(ok && found);
}
int quat_2x2_lattice_enumerate_cvp_filter(quat_alg_elem_t *res, const ibz_mat_2x2_t *lat_basis, const ibz_vec_2_t *target,unsigned int qf, unsigned int dist_bound, int (*condition)(quat_alg_elem_t* , const ibz_vec_2_t*, const void*), const void* params, unsigned int max_tries){
int found = 0;
unsigned int counter = 0;
ibz_t norm_q, norm_bound;
ibz_mat_2x2_t reduced;
ibz_vec_2_t closest_coords, target_minus_closest;
ibz_init(&norm_q);
ibz_init(&norm_bound);
ibz_mat_2x2_init(&reduced);
ibz_vec_2_init(&target_minus_closest);
ibz_vec_2_init(&closest_coords);
ibz_set(&norm_q,qf);
ibz_set(&norm_bound,2);
ibz_pow(&norm_bound,&norm_bound,dist_bound);
// reduce lattice basis
quat_dim2_lattice_short_basis(&reduced,lat_basis,&norm_q);
// find closest vector
quat_dim2_lattice_closest_vector(&target_minus_closest,&closest_coords,&reduced,target,&norm_q);
// enumerate short vector and test
found = quat_dim2_lattice_qf_enumerate_short_vec(res,condition,params, &target_minus_closest, &reduced, &norm_q, &norm_bound, max_tries);
ibz_finalize(&norm_q);
ibz_finalize(&norm_bound);
ibz_mat_2x2_finalize(&reduced);
ibz_vec_2_finalize(&target_minus_closest);
ibz_vec_2_finalize(&closest_coords);
return(found);
}

View File

@@ -0,0 +1,842 @@
#include <quaternion.h>
#include "internal.h"
//internal helper functions
void ibz_mat_4x4_mul(ibz_mat_4x4_t *res, const ibz_mat_4x4_t *a, const ibz_mat_4x4_t *b){
ibz_mat_4x4_t mat;
ibz_t prod;
ibz_init(&prod);
ibz_mat_4x4_init(&mat);
for (int i = 0; i <4; i++){
for (int j = 0; j <4; j++){
ibz_set(&(mat[i][j]),0);
for (int k = 0; k <4; k++){
ibz_mul(&prod,&((*a)[i][k]), &((*b)[k][j]));
ibz_add(&(mat[i][j]), &(mat[i][j]), &prod);
}
}
}
for (int i = 0; i <4; i++){
for (int j = 0; j <4; j++){
ibz_copy(&((*res)[i][j]),&(mat[i][j]));
}
}
ibz_mat_4x4_finalize(&mat);
ibz_finalize(&prod);
}
//helper functions for lattices
void ibz_vec_4_set(ibz_vec_4_t *vec, int64_t coord0, int64_t coord1, int64_t coord2, int64_t coord3){
ibz_set(&((*vec)[0]),coord0);
ibz_set(&((*vec)[1]),coord1);
ibz_set(&((*vec)[2]),coord2);
ibz_set(&((*vec)[3]),coord3);
}
void ibz_vec_4_copy(ibz_vec_4_t *new, const ibz_vec_4_t *vec){
for (int i = 0; i <4; i++){
ibz_copy(&((*new)[i]),&((*vec)[i]));
}
}
void ibz_vec_4_negate(ibz_vec_4_t *neg, const ibz_vec_4_t *vec){
for (int i = 0; i <4; i++){
ibz_neg(&((*neg)[i]),&((*vec)[i]));
}
}
void ibz_vec_4_linear_combination(ibz_vec_4_t *lc, const ibz_t *coeff_a, const ibz_vec_4_t *vec_a, const ibz_t *coeff_b, const ibz_vec_4_t *vec_b){
ibz_t prod;
ibz_vec_4_t sums;
ibz_vec_4_init(&sums);
ibz_init(&prod);
for (int i = 0; i <4; i++){
ibz_mul(&(sums[i]),coeff_a,&((*vec_a)[i]));
ibz_mul(&prod,coeff_b,&((*vec_b)[i]));
ibz_add(&(sums[i]),&(sums[i]),&prod);
}
for (int i = 0; i <4; i++){
ibz_copy(&((*lc)[i]),&(sums[i]));
}
ibz_finalize(&prod);
ibz_vec_4_finalize(&sums);
}
int ibz_vec_4_scalar_div(ibz_vec_4_t *quot, const ibz_t *scalar, const ibz_vec_4_t *vec){
int res = 1;
ibz_t r;
ibz_init(&r);
for(int i = 0; i < 4; i++){
ibz_div(&((*quot)[i]),&r,&((*vec)[i]),scalar);
res = res && ibz_is_zero(&r);
}
ibz_finalize(&r);
return(res);
}
void ibz_mat_4x4_copy(ibz_mat_4x4_t *new, const ibz_mat_4x4_t *mat){
for(int i = 0; i <4; i++){
for(int j = 0; j<4; j++){
ibz_copy(&((*new)[i][j]),&((*mat)[i][j]));
}
}
}
void ibz_mat_4x4_negate(ibz_mat_4x4_t *neg, const ibz_mat_4x4_t *mat){
for(int i = 0; i <4; i++){
for(int j = 0; j<4; j++){
ibz_neg(&((*neg)[i][j]),&((*mat)[i][j]));
}
}
}
void ibz_mat_4x4_transpose(ibz_mat_4x4_t *transposed, const ibz_mat_4x4_t *mat){
ibz_mat_4x4_t work;
ibz_mat_4x4_init(&work);
for(int i = 0; i < 4; i ++){
for(int j = 0; j < 4; j ++){
ibz_copy(&(work[i][j]),&((*mat)[j][i]));
}
}
ibz_mat_4x4_copy(transposed,&work);
ibz_mat_4x4_finalize(&work);
}
void ibz_mat_4x4_zero(ibz_mat_4x4_t *zero){
for(int i = 0; i <4; i++){
for(int j = 0; j<4; j++){
ibz_set(&((*zero)[i][j]),0);
}
}
}
void ibz_mat_4x4_identity(ibz_mat_4x4_t *id){
for(int i = 0; i <4; i++){
for(int j = 0; j<4; j++){
ibz_set(&((*id)[i][j]),0);
}
ibz_set(&((*id)[i][i]),1);
}
}
int ibz_mat_4x4_is_identity(const ibz_mat_4x4_t *mat){
int res = 1;
for(int i = 0; i <4; i++){
for(int j = 0; j<4; j++){
res = res && (ibz_get(&((*mat)[i][j])) == (i==j));
}
}
return(res);
}
int ibz_mat_4x4_equal(const ibz_mat_4x4_t *mat1, const ibz_mat_4x4_t *mat2){
int res = 0;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
res = res || ibz_cmp(&((*mat1)[i][j]),&((*mat2)[i][j]));
}
}
return(!res);
}
void ibz_mat_4x4_scalar_mul(ibz_mat_4x4_t *prod, const ibz_t *scalar, const ibz_mat_4x4_t *mat){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_mul(&((*prod)[i][j]),&((*mat)[i][j]),scalar);
}
}
}
void ibz_mat_4x4_gcd(ibz_t *gcd, const ibz_mat_4x4_t *mat){
ibz_t d;
ibz_init(&d);
ibz_copy(&d, &((*mat)[0][0]));
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_gcd(&d,&d,&((*mat)[i][j]));
}
}
ibz_copy(gcd,&d);
ibz_finalize(&d);
}
int ibz_mat_4x4_scalar_div(ibz_mat_4x4_t *quot, const ibz_t *scalar, const ibz_mat_4x4_t *mat){
int res = 1;
ibz_t r;
ibz_init(&r);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_div(&((*quot)[i][j]),&r,&((*mat)[i][j]),scalar);
res = res && ibz_is_zero(&r);
}
}
ibz_finalize(&r);
return(res);
}
int ibz_mat_4x4_is_hnf(const ibz_mat_4x4_t *mat){
int res = 1;
int found = 0;
int ind = 0;
ibz_t zero;
ibz_init(&zero);
// upper triangular
for (int i = 0; i < 4; i++){
// upper triangular
for (int j = 0; j < i; j++){
res = res && ibz_is_zero(&((*mat)[i][j]));
}
// find first non 0 element of line
found = 0;
for (int j = i; j < 4; j++){
if(found){
// all values are positive, and first non-0 is the largest of that line
res = res && (ibz_cmp(&((*mat)[i][j]),&zero)>=0);
res = res && (ibz_cmp(&((*mat)[i][ind]),&((*mat)[i][j]))>0);
} else {
if(!ibz_is_zero(&((*mat)[i][j]))){
found = 1;
ind = j;
// mustbe non-negative
res = res && (ibz_cmp(&((*mat)[i][j]),&zero)>0);
}
}
}
}
// check that first nom-zero elements ndex per column is strictly increasing
int linestart = -1;
int i = 0;
for(int j = 0; j<4; j++){
while((i < 4) &&(ibz_is_zero(&((*mat)[i][j])))){
i = i+1;
} if (i != 4) {
res = res && (linestart < i);
}
i = 0;
}
ibz_finalize(&zero);
return res;
}
//Algorithm used is the one at number 2.4.5 in Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
// assumes ibz_xgcd outputs u,v which are small in absolute value (as described in the book)void ibz_mat_4x8_hnf_core(ibz_mat_4x4_t *hnf, const ibz_mat_4x8_t *generators)
void ibz_mat_4x8_hnf_core(ibz_mat_4x4_t *hnf, const ibz_mat_4x8_t *generators)
{
int i = 3;
int j = 7;
int k = 7;
ibz_t b, u, v, d, zero, coeff_1, coeff_2, r;
ibz_vec_4_t c;
ibz_vec_4_t a[8];
ibz_init(&b);
ibz_init(&d);
ibz_init(&u);
ibz_init(&v);
ibz_init(&r);
ibz_init(&coeff_1);
ibz_init(&coeff_2);
ibz_init(&zero);
ibz_set(&zero,0);
ibz_vec_4_init(&c);
for (int h = 0; h < 8; h++){
ibz_vec_4_init(&(a[h]));
ibz_copy(&(a[h][0]), &((*generators)[0][h]));
ibz_copy(&(a[h][1]), &((*generators)[1][h]));
ibz_copy(&(a[h][2]), &((*generators)[2][h]));
ibz_copy(&(a[h][3]), &((*generators)[3][h]));
}
while (i != -1){
while (j != 0){
j = j - 1;
if (!ibz_is_zero(&(a[j][i]))){
// assumtion that ibz_xgcd outputs u,v which are small in absolute value is needed here
ibz_xgcd(&d,&u,&v,&(a[k][i]),&(a[j][i]));
// also, needs u non 0, but v can be 0 if needed
if(ibz_is_zero(&u)){
ibz_div(&v,&r,&(a[k][i]),&(a[j][i]));
ibz_set(&u,1);
ibz_sub(&v,&u,&v);
}
ibz_vec_4_linear_combination(&c,&u,&(a[k]),&v,&(a[j]));
ibz_div(&coeff_1,&r, &(a[k][i]),&d);
ibz_div(&coeff_2,&r, &(a[j][i]),&d);
ibz_neg(&coeff_2, &coeff_2);
ibz_vec_4_linear_combination(&(a[j]),&coeff_1,&(a[j]),&coeff_2,&(a[k]));
ibz_vec_4_copy(&(a[k]),&c);
}
}
ibz_copy(&b,&(a[k][i]));
if (ibz_cmp(&b, &zero) < 0){
ibz_vec_4_negate(&(a[k]),&(a[k]));
ibz_neg(&b, &b);
}
if (ibz_is_zero(&b)){
k = k + 1;
} else {
for(j = k+1; j < 8; j++) {
ibz_div(&d,&r,&(a[j][i]),&b);
if(ibz_cmp(&r,&zero) < 0){
ibz_set(&r,1);
ibz_sub(&d,&d,&r);
}
ibz_set(&r,1);
ibz_neg(&d,&d);
ibz_vec_4_linear_combination(&(a[j]),&r,&(a[j]),&d ,&(a[k]));
}
}
if (i != 0) {
k = k - 1;
j = k;
}
i = i - 1;
}
for (j = 4; j < 8; j++) {
for(i = 0; i < 4; i++){
ibz_copy(&((*hnf)[i][j-4]),&(a[j][i]));
}
}
ibz_finalize(&b);
ibz_finalize(&d);
ibz_finalize(&u);
ibz_finalize(&v);
ibz_finalize(&r);
ibz_finalize(&coeff_1);
ibz_finalize(&coeff_2);
ibz_finalize(&zero);
ibz_vec_4_finalize(&c);
for (int h = 0; h < 8; h++)
{
ibz_vec_4_finalize(&(a[h]));
}
}
void ibz_mat_4x4_hnf_mod(ibz_mat_4x4_t *hnf, const ibz_mat_4x4_t *mat, const ibz_t *mod){
ibz_mat_4x8_t input;
ibz_mat_4x8_init(&input);
for(int i = 0; i <4; i++){
for(int j = 0; j <4; j++){
ibz_copy(&(input[i][j]),&((*mat)[i][j]));
ibz_set(&(input[i][j+4]),0);
}
ibz_copy(&(input[i][i+4]),mod);
}
ibz_mat_4x8_hnf_core(hnf,&input);
ibz_mat_4x8_finalize(&input);
}
// functions to verify lll
void ibq_vec_4_copy_ibz(ibq_t (*vec)[4], const ibz_t *coeff0, const ibz_t *coeff1,const ibz_t *coeff2,const ibz_t *coeff3){
ibz_t one;
ibz_init(&one);
ibz_set(&one,1);
ibq_set(&((*vec)[0]),coeff0,&one);
ibq_set(&((*vec)[1]),coeff1,&one);
ibq_set(&((*vec)[2]),coeff2,&one);
ibq_set(&((*vec)[3]),coeff3,&one);
ibz_finalize(&one);
}
void quat_dim4_lll_bilinear(ibq_t *b, const ibq_t (*vec0)[4], const ibq_t (*vec1)[4], const ibz_t *q){
ibq_t sum, prod,norm_q;
ibz_t one;
ibz_init(&one);
ibz_set(&one,1);
ibq_init(&sum);
ibq_init(&prod);
ibq_init(&norm_q);
ibq_set(&norm_q,q,&one);
ibq_mul(&sum,&((*vec0)[0]),&((*vec1)[0]));
ibq_mul(&prod,&((*vec0)[1]),&((*vec1)[1]));
ibq_add(&sum,&sum,&prod);
ibq_mul(&prod,&((*vec0)[2]),&((*vec1)[2]));
ibq_mul(&prod,&prod,&norm_q);
ibq_add(&sum,&sum,&prod);
ibq_mul(&prod,&((*vec0)[3]),&((*vec1)[3]));
ibq_mul(&prod,&prod,&norm_q);
ibq_add(b,&sum,&prod);
ibz_finalize(&one);
ibq_finalize(&sum);
ibq_finalize(&prod);
ibq_finalize(&norm_q);
}
void quat_dim4_gram_schmidt_transposed_with_ibq(ibq_t (*orthogonalised_transposed)[4][4], const ibz_mat_4x4_t *mat, const ibz_t *q){
ibq_t work[4][4];
ibq_t vec[4];
ibq_t norm, b, coeff, prod;
ibq_init(&norm);
ibq_init(&coeff);
ibq_init(&prod);
ibq_init(&b);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibq_init(&(work[i][j]));
}
ibq_init(&(vec[i]));
}
// transpose the input matrix to be able to work on vectors
for(int i = 0; i < 4; i++){
ibq_vec_4_copy_ibz(&(work[i]),&((*mat)[0][i]),&((*mat)[1][i]),&((*mat)[2][i]),&((*mat)[3][i]));
}
for(int i = 0; i < 4; i++){
quat_dim4_lll_bilinear(&norm,&(work[i]),&(work[i]),q);
ibq_inv(&norm,&norm);
for(int j = i+1; j < 4; j++){
ibq_vec_4_copy_ibz(&vec, &((*mat)[0][j]),&((*mat)[1][j]),&((*mat)[2][j]),&((*mat)[3][j]));
quat_dim4_lll_bilinear(&b,&(work[i]),&vec,q);
ibq_mul(&coeff,&norm,&b);
for(int k = 0; k < 4; k++){
ibq_mul(&prod,&coeff,&(work[i][k]));
ibq_sub(&(work[j][k]),&(work[j][k]),&prod);
}
}
}
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibq_copy(&((*orthogonalised_transposed)[i][j]),&(work[i][j]));
}
}
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibq_finalize(&(work[i][j]));
}
ibq_finalize(&(vec[i]));
}
ibq_finalize(&norm);
ibq_finalize(&coeff);
ibq_finalize(&prod);
ibq_finalize(&b);
}
int quat_dim4_lll_verify(const ibz_mat_4x4_t *mat, const ibq_t *coeff, const ibz_t *q){
int res = 1;
ibq_t orthogonalised_transposed[4][4];
ibq_t tmp_vec[4];
ibq_t div,tmp,mu,two, norm, b;
ibz_t mu2_floored,num,denom;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibq_init(&(orthogonalised_transposed[i][j]));
}
ibq_init(&(tmp_vec[i]));
}
ibq_init(&div);
ibq_init(&tmp);
ibq_init(&norm);
ibq_init(&b);
ibq_init(&mu);
ibq_init(&two);
ibz_init(&mu2_floored);
ibz_init(&num);
ibz_init(&denom);
ibz_set(&num,2);
ibz_set(&denom,1);
ibq_set(&two,&num,&denom);
quat_dim4_gram_schmidt_transposed_with_ibq(&orthogonalised_transposed, mat,q);
// check small bilinear products/norms
for(int i = 0; i < 4; i++){
for(int j = 0; j < i; j++){
ibq_vec_4_copy_ibz(&tmp_vec, &((*mat)[0][i]),&((*mat)[1][i]),&((*mat)[2][i]),&((*mat)[3][i]));
quat_dim4_lll_bilinear(&b, &(orthogonalised_transposed[j]),&tmp_vec,q);
quat_dim4_lll_bilinear(&norm, &(orthogonalised_transposed[j]),&(orthogonalised_transposed[j]),q);
ibq_inv(&tmp,&norm);
ibq_mul(&mu,&b,&tmp);
//mu contains 2mu from now on
ibq_mul(&tmp,&mu,&two);
ibq_num(&num,&tmp);
ibq_denom(&denom,&tmp);
//assume rounding to 0
ibz_div(&mu2_floored,&denom,&num,&denom);
// 2*mu floores is 0 or mu is exactly 1/2, so (2mu)^2 is exactly 1
ibq_mul(&tmp,&tmp,&tmp);
res = res && (ibz_is_zero(&mu2_floored) || ibq_is_one(&tmp));
}
}
for(int i = 1; i < 4; i++){
ibq_vec_4_copy_ibz(&tmp_vec, &((*mat)[0][i]),&((*mat)[1][i]),&((*mat)[2][i]),&((*mat)[3][i]));
quat_dim4_lll_bilinear(&b, &(orthogonalised_transposed[i-1]),&tmp_vec,q);
quat_dim4_lll_bilinear(&norm, &(orthogonalised_transposed[i-1]),&(orthogonalised_transposed[i-1]),q);
ibq_inv(&tmp,&norm);
ibq_mul(&mu,&b,&tmp);
// tmp is mu^2
ibq_mul(&tmp,&mu,&mu);
// mu is coeff-mu^2
ibq_sub(&mu,coeff,&tmp);
quat_dim4_lll_bilinear(&tmp, &(orthogonalised_transposed[i]),&(orthogonalised_transposed[i]),q);
//get (3/4-mu^2)norm(i-1)
ibq_mul(&div,&norm,&mu);
res = res && (ibq_cmp(&tmp,&div) >= 0);
}
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibq_finalize(&(orthogonalised_transposed[i][j]));
}
ibq_finalize(&(tmp_vec[i]));
}
ibq_finalize(&div);
ibq_finalize(&norm);
ibq_finalize(&b);
ibq_finalize(&tmp);
ibq_finalize(&mu);
ibq_finalize(&two);
ibz_finalize(&mu2_floored);
ibz_finalize(&num);
ibz_finalize(&denom);
return(res);
}
//4x4 inversion helper functions
void ibz_inv_dim4_make_coeff_pmp(ibz_t *coeff, const ibz_t *a1, const ibz_t *a2, const ibz_t *b1, const ibz_t *b2, const ibz_t *c1, const ibz_t *c2){
ibz_t prod, sum;
ibz_init(&prod);
ibz_init(&sum);
ibz_mul(&sum,a1,a2);
ibz_mul(&prod,b1,b2);
ibz_sub(&sum,&sum,&prod);
ibz_mul(&prod,c1,c2);
ibz_add(coeff,&sum,&prod);
ibz_finalize(&prod);
ibz_finalize(&sum);
}
void ibz_inv_dim4_make_coeff_mpm(ibz_t *coeff, const ibz_t *a1, const ibz_t *a2, const ibz_t *b1, const ibz_t *b2, const ibz_t *c1, const ibz_t *c2){
ibz_t prod, sum;
ibz_init(&prod);
ibz_init(&sum);
ibz_mul(&sum,b1,b2);
ibz_mul(&prod,a1,a2);
ibz_sub(&sum,&sum,&prod);
ibz_mul(&prod,c1,c2);
ibz_sub(coeff,&sum,&prod);
ibz_finalize(&prod);
ibz_finalize(&sum);
}
//Method from https://www.geometrictools.com/Documentation/LaplaceExpansionTheorem.pdf 3rd of May 2023, 16h15 CEST
int ibz_mat_4x4_inv_with_det_as_denom(ibz_mat_4x4_t *inv, ibz_t *det, const ibz_mat_4x4_t *mat){
ibz_t prod,work_det;
ibz_mat_4x4_t work;
ibz_t s[6];
ibz_t c[6];
for (int i = 0; i < 6; i++){
ibz_init(&(s[i]));
ibz_init(&(c[i]));
}
ibz_mat_4x4_init(&work);
ibz_init(&prod);
ibz_init(&work_det);
//compute some 2x2 minors, store them in s and c
for (int i = 0; i < 3; i++){
ibz_mat_2x2_det_from_ibz(&(s[i]),&((*mat)[0][0]),&((*mat)[0][i+1]),&((*mat)[1][0]),&((*mat)[1][i+1]));
ibz_mat_2x2_det_from_ibz(&(c[i]),&((*mat)[2][0]),&((*mat)[2][i+1]),&((*mat)[3][0]),&((*mat)[3][i+1]));
}
for (int i = 0; i < 2; i++){
ibz_mat_2x2_det_from_ibz(&(s[3+i]),&((*mat)[0][1]),&((*mat)[0][2+i]),&((*mat)[1][1]),&((*mat)[1][2+i]));
ibz_mat_2x2_det_from_ibz(&(c[3+i]),&((*mat)[2][1]),&((*mat)[2][2+i]),&((*mat)[3][1]),&((*mat)[3][2+i]));
}
ibz_mat_2x2_det_from_ibz(&(s[5]),&((*mat)[0][2]),&((*mat)[0][3]),&((*mat)[1][2]),&((*mat)[1][3]));
ibz_mat_2x2_det_from_ibz(&(c[5]),&((*mat)[2][2]),&((*mat)[2][3]),&((*mat)[3][2]),&((*mat)[3][3]));
//compute det
ibz_set(&work_det,0);
for (int i = 0; i < 6; i++){
ibz_mul(&prod,&(s[i]),&(c[5-i]));
if ((i != 1) && (i != 4)){
ibz_add(&work_det,&work_det,&prod);
} else {
ibz_sub(&work_det,&work_det,&prod);
}
}
if (!ibz_is_zero(&work_det)){
//compute transposed adjugate
for (int j = 0; j < 4; j++){
for (int k = 0; k < 2; k++){
if ((k + j + 1) % 2 == 1){
ibz_inv_dim4_make_coeff_pmp(&(work[j][k]), &((*mat)[1-k][(j==0)]), &(c[6-j-(j==0)]), &((*mat)[1-k][2-(j>1)]), &(c[4-j-(j==1)]), &((*mat)[1-k][3-(j==3)]), &(c[3-j-(j==1)-(j==2)]));
} else {
ibz_inv_dim4_make_coeff_mpm(&(work[j][k]), &((*mat)[1-k][(j==0)]), &(c[6-j-(j==0)]), &((*mat)[1-k][2-(j>1)]), &(c[4-j-(j==1)]), &((*mat)[1-k][3-(j==3)]), &(c[3-j-(j==1)-(j==2)]));
}
}
for (int k = 2; k < 4; k++){
if ((k + j + 1) % 2 == 1){
ibz_inv_dim4_make_coeff_pmp(&(work[j][k]), &((*mat)[3-(k==3)][(j==0)]), &(s[6-j-(j==0)]),&((*mat)[3-(k==3)][2-(j>1)]), &(s[4-j-(j==1)]),&((*mat)[3-(k==3)][3-(j==3)]), &(s[3-j-(j==1)-(j==2)]));
} else {
ibz_inv_dim4_make_coeff_mpm(&(work[j][k]), &((*mat)[3-(k==3)][(j==0)]), &(s[6-j-(j==0)]),&((*mat)[3-(k==3)][2-(j>1)]), &(s[4-j-(j==1)]),&((*mat)[3-(k==3)][3-(j==3)]), &(s[3-j-(j==1)-(j==2)]));
}
}
}
// put transposed adjugate in result
if(inv != NULL)
ibz_mat_4x4_copy(inv,&work);
}
//output det in any case
if(det != NULL)
ibz_copy(det,&work_det);
for (int i = 0; i < 6; i++){
ibz_finalize(&s[i]);
ibz_finalize(&c[i]);
}
ibz_mat_4x4_finalize(&work);
ibz_finalize(&work_det);
ibz_finalize(&prod);
return(!ibz_is_zero(det));
}
// larger matrix modular kernel
//Algorithm used is the one at number 2.3.1 in Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
//most notations also are from there, except their r becoming kernel_dimension here and prod being used instaed of d as temporary variable.
int ibz_4x5_right_ker_mod_prime(ibz_vec_5_t *ker, const ibz_mat_4x5_t *mat, const ibz_t *p){
int k, i, j, kernel_dim, columns, rows;
int c[4] ={0,0,0,0};
int d[5];
ibz_mat_4x5_t work_mat;
ibz_t prod, var;
ibz_init(&prod);
ibz_init(&var);
k = 0;
columns = 5;
rows = 4;
j = 0;
kernel_dim = 0;
ibz_mat_4x5_init(&work_mat);
for(int s = 0; s < rows; s++){
for(int t = 0; t < columns; t++){
ibz_mod(&(work_mat[s][t]),&((*mat)[s][t]),p);
}
}
while(k<columns){
j = 0;
while((j<rows)&&(ibz_is_zero(&(work_mat[j][k]))||(c[j]!=0))){
j = j + 1;
}
// found none
if(j == rows){
kernel_dim = kernel_dim+1;
d[k]=0;
k = k+1;
} else { // found such a j
ibz_invmod(&prod,&(work_mat[j][k]),p);
ibz_neg(&prod,&prod);
ibz_mod(&prod,&prod,p);
ibz_set(&(work_mat[j][k]),-1);
ibz_mod(&(work_mat[j][k]),&(work_mat[j][k]),p);
for(int s = k+1; s < columns; s++){
ibz_mul(&(work_mat[j][s]),&(work_mat[j][s]),&prod);
ibz_mod(&(work_mat[j][s]),&(work_mat[j][s]),p);
}
for(i = 0; i< rows; i++){
if(i!=j){
ibz_copy(&var,&(work_mat[i][k]));
ibz_set(&(work_mat[i][k]),0);
for(int s = k+1; s < columns; s++){
ibz_mul(&prod,&(work_mat[j][s]),&var);
ibz_add(&(work_mat[i][s]),&(work_mat[i][s]),&prod);
ibz_mod(&(work_mat[i][s]),&(work_mat[i][s]),p);
}
}
}
c[j] = k+1;
d[k] = j+1;
k = k + 1;
}
}
//create output
if(kernel_dim==1){
for(k = 0; k <columns; k++){
// should be true exactly for 1 k, since kernel_dim = 1
if(d[k]== 0){
for(int s = 0; s < columns; s++){
ibz_set(&((*ker)[s]),0);
if(s == k){
ibz_set(&((*ker)[s]),1);
}
if(d[s] > 0){
ibz_mod(&((*ker)[s]),&(work_mat[d[s]-1][k]),p);
}
}
}
}
}
ibz_finalize(&prod);
ibz_finalize(&var);
ibz_mat_4x5_finalize(&work_mat);
return(kernel_dim==1);
}
//same algo as ibz_4x5_right_ker_mod_prime, same notations, just the column number changes
int ibz_4x4_right_ker_mod_prime(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, const ibz_t *p){
int k, i, j, kernel_dim, columns, rows;
int c[4] ={0,0,0,0};
int d[4];
ibz_mat_4x4_t work_mat;
ibz_t prod, var;
ibz_init(&prod);
ibz_init(&var);
k = 0;
columns = 4;
rows = 4;
j = 0;
kernel_dim = 0;
ibz_mat_4x4_init(&work_mat);
for(int s = 0; s < rows; s++){
for(int t = 0; t < columns; t++){
ibz_mod(&(work_mat[s][t]),&((*mat)[s][t]),p);
}
}
while(k<columns){
j = 0;
while((j<rows)&&(ibz_is_zero(&(work_mat[j][k]))||(c[j]!=0))){
j = j + 1;
}
// found none
if(j == rows){
kernel_dim = kernel_dim+1;
d[k]=0;
k = k+1;
} else { // found such a j
ibz_invmod(&prod,&(work_mat[j][k]),p);
ibz_neg(&prod,&prod);
ibz_mod(&prod,&prod,p);
ibz_set(&(work_mat[j][k]),-1);
ibz_mod(&(work_mat[j][k]),&(work_mat[j][k]),p);
for(int s = k+1; s < columns; s++){
ibz_mul(&(work_mat[j][s]),&(work_mat[j][s]),&prod);
ibz_mod(&(work_mat[j][s]),&(work_mat[j][s]),p);
}
for(i = 0; i< rows; i++){
if(i!=j){
ibz_copy(&var,&(work_mat[i][k]));
ibz_set(&(work_mat[i][k]),0);
for(int s = k+1; s < columns; s++){
ibz_mul(&prod,&(work_mat[j][s]),&var);
ibz_add(&(work_mat[i][s]),&(work_mat[i][s]),&prod);
ibz_mod(&(work_mat[i][s]),&(work_mat[i][s]),p);
}
}
}
c[j] = k+1;
d[k] = j+1;
k = k + 1;
}
}
//create output
if(kernel_dim==1){
for(k = 0; k <columns; k++){
// should be true exactly for 1 k, since kernel_dim = 1
if(d[k]== 0){
for(int s = 0; s < columns; s++){
ibz_set(&((*ker)[s]),0);
if(s == k){
ibz_set(&((*ker)[s]),1);
}
if(d[s] > 0){
ibz_mod(&((*ker)[s]),&(work_mat[d[s]-1][k]),p);
}
}
}
}
}
ibz_finalize(&prod);
ibz_finalize(&var);
ibz_mat_4x4_finalize(&work_mat);
return(kernel_dim==1);
}
int ibz_4x4_right_ker_mod_power_of_2(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, unsigned short exp)
{
ibz_mat_4x4_t full_ker;
ibz_mat_4x5_t howell;
ibz_t pow2;
ibz_mat_4x4_init(&full_ker);
ibz_mat_4x5_init(&howell);
ibz_init(&pow2);
// 2^exp
ibz_set(&pow2, 1);
ibz_mul_2exp(&pow2, &pow2, exp);
ibz_mat_right_ker_mod(4, 4, full_ker, *mat, &pow2);
int zeros = ibz_mat_howell(4, 4, howell, NULL, full_ker, &pow2);
int dim = 0;
for (int j = zeros; j < 5; j++) {
int primitive = 0;
for (int i = 0; i < 4; i++)
primitive |= !ibz_is_even(&howell[i][j]);
if (primitive) {
for (int i = 0; i < 4; i++)
ibz_copy(&(*ker)[i], &howell[i][j]);
dim++;
}
}
ibz_mat_4x4_finalize(&full_ker);
ibz_mat_4x5_finalize(&howell);
ibz_finalize(&pow2);
return dim == 1;
}
// matrix evaluation
void ibz_mat_4x4_eval(quat_alg_coord_t *res, const ibz_mat_4x4_t *mat, const quat_alg_coord_t *vec){
quat_alg_coord_t sum;
ibz_t prod;
ibz_init(&prod);
quat_alg_coord_init(&sum);
for (int i = 0; i <4; i++){
ibz_set(&(sum[i]),0);
}
for (int i = 0; i <4; i++){
for (int j = 0; j <4; j++){
ibz_mul(&prod, &(*mat)[i][j], &(*vec)[j]);
ibz_add(&(sum[i]),&(sum[i]), &prod);
}
}
for (int i = 0; i <4; i++){
ibz_copy(&(*res)[i],&(sum[i]));
}
ibz_finalize(&prod);
quat_alg_coord_finalize(&sum);
}
// quadratic forms
void quat_qf_eval(ibz_t *res, const ibz_mat_4x4_t *qf, const quat_alg_coord_t *coord){
quat_alg_coord_t sum;
ibz_t prod;
ibz_init(&prod);
quat_alg_coord_init(&sum);
ibz_mat_4x4_eval(&sum, qf, coord);
for (int i = 0; i <4; i++){
ibz_mul(&prod,&(sum[i]), &(*coord)[i]);
if (i>0){
ibz_add(&(sum[0]),&(sum[0]), &prod);
} else {
ibz_copy(&sum[0],&prod);
}
}
ibz_copy(res,&sum[0]);
ibz_finalize(&prod);
quat_alg_coord_finalize(&sum);
}
//Defined in headerfile
//static void ibz_content(ibz_t *content, const quat_alg_coord_t *v) {
// ibz_gcd(content, v[0], v[1]);
// ibz_gcd(content, v[2], content);
// ibz_gcd(content, v[3], content);
//}

View File

@@ -0,0 +1,158 @@
#include <quaternion.h>
void quat_alg_init_set(quat_alg_t *alg, const ibz_t *p){
ibz_init(&(*alg).p);
ibz_mat_4x4_init(&(*alg).gram);
ibz_copy(&(*alg).p, p);
ibz_set(&(*alg).gram[0][0], 1);
ibz_set(&(*alg).gram[1][1], 1);
ibz_copy(&(*alg).gram[2][2], p);
ibz_copy(&(*alg).gram[3][3], p);
}
void quat_alg_finalize(quat_alg_t *alg){
ibz_finalize(&(*alg).p);
ibz_mat_4x4_finalize(&(*alg).gram);
}
void quat_alg_elem_init(quat_alg_elem_t *elem){
quat_alg_coord_init(&(*elem).coord);
ibz_init(&(*elem).denom);
ibz_set(&(*elem).denom, 1);
}
void quat_alg_elem_finalize(quat_alg_elem_t *elem){
quat_alg_coord_finalize(&(*elem).coord);
ibz_finalize(&(*elem).denom);
}
void quat_alg_coord_init(quat_alg_coord_t *coord){
for(int i = 0; i < 4; i++){
ibz_init(&(*coord)[i]);
}
}
void quat_alg_coord_finalize(quat_alg_coord_t *coord){
for(int i = 0; i < 4; i++){
ibz_finalize(&(*coord)[i]);
}
}
void ibz_vec_4_init(ibz_vec_4_t *vec){
for(int i = 0; i < 4; i++){
ibz_init(&(*vec)[i]);
}
}
void ibz_vec_4_finalize(ibz_vec_4_t *vec){
for(int i = 0; i < 4; i++){
ibz_finalize(&(*vec)[i]);
}
}
void ibz_vec_5_init(ibz_vec_5_t *vec){
for(int i = 0; i < 5; i++){
ibz_init(&(*vec)[i]);
}
}
void ibz_vec_5_finalize(ibz_vec_5_t *vec){
for(int i = 0; i < 5; i++){
ibz_finalize(&(*vec)[i]);
}
}
void ibz_mat_2x2_init(ibz_mat_2x2_t *mat){
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
ibz_init(&(*mat)[i][j]);
}
}
}
void ibz_mat_2x2_finalize(ibz_mat_2x2_t *mat){
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
ibz_finalize(&(*mat)[i][j]);
}
}
}
void ibz_mat_4x4_init(ibz_mat_4x4_t *mat){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_init(&(*mat)[i][j]);
}
}
}
void ibz_mat_4x4_finalize(ibz_mat_4x4_t *mat){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_finalize(&(*mat)[i][j]);
}
}
}
void ibz_mat_4x5_init(ibz_mat_4x5_t *mat){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 5; j++){
ibz_init(&(*mat)[i][j]);
}
}
}
void ibz_mat_4x5_finalize(ibz_mat_4x5_t *mat){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 5; j++){
ibz_finalize(&(*mat)[i][j]);
}
}
}
void ibz_mat_4x8_init(ibz_mat_4x8_t *mat){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 8; j++){
ibz_init(&(*mat)[i][j]);
}
}
}
void ibz_mat_4x8_finalize(ibz_mat_4x8_t *mat){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 8; j++){
ibz_finalize(&(*mat)[i][j]);
}
}
}
void ibz_mat_init(int rows, int cols, ibz_t mat[rows][cols]) {
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
ibz_init(&mat[i][j]);
}
void ibz_mat_finalize(int rows, int cols, ibz_t mat[rows][cols]) {
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
ibz_finalize(&mat[i][j]);
}
void quat_lattice_init(quat_lattice_t *lat){
ibz_mat_4x4_init(&(*lat).basis);
ibz_init(&(*lat).denom);
ibz_set(&(*lat).denom, 1);
}
void quat_lattice_finalize(quat_lattice_t *lat){
ibz_finalize(&(*lat).denom);
ibz_mat_4x4_finalize(&(*lat).basis);
}
void quat_order_init(quat_order_t *order){
quat_lattice_init(order);
}
void quat_order_finalize(quat_order_t *order){
quat_lattice_finalize(order);
}
void quat_left_ideal_init(quat_left_ideal_t *lideal){
quat_lattice_init(&(*lideal).lattice);
ibz_init(&(*lideal).norm);
(*lideal).parent_order=NULL;
}
void quat_left_ideal_finalize(quat_left_ideal_t *lideal){
ibz_finalize(&(*lideal).norm);
quat_lattice_finalize(&(*lideal).lattice);
}

View File

@@ -0,0 +1,371 @@
#include <quaternion.h>
#include <stdlib.h>
#include "internal.h"
void quat_lideal_create_principal(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg) {
ibz_mat_4x4_t mulmat;
ibq_t norm;
ibz_mat_4x4_init(&mulmat);
ibq_init(&norm);
// Multiply order basis on the right by x
quat_alg_rightmul_mat(&mulmat, x, alg);
ibz_mat_4x4_mul(&lideal->lattice.basis, &mulmat, &order->basis);
// Adjust denominators
ibz_mul(&lideal->lattice.denom, &x->denom, &order->denom);
quat_lattice_reduce_denom(&lideal->lattice, &lideal->lattice);
// Compute HNF
quat_lattice_hnf(&lideal->lattice);
// Compute norm and check it's integral
quat_alg_norm(&norm, x, alg);
assert(ibq_is_ibz(&norm));
ibq_to_ibz(&lideal->norm, &norm);
// Set order
lideal->parent_order = order;
ibq_finalize(&norm);
ibz_mat_4x4_finalize(&mulmat);
}
void quat_lideal_create_from_primitive(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg) {
quat_lattice_t ON;
quat_lattice_init(&ON);
// Compute ideal generated by x
quat_lideal_create_principal(lideal, x, order, alg);
// Compute norm
ibz_gcd(&lideal->norm, &lideal->norm, N);
// Compute ideal generated by N (without reducing denominator)
ibz_mat_4x4_scalar_mul(&ON.basis, N, &order->basis);
ibz_copy(&ON.denom, &order->denom);
// Add lattices (reduces denominators)
quat_lattice_add(&lideal->lattice, &lideal->lattice, &ON);
#ifndef NDEBUG
// verify norm
ibz_t tmp;
ibz_init(&tmp);
quat_lattice_index(&tmp,&(lideal->lattice),(lideal->parent_order));
int ok = ibz_sqrt(&tmp,&tmp);
assert(ok);
ok = (0==ibz_cmp(&tmp,&(lideal->norm)));
assert(ok);
ibz_finalize(&tmp);
#endif
// Set order
lideal->parent_order = order;
quat_lattice_finalize(&ON);
}
void quat_lideal_make_primitive_then_create(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg) {
quat_alg_elem_t prim;
ibz_t imprim, n1;
quat_alg_elem_init(&prim);
ibz_init(&imprim);
ibz_init(&n1);
// Store the primitive part of x in elem
quat_alg_make_primitive(&prim.coord, &imprim, x, order, alg);
ibz_mat_4x4_eval(&prim.coord, &order->basis, &prim.coord);
ibz_copy(&prim.denom, &order->denom);
// imprim = gcd(imprimitive part of x, N)
// n1 = N / imprim
ibz_gcd(&imprim, &imprim, N);
ibz_div(&n1, &imprim, N, &imprim);
// Generate the ideal (elem, n1)
quat_lideal_create_from_primitive(lideal, &prim, &n1, order, alg);
quat_alg_elem_finalize(&prim);
ibz_finalize(&imprim);
ibz_finalize(&n1);
}
int quat_lideal_random_2e(quat_left_ideal_t *lideal, const quat_order_t *order, const quat_alg_t *alg, int64_t e, unsigned char n) {
ibq_t norm;
ibz_t norm_num;
ibq_init(&norm);
ibz_init(&norm_num);
quat_alg_elem_t gen;
quat_alg_elem_init(&gen);
ibz_set(&gen.coord[0], 1);
// Start with the trivial left ideal of O
quat_lideal_create_principal(lideal, &gen, order, alg);
// Create the lattice 2·O
quat_lattice_t O2;
quat_lattice_init(&O2);
if (ibz_get(&order->denom) % 2 == 0) {
ibz_mat_4x4_copy(&O2.basis, &order->basis);
ibz_div_2exp(&O2.denom, &order->denom, 1);
} else {
ibz_mat_4x4_scalar_mul(&O2.basis, &ibz_const_two, &order->basis);
ibz_copy(&O2.denom, &order->denom);
}
for (int i = 0; i < e; ++i) {
// Take random gen ∈ lideal until one is found such that
// val₂(N(gen)) > i and gen ∉ 2·O
do {
if (!quat_lattice_random_elem(&gen, &lideal->lattice, n))
return 0;
quat_alg_norm(&norm, &gen, alg);
int ok = ibq_to_ibz(&norm_num, &norm);
assert(ok);
// N(gen)/N(I)
ibz_div_2exp(&norm_num, &norm_num, i);
}
// Check that 2-norm has increased, do not backtrack
while ((ibz_get(&norm_num) % 2 != 0) ||
quat_lattice_contains(NULL, &O2, &gen, alg));
// Redefine lideal as (gen, 2^(i+1))
ibz_mul(&norm_num, &lideal->norm, &ibz_const_two);
quat_lideal_create_from_primitive(lideal, &gen, &norm_num, order, alg);
assert(ibz_cmp(&lideal->norm, &norm_num) == 0);
}
quat_lattice_finalize(&O2);
ibq_finalize(&norm);
ibz_finalize(&norm_num);
quat_alg_elem_finalize(&gen);
return 1;
}
int quat_lideal_generator(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const quat_alg_t *alg, int bound) {
return(quat_lideal_generator_coprime(gen, lideal, &ibz_const_one, alg,bound));
}
int quat_lideal_generator_coprime(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const ibz_t *n, const quat_alg_t *alg, int bound) {
ibq_t norm;
ibz_t norm_int, norm_n,gcd, r, q, n2, gcd_ref;
ibz_vec_4_t vec;
ibz_vec_4_init(&vec);
ibq_init(&norm);
ibz_init(&norm_int);
ibz_init(&norm_n);
ibz_init(&gcd_ref);
ibz_init(&n2);
ibz_init(&r);
ibz_init(&q);
ibz_init(&gcd);
ibz_mul(&norm_n, &lideal->norm, n);
ibz_mul(&n2, n, n);
ibz_gcd(&gcd_ref,n,&(lideal->norm));
int a, b, c, d, int_norm;
int used_bound = QUATERNION_lideal_generator_search_bound;
if(bound != 0)
used_bound = bound;
assert(used_bound > 0);
int found = 0;
for (int_norm = 1; int_norm < used_bound; int_norm++){
for (a = -int_norm; a <= int_norm; a++){
for (b = -int_norm + abs(a); b <= int_norm - abs(a); b++){
for (c = -int_norm + abs(a) + abs(b); c <= int_norm - abs(a) - abs(b); c++){
d = int_norm - abs(a) - abs(b) - abs(c);
ibz_vec_4_set(&vec,a,b,c,d);
ibz_content(&gcd, &vec);
if(ibz_is_one(&gcd)){
ibz_mat_4x4_eval(&(gen->coord),&(lideal->lattice.basis),&vec);
ibz_copy(&(gen->denom),&(lideal->lattice.denom));
quat_alg_norm(&norm, gen, alg);
found = ibq_to_ibz(&norm_int, &norm);
if(found){
ibz_div(&q,&r,&norm_int,&(lideal->norm));
found = ibz_is_zero(&r);
if(found){
ibz_gcd(&gcd, &norm_n, &q);
found = (0==ibz_cmp(&gcd, &ibz_const_one));
if(found){
ibz_gcd(&gcd, &n2, &norm_int);
found = (0==ibz_cmp(&gcd, &gcd_ref));
}
}
}
if(found)
goto fin;
}
}
}
}
}
fin:;
ibz_finalize(&r);
ibz_finalize(&q);
ibq_finalize(&norm);
ibz_finalize(&norm_int);
ibz_finalize(&norm_n);
ibz_finalize(&gcd_ref);
ibz_finalize(&n2);
ibz_vec_4_finalize(&vec);
ibz_finalize(&gcd);
return(found);
}
int quat_lideal_mul(quat_left_ideal_t *product, const quat_left_ideal_t *lideal, const quat_alg_elem_t *alpha, const quat_alg_t *alg, int bound) {
ibq_t norm, norm_lideal;
ibz_t norm_int, num, denom;
quat_alg_elem_t gen;
ibq_init(&norm);
ibq_init(&norm_lideal);
ibz_init(&norm_int);
ibz_init(&num);
ibz_init(&denom);
quat_alg_elem_init(&gen);
quat_alg_norm(&norm, alpha, alg);
ibq_num(&num,&norm);
ibq_denom(&denom,&norm);
ibz_gcd(&denom,&denom,&num);
ibz_div(&norm_int,&denom,&num,&denom);
// Find a random generator gen of norm coprime to N(alpha)
// Warning: hardcoded the default constant, and this call can fail!
int found = quat_lideal_generator_coprime(&gen, lideal, &norm_int, alg,0);
if(found){
// Define the ideal (gen·α, N(lideal)·N(α))
quat_alg_mul(&gen, &gen, alpha, alg);
ibz_copy(&num,&(lideal->norm));
ibz_set(&denom,1);
ibq_set(&norm_lideal,&num,&denom);
ibq_mul(&norm, &norm, &norm_lideal);
int ok = ibq_to_ibz(&norm_int, &norm);
assert(ok);
quat_lideal_create_from_primitive(product, &gen, &norm_int, lideal->parent_order, alg);
}
ibq_finalize(&norm);
ibz_finalize(&norm_int);
ibq_finalize(&norm_lideal);
ibz_finalize(&num);
ibz_finalize(&denom);
quat_alg_elem_finalize(&gen);
return(found);
}
void quat_lideal_add(quat_left_ideal_t *sum, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg) {
assert(I1->parent_order == I2->parent_order);
quat_lattice_add(&sum->lattice, &I1->lattice, &I2->lattice);
quat_lattice_index(&sum->norm, &sum->lattice, I1->parent_order);
int ok = ibz_sqrt(&sum->norm, &sum->norm);
assert(ok);
sum->parent_order = I1->parent_order;
}
void quat_lideal_inter(quat_left_ideal_t *inter, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg) {
assert(I1->parent_order == I2->parent_order);
quat_lattice_intersect(&inter->lattice, &I1->lattice, &I2->lattice);
quat_lattice_index(&inter->norm, &inter->lattice, I1->parent_order);
int ok = ibz_sqrt(&inter->norm, &inter->norm);
assert(ok);
inter->parent_order = I1->parent_order;
}
int quat_lideal_equals(const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg) {
return I1->parent_order == I2->parent_order
&& ibz_cmp(&I1->norm, &I2->norm) == 0
&& quat_lattice_equal(&I1->lattice, &I2->lattice);
}
int quat_lideal_isom(quat_alg_elem_t *iso, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg){
// Only accept strict equality of parent orders
if (I1->parent_order != I2->parent_order)
return 0;
quat_lattice_t trans;
ibz_mat_4x4_t lll;
ibq_t norm, norm_bound;
quat_lattice_init(&trans);
ibz_mat_4x4_init(&lll);
ibq_init(&norm_bound); ibq_init(&norm);
quat_lattice_right_transporter(&trans, &I1->lattice, &I2->lattice, alg);
int err = quat_lattice_lll(&lll, &trans, &alg->p, 0);
assert(!err);
// The shortest vector found by lll is a candidate
for (int i = 0; i < 4; i++)
ibz_copy(&iso->coord[i], &lll[i][0]);
ibz_copy(&iso->denom, &trans.denom);
// bound on the smallest vector in transporter: N₂ / N₁
ibq_set(&norm_bound, &I2->norm, &I1->norm);
quat_alg_norm(&norm, iso, alg);
int isom = ibq_cmp(&norm, &norm_bound) <= 0;
quat_lattice_finalize(&trans);
ibz_mat_4x4_finalize(&lll);
ibq_finalize(&norm_bound); ibq_finalize(&norm);
return isom;
}
void quat_lideal_right_order(quat_order_t *order, const quat_left_ideal_t *lideal, const quat_alg_t *alg){
quat_lattice_right_transporter(order, &lideal->lattice, &lideal->lattice, alg);
}
void quat_lideal_reduce_basis(ibz_mat_4x4_t *reduced, ibz_mat_4x4_t *gram, const quat_left_ideal_t *lideal, const quat_alg_t *alg){
ibz_mat_4x4_t prod;
ibz_mat_4x4_init(&prod);
quat_lattice_lll(reduced,&(lideal ->lattice),&(alg->p),0);
ibz_mat_4x4_transpose(&prod,reduced);
ibz_mat_4x4_mul(&prod,&prod,&(alg ->gram));
ibz_mat_4x4_mul(gram,&prod,reduced);
ibz_mat_4x4_finalize(&prod);
}
/***************************** Function from quaternion_tools.c ***************************************/
void quat_connecting_ideal(quat_left_ideal_t *connecting_ideal, const quat_order_t *O1, const quat_order_t *O2, const quat_alg_t *alg){
quat_lattice_t inter;
ibz_t norm,one;
ibz_mat_4x4_t gens;
quat_alg_elem_t gen;
quat_left_ideal_t I[4];
ibz_init(&norm);
ibz_init(&one);
quat_lattice_init(&inter);
ibz_mat_4x4_init(&gens);
quat_alg_elem_init(&gen);
for (int i = 0; i < 4; i++) {
quat_left_ideal_init(I + i);
}
ibz_set(&one,1);
quat_lattice_intersect(&inter,O1,O2);
quat_lattice_index(&norm,&inter,O1);
ibz_mat_4x4_scalar_mul(&gens,&norm,&(O2->basis));
quat_alg_scalar(&gen,&norm,&one);
quat_lideal_create_principal(connecting_ideal, &gen, O1, alg);
for (int i = 0; i < 4; i++) {;
quat_alg_elem_copy_ibz(&gen,&(O2->denom),&(gens[0][i]),&(gens[1][i]),&(gens[2][i]),&(gens[3][i]));
quat_lideal_create_principal(I + i, &gen, O1, alg);
}
quat_lideal_add(connecting_ideal, connecting_ideal, I+0, alg);
quat_lideal_add(connecting_ideal, connecting_ideal, I+1, alg);
quat_lideal_add(connecting_ideal, connecting_ideal, I+2, alg);
quat_lideal_add(connecting_ideal, connecting_ideal, I+3, alg);
ibz_finalize(&norm);
ibz_finalize(&one);
quat_lattice_finalize(&inter);
ibz_mat_4x4_finalize(&gens);
quat_alg_elem_finalize(&gen);
for (int i = 0; i < 4; i++) {
quat_left_ideal_finalize(I + i);
}
}

View File

@@ -0,0 +1,876 @@
/** @file
*
* @authors Luca De Feo, Sina Schaeffler
*
* @brief Declarations for quaternion algebra operations
*/
#ifndef QUATERNION_H
#define QUATERNION_H
//#include <rng.h>
#include <intbig.h>
#include <assert.h>
#define QUATERNION_lideal_generator_search_bound 1024
/** @defgroup quat_quat Quaternion algebra
* @{
*/
/** @defgroup quat_vec_t Types for integer vectors and matrices
* @{
*/
/** @brief Type for vectors of 4 integers
*
* @typedef ibz_vec_4_t
*
* Represented as a vector of 4 ibz_t (big integer) elements
*/
typedef ibz_t ibz_vec_4_t[4];
/** @brief Type for vectors of 5 integers
*
* @typedef ibz_vec_5_t
*
* Represented as a vector of 5 ibz_t (big integer) elements
*/
typedef ibz_t ibz_vec_5_t[5];
/** @brief Type for 2 by 2 matrices of integers
*
* @typedef ibz_mat_2x2_t
*
* Represented as a matrix of 2 vectors of 2 ibz_t (big integer) elements
*/
typedef ibz_t ibz_mat_2x2_t[2][2];
/** @brief Type for 4 by 4 matrices of integers
*
* @typedef ibz_mat_4x4_t
*
* Represented as a matrix of 4 vectors of 4 ibz_t (big integer) elements
*/
typedef ibz_t ibz_mat_4x4_t[4][4];
/** @brief Type for 4 by 5 matrices of integers
*
* @typedef ibz_mat_4x5_t
*
* Represented as a matrix of 4 vectors of 5 ibz_t (big integer) elements
*/
typedef ibz_t ibz_mat_4x5_t[4][5];
/** @brief Type for 4 by 8 matrices of integers
*
* @typedef ibz_mat_4x8_t
*
* Consists in and 4 by 8 matrix of ibz_t (big integers)
*/
typedef ibz_t ibz_mat_4x8_t[4][8];
/**
* @}
*/
/** @defgroup quat_quat_t Types for quaternion algebras
* @{
*/
/** @brief Type for quaternion algebras
*
* @typedef quat_alg_t
*
* @struct quat_alg
*
* The quaternion algebra ramified at p = 3 mod 4 and ∞.
*/
typedef struct quat_alg {
ibz_t p; ///< Prime number, must be = 3 mod 4.
ibz_mat_4x4_t gram; ///< Gram matrix of the norm form
} quat_alg_t;
/** @brief Type for integer coordinates in a basis
*
* @typedef quat_alg_coord_t
*
* Represented as a vector of 4 ibz_t (big integer) elements
*/
typedef ibz_vec_4_t quat_alg_coord_t;
/** @brief Type for quaternion algebra elements
*
* @typedef quat_alg_elem_t
*
* @struct quat_alg_elem
*
* Represented as a array *coord* of 4 ibz_t integers and a common ibz_t denominator *denom*.
*
* The representation is not necessarily normalized, that is, gcd(denom, content(coord)) might not be 1.
* For getting a normalized representation, use the quat_alg_normalize function
*
* The elements are always represented in basis (1,i,j,ij) of the quaternion algebra, with i^2=-1 and j^2 = -p
*/
typedef struct quat_alg_elem {
ibz_t denom; ///< Denominator by which all coordinates are divided (big integer, must not be 0)
quat_alg_coord_t coord; ///< Numerators of the 4 coordinates of the quaternion algebra element in basis (1,i,j,ij)
} quat_alg_elem_t;
/** @brief Type for lattices in dimension 4
*
* @typedef quat_lattice_t
*
* @struct quat_lattice
*
* Represented as a rational (`frac`) times an integreal lattice (`basis`)
*
* The basis is in hermite normal form, and its columns divided by its denominator are elements of the quaternion algebra, represented in basis (1,i,j,ij) where i^2 = -1, j^2 = -p.
*
* All lattices must have full rank (4)
*/
typedef struct quat_lattice {
ibz_t denom; ///< Denominator by which the basis is divided (big integer, must not be 0)
ibz_mat_4x4_t basis; ///< Integer basis of the lattice in hermite normal form (its columns divided by denom are algebra elements in the usual basis)
} quat_lattice_t;
/** @brief Type for quaternion orders
*
* @typedef quat_order_t
*
* Internally represented as a quat_lattice_t.
*
* That means that the basis is in hermite normal form, and its columns divided by its denominator are elements of the quaternion algebra, represented in basis (1,i,j,ij) where i^2 = -1, j^2 = -p.
*/
typedef quat_lattice_t quat_order_t;
/** @brief Type for left ideals in quaternion algebras
*
* @typedef quat_left_ideal_t
*
* @struct quat_left_ideal
*
* The basis of the lattice representing it is in hermite normal form, and its columns divided by its denominator are elements of the quaternion algebra, represented in basis (1,i,j,ij) where i^2 = -1, j^2 = -p.
*/
typedef struct quat_left_ideal {
quat_lattice_t lattice; ///< lattice representing the ideal
ibz_t norm; ///< norm of the lattice
const quat_order_t* parent_order; ///< should be a maximal ideal
} quat_left_ideal_t;
/** @}
*/
/** @brief Type for extremal maximal orders
*
* @typedef quat_p_extremal_maximal_order_t
*
* @struct quat_p_extremal_maximal_order
*
* The basis of the order representing it is in hermite normal form, and its columns divid
ed by its denominator are elements of the quaternion algebra, represented in basis (1,i,j,
ij) where i^2 = -q, j^2 = -p.
*/
typedef struct quat_p_extremal_maximal_order {
quat_order_t order; ///< the order represented as a lattice
quat_alg_elem_t i; ///< the element of small discriminant
quat_alg_elem_t j; ///< the element of norm p orthogonal to i
int64_t q; ///< the absolute value of sqrt of i
} quat_p_extremal_maximal_order_t;
/*************************** Functions *****************************/
/** @defgroup quat_c Constructors and Destructors
* @{
*/
void quat_alg_init_set(quat_alg_t *alg, const ibz_t *p);
void quat_alg_finalize(quat_alg_t *alg);
void quat_alg_elem_init(quat_alg_elem_t *elem);
void quat_alg_elem_finalize(quat_alg_elem_t *elem);
void quat_alg_coord_init(quat_alg_coord_t *coord);
void quat_alg_coord_finalize(quat_alg_coord_t *coord);
void ibz_vec_4_init(ibz_vec_4_t *vec);
void ibz_vec_4_finalize(ibz_vec_4_t *vec);
void ibz_vec_5_init(ibz_vec_5_t *vec);
void ibz_vec_5_finalize(ibz_vec_5_t *vec);
void ibz_mat_2x2_init(ibz_mat_2x2_t *mat);
void ibz_mat_2x2_finalize(ibz_mat_2x2_t *mat);
void ibz_mat_4x4_init(ibz_mat_4x4_t *mat);
void ibz_mat_4x4_finalize(ibz_mat_4x4_t *mat);
void ibz_mat_4x5_init(ibz_mat_4x5_t *mat);
void ibz_mat_4x5_finalize(ibz_mat_4x5_t *mat);
void ibz_mat_4x8_init(ibz_mat_4x8_t *mat);
void ibz_mat_4x8_finalize(ibz_mat_4x8_t *mat);
/** @brief initiazlize matrix with arbitrary dimensions
*/
void ibz_mat_init(int rows, int cols, ibz_t mat[rows][cols]);
/** @brief finalize matrix with arbitrary dimensions
*/
void ibz_mat_finalize(int rows, int cols, ibz_t mat[rows][cols]);
void quat_lattice_init(quat_lattice_t *lat);
void quat_lattice_finalize(quat_lattice_t *lat);
void quat_order_init(quat_order_t *order);
void quat_order_finalize(quat_order_t *order);
void quat_left_ideal_init(quat_left_ideal_t *lideal);
void quat_left_ideal_finalize(quat_left_ideal_t *lideal);
/** @}
*/
/** @defgroup quat_printers Print functions for types from the quaternion module
* @{
*/
void ibz_mat_2x2_print(const ibz_mat_2x2_t *mat);
void ibz_mat_4x4_print(const ibz_mat_4x4_t *mat);
void ibz_mat_4x5_print(const ibz_mat_4x5_t *mat);
void ibz_mat_4x8_print(const ibz_mat_4x8_t *mat);
void ibz_mat_print(int rows, int cols, const ibz_t mat[rows][cols]);
void ibz_vec_2_print(const ibz_vec_2_t *vec);
void ibz_vec_4_print(const ibz_vec_4_t *vec);
void ibz_vec_5_print(const ibz_vec_5_t *vec);
void quat_lattice_print(const quat_lattice_t *lat);
void quat_alg_print(const quat_alg_t *alg);
void quat_alg_elem_print(const quat_alg_elem_t *elem);
void quat_alg_coord_print(const quat_alg_coord_t *coord);
void quat_order_print(const quat_order_t *order);
void quat_left_ideal_print(const quat_left_ideal_t *lideal);
/** @}
*/
/** @defgroup quat_int Integer functions for quaternion algebra
* @{
*/
/** @defgroup quat_int_mat Integer matrix and vector functions
* @{
*/
/** @brief mat*vec in dimension 2 for integers
*
* @param res Output vector
* @param mat Input vector
* @param vec Input vector
*/
void ibz_mat_2x2_eval(ibz_vec_2_t *res, const ibz_mat_2x2_t *mat, const ibz_vec_2_t *vec);
/**
* @brief a*b for 2x2 integer matrices modulo m
*
* @param prod Output matrix
* @param mat_a Input matrix
* @param mat_b Input matrix
* @param m Integer modulo
*/
void ibz_2x2_mul_mod(ibz_mat_2x2_t *prod, const ibz_mat_2x2_t *mat_a, const ibz_mat_2x2_t *mat_b, const ibz_t *m);
/**
* @brief Inverse of 2x2 integer matrices modulo m
*
* @param inv Output matrix
* @param mat Input matrix
* @param m Integer modulo
* @return 1 if inverse exists 0 otherwise
*/
int ibz_2x2_inv_mod(ibz_mat_2x2_t *inv, const ibz_mat_2x2_t *mat, const ibz_t *m);
/**
* @brief mat*vec
*
*
* @param res Output: coordinate vector
* @param mat Integer 4x4 matrix
* @param vec Integer vector (coordinate vector)
*
* Multiplies 4x4 integer matrix mat by a 4-integers vector vec
*/
void ibz_mat_4x4_eval(ibz_vec_4_t *res, const ibz_mat_4x4_t *mat, const ibz_vec_4_t *vec);
/**
* @brief Computes modulo p the left kernel of mat where p is prime
*
* Algorithm used is the one at number 2.3.1 in Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
*
* @param ker Output: a vector in kernel which is not 0 modulo p if exists, otherwise 0
* @param mat A 4×5 matrix
* @param p Integer modulo which the kernel computation is done, must be prime
* @return 1 if a unique such vector was found, 0 otherwise
*
*/
int ibz_4x5_right_ker_mod_prime(ibz_vec_5_t *ker, const ibz_mat_4x5_t *mat, const ibz_t *p);
/**
* @brief Computes modulo p the left kernel of mat where p is prime
*
* Algorithm used is the one at number 2.3.1 in Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
*
* @param ker Output: a vector in kernel which is not 0 modulo p if exists, otherwise 0
* @param mat A 4×4 matrix
* @param p Integer modulo which the kernel computation is done, must be prime
* @return 1 if a unique such vector was found, 0 otherwise
*
*/
int ibz_4x4_right_ker_mod_prime(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, const ibz_t *p);
/**
* @brief Computes the right kernel of mat modulo 2^exp
*
* @param ker Output: a vector in kernel which is not 0 modulo 2, if it exists, otherwise 0
* @param mat A 4×4 matrix
* @param exp exponent defining the modulus
* @return 1 if a unique such vector was found, 0 otherwise
*
*/
int ibz_4x4_right_ker_mod_power_of_2(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, unsigned short exp);
/**
* @brief content of a 4-vector of integers
*
* The content is the GCD of all entries.
*
* @param v A 4-vector of integers
* @param content Output: the resulting gcd
*/
static inline void ibz_content(ibz_t *content, const ibz_vec_4_t *v) {
ibz_gcd(content, &((*v)[0]), &((*v)[1]));
ibz_gcd(content, &((*v)[2]), content);
ibz_gcd(content, &((*v)[3]), content);
}
/** @}
*/
/** @defgroup quat_integer Integer functions for quaternion algebra
* @{
*/
/**
* @brief Find integers x and y such that x^2 + n*y^2 = p
*
* Uses Cornacchia's algorithm, should be used only for prime p
*
* @param x Output
* @param y Output
* @param n first parameter defining the equation
* @param p seond parameter defining the equation, must be prime
* @return 1 if success, 0 otherwise
*/
int ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n , const ibz_t *p);
/**
* @brief Solving cornacchia to find x² + n y³ = 2^exp_adjust * p in the special case of n=3 mod 4
*
* This function sometimes fails to find a solution even if one exists and all parameters are as specified.
*
* @param x Output: an integer
* @param y Output: an integer
* @param n an integer
* @param p a prime integer
* @param exp_adjust an exponent, must be > 0
* @returns 1 if success, 0 if failure to find a solution
*/
int ibz_cornacchia_special_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p, const int exp_adjust);
/**
* @brief Find x and y such that x^2 + y^2 = n
*
* Uses "extended" version of Cornacchia's algorithm which also allows to solve x^2 + y^2 = n for some composite numbers.
* This uses a prime factor decomposition of n via trial division for primes in the list, computes solutions for n's prime factors
* and then uses complex multiplication. Since (x+iz)(x-iy) = x^2 + y^2,
* so a solution xp,yp for p and xq,yq for q give a solution for pq by computing (xp+iyp)*(xq+iyq).
*
* @param x Output
* @param y Output
* @param n parameter defining the equation. To get an output if one exists, only 1 of its prime factors can exceed the largest prime in prime_list
* @param prime_list list of consecutive primes starting from 2
* @param prime_list_length number of elements of prime_list. Cans be smaller than that number, in which case only the beginning of the list is used.
* @param primality_test_iterations number of Miller-Rabin iterations to verify primality before trying to compute a square root of 1 modulo the remaining number once all small primes were factored out
* @param bad_primes_prod Assumed to be a product of small primes which are 3 mod 4. Used only to accelerate failure in case its gcd with n is not 1. Can be NULL
* @return 1 if success, 0 otherwise
*/
int ibz_cornacchia_extended(ibz_t *x, ibz_t *y, const ibz_t *n, const short *prime_list, const int prime_list_length, short primality_test_iterations, const ibz_t *bad_primes_prod);
/** @}
*/
/** @defgroup quat_qf Quadratic form functions
* @{
*/
/**
* @brief Quadratic form evaluation
*
* qf and coord must be represented in the same basis.
*
* @param res Output: coordinate vector
* @param qf Quadratic form (4x4 integer matrix)
* @param coord Integer vector (coordinate vector)
*/
void quat_qf_eval(ibz_t *res, const ibz_mat_4x4_t *qf, const quat_alg_coord_t *coord);
/** @}
*/
/** @defgroup quat_lat_2x2 Functions for lattices in dimension 2
* @{
*/
/**
* @brief Find a lattice vector close to a target vector satisfying extra conditions.
*
* Given a target vector `target` = (x₀,y₀), enumerate vectors `v` = (x,y) in lattice `lat` with distance
* (x₀ - x)² + `qf` (y₀ - y)² < 2^`dist_bound`. On each vector `condition(res, target - v, params)` is called: if it returns a
* non-zero value, processing stops and the same value is returned; if it returns 0 processing continues
* until `max_tries` vectors have been tried.
*
* The implementation will first reduce the basis by finding a short vector with algorithm 1.3.14 (Gauss) from Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993.
* Then a second one is added to this basis after reduction by projection on the first one.
* A close vector is found using 2 projection as in https://cims.nyu.edu/~regev/teaching/lattices_fall_2004/ln/cvp.pdf (15.5.2023,16h15CEST) using the already reduced matrix
* Finally, short vectors are enumerated below an enumeration bound set to (2 ^ dist_bound - (distance of target to close vector)).
* Each short vector is added to the close vector, and then the distance to the target is measured. If it is below 2^dist_bound, it is tested for condition.
* If coundition returns 1, the algorithm terminates and returns 1, otherwise it returns 0 when max_tries vectors have been tried
*
* The enumeration bound is an heuristic to avoid starting the search with vectors which will be too far from the target, since enumeration starts at the largest vectors.
* It can in some cases lead to failures even though solutions do exist, an in other cases be insufficient to get a valid result in reasonable time.
*
* Enumeration uses algorithm 2.7.5 (Fincke-Pohst) from Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
* Slightly adapted to work without rational numbers and their square roots
* Therefore needing a test to make sure the bounds are respected, which is integrated in quat_dim2_lattice_bound_and_condition
*
* @param res Output: quaternion element returned by `condition`
* @param lat_basis basis of an integral 2-dimensional lattice
* @param target target vector for CVP
* @param qf Small integer defining a quadratic form x² + qf y²
* @param dist_bound Log of the maximum distance between `target` and the enumerated vectors
* @param condition Filter the vectors of `lat` by passing them to `condition`. When it returns a non-zero value, the result is put into `res` and the processing stops.
* @param params Extra parameters passed to `condition`. May be NULL.
* @param max_tries Try at most `max_tries` vectors close to `target`
* @return 1 if an element was found, 0 otherwise
*/
int quat_2x2_lattice_enumerate_cvp_filter(quat_alg_elem_t *res,
const ibz_mat_2x2_t *lat_basis, const ibz_vec_2_t *target,
unsigned int qf, unsigned int dist_bound,
int (*condition)(quat_alg_elem_t* , const ibz_vec_2_t*, const void*), const void* params,
unsigned int max_tries);
/** @}
*/
/** @}
*/
/** @defgroup quat_quat_f Quaternion algebra functions
* @{
*/
void quat_alg_add(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b);
void quat_alg_sub(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b);
void quat_alg_mul(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b, const quat_alg_t *alg);
/** @brief reduced norm of alg_elem x
*
* @param res Output: rational which will contain the reduced norm of a
* @param x Algebra element whose norm is computed
* @param alg The quaternion algebra
*/
void quat_alg_norm(ibq_t *res, const quat_alg_elem_t *x, const quat_alg_t *alg);
/** @brief reduced trace of alg_elem x
*
* @param res Output: rational which will contain the reduced trace of a
* @param x Algebra element whose trace will be computed
*/
void quat_alg_trace(ibq_t *res, const quat_alg_elem_t *x);
/** @brief Normalize representation of alg_elem x
*
* @param x Algebra element whose representation will be normalized
*
* Modification of x.
* Sets coord and denom of x so that gcd(denom, content(coord))=1
* without changing the value of x = (coord0/denom, coord1/denom, coord2/denom, coord3/denom).
*/
void quat_alg_normalize(quat_alg_elem_t *x);
/** @brief Test if x is 0
*
* @returns 1 if x=0, 0 otherwise
*
* x is 0 iff all coordinates in x->coord are 0
*/
int quat_alg_elem_is_zero(const quat_alg_elem_t *x);
/** @brief Test if x is 0
*
* @returns 1 if x=0, 0 otherwise
*
* x is 0 iff all coordinates in x are 0
*/
int quat_alg_coord_is_zero(const quat_alg_coord_t *x);
// end quat_quat_f
/** @}
*/
/** @defgroup quat_lat_f Lattice functions
* @{
*/
void quat_lattice_add(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2);
void quat_lattice_intersect(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2);
void quat_lattice_mul(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2, const quat_alg_t *alg);
/**
* @brief Modifies a lattice to put it in hermite normal form
*
* In-place modification of the lattice.
*
* @param lat input lattice
*
* On a correct lattice this function changes nothing (since it is already in HNF), but it can be used to put a handmade one in correct form in order to use the other lattice functions.
*/
void quat_lattice_hnf(quat_lattice_t *lat);
/**
* @brief Test whether x ∈ lat. If so, compute its coordinates in lat's basis.
*
* @param coord Output: Set to the coordinates of x in lat. May be NULL.
* @param lat The lattice
* @param x An element of the quaternion algebra
* @param alg The quaternion algebra
* @return true if x ∈ lat
*/
int quat_lattice_contains(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x, const quat_alg_t *alg);
/********************************* Functions from ideal.c ************************************/
/** @addtogroup quat_quat_f
* @{
*/
/**
* @brief Creates algebra element from scalar
*
* Resulting element has 1-coordinate equal to numerator/denominator
*
* @param elem Output: algebra element with numerator/denominator as first coordiante (1-coordinate), 0 elsewhere (i,j,ij coordinates)
* @param numerator
* @param denominator Assumed non zero
*/
void quat_alg_scalar(quat_alg_elem_t *elem, const ibz_t *numerator, const ibz_t *denominator);
/**
* @brief Standard involution in a quaternion algebra
*
* @param conj Output: image of x by standard involution of the quaternion algebra alg
* @param x element of alg whose image is searched
*/
void quat_alg_conj(quat_alg_elem_t *conj, const quat_alg_elem_t *x);
/**
* @brief Given `x` ∈ `order`, factor it into its primitive and impritive parts
*
* Given `x` ∈ `order`, return a coordinate vector `primitive_x` and an integer `content`
* such that `x` = `content` · Λ `primitive_x`, where Λ is the basis of `order`
* and `x` / `content` is primitive in `order`.
*
* @param primitive_x Output: coordinates of a primitive element of `order` (in `order`'s basis)
* @param content Output: content of `x`'s coordinate vector in order's basis
* @param alg the quaternion algebra
* @param order order of `alg`
* @param x element of order, must be in `order`
*/
static inline void quat_alg_make_primitive(quat_alg_coord_t *primitive_x, ibz_t *content, const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg) {
int ok = quat_lattice_contains(primitive_x, order, x, alg);
assert(ok);
ibz_content(content, primitive_x);
ibz_t r;
ibz_init(&r);
for(int i = 0; i < 4; i++) {
ibz_div(*primitive_x + i, &r, *primitive_x + i, content);
}
ibz_finalize(&r);
}
/**
* @brief Tests if x ∈ order is primitive
*
* @param alg quaternion algebra
* @param order quaternion order
* @param x element of the order (fails if x ∉ order)
*
* @returns 1 if x is primitive, 0 otherwise
*/
static inline int quat_alg_is_primitive(const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg) {
quat_alg_coord_t coord;
ibz_t cnt;
quat_alg_coord_init(&coord);
ibz_init(&cnt);
quat_alg_make_primitive(&coord,&cnt,x,order,alg);
int ok = ibz_is_one(&cnt);
ibz_finalize(&cnt);
quat_alg_coord_finalize(&coord);
return ok;
}
//end quat_quat_f
/** @}
*/
/** @defgroup quat_lideal_f Functions for left ideals
* @{
*/
/** @defgroup quat_lideal_c Creating left ideals
* @{
*/
/**
* @brief Left principal ideal of order, generated by x
*
* @param lideal Output: left ideal
* @param alg quaternion algebra
* @param order maximal order of alg whose left ideal is searched
* @param x generating element
*
* Creates the left ideal in 'order' generated by the element 'x'
*/
void quat_lideal_create_principal(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg);
/**
* @brief Left ideal of order, generated by primitive x and N
*
* @param lideal Output: left ideal
* @param alg quaternion algebra
* @param order maximal order of alg whose left ideal is searched
* @param x generating element, must be a primitive element of order
* @param N generating integer
*
* Creates the left ideal in order generated by the element x and the integer N
*
*/
void quat_lideal_create_from_primitive(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg);
/**
* @brief Make x primitive, remove content from N, then generate ideal
*
* @param lideal Output: left ideal
* @param alg quaternion algebra
* @param order maximal order of alg whose left ideal is searched
* @param x generating element, a primitive element of order obtained from it will be used for generation
* @param N generating integer
*
* Given `x` = n·y ∈ order` with `y` primitive, given an integer `N`, create the ideal
* generated by `y` and `N / gcd(n, N)`.
*
*/
void quat_lideal_make_primitive_then_create(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg);
/**
* @brief Random left ideal of norm 2^e
*
* @param lideal Output: random left ideal (of parents alg and order) with norm 2^e
* @param alg parent algebra for which an ideal is chosen. Is a quaterion algebra
* @param order parent order for which an ideal is chosen. Must be maximal
* @param e int64_t determining the norm of the resulting ideal
* @param n Parameter controlling the amount of randomness used by the algorithm
* @return 0 if PRNG failed, 1 otherwise.
*/
int quat_lideal_random_2e(quat_left_ideal_t *lideal, const quat_order_t *order, const quat_alg_t *alg, int64_t e, unsigned char n);
/** @}
*/
/** @defgroup quat_lideal_gen Generators of left ideals
* @{
*/
/**
* @brief Generator of 'lideal'
*
* @returns 1 if such a generator was found, 0 otherwise
* @param gen Output: non scalar generator of lideal
* @param lideal left ideal
* @param alg the quaternion algebra
* @param bound Bound used for enumeration of elements. Must be non-negative. If 0, a default value is used.
*
* Ideal is generated by gen and the ideal's norm
*
* Bound has as default value QUATERNION_lideal_generator_search_bound
*/
int quat_lideal_generator(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const quat_alg_t *alg, int bound);
/**
* @brief Primitive generator of a left ideal of norm coprime to a given integer
*
* @returns 1 if such a generator was found, 0 otherwise
* @param gen Output: generator of lideal, coprime to n
* @param lideal left ideal
* @param n value to which the generator norm should be coprime. (assumes gcd(n, norm(lideal)) = 1). If this is not fulfilled, the returned element is a generator such that gcd(n^2,norm(gen)) = gcd(n,norm(lideal))
* @param alg the quaternion algebra
* @param bound Bound used for enumeration of elements. Must be non-negative. If 0, a default value 0 is used.
*
* Finds a primitive generator of 'lideal' of norm coprime with n (assumes gcd(n, norm(lideal)) = 1)
*
* gen is such that lideal is generated by gen and the ideal's norm
*
* Bound has as default value QUATERNION_lideal_generator_search_bound
*/
int quat_lideal_generator_coprime(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const ibz_t *n, const quat_alg_t *alg, int bound);
/** @}
*/
/** @defgroup quat_lideal_op Operations on left ideals
* @{
*/
/**
* @brief Left ideal product of left ideal I and element alpha
*
* @returns 1 if a product could be computed, 0 otherwise (in the later case, one might retry with a higher bound)
* @param product Output: lideal I*alpha, must have integer norm
* @param lideal left ideal
* @param alpha element multiplied to lideal to get the product ideal
* @param alg the quaternion algebra
* @param bound used for internal ideal_generator search. Must be positive. Setting it to 0 uses the default value.
*
* I*alpha where I is a left-ideal and alpha an element of the algebra
*
* The resulting ideal must have an integer norm
*
* Bound has as default value QUATERNION_lideal_generator_search_bound
*/
int quat_lideal_mul(quat_left_ideal_t *product, const quat_left_ideal_t *lideal, const quat_alg_elem_t *alpha, const quat_alg_t *alg, int bound);
/**
* @brief Sum of two left ideals
*
* @param sum Output: Left ideal which is the sum of the 2 inputs
* @param lideal1 left ideal
* @param lideal2 left ideal
* @param alg the quaternion algebra
*/
void quat_lideal_add(quat_left_ideal_t *sum, const quat_left_ideal_t *lideal1, const quat_left_ideal_t *lideal2, const quat_alg_t *alg);
/**
* @brief Intersection of two left ideals
*
* @param intersection Output: Left ideal which is the intersection of the 2 inputs
* @param lideal1 left ideal
* @param lideal2 left ideal
* @param alg the quaternion algebra
*/
void quat_lideal_inter(quat_left_ideal_t *intersection, const quat_left_ideal_t *lideal1, const quat_left_ideal_t *lideal2, const quat_alg_t *alg);
/**
* @brief Equality test for left ideals
*
* @returns 1 if both left ideals are equal, 0 otherwise
* @param lideal1 left ideal
* @param lideal2 left ideal
* @param alg the quaternion algebra
*/
int quat_lideal_equals(const quat_left_ideal_t *lideal1, const quat_left_ideal_t *lideal2, const quat_alg_t *alg);
/**
* @brief Element representing an isomorphism between I1 and I2
*
* @returns 1 if isomorphic, 0 otherwise
* @param iso Output: algebra element such that I1*iso = I2.
* @param lideal1 left ideal
* @param lideal2 left ideal
* @param alg the quaternion algebra
*
* If I1 and I2 are isomorphic, set `iso` to an element of `alg` such that I1*iso = I2,
* and return 1. Otherwise return 0.
*
* I1 and I2 must have the same parent order (where "same" means they
* point to the same object), in order to be considered isomorphic.
*/
int quat_lideal_isom(quat_alg_elem_t *iso, const quat_left_ideal_t *lideal1, const quat_left_ideal_t *lideal2, const quat_alg_t *alg);
/**
* @brief Right order of a left ideal
*
* @param order Output: right order of the given ideal
* @param lideal left ideal
* @param alg the quaternion algebra
*/
void quat_lideal_right_order(quat_order_t *order, const quat_left_ideal_t *lideal, const quat_alg_t *alg);
/**
* @brief LLL-reduce the basis of the left ideal, without considering its denominator
*
* This function reduce the basis of the lattice of the ideal, but it does completely ignore its denominator.
* So the outputs of this function must still e divided by the appropriate power of lideal.lattice.denom.
*
* @param reduced Output: Lattice defining the ideal, which has its basis in a lll-reduced form. Must be divided by lideal.lattice.denom before usage
* @param gram Output: gram matrix of the reduced basis. Must be divided by (lideal.lattice.denom)^2 before usage
* @param lideal Ideal whose basis will be reduced
* @param alg the quaternion algebra
*/
void quat_lideal_reduce_basis(ibz_mat_4x4_t *reduced, ibz_mat_4x4_t *gram, const quat_left_ideal_t *lideal, const quat_alg_t *alg); //replaces lideal_lll
/** @}
*/
// end quat_lideal_f
/** @}
*/
/***************************** Functions from quaternion_tools.c ***************************************/
/** @defgroup quat_order_op Operations on orders
* @{
*/
/**
* @brief Connecting ideal of 2 orders in the same algebra
*
* @param connecting_ideal Output: ideal which is a right ideal of O1 and a left ideal of O2
* @param alg quaternion algebra
* @param order1 maximal order left of the searched ideal
* @param order2 order right of the searched ideal
*/
void quat_connecting_ideal(quat_left_ideal_t *connecting_ideal, const quat_order_t *order1, const quat_order_t *order2, const quat_alg_t *alg);
/**
* @brief LLL reduction on 4-dimensional lattice, coefficient is 0.99
*
* @param red Output: LLL reduced basis
* @param lattice lattice with 4-dimensional basis
* @param q parameter q for non-standard basis
* @param precision floating-point precision for LLL algorithm, if 0 is provided a default precision is taken
*/
int quat_lattice_lll(ibz_mat_4x4_t *red, const quat_lattice_t *lattice, const ibz_t *q, int precision);
/** @}
*/
// end quat_quat
/** @}
*/
#endif

View File

@@ -0,0 +1,319 @@
#include <quaternion.h>
#include "internal.h"
#include <stdlib.h>
//Small helper for integers
void ibz_rounded_div(ibz_t *q, const ibz_t *a, const ibz_t *b){
ibz_t r,sign_q, abs_b;
ibz_init(&r);
ibz_init(&sign_q);
ibz_init(&abs_b);
//assumed to round towards 0
ibz_abs(&abs_b,b);
// q is of same sign as a*b (and 0 if a is 0)
ibz_mul(&sign_q,a,b);
ibz_div(q,&r,a,b);
ibz_abs(&r,&r);
ibz_add(&r,&r,&r);
if(ibz_cmp(&r,&abs_b)>0){
ibz_set(&r,0);
if(ibz_cmp(&sign_q,&r)<0){
ibz_set(&sign_q,-1);
} else {
ibz_set(&sign_q,1);
}
ibz_add(q,q,&sign_q);
}
ibz_finalize(&r);
ibz_finalize(&sign_q);
ibz_finalize(&abs_b);
}
// this function assumes that there is a sqrt of -1 mod p and p is prime
//algorithm read at http://www.lix.polytechnique.fr/Labo/Francois.Morain/Articles/cornac.pdf, on 2nd of may 2023, 14h45 CEST
int ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p){
int res = 1;
ibz_t two, r0, r1, r2, a, x0, prod;
ibz_init(&r0);
ibz_init(&r1);
ibz_init(&r2);
ibz_init(&a);
ibz_init(&prod);
ibz_init(&two);
// manage case p = 2 separately
ibz_set(&two,2);
int test = ibz_cmp(p,&two);
if(test==0){
if (ibz_is_one(n)){
ibz_set(x,1);
ibz_set(y,1);
res = 1;
} else {
res = 0;
}
}
//test coprimality (should always be ok in our cases)
ibz_gcd(&r2,p,n);
if (ibz_is_one(&r2) && (test != 0)){
// get sqrt of -n mod p
ibz_set(&r2,0);
ibz_sub(&r2,&r2,n);
res = res && ibz_sqrt_mod_p(&r2,&r2,p);
if (res){
// run loop
ibz_copy(&prod,p);
ibz_copy(&r1,p);
while(ibz_cmp(&prod,p)>=0){
ibz_div(&a,&r0,&r2,&r1);
ibz_mul(&prod,&r0,&r0);
ibz_copy(&r2,&r1);
ibz_copy(&r1,&r0);
}
// test if result is solution
ibz_sub(&a,p,&prod);
ibz_div(&a,&r2,&a,n);
res = res && (ibz_is_zero(&r2));
res = res && ibz_sqrt(y,&a);
if (res){
ibz_copy(x,&r0);
ibz_mul(&a,y,y);
ibz_mul(&a,&a,n);
ibz_add(&prod,&prod,&a);
res = res && (0==ibz_cmp(&prod,p));
}
}
}
ibz_finalize(&r0);
ibz_finalize(&r1);
ibz_finalize(&r2);
ibz_finalize(&a);
ibz_finalize(&prod);
ibz_finalize(&two);
return(res);
}
int ibz_cornacchia_special_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p, const int exp_adjust){
int res = 1;
ibz_t two, r0, r1, r2, a, x0, prod;
ibz_t p4;
ibz_t test_square;
ibz_init(&test_square);
ibz_init(&r0);
ibz_init(&r1);
ibz_init(&r2);
ibz_init(&a);
ibz_init(&prod);
ibz_init(&two);
ibz_init(&p4);
ibz_set(&two,2);
ibz_pow(&p4,&two,exp_adjust);
ibz_mul(&p4,p,&p4);
assert(exp_adjust>0);
#ifndef NDEBUG
ibz_set(&r0,3);
ibz_set(&r1,4);
ibz_mod(&r2,n,&r1);
assert((ibz_cmp(&r2,&r0)==0));
ibz_set(&r0,0);
ibz_set(&r1,0);
ibz_set(&r2,0);
#endif
// manage case p = 2 separately
int test = ibz_cmp(p,&two);
if(test==0){
if (ibz_is_one(n)){
ibz_set(x,1);
ibz_set(y,1);
res = 1;
} else {
res = 0;
}
}
//test coprimality (should always be ok in our cases)
ibz_gcd(&r2,p,n);
if (ibz_is_one(&r2) && (test != 0)){
// get sqrt of -n mod p
ibz_set(&r2,0);
ibz_sub(&r2,&r2,n);
res = res && ibz_sqrt_mod_p(&r2,&r2,p);
res = res && (ibz_get(&r2)%2 != 0);
ibz_mul(&test_square,&r2,&r2);
ibz_add(&test_square,&test_square,n);
ibz_mod(&test_square,&test_square,&p4);
res = res && ibz_is_zero(&test_square);
if (res){
// run loop
ibz_copy(&prod,&p4);
ibz_copy(&r1,&p4);
while(ibz_cmp(&prod,&p4)>=0){
ibz_div(&a,&r0,&r2,&r1);
ibz_mul(&prod,&r0,&r0);
ibz_copy(&r2,&r1);
ibz_copy(&r1,&r0);
}
// test if result is solution
ibz_sub(&a,&p4,&prod);
ibz_div(&a,&r2,&a,n);
res = res && ibz_is_zero(&r2);
res = res && ibz_sqrt(y,&a);
if (res){
ibz_copy(x,&r0);
ibz_mul(&a,y,y);
ibz_mul(&a,&a,n);
ibz_add(&prod,&prod,&a);
res = res && (0==ibz_cmp(&prod,&p4));
}
}
}
ibz_finalize(&r0);
ibz_finalize(&r1);
ibz_finalize(&r2);
ibz_finalize(&a);
ibz_finalize(&prod);
ibz_finalize(&two);
ibz_finalize(&p4);
ibz_finalize(&test_square);
return(res);
}
//returns complex product of a and b
void ibz_complex_mul(ibz_t *re_res, ibz_t *im_res, const ibz_t *re_a, const ibz_t *im_a, const ibz_t *re_b, const ibz_t *im_b){
ibz_t prod, re, im;
ibz_init(&prod);
ibz_init(&re);
ibz_init(&im);
ibz_mul(&re, re_a,re_b);
ibz_mul(&prod, im_a, im_b);
ibz_sub(&re,&re,&prod);
ibz_mul(&im, re_a,im_b);
ibz_mul(&prod, im_a, re_b);
ibz_add(&im,&im,&prod);
ibz_copy(im_res,&im);
ibz_copy(re_res,&re);
ibz_finalize(&prod);
ibz_finalize(&re);
ibz_finalize(&im);
}
//multiplies res by a^e with res and a integer complex numbers
void ibz_complex_mul_by_complex_power(ibz_t *re_res, ibz_t *im_res, const ibz_t *re_a, const ibz_t *im_a, int64_t exp){
ibz_t re_x,im_x;
ibz_init(&re_x);
ibz_init(&im_x);
ibz_set(&re_x,1);
ibz_set(&im_x,0);
for (int i = 0; i < 64; i++){
ibz_complex_mul(&re_x,&im_x,&re_x,&im_x,&re_x,&im_x);
if((exp>>(63-i)) & 1){
ibz_complex_mul(&re_x,&im_x,&re_x,&im_x,re_a,im_a);
}
}
ibz_complex_mul(re_res,im_res,re_res,im_res,&re_x,&im_x);
ibz_finalize(&re_x);
ibz_finalize(&im_x);
}
//multiplies to res the result of the solutions of cornacchia for prime depending on valuation val (prime-adic valuation)
int ibz_cornacchia_extended_prime_loop(ibz_t *re_res, ibz_t *im_res, int64_t prime, int64_t val){
ibz_t re, im, p, n;
ibz_init(&re);
ibz_init(&im);
ibz_init(&p);
ibz_init(&n);
ibz_set(&n,1);
ibz_set(&p, prime);
int res = ibz_cornacchia_prime(&re,&im,&n,&p);
if(res){
ibz_complex_mul_by_complex_power(re_res, im_res, &re, &im, val);
}
ibz_finalize(&re);
ibz_finalize(&im);
ibz_finalize(&p);
ibz_finalize(&n);
return(res);
}
int ibz_cornacchia_extended(ibz_t *x, ibz_t *y, const ibz_t *n, const short *prime_list, const int prime_list_length, short primality_test_iterations, const ibz_t *bad_primes_prod){
int res = 1;
ibz_t four, one, nodd, q, r, p;
ibz_init(&four);
ibz_init(&one);
ibz_init(&nodd);
ibz_init(&r);
ibz_init(&q);
ibz_init(&p);
int64_t *valuations = malloc(prime_list_length*sizeof(int64_t));
ibz_set(&four,4);
ibz_set(&one,1);
// if a prime which is 3 mod 4 divides n, extended Cornacchia can't solve the equation
if(bad_primes_prod != NULL){
ibz_gcd(&q,n,bad_primes_prod);
if(!ibz_is_one(&q)){
res = 0;
}
}
if(res){
// get the valuations and the unfactored part by attempting division by all given primes
ibz_copy(&nodd,n);
for (int i = 0; i < prime_list_length; i++){
valuations[i] = 0;
if (((*(prime_list + i) % 4) == 1) || (i == 0)){
ibz_set(&r, 0);
ibz_set(&p, *(prime_list + i));
ibz_copy(&q,&nodd);
while(ibz_is_zero(&r)){
valuations[i] +=1;
ibz_copy(&nodd,&q);
ibz_div(&q,&r,&nodd,&p);
}
valuations[i] -=1;
}
}
// compute the remainder mod 4
ibz_mod(&r,&nodd,&four);
if (ibz_is_one(&r)){ // we hope the 'unfactored' part is a prime 1 mod 4
if (ibz_probab_prime(&nodd, primality_test_iterations) || ibz_is_one(&nodd)){
if (ibz_is_one(&nodd)){ // the unfactored part is 1
ibz_set(x,1);
ibz_set(y,0);
} else { // the 'unfactored' part is prime, can use Cornacchia
res = res && ibz_cornacchia_prime(x,y,&one, &nodd);
}
if (res == 1){ // no need to continue if failure here
for (int i = 0; i < prime_list_length; i++){
if (valuations[i] != 0){
res = res && ibz_cornacchia_extended_prime_loop(x, y, prime_list[i], valuations[i]);
}
}
}
} else {
res = 0;
}
} else {
res = 0;
}
}
free(valuations);
ibz_finalize(&four);
ibz_finalize(&one);
ibz_finalize(&nodd);
ibz_finalize(&r);
ibz_finalize(&q);
ibz_finalize(&p);
return(res);
}

View File

@@ -0,0 +1,740 @@
/** @file
*
* @authors Sina Schaeffler
*
* @brief Declarations for helper functions for quaternion algebra implementation
*/
#ifndef QUAT_HELPER_H
#define QUAT_HELPER_H
#include <quaternion.h>
/** @internal
* @ingroup quat_quat
* @defgroup quat_helpers Quaternion module internal functions
* @{
*/
/** @internal
* @defgroup quat_alg_helpers Helper functions for the alg library
* @{
*/
/** @internal
* @brief helper function for initializing small quaternion algebras.
*/
static inline void quat_alg_init_set_ui(quat_alg_t *alg, unsigned int p) {
ibz_t bp;
ibz_init(&bp);
ibz_set(&bp, p);
quat_alg_init_set(alg, &bp);
ibz_finalize(&bp);
}
/** @brief a+b
*
* Add two integer 4-vectors
*
* @param res Output: Will contain sum
* @param a
* @param b
*/
void quat_alg_coord_add(quat_alg_coord_t *res, const quat_alg_coord_t *a, const quat_alg_coord_t *b);
/** @brief a-b
*
* Substract two integer 4-vectors
*
* @param res Output: Will contain difference
* @param a
* @param b
*/
void quat_alg_coord_sub(quat_alg_coord_t *res, const quat_alg_coord_t *a, const quat_alg_coord_t *b);
/** @brief Compute same denominator form of two quaternion algebra elements
*
* res_a=a and res_b=b (representing the same element) and res_a.denom = res_b.denom
*
* @param res_a
* @param res_b
* @param a
* @param b
*/
void quat_alg_equal_denom(quat_alg_elem_t *res_a, quat_alg_elem_t *res_b, const quat_alg_elem_t *a, const quat_alg_elem_t *b);
/** @brief Copies the given values into an algebra element, without normalizing it
*
* @param elem Output: algebra element of coordinates [coord0,coord1,coord2,coord3] and denominator denom
* @param denom Denominator, must be non zero
* @param coord0 Coordinate on 1 (0th vector of standard algebra basis)
* @param coord1 Coordinate on i (1st vector of standard algebra basis)
* @param coord2 Coordinate on j (2nd vector of standard algebra basis)
* @param coord3 Coordinate on ij (3rd vector of standard algebra basis)
*/
void quat_alg_elem_copy_ibz(quat_alg_elem_t *elem, const ibz_t *denom, const ibz_t *coord0, const ibz_t *coord1, const ibz_t *coord2, const ibz_t *coord3);
/** @brief Sets an algebra element to the given integer values, without normalizing it
*
* @param elem Output: algebra element of coordinates [coord0,coord1,coord2,coord3] and denominator denom
* @param denom Denominator, must be non zero
* @param coord0 Coordinate on 1 (0th vector of standard algebra basis)
* @param coord1 Coordinate on i (1st vector of standard algebra basis)
* @param coord2 Coordinate on j (2nd vector of standard algebra basis)
* @param coord3 Coordinate on ij (3rd vector of standard algebra basis)
*/
void quat_alg_elem_set(quat_alg_elem_t *elem, int64_t denom, int64_t coord0, int64_t coord1, int64_t coord2, int64_t coord3);
/** @brief Multiplies algebra element by integer scalar, without normalizing it
*
* @param res Output
* @param scalar Integer
* @param elem Algebra element
*/
void quat_alg_elem_mul_by_scalar(quat_alg_elem_t *res, const ibz_t *scalar, const quat_alg_elem_t *elem);
/** @brief Compute the right multiplication table of a quaternion (without denominator)
*
* @param mulmat Output
* @param a Quaternion
* @param alg The algebra
*/
void quat_alg_rightmul_mat(ibz_mat_4x4_t *mulmat, const quat_alg_elem_t *a, const quat_alg_t *alg);
/** @}
*/
/** @internal
* @defgroup quat_int_helpers Helper functions for integer functions
* @{
*/
/** @internal
* @defgroup quat_int_small_helpers Small integer functions
* @{
*/
/** @brief round a/b to closest integer q
*/
void ibz_rounded_div(ibz_t *q, const ibz_t *a, const ibz_t *b);
/** @brief Compare ibz_t with long
*/
static int ibz_cmp_si(ibz_t *x, int64_t y) {
ibz_t Y;
ibz_init(&Y);
ibz_set(&Y, y);
int res = ibz_cmp(x, &Y);
ibz_finalize(&Y);
return res;
}
/** @}
*/
/** @internal
* @defgroup quat_cornacchia_helpers Helper functions for the cornacchia function
* @{
*/
/** @brief Complex product of a and b
*
* re_res + i*im_res = (re_a+i*im_a)*(re_b+i*im_b) where i a usual complex 4th root of unity
*
* @param re_res Output: real part of the result
* @param im_res Output: imaginary part of the result
* @param re_a Real part of a
* @param im_a Imaginary part of a
* @param re_b Real part of b
* @param im_b Imaginary part of b
*/
void ibz_complex_mul(ibz_t *re_res, ibz_t *im_res, const ibz_t *re_a, const ibz_t *im_a, const ibz_t *re_b, const ibz_t *im_b);
/** @brief Multiplies res by a^e with res and a integer complex numbers
*
* re_res + i*im_res = (re_res+i*im_res)*((re_a+i*im_a)^exp) where i a usual complex 4th root of unity
*
* @param re_res Output: real part of the result. Also used as input.
* @param im_res Output: imaginary part of the result. Also used as input.
* @param re_a Real part of a
* @param im_a Imaginary part of a
* @param exp res*(a^exp) will be computed, exp should be a positive integer or 0
*/
void ibz_complex_mul_by_complex_power(ibz_t *re_res, ibz_t *im_res, const ibz_t *re_a, const ibz_t *im_a, int64_t exp);
/** @brief Multiplies to res the result of the solutions of cornacchia for prime depending on valuation val (prime-adic valuation)
*
* re_res + i*im_res = (re_res+i*im_res)*((x+i*y)^val) where i a usual complex 4th root of unity, and x,y an integer sulotion to x^2 + y^2 = prime
*
* @param re_res Output: real part of the result. Also used as input.
* @param im_res Output: imaginary part of the result. Also used as input.
* @param prime a prime factor of n on which extended Cornacchia was called
* @param val prime-adic valuation of the n on which extended Cornacchia was called
* @returns 1 if an integer solution x,y to x^2 + y^2 = prime was found by Cornacchia_prime, 0 otherwise
*/
int ibz_cornacchia_extended_prime_loop(ibz_t *re_res, ibz_t *im_res, int64_t prime, int64_t val);
/** @}
*/
/** @}
*/
/** @internal
* @defgroup quat_dim4_helpers Helper functions for functions for matrices or vectors in dimension 4
* @{
*/
/** @internal
* @defgroup quat_inv_helpers Helper functions for the integer matrix inversion function
* @{
*/
/** @brief a1a2+b1b2+c1c2
*
* @param coeff Output: The coefficien which was computed as a1a2+b1b2-c1c2
* @param a1
* @param a2
* @param b1
* @param b2
* @param c1
* @param c2
*/
void ibz_inv_dim4_make_coeff_pmp(ibz_t *coeff, const ibz_t *a1, const ibz_t *a2, const ibz_t *b1, const ibz_t *b2, const ibz_t *c1, const ibz_t *c2);
/** @brief -a1a2+b1b2-c1c2
*
* @param coeff Output: The coefficien which was computed as -a1a2+b1b2-c1c2
* @param a1
* @param a2
* @param b1
* @param b2
* @param c1
* @param c2
*/
void ibz_inv_dim4_make_coeff_mpm(ibz_t *coeff, const ibz_t *a1, const ibz_t *a2, const ibz_t *b1, const ibz_t *b2, const ibz_t *c1, const ibz_t *c2);
/** @brief Matrix determinant and a matrix inv such that inv/det is the inverse matrix of the input
*
* Implemented following the methof of 2x2 minors explained at Method from https://www.geometrictools.com/Documentation/LaplaceExpansionTheorem.pdf (visited on 3rd of May 2023, 16h15 CEST)
*
* @returns 1 if the determinant of mat is not 0 and an inverse was computed, 0 otherwise
* @param inv Output: Will contain an integer matrix which, dividet by det, will yield the rational inverse of the matrix if it exists, can be NULL
* @param det Output: Will contain the determinant of the input matrix, can be NULL
* @param mat Matrix of which the inverse will be computed
*/
int ibz_mat_4x4_inv_with_det_as_denom(ibz_mat_4x4_t *inv, ibz_t *det, const ibz_mat_4x4_t *mat);
/** @brief a*b for a,b integer 4x4 matrices
*
* Naive implementation
*
* @param res Output: A 4x4 integer matrix
* @param a
* @param b
*/
void ibz_mat_4x4_mul(ibz_mat_4x4_t *res, const ibz_mat_4x4_t *a, const ibz_mat_4x4_t *b);
/** @}
*/
/** @internal
* @defgroup quat_lll_verify_helpers Helper functions for lll verification in dimension 4
* @{
*/
/** @brief Set an ibq vector to 4 given integer coefficients
*/
void ibq_vec_4_copy_ibz(ibq_t (*vec)[4], const ibz_t *coeff0, const ibz_t *coeff1,const ibz_t *coeff2,const ibz_t *coeff3);
/** @brief Bilinear form vec00*vec10+vec01*vec11+q*vec02*vec12+q*vec03*vec13 for ibz_q
*/
void quat_dim4_lll_bilinear(ibq_t *b, const ibq_t (*vec0)[4], const ibq_t (*vec1)[4], const ibz_t *q);
/** @brief Outputs the transposition of the orthogonalised matrix of mat (as fractions)
*
* For the bilinear form vec00*vec10+vec01*vec11+q*vec02*vec12+q*vec03*vec13
*/
void quat_dim4_gram_schmidt_transposed_with_ibq(ibq_t (*orthogonalised_transposed)[4][4], const ibz_mat_4x4_t *mat, const ibz_t *q);
/** @brief Verifies if mat is lll-reduced for parameter coeff and norm defined by q
*
* For the bilinear form vec00*vec10+vec01*vec11+q*vec02*vec12+q*vec03*vec13
*/
int quat_dim4_lll_verify(const ibz_mat_4x4_t *mat, const ibq_t *coeff, const ibz_t *q);
/** @}
*/
/** @internal
* @defgroup quat_dim4_lat_helpers Helper functions on vectors and matrices used mainly for lattices
* @{
*/
/** @brief Set a vector of 4 integers to given values
*
* @param vec Output: is set to given coordinates
* @param coord0
* @param coord1
* @param coord2
* @param coord3
*/
void ibz_vec_4_set(ibz_vec_4_t *vec, int64_t coord0, int64_t coord1, int64_t coord2, int64_t coord3);
/** @brief Copy all values from one vector to another
*
* @param new Output: is set to same values as vec
* @param vec
*/
void ibz_vec_4_copy(ibz_vec_4_t *new, const ibz_vec_4_t *vec);
/** @brief Compute the linear combination lc = coeff_a vec_a + coeff_b vec_b
*
* @param lc Output: linear combination lc = coeff_a vec_a + coeff_b vec_b
* @param coeff_a Scalar multiplied to vec_a
* @param vec_a
* @param coeff_b Scalar multiplied to vec_b
* @param vec_b
*/
void ibz_vec_4_linear_combination(ibz_vec_4_t *lc, const ibz_t *coeff_a, const ibz_vec_4_t *vec_a, const ibz_t *coeff_b, const ibz_vec_4_t *vec_b);
/** @brief divides all values in vector by same scalar
*
* @returns 1 if scalar divided all values in mat, 0 otherwise (division is performed in both cases)
* @param quot Output
* @param scalar
* @param vec
*/
int ibz_vec_4_scalar_div(ibz_vec_4_t *quot, const ibz_t *scalar, const ibz_vec_4_t *vec);
/** @brief Negation for vectors of 4 integers
*
* @param neg Output: is set to -vec
* @param vec
*/
void ibz_vec_4_negate(ibz_vec_4_t *neg, const ibz_vec_4_t *vec);
/** @brief Copies all values from a 4x4 integer matrix to another one
*
* @param new Output: matrix which will have its entries set to mat's entries
* @param mat Input matrix
*/
void ibz_mat_4x4_copy(ibz_mat_4x4_t *new, const ibz_mat_4x4_t *mat);
/** @brief -mat for mat a 4x4 integer matrix
*
* @param neg Output: is set to -mat
* @param mat Input matrix
*/
void ibz_mat_4x4_negate(ibz_mat_4x4_t *neg, const ibz_mat_4x4_t *mat);
/** @brief transpose a 4x4 integer matrix
*
* @param transposed Output: is set to the transposition of mat
* @param mat Input matrix
*/
void ibz_mat_4x4_transpose(ibz_mat_4x4_t *transposed, const ibz_mat_4x4_t *mat);
/** @brief Set all coefficients of a matrix to zero for 4x4 integer matrices
*
* @param zero
*/
void ibz_mat_4x4_zero(ibz_mat_4x4_t *zero);
/** @brief Set a matrix to the identity for 4x4 integer matrices
*
* @param id
*/
void ibz_mat_4x4_identity(ibz_mat_4x4_t *id);
/** @brief Test equality to identity for 4x4 integer matrices
*
* @returns 1 if mat is the identity matrix, 0 otherwise
* @param mat
*/
int ibz_mat_4x4_is_identity(const ibz_mat_4x4_t *mat);
/** @brief Equality test for 4x4 integer matrices
*
* @returns 1 if equal, 0 otherwise
* @param mat1
* @param mat2
*/
int ibz_mat_4x4_equal(const ibz_mat_4x4_t *mat1, const ibz_mat_4x4_t *mat2);
/** @brief Matrix by integer multiplication
*
* @param prod Output
* @param scalar
* @param mat
*/
void ibz_mat_4x4_scalar_mul(ibz_mat_4x4_t *prod, const ibz_t *scalar, const ibz_mat_4x4_t *mat);
/** @brief gcd of all values in matrix
*
* @param gcd Output
* @param mat
*/
void ibz_mat_4x4_gcd(ibz_t *gcd, const ibz_mat_4x4_t *mat);
/** @brief divides all values in matrix by same scalar
*
* @returns 1 if scalar divided all values in mat, 0 otherwise (division is performed in both cases)
* @param quot Output
* @param scalar
* @param mat
*/
int ibz_mat_4x4_scalar_div(ibz_mat_4x4_t *quot, const ibz_t *scalar, const ibz_mat_4x4_t *mat);
/** @brief Modular matrix multiplication of aribitrarily sized matrices
*
* res = A * B (mod)
*
* @param from
* @param through
* @param to
* @param res Output: a from × to matrix. Cannot point to the same memory as A or B.
* @param A a from × through matrix
* @param B a through × to matrix
* @param mod modulo
*/
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);
/** @brief Compute the Howell form of a matrix modulo an integer
*
* Source: https://link.springer.com/chapter/10.1007/3-540-68530-8_12
* Adapted from PARI/GP
*
* @param rows The number of rows of the input
* @param cols The number of columns of the input, must be <= rows
* @param howell Output: the Howell form H of mat
* @param trans Output: the transformation matrix U s.t. mat·U = H. May be NULL.
* @param mat The matrix
* @param mod The integer modulus
* @return the number of zero-columns to the lef of `howell`.
*/
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);
/** @brief Compute the right kernel of a matrix modulo an integer
*
* Computes the Howell normal form of the kernel.
*
* Source: https://link.springer.com/chapter/10.1007/3-540-68530-8_12
* Adapted from PARI/GP
*
* @param rows The number of rows of the input
* @param cols The number of columns of the input, must be <= rows
* @param ker Output: the kernel
* @param mat The matrix
* @param mod The integer modulus
*/
void ibz_mat_right_ker_mod(int rows, int cols, ibz_t ker[cols][cols], const ibz_t mat[rows][cols], ibz_t *mod);
/** @brief Verifies whether the 4x4 input matrix is in Hermite Normal Form
*
* @returns 1 if mat is in HNF, 0 otherwise
* @param mat Matrix to be tested
*/
int ibz_mat_4x4_is_hnf(const ibz_mat_4x4_t *mat);
/** @brief Hermite Normal Form of a matrix of 8 integer vectors
*
* Algorithm used is the one at number 2.4.5 in Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
*
* @param hnf Output: Matrix in Hermite Normal Form generating the same lattice as generators
* @param generators matrix whose colums generate the same lattice than the output
*/
void ibz_mat_4x8_hnf_core(ibz_mat_4x4_t *hnf, const ibz_mat_4x8_t *generators);
/** @brief Hermite Normal Form of a matrix concatenated with mod*Id
*
* @param hnf Output: Matrix in Hermite Normal Form generating the same lattice as generators
* @param mat matrix whose colums and mod*Id generate the same lattice than the output
* @param mod mod*Id is appended to the matrix. A hnf of this concatenation is then output
*/
void ibz_mat_4x4_hnf_mod(ibz_mat_4x4_t *hnf, const ibz_mat_4x4_t *mat, const ibz_t *mod);
/** @}
*/
/** @}
*/
/** @internal
* @defgroup quat_dim2_helpers Helper functions for dimension 2
* @{
*/
/** @internal
* @defgroup quat_dim2_other_helpers Set and other small helper functions for dimension 2 matrix and vector
* @{
*/
/** @brief Set vector coefficients to the given integers
*
* @param vec Output: Vector
* @param a0
* @param a1
*/
void ibz_vec_2_set(ibz_vec_2_t *vec, int a0, int a1);
/** @brief Set matrix coefficients to the given integers
*
* @param mat Output: Matrix
* @param a00
* @param a01
* @param a10
* @param a11
*/
void ibz_mat_2x2_set(ibz_mat_2x2_t *mat, int a00, int a01, int a10, int a11);
/** @brief Determinant of a 2x2 integer matrix given as 4 integers
*
* @param det Output: Determinant of the matrix
* @param a11 matrix coefficient (upper left corner)
* @param a12 matrix coefficient (upper right corner)
* @param a21 matrix coefficient (lower left corner)
* @param a22 matrix coefficient (lower right corner)
*/
void ibz_mat_2x2_det_from_ibz(ibz_t *det, const ibz_t *a11, const ibz_t *a12, const ibz_t *a21, const ibz_t *a22);
/** @}
*/
/** @internal
* @defgroup quat_cvp_helpers Helper functions for 2x2 cvp
* @{
*/
/** @brief Checks if the vector (coord1,coord2) has integer coordinates in basis
*
* @param basis Rank 2 matrix
* @param coord1
* @param coord2
* @returns 1 if the vector (coord1,coord2) has integer coordinates in basis, 0 otherwise
*/
int quat_dim2_lattice_contains(ibz_mat_2x2_t *basis, ibz_t *coord1, ibz_t *coord2);
/** @brief coord1^2 + norm_q*coord2^2
*
* This defines a quadratic form in dimension 2 where coord1 is the first and coord2 the second coordinate of the vector on which it is evaluated
*
* @param norm Output: coord1^2 + norm_q*coord2^2
* @param coord1
* @param coord2
* @param norm_q Positive integer
*/
void quat_dim2_lattice_norm(ibz_t *norm, const ibz_t *coord1, const ibz_t *coord2, const ibz_t *norm_q);
/** @brief v11*v21 + norm_q*v12*v22
*
* This defines a bilinear form in dimension 2 where v11,v12 are coordinates of the first vector, v21,v22 of the second vector
*
* @param res Output: v11*v21+q*v12*v22
* @param v11
* @param v12
* @param v21
* @param v22
* @param norm_q Positive integer
*/
void quat_dim2_lattice_bilinear(ibz_t *res, const ibz_t *v11, const ibz_t *v12, const ibz_t *v21, const ibz_t *v22, const ibz_t *norm_q);
/** @brief Basis of the integral lattice represented by basis, which is small for the norm x^2 + qy^2 (in standard basis)
*
* Additionally, the first vector in the basis has a smaller norm than the second one.
*
* Algorithm 1.3.14 from Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
* which finds a shortest vector in the lattice.
*
* @param reduced Output: Matrix of 2 column vectors of small norm x^2 + qy^2 which are a basis of the lattice the basis argument represents
* @param basis Basis of a rank 2 lattice in dimension 2
* @param norm_q Positive integer defining a quadratic form x^2 + qy^2 (x,y coordinates of a vector in the standard basis of R^2) for which the reduced output basis shound be small
*/
void quat_dim2_lattice_short_basis(ibz_mat_2x2_t *reduced, const ibz_mat_2x2_t *basis, const ibz_t *norm_q);
/** @brief Uses a and b to compute a* orthogonal to b, then computes the projection res of t on a* (uses the norm associated to norm_q)
*
* Helper for quat_dim2_lattice_closest_vector, implicitly computes an orthogonalised basis (b,a*) from (b,a) and a projection f the target t on its second vector
*
* Uses the norm associated to norm_q by x^2 + qy^2, for x,y coordinates of a vector
*
* @param res Output: The coefficient (projection of t on a* orthogonal to b) which was computed
* @param a0 will be the 1st coeff of a, the second vector of the input basis
* @param a1 will be the 2nd coeff of a, the second vector of the input basis
* @param b0 will be the 1st coeff of b, the first vector of the input basis
* @param b1 will be the 2nd coeff of b, the first vector of the input basis
* @param t0 will be the 1st coeff of the target vector, which one wnat to project on the orthogonalised basis' second vector
* @param t1 will be the 2nd coeff of the target vector, which one wnat to project on the orthogonalised basis' second vector
* @param norm_q Positive integer defining a quadratic form x^2 + qy^2
*/
void quat_dim2_lattice_get_coefficient_with_orthogonalisation(ibz_t *res, const ibz_t *a0,const ibz_t *a1,const ibz_t *b0,const ibz_t *b1,const ibz_t *t0,const ibz_t *t1,const ibz_t *norm_q);
/** @brief Finds a vector close to the given target in a lattice of which a reduced basis is given
*
* Nearest plane algo as in https://cims.nyu.edu/~regev/teaching/lattices_fall_2004/ln/cvp.pdf (15.5.23,16h10), but without basis reduction:
* Basically just two projections
*
* @param target_minus_closest Output: Vector in standard basis corresponding to target minus the close vector in the lattice which is output in closest_coords_in_basis
* @param closest_coords_in_basis Output: Represents a vector in the lattice close to target, represented by its coordinates in the given absis of the lattice
* @param reduced_basis Reduced basis of the lattice, reduction should be done using lll or an exact shortest vector algorithm
* @param target vector to which the solution should be close
* @param norm_q Positive integer defining a quadratic form x^2 + qy^2 (x,y coordinates of a vector in the standard basis of R^2) for which the reduced output basis shound be small
*/
void quat_dim2_lattice_closest_vector(ibz_vec_2_t *target_minus_closest, ibz_vec_2_t *closest_coords_in_basis, const ibz_mat_2x2_t *reduced_basis, const ibz_vec_2_t *target, const ibz_t *norm_q);
/** @brief give a,b,c such that ax^2 + bxy + cy^2 = N(Bz), where B is the basis, z the vector x,y and N the quadratic form (coord1^2 + q coord2^2)
*
* @param qf_a Output: b in the formula
* @param qf_b Output: b in the formula
* @param qf_c Output: c in the formula
* @param basis Basis of the lattice
* @param norm_q Positive integer defining a quadratic form x^2 + qy^2 (x,y coordinates of a vector in the standard basis of R^2) for which the reduced output basis shound be small
*/
void quat_dim2_lattice_get_qf_on_lattice(ibz_t *qf_a, ibz_t *qf_b, ibz_t *qf_c, const ibz_mat_2x2_t *basis, const ibz_t *norm_q);
/** @brief Test version of the condition argument in the cvp enumeration algorithms.
*
* Sets elem[0] and elem[2] to vec[0], elem[1] and elem[3] to vec[1] if vec[0] + vec[1] mod p is 2, where p is the ibz_t to which params points
*
* This defines a quadratic form in dimension 2 where coord1 is the first and coord2 the second coordinate of the vector on which it is evaluated
*
* @param elem Output as described below
* @param vec
* @param params void* which must point to an ibz_t
*/
int quat_dim2_lattice_test_cvp_condition(quat_alg_elem_t* elem, const ibz_vec_2_t* vec, const void* params);
/**
* @brief Find vector of small norm in a positive definite quadratic form, satisfying extra conditions.
*
* @param res Output: The quat_alg_elem (dimension 4, with denominator) which the condition sets when it is fulfilled
* @param x first coordinate in the lattice basis lat_basis of a short vector (for the norm written coeff1^2 + q coeff2 ^2 in the standard basis)
* @param y first coordinate in the lattice basis lat_basis of a short vector (for the norm written coeff1^2 + q coeff2 ^2 in the standard basis)
* @param condition a filter function returning whether res is set to a valid output or not. The algorithm stops when it succeeds (outputs 1)
* @param params extra parameters passed to `condition`. May be NULL.
* @param target_minus_closest vector which will be added to the enumerated short vectors before checking the bound
* @param lat_basis reduced basis of the lattice, in which a short vector is searched for
* @param norm_q defines the quadratic form coord1^2+norm_q*coord2^2 (in standard basis) used as norm
* @param norm_bound only vectors with norm smaller than this bound are tested for condition
* @return 1 if vector was found, 0 otherwise
*/
int quat_dim2_lattice_bound_and_condition(quat_alg_elem_t *res, const ibz_t *x, const ibz_t *y, int (*condition)(quat_alg_elem_t *, const ibz_vec_2_t *, const void *), const void *params, const ibz_vec_2_t *target_minus_closest, const ibz_mat_2x2_t *lat_basis, const ibz_t *norm_q, const ibz_t *norm_bound);
/** @brief Computes an integer slightly larger than sqrt(num_a/denom_a)+num_b/denom_b.
*
* More precisely, if denoting sqrt_ the integer part of the square root, and _ the integer part of a rational,
* res = 1+_((sqrt_(num_a)+1)/sqrt_(denom_a) + num_b/denom_b)
*
* @param res upper approximation of sqrt(num_a/denom_a)+num_b/denom_b
* @param num_a must be of same sign as denom_a
* @param denom_a must be non 0 and of same sign as num_a
* @param num_b
* @param denom_b must be non 0
*/
int quat_dim2_lattice_qf_value_bound_generation(ibz_t *res, const ibz_t *num_a, const ibz_t *denom_a, const ibz_t *num_b, const ibz_t *denom_b);
/**
* @brief Find vector of small norm in a positive definite quadratic form, satisfying extra conditions.
*
* Enumerates up to `max_tries` vectors `(x,y)` such that `qfa x² + qfb xy + qfc y² < norm_bound`; the first
* vector such that `quat_dim2_lattice_bound_and_condition(res,x,y,condition,params,target_minus_closest,lat_basis, norm_q,norm_bound) == 1` is returned.
* The vector enumeration starts by vectors close to the bound
*
* Uses algorithm 2.7.5 (Fincke-Pohst) from Henri Cohen's "A Course in Computational Algebraic Number Theory" (Springer Verlag, in series "Graduate texts in Mathematics") from 1993
* Slightly adapted to work without rational numbers and their square roots
* Therefore needing a test to make sure the bounds are respected, which is integrated in quat_dim2_lattice_bound_and_condition
* Also the norm bound is initialised a bit lower that the bound received, to leave space for the added target_minus_closest
*
* @param res Output: selected vector (x,y)
* @param condition a filter function returning whether a vector should be output or not
* @param params extra parameters passed to `condition`. May be NULL.
* @param target_minus_closest vector which will be added to the enumerated short vectors before checking the bound
* @param lat_basis reduced basis of the lattice, in which a short vector is searched for
* @param norm_q defines the quadratic form coord1^2+norm_q*coord2^2 (in standard basis) used as norm
* @param norm_bound only vectors with norm smaller than this bound are tested for condition and output
* @param max_tries maximum number of calls to filter
* @return 1 if vector was found, 0 otherwise
*/
int quat_dim2_lattice_qf_enumerate_short_vec(quat_alg_elem_t *res, int (*condition)(quat_alg_elem_t *, const ibz_vec_2_t *, const void *), const void *params, const ibz_vec_2_t *target_minus_closest, const ibz_mat_2x2_t *lat_basis, const ibz_t *norm_q, const ibz_t *norm_bound, const int max_tries);
/** @}
*/
/** @}
*/
/** @internal
* @defgroup quat_lattice_helper Helper functions for the lattice library (dimension 4)
* @{
*/
/**
* @brief Lattice equality
*
* Lattice bases are assumed to be under HNF, but denominators are free.
*
* @returns 1 if both lattices are equal, 0 otherwise
*/
int quat_lattice_equal(const quat_lattice_t *lat1, const quat_lattice_t *lat2);
/** @brief Divides basis and denominator of a lattice by their gcd
*
* @param reduced Output
* @param lat Lattice
*/
void quat_lattice_reduce_denom(quat_lattice_t *reduced, const quat_lattice_t *lat);
/**
* @brief Computes the dual lattice of lat, without putting its basis in HNF
*
* This function returns a lattice not under HNF. For careful internal use only.
*
* Coputation method described in https://cseweb.ucsd.edu/classes/sp14/cse206A-a/lec4.pdf consulted on 19 of May 2023, 12h40 CEST
*
* @param dual Output: The dual lattice of lat. ATTENTION: is not under HNF. hnf computation must be applied before using lattice functions on it
* @param lat lattice, the dual of it will be computed
*/
void quat_lattice_dual_without_hnf(quat_lattice_t *dual, const quat_lattice_t *lat);
/**
* @brief Test whether x ∈ lat. If so, compute its coordinates in lat's basis.
*
* Lattice assumed of full rank and under HNF, none of both is tested so far.
*
* @param coord Output: Set to the coordinates of x in lat
* @param lat The lattice
* @param x An element of the quaternion algebra, in same basis as the lattice lat
* @return true if x ∈ lat
*/
int quat_lattice_contains_without_alg(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x);
/** @brief The index of sublat into overlat
*
* Assumes inputs are in HNF.
*
* @param index Output
* @param sublat A lattice in HNF, must be sublattice of overlat
* @param overlat A lattice in HNF, must be overlattice of sublat
*/
void quat_lattice_index(ibz_t *index, const quat_lattice_t *sublat, const quat_lattice_t *overlat);
/** @brief Random lattice element from a small parallelogram
*
* Sample a random element in `lattice` by taking a random linear
* combination of the basis with uniform coefficients in
* [-2^(n-1),2^(n-1)-1].
*
* @param elem Output element
* @param lattice Whence the element is sampled
* @param n Number of random bits for the coefficients. Must be <= 64.
* @return 0 if PRNG failed (in this case elem is set to 0), 1 otherwise
*/
int quat_lattice_random_elem(quat_alg_elem_t *elem, const quat_lattice_t *lattice, unsigned char n);
/** @brief Compute the right transporter from lat1 to lat2
*
* The ideal of those x such that lat1·x ⊂ lat2
*/
void quat_lattice_right_transporter(quat_lattice_t *trans, const quat_lattice_t *lat1, const quat_lattice_t *lat2, const quat_alg_t *alg);
/** @}
*/
/** @}
*/
/** @}
*/
#endif

View File

@@ -0,0 +1,469 @@
#include <quaternion.h>
#include <rng.h>
#include "internal.h"
//helper functions
int quat_lattice_equal(const quat_lattice_t *lat1, const quat_lattice_t *lat2){
int equal;
ibz_t abs_denom1, abs_denom2;
ibz_mat_4x4_t m1,m2;
ibz_init(&abs_denom1);
ibz_init(&abs_denom2);
ibz_mat_4x4_init(&m1);
ibz_mat_4x4_init(&m2);
// test if both are in HNF as needed
assert(ibz_mat_4x4_is_hnf(&(lat1->basis)));
assert(ibz_mat_4x4_is_hnf(&(lat2->basis)));
// get absolute values of denominators
ibz_neg(&abs_denom1,&(lat1->denom));
if(ibz_cmp(&abs_denom1,&(lat1->denom)) <0 ){
ibz_neg(&abs_denom1,&abs_denom1);
}
ibz_neg(&abs_denom2,&(lat2->denom));
if(ibz_cmp(&abs_denom2,&(lat2->denom)) <0 ){
ibz_neg(&abs_denom2,&abs_denom2);
}
// cross-multiply by denomiators to get both basis on same denominators
ibz_mat_4x4_scalar_mul(&m1,&abs_denom2,&(lat1->basis));
ibz_mat_4x4_scalar_mul(&m2,&abs_denom1,&(lat2->basis));
// baoth are still HNF, so simply test for equality
equal = ibz_mat_4x4_equal(&m1,&m2);
ibz_finalize(&abs_denom1);
ibz_finalize(&abs_denom2);
ibz_mat_4x4_finalize(&m1);
ibz_mat_4x4_finalize(&m2);
return(equal);
}
void quat_lattice_reduce_denom(quat_lattice_t *reduced, const quat_lattice_t *lat){
ibz_t gcd;
ibz_init(&gcd);
ibz_mat_4x4_gcd(&gcd,&(lat->basis));
ibz_gcd(&gcd,&gcd,&(lat->denom));
ibz_mat_4x4_scalar_div(&(reduced->basis),&gcd,&(lat->basis));
ibz_div(&(reduced->denom),&gcd,&(lat->denom),&gcd);
ibz_finalize(&gcd);
}
// This function returns a lattice not under HNF. For careful internal use only
// method described in https://cseweb.ucsd.edu/classes/sp14/cse206A-a/lec4.pdf consulted on 19 of May 2023, 12h40 CEST
void quat_lattice_dual_without_hnf(quat_lattice_t *dual, const quat_lattice_t *lat){
ibz_mat_4x4_t inv;
ibz_t det;
ibz_init(&det);
ibz_mat_4x4_init(&inv);
ibz_mat_4x4_transpose(&inv,&(lat->basis));
ibz_mat_4x4_inv_with_det_as_denom(&inv,&det,&inv);
// dual_denom = det/lat_denom
ibz_mat_4x4_scalar_mul(&(dual->basis),&(lat->denom),&inv);
ibz_copy(&(dual-> denom),&det);
ibz_finalize(&det);
ibz_mat_4x4_finalize(&inv);
}
void quat_lattice_add(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2){
ibz_mat_4x8_t hnf_input;
ibz_mat_4x4_t tmp;
ibz_mat_4x4_init(&tmp);
ibz_mat_4x8_init(&hnf_input);
ibz_mat_4x4_scalar_mul(&tmp,&(lat1->denom),&(lat2->basis));
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_copy(&(hnf_input[i][j]),&(tmp[i][j]));
}
}
ibz_mat_4x4_scalar_mul(&tmp,&(lat2->denom),&(lat1->basis));
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_copy(&(hnf_input[i][4+j]),&(tmp[i][j]));
}
}
ibz_mat_4x8_hnf_core(&(res->basis),&hnf_input);
ibz_mul(&(res->denom),&(lat1->denom), &(lat2->denom));
quat_lattice_reduce_denom(res,res);
ibz_mat_4x8_finalize(&hnf_input);
ibz_mat_4x4_finalize(&tmp);
}
// method described in https://cseweb.ucsd.edu/classes/sp14/cse206A-a/lec4.pdf consulted on 19 of May 2023, 12h40 CEST
void quat_lattice_intersect(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2){
quat_lattice_t dual1, dual2, dual_res;
quat_lattice_init(&dual1);
quat_lattice_init(&dual2);
quat_lattice_init(&dual_res);
quat_lattice_dual_without_hnf(&dual1,lat1);
quat_lattice_dual_without_hnf(&dual2,lat2);
quat_lattice_add(&dual_res,&dual1,&dual2);
quat_lattice_dual_without_hnf(res,&dual_res);
quat_lattice_hnf(res);
quat_lattice_finalize(&dual1);
quat_lattice_finalize(&dual2);
quat_lattice_finalize(&dual_res);
}
void quat_lattice_mul(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2, const quat_alg_t *alg){
ibz_t denom, d, r;
ibz_mat_4x8_t hnf_input;
ibz_mat_4x4_t tmp1,tmp2;
quat_alg_elem_t elem1,elem2,elem_res;
quat_lattice_t lat_res;
ibz_init(&denom);
ibz_init(&d);
ibz_init(&r);
quat_lattice_init(&lat_res);
ibz_mat_4x4_init(&tmp1);
ibz_mat_4x4_init(&tmp2);
quat_alg_elem_init(&elem1);
quat_alg_elem_init(&elem2);
quat_alg_elem_init(&elem_res);
ibz_mat_4x8_init(&hnf_input);
ibz_mul(&denom,&(lat1->denom),&(lat2->denom));
for(int k = 0; k < 2; k++){
quat_alg_elem_copy_ibz(&elem1,&(lat1->denom),&(lat1->basis[0][k]),&(lat1->basis[1][k]),&(lat1->basis[2][k]),&(lat1->basis[3][k]));
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_copy(&(elem2.coord[j]),&(lat2->basis[i][j]));
}
quat_alg_elem_copy_ibz(&elem2,&(lat2->denom),&(lat2->basis[0][i]),&(lat2->basis[1][i]),&(lat2->basis[2][i]),&(lat2->basis[3][i]));
quat_alg_mul(&elem_res,&elem1,&elem2,alg);
//should check that denom is as expected (product of both), otherwise set it to that to avoid issues
if(0!=ibz_cmp(&denom,&(elem_res.denom))){
ibz_div(&d,&r,&denom,&(elem_res.denom));
quat_alg_elem_mul_by_scalar(&elem_res,&d,&elem_res);
}
for(int j = 0; j < 4; j++){
ibz_copy(&(hnf_input[j][4*k+i]),&(elem_res.coord[j]));
}
}
}
ibz_mat_4x8_hnf_core(&tmp1,&hnf_input);
for(int k = 0; k < 2; k++){
quat_alg_elem_copy_ibz(&elem1,&(lat1->denom),&(lat1->basis[0][2+k]),&(lat1->basis[1][2+k]),&(lat1->basis[2][2+k]),&(lat1->basis[3][2+k]));
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_copy(&(elem2.coord[j]),&(lat2->basis[i][j]));
}
quat_alg_elem_copy_ibz(&elem2,&(lat2->denom),&(lat2->basis[0][i]),&(lat2->basis[1][i]),&(lat2->basis[2][i]),&(lat2->basis[3][i]));
quat_alg_mul(&elem_res,&elem1,&elem2,alg);
//should check that denom is as expected (product of both), otherwise set it to that to avoid issues
if(0!=ibz_cmp(&denom,&(elem_res.denom))){
ibz_div(&d,&r,&denom,&(elem_res.denom));
quat_alg_elem_mul_by_scalar(&elem_res,&d,&elem_res);
}
for(int j = 0; j < 4; j++){
ibz_copy(&(hnf_input[j][4*k+i]),&(elem_res.coord[j]));
}
}
}
ibz_mat_4x8_hnf_core(&tmp2,&hnf_input);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_copy(&(hnf_input[i][j]),&(tmp1[i][j]));
ibz_copy(&(hnf_input[i][4+j]),&(tmp2[i][j]));
}
}
ibz_mat_4x8_hnf_core(&(res->basis),&hnf_input);
ibz_copy(&(res->denom),&denom);
quat_lattice_reduce_denom(res,res);
ibz_mat_4x8_finalize(&hnf_input);
ibz_mat_4x4_finalize(&tmp1);
ibz_mat_4x4_finalize(&tmp2);
quat_alg_elem_finalize(&elem1);
quat_alg_elem_finalize(&elem2);
quat_alg_elem_finalize(&elem_res);
quat_lattice_finalize(&lat_res);
ibz_finalize(&denom);
ibz_finalize(&d);
ibz_finalize(&r);
}
// lattice assumed of full rank and under HNF, none of both is tested so far
int quat_lattice_contains_without_alg(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x){
int res = 1;
ibz_vec_4_t work_coord, work_x, column;
ibz_t r, prod, one;
ibz_init(&r);
ibz_init(&prod);
ibz_init(&one);
ibz_vec_4_init(&work_coord);
ibz_vec_4_init(&work_x);
ibz_vec_4_init(&column);
// test if rank 4 lattice under HNF
assert(ibz_mat_4x4_is_hnf(&(lat->basis)));
for(int i = 0; i < 4; i++){
assert(!ibz_is_zero(&(lat->basis[i][i])));
}
ibz_set(&one,1);
for(int i = 0; i < 4;i++){
// put on same denominator, 1st part
ibz_mul(&(work_x[i]), &(x->coord[i]),&(lat->denom));
}
for(int i = 0; i < 4;i++){
if(res){
// put on same denominator, 2nd part
ibz_mul(&prod,&(x->denom), &(lat->basis[3-i][3-i]));
ibz_div(&(work_coord[3-i]), &r,&(work_x[3-i]), &prod);
if(ibz_is_zero(&r)){
for (int j = 0; j < 4;j++){
// put on same denominator here also
ibz_mul(&(column[j]),&(lat->basis[j][3-i]),&(x->denom));
}
// negate quotient
ibz_neg(&r,&(work_coord[3-i]));
ibz_vec_4_linear_combination(&work_x, &one, &work_x, &r,&column);
} else {
res = 0;
}
}
}
//final test
for(int i = 0; i < 4;i++){
// now x should be 0 if it is in lattice
res = res && ibz_is_zero(&(work_x[i]));
}
//copy result
if(res && (coord != NULL)){
for(int i = 0; i < 4;i++){
ibz_copy(&((*coord)[i]),&(work_coord[i]));
}
}
ibz_finalize(&r);
ibz_finalize(&prod);
ibz_finalize(&one);
ibz_vec_4_finalize(&work_coord);
ibz_vec_4_finalize(&work_x);
ibz_vec_4_finalize(&column);
return(res);
}
// lattice assumed of full rank and under HNF, none of both is tested so far
int quat_lattice_contains(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x, const quat_alg_t *alg){
return(quat_lattice_contains_without_alg(coord,lat,x));
}
void quat_lattice_index(ibz_t *index, const quat_lattice_t *sublat, const quat_lattice_t *overlat) {
ibz_t tmp;
ibz_init(&tmp);
// index = (overlat->denom)⁴
ibz_mul(index, &overlat->denom, &overlat->denom);
ibz_mul(index, index, index);
// index = (overlat->denom)⁴ · det(sublat->basis)
for (int i = 0; i < 4; i++) {
ibz_mul(index, index, &sublat->basis[i][i]);
}
// tmp = (sublat->denom)⁴
ibz_mul(&tmp, &sublat->denom, &sublat->denom);
ibz_mul(&tmp, &tmp, &tmp);
// tmp = (sublat->denom)⁴ · det(overlat->basis)
for (int i = 0; i < 4; i++) {
ibz_mul(&tmp, &tmp, &overlat->basis[i][i]);
}
// index = index / tmp
ibz_div(index, &tmp, index, &tmp);
assert(ibz_is_zero(&tmp));
// index = |index|
ibz_abs(index, index);
ibz_finalize(&tmp);
}
void quat_lattice_hnf(quat_lattice_t *lat){
ibz_mat_4x8_t hnf_input;
ibz_mat_4x8_init(&hnf_input);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(hnf_input[i][j]),0);
}
}
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_copy(&(hnf_input[i][4+j]),&(lat->basis[i][j]));
}
}
ibz_mat_4x8_hnf_core(&(lat->basis),&hnf_input);
quat_lattice_reduce_denom(lat,lat);
ibz_mat_4x8_finalize(&hnf_input);
}
int quat_lattice_random_elem(quat_alg_elem_t *elem, const quat_lattice_t *lattice, unsigned char n) {
assert(n <= 64);
// Set elem to 0
for (int i = 0; i < 4; i++)
ibz_set(&elem->coord[i], 0);
ibz_set(&elem->denom, 1);
// Take random coefficients
int64_t rand[4];
int randret = randombytes((unsigned char*)rand, 4*sizeof(uint64_t));
if (randret != 0)
return 0;
// Make the linear combination
quat_alg_elem_t tmp;
quat_alg_elem_init(&tmp);
for (int j = 0; j < 4; j++) {
rand[j] >>= (64-n);
for (int i = 0; i < 4; i++) {
ibz_set(&tmp.coord[i], rand[j]);
ibz_mul(&tmp.coord[i], &tmp.coord[i], &lattice->basis[i][j]);
}
quat_alg_add(elem, elem, &tmp);
}
quat_alg_elem_finalize(&tmp);
// Set the denominator
ibz_copy(&elem->denom, &lattice->denom);
return 1;
}
/** Right transporter **/
/** @brief to[to_row] = (-1)^neg · c · from[from_row]
*
* c may be NULL, in which case c = 1
*/
static inline void copy_row(ibz_mat_4x4_t to, int to_row, int neg, const ibz_t *c, const ibz_mat_4x4_t from, int from_row) {
for (int j = 0; j < 4; j++) {
if (c)
ibz_mul(&to[to_row][j], c, &from[from_row][j]);
else
ibz_copy(&to[to_row][j], &from[from_row][j]);
if (neg)
ibz_neg(&to[to_row][j], &to[to_row][j]);
}
}
/** @brief copy matrix into columns of cols
*/
static inline void mat_to_col_16x4(ibz_t (*cols)[16][4], int col, const ibz_mat_4x4_t *mat) {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
ibz_copy(&((*cols)[j+4*i][col]), &((*mat)[i][j]));
}
/** @brief take the gcd of content with all entries
*/
static inline void mat_16x4_content(ibz_t *content, const ibz_t (*mat)[16][4]){
for(int i = 0; i < 16; i++)
for(int j = 0; j < 4; j++)
ibz_gcd(content, content, &((*mat)[i][j]));
}
/** @brief take the gcd of content with all entries
*/
static inline void mat_16x4_mul_by_scalar(ibz_t (*prod)[16][4], const ibz_t (*mat)[16][4],const ibz_t *scalar){
for(int i = 0; i < 16; i++)
for(int j = 0; j < 4; j++)
ibz_mul(&((*prod)[i][j]), scalar, &((*mat)[i][j]));
}
/** @brief take the gcd of content with all entries
*/
static inline int mat_16x4_div_by_scalar(ibz_t (*quot)[16][4], const ibz_t (*mat)[16][4],const ibz_t *scalar){
int ok = 1;
ibz_t r;
ibz_init(&r);
for(int i = 0; i < 16; i++)
for(int j = 0; j < 4; j++)
ibz_div(&((*quot)[i][j]),&r,&((*mat)[i][j]),scalar);
ok = ok && (ibz_is_zero(&r));
ibz_finalize(&r);
return(ok);
}
void quat_lattice_right_transporter(quat_lattice_t *trans, const quat_lattice_t *lat1, const quat_lattice_t *lat2, const quat_alg_t *alg)
{
ibz_t det1, det2, tmp, thrash, content;
ibz_mat_4x4_t lat2_inv, tmpmat;
ibz_t work[16][4];
ibz_init(&det1);
ibz_init(&det2);
ibz_init(&tmp);
ibz_init(&thrash);
ibz_init(&content);
ibz_mat_4x4_init(&lat2_inv);
ibz_mat_4x4_init(&tmpmat);
ibz_mat_init(16,4,work);
// Compute the dual lattice Λ₂* of Λ₂ = num(lat2)
// Could be optimized, given it's triangular
// M₂ = Λ₂*/det₂
int invertible = ibz_mat_4x4_inv_with_det_as_denom(&lat2_inv, &det2, &lat2->basis);
assert(invertible);
// WARNING: hard-cording right multiplication table of "standard" basis
// Prone to breakage if the basis changes
//
// M = d₂/(d₁ det₂) · table
//
// Λ₂* Id Λ₁
ibz_mat_4x4_mul(&tmpmat, &lat2_inv, &lat1->basis);
mat_to_col_16x4(&work, 0, &tmpmat);
// Λ₂* [0 -1 0 0 ; 1 0 0 0 ; 0 0 0 1 ; 0 0 -1 0] Λ₁
copy_row(tmpmat, 0, 1, NULL, lat1->basis, 1);
copy_row(tmpmat, 1, 0, NULL, lat1->basis, 0);
copy_row(tmpmat, 2, 0, NULL, lat1->basis, 3);
copy_row(tmpmat, 3, 1, NULL, lat1->basis, 2);
ibz_mat_4x4_mul(&tmpmat, &lat2_inv, &tmpmat);
mat_to_col_16x4(&work, 1, &tmpmat);
// Λ₂* [0 0 -p 0 ; 0 0 0 -p ; 1 0 0 0 ; 0 1 0 0] Λ₁
copy_row(tmpmat, 0, 1, &alg->p, lat1->basis, 2);
copy_row(tmpmat, 1, 1, &alg->p, lat1->basis, 3);
copy_row(tmpmat, 2, 0, NULL, lat1->basis, 0);
copy_row(tmpmat, 3, 0, NULL, lat1->basis, 1);
ibz_mat_4x4_mul(&tmpmat, &lat2_inv, &tmpmat);
mat_to_col_16x4(&work, 2, &tmpmat);
// Λ₂*[0 0 0 -p ; 0 0 p 0 ; 0 -1 0 0 ; 1 0 0 0] Λ₁
copy_row(tmpmat, 0, 1, &alg->p, lat1->basis, 3);
copy_row(tmpmat, 1, 0, &alg->p, lat1->basis, 2);
copy_row(tmpmat, 2, 1, NULL, lat1->basis, 1);
copy_row(tmpmat, 3, 0, NULL, lat1->basis, 0);
ibz_mat_4x4_mul(&tmpmat, &lat2_inv, &tmpmat);
mat_to_col_16x4(&work, 3, &tmpmat);
ibz_mul(&det1, &lat1->basis[0][0], &lat1->basis[1][1]);
ibz_mul(&tmp, &lat1->basis[2][2], &lat1->basis[3][3]);
ibz_mul(&det1, &det1, &tmp);
ibz_mul(&det1, &det1, &lat2->denom);
ibz_gcd(&tmp, &det1, &lat1->denom);
ibz_div(&det1, &thrash, &det1, &tmp);
{
int ok = 1;
mat_16x4_content(&content,&work);
ok &= mat_16x4_div_by_scalar(&work,&work,&content);
assert(ok);
ibz_mul(&content, &content, &lat2->denom);
ibz_mul(&det2, &det2, &lat1->denom);
ibz_gcd(&tmp, &det2, &content);
ibz_div(&det2, &thrash, &det2, &tmp);
ibz_div(&content, &thrash, &content, &tmp);
mat_16x4_mul_by_scalar(&work,&work,&content);
ibz_mul(&det2, &det2, &det1);
ibz_mat_right_ker_mod(16, 4, trans->basis, work, &det2);
ibz_mat_4x4_hnf_mod(&trans->basis, &trans->basis, &det2);
ibz_copy(&trans->denom, &det1);
quat_lattice_reduce_denom(trans, trans);
}
ibz_mat_finalize(16,4,work);
ibz_finalize(&det1);
ibz_finalize(&det2);
ibz_finalize(&tmp);
ibz_finalize(&thrash);
ibz_finalize(&content);
ibz_mat_4x4_finalize(&lat2_inv);
ibz_mat_4x4_finalize(&tmpmat);
}

View File

@@ -0,0 +1,366 @@
#include <quaternion.h>
#include "internal.h"
// RED(k,l) sub-algorithm
static void RED(ibz_mat_4x4_t *basis, ibq_t (*u)[4][4], ibz_t (*H)[4][4], int k, int l) {
ibq_t tmp, tmp2;
ibz_t q, tibz, num, den, r;
ibq_init(&tmp);
ibq_init(&tmp2);
ibz_init(&q);
ibz_init(&tibz);
ibz_init(&num);
ibz_init(&den);
ibz_init(&r);
ibz_set(&num,1);
ibz_set(&den,2);
ibq_set(&tmp,&num,&den);
ibz_set(&num,0);
ibz_set(&den,0);
// if |u_{k,l}| <= 0.5, terminate
ibq_abs(&tmp2, &((*u)[k][l]));
if (ibq_cmp(&tmp2, &tmp) <= 0)
goto end;
// q <- floor(0.5 + u_{k,l})
ibq_add(&tmp, &tmp, &((*u)[k][l]));
ibq_num(&num, &tmp);
ibq_denom(&den, &tmp);
//FDIV was used, needs reeimplementation
ibz_div_floor(&q,&r, &num, &den);
//ibq_floor(tmp, tmp);
//ibz_set_f(q, tmp);
// b_k = b_k - q*b_l
for (int i = 0; i < 4; ++i) {
ibz_mul(&tibz, &q, &((*basis)[l][i]));
ibz_sub(&((*basis)[k][i]), &((*basis)[k][i]), &tibz);
}
// H_k = H_k - q*H_l
for (int i = 0; i < 4; ++i) {
ibz_mul(&tibz, &q, &((*H)[l][i]));
ibz_sub(&((*H)[k][i]), &((*H)[k][i]), &tibz);
}
// u_{k,j} = u_{k,l}-q
ibq_set(&tmp2, &q,&ibz_const_one);
ibq_sub(&((*u)[k][l]), &((*u)[k][l]), &tmp2);
// forall_i \in 1..l-1: u_{k,i} = u_{k,i} - q*u_{l,i}
for (int i = 0; i <= l-1; ++i) {
ibq_mul(&tmp, &tmp2, &((*u)[l][i]));
ibq_sub(&((*u)[k][i]), &((*u)[k][i]), &tmp);
}
end:
ibq_finalize(&tmp);
ibq_finalize(&tmp2);
ibz_finalize(&q);
ibz_finalize(&tibz);
ibz_finalize(&num);
ibz_finalize(&den);
ibz_finalize(&r);
}
// SWAP(k) sub-algorithm
static void SWAP(ibz_mat_4x4_t *basis, ibq_t (*u)[4][4], ibz_t (*H)[4][4], ibq_t (*B)[4], ibq_t (*bStar)[4][4], int k, int kmax) {
ibq_t tmp, tmp2, tmp3, u_tmp, B_tmp, b[4];
ibq_init(&tmp);
ibq_init(&tmp2);
ibq_init(&tmp3);
ibq_init(&u_tmp);
ibq_init(&B_tmp);
for (int i = 0; i < 4; ++i) {
ibq_init(&(b[i]));
}
// swap b_k and b_{k-1}
for (int i = 0; i < 4; ++i) {
ibz_swap(&((*basis)[k][i]), &((*basis)[k-1][i]));
}
// swap H_k and H_{k-1}
for (int i = 0; i < 4; ++i) {
ibz_swap(&((*H)[k][i]), &((*H)[k-1][i]));
}
if (k > 1) {
// swap u_{k,j} and u_{k-1,j}
for (int j = 0; j <= k - 2; ++j) {
ibq_swap(&((*u)[k][j]), &((*u)[k-1][j]));
}
}
// u = u_{k,k-1}
ibq_copy(&u_tmp, &((*u)[k][k - 1]));
// B = B_k + u^2*B_{k-1}
ibq_mul(&B_tmp, &u_tmp, &u_tmp);
ibq_mul(&B_tmp, &B_tmp, &((*B)[k-1]));
ibq_add(&B_tmp, &((*B)[k]), &B_tmp);
// u_{k,k-1} = u*B_{k-1} / B
ibq_mul(&tmp, &u_tmp, &((*B)[k-1]));
ibq_div(&((*u)[k][k-1]), &tmp, &B_tmp);
// b = bSTAR_{k-1}
for (int i = 0; i < 4; ++i) {
ibq_copy(&(b[i]), &((*bStar)[k-1][i]));
}
// bSTAR_{k-1}=bSTAR_k+u*b
for (int i = 0; i < 4; ++i) {
ibq_mul(&tmp, &u_tmp, &(b[i]));
ibq_add(&((*bStar)[k-1][i]), &((*bStar)[k][i]), &tmp);
}
// bSTAR_k = -u_{k,k-1}*bSTAR_k+(B_k/B)*b
ibq_div(&tmp2, &((*B)[k]), &B_tmp); // B_k/B
ibq_neg(&tmp, &((*u)[k][k-1]));
for (int i = 0; i < 4; ++i) {
ibq_mul(&((*bStar)[k][i]), &tmp, &((*bStar)[k][i]));
ibq_mul(&tmp3, &tmp2, &(b[i]));
ibq_add(&((*bStar)[k][i]), &((*bStar)[k][i]), &tmp3);
}
// B_k = B_{k-1}*B_k/B
ibq_mul(&((*B)[k]), &((*B)[k-1]), &((*B)[k]));
ibq_div(&((*B)[k]), &((*B)[k]), &B_tmp);
// B_{k-1} = B
ibq_copy(&((*B)[k-1]), &B_tmp);
for (int i = k+1; i <= kmax; ++i) {
// t = u_{i,k}
ibq_copy(&tmp, &((*u)[i][k]));
// u_{i,k} = u_{i,k-1} - u*t
ibq_mul(&((*u)[i][k]), &u_tmp, &tmp);
ibq_sub(&((*u)[i][k]), &((*u)[i][k-1]), &((*u)[i][k]));
// u_{i,k-1} = t + u_{k,k-1}*u_{i,k}
ibq_mul(&tmp2, &((*u)[k][k-1]), &((*u)[i][k]));
ibq_add(&((*u)[i][k-1]), &tmp, &tmp2);
}
ibq_finalize(&tmp);
ibq_finalize(&tmp2);
ibq_finalize(&tmp3);
ibq_finalize(&u_tmp);
ibq_finalize(&B_tmp);
for (int i = 0; i < 4; ++i) {
ibq_finalize(&(b[i]));
}
}
// m1[0]*m2[0] + m1[1]*m2[1] + q*(m1[2]*m2[2] + m1[3]*m2[3])
static void dotproduct_row(ibz_t *mul, const ibz_mat_4x4_t *m1, const ibz_mat_4x4_t *m2, const ibz_t *q, int m1j, int m2j) {
ibz_set(mul, 0);
ibz_t tmp1, tmp2;
ibz_init(&tmp1);
ibz_init(&tmp2);
for (int i = 0; i < 2; ++i) {
ibz_mul(&tmp1, &((*m1)[m1j][i]), &((*m2)[m2j][i]));
ibz_add(mul, mul, &tmp1);
}
for (int i = 2; i < 4; ++i) {
ibz_mul(&tmp1, &((*m1)[m1j][i]), &((*m2)[m2j][i]));
ibz_add(&tmp2, &tmp2, &tmp1);
}
ibz_mul(&tmp2, &tmp2, q);
ibz_add(mul, mul, &tmp2);
ibz_finalize(&tmp1);
ibz_finalize(&tmp2);
}
static void dotproduct_zr_row(ibq_t *mul, const ibz_mat_4x4_t *m1, const ibq_t (*m2)[4][4], const ibz_t *q, int m1j, int m2j) {
ibq_set(mul, &ibz_const_zero, &ibz_const_one);
ibq_t tmp1, tmp2;
ibq_init(&tmp1);
ibq_init(&tmp2);
for (int i = 0; i < 2; ++i) {
ibq_set(&tmp1, &((*m1)[m1j][i]), &ibz_const_one);
ibq_mul(&tmp1, &tmp1, &((*m2)[m2j][i]));
ibq_add(mul, mul, &tmp1);
}
for (int i = 2; i < 4; ++i) {
ibq_set(&tmp1, &((*m1)[m1j][i]), &ibz_const_one);
ibq_mul(&tmp1, &tmp1, &((*m2)[m2j][i]));
ibq_add(&tmp2, &tmp2, &tmp1);
}
ibq_set(&tmp1, q, &ibz_const_one);
ibq_mul(&tmp2, &tmp2, &tmp1);
ibq_add(mul, mul, &tmp2);
ibq_finalize(&tmp1);
ibq_finalize(&tmp2);
}
static void dotproduct_rr_row(ibq_t *mul, const ibq_t (*m1)[4][4], const ibq_t (*m2)[4][4], const ibz_t *q, int m1j, int m2j) {
//ibq_set(mul, 0);
ibq_set(mul, &ibz_const_zero, &ibz_const_one);
ibq_t tmp1, tmp2;
ibq_init(&tmp1);
ibq_init(&tmp2);
for (int i = 0; i < 2; ++i) {
ibq_mul(&tmp1, &((*m1)[m1j][i]), &((*m2)[m2j][i]));
ibq_add(mul, mul, &tmp1);
}
for (int i = 2; i < 4; ++i) {
ibq_mul(&tmp1, &((*m1)[m1j][i]), &((*m2)[m2j][i]));
ibq_add(&tmp2, &tmp2, &tmp1);
}
ibq_set(&tmp1, q, &ibz_const_one);
ibq_mul(&tmp2, &tmp2, &tmp1);
ibq_add(mul, mul, &tmp2);
ibq_finalize(&tmp1);
ibq_finalize(&tmp2);
}
static void mul_row(ibq_t (*mul)[4][4], const ibq_t *a, const ibq_t (*m)[4][4], int j) {
for (int i = 0; i < 4; ++i) {
ibq_mul(&((*mul)[j][i]), a, &((*m)[j][i]));
}
}
static void add_row(ibz_mat_4x4_t *add, const ibz_mat_4x4_t *a, const ibz_mat_4x4_t *b, int j, int aj, int bj) {
for (int i = 0; i < 4; ++i) {
ibz_add(&((*add)[j][i]), &((*a)[aj][i]), &((*b)[bj][i]));
}
}
static void sub_row(ibq_t (*add)[4][4], const ibq_t (*a)[4][4], const ibq_t (*b)[4][4], int j, int aj, int bj) {
for (int i = 0; i < 4; ++i) {
ibq_sub(&((*add)[j][i]), &((*a)[aj][i]), &((*b)[bj][i]));
}
}
/// @brief LLL reduction on 4-dimensional lattice
/// Implements Algorithm 2.6.3 from Henri Cohen's "A Course in Computational Algebraic Number Theory"
/// @param red
/// @param lattice
/// @return
int quat_lattice_lll(ibz_mat_4x4_t *red, const quat_lattice_t *lattice, const ibz_t *q, int precision) {
(void) precision;
int ret = 0;
ibz_mat_4x4_t basis;
ibq_t bStar[4][4];
ibq_t bStar_tmp[4][4];
ibq_t tmp;
ibz_t tmp_z;
ibz_t den;
ibz_t num;
ibq_t cnst;
ibq_t u[4][4];
ibz_t H[4][4]; // -> I_4
ibq_t B[4];
ibq_init(&tmp);
ibz_init(&tmp_z);
ibz_init(&den);
ibz_init(&num);
ibq_init(&cnst);
for (int i = 0; i < 4; ++i)
ibq_init(&(B[i]));
ibz_mat_4x4_init(&basis);
ibz_mat_4x4_transpose(&basis, &(lattice->basis));
// Step 1: Initialize: ...
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
ibq_init(&(u[i][j]));
ibq_init(&(bStar[i][j]));
ibq_init(&(bStar_tmp[i][j]));
// bSTAR_1 = b_1 (we copy all)
if (i == j){
ibz_init(&(H[i][j]));
ibz_set(&(H[i][j]), 1);
}
else {
ibz_init(&(H[i][j]));
}
}
}
int k = 1, kmax = 0;
// bStar_1 = b_1
for (int i = 0; i < 4; ++i)
ibq_set(&(bStar[0][i]), &(basis[0][i]), &ibz_const_one);
// B_1 = b_1 * b_1
dotproduct_row(&tmp_z, &basis, &basis, q, 0, 0);
ibq_set(&(B[0]), &tmp_z, &ibz_const_one);
ibz_set(&num,99);
ibz_set(&den,100);
ibq_set(&cnst,&num,&den);
while (k < 4) {
// Step 2: Incremental Gram-Schmidt
// if (k <= kmax) -> we can omit..
if (k > kmax) {
kmax = k;
for (int i = 0; i < 4; ++i) {
ibq_set(&(bStar[k][i]), &(basis[k][i]), &ibz_const_one);
}
for (int j = 0; j <= k-1; ++j) {
// bStar_k = b_k -> already done initially
// nop
// u_{k,j} = b_k*bSTAR_j/B_j
dotproduct_zr_row(&tmp, &basis, &bStar, q, k, j);
ibq_div(&(u[k][j]), &tmp, &(B[j]));
// bStar_k = bStar_k - u_{k,j}*bStar_j
mul_row(&bStar_tmp, &(u[k][j]), &bStar, j);
sub_row(&bStar, &bStar, &bStar_tmp, k, k, j);
}
// B_k = bStar_k*bStar_k
dotproduct_rr_row(&(B[k]), &bStar, &bStar, q, k, k);
if (ibq_is_zero(&(B[k]))) {
// b_i did not form a basis, terminate with error
ret = -1;
goto err;
}
}
while(1) {
// Step 3: Test LLL condition
RED(&basis, &u, &H, k, k - 1);
// If B_k < (0.75 - u_{k,k-1}^2)*B_{k-1}
ibq_mul(&tmp, &(u[k][k-1]), &(u[k][k-1]));
ibq_sub(&tmp, &cnst, &tmp);
ibq_mul(&tmp, &tmp, &(B[k-1]));
if (ibq_cmp(&(B[k]), &tmp) < 0) {
SWAP(&basis, &u, &H, &B, &bStar, k, kmax);
k = (k - 1 > 1 ? k - 1 : 1);
} else {
for (int l = k - 2; l >= 0; --l) {
RED(&basis, &u, &H, k, l);
}
k++;
break;
}
}
}
ibz_mat_4x4_transpose(red, &basis);
err:
ibq_finalize(&tmp);
ibz_finalize(&tmp_z);
ibz_finalize(&num);
ibz_finalize(&den);
ibq_finalize(&cnst);
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
ibq_finalize(&(u[i][j]));
ibz_finalize(&(H[i][j]));
ibq_finalize(&(bStar[i][j]));
ibq_finalize(&(bStar_tmp[i][j]));
}
}
for (int i = 0; i < 4; ++i)
ibq_finalize(&(B[i]));
ibz_mat_4x4_finalize(&basis);
return ret;
}

View 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);
}

View File

@@ -0,0 +1,158 @@
#include <quaternion.h>
void ibz_mat_2x2_print(const ibz_mat_2x2_t *mat){
ibz_printf("matrix: ");
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
ibz_printf("%Zd ", &((*mat)[i][j]));
}
ibz_printf("\n ");
}
ibz_printf("\n");
}
void ibz_mat_4x4_print(const ibz_mat_4x4_t *mat){
ibz_printf("matrix: ");
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_printf("%Zd ", &((*mat)[i][j]));
}
ibz_printf("\n ");
}
ibz_printf("\n");
}
void ibz_mat_4x5_print(const ibz_mat_4x5_t *mat){
ibz_printf("matrix: ");
for(int i = 0; i < 4; i++){
for(int j = 0; j < 5; j++){
ibz_printf("%Zd ", &((*mat)[i][j]));
}
ibz_printf("\n ");
}
ibz_printf("\n");
}
void ibz_mat_4x8_print(const ibz_mat_4x8_t *mat){
ibz_printf("matrix: ");
for(int i = 0; i < 4; i++){
for(int j = 0; j < 8; j++){
ibz_printf("%Zd ", &((*mat)[i][j]));
}
ibz_printf("\n ");
}
ibz_printf("\n");
}
void ibz_mat_print(int rows, int cols, const ibz_t mat[rows][cols]){
ibz_printf("matrix: ");
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
ibz_printf("%Zd ", &mat[i][j]);
}
ibz_printf("\n ");
}
ibz_printf("\n");
}
void ibz_vec_2_print(const ibz_vec_2_t *vec){
ibz_printf("vector: ");
for(int i = 0; i < 2; i++){
ibz_printf("%Zd ", &((*vec)[i]));
}
ibz_printf("\n\n");
}
void ibz_vec_4_print(const ibz_vec_4_t *vec){
ibz_printf("vector: ");
for(int i = 0; i < 4; i++){
ibz_printf("%Zd ", &((*vec)[i]));
}
ibz_printf("\n\n");
}
void ibz_vec_5_print(const ibz_vec_5_t *vec){
ibz_printf("vector: ");
for(int i = 0; i < 5; i++){
ibz_printf("%Zd ", &((*vec)[i]));
}
ibz_printf("\n\n");
}
void quat_lattice_print(const quat_lattice_t *lat){
ibz_printf("lattice\n");
ibz_printf("denominator: %Zd\n",(lat->denom));
ibz_printf("basis: ");
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_printf("%Zd ", &((lat->basis)[i][j]));
}
ibz_printf("\n ");
}
ibz_printf("\n");
}
void quat_alg_print(const quat_alg_t *alg){
ibz_printf("quaternion algebra ramified at %Zd and infinity\n\n", &(alg->p));
}
void quat_alg_elem_print(const quat_alg_elem_t *elem){
ibz_printf("denominator: %Zd\n",(elem->denom));
ibz_printf("coordinates: ");
for(int i = 0; i < 4; i++){
ibz_printf("%Zd ",&((elem->coord)[i]));
}
ibz_printf("\n\n");
}
void quat_alg_coord_print(const quat_alg_coord_t *coord){
ibz_printf("coordinates: ");
for(int i = 0; i < 4; i++){
ibz_printf("%Zd ",&((*coord)[i]));
}
ibz_printf("\n\n");
}
void quat_order_print(const quat_order_t *order){
ibz_printf("order\n");
ibz_printf("denominator: %Zd\n",&(order->denom));
ibz_printf("basis: ");
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_printf("%Zd ", &((order->basis)[i][j]));
}
ibz_printf("\n ");
}
ibz_printf("\n");
}
void quat_left_ideal_print(const quat_left_ideal_t *lideal){
ibz_printf("left ideal\n");
ibz_printf("norm : %Zd\n",&(lideal->norm));
ibz_printf("denominator: %Zd\n",&(lideal->lattice.denom));
ibz_printf("basis: ");
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_printf("%Zd ", &((lideal->lattice.basis)[i][j]));
}
if(i!=3){
ibz_printf("\n ");
} else {
ibz_printf("\n");
}
}
if((lideal->parent_order )!= NULL){
ibz_printf("parent order denominator: %Zd\n",&(lideal->parent_order->denom));
ibz_printf("parent order basis: ");
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_printf("%Zd ", &((lideal->parent_order->basis)[i][j]));
}
ibz_printf("\n ");
}
} else {
ibz_printf("Parent order not given!\n");
}
ibz_printf("\n");
}

View File

@@ -0,0 +1,21 @@
set(SOURCE_FILES_QUATERNION_GENERIC_REF_TESTS
algebra.c
ideal.c
dim4.c
dim2.c
integers.c
lattice.c
finit.c
matkermod.c
randomized.c
test_quaternions.c
)
add_executable(sqisign_test_quaternion ${SOURCE_FILES_QUATERNION_GENERIC_REF_TESTS})
target_link_libraries(sqisign_test_quaternion ${LIB_INTBIG} ${LIB_QUATERNION} ${GMP} sqisign_common_sys )
target_include_directories(sqisign_test_quaternion PRIVATE ${INC_INTBIG} ${INC_COMMON} ${INC_QUATERNION} ${INC_PUBLIC} )
# MSAN and GMP lead to false positives, see
# https://gmplib.org/list-archives/gmp-bugs/2019-March/004529.html
if(NOT CMAKE_BUILD_TYPE STREQUAL "MSAN")
add_test(sqisign_test_quaternion sqisign_test_quaternion)
endif()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,299 @@
#include "quaternion_tests.h"
// Schema of tests: initialize structure, assign values, finalize
//void quat_alg_init(quat_alg_t *alg);
//void quat_alg_finalize(quat_alg_t *alg);
int quat_test_finit_alg(){
int res = 0;
quat_alg_t alg;
ibz_t p;
ibz_mat_4x4_t cmp;
ibz_mat_4x4_init(&cmp);
ibz_init(&p);
ibz_set(&p,7);
quat_alg_init_set(&alg, &p);
res = res || ibz_cmp(&(alg.p),&p);
ibz_mat_4x4_identity(&cmp);
ibz_copy(&(cmp[2][2]),&p);
ibz_copy(&(cmp[3][3]),&p);
res = res || !ibz_mat_4x4_equal(&cmp,&(alg.gram));
if (res != 0){
printf("Quaternion unit test finit_alg failed\n");
}
ibz_mat_4x4_finalize(&cmp);
ibz_finalize(&p);
quat_alg_finalize(&alg);
return res;
}
//void quat_alg_elem_init(quat_alg_elem_t *elem);
//void quat_alg_elem_finalize(quat_alg_elem_t *elem);
int quat_test_finit_alg_elem(){
quat_alg_elem_t elem;
int res;
quat_alg_elem_init(&elem);
ibz_set(&(elem.coord[0]),0);
ibz_set(&(elem.coord[1]),1);
ibz_set(&(elem.coord[2]),2);
ibz_set(&(elem.coord[3]),3);
ibz_set(&(elem.denom),1);
res = 1-(1==ibz_is_one(&(elem.denom)));
for(int i = 0; i <4; i++){
res = res || (i!=ibz_get(&(elem.coord[i])));
}
if (res != 0){
printf("Quaternion unit test finit_alg_elem failed\n");
}
quat_alg_elem_finalize(&elem);
return res;
}
//void quat_alg_coord_init(quat_alg_coord_t *coord);
//void quat_alg_coord_finalize(quat_alg_coord_t *coord);
int quat_test_finit_alg_coord(){
quat_alg_coord_t coord;
int res = 0;
quat_alg_coord_init(&coord);
ibz_set(&(coord[0]),0);
ibz_set(&(coord[1]),1);
ibz_set(&(coord[2]),2);
ibz_set(&(coord[3]),3);
for(int i = 0; i <4; i++){
res = res || (i!=ibz_get(&(coord[i])));
}
if (res != 0){
printf("Quaternion unit test finit_alg_coord failed\n");
}
quat_alg_coord_finalize(&coord);
return res;
}
//void ibz_vec_4_init(ibz_vec_4_t *vec);
//void ibz_vec_4_finalize(ibz_vec_4_t *vec);
int quat_test_finit_ibz_vec_4(){
ibz_vec_4_t vec;
int res = 0;
ibz_vec_4_init(&vec);
for(int i = 0; i <4; i++){
ibz_set(&(vec[i]),i);
}
for(int i = 0; i <4; i++){
res = res || (i!=ibz_get(&(vec[i])));
}
if (res != 0){
printf("Quaternion unit test finit_ibz_vec_4 failed\n");
}
ibz_vec_4_finalize(&vec);
return res;
}
//void ibz_vec_5_init(ibz_vec_5_t *vec);
//void ibz_vec_5_finalize(ibz_vec_5_t *vec);
int quat_test_finit_ibz_vec_5(){
ibz_vec_5_t vec;
int res = 0;
ibz_vec_5_init(&vec);
for(int i = 0; i <5; i++){
ibz_set(&(vec[i]),i);
}
for(int i = 0; i <5; i++){
res = res || (i!=ibz_get(&(vec[i])));
}
if (res != 0){
printf("Quaternion unit test finit_ibz_vec_5 failed\n");
}
ibz_vec_5_finalize(&vec);
return res;
}
//void ibz_mat_2x2_init(ibz_mat_2x2_t *mat);
//void ibz_mat_2x2_finalize(ibz_mat_2x2_t *mat);
int quat_test_finit_ibz_mat_2x2(){
ibz_mat_2x2_t mat;
int res = 0;
ibz_mat_2x2_init(&mat);
for (int i = 0; i < 2; i++){
for (int j = 0; j < 2; j++){
ibz_set(&(mat[i][j]),i+j);
}
}
for(int i = 0; i <2; i++){
for (int j = 0; j < 2; j++){
res = res || (i+j!=ibz_get(&(mat[i][j])));
}
}
if (res != 0){
printf("Quaternion unit test finit_ibz_mat_2x2 failed\n");
}
ibz_mat_2x2_finalize(&mat);
return res;
}
//void ibz_mat_4x4_init(ibz_mat_4x4_t *mat);
//void ibz_mat_4x4_finalize(ibz_mat_4x4_t *mat);
int quat_test_finit_ibz_mat_4x4(){
ibz_mat_4x4_t mat;
int res = 0;
ibz_mat_4x4_init(&mat);
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(mat[i][j]),i+j);
}
}
for(int i = 0; i <4; i++){
for (int j = 0; j < 4; j++){
res = res || (i+j!=ibz_get(&(mat[i][j])));
}
}
if (res != 0){
printf("Quaternion unit test finit_ibz_mat_4x4 failed\n");
}
ibz_mat_4x4_finalize(&mat);
return res;
}
//void ibz_mat_4x5_init(ibz_mat_4x5_t *mat);
//void ibz_mat_4x5_finalize(ibz_mat_4x5_t *mat);
int quat_test_finit_ibz_mat_4x5(){
ibz_mat_4x5_t mat;
int res = 0;
ibz_mat_4x5_init(&mat);
for (int i = 0; i < 4; i++){
for (int j = 0; j < 5; j++){
ibz_set(&(mat[i][j]),i+j);
}
}
for(int i = 0; i <4; i++){
for (int j = 0; j < 5; j++){
res = res || (i+j!=ibz_get(&(mat[i][j])));
}
}
if (res != 0){
printf("Quaternion unit test finit_ibz_mat_4x5 failed\n");
}
ibz_mat_4x5_finalize(&mat);
return res;
}
//void ibz_mat_4x8_init(ibz_mat_4x8_t *mat);
//void ibz_mat_4x8_finalize(ibz_mat_4x8_t *mat);
int quat_test_finit_ibz_mat_4x8(){
ibz_mat_4x8_t mat;
int res = 0;
ibz_mat_4x8_init(&mat);
for (int i = 0; i < 4; i++){
for (int j = 0; j < 8; j++){
ibz_set(&(mat[i][j]),i+j);
}
}
for(int i = 0; i <4; i++){
for (int j = 0; j < 8; j++){
res = res || (i+j!=ibz_get(&(mat[i][j])));
}
}
if (res != 0){
printf("Quaternion unit test finit_ibz_mat_4x8 failed\n");
}
ibz_mat_4x8_finalize(&mat);
return res;
}
//void quat_lattice_init(quat_lattice_t *lat);
//void quat_lattice_finalize(quat_lattice_t *lat);
int quat_test_finit_lattice(){
quat_lattice_t lat;
int res = 0;
quat_lattice_init(&lat);
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),i+j);
}
}
ibz_set(&(lat.denom),1);
res = 1-(1==ibz_is_one(&(lat.denom)));
for(int i = 0; i <4; i++){
for (int j = 0; j < 4; j++){
res = res || (i+j!=ibz_get(&(lat.basis[i][j])));
}
}
if (res != 0){
printf("Quaternion unit test finit_alg_lattice failed\n");
}
quat_lattice_finalize(&lat);
return res;
}
//void quat_order_init(quat_order_t *order);
//void quat_order_finalize(quat_order_t *order);
int quat_test_finit_order(){
quat_order_t order;
int res = 0;
quat_order_init(&order);
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(order.basis[i][j]),i+j);
}
}
ibz_set(&(order.denom),1);
res = 1-(1==ibz_is_one(&(order.denom)));
for(int i = 0; i <4; i++){
for (int j = 0; j < 4; j++){
res = res || (i+j!=ibz_get(&(order.basis[i][j])));
}
}
if (res != 0){
printf("Quaternion unit test finit_alg_order failed\n");
}
quat_order_finalize(&order);
return res;
}
//void quat_left_ideal_init(quat_left_ideal_t *lideal);
//void quat_left_ideal_finalize(quat_left_ideal_t *lideal);
int quat_test_finit_lideal(){
quat_left_ideal_t lideal;
int res = 0;
quat_left_ideal_init(&lideal);
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(lideal.lattice.basis[i][j]),i+j);
}
}
ibz_set(&(lideal.lattice.denom),1);
ibz_set(&(lideal.norm),5);
lideal.parent_order = NULL;
res = 1-(1==ibz_is_one(&(lideal.lattice.denom)));
for(int i = 0; i <4; i++){
for (int j = 0; j < 4; j++){
res = res || (i+j!=ibz_get(&(lideal.lattice.basis[i][j])));
}
}
res = res || (5!=ibz_get(&(lideal.norm)));
if (res != 0){
printf("Quaternion unit test finit_alg_lideal failed\n");
}
quat_left_ideal_finalize(&lideal);
return res;
}
// run all previous tests
int quat_test_finit(){
int res = 0;
printf("\nRunning quaternion tests of initializers and finalizers\n");
res = res | quat_test_finit_alg();
res = res | quat_test_finit_alg_elem();
res = res | quat_test_finit_alg_coord();
res = res | quat_test_finit_ibz_vec_4();
res = res | quat_test_finit_ibz_vec_5();
res = res | quat_test_finit_ibz_mat_2x2();
res = res | quat_test_finit_ibz_mat_4x4();
res = res | quat_test_finit_ibz_mat_4x5();
res = res | quat_test_finit_ibz_mat_4x8();
res = res | quat_test_finit_lattice();
res = res | quat_test_finit_order();
res = res | quat_test_finit_lideal();
return(res);
}

View File

@@ -0,0 +1,967 @@
#include "quaternion_tests.h"
//void quat_lideal_create_principal(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg);
int quat_test_lideal_create_principal(){
int res = 0;
// Example from https://github.com/SQISign/sqisign-nist/issues/38#issuecomment-1554585079
quat_alg_t alg;
quat_order_t lat;
quat_alg_elem_t gamma;
quat_left_ideal_t I;
quat_alg_init_set_ui(&alg, 367);
quat_order_init(&lat);
quat_alg_elem_init(&gamma);
quat_left_ideal_init(&I);
ibz_set(&lat.denom, 2);
ibz_set(&lat.basis[0][0], 2);
ibz_set(&lat.basis[1][1], 2);
ibz_set(&lat.basis[1][2], 1);
ibz_set(&lat.basis[2][2], 1);
ibz_set(&lat.basis[3][3], 1);
ibz_set(&lat.basis[0][3], 1);
ibz_set(&gamma.coord[0], 219);
ibz_set(&gamma.coord[1], 200);
ibz_set(&gamma.coord[2], 78);
ibz_set(&gamma.coord[3], -1);
quat_lideal_create_principal(&I, &gamma, &lat, &alg);
res |= I.parent_order != &lat;
res |= ibz_cmp_si(&I.norm, 2321156);
res |= ibz_cmp(&I.lattice.denom, &ibz_const_one);
res |= ibz_cmp_si(&I.lattice.basis[0][0], 1160578);
res |= ibz_cmp_si(&I.lattice.basis[1][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[2][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[1][1], 1160578);
res |= ibz_cmp_si(&I.lattice.basis[2][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][2], 310126);
res |= ibz_cmp_si(&I.lattice.basis[1][2], 182529);
res |= ibz_cmp_si(&I.lattice.basis[2][2], 1);
res |= ibz_cmp_si(&I.lattice.basis[3][2], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][3], 978049);
res |= ibz_cmp_si(&I.lattice.basis[1][3], 310126);
res |= ibz_cmp_si(&I.lattice.basis[2][3], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][3], 1);
// same test, just with gamma not reduced
ibz_set(&gamma.coord[0], 438);
ibz_set(&gamma.coord[1], 400);
ibz_set(&gamma.coord[2], 156);
ibz_set(&gamma.coord[3], -2);
ibz_set(&gamma.denom, 2);
quat_lideal_create_principal(&I, &gamma, &lat, &alg);
res |= I.parent_order != &lat;
res |= ibz_cmp_si(&I.norm, 2321156);
res |= ibz_cmp(&I.lattice.denom, &ibz_const_one);
res |= ibz_cmp_si(&I.lattice.basis[0][0], 1160578);
res |= ibz_cmp_si(&I.lattice.basis[1][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[2][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[1][1], 1160578);
res |= ibz_cmp_si(&I.lattice.basis[2][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][2], 310126);
res |= ibz_cmp_si(&I.lattice.basis[1][2], 182529);
res |= ibz_cmp_si(&I.lattice.basis[2][2], 1);
res |= ibz_cmp_si(&I.lattice.basis[3][2], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][3], 978049);
res |= ibz_cmp_si(&I.lattice.basis[1][3], 310126);
res |= ibz_cmp_si(&I.lattice.basis[2][3], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][3], 1);
// same test, just with gamma and basis not reduced
ibz_set(&lat.denom, 6);
ibz_set(&lat.basis[0][0], 6);
ibz_set(&lat.basis[1][1], 6);
ibz_set(&lat.basis[1][2], 3);
ibz_set(&lat.basis[2][2], 3);
ibz_set(&lat.basis[3][3], 3);
ibz_set(&lat.basis[0][3], 3);
ibz_set(&gamma.coord[0], 438);
ibz_set(&gamma.coord[1], 400);
ibz_set(&gamma.coord[2], 156);
ibz_set(&gamma.coord[3], -2);
ibz_set(&gamma.denom, 2);
quat_lideal_create_principal(&I, &gamma, &lat, &alg);
res |= I.parent_order != &lat;
res |= ibz_cmp_si(&I.norm, 2321156);
res |= ibz_cmp(&I.lattice.denom, &ibz_const_one);
res |= ibz_cmp_si(&I.lattice.basis[0][0], 1160578);
res |= ibz_cmp_si(&I.lattice.basis[1][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[2][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[1][1], 1160578);
res |= ibz_cmp_si(&I.lattice.basis[2][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][2], 310126);
res |= ibz_cmp_si(&I.lattice.basis[1][2], 182529);
res |= ibz_cmp_si(&I.lattice.basis[2][2], 1);
res |= ibz_cmp_si(&I.lattice.basis[3][2], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][3], 978049);
res |= ibz_cmp_si(&I.lattice.basis[1][3], 310126);
res |= ibz_cmp_si(&I.lattice.basis[2][3], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][3], 1);
quat_alg_finalize(&alg);
quat_order_finalize(&lat);
quat_alg_elem_finalize(&gamma);
quat_left_ideal_finalize(&I);
if (res != 0){
printf("Quaternion unit test lideal_create_principal failed\n");
}
return(res);
}
//void quat_lideal_create_from_primitive(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg);
int quat_test_lideal_create_from_primitive(){
int res = 0;
// Example from https://github.com/SQISign/sqisign-nist/issues/38#issuecomment-1554585079
quat_alg_t alg;
quat_order_t lat;
quat_alg_elem_t gamma;
ibz_t N;
quat_left_ideal_t I;
quat_alg_init_set_ui(&alg, 367);
quat_order_init(&lat);
quat_alg_elem_init(&gamma);
ibz_init(&N);
quat_left_ideal_init(&I);
ibz_set(&lat.denom, 2);
ibz_set(&lat.basis[0][0], 2);
ibz_set(&lat.basis[1][1], 2);
ibz_set(&lat.basis[1][2], 1);
ibz_set(&lat.basis[2][2], 1);
ibz_set(&lat.basis[3][3], 1);
ibz_set(&lat.basis[0][3], 1);
ibz_set(&gamma.coord[0], 219);
ibz_set(&gamma.coord[1], 200);
ibz_set(&gamma.coord[2], 78);
ibz_set(&gamma.coord[3], -1);
ibz_set(&N, 31);
quat_lideal_create_from_primitive(&I, &gamma, &N, &lat, &alg);
res |= I.parent_order != &lat;
res |= ibz_cmp(&I.norm, &N);
res |= ibz_cmp(&I.lattice.denom, &ibz_const_two);
res |= ibz_cmp_si(&I.lattice.basis[0][0], 62);
res |= ibz_cmp_si(&I.lattice.basis[1][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[2][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][0], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[1][1], 62);
res |= ibz_cmp_si(&I.lattice.basis[2][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][1], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][2], 2);
res |= ibz_cmp_si(&I.lattice.basis[1][2], 1);
res |= ibz_cmp_si(&I.lattice.basis[2][2], 1);
res |= ibz_cmp_si(&I.lattice.basis[3][2], 0);
res |= ibz_cmp_si(&I.lattice.basis[0][3], 61);
res |= ibz_cmp_si(&I.lattice.basis[1][3], 2);
res |= ibz_cmp_si(&I.lattice.basis[2][3], 0);
res |= ibz_cmp_si(&I.lattice.basis[3][3], 1);
quat_alg_finalize(&alg);
quat_order_finalize(&lat);
quat_alg_elem_finalize(&gamma);
ibz_finalize(&N);
quat_left_ideal_finalize(&I);
if (res != 0){
printf("Quaternion unit test lideal_create_from_primitive failed\n");
}
return(res);
}
//void quat_lideal_make_primitive_then_create(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg);
int quat_test_lideal_make_primitive_then_create(){
int res = 0;
ibz_t n, cnt;
quat_alg_t alg;
quat_alg_elem_t gen, x;
quat_alg_coord_t prim;
quat_order_t order;
quat_left_ideal_t lideal,cmp;
quat_left_ideal_init(&lideal);
quat_left_ideal_init(&cmp);
quat_alg_init_set_ui(&alg, 103);
quat_alg_elem_init(&gen);
quat_alg_elem_init(&x);
quat_alg_coord_init(&prim);
quat_order_init(&order);
ibz_init(&n);
ibz_init(&cnt);
ibz_set(&order.denom, 2);
ibz_set(&order.basis[0][0], 2);
ibz_set(&order.basis[1][1], 2);
ibz_set(&order.basis[1][2], 1);
ibz_set(&order.basis[2][2], 1);
ibz_set(&order.basis[3][3], 1);
ibz_set(&order.basis[0][3], 1);
ibz_set(&x.coord[0], 6);
ibz_set(&x.coord[1], 10);
ibz_set(&x.coord[2], 14);
ibz_set(&x.coord[3], 22);
ibz_set(&n, 17);
quat_lideal_make_primitive_then_create(&lideal,&x,&n,&order,&alg);
quat_alg_make_primitive(&prim,&cnt,&x,&order,&alg);
ibz_mat_4x4_eval(&(gen.coord),&(order.basis),&prim);
ibz_copy(&gen.denom,&order.denom);
quat_lideal_create_from_primitive(&cmp,&gen,&n,&order,&alg);
res = res || !quat_lideal_equals(&lideal,&cmp,&alg);
if (res != 0){
printf("Quaternion unit test lideal_make_primitive_then_create failed\n");
}
quat_alg_finalize(&alg);
quat_alg_elem_finalize(&gen);
quat_alg_elem_finalize(&x);
quat_alg_coord_finalize(&prim);
quat_order_finalize(&order);
ibz_finalize(&n);
ibz_finalize(&cnt);
quat_left_ideal_finalize(&lideal);
quat_left_ideal_finalize(&cmp);
return(res);
}
//void quat_lideal_random_2e(quat_left_ideal_t *lideal, const quat_order_t *order, const quat_alg_t *alg, int64_t e, unsigned char n);
int quat_test_lideal_random_2e(){
int res = 0;
quat_alg_t alg;
quat_order_t order;
quat_left_ideal_t lideal;
quat_alg_init_set_ui(&alg, 103);
quat_order_init(&order);
quat_left_ideal_init(&lideal);
ibz_set(&order.denom, 2);
ibz_set(&order.basis[0][0], 2);
ibz_set(&order.basis[1][1], 2);
ibz_set(&order.basis[1][2], 1);
ibz_set(&order.basis[2][2], 1);
ibz_set(&order.basis[3][3], 1);
ibz_set(&order.basis[0][3], 1);
// Pretty much self-testing (thanks to embedded asserts)
res |= quat_lideal_random_2e(&lideal, &order, &alg, 100, 10) == 0;
quat_alg_finalize(&alg);
quat_order_finalize(&order);
quat_left_ideal_finalize(&lideal);
if (res != 0){
printf("Quaternion unit test lideal_random_2e failed\n");
}
return(res);
}
//int quat_lideal_generator(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const quat_alg_t *alg, int bound)
int quat_test_lideal_generator(){
int res = 0;
quat_alg_t alg;
quat_order_t order;
quat_alg_elem_t gen;
ibz_t N;
quat_left_ideal_t lideal, lideal2;
quat_alg_init_set_ui(&alg, 103);
quat_order_init(&order);
quat_alg_elem_init(&gen);
ibz_init(&N);
quat_left_ideal_init(&lideal);
quat_left_ideal_init(&lideal2);
ibz_set(&order.denom, 2);
ibz_set(&order.basis[0][0], 2);
ibz_set(&order.basis[1][1], 2);
ibz_set(&order.basis[1][2], 1);
ibz_set(&order.basis[2][2], 1);
ibz_set(&order.basis[3][3], 1);
ibz_set(&order.basis[0][3], 1);
ibz_set(&gen.coord[0], 3);
ibz_set(&gen.coord[1], 5);
ibz_set(&gen.coord[2], 7);
ibz_set(&gen.coord[3], 11);
ibz_set(&N, 17);
quat_lideal_create_from_primitive(&lideal, &gen, &N, &order, &alg);
// Try to regenerate the same ideal
for (int i = 0; i <= 10; i++) {
res |= !quat_lideal_generator(&gen, &lideal, &alg,0);
quat_lideal_create_from_primitive(&lideal2, &gen, &N, &order, &alg);
res |= !quat_lideal_equals(&lideal, &lideal2, &alg);
}
quat_alg_finalize(&alg);
quat_order_finalize(&order);
quat_alg_elem_finalize(&gen);
ibz_finalize(&N);
quat_left_ideal_finalize(&lideal);
quat_left_ideal_finalize(&lideal2);
if (res != 0){
printf("Quaternion unit test lideal_generator failed\n");
}
return(res);
}
//int quat_lideal_generator_coprime(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const ibz_t *n, const quat_alg_t *alg, int bound);
int quat_test_lideal_generator_coprime(){
int res = 0;
quat_alg_t alg;
quat_order_t order, order2;
quat_alg_elem_t gen;
ibz_t N, M, gcd, norm_int, prod;
ibq_t norm;
quat_left_ideal_t lideal, lideal2;
quat_alg_init_set_ui(&alg, 103);
quat_order_init(&order);
quat_order_init(&order2);
quat_alg_elem_init(&gen);
ibz_init(&N);
ibz_init(&M);
ibz_init(&norm_int);
ibz_init(&prod);
ibz_init(&gcd);
ibq_init(&norm);
quat_left_ideal_init(&lideal);
quat_left_ideal_init(&lideal2);
ibz_set(&order.denom, 2);
ibz_set(&order.basis[0][0], 2);
ibz_set(&order.basis[1][1], 2);
ibz_set(&order.basis[1][2], 1);
ibz_set(&order.basis[2][2], 1);
ibz_set(&order.basis[3][3], 1);
ibz_set(&order.basis[0][3], 1);
ibz_set(&gen.coord[0], 3);
ibz_set(&gen.coord[1], 5);
ibz_set(&gen.coord[2], 7);
ibz_set(&gen.coord[3], 11);
ibz_set(&N, 5);
// product of primes < 50
ibz_set(&M, 614889782588491410l);
quat_lideal_create_from_primitive(&lideal, &gen, &N, &order, &alg);
res |= !quat_lideal_generator_coprime(&gen, &lideal, &M, &alg,0);
quat_alg_norm(&norm, &gen, &alg);
res |= !ibq_to_ibz(&norm_int, &norm);
// case with coprimality
ibz_gcd(&gcd, &norm_int, &M);
res |= !ibz_is_one(&gcd);
// test other formula
ibz_mul(&prod,&M,&M);
ibz_gcd(&prod,&prod,&norm_int);
ibz_gcd(&gcd, &(lideal.norm), &M);
res |= ibz_cmp(&gcd,&prod);
quat_lideal_create_from_primitive(&lideal2, &gen, &N, &order, &alg);
res |= !quat_lideal_equals(&lideal, &lideal2, &alg);
res |= !quat_alg_is_primitive(&gen,&(lideal.lattice), &alg);
ibz_set(&(order.denom),1);
quat_alg_elem_set(&gen,1,2,4,2,6);
quat_lideal_create_from_primitive(&lideal, &gen, &N, &order, &alg);
ibz_set(&M,15);
res |= !quat_lideal_generator_coprime(&gen, &lideal, &M, &alg,0);
quat_alg_norm(&norm, &gen, &alg);
res |= !ibq_to_ibz(&norm_int, &norm);
// test other formula
ibz_mul(&prod,&M,&M);
ibz_gcd(&prod,&prod,&norm_int);
ibz_gcd(&gcd, &(lideal.norm), &M);
res |= ibz_cmp(&gcd,&prod);
quat_lideal_create_from_primitive(&lideal2, &gen, &N, &order, &alg);
res |= !quat_lideal_equals(&lideal, &lideal2, &alg);
res |= !quat_alg_is_primitive(&gen,&(lideal.lattice), &alg);
quat_alg_finalize(&alg);
quat_order_finalize(&order);
quat_order_finalize(&order2);
quat_alg_elem_finalize(&gen);
ibz_finalize(&N);
ibz_finalize(&M);
ibz_finalize(&norm_int);
ibz_finalize(&prod);
ibz_finalize(&gcd);
ibq_finalize(&norm);
quat_left_ideal_finalize(&lideal);
quat_left_ideal_finalize(&lideal2);
if (res != 0){
printf("Quaternion unit test lideal_generator_coprime failed\n");
}
return(res);
}
//int quat_lideal_mul(quat_left_ideal_t *product, const quat_left_ideal_t *lideal, const quat_alg_elem_t *alpha, const quat_alg_t *alg, int bound);
int quat_test_lideal_mul(){
int res = 0;
quat_alg_t alg;
quat_order_t order;
quat_alg_elem_t gen1, gen2, gen_prod;
quat_left_ideal_t lideal, lideal2;
quat_alg_init_set_ui(&alg, 103);
quat_order_init(&order);
quat_alg_elem_init(&gen1);
quat_alg_elem_init(&gen2);
quat_alg_elem_init(&gen_prod);
quat_left_ideal_init(&lideal);
quat_left_ideal_init(&lideal2);
ibz_set(&order.denom, 2);
ibz_set(&order.basis[0][0], 2);
ibz_set(&order.basis[1][1], 2);
ibz_set(&order.basis[1][2], 1);
ibz_set(&order.basis[2][2], 1);
ibz_set(&order.basis[3][3], 1);
ibz_set(&order.basis[0][3], 1);
ibz_set(&gen1.coord[0], 3);
ibz_set(&gen1.coord[1], 5);
ibz_set(&gen1.coord[2], 7);
ibz_set(&gen1.coord[3], 11);
ibz_set(&gen2.coord[0], -2);
ibz_set(&gen2.coord[1], 13);
ibz_set(&gen2.coord[2], -17);
ibz_set(&gen2.coord[3], 19);
// Check that (O·gen1)·gen2 == O·(gen1·gen2)
quat_lideal_create_principal(&lideal, &gen1, &order, &alg);
res |= !quat_lideal_mul(&lideal, &lideal, &gen2, &alg,0);
quat_alg_mul(&gen_prod, &gen1, &gen2, &alg);
quat_lideal_create_principal(&lideal2, &gen_prod, &order, &alg);
res |= !quat_lideal_equals(&lideal, &lideal2, &alg);
// Same test, but with non-integral gen2
ibz_set(&(gen2.denom),2);
quat_lideal_create_principal(&lideal, &gen1, &order, &alg);
res |= !quat_lideal_mul(&lideal, &lideal, &gen2, &alg,0);
quat_alg_mul(&gen_prod, &gen1, &gen2, &alg);
quat_lideal_create_principal(&lideal2, &gen_prod, &order, &alg);
res |= !quat_lideal_equals(&lideal, &lideal2, &alg);
quat_alg_finalize(&alg);
quat_order_finalize(&order);
quat_alg_elem_finalize(&gen1);
quat_alg_elem_finalize(&gen2);
quat_alg_elem_finalize(&gen_prod);
quat_left_ideal_finalize(&lideal);
quat_left_ideal_finalize(&lideal2);
if (res != 0){
printf("Quaternion unit test lideal_mul failed\n");
}
return(res);
}
//void quat_lideal_add(quat_left_ideal_t *sum, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
//void quat_lideal_inter(quat_left_ideal_t *intersection, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
//int quat_lideal_equals(const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
int quat_test_lideal_add_intersect_equals(){
int res = 0;
quat_alg_t alg;
quat_order_t order;
quat_alg_elem_t gen1, gen2, gen3;
ibz_t N1, N2, N3;
quat_left_ideal_t lideal1, lideal2, lideal3, lideal4, lideal5;
quat_alg_init_set_ui(&alg, 103);
quat_order_init(&order);
quat_alg_elem_init(&gen1);
quat_alg_elem_init(&gen2);
quat_alg_elem_init(&gen3);
ibz_init(&N1);
ibz_init(&N2);
ibz_init(&N3);
quat_left_ideal_init(&lideal1);
quat_left_ideal_init(&lideal2);
quat_left_ideal_init(&lideal3);
quat_left_ideal_init(&lideal4);
quat_left_ideal_init(&lideal5);
ibz_set(&order.denom, 2);
ibz_set(&order.basis[0][0], 2);
ibz_set(&order.basis[1][1], 2);
ibz_set(&order.basis[1][2], 1);
ibz_set(&order.basis[2][2], 1);
ibz_set(&order.basis[3][3], 1);
ibz_set(&order.basis[0][3], 1);
ibz_set(&gen1.coord[0], 3);
ibz_set(&gen1.coord[1], 5);
ibz_set(&gen1.coord[2], 7);
ibz_set(&gen1.coord[3], 11);
ibz_set(&N1, 17);
quat_lideal_create_from_primitive(&lideal1, &gen1, &N1, &order, &alg);
ibz_set(&gen2.coord[0], -2);
ibz_set(&gen2.coord[1], 13);
ibz_set(&gen2.coord[2], -17);
ibz_set(&gen2.coord[3], 19);
ibz_set(&N2, 43);
quat_lideal_create_from_primitive(&lideal2, &gen2, &N2, &order, &alg);
quat_alg_mul(&gen3, &gen2, &gen1, &alg);
quat_lideal_create_principal(&lideal3, &gen3, &order, &alg);
// Union should be the whole ring
quat_lideal_add(&lideal4, &lideal1, &lideal2, &alg);
res |= !ibz_is_one(&lideal4.norm);
res |= !quat_lattice_equal(&lideal4.lattice, &order);
// Self-intersection should be stable
quat_lideal_inter(&lideal4, &lideal1, &lideal1, &alg);
res |= !quat_lideal_equals(&lideal4, &lideal1, &alg);
// Self-union should be stable
quat_lideal_add(&lideal4, &lideal1, &lideal1, &alg);
res |= !quat_lideal_equals(&lideal4, &lideal1, &alg);
// lideal3 ⊂ lideal1
quat_lideal_add(&lideal4, &lideal1, &lideal3, &alg);
res |= !quat_lideal_equals(&lideal4, &lideal1, &alg);
quat_lideal_inter(&lideal4, &lideal1, &lideal3, &alg);
res |= !quat_lideal_equals(&lideal4, &lideal3, &alg);
// Intersection then union should be stable
quat_lideal_inter(&lideal4, &lideal1, &lideal2, &alg);
quat_lideal_add(&lideal4, &lideal4, &lideal2, &alg);
res |= !quat_lideal_equals(&lideal4, &lideal2, &alg);
// (A ∩ B) (A ∩ C) == A ∩ (B C)
quat_lideal_inter(&lideal4, &lideal1, &lideal2, &alg);
quat_lideal_inter(&lideal5, &lideal1, &lideal3, &alg);
quat_lideal_add(&lideal4, &lideal4, &lideal5, &alg);
quat_lideal_add(&lideal5, &lideal2, &lideal3, &alg);
quat_lideal_inter(&lideal5, &lideal1, &lideal5, &alg);
res |= !quat_lideal_equals(&lideal4, &lideal5, &alg);
res |= ibz_cmp_si(&lideal4.norm, 17);
quat_alg_finalize(&alg);
quat_order_finalize(&order);
quat_alg_elem_finalize(&gen1);
quat_alg_elem_finalize(&gen2);
quat_alg_elem_finalize(&gen3);
ibz_finalize(&N1);
ibz_finalize(&N2);
ibz_finalize(&N3);
quat_left_ideal_finalize(&lideal1);
quat_left_ideal_finalize(&lideal2);
quat_left_ideal_finalize(&lideal3);
quat_left_ideal_finalize(&lideal4);
quat_left_ideal_finalize(&lideal5);
if (res != 0){
printf("Quaternion unit test lideal_add_intersect_equals failed\n");
}
return(res);
}
//int quat_lideal_isom(quat_alg_elem_t *iso, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
int quat_test_lideal_isom(){
int res = 0;
quat_left_ideal_t lideal1,lideal2,prod;
ibz_t norm;
quat_alg_elem_t init_helper, isom;
quat_order_t order, order2;
quat_alg_t alg;
ibz_init(&norm);
quat_left_ideal_init(&lideal1);
quat_left_ideal_init(&lideal2);
quat_left_ideal_init(&prod);
quat_alg_init_set_ui(&alg,19);
quat_order_init(&order);
quat_order_init(&order2);
quat_alg_elem_init(&isom);
quat_alg_elem_init(&init_helper);
ibz_set(&(order.basis[0][0]),4);
ibz_set(&(order.basis[0][1]),0);
ibz_set(&(order.basis[0][2]),2);
ibz_set(&(order.basis[0][3]),2);
ibz_set(&(order.basis[1][0]),0);
ibz_set(&(order.basis[1][1]),8);
ibz_set(&(order.basis[1][2]),4);
ibz_set(&(order.basis[1][3]),3);
ibz_set(&(order.basis[2][0]),0);
ibz_set(&(order.basis[2][1]),0);
ibz_set(&(order.basis[2][2]),2);
ibz_set(&(order.basis[2][3]),0);
ibz_set(&(order.basis[3][0]),0);
ibz_set(&(order.basis[3][1]),0);
ibz_set(&(order.basis[3][2]),0);
ibz_set(&(order.basis[3][3]),1);
ibz_set(&(order.denom),4);
quat_alg_elem_set(&init_helper,1,2,6,2,1);
quat_lideal_create_principal(&lideal1,&init_helper,&order,&alg);
quat_alg_elem_set(&init_helper,3,1,6,5,1);
res |= !quat_lideal_mul(&lideal2,&lideal1,&init_helper,&alg,0);
res = res || !quat_lideal_isom(&isom,&lideal1,&lideal2,&alg);
if(!res){
res |= !quat_lideal_mul(&prod,&lideal1,&isom,&alg,0);
res = res || !quat_lideal_equals(&prod,&lideal2,&alg);
}
// not same order
ibz_set(&(order2.basis[0][0]),2);
ibz_set(&(order2.basis[0][1]),0);
ibz_set(&(order2.basis[0][2]),1);
ibz_set(&(order2.basis[0][3]),0);
ibz_set(&(order2.basis[1][0]),0);
ibz_set(&(order2.basis[1][1]),2);
ibz_set(&(order2.basis[1][2]),0);
ibz_set(&(order2.basis[1][3]),1);
ibz_set(&(order2.basis[2][0]),0);
ibz_set(&(order2.basis[2][1]),0);
ibz_set(&(order2.basis[2][2]),1);
ibz_set(&(order2.basis[2][3]),0);
ibz_set(&(order2.basis[3][0]),0);
ibz_set(&(order2.basis[3][1]),0);
ibz_set(&(order2.basis[3][2]),0);
ibz_set(&(order2.basis[3][3]),1);
ibz_set(&(order2.denom),2);
quat_alg_elem_set(&init_helper,1,2,6,2,1);
quat_lideal_create_principal(&lideal2,&init_helper,&order2,&alg);
res = res || quat_lideal_isom(&isom,&lideal1,&lideal2,&alg);
// should add test for not isomorphic ideals
if (res != 0){
printf("Quaternion unit test lideal_isom failed\n");
}
quat_left_ideal_finalize(&lideal1);
quat_left_ideal_finalize(&lideal2);
quat_left_ideal_finalize(&prod);
ibz_finalize(&norm);
quat_alg_finalize(&alg);
quat_order_finalize(&order);
quat_order_finalize(&order2);
quat_alg_elem_finalize(&isom);
quat_alg_elem_finalize(&init_helper);
return(res);
}
//void quat_lideal_right_order(quat_order_t *order, const quat_left_ideal_t *lideal, const quat_alg_t *alg);
int quat_test_lideal_right_order(){
int res = 0;
ibz_t norm;
quat_alg_t alg;
quat_alg_elem_t gen;
quat_order_t order,rorder;
quat_lattice_t prod;
quat_alg_elem_t test;
quat_left_ideal_t lideal, cmp;
quat_order_init(&order);
quat_order_init(&rorder);
quat_lattice_init(&prod);
quat_alg_init_set_ui(&alg,19);
quat_left_ideal_init(&lideal);
quat_alg_elem_init(&test);
quat_alg_elem_init(&gen);
ibz_set(&(order.basis[0][0]),4);
ibz_set(&(order.basis[0][1]),0);
ibz_set(&(order.basis[0][2]),2);
ibz_set(&(order.basis[0][3]),2);
ibz_set(&(order.basis[1][0]),0);
ibz_set(&(order.basis[1][1]),8);
ibz_set(&(order.basis[1][2]),4);
ibz_set(&(order.basis[1][3]),3);
ibz_set(&(order.basis[2][0]),0);
ibz_set(&(order.basis[2][1]),0);
ibz_set(&(order.basis[2][2]),2);
ibz_set(&(order.basis[2][3]),0);
ibz_set(&(order.basis[3][0]),0);
ibz_set(&(order.basis[3][1]),0);
ibz_set(&(order.basis[3][2]),0);
ibz_set(&(order.basis[3][3]),1);
ibz_set(&(order.denom),4);
quat_alg_elem_set(&gen,1,2,1,8,-8);
quat_lideal_create_principal(&lideal,&gen,&order,&alg);
quat_lideal_right_order(&rorder,&lideal,&alg);
// test order is in HNF
res = res || !ibz_mat_4x4_is_hnf(&(rorder.basis));
// test order is of dimension 4 (assuming HNF)
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(rorder.denom),&(rorder.basis[0][i]),&(rorder.basis[1][i]),&(rorder.basis[2][i]),&(rorder.basis[3][i]));
res = res || quat_alg_elem_is_zero(&test);
}
// test order contains 1
quat_alg_elem_set(&test,1,1,0,0,0);
res = res || !quat_lattice_contains(NULL,&rorder,&test,&alg);
// test it is right order of ideal
quat_lattice_mul(&prod,&(lideal.lattice),&rorder,&alg);
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(prod.denom),&(prod.basis[0][i]),&(prod.basis[1][i]),&(prod.basis[2][i]),&(prod.basis[3][i]));
res = res || !quat_lattice_contains(NULL,&(lideal.lattice),&test,&alg);
}
ibz_set(&(order.basis[0][0]),2);
ibz_set(&(order.basis[0][1]),0);
ibz_set(&(order.basis[0][2]),1);
ibz_set(&(order.basis[0][3]),0);
ibz_set(&(order.basis[1][0]),0);
ibz_set(&(order.basis[1][1]),2);
ibz_set(&(order.basis[1][2]),0);
ibz_set(&(order.basis[1][3]),1);
ibz_set(&(order.basis[2][0]),0);
ibz_set(&(order.basis[2][1]),0);
ibz_set(&(order.basis[2][2]),1);
ibz_set(&(order.basis[2][3]),0);
ibz_set(&(order.basis[3][0]),0);
ibz_set(&(order.basis[3][1]),0);
ibz_set(&(order.basis[3][2]),0);
ibz_set(&(order.basis[3][3]),1);
ibz_set(&(order.denom),2);
quat_alg_elem_set(&gen,1,1,2,8,8);
quat_lideal_create_principal(&lideal,&gen,&order,&alg);
quat_lideal_right_order(&rorder,&lideal,&alg);
quat_lattice_mul(&prod,&(lideal.lattice),&rorder,&alg);
// test order is in HNF
res = res || !ibz_mat_4x4_is_hnf(&(rorder.basis));
// test order is of dimension 4 (assuming HNF)
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(rorder.denom),&(rorder.basis[0][i]),&(rorder.basis[1][i]),&(rorder.basis[2][i]),&(rorder.basis[3][i]));
res = res || quat_alg_elem_is_zero(&test);
}
// test order contains 1
quat_alg_elem_set(&test,1,1,0,0,0);
res = res || !quat_lattice_contains(NULL,&rorder,&test,&alg);
// test it is right order of ideal
quat_lattice_mul(&prod,&(lideal.lattice),&rorder,&alg);
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(prod.denom),&(prod.basis[0][i]),&(prod.basis[1][i]),&(prod.basis[2][i]),&(prod.basis[3][i]));
res = res || !quat_lattice_contains(NULL,&(lideal.lattice),&test,&alg);
}
if (res != 0){
printf("Quaternion unit test lideal_right_order failed\n");
}
quat_alg_elem_finalize(&test);
quat_order_finalize(&order);
quat_order_finalize(&rorder);
quat_lattice_finalize(&prod);
quat_left_ideal_finalize(&lideal);
quat_alg_finalize(&alg);
quat_alg_elem_finalize(&gen);
return(res);
}
//void quat_lideal_reduce_basis(ibz_mat_4x4_t *reduced, ibz_mat_4x4_t *gram, const quat_left_ideal_t *lideal, const quat_alg_t *alg); //replaces lideal_lll
int quat_test_lideal_reduce_basis(){
int res = 0;
ibz_mat_4x4_t red, gram, prod;
quat_left_ideal_t lideal;
quat_lattice_t test;
quat_alg_t alg;
quat_alg_elem_t init_helper;
quat_order_t order;
ibq_t coeff;
ibz_t num,denom;
ibz_init(&num);
ibz_init(&denom);
ibq_init(&coeff);
ibz_mat_4x4_init(&prod);
quat_lattice_init(&test);
quat_alg_elem_init(&init_helper);
quat_order_init(&order);
quat_alg_init_set_ui(&alg,19);
ibz_mat_4x4_init(&gram);
ibz_mat_4x4_init(&red);
quat_left_ideal_init(&lideal);
ibz_mat_4x4_identity(&(order.basis));
ibz_set(&(order.denom),2);
quat_alg_elem_set(&init_helper,1,1,2,8,8);
quat_lideal_create_principal(&lideal,&init_helper,&order,&alg);
quat_lattice_reduce_denom(&(lideal.lattice),&(lideal.lattice));
quat_lideal_reduce_basis(&red,&gram,&lideal,&alg);
// test if red is lll-reduced, assuming he used coeff was at least 3/4
ibz_set(&num,99);
ibz_set(&denom,100);
ibq_set(&coeff,&num,&denom);
res = res || !quat_dim4_lll_verify(&red,&coeff,&(alg.p));
// test reduced and lideal generate same lattice
ibz_mat_4x4_copy(&(test.basis),&red);
ibz_copy(&(test.denom),&(lideal.lattice.denom));
quat_lattice_hnf(&test);
res = res || !quat_lattice_equal(&(lideal.lattice),&test);
// test gram matrix is gram matrix
ibz_mat_4x4_transpose(&prod,&red);
ibz_mat_4x4_mul(&prod,&prod,&(alg.gram));
ibz_mat_4x4_mul(&prod,&prod,&red);
res = res || !ibz_mat_4x4_equal(&prod,&gram);
if (res != 0){
printf("Quaternion unit test lideal_reduce_basis failed\n");
}
ibz_finalize(&num);
ibz_finalize(&denom);
ibq_finalize(&coeff);
quat_lattice_finalize(&test);
quat_alg_elem_finalize(&init_helper);
ibz_mat_4x4_finalize(&prod);
quat_order_finalize(&order);
quat_alg_finalize(&alg);
ibz_mat_4x4_finalize(&gram);
ibz_mat_4x4_finalize(&red);
quat_left_ideal_finalize(&lideal);
return(res);
}
/***************************** Functions from quaternion_tools.c ***************************************/
//void quat_connecting_ideal(quat_left_ideal_t *connecting_ideal, const quat_order_t *O1, const quat_order_t *O2, const quat_alg_t *alg);
int quat_test_lideal_connecting_ideal(){
int res = 0;
ibz_t norm;
quat_alg_t alg;
quat_alg_elem_t gen;
quat_order_t o1, o2;
quat_lattice_t prod;
quat_alg_elem_t test;
quat_left_ideal_t lideal, cmp;
quat_order_init(&o1);
quat_order_init(&o2);
quat_lattice_init(&prod);
quat_alg_init_set_ui(&alg,19);
quat_left_ideal_init(&lideal);
quat_alg_elem_init(&test);
ibz_set(&(o1.basis[0][0]),4);
ibz_set(&(o1.basis[0][1]),0);
ibz_set(&(o1.basis[0][2]),2);
ibz_set(&(o1.basis[0][3]),2);
ibz_set(&(o1.basis[1][0]),0);
ibz_set(&(o1.basis[1][1]),8);
ibz_set(&(o1.basis[1][2]),4);
ibz_set(&(o1.basis[1][3]),3);
ibz_set(&(o1.basis[2][0]),0);
ibz_set(&(o1.basis[2][1]),0);
ibz_set(&(o1.basis[2][2]),2);
ibz_set(&(o1.basis[2][3]),0);
ibz_set(&(o1.basis[3][0]),0);
ibz_set(&(o1.basis[3][1]),0);
ibz_set(&(o1.basis[3][2]),0);
ibz_set(&(o1.basis[3][3]),1);
ibz_set(&(o1.denom),4);
ibz_set(&(o2.basis[0][0]),2);
ibz_set(&(o2.basis[0][1]),0);
ibz_set(&(o2.basis[0][2]),1);
ibz_set(&(o2.basis[0][3]),0);
ibz_set(&(o2.basis[1][0]),0);
ibz_set(&(o2.basis[1][1]),2);
ibz_set(&(o2.basis[1][2]),0);
ibz_set(&(o2.basis[1][3]),1);
ibz_set(&(o2.basis[2][0]),0);
ibz_set(&(o2.basis[2][1]),0);
ibz_set(&(o2.basis[2][2]),1);
ibz_set(&(o2.basis[2][3]),0);
ibz_set(&(o2.basis[3][0]),0);
ibz_set(&(o2.basis[3][1]),0);
ibz_set(&(o2.basis[3][2]),0);
ibz_set(&(o2.basis[3][3]),1);
ibz_set(&(o2.denom),2);
quat_connecting_ideal(&lideal,&o1,&o2,&alg);
quat_lattice_mul(&prod,&o1,&(lideal.lattice),&alg);
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(prod.denom),&(prod.basis[0][i]),&(prod.basis[1][i]),&(prod.basis[2][i]),&(prod.basis[3][i]));
res = res || !quat_lattice_contains(NULL,&(lideal.lattice),&test,&alg);
}
quat_lattice_mul(&prod,&(lideal.lattice),&o2,&alg);
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(prod.denom),&(prod.basis[0][i]),&(prod.basis[1][i]),&(prod.basis[2][i]),&(prod.basis[3][i]));
res = res || !quat_lattice_contains(NULL,&(lideal.lattice),&test,&alg);
}
quat_connecting_ideal(&lideal,&o2,&o2,&alg);
quat_lattice_mul(&prod,&o2,&(lideal.lattice),&alg);
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(prod.denom),&(prod.basis[0][i]),&(prod.basis[1][i]),&(prod.basis[2][i]),&(prod.basis[3][i]));
res = res || !quat_lattice_contains(NULL,&(lideal.lattice),&test,&alg);
}
quat_lattice_mul(&prod,&(lideal.lattice),&o2,&alg);
for(int i = 0; i < 4; i++){
quat_alg_elem_copy_ibz(&test,&(prod.denom),&(prod.basis[0][i]),&(prod.basis[1][i]),&(prod.basis[2][i]),&(prod.basis[3][i]));
res = res || !quat_lattice_contains(NULL,&(lideal.lattice),&test,&alg);
}
if (res != 0){
printf("Quaternion unit test lideal_connecting_ideal failed\n");
}
quat_alg_elem_finalize(&test);
quat_order_finalize(&o1);
quat_order_finalize(&o2);
quat_lattice_finalize(&prod);
quat_left_ideal_finalize(&lideal);
quat_alg_finalize(&alg);
return(res);
}
// run all previous tests
int quat_test_lideal(){
int res = 0;
printf("\nRunning quaternion tests of ideal and order functions\n");
res = res | quat_test_lideal_create_principal();
res = res | quat_test_lideal_create_from_primitive();
res = res | quat_test_lideal_make_primitive_then_create();
res = res | quat_test_lideal_random_2e();
res = res | quat_test_lideal_generator();
res = res | quat_test_lideal_generator_coprime();
res = res | quat_test_lideal_mul();
res = res | quat_test_lideal_add_intersect_equals();
res = res | quat_test_lideal_isom();
res = res | quat_test_lideal_right_order();
res = res | quat_test_lideal_reduce_basis();
res = res | quat_test_lideal_connecting_ideal();
return(res);
}

View File

@@ -0,0 +1,587 @@
#include "quaternion_tests.h"
//integer helpers
//void ibz_rounded_div(ibz_t *q, const ibz_t *a, const ibz_t *b);
int quat_test_integer_ibz_rounded_div(){
int res = 0;
ibz_t q, a, b;
ibz_init(&a);
ibz_init(&b);
ibz_init(&q);
// basic tests
ibz_set(&a,15);
ibz_set(&b,3);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==5);
ibz_set(&a,16);
ibz_set(&b,3);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==5);
ibz_set(&a,17);
ibz_set(&b,3);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==6);
ibz_set(&a,37);
ibz_set(&b,5);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==7);
// test sign combination
ibz_set(&a,149);
ibz_set(&b,12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==12);
ibz_set(&a,149);
ibz_set(&b,-12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-12);
ibz_set(&a,-149);
ibz_set(&b,-12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==12);
ibz_set(&a,-149);
ibz_set(&b,12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-12);
ibz_set(&a,151);
ibz_set(&b,12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==13);
ibz_set(&a,-151);
ibz_set(&b,-12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==13);
ibz_set(&a,151);
ibz_set(&b,-12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-13);
ibz_set(&a,-151);
ibz_set(&b,12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-13);
//divisibles with sign
ibz_set(&a,144);
ibz_set(&b,12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==12);
ibz_set(&a,-144);
ibz_set(&b,-12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==12);
ibz_set(&a,144);
ibz_set(&b,-12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-12);
ibz_set(&a,-144);
ibz_set(&b,12);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-12);
// tests close to 0
ibz_set(&a,-12);
ibz_set(&b,-25);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==0);
ibz_set(&a,12);
ibz_set(&b,25);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==0);
ibz_set(&a,-12);
ibz_set(&b,25);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==0);
ibz_set(&a,12);
ibz_set(&b,-25);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==0);
ibz_set(&a,-12);
ibz_set(&b,-23);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==1);
ibz_set(&a,12);
ibz_set(&b,23);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==1);
ibz_set(&a,-12);
ibz_set(&b,23);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-1);
ibz_set(&a,12);
ibz_set(&b,-23);
ibz_rounded_div(&q,&a,&b);
res = res || !(ibz_get(&q)==-1);
// test output equal input
ibz_set(&a,-151);
ibz_set(&b,12);
ibz_rounded_div(&a,&a,&b);
res = res || !(ibz_get(&a)==-13);
ibz_set(&a,-151);
ibz_set(&b,12);
ibz_rounded_div(&b,&a,&b);
res = res || !(ibz_get(&b)==-13);
// test for cmp not returning 1 or -1 or 0
ibz_set(&a,4292606433816540);
ibz_set(&b,864673106105940);
ibz_rounded_div(&b,&a,&b);
res = res || !(ibz_get(&b)==5);
if (res != 0){
printf("Quaternion unit test integer_ibz_rounded_div failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&q);
return(res);
}
// tests for cornacchia helper functions
//void ibz_complex_mul(ibz_t *re_res, ibz_t *im_res, const ibz_t *re_a, const ibz_t *im_a, const ibz_t *re_b, const ibz_t *im_b);
int quat_test_integer_ibz_complex_mul(){
int res = 0;
ibz_t re_res, re_a, re_b, re_cmp, im_res, im_a, im_b, im_cmp;
ibz_init(&re_res);
ibz_init(&re_a);
ibz_init(&re_b);
ibz_init(&re_cmp);
ibz_init(&im_res);
ibz_init(&im_a);
ibz_init(&im_b);
ibz_init(&im_cmp);
ibz_set(&re_a, 1);
ibz_set(&im_a, 2);
ibz_set(&re_b, 3);
ibz_set(&im_b, -4);
ibz_set(&re_cmp, 11);
ibz_set(&im_cmp, 2);
ibz_complex_mul(&re_res,&im_res,&re_a,&im_a,&re_b,&im_b);
res = res || ibz_cmp(&re_res,&re_cmp);
res = res || ibz_cmp(&im_res,&im_cmp);
ibz_set(&re_a, -3);
ibz_set(&im_a, -5);
ibz_set(&re_b, 2);
ibz_set(&im_b, 4);
ibz_set(&re_cmp, 14);
ibz_set(&im_cmp, -22);
ibz_complex_mul(&re_res,&im_res,&re_a,&im_a,&re_b,&im_b);
res = res || ibz_cmp(&re_res,&re_cmp);
res = res || ibz_cmp(&im_res,&im_cmp);
if (res != 0){
printf("Quaternion unit test integer_ibz_complex_mul failed\n");
}
ibz_finalize(&re_res);
ibz_finalize(&re_cmp);
ibz_finalize(&re_a);
ibz_finalize(&re_b);
ibz_finalize(&im_res);
ibz_finalize(&im_cmp);
ibz_finalize(&im_a);
ibz_finalize(&im_b);
return(res);
}
//void ibz_complex_mul_by_complex_power(ibz_t *re_res, ibz_t *im_res, const ibz_t *re_a, const ibz_t *im_a, int64_t exp);
int quat_test_integer_ibz_complex_mul_by_complex_power(){
int res = 0;
int64_t exp;
ibz_t re_res, re_a, re_cmp, im_res, im_a, im_cmp;
ibz_init(&re_res);
ibz_init(&re_a);
ibz_init(&re_cmp);
ibz_init(&im_res);
ibz_init(&im_a);
ibz_init(&im_cmp);
exp = 0;
ibz_set(&re_a, 1);
ibz_set(&im_a, 2);
ibz_set(&re_res, 3);
ibz_set(&im_res, -4);
ibz_set(&re_cmp, 3);
ibz_set(&im_cmp, -4);
ibz_complex_mul_by_complex_power(&re_res,&im_res,&re_a,&im_a,exp);
res = res || ibz_cmp(&re_res,&re_cmp);
res = res || ibz_cmp(&im_res,&im_cmp);
exp = 1;
ibz_set(&re_a, 1);
ibz_set(&im_a, 2);
ibz_set(&re_res, 3);
ibz_set(&im_res, -4);
ibz_set(&re_cmp, 11);
ibz_set(&im_cmp, 2);
ibz_complex_mul_by_complex_power(&re_res,&im_res,&re_a,&im_a,exp);
res = res || ibz_cmp(&re_res,&re_cmp);
res = res || ibz_cmp(&im_res,&im_cmp);
exp = 2;
ibz_set(&re_a, 1);
ibz_set(&im_a, 2);
ibz_set(&re_res, 3);
ibz_set(&im_res, -4);
ibz_set(&re_cmp, 7);
ibz_set(&im_cmp, 24);
ibz_complex_mul_by_complex_power(&re_res,&im_res,&re_a,&im_a,exp);
res = res || ibz_cmp(&re_res,&re_cmp);
res = res || ibz_cmp(&im_res,&im_cmp);
if (res != 0){
printf("Quaternion unit test integer_ibz_complex_mul_by_complex_power failed\n");
}
ibz_finalize(&re_res);
ibz_finalize(&re_cmp);
ibz_finalize(&re_a);
ibz_finalize(&im_res);
ibz_finalize(&im_cmp);
ibz_finalize(&im_a);
return(res);
}
//int ibz_cornacchia_extended_prime_loop(ibz_t *re_res, ibz_t *im_res, int64_t prime, int64_t val);
int quat_test_integer_ibz_cornacchia_extended_prime_loop(){
int res = 0;
int64_t p, re_a, im_a;
ibz_t re_res, re_cmp, im_res, im_cmp, prod;
ibz_init(&re_res);
ibz_init(&re_cmp);
ibz_init(&im_res);
ibz_init(&im_cmp);
ibz_init(&prod);
p = 5;
ibz_set(&re_res, 1);
ibz_set(&im_res, 1);
ibz_set(&re_cmp, -1);
ibz_set(&im_cmp, 7);
ibz_cornacchia_extended_prime_loop(&re_res, &im_res, p, 2);
res = res || ibz_cmp(&re_res,&re_cmp);
res = res || ibz_cmp(&im_res,&im_cmp);
p = 7;
ibz_set(&re_res, -1);
ibz_set(&im_res, 7);
ibz_set(&re_cmp, -1);
ibz_set(&im_cmp, 7);
ibz_cornacchia_extended_prime_loop(&re_res, &im_res, p, 0);
res = res || ibz_cmp(&re_res,&re_cmp);
res = res || ibz_cmp(&im_res,&im_cmp);
if (res != 0){
printf("Quaternion unit test integer_ibz_cornacchia_extended_prime_loop failed\n");
}
ibz_finalize(&re_res);
ibz_finalize(&re_cmp);
ibz_finalize(&im_res);
ibz_finalize(&im_cmp);
ibz_finalize(&prod);
return(res);
}
//int ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p);
int quat_test_integer_ibz_cornacchia_prime(){
int res = 0;
ibz_t x,y,n, prod,c_res,p;
ibz_init(&x);
ibz_init(&y);
ibz_init(&n);
ibz_init(&p);
ibz_init(&prod);
ibz_init(&c_res);
ibz_set(&n, 1);
// there is a solution in these cases
ibz_set(&p, 5);
if(ibz_cornacchia_prime(&x,&y,&n,&p)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&p,&c_res);
} else {
res = 1;
}
ibz_set(&p, 2);
if(ibz_cornacchia_prime(&x,&y,&n,&p)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&p,&c_res);
} else {
res = 1;
}
ibz_set(&p, 41);
if(ibz_cornacchia_prime(&x,&y,&n,&p)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&p,&c_res);
} else {
res = 1;
}
ibz_set(&n, 2);
ibz_set(&p, 3);
if(ibz_cornacchia_prime(&x,&y,&n,&p)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&p,&c_res);
} else {
res = 1;
}
ibz_set(&n, 3);
ibz_set(&p, 7);
if(ibz_cornacchia_prime(&x,&y,&n,&p)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&p,&c_res);
} else {
res = 1;
}
ibz_set(&n, 1);
// there is no solution in these cases
ibz_set(&p, 7);
res = res || ibz_cornacchia_prime(&x,&y,&n,&p);
ibz_set(&p, 3);
res = res || ibz_cornacchia_prime(&x,&y,&n,&p);
ibz_set(&n, 3);
ibz_set(&p, 5);
res = res || ibz_cornacchia_prime(&x,&y,&n,&p);
if (res != 0){
printf("Quaternion unit test integer_ibz_cornacchia_prime failed\n");
}
ibz_finalize(&x);
ibz_finalize(&y);
ibz_finalize(&n);
ibz_finalize(&p);
ibz_finalize(&prod);
ibz_finalize(&c_res);
return res;
}
//int ibz_cornacchia_special_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p, const int exp_adjust);
int quat_test_integer_ibz_cornacchia_special_prime(){
int res = 0;
int exp_adjust;
ibz_t x,y,n, prod,c_res,p, cmp, two;
ibz_init(&x);
ibz_init(&y);
ibz_init(&n);
ibz_init(&p);
ibz_init(&prod);
ibz_init(&c_res);
ibz_init(&cmp);
ibz_init(&two);
ibz_set(&two,2);
ibz_set(&n, 3);
// there is a solution in these cases
ibz_set(&p, 43);
exp_adjust = 2;
//x^2 + 3y^2 = 4*43, (5,7) is solution
if(ibz_cornacchia_special_prime(&x,&y,&n,&p, exp_adjust)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
ibz_pow(&cmp,&two,exp_adjust);
ibz_mul(&cmp,&cmp,&p);
res = res || ibz_cmp(&cmp,&c_res);
} else {
res = 1;
}
ibz_set(&n, 7);
exp_adjust = 3;
ibz_set(&p, 11);
//x^2 + 7y^2 = 8*11, (5,3) is solution
if(ibz_cornacchia_special_prime(&x,&y,&n,&p, exp_adjust)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
ibz_pow(&cmp,&two,exp_adjust);
ibz_mul(&cmp,&cmp,&p);
res = res || ibz_cmp(&cmp,&c_res);
} else {
res = 1;
}
/* Failing testcase, needs investigation
ibz_set(&n, 15);
exp_adjust = 4;
ibz_set(&p, 31);
//x^2 + 15y^2 = 16*31, (19,3) is solution
if(ibz_cornacchia_special_prime(&x,&y,&n,&p, exp_adjust)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
ibz_pow(&cmp,&two,exp_adjust);
ibz_mul(&cmp,&cmp,&p);
res = res || ibz_cmp(&cmp,&c_res);
} else {
res = 1;
}
ibz_set(&n, 3);
exp_adjust = 2;
ibz_set(&p, 7);
//x^2 + 3y^2 = 4*7, (1,3) is solution
if(ibz_cornacchia_special_prime(&x,&y,&n,&p, exp_adjust)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_mul(&prod,&prod,&n);
ibz_add(&c_res,&c_res,&prod);
ibz_pow(&cmp,&two,exp_adjust);
ibz_mul(&cmp,&cmp,&p);
res = res || ibz_cmp(&cmp,&c_res);
} else {
res = 1;
}
*/
// there is no solution in these cases
ibz_set(&n, 3);
ibz_set(&p, 11);
exp_adjust = 3;
res = res || ibz_cornacchia_special_prime(&x,&y,&n,&p, exp_adjust);
if (res != 0){
printf("Quaternion unit test integer_ibz_cornacchia_special_prime failed\n");
}
ibz_finalize(&x);
ibz_finalize(&y);
ibz_finalize(&n);
ibz_finalize(&p);
ibz_finalize(&prod);
ibz_finalize(&c_res);
ibz_finalize(&two);
ibz_finalize(&cmp);
return res;
}
//int ibz_cornacchia_extended(ibz_t *x, ibz_t *y, const ibz_t *n, const short *prime_list, const int prime_list_length, short primality_test_iterations, const ibz_t *bad_primes_prod);
int quat_test_integer_ibz_cornacchia_extended(){
int res = 0;
ibz_t x,y,n, prod,c_res,bad;
short primes[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101};
int primes_length = 26;
short iterations = 20;
ibz_init(&x);
ibz_init(&y);
ibz_init(&n);
ibz_init(&prod);
ibz_init(&c_res);
ibz_init(&bad);
ibz_set(&bad,3*7*11*19);
// there is a solution in these cases
ibz_set(&n, 5);
if(ibz_cornacchia_extended(&x,&y,&n, primes, 4,iterations, NULL)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
} else {
res = 1;
}
ibz_set(&n, 50);
if(ibz_cornacchia_extended(&x,&y,&n, primes, 7,iterations,NULL)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
} else {
res = 1;
}
ibz_set(&n, 4100);
if(ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,&bad)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
} else {
res = 1;
}
// test product of all small primes
ibz_set(&n, 5*13*17*29*37*41);
ibz_set(&x, 53*61*73*89*97);
ibz_mul(&n, &n, &x);
ibz_set(&x, 404);
ibz_mul(&n, &n, &x);
if(ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,NULL)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
} else {
res = 1;
}
// test with large prime
ibz_set(&n, 1381); // prime and 1 mod 4
if(ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,&bad)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
} else {
res = 1;
}
// test with large prime part
ibz_set(&n, 5*13*17*29*37*97);
ibz_set(&x, 1381); // prime and 1 mod 4
ibz_mul(&n, &n, &x);
if(ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,NULL)){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
} else {
res = 1;
}
// there is no solution in these cases
ibz_set(&n, 7);
res = res || ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,&bad);
ibz_set(&n, 3);
res = res || ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,&bad);
ibz_set(&n, 6);
res = res || ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,NULL);
ibz_set(&n, 30);
res = res || ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,&bad);
ibz_set(&n, 30*1381);
res = res || ibz_cornacchia_extended(&x,&y,&n, primes, primes_length,iterations,&bad);
if (res != 0){
printf("Quaternion unit test integer_ibz_cornacchia_extended failed\n");
}
ibz_finalize(&x);
ibz_finalize(&y);
ibz_finalize(&n);
ibz_finalize(&prod);
ibz_finalize(&c_res);
ibz_finalize(&bad);
return res;
}
// run all previous tests
int quat_test_integers(){
int res = 0;
printf("\nRunning quaternion tests of integer functions\n");
res = res | quat_test_integer_ibz_rounded_div();
res = res | quat_test_integer_ibz_complex_mul();
res = res | quat_test_integer_ibz_complex_mul_by_complex_power();
res = res | quat_test_integer_ibz_cornacchia_extended_prime_loop();
res = res | quat_test_integer_ibz_cornacchia_prime();
res = res | quat_test_integer_ibz_cornacchia_special_prime();
res = res | quat_test_integer_ibz_cornacchia_extended();
return(res);
}

View File

@@ -0,0 +1,786 @@
#include "quaternion_tests.h"
// helper functions
//int quat_lattice_equal(const quat_lattice_t *lat1, const quat_lattice_t *lat2);
int quat_test_lattice_equal(){
int res = 0;
quat_lattice_t lat, cmp;
quat_lattice_init(&lat);
quat_lattice_init(&cmp);
ibz_mat_4x4_identity(&(lat.basis));
ibz_mat_4x4_identity(&(cmp.basis));
res = res || !quat_lattice_equal(&lat,&cmp);
ibz_set(&(lat.denom),5);
ibz_set(&(cmp.denom),4);
res = res || quat_lattice_equal(&lat,&cmp);
ibz_set(&(lat.denom),1);
ibz_set(&(cmp.denom),-1);
res = res || !quat_lattice_equal(&lat,&cmp);
ibz_set(&(lat.denom),3);
ibz_set(&(cmp.denom),3);
res = res || !quat_lattice_equal(&lat,&cmp);
ibz_set(&(lat.basis[0][0]),1);
ibz_set(&(lat.basis[0][3]),-1);
ibz_set(&(lat.basis[1][1]),-2);
ibz_set(&(lat.basis[2][2]),1);
ibz_set(&(lat.basis[2][1]),1);
ibz_set(&(lat.basis[3][3]),-3);
ibz_set(&(lat.denom),6);
quat_lattice_hnf(&lat);
ibz_mat_4x4_copy(&(cmp.basis), &(lat.basis));
ibz_set(&(cmp.denom),6);
res = res || !quat_lattice_equal(&lat,&cmp);
ibz_set(&(cmp.denom),-7);
res = res || quat_lattice_equal(&lat,&cmp);
ibz_set(&(cmp.denom),6);
ibz_set(&(cmp.basis[3][3]),165);
res = res || quat_lattice_equal(&lat,&cmp);
if (res != 0){
printf("Quaternion unit test lattice_equal failed\n");
}
quat_lattice_finalize(&lat);
quat_lattice_finalize(&cmp);
return(res);
}
//void quat_lattice_reduce_denom(quat_lattice_t *reduced, const quat_lattice_t *lat);
int quat_test_lattice_reduce_denom(){
int res = 0;
int s;
quat_lattice_t red, lat, cmp;
quat_lattice_init(&red);
quat_lattice_init(&cmp);
quat_lattice_init(&lat);
s = 15;
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),(i+j)*s);
ibz_set(&(cmp.basis[i][j]),(i+j));
}
}
ibz_set(&(lat.denom),4*s);
ibz_set(&(cmp.denom), 4);
quat_lattice_reduce_denom(&red,&lat);
res = res || (!ibz_mat_4x4_equal(&(red.basis),&(cmp.basis)));
res = res || ibz_cmp(&(red.denom),&(cmp.denom));
quat_lattice_reduce_denom(&lat,&lat);
res = res || (!ibz_mat_4x4_equal(&(lat.basis),&(cmp.basis)));
res = res || ibz_cmp(&(lat.denom),&(cmp.denom));
if (res != 0){
printf("Quaternion unit test lattice_reduce_denom failed\n");
}
quat_lattice_finalize(&red);
quat_lattice_finalize(&cmp);
quat_lattice_finalize(&lat);
return(res);
}
//void quat_lattice_dual_without_hnf(quat_lattice_t *dual, const quat_lattice_t *lat);
int quat_test_lattice_dual_without_hnf(){
int res = 0;
quat_lattice_t lat, dual,cmp;
quat_lattice_init(&lat);
quat_lattice_init(&dual);
quat_lattice_init(&cmp);
// set lattice
ibz_mat_4x4_zero(&(lat.basis));
ibz_set(&(lat.basis[0][0]),1);
ibz_set(&(lat.basis[0][3]),-1);
ibz_set(&(lat.basis[1][1]),-2);
ibz_set(&(lat.basis[2][2]),1);
ibz_set(&(lat.basis[2][1]),1);
ibz_set(&(lat.basis[3][3]),-3);
ibz_set(&(lat.denom),6);
ibz_mat_4x4_zero(&(cmp.basis));
ibz_set(&(cmp.basis[0][0]),6);
ibz_set(&(cmp.basis[1][1]),3);
ibz_set(&(cmp.basis[2][2]),6);
ibz_set(&(cmp.basis[3][3]),2);
ibz_set(&(cmp.denom),1);
quat_lattice_hnf(&lat);
// test whether dual of dual is original lattice, but dual is not.
quat_lattice_dual_without_hnf(&dual,&lat);
quat_lattice_hnf(&dual);
quat_lattice_hnf(&cmp);
res = res || !quat_lattice_equal(&dual,&cmp);
res = res || quat_lattice_equal(&dual,&lat);
quat_lattice_dual_without_hnf(&dual,&dual);
quat_lattice_hnf(&dual);
res = res || !quat_lattice_equal(&dual,&lat);
if (res != 0){
printf("Quaternion unit test lattice_dual_without_hnf failed\n");
}
quat_lattice_finalize(&lat);
quat_lattice_finalize(&dual);
quat_lattice_finalize(&cmp);
return(res);
}
//void quat_lattice_add(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2);
int quat_test_lattice_add(){
int res = 0;
quat_lattice_t lat1, lat2, cmp, sum;
quat_lattice_init(&lat1);
quat_lattice_init(&lat2);
quat_lattice_init(&sum);
quat_lattice_init(&cmp);
ibz_mat_4x4_zero(&(lat1.basis));
ibz_mat_4x4_zero(&(lat2.basis));
ibz_mat_4x4_zero(&(cmp.basis));
ibz_set(&(lat1.basis[0][0]),44);
ibz_set(&(lat1.basis[0][2]),3);
ibz_set(&(lat1.basis[0][3]),32);
ibz_set(&(lat2.basis[0][0]),1);
ibz_set(&(cmp.basis[0][0]),2);
ibz_set(&(cmp.basis[0][2]),1);
ibz_set(&(lat1.basis[1][1]),5);
ibz_set(&(lat2.basis[1][1]),2);
ibz_set(&(cmp.basis[1][1]),1);
ibz_set(&(lat1.basis[2][2]),3);
ibz_set(&(lat2.basis[2][2]),1);
ibz_set(&(cmp.basis[2][2]),1);
ibz_set(&(lat1.basis[3][3]),1);
ibz_set(&(lat2.basis[3][3]),3);
ibz_set(&(cmp.basis[3][3]),3);
ibz_set(&(lat1.denom),4);
ibz_set(&(lat2.denom),6);
ibz_set(&(cmp.denom),12);
quat_lattice_add(&sum,&lat1,&lat2);
res = res || (!ibz_mat_4x4_equal(&(sum.basis),&(cmp.basis)));
res = res || ibz_cmp(&(sum.denom),&(cmp.denom));
// same lattices but not under hnf
ibz_mat_4x4_zero(&(lat1.basis));
ibz_mat_4x4_zero(&(lat2.basis));
ibz_set(&(lat1.basis[0][0]),4);
ibz_set(&(lat1.basis[0][2]),3);
ibz_set(&(lat2.basis[0][0]),1);
ibz_set(&(lat2.basis[0][3]),-1);
ibz_set(&(lat1.basis[1][1]),5);
ibz_set(&(lat2.basis[1][1]),-2);
ibz_set(&(lat1.basis[2][2]),3);
ibz_set(&(lat2.basis[2][2]),1);
ibz_set(&(lat2.basis[2][1]),1);
ibz_set(&(lat1.basis[3][3]),7);
ibz_set(&(lat2.basis[3][3]),-3);
ibz_set(&(lat1.denom),4);
ibz_set(&(lat2.denom),6);
quat_lattice_add(&sum,&lat1,&lat2);
res = res || (!ibz_mat_4x4_equal(&(sum.basis),&(cmp.basis)));
res = res || ibz_cmp(&(sum.denom),&(cmp.denom));
//double in place gives hnf
ibz_mat_4x4_copy(&(cmp.basis),&lat2.basis);
ibz_copy(&(cmp.denom),&(lat2.denom));
quat_lattice_hnf(&cmp);
quat_lattice_add(&lat2,&lat2,&lat2);
res = res || (!ibz_mat_4x4_equal(&(lat2.basis),&(cmp.basis)));
res = res || ibz_cmp(&(lat2.denom),&(cmp.denom));
if (res != 0){
printf("Quaternion unit test lattice_add failed\n");
}
quat_lattice_finalize(&lat1);
quat_lattice_finalize(&lat2);
quat_lattice_finalize(&sum);
quat_lattice_finalize(&cmp);
return(res);
}
//void quat_lattice_intersect(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2);
int quat_test_lattice_intersect(){
int res = 0;
quat_lattice_t lat1,lat2, inter, cmp;
quat_lattice_init(&lat1);
quat_lattice_init(&lat2);
quat_lattice_init(&inter);
quat_lattice_init(&cmp);
ibz_mat_4x4_zero(&(cmp.basis));
ibz_mat_4x4_zero(&(lat1.basis));
ibz_mat_4x4_zero(&(lat2.basis));
ibz_set(&(lat1.basis[0][0]),4);
ibz_set(&(lat1.basis[0][2]),3);
ibz_set(&(lat2.basis[0][0]),1);
ibz_set(&(lat2.basis[0][3]),-1);
ibz_set(&(lat1.basis[1][1]),5);
ibz_set(&(lat2.basis[1][1]),-2);
ibz_set(&(lat1.basis[2][2]),3);
ibz_set(&(lat2.basis[2][2]),1);
ibz_set(&(lat2.basis[2][1]),1);
ibz_set(&(lat1.basis[3][3]),7);
ibz_set(&(lat2.basis[3][3]),-3);
ibz_set(&(lat1.denom),4);
ibz_set(&(lat2.denom),6);
quat_lattice_hnf(&lat1);
quat_lattice_hnf(&lat2);
ibz_set(&(cmp.basis[0][0]),2);
ibz_set(&(cmp.basis[0][2]),1);
ibz_set(&(cmp.basis[1][1]),10);
ibz_set(&(cmp.basis[2][2]),3);
ibz_set(&(cmp.basis[3][3]),7);
ibz_set(&(cmp.denom),2);
quat_lattice_intersect(&inter,&lat1,&lat2);
res = res || !quat_lattice_equal(&inter,&cmp);
quat_lattice_intersect(&lat2,&lat1,&lat2);
res = res || !quat_lattice_equal(&lat2,&cmp);
ibz_mat_4x4_copy(&(cmp.basis),&(lat1.basis));
ibz_copy(&(cmp.denom),&(lat1.denom));
quat_lattice_intersect(&lat1,&lat1,&lat1);
res = res || !quat_lattice_equal(&lat1,&cmp);
if (res != 0){
printf("Quaternion unit test lattice_intersect failed\n");
}
quat_lattice_finalize(&lat1);
quat_lattice_finalize(&lat2);
quat_lattice_finalize(&inter);
quat_lattice_finalize(&cmp);
return(res);
}
//void quat_lattice_mul(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2, const quat_alg_t *alg);
int quat_test_lattice_mul(){
int res = 0;
quat_lattice_t lat1, lat2, cmp, prod;
quat_alg_t alg;
quat_lattice_init(&lat1);
quat_lattice_init(&lat2);
quat_lattice_init(&prod);
quat_lattice_init(&cmp);
quat_alg_init_set_ui(&alg, 19);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat1.basis[i][j]),0);
ibz_set(&(lat2.basis[i][j]),0);
ibz_set(&(cmp.basis[i][j]),0);
}
}
ibz_set(&(lat1.basis[0][0]),44);
ibz_set(&(lat1.basis[0][2]),3);
ibz_set(&(lat1.basis[0][3]),32);
ibz_set(&(lat2.basis[0][0]),1);
ibz_set(&(cmp.basis[0][0]),1);
ibz_set(&(lat1.basis[1][1]),5);
ibz_set(&(lat2.basis[1][1]),2);
ibz_set(&(cmp.basis[1][1]),1);
ibz_set(&(lat1.basis[2][2]),3);
ibz_set(&(lat2.basis[2][2]),1);
ibz_set(&(cmp.basis[2][2]),1);
ibz_set(&(lat1.basis[3][3]),1);
ibz_set(&(lat2.basis[3][3]),3);
ibz_set(&(cmp.basis[3][3]),1);
ibz_set(&(lat1.denom),4);
ibz_set(&(lat2.denom),6);
ibz_set(&(cmp.denom),24);
quat_lattice_mul(&prod,&lat1,&lat2,&alg);
res = res || (!ibz_mat_4x4_equal(&(prod.basis),&(cmp.basis)));
res = res || ibz_cmp(&(prod.denom),&(cmp.denom));
// same lattices but not under hnf
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat1.basis[i][j]),0);
ibz_set(&(lat2.basis[i][j]),0);
}
}
ibz_set(&(lat1.basis[0][0]),4);
ibz_set(&(lat1.basis[0][2]),3);
ibz_set(&(lat2.basis[0][0]),1);
ibz_set(&(lat2.basis[0][3]),-1);
ibz_set(&(lat1.basis[1][1]),5);
ibz_set(&(lat2.basis[1][1]),-2);
ibz_set(&(lat1.basis[2][2]),3);
ibz_set(&(lat2.basis[2][2]),1);
ibz_set(&(lat2.basis[2][1]),1);
ibz_set(&(lat1.basis[3][3]),7);
ibz_set(&(lat2.basis[3][3]),-3);
ibz_set(&(lat1.denom),4);
ibz_set(&(lat2.denom),6);
quat_lattice_mul(&prod,&lat1,&lat2,&alg);
res = res || (!ibz_mat_4x4_equal(&(prod.basis),&(cmp.basis)));
res = res || ibz_cmp(&(prod.denom),&(cmp.denom));
//double in place gives hnf
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(cmp.basis[i][j]),0);
}
}
ibz_set(&(cmp.basis[0][0]),1);
ibz_set(&(cmp.basis[1][1]),1);
ibz_set(&(cmp.basis[2][2]),1);
ibz_set(&(cmp.basis[3][3]),1);
ibz_set(&(cmp.denom),36);
quat_lattice_mul(&lat2,&lat2,&lat2,&alg);
res = res || (!ibz_mat_4x4_equal(&(lat2.basis),&(cmp.basis)));
res = res || ibz_cmp(&(lat2.denom),&(cmp.denom));
if (res != 0){
printf("Quaternion unit test lattice_mul failed\n");
}
quat_lattice_finalize(&lat1);
quat_lattice_finalize(&lat2);
quat_lattice_finalize(&prod);
quat_lattice_finalize(&cmp);
quat_alg_finalize(&alg);
return(res);
}
//int quat_lattice_contains_without_alg(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x);
int quat_test_lattice_contains_without_alg(){
int res = 0;
quat_alg_elem_t x;
quat_alg_coord_t coord, cmp;
quat_lattice_t lat;
quat_alg_elem_init(&x);
quat_alg_coord_init(&coord);
quat_alg_coord_init(&cmp);
quat_lattice_init(&lat);
// lattice 1
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),0);
}
}
ibz_set(&(lat.basis[0][0]),4);
ibz_set(&(lat.basis[0][2]),3);
ibz_set(&(lat.basis[1][1]),5);
ibz_set(&(lat.basis[2][2]),3);
ibz_set(&(lat.basis[3][3]),7);
ibz_set(&(lat.denom),4);
// x 1, should fail
ibz_set(&(x.denom),3);
ibz_set(&(x.coord[0]),1);
ibz_set(&(x.coord[1]),-2);
ibz_set(&(x.coord[2]),26);
ibz_set(&(x.coord[3]),9);
res = res || quat_lattice_contains_without_alg(&coord,&lat,&x);
// again, but with NULL
res = res || quat_lattice_contains_without_alg(NULL,&lat,&x);
// lattice 2
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),0);
}
}
ibz_set(&(lat.basis[0][0]),1);
ibz_set(&(lat.basis[0][3]),-1);
ibz_set(&(lat.basis[1][1]),-2);
ibz_set(&(lat.basis[2][2]),1);
ibz_set(&(lat.basis[2][1]),1);
ibz_set(&(lat.basis[3][3]),-3);
ibz_set(&(lat.denom),6);
quat_lattice_hnf(&lat);
// x 1, should succeed
ibz_set(&(x.denom),3);
ibz_set(&(x.coord[0]),1);
ibz_set(&(x.coord[1]),-2);
ibz_set(&(x.coord[2]),26);
ibz_set(&(x.coord[3]),9);
ibz_set(&(cmp[0]),2);
ibz_set(&(cmp[1]),-2);
ibz_set(&(cmp[2]),52);
ibz_set(&(cmp[3]),6);
res = res || (0==quat_lattice_contains_without_alg(&coord,&lat,&x));
res = res || ibz_cmp(&(coord[0]),&(cmp[0]));
res = res || ibz_cmp(&(coord[1]),&(cmp[1]));
res = res || ibz_cmp(&(coord[2]),&(cmp[2]));
res = res || ibz_cmp(&(coord[3]),&(cmp[3]));
// again, but with NULL
res = res || (0==quat_lattice_contains_without_alg(NULL,&lat,&x));
if (res != 0){
printf("Quaternion unit test lattice_contains_without_alg failed\n");
}
quat_alg_elem_finalize(&x);
quat_alg_coord_finalize(&coord);
quat_alg_coord_finalize(&cmp);
quat_lattice_finalize(&lat);
return(res);
}
//int quat_lattice_contains(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x, const quat_alg_t *alg);
int quat_test_lattice_contains(){
int res = 0;
quat_alg_t alg;
quat_alg_elem_t x;
quat_alg_coord_t coord, cmp;
quat_lattice_t lat;
quat_alg_init_set_ui(&alg, 103);
quat_alg_elem_init(&x);
quat_alg_coord_init(&coord);
quat_alg_coord_init(&cmp);
quat_lattice_init(&lat);
// lattice 1
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),0);
}
}
ibz_set(&(lat.basis[0][0]),4);
ibz_set(&(lat.basis[0][2]),3);
ibz_set(&(lat.basis[1][1]),5);
ibz_set(&(lat.basis[2][2]),3);
ibz_set(&(lat.basis[3][3]),7);
ibz_set(&(lat.denom),4);
// x 1, should fail
ibz_set(&(x.denom),3);
ibz_set(&(x.coord[0]),1);
ibz_set(&(x.coord[1]),-2);
ibz_set(&(x.coord[2]),26);
ibz_set(&(x.coord[3]),9);
res = res || quat_lattice_contains(&coord,&lat,&x,&alg);
// again, but with NULL
res = res || quat_lattice_contains(NULL,&lat,&x,&alg);
// lattice 2
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),0);
}
}
ibz_set(&(lat.basis[0][0]),1);
ibz_set(&(lat.basis[0][3]),-1);
ibz_set(&(lat.basis[1][1]),-2);
ibz_set(&(lat.basis[2][2]),1);
ibz_set(&(lat.basis[2][1]),1);
ibz_set(&(lat.basis[3][3]),-3);
ibz_set(&(lat.denom),6);
quat_lattice_hnf(&lat);
// x 1, should succeed
ibz_set(&(x.denom),3);
ibz_set(&(x.coord[0]),1);
ibz_set(&(x.coord[1]),-2);
ibz_set(&(x.coord[2]),26);
ibz_set(&(x.coord[3]),9);
ibz_set(&(cmp[0]),2);
ibz_set(&(cmp[1]),-2);
ibz_set(&(cmp[2]),52);
ibz_set(&(cmp[3]),6);
res = res || (0==quat_lattice_contains(&coord,&lat,&x,&alg));
res = res || ibz_cmp(&(coord[0]),&(cmp[0]));
res = res || ibz_cmp(&(coord[1]),&(cmp[1]));
res = res || ibz_cmp(&(coord[2]),&(cmp[2]));
res = res || ibz_cmp(&(coord[3]),&(cmp[3]));
// again, but with NULL
res = res || (0==quat_lattice_contains(NULL,&lat,&x,&alg));
if (res != 0){
printf("Quaternion unit test lattice_contains failed\n");
}
quat_alg_finalize(&alg);
quat_alg_elem_finalize(&x);
quat_alg_coord_finalize(&coord);
quat_alg_coord_finalize(&cmp);
quat_lattice_finalize(&lat);
return(res);
}
//void quat_lattice_index(ibz_t *index, const quat_lattice_t *sublat, const quat_lattice_t *overlat);
int quat_test_lattice_index(){
int res = 0;
quat_lattice_t sublat,overlat;
ibz_t index;
ibz_init(&index);
quat_lattice_init(&sublat);
quat_lattice_init(&overlat);
ibz_mat_4x4_zero(&(sublat.basis));
ibz_mat_4x4_identity(&(overlat.basis));
ibz_set(&(overlat.denom),2);
ibz_set(&(sublat.basis[0][0]),2);
ibz_set(&(sublat.basis[0][1]),0);
ibz_set(&(sublat.basis[0][2]),1);
ibz_set(&(sublat.basis[0][3]),0);
ibz_set(&(sublat.basis[1][0]),0);
ibz_set(&(sublat.basis[1][1]),4);
ibz_set(&(sublat.basis[1][2]),2);
ibz_set(&(sublat.basis[1][3]),3);
ibz_set(&(sublat.basis[2][0]),0);
ibz_set(&(sublat.basis[2][1]),0);
ibz_set(&(sublat.basis[2][2]),1);
ibz_set(&(sublat.basis[2][3]),0);
ibz_set(&(sublat.basis[3][0]),0);
ibz_set(&(sublat.basis[3][1]),0);
ibz_set(&(sublat.basis[3][2]),0);
ibz_set(&(sublat.basis[3][3]),1);
ibz_set(&(sublat.denom),2);
quat_lattice_index(&index,&sublat,&overlat);
res = res || !(ibz_get(&index)==8);
if (res != 0){
printf("Quaternion unit test lattice_index failed\n");
}
quat_lattice_finalize(&sublat);
quat_lattice_finalize(&overlat);
ibz_finalize(&index);
return(res);
}
//void quat_lattice_hnf(quat_lattice_t *lat);
int quat_test_lattice_hnf(){
int res = 0;
quat_lattice_t lat, cmp;
quat_lattice_init(&lat);
quat_lattice_init(&cmp);
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),0);
ibz_set(&(cmp.basis[i][j]),0);
}
}
ibz_set(&(lat.basis[0][0]),1);
ibz_set(&(lat.basis[0][3]),-1);
ibz_set(&(lat.basis[1][1]),-2);
ibz_set(&(lat.basis[2][2]),1);
ibz_set(&(lat.basis[2][1]),1);
ibz_set(&(lat.basis[3][3]),-3);
ibz_set(&(cmp.basis[0][0]),1);
ibz_set(&(cmp.basis[1][1]),2);
ibz_set(&(cmp.basis[2][2]),1);
ibz_set(&(cmp.basis[3][3]),3);
ibz_set(&(cmp.denom),6);
ibz_set(&(lat.denom),6);
quat_lattice_hnf(&lat);
res = res || (!ibz_mat_4x4_equal(&(lat.basis),&(cmp.basis)));
res = res || ibz_cmp(&(lat.denom),&(cmp.denom));
if (res != 0){
printf("Quaternion unit test lattice_hnf failed\n");
}
quat_lattice_finalize(&lat);
quat_lattice_finalize(&cmp);
return(res);
}
//int quat_lattice_lll(ibz_mat_4x4_t *red, const quat_lattice_t *lattice, const ibz_t *q, int precision);
int quat_test_lattice_lll(){
int res = 0;
quat_lattice_t lat, test;
ibz_mat_4x4_t red;
ibz_t num, denom, q;
ibq_t coeff;
ibz_init(&num);
ibz_init(&denom);
ibz_init(&q);
ibq_init(&coeff);
ibz_mat_4x4_init(&red);
quat_lattice_init(&lat);
quat_lattice_init(&test);
// set lattice
ibz_set(&lat.denom, 60);
ibz_mat_4x4_zero(&(lat.basis));
ibz_set(&lat.basis[0][0], 3);
ibz_set(&lat.basis[1][0], 7);
ibz_set(&lat.basis[0][1], 1);
ibz_set(&lat.basis[3][1], -6);
ibz_set(&lat.basis[1][2], 12);
ibz_set(&lat.basis[2][2], 5);
ibz_set(&lat.basis[0][3], -19);
ibz_set(&lat.basis[3][3], 3);
quat_lattice_hnf(&lat);
ibz_set(&q,103);
res = res || quat_lattice_lll(&red,&lat,&q,10);
// test lll reduced
ibz_set(&num,3);
ibz_set(&denom,4);
ibq_set(&coeff,&num,&denom);
res = res || !quat_dim4_lll_verify(&red,&coeff,&q);
// test lattice equality
ibz_copy(&(test.denom),&(lat.denom));
ibz_mat_4x4_copy(&(test.basis),&(red));
quat_lattice_hnf(&test);
res = res || !quat_lattice_equal(&test,&lat);
if (res != 0){
printf("Quaternion unit test lattice_lll failed\n");
}
ibz_finalize(&num);
ibz_finalize(&denom);
ibz_finalize(&q);
ibq_finalize(&coeff);
ibz_mat_4x4_finalize(&red);
quat_lattice_finalize(&lat);
quat_lattice_finalize(&test);
return(res);
}
//int quat_lattice_random_elem(quat_alg_elem_t *elem, const quat_lattice_t *lattice, unsigned char n)
int quat_test_lattice_random_elem(){
int res = 0;
quat_lattice_t lat;
quat_alg_elem_t elem;
quat_lattice_init(&lat);
quat_alg_elem_init(&elem);
ibz_set(&lat.denom, 60);
ibz_set(&lat.basis[0][0], 3);
ibz_set(&lat.basis[1][0], 7);
ibz_set(&lat.basis[0][1], 1);
ibz_set(&lat.basis[3][1], -6);
ibz_set(&lat.basis[1][2], 12);
ibz_set(&lat.basis[2][2], 5);
ibz_set(&lat.basis[0][3], -19);
ibz_set(&lat.basis[3][3], 3);
quat_lattice_hnf(&lat);
for (int i = 0; i <= 10; i++) {
int prng = quat_lattice_random_elem(&elem, &lat, 3);
assert(prng);
res |= !quat_lattice_contains_without_alg(&elem.coord, &lat, &elem);
for (int j = 0; j < 4; j++) {
res |= !(ibz_get(&elem.coord[j]) < (1 << 2));
res |= !(-(1 << 2) <= ibz_get(&elem.coord[j]));
}
}
quat_lattice_finalize(&lat);
quat_alg_elem_finalize(&elem);
if (res != 0){
printf("Quaternion unit test lattice_random_elem failed\n");
}
return res;
}
//void quat_lattice_right_transporter(quat_lattice_t *trans, const quat_lattice_t *lat1, const quat_lattice_t *lat1)
int quat_test_lattice_right_transporter_hnf(){
int res = 0;
quat_alg_t alg;
quat_lattice_t lat1, lat2, trans, exp;
quat_alg_init_set_ui(&alg, 103);
quat_lattice_init(&lat1);
quat_lattice_init(&lat2);
quat_lattice_init(&trans);
quat_lattice_init(&exp);
ibz_set(&lat1.basis[0][0], 9);
ibz_set(&lat1.basis[0][1], 4);
ibz_set(&lat1.basis[0][2], 7);
ibz_set(&lat1.basis[1][1], 12);
ibz_set(&lat1.basis[1][3], 6);
ibz_set(&lat1.basis[2][2], 8);
ibz_set(&lat1.basis[2][3], 3);
ibz_set(&lat1.basis[3][3], 3);
ibz_set(&lat1.denom, 11);
quat_lattice_mul(&lat2, &lat1, &lat1, &alg);
quat_lattice_right_transporter(&trans, &lat1, &lat2, &alg);
ibz_set(&exp.basis[0][0], 1);
ibz_set(&exp.basis[1][1], 12);
ibz_set(&exp.basis[1][3], 6);
ibz_set(&exp.basis[2][2], 8);
ibz_set(&exp.basis[2][3], 3);
ibz_set(&exp.basis[3][3], 3);
ibz_set(&exp.denom, 11);
res |= !quat_lattice_equal(&trans, &exp);
ibz_set(&(alg.p),19);
ibz_set(&(alg.gram[2][2]),19);
ibz_set(&(alg.gram[3][3]),19);
ibz_set(&lat1.basis[0][0], -12);
ibz_set(&lat1.basis[0][1], 60);
ibz_set(&lat1.basis[0][2], 42);
ibz_set(&lat1.basis[0][3], 60);
ibz_set(&lat1.basis[1][0], 9);
ibz_set(&lat1.basis[1][1], 24);
ibz_set(&lat1.basis[1][2], 0);
ibz_set(&lat1.basis[1][3], -21);
ibz_set(&lat1.basis[2][0], 0);
ibz_set(&lat1.basis[2][1], -18);
ibz_set(&lat1.basis[2][2], 27);
ibz_set(&lat1.basis[2][3], 3);
ibz_set(&lat1.basis[3][0], 0);
ibz_set(&lat1.basis[3][1], 0);
ibz_set(&lat1.basis[3][2], 15);
ibz_set(&lat1.basis[3][3], 3);
ibz_set(&lat1.denom, 15);
quat_lattice_hnf(&lat1);
quat_lattice_reduce_denom(&lat1, &lat1);
quat_lattice_mul(&lat2, &lat1, &lat1, &alg);
quat_lattice_right_transporter(&trans, &lat1, &lat2, &alg);
ibz_mat_4x4_zero(&(exp.basis));
ibz_set(&exp.basis[0][0], 2);
ibz_set(&exp.basis[1][1], 1);
ibz_set(&exp.basis[2][2], 2);
ibz_set(&exp.basis[2][3], 1);
ibz_set(&exp.basis[3][3], 1);
ibz_set(&exp.denom, 5);
res |= !quat_lattice_equal(&trans, &exp);
if (res != 0){
printf("Quaternion unit test lattice_right_transporter_hnf failed\n");
}
quat_alg_finalize(&alg);
quat_lattice_finalize(&lat1);
quat_lattice_finalize(&lat2);
quat_lattice_finalize(&trans);
quat_lattice_finalize(&exp);
return(res);
}
// run all previous tests
int quat_test_lattice(){
int res = 0;
printf("\nRunning quaternion tests of lattice functions\n");
res = res | quat_test_lattice_equal();
res = res | quat_test_lattice_reduce_denom();
res = res | quat_test_lattice_dual_without_hnf();
res = res | quat_test_lattice_add();
res = res | quat_test_lattice_intersect();
res = res | quat_test_lattice_mul();
res = res | quat_test_lattice_contains_without_alg();
res = res | quat_test_lattice_contains();
res = res | quat_test_lattice_index();
res = res | quat_test_lattice_hnf();
res = res | quat_test_lattice_lll();
res = res | quat_test_lattice_random_elem();
res = res | quat_test_lattice_right_transporter_hnf();
return(res);
}

View File

@@ -0,0 +1,218 @@
#include "quaternion_tests.h"
//void ibz_mat_howell(int rows, int cols, ibz_t howell[rows][rows+1], const ibz_t mat[rows][cols], ibz_t *mod)
int quat_test_ibz_mat_howell(){
int res = 0;
// Examples from Mulders & Storjohan
ibz_t mod;
ibz_t stormin[3][3], stormout[3][4], stormtrans[4][4], expected[3][4];
ibz_init(&mod);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
ibz_init(&stormin[i][j]);
for (int j = 0; j < 4; j++) {
ibz_init(&stormout[i][j]);
ibz_init(&expected[i][j]);
}
}
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
ibz_init(&stormtrans[i][j]);
ibz_set(&mod, 12);
ibz_set(&expected[0][2], 4);
ibz_set(&expected[1][2], 1);
ibz_set(&expected[2][3], 1);
ibz_set(&stormin[0][0], 4);
ibz_set(&stormin[1][0], 1);
ibz_set(&stormin[2][1], 5);
int zeros = ibz_mat_howell(3, 3, stormout, stormtrans, stormin, &mod);
res |= zeros != 2;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
res |= ibz_cmp(&stormout[i][j], &expected[i][j]);
ibz_set(&stormin[0][0], 8);
ibz_set(&stormin[1][0], 5);
ibz_set(&stormin[2][0], 5);
ibz_set(&stormin[1][1], 9);
ibz_set(&stormin[2][1], 8);
ibz_set(&stormin[2][2], 10);
zeros = ibz_mat_howell(3, 3, stormout, stormtrans, stormin, &mod);
res |= zeros != 2;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 4; j++)
res |= ibz_cmp(&stormout[i][j], &expected[i][j]);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
ibz_finalize(&stormin[i][j]);
for (int j = 0; j < 4; j++) {
ibz_finalize(&stormout[i][j]);
ibz_finalize(&expected[i][j]);
}
}
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
ibz_finalize(&stormtrans[i][j]);
// An example computed in Pari
ibz_t in[5][3], out[5][6], exp[5][6], trans[6][6];
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++)
ibz_init(&in[i][j]);
for (int j = 0; j < 6; j++) {
ibz_init(&out[i][j]);
ibz_init(&exp[i][j]);
}
}
for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++)
ibz_init(&trans[i][j]);
ibz_set(&mod, 60);
ibz_set(&in[0][0], 1);
ibz_set(&in[0][1], 2);
ibz_set(&in[0][2], -3);
ibz_set(&in[1][0], 4);
ibz_set(&in[1][1], 5);
ibz_set(&in[1][2], 6);
ibz_set(&in[2][0], 7);
ibz_set(&in[2][1], 8);
ibz_set(&in[2][2], 19);
ibz_set(&in[3][0], 10);
ibz_set(&in[3][1], 11);
ibz_set(&in[3][2], 12);
ibz_set(&in[4][0], 13);
ibz_set(&in[4][1], 14);
ibz_set(&in[4][2], 15);
ibz_set(&exp[0][2], 12);
ibz_set(&exp[0][3], 6);
ibz_set(&exp[2][3], 10);
ibz_set(&exp[1][4], 9);
ibz_set(&exp[2][4], 6);
ibz_set(&exp[3][4], 3);
ibz_set(&exp[0][5], 1);
ibz_set(&exp[1][5], 1);
ibz_set(&exp[2][5], 1);
ibz_set(&exp[3][5], 1);
ibz_set(&exp[4][5], 1);
zeros = ibz_mat_howell(5, 3, out, trans, in, &mod);
res |= zeros != 2;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 6; j++)
res |= ibz_cmp(&out[i][j], &exp[i][j]);
if (res != 0){
printf("Quaternion unit test ibz_mat_howell failed\n");
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++)
ibz_finalize(&in[i][j]);
for (int j = 0; j < 6; j++) {
ibz_finalize(&out[i][j]);
ibz_finalize(&exp[i][j]);
}
}
for (int i = 0; i < 6; i++)
for (int j = 0; j < 6; j++)
ibz_finalize(&trans[i][j]);
ibz_finalize(&mod);
return res;
}
//void ibz_mat_right_ker_mod(int rows, int cols, ibz_t ker[cols][rows], const ibz_t mat[rows][cols], ibz_t *mod)
int quat_test_ibz_mat_right_ker_mod() {
int res = 0;
// An example computed in Pari
ibz_t mod, a, b, tmp;
ibz_init(&mod);
ibz_init(&a);
ibz_init(&b);
ibz_init(&tmp);
ibz_t mat[5][3], ker[3][5];
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
ibz_init(&mat[i][j]);
ibz_init(&ker[j][i]);
}
}
ibz_set(&mod, 60);
ibz_set(&mat[0][0], 1);
ibz_set(&mat[0][1], 2);
ibz_set(&mat[0][2], -3);
ibz_set(&mat[1][0], 4);
ibz_set(&mat[1][1], 5);
ibz_set(&mat[1][2], 6);
ibz_set(&mat[2][0], 7);
ibz_set(&mat[2][1], 8);
ibz_set(&mat[2][2], 19);
ibz_set(&mat[3][0], 10);
ibz_set(&mat[3][1], 11);
ibz_set(&mat[3][2], 12);
ibz_set(&mat[4][0], 13);
ibz_set(&mat[4][1], 14);
ibz_set(&mat[4][2], 15);
// self-testing thanks to assertions
ibz_mat_right_ker_mod(5, 3, ker, mat, &mod);
// Randomized test
ibz_set(&mod, 6402373705728000l);
for (int r = 0; r < 10; r++) {
for (int i = 0; i < 2; i++)
for (int j = 0; j < 3; j++)
ibz_rand_interval(&mat[i][j], &ibz_const_zero, &mod);
for (int i = 2; i < 5; i++) {
ibz_rand_interval(&a, &ibz_const_zero, &mod);
ibz_rand_interval(&b, &ibz_const_zero, &mod);
for (int j = 0; j < 3; j++) {
ibz_mul(&tmp, &mat[0][j], &a);
ibz_mul(&mat[i][j], &mat[1][1], &b);
ibz_add(&mat[i][j], &mat[i][j], &tmp);
ibz_mod(&mat[i][j], &mat[i][j], &mod);
}
}
// self-testing thanks to assertions
ibz_mat_right_ker_mod(5, 3, ker, mat, &mod);
}
if (res != 0){
printf("Quaternion unit test ibz_mat_howell failed\n");
}
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
ibz_finalize(&mat[i][j]);
ibz_finalize(&ker[j][i]);
}
}
ibz_finalize(&mod);
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&tmp);
return res;
}
// run all previous tests
int quat_test_matkermod(){
int res = 0;
printf("\nRunning quaternion tests of matkermod\n");
res = res | quat_test_ibz_mat_howell();
res = res | quat_test_ibz_mat_right_ker_mod();
return(res);
}

View File

@@ -0,0 +1,188 @@
/** @file
*
* @authors Sina Schaeffler
*
* @brief Declarations of tests of quaternion algebra operations
*/
#ifndef QUATERNION_TESTS_H
#define QUATERNION_TESTS_H
#include <quaternion.h>
#include <stdio.h>
#include "../internal.h"
/** @internal
* @ingroup quat_quat
* @defgroup quat_tests Quaternion module test functions
* @{
*/
/** @brief Test initializers and finalizers for quaternion algebra types
*
* Test initializers and finalizers for the following types:
*
* quat_alg_t
*
* quat_alg_elem_t
*
* quat_alg_coord_t
*
* ibz_vec_4_t
*
* ibz_vec_5_t
*
* ibz_mat_2x2_t
*
* ibz_mat_4x4_t
*
* ibz_mat_4x5_t
*
* ibz_mat_4x8_t
*
* quat_lattice_t
*
* quat_order_t
*
* quat_left_ideal_t
*/
int quat_test_finit();
/** @brief Test integer, quadratic form and matrix functions for dimension 4 from the quaternion module
*
* Runs unit tests for the following functions
*
* int ibz_4x5_right_ker_mod_prime(ibz_vec_5_t *ker, const ibz_mat_4x5_t *mat, const ibz_t *p);
*
* int ibz_4x4_right_ker_mod_prime(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, const ibz_t *p);
*
* int ibz_4x4_right_ker_mod_power_of_2(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, unsigned short exp);
*
* void ibz_mat_4x4_eval(quat_alg_coord_t *res, const ibz_mat_4x4_t *mat, const quat_alg_coord_t *vec);
*
* void quat_qf_eval(ibz_t *res, const ibz_mat_4x4_t *qf, const quat_alg_coord_t *coord);
*
* void ibz_content(ibz_t *content, const quat_alg_coord_t *v);
*/
int quat_test_dim4();
/** @brief Test integer, lattice and matrix functions for dimension 2 from the quaternion module
*
* Runs unit tests for the following functions
*
* void ibz_mat_2x2_eval(ibz_vec_2_t *res, const ibz_mat_2x2_t *mat, const ibz_vec_2_t *vec);
*
* void ibz_2x2_mul_mod(ibz_mat_2x2_t *prod, const ibz_mat_2x2_t *mat_a, const ibz_mat_2x2_t *mat_b, const ibz_t *m);
*
* int ibz_2x2_inv_mod(ibz_mat_2x2_t *inv, const ibz_mat_2x2_t *mat, const ibz_t *m);
*
* int quat_2x2_lattice_enumerate_cvp_filter(quat_alg_elem_t *res, const ibz_mat_2x2_t *lat_basis, const ibz_vec_2_t *target,unsigned int qf, unsigned int dist_bound, int (*condition)(quat_alg_elem_t* , const ibz_vec_2_t*, const void*), const void* params, unsigned int max_tries);
*/
int quat_test_dim2();
/** @brief Test integer functions
*
* Runs unit tests for the following functions
*
* int ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p);
*
* int ibz_cornacchia_special_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p, const int exp_adjust);
*
* int ibz_cornacchia_extended(ibz_t *x, ibz_t *y, const ibz_t *n, const short *prime_list, const int prime_list_length, short primality_test_iterations, const ibz_t *bad_primes_prod);
*/
int quat_test_integers();
/** @brief Test operations on quaternion algebra elements
*
* Runs unit tests for the following functions
*
* void quat_alg_add(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b);
*
* void quat_alg_sub(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b);
*
* void quat_alg_mul(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b, const quat_alg_t *alg);
*
* void quat_alg_norm(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_t *alg);
*
* void quat_alg_trace(quat_alg_elem_t *res, const quat_alg_elem_t *a);
*
* void quat_alg_scalar(quat_alg_elem_t *elem, const ibz_t *numerator, const ibz_t *denominator);
*
* void quat_alg_conj(quat_alg_elem_t *conj, const quat_alg_elem_t *x);
*
* void quat_alg_make_primitive(quat_alg_coord_t *primitive_x, ibz_t *content, const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg){
*
* int quat_alg_is_primitive(const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg):
*
* void quat_alg_normalize(quat_alg_elem_t *x);
*
* int quat_alg_elem_is_zero(const quat_alg_elem_t *x);
*
* int quat_alg_coord_is_zero(const quat_alg_coord_t *x);
*/
int quat_test_algebra();
/** @brief Test operations on lattices
*
* Runs unit tests for the following functions
*
* void quat_lattice_add(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2);
*
* void quat_lattice_intersect(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2);
*
* void quat_lattice_mul(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2, const quat_alg_t *alg);
*
* int quat_lattice_contains(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x, const quat_alg_t *alg);
*
* void quat_lattice_hnf(quat_lattice_t *lat);
*/
int quat_test_lattice();
/** @brief Test operations on left ideals and their creation
*
* Runs unit tests for the following functions
*
* void quat_lideal_create_principal(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const quat_order_t *order, const quat_alg_t *alg);
*
* void quat_lideal_create_from_primitive(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg);
*
* void quat_lideal_make_primitive_then_create(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const ibz_t *N, const quat_order_t *order, const quat_alg_t *alg);
*
* void quat_lideal_random_2e(quat_left_ideal_t *lideal, const quat_order_t *order, const quat_alg_t *alg, int64_t e);
*
* int quat_lideal_generator(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const quat_alg_t *alg, int bound);
*
* int quat_lideal_generator_coprime(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const ibz_t *n, const quat_alg_t *alg, int bound);
*
* int quat_lideal_mul(quat_left_ideal_t *product, const quat_left_ideal_t *lideal, const quat_alg_elem_t *alpha, const quat_alg_t *alg, int bound);
*
* void quat_lideal_add(quat_left_ideal_t *sum, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
*
* void quat_lideal_inter(quat_left_ideal_t *intersection, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
*
* int quat_lideal_equals(const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
*
* int quat_lideal_isom(quat_alg_elem_t *iso, const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg);
*
* void quat_lideal_right_order(quat_order_t *order, const quat_left_ideal_t *lideal, const quat_alg_t *alg);
*
* void quat_lideal_reduce_basis(ibz_mat_4x4_t *reduced, ibz_mat_4x4_t *gram, const quat_left_ideal_t *lideal, const quat_alg_t *alg); //replaces lideal_lll
*
* void quat_connecting_ideal(quat_left_ideal_t *connecting_ideal, const quat_order_t *O1, const quat_order_t *O2, const quat_alg_t *alg);
*/
int quat_test_lideal();
/** @brief test matkermod and Howell form
*/
int quat_test_matkermod();
/** @brief Test with randomization for complex functions where this is possible
*
*/
int quat_test_with_randomization();
/** @}
*/
#endif

View File

@@ -0,0 +1,762 @@
#include "quaternion_tests.h"
#include <rng.h>
// int ibz_2x2_inv_mod(ibz_mat_2x2_t *inv, const ibz_mat_2x2_t *mat, const ibz_t *m);
int quat_test_randomized_ibz_mat_2x2_inv_mod()
{
int res = 0;
ibz_t m, det, gcd;
ibz_mat_2x2_t a, inv, id, prod;
int64_t rand[4];
int64_t rand_m;
ibz_init(&m);
ibz_init(&det);
ibz_init(&gcd);
ibz_mat_2x2_init(&a);
ibz_mat_2x2_init(&inv);
ibz_mat_2x2_init(&prod);
ibz_mat_2x2_init(&id);
ibz_mat_2x2_set(&id, 1, 0, 0, 1);
for (int iter = 0; iter < 100; iter++){
// generate random matrix and modulo, with modulo larger than 2
int randret = randombytes((unsigned char*)rand, 4*sizeof(uint64_t));
if (randret != 0)
return 1;
ibz_mat_2x2_set(&a, rand[0],rand[1],rand[2],rand[3]);
rand_m = 0;
while(rand_m < 2) {
int randret = randombytes((unsigned char*)&rand_m, sizeof(uint64_t));
if (randret != 0)
return 1;
}
ibz_set(&m,rand_m);
// compute det
ibz_mat_2x2_det_from_ibz(&det,&(a[0][0]),&(a[0][1]),&(a[1][0]),&(a[1][1]));
// is it prime to mod
ibz_gcd(&gcd,&det,&m);
if(ibz_is_one(&gcd)){
// matrix should be invertible mod m
if (ibz_2x2_inv_mod(&inv, &a, &m))
{
// ibz_2x2_mul_mod(&prod,&a,&inv, &m);
ibz_2x2_mul_mod(&prod, &inv, &a, &m);
res = res || ibz_cmp(&(prod[0][0]), &(id[0][0]));
res = res || ibz_cmp(&(prod[0][1]), &(id[0][1]));
res = res || ibz_cmp(&(prod[1][0]), &(id[1][0]));
res = res || ibz_cmp(&(prod[1][1]), &(id[1][1]));
}
else
{
res = 1;
}
} else {
res = res || ibz_2x2_inv_mod(&inv, &a, &m);
}
}
if (res != 0)
{
printf("Quaternion unit test with randomization for ibz_mat_2x2_inv_mod failed\n");
}
ibz_mat_2x2_finalize(&a);
ibz_mat_2x2_finalize(&inv);
ibz_mat_2x2_finalize(&prod);
ibz_mat_2x2_finalize(&id);
ibz_finalize(&m);
ibz_finalize(&det);
ibz_finalize(&gcd);
return (res);
}
// int quat_2x2_lattice_enumerate_cvp_filter(quat_alg_elem_t *res, const ibz_mat_2x2_t *lat_basis, const ibz_vec_2_t *target,unsigned int qf, unsigned int dist_bound, int (*condition)(quat_alg_elem_t* , const ibz_vec_2_t*, const void*), const void* params, unsigned int max_tries);
int quat_test_randomized_2x2_lattice_enumerate_cvp_filter()
{
// test only wether a result is returned which verifies the conditions, if true is returned
// cannot test wether false is retured correctly or not
int res = 0;
ibz_t p, bound, norm_q, norm;
quat_alg_elem_t cvp_res;
ibz_mat_2x2_t basis;
ibz_vec_2_t target, diff, found;
ibz_mat_2x2_init(&basis);
quat_alg_elem_init(&cvp_res);
ibz_vec_2_init(&target);
ibz_vec_2_init(&diff);
ibz_vec_2_init(&found);
ibz_init(&p);
ibz_init(&norm_q);
ibz_init(&norm);
ibz_init(&bound);
unsigned int q;
unsigned int dist_bound;
void *params = (void *)&p;
unsigned int max_tries;
int64_t rand[9];
// fix p since only used inside condition which is internal, unused by any external function
ibz_set(&p, 3);
for (int iter = 0; iter < 20; iter++){
// generate random matrix with non-0 det
ibz_set(&bound,0);
while(ibz_is_zero(&bound)){
int randret = randombytes((unsigned char*)rand, 9*sizeof(uint64_t));
if (randret != 0)
return 1;
ibz_mat_2x2_set(&basis, rand[0],rand[1],rand[2],rand[3]);
// check det is not 0
ibz_mat_2x2_det_from_ibz(&bound,&(basis[0][0]),&(basis[0][1]),&(basis[1][0]),&(basis[1][1]));
}
// set target
ibz_vec_2_set(&target, rand[4], rand[5]);
//set other params randomly in reasonable ranges
q = ((unsigned int)rand[6]) % 1024;
if(q == 0){
q = 1;
}
dist_bound = ((unsigned int)rand[7]) % 128;
max_tries = ((unsigned int)rand[8]) % 16384;
if (quat_2x2_lattice_enumerate_cvp_filter(&cvp_res, &basis, &target, q, dist_bound, &quat_dim2_lattice_test_cvp_condition, params, max_tries)){
// used cvp_res and condition to exfiltrate standard coords of distance to target from close vector found
ibz_copy(&(diff[0]), &(cvp_res.coord[0]));
ibz_copy(&(diff[1]), &(cvp_res.coord[1]));
// compute close vector in lattice from target and diff
ibz_sub(&(found[0]), &(target[0]), &(diff[0]));
ibz_sub(&(found[1]), &(target[1]), &(diff[1]));
// norm bound on diff ok?
ibz_set(&norm_q, q);
ibz_set(&bound, 2);
ibz_pow(&bound, &bound, dist_bound);
quat_dim2_lattice_norm(&norm, &(diff[0]), &(diff[1]), &norm_q);
res = res || !(ibz_cmp(&norm, &bound) < 0);
// Condition ok
res = res || (!quat_dim2_lattice_test_cvp_condition(&cvp_res, &diff, params));
// Is in lattice
res = res || (!quat_dim2_lattice_contains(&basis, &(found[0]), &(found[1])));
}
}
if (res != 0)
{
printf("Quaternion unit test with randomization for 2x2_lattice_enumerate_cvp_filter failed\n");
}
ibz_mat_2x2_finalize(&basis);
quat_alg_elem_finalize(&cvp_res);
ibz_vec_2_finalize(&target);
ibz_vec_2_finalize(&diff);
ibz_vec_2_finalize(&found);
ibz_finalize(&p);
ibz_finalize(&norm_q);
ibz_finalize(&norm);
ibz_finalize(&bound);
return res;
}
//int ibz_4x4_inv_with_det_as_denom(ibz_mat_4x4_t *inv, ibz_t *det, const ibz_mat_4x4_t mat);
int quat_test_randomized_ibz_mat_4x4_inv_with_det_as_denom(){
int res = 0;
ibz_t det;
ibz_mat_4x4_t mat, inv;
ibz_mat_4x4_t det_id, prod;
ibz_mat_4x4_init(&det_id);
ibz_mat_4x4_init(&prod);
ibz_init(&det);
ibz_mat_4x4_init(&mat);
ibz_mat_4x4_init(&inv);
for (int r = 0; r < 10; r++) {
do {
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
ibz_rand_interval_i(&mat[i][j], -(1 << 20), 1 << 20);
} while (!ibz_mat_4x4_inv_with_det_as_denom(&inv, &det, &mat));
ibz_mat_4x4_identity(&det_id);
ibz_mat_4x4_scalar_mul(&det_id,&det,&det_id);
ibz_mat_4x4_mul(&prod, &inv, &mat);
res = res || !ibz_mat_4x4_equal(&det_id,&prod);
ibz_mat_4x4_mul(&prod, &mat, &inv);
res = res || !ibz_mat_4x4_equal(&det_id,&prod);
}
if (res != 0){
printf("Quaternion unit test with randomization for ibz_mat_4x4_inv_with_det_as_denom failed\n");
}
ibz_mat_4x4_finalize(&det_id);
ibz_mat_4x4_finalize(&prod);
ibz_mat_4x4_finalize(&mat);
ibz_mat_4x4_finalize(&inv);
ibz_finalize(&det);
return res;
}
//void ibz_mat_4x8_hnf_core(ibz_mat_4x4_t *hnf, const ibz_mat_4x8_t *generators);
int quat_test_randomized_ibz_mat_4x8_hnf_core(){
// only partial test, since lattice equality cannot be tested without hnf, so only one inclusion is checked
int res = 0;
quat_alg_elem_t vec;
quat_lattice_t lat;
ibz_mat_4x8_t mat;
ibz_mat_4x4_t hnf,cmp;
quat_lattice_init(&lat);
quat_alg_elem_init(&vec);
ibz_mat_4x8_init(&mat);
ibz_mat_4x4_init(&hnf);
ibz_mat_4x4_init(&cmp);
int64_t rand[8][4];
int det_non_0;
for (int iter = 0; iter < 100; iter++){
int randret = randombytes((unsigned char*)rand, 8*4*sizeof(uint64_t));
if (randret != 0)
return 1;
for (int i = 0; i < 4; i++){
for (int j = 0; j < 8; j++){
ibz_set(&(mat[i][j]),rand[j][i]);
}
}
ibz_mat_4x8_hnf_core(&hnf,&mat);
res = res || (!ibz_mat_4x4_is_hnf(&hnf));
// also should test that they generate the same lattice. However, can only check one inclusion efficiently (and this only if full rank), so do so
det_non_0 = 1;
for(int i = 0; i <4; i ++){
det_non_0 = det_non_0 && ibz_is_zero(&(hnf[i][i]));
}
if(det_non_0){
ibz_mat_4x4_copy(&(lat.basis),&hnf);
ibz_set(&(lat.denom),1);
for(int i = 0; i <8; i ++){
quat_alg_elem_copy_ibz(&vec,&(lat.denom), &(mat[i][0]), &(mat[i][1]), &(mat[i][2]), &(mat[i][3]));
res = res || !quat_lattice_contains_without_alg(NULL,&lat,&vec);
}
}
}
if (res != 0){
printf("Quaternion unit test with randomization for ibz_mat_4x8_hnf_core failed\n");
}
quat_lattice_finalize(&lat);
quat_alg_elem_finalize(&vec);
ibz_mat_4x8_finalize(&mat);
ibz_mat_4x4_finalize(&hnf);
ibz_mat_4x4_finalize(&cmp);
return(res);
}
//int ibz_4x5_right_ker_mod_prime(ibz_vec_5_t *ker, const ibz_mat_4x5_t *mat, const ibz_t *p);
int quat_test_randomized_ibz_4x5_right_ker_mod_prime(){
// check that if the function returns 1, the vector is in the kernel
int res = 0;
ibz_t prime;
ibz_mat_4x5_t mat;
ibz_vec_5_t ker;
ibz_t prod, sum;
ibz_init(&prod);
ibz_init(&sum);
ibz_mat_4x5_init(&mat);
ibz_vec_5_init(&ker);
ibz_init(&prime);
int64_t rand[5][4];
int64_t rand_p;
for (int iter = 0; iter < 100; iter++){
// generate random matrix and modulo, with modulo larger than 2
int randret = randombytes((unsigned char*)rand, 4*5*sizeof(uint64_t));
if (randret != 0)
return 1;
for (int i = 0; i < 4; i++){
for (int j = 0; j < 5; j++){
ibz_set(&(mat[i][j]),rand[j][i]);
}
}
rand_p = 0;
while(rand_p ==0) {
int randret = randombytes((unsigned char*)&rand_p, sizeof(uint64_t));
if (randret != 0)
return 1;
ibz_set(&prime,rand_p);
if(!ibz_probab_prime(&prime,20))
rand_p = 0;
}
if (ibz_4x5_right_ker_mod_prime(&ker,&mat,&prime)){
for(int i = 0; i < 4; i++){
ibz_set(&sum,0);
for(int j = 0; j < 5; j++){
ibz_mul(&prod,&(mat[i][j]),&(ker[j]));
ibz_add(&sum,&sum,&prod);
ibz_mod(&sum,&sum,&prime);
}
res = res || !ibz_is_zero(&sum);
}
}
}
if (res != 0){
printf("Quaternion unit test with randomization for ibz_4x5_right_ker_mod_prime failed\n");
}
ibz_finalize(&sum);
ibz_finalize(&prod);
ibz_vec_5_finalize(&ker);
ibz_mat_4x5_finalize(&mat);
ibz_finalize(&prime);
return(res);
}
//int ibz_4x4_right_ker_mod_prime(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, const ibz_t *p);
int quat_test_randomized_ibz_4x4_right_ker_mod_prime(){
int res = 0;
ibz_t prime;
ibz_mat_4x4_t mat, rank3, rank2;
ibz_vec_4_t ker;
ibz_t prod, sum,det;
ibz_init(&prod);
ibz_init(&sum);
ibz_init(&det);
ibz_mat_4x4_init(&mat);
ibz_mat_4x4_init(&rank3);
ibz_mat_4x4_init(&rank2);
ibz_vec_4_init(&ker);
ibz_init(&prime);
int64_t rand[4][4];
int64_t rand_p;
ibz_mat_4x4_identity(&rank3);
ibz_set(&(rank3[3][3]),0);
ibz_mat_4x4_identity(&rank2);
ibz_set(&(rank2[3][3]),0);
ibz_set(&(rank2[2][2]),0);
// test case where always 1 solution
for (int iter = 0; iter < 100; iter++){
rand_p = 0;
while(rand_p ==0) {
int randret = randombytes((unsigned char*)&rand_p, sizeof(uint64_t));
if (randret != 0)
return 1;
ibz_set(&prime,rand_p);
if(!ibz_probab_prime(&prime,20))
rand_p = 0;
}
// generate random invertible matrix
ibz_set(&det,0);
while(ibz_is_zero(&det)){
int randret = randombytes((unsigned char*)rand, 4*4*sizeof(uint64_t));
if (randret != 0)
return 1;
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(mat[i][j]),rand[j][i]);
}
}
ibz_mat_4x4_inv_with_det_as_denom(NULL,&det,&mat);
ibz_mod(&det,&det,&prime);
}
// rank 4 matrix does not work
res = res || ibz_4x4_right_ker_mod_prime(&ker,&mat,&prime);
// multiply with rank3 to get random rank 3 matrix
ibz_mat_4x4_mul(&mat,&mat,&rank3);
if (ibz_4x4_right_ker_mod_prime(&ker,&mat,&prime)){
for(int i = 0; i < 4; i++){
ibz_set(&sum,0);
for(int j = 0; j < 4; j++){
ibz_mul(&prod,&(mat[i][j]),&(ker[j]));
ibz_add(&sum,&sum,&prod);
ibz_mod(&sum,&sum,&prime);
}
res = res || !ibz_is_zero(&sum);
}
} else {
res = 1;
}
// multiply with rank2 to get matrix of too small rank
ibz_mat_4x4_mul(&mat,&mat,&rank2);
res = res || ibz_4x4_right_ker_mod_prime(&ker,&mat,&prime);
ibz_mat_4x4_mul(&mat,&mat,&rank2);
res = res || ibz_4x4_right_ker_mod_prime(&ker,&mat,&prime);
ibz_mat_4x4_mul(&mat,&mat,&rank2);
res = res || ibz_4x4_right_ker_mod_prime(&ker,&mat,&prime);
}
if (res != 0){
printf("Quaternion unit test with randomization for ibz_4x4_right_ker_mod_prime failed\n");
}
ibz_finalize(&sum);
ibz_finalize(&prod);
ibz_finalize(&det);
ibz_vec_4_finalize(&ker);
ibz_mat_4x4_finalize(&mat);
ibz_mat_4x4_finalize(&rank3);
ibz_mat_4x4_finalize(&rank2);
ibz_finalize(&prime);
return(res);
}
//int ibz_4x4_right_ker_mod_power_of_2(ibz_vec_4_t *ker, const ibz_mat_4x4_t *mat, unsigned short exp);
int quat_test_randomized_ibz_4x4_right_ker_mod_power_of_2(){
// this only tests that if a vector is returned, it fullfills the conditions
int res = 0;
int zero = 1;
short exp;
int64_t rand[4][4];
uint64_t rand_exp;
ibz_mat_4x4_t mat, rank3;
ibz_vec_4_t ker;
ibz_vec_4_t prod;
ibz_t q, r, two, det;
ibz_mat_4x4_init(&mat);
ibz_mat_4x4_init(&rank3);
ibz_vec_4_init(&ker);
ibz_vec_4_init(&prod);
ibz_init(&q);
ibz_init(&r);
ibz_init(&det);
ibz_init(&two);
ibz_set(&two,2);
ibz_mat_4x4_identity(&rank3);
ibz_set(&(rank3[3][3]),0);
for (int iter = 0; iter < 100; iter++){
rand_exp = 0;
while(rand_exp <=0) {
int randret = randombytes((unsigned char*)&rand_exp, sizeof(uint64_t));
if (randret != 0)
return 1;
exp = ((short) ((unsigned char) rand_exp));
}
// generate random invertible matrix
ibz_set(&det,0);
while(ibz_is_zero(&det)){
int randret = randombytes((unsigned char*)rand, 4*4*sizeof(uint64_t));
if (randret != 0)
return 1;
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(mat[i][j]),rand[j][i]);
}
}
ibz_mat_4x4_inv_with_det_as_denom(NULL,&det,&mat);
ibz_div(&q,&det,&det,&two);
}
// rank 4 matrix does not work
res = res || ibz_4x4_right_ker_mod_power_of_2(&ker,&mat,exp);
// multiply with rank3 to get random rank 3 matrix
ibz_mat_4x4_mul(&mat,&mat,&rank3);
if (ibz_4x4_right_ker_mod_power_of_2(&ker, &mat, exp)){
zero = 1;
ibz_mat_4x4_eval(&prod,&mat,&ker);
for (int i = 0; i < 4; i++){
ibz_div(&q,&r,&(ker[i]),&two);
zero = zero && ibz_is_zero(&r);
ibz_pow(&q,&two,exp);
ibz_mod(&(prod[i]),&(prod[i]),&q);
}
res |= !quat_alg_coord_is_zero(&prod);
res |= zero;
}
}
if (res != 0){
printf("Quaternion unit test with randomization for ibz_4x4_right_ker_mod_power_of_2 failed\n");
}
ibz_mat_4x4_finalize(&mat);
ibz_mat_4x4_finalize(&rank3);
ibz_vec_4_finalize(&ker);
ibz_vec_4_finalize(&prod);
ibz_finalize(&q);
ibz_finalize(&r);
ibz_finalize(&two);
ibz_finalize(&det);
return(res);
}
//int quat_lattice_lll(ibz_mat_4x4_t *red, const quat_lattice_t *lattice, const ibz_t *q, int precision);
int quat_test_randomized_lattice_lll(){
int res = 0;
quat_lattice_t lat, test;
ibz_mat_4x4_t red;
ibz_t num, denom, q, det;
ibq_t coeff;
int64_t rand[4][4];
uint32_t rand_q, rand_prec, rand_denom;
ibz_init(&num);
ibz_init(&denom);
ibz_init(&q);
ibz_init(&det);
ibq_init(&coeff);
ibz_mat_4x4_init(&red);
quat_lattice_init(&lat);
quat_lattice_init(&test);
ibz_set(&num,3);
ibz_set(&denom,4);
ibq_set(&coeff,&num,&denom);
for (int iter = 0; iter < 20; iter++){
rand_denom = 0;
while(rand_denom ==0) {
int randret = randombytes((unsigned char*)&rand_denom, sizeof(uint32_t));
if (randret != 0)
return 1;
}
int randret = randombytes((unsigned char*)&rand_q, sizeof(uint32_t));
if (randret != 0)
return 1;
// generate random invertible matrix
ibz_set(&det,0);
while(ibz_is_zero(&det)){
int randret = randombytes((unsigned char*)rand, 4*4*sizeof(uint64_t));
if (randret != 0)
return 1;
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),rand[j][i]);
}
}
ibz_mat_4x4_inv_with_det_as_denom(NULL,&det,&(lat.basis));
}
// set lattice
ibz_set(&lat.denom, rand_denom);
quat_lattice_hnf(&lat);
// set other parameter
ibz_set(&q,rand_q % 1024);
//reduce
res = res || quat_lattice_lll(&red,&lat,&q,0);
// test lll reduced
res = res || !quat_dim4_lll_verify(&red,&coeff,&q);
// test lattice equality
ibz_copy(&(test.denom),&(lat.denom));
ibz_mat_4x4_copy(&(test.basis),&(red));
quat_lattice_hnf(&test);
res = res || !quat_lattice_equal(&test,&lat);
}
if (res != 0){
printf("Quaternion unit test with randomization for lattice_lll failed\n");
}
ibz_finalize(&num);
ibz_finalize(&denom);
ibz_finalize(&q);
ibz_finalize(&det);
ibq_finalize(&coeff);
ibz_mat_4x4_finalize(&red);
quat_lattice_finalize(&lat);
quat_lattice_finalize(&test);
return(res);
}
//int quat_lattice_contains_without_alg(quat_alg_coord_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x);
int quat_test_randomized_lattice_contains_without_alg(){
// only tests the case where the element is in the lattice
int res = 0;
ibz_t det;
quat_alg_elem_t x, cmp;
quat_alg_coord_t coord;
quat_lattice_t lat;
uint64_t rand_denom;
int64_t rand[5][4];
ibz_init(&det);
quat_alg_coord_init(&coord);
quat_alg_elem_init(&cmp);
quat_alg_elem_init(&x);
quat_lattice_init(&lat);
for (int iter = 0; iter < 10; iter++){
rand_denom = 0;
while(rand_denom ==0) {
int randret = randombytes((unsigned char*)&rand_denom, sizeof(uint64_t));
if (randret != 0)
return 1;
}
// generate random invertible matrix
ibz_set(&det,0);
while(ibz_is_zero(&det)){
int randret = randombytes((unsigned char*)rand, 5*4*sizeof(uint64_t));
if (randret != 0)
return 1;
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
ibz_set(&(lat.basis[i][j]),rand[j][i]);
}
ibz_set(&(coord[i]),rand[4][i]);
}
ibz_mat_4x4_inv_with_det_as_denom(NULL,&det,&(lat.basis));
}
ibz_set(&(lat.denom),rand_denom);
quat_lattice_hnf(&lat);
ibz_mat_4x4_eval(&(x.coord),&(lat.basis),&coord);
ibz_copy(&(x.denom),&(lat.denom));
ibz_vec_4_set(&coord,1,0,1,0);
if(quat_lattice_contains_without_alg(&coord,&lat,&x)){
ibz_mat_4x4_eval(&(cmp.coord),&(lat.basis),&coord);
ibz_copy(&(cmp.denom),&(lat.denom));
quat_alg_sub(&cmp,&x,&cmp);
res = res || !quat_alg_elem_is_zero(&cmp);
} else {
res = 1;
}
}
if (res != 0){
printf("Quaternion unit test with randomization for lattice_contains_without_alg failed\n");
}
ibz_finalize(&det);
quat_alg_coord_finalize(&coord);
quat_alg_elem_finalize(&x);
quat_alg_elem_finalize(&cmp);
quat_lattice_finalize(&lat);
return(res);
}
//int ibz_cornacchia_extended(ibz_t *x, ibz_t *y, const ibz_t *n, const short *prime_list, const int prime_list_length, short primality_test_iterations, const ibz_t *bad_primes_prod);
int quat_test_randomized_ibz_cornacchia_extended(){
int res = 0;
ibz_t x,y,n, prod,c_res,bad, p;
int counter = 1;
int counter_p = 3;
int counter_good = 1;
short rand_exps[100];
int64_t rand_fact;
short primes[100];
short good_primes[100];
int primes_length = 100;
short iterations = 20;
ibz_init(&x);
ibz_init(&y);
ibz_init(&n);
ibz_init(&p);
ibz_init(&prod);
ibz_init(&c_res);
ibz_init(&bad);
ibz_set(&bad,1);
good_primes[0] = 2;
primes[0] = 2;
while(counter <100){
ibz_set(&p,counter_p);
if(ibz_probab_prime(&p,20)){
primes[counter]=(short) counter_p;
if(counter_p%4 == 3){
ibz_mul(&bad,&bad,&p);
} else {
good_primes[counter_good]=(short) counter_p;
counter_good++;
}
counter++;
}
counter_p++;
}
for (int iter = 0; iter < 1000; iter++){
ibz_set(&n,1);
int randret = randombytes((unsigned char*)&rand_exps, 100*sizeof(short));
for(int i = 0; i < counter_good; i++){
ibz_set(&p,good_primes[i]);
ibz_pow(&p,&p,((int)((unsigned short)rand_exps[i])%16));
ibz_mul(&n,&n,&p);
}
// sample large prime as last factor
rand_fact = 0;
while(rand_fact == 0) {
int randret = randombytes((unsigned char*)&rand_fact, sizeof(uint64_t));
if (randret != 0)
return 1;
if(rand_fact < 0)
rand_fact = - rand_fact;
ibz_set(&p,rand_fact);
if(!ibz_probab_prime(&p,20))
rand_fact = 0;
}
ibz_mul(&n,&n,&p);
ibz_set(&prod,4);
ibz_mod(&prod,&p,&prod);
if((ibz_probab_prime(&p,100) || (ibz_is_one(&p))) && (ibz_is_one(&prod))){
res = res || (!ibz_cornacchia_extended(&x,&y,&n,primes,100,100,&bad));
if(res){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
}
//(this test depends on the primality test)
// now it is not a prime factor any more
ibz_set(&p,rand_fact);
ibz_mul(&n,&n,&p);
res = res || ibz_cornacchia_extended(&x,&y,&n,primes,100,1000,&bad);
// multiply with random bad primes
ibz_div(&n,&p,&n,&p);
int randret = randombytes((unsigned char*)&rand_exps, 100*sizeof(short));
if (randret != 0)
return 1;
for(int i = 0; i < 100; i++){
ibz_set(&p,primes[i]);
ibz_pow(&p,&p,((int)((unsigned short)rand_exps[i])%8));
ibz_mul(&n,&n,&p);
}
ibz_gcd(&p,&n,&bad);
if(ibz_is_one(&p)){
res = res || !ibz_cornacchia_extended(&x,&y,&n,primes,100,100,&bad);
if(res){
ibz_mul(&c_res,&x,&x);
ibz_mul(&prod,&y,&y);
ibz_add(&c_res,&c_res,&prod);
res = res || ibz_cmp(&n,&c_res);
}
} else {
res = res || ibz_cornacchia_extended(&x,&y,&n,primes,100,100,&bad);
}
} else {
res = res || ibz_cornacchia_extended(&x,&y,&n,primes,100,1000,&bad);
for(int i = 0; i < 100; i++){
ibz_set(&p,primes[i]);
ibz_pow(&p,&p,((int)((unsigned short)rand_exps[i])%8));
ibz_mul(&n,&n,&p);
}
ibz_gcd(&p,&n,&bad);
res = res || ibz_cornacchia_extended(&x,&y,&n,primes,100,100,&bad);
}
}
if (res != 0){
printf("Quaternion unit test with randomization for ibz_cornacchia_extended failed\n");
}
ibz_finalize(&x);
ibz_finalize(&y);
ibz_finalize(&n);
ibz_finalize(&p);
ibz_finalize(&prod);
ibz_finalize(&c_res);
ibz_finalize(&bad);
return res;
}
// run all previous tests
int quat_test_with_randomization(){
int res = 0;
printf("\nRunning randomized tests from quaternion module\n");
res = res | quat_test_randomized_ibz_mat_2x2_inv_mod();
res = res | quat_test_randomized_2x2_lattice_enumerate_cvp_filter();
res = res | quat_test_randomized_ibz_mat_4x4_inv_with_det_as_denom();
res = res | quat_test_randomized_ibz_mat_4x8_hnf_core();
res = res | quat_test_randomized_ibz_4x5_right_ker_mod_prime();
res = res | quat_test_randomized_ibz_4x4_right_ker_mod_prime();
res = res | quat_test_randomized_ibz_4x4_right_ker_mod_power_of_2();
res = res | quat_test_randomized_lattice_lll();
res = res | quat_test_randomized_lattice_contains_without_alg();
res = res | quat_test_randomized_ibz_cornacchia_extended();
return(res);
}

View File

@@ -0,0 +1,20 @@
#include "quaternion_tests.h"
// run all tests in module
int main(){
int res = 0;
printf("Running quaternion module unit tests\n");
res = res | quat_test_finit();
res = res | quat_test_dim4();
res = res | quat_test_dim2();
res = res | quat_test_matkermod();
res = res | quat_test_integers();
res = res | quat_test_algebra();
res = res | quat_test_lattice();
res = res | quat_test_lideal();
res = res | quat_test_with_randomization();
if(res != 0){
printf("\nSome tests failed!\n");
}
return(res);
}