second-round version of SQIsign

Co-authored-by: Marius A. Aardal <marius.andre.aardal@gmail.com>
Co-authored-by: Gora Adj <gora.adj@tii.ae>
Co-authored-by: Diego F. Aranha <dfaranha@cs.au.dk>
Co-authored-by: Andrea Basso <sqisign@andreabasso.com>
Co-authored-by: Isaac Andrés Canales Martínez <icanalesm0500@gmail.com>
Co-authored-by: Jorge Chávez-Saab <jorgechavezsaab@gmail.com>
Co-authored-by: Maria Corte-Real Santos <mariascrsantos98@gmail.com>
Co-authored-by: Luca De Feo <github@defeo.lu>
Co-authored-by: Max Duparc <max.duparc@epfl.ch>
Co-authored-by: Jonathan Komada Eriksen <jonathan.eriksen97@gmail.com>
Co-authored-by: Décio Luiz Gazzoni Filho <decio@decpp.net>
Co-authored-by: Basil Hess <bhe@zurich.ibm.com>
Co-authored-by: Antonin Leroux <antonin.leroux@polytechnique.org>
Co-authored-by: Patrick Longa <plonga@microsoft.com>
Co-authored-by: Luciano Maino <mainoluciano.96@gmail.com>
Co-authored-by: Michael Meyer <michael@random-oracles.org>
Co-authored-by: Hiroshi Onuki <onuki@mist.i.u-tokyo.ac.jp>
Co-authored-by: Lorenz Panny <lorenz@yx7.cc>
Co-authored-by: Giacomo Pope <giacomopope@gmail.com>
Co-authored-by: Krijn Reijnders <reijnderskrijn@gmail.com>
Co-authored-by: Damien Robert <damien.robert@inria.fr>
Co-authored-by: Francisco Rodríguez-Henriquez <francisco.rodriguez@tii.ae>
Co-authored-by: Sina Schaeffler <sschaeffle@student.ethz.ch>
Co-authored-by: Benjamin Wesolowski <benjamin.wesolowski@ens-lyon.fr>
This commit is contained in:
SQIsign team
2025-02-06 00:00:00 +00:00
committed by Lorenz Panny
parent ff34a8cd18
commit 91e9e464fe
481 changed files with 80785 additions and 55963 deletions

View File

@@ -1,18 +1,29 @@
set(SOURCE_FILES_QUATERNION_GENERIC_REF
intbig.c
algebra.c
ideal.c
dim4.c
dim2.c
integers.c
lattice.c
lat_ball.c
finit.c
printer.c
lll.c
matkermod.c
lll/rationals.c
lll/l2.c
lll/lll_verification.c
lll/lll_applications.c
lll/rationals.c
normeq.c
hnf/ibz_division.c
hnf/hnf_internal.c
hnf/hnf.c
test/random_input_generation.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})
add_library(${LIB_QUATERNION} STATIC ${SOURCE_FILES_QUATERNION_GENERIC_REF})
target_link_libraries(${LIB_QUATERNION} GMP m)
target_include_directories(${LIB_QUATERNION} PRIVATE common ${INC_PUBLIC} ${INC_COMMON} ${INC_QUATERNION} internal_quaternion_headers)
target_compile_options(${LIB_QUATERNION} PRIVATE ${C_OPT_FLAGS})
add_subdirectory(test)

View File

@@ -1,287 +1,280 @@
#include <quaternion.h>
#include "internal.h"
//Internal helper functions
// 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_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_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_coord_mul(ibz_vec_4_t *res, const ibz_vec_4_t *a, const ibz_vec_4_t *b, const quat_alg_t *alg)
{
ibz_t prod;
ibz_vec_4_t sum;
ibz_init(&prod);
ibz_vec_4_init(&sum);
ibz_set(&(sum[0]), 0);
ibz_set(&(sum[1]), 0);
ibz_set(&(sum[2]), 0);
ibz_set(&(sum[3]), 0);
// compute 1 coordinate
ibz_mul(&prod, &((*a)[2]), &((*b)[2]));
ibz_sub(&(sum[0]), &(sum[0]), &prod);
ibz_mul(&prod, &((*a)[3]), &((*b)[3]));
ibz_sub(&(sum[0]), &(sum[0]), &prod);
ibz_mul(&(sum[0]), &(sum[0]), &(alg->p));
ibz_mul(&prod, &((*a)[0]), &((*b)[0]));
ibz_add(&(sum[0]), &(sum[0]), &prod);
ibz_mul(&prod, &((*a)[1]), &((*b)[1]));
ibz_sub(&(sum[0]), &(sum[0]), &prod);
// compute i coordiante
ibz_mul(&prod, &((*a)[2]), &((*b)[3]));
ibz_add(&(sum[1]), &(sum[1]), &prod);
ibz_mul(&prod, &((*a)[3]), &((*b)[2]));
ibz_sub(&(sum[1]), &(sum[1]), &prod);
ibz_mul(&(sum[1]), &(sum[1]), &(alg->p));
ibz_mul(&prod, &((*a)[0]), &((*b)[1]));
ibz_add(&(sum[1]), &(sum[1]), &prod);
ibz_mul(&prod, &((*a)[1]), &((*b)[0]));
ibz_add(&(sum[1]), &(sum[1]), &prod);
// compute j coordiante
ibz_mul(&prod, &((*a)[0]), &((*b)[2]));
ibz_add(&(sum[2]), &(sum[2]), &prod);
ibz_mul(&prod, &((*a)[2]), &((*b)[0]));
ibz_add(&(sum[2]), &(sum[2]), &prod);
ibz_mul(&prod, &((*a)[1]), &((*b)[3]));
ibz_sub(&(sum[2]), &(sum[2]), &prod);
ibz_mul(&prod, &((*a)[3]), &((*b)[1]));
ibz_add(&(sum[2]), &(sum[2]), &prod);
// compute ij coordiante
ibz_mul(&prod, &((*a)[0]), &((*b)[3]));
ibz_add(&(sum[3]), &(sum[3]), &prod);
ibz_mul(&prod, &((*a)[3]), &((*b)[0]));
ibz_add(&(sum[3]), &(sum[3]), &prod);
ibz_mul(&prod, &((*a)[2]), &((*b)[1]));
ibz_sub(&(sum[3]), &(sum[3]), &prod);
ibz_mul(&prod, &((*a)[1]), &((*b)[2]));
ibz_add(&(sum[3]), &(sum[3]), &prod);
ibz_copy(&((*res)[0]), &(sum[0]));
ibz_copy(&((*res)[1]), &(sum[1]));
ibz_copy(&((*res)[2]), &(sum[2]));
ibz_copy(&((*res)[3]), &(sum[3]));
ibz_finalize(&prod);
ibz_vec_4_finalize(&sum);
}
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);
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++) {
// 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]);
// 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));
}
quat_alg_elem_finalize(&e);
quat_alg_elem_finalize(&res);
// 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);
}
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));
// Public Functions
quat_alg_elem_finalize(&conj);
quat_alg_elem_finalize(&norm);
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));
ibz_vec_4_add(&(res->coord), &(res_a.coord), &(res_b.coord));
quat_alg_elem_finalize(&res_a);
quat_alg_elem_finalize(&res_b);
}
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_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);
ibz_vec_4_sub(&res->coord, &res_a.coord, &res_b.coord);
quat_alg_elem_finalize(&res_a);
quat_alg_elem_finalize(&res_b);
}
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_mul(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b, const quat_alg_t *alg)
{
// denominator: product of denominators
ibz_mul(&(res->denom), &(a->denom), &(b->denom));
quat_alg_coord_mul(&(res->coord), &(a->coord), &(b->coord), alg);
}
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]));
void
quat_alg_norm(ibz_t *res_num, ibz_t *res_denom, const quat_alg_elem_t *a, const quat_alg_t *alg)
{
ibz_t r, g;
quat_alg_elem_t norm;
ibz_init(&r);
ibz_init(&g);
quat_alg_elem_init(&norm);
quat_alg_conj(&norm, a);
quat_alg_mul(&norm, a, &norm, alg);
ibz_gcd(&g, &(norm.coord[0]), &(norm.denom));
ibz_div(res_num, &r, &(norm.coord[0]), &g);
ibz_div(res_denom, &r, &(norm.denom), &g);
ibz_abs(res_denom, res_denom);
ibz_abs(res_num, res_num);
assert(ibz_cmp(res_denom, &ibz_const_zero) > 0);
quat_alg_elem_finalize(&norm);
ibz_finalize(&r);
ibz_finalize(&g);
}
//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_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_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]));
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]));
}
void
quat_alg_make_primitive(ibz_vec_4_t *primitive_x, ibz_t *content, const quat_alg_elem_t *x, const quat_lattice_t *order)
{
int ok UNUSED = quat_lattice_contains(primitive_x, order, x);
assert(ok);
ibz_vec_4_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_neg(&(x->denom),&(x->denom));
}
ibz_finalize(&gcd);
ibz_finalize(&r);
ibz_finalize(&zero);
ibz_finalize(&r);
}
int quat_alg_elem_is_zero(const quat_alg_elem_t *x){
int res = quat_alg_coord_is_zero(&(x->coord));
return(res);
void
quat_alg_normalize(quat_alg_elem_t *x)
{
ibz_t gcd, sign, r;
ibz_init(&gcd);
ibz_init(&sign);
ibz_init(&r);
ibz_vec_4_content(&gcd, &(x->coord));
ibz_gcd(&gcd, &gcd, &(x->denom));
ibz_div(&(x->denom), &r, &(x->denom), &gcd);
ibz_vec_4_scalar_div(&(x->coord), &gcd, &(x->coord));
ibz_set(&sign, 2 * (0 > ibz_cmp(&ibz_const_zero, &(x->denom))) - 1);
ibz_vec_4_scalar_mul(&(x->coord), &sign, &(x->coord));
ibz_mul(&(x->denom), &sign, &(x->denom));
ibz_finalize(&gcd);
ibz_finalize(&sign);
ibz_finalize(&r);
}
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);
int
quat_alg_elem_equal(const quat_alg_elem_t *a, const quat_alg_elem_t *b)
{
quat_alg_elem_t diff;
quat_alg_elem_init(&diff);
quat_alg_sub(&diff, a, b);
int res = quat_alg_elem_is_zero(&diff);
quat_alg_elem_finalize(&diff);
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);
int
quat_alg_elem_is_zero(const quat_alg_elem_t *x)
{
int res = ibz_vec_4_is_zero(&(x->coord));
return (res);
}
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){
void
quat_alg_elem_set(quat_alg_elem_t *elem, int32_t denom, int32_t coord0, int32_t coord1, int32_t coord2, int32_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);
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));
void
quat_alg_elem_copy(quat_alg_elem_t *copy, const quat_alg_elem_t *copied)
{
ibz_copy(&copy->denom, &copied->denom);
ibz_copy(&copy->coord[0], &copied->coord[0]);
ibz_copy(&copy->coord[1], &copied->coord[1]);
ibz_copy(&copy->coord[2], &copied->coord[2]);
ibz_copy(&copy->coord[3], &copied->coord[3]);
}
// 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_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

@@ -1,40 +1,66 @@
#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);
// 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_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){
void
ibz_mat_2x2_copy(ibz_mat_2x2_t *copy, const ibz_mat_2x2_t *copied)
{
ibz_copy(&((*copy)[0][0]), &((*copied)[0][0]));
ibz_copy(&((*copy)[0][1]), &((*copied)[0][1]));
ibz_copy(&((*copy)[1][0]), &((*copied)[1][0]));
ibz_copy(&((*copy)[1][1]), &((*copied)[1][1]));
}
void
ibz_mat_2x2_add(ibz_mat_2x2_t *sum, const ibz_mat_2x2_t *a, const ibz_mat_2x2_t *b)
{
ibz_add(&((*sum)[0][0]), &((*a)[0][0]), &((*b)[0][0]));
ibz_add(&((*sum)[0][1]), &((*a)[0][1]), &((*b)[0][1]));
ibz_add(&((*sum)[1][0]), &((*a)[1][0]), &((*b)[1][0]));
ibz_add(&((*sum)[1][1]), &((*a)[1][1]), &((*b)[1][1]));
}
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_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){
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_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_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_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);
@@ -43,511 +69,64 @@ void ibz_mat_2x2_eval(ibz_vec_2_t *res, const ibz_mat_2x2_t *mat, const ibz_vec_
// 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){
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++) {
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++) {
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]));
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;
int
ibz_mat_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_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);
// return 0 matrix if non invertible determinant
ibz_set(&prod, res);
ibz_mul(&det, &det, &prod);
// compute inverse
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);
return (res);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,158 +1,122 @@
#include <quaternion.h>
#include "internal.h"
void quat_alg_init_set(quat_alg_t *alg, const ibz_t *p){
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){
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);
void
quat_alg_elem_init(quat_alg_elem_t *elem)
{
ibz_vec_4_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);
void
quat_alg_elem_finalize(quat_alg_elem_t *elem)
{
ibz_vec_4_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
ibz_vec_2_init(ibz_vec_2_t *vec)
{
ibz_init(&((*vec)[0]));
ibz_init(&((*vec)[1]));
}
void
ibz_vec_2_finalize(ibz_vec_2_t *vec)
{
ibz_finalize(&((*vec)[0]));
ibz_finalize(&((*vec)[1]));
}
void
ibz_vec_4_init(ibz_vec_4_t *vec)
{
for (int i = 0; i < 4; i++) {
ibz_init(&(*vec)[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_finalize(ibz_vec_4_t *vec)
{
for (int i = 0; i < 4; i++) {
ibz_finalize(&(*vec)[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++){
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++){
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++){
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++){
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){
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){
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){
void
quat_left_ideal_init(quat_left_ideal_t *lideal)
{
quat_lattice_init(&(*lideal).lattice);
ibz_init(&(*lideal).norm);
(*lideal).parent_order=NULL;
(*lideal).parent_order = NULL;
}
void quat_left_ideal_finalize(quat_left_ideal_t *lideal){
void
quat_left_ideal_finalize(quat_left_ideal_t *lideal)
{
ibz_finalize(&(*lideal).norm);
quat_lattice_finalize(&(*lideal).lattice);
}

View File

@@ -0,0 +1,210 @@
#include "hnf_internal.h"
#include "internal.h"
// HNF test function
int
ibz_mat_4x4_is_hnf(const ibz_mat_4x4_t *mat)
{
int res = 1;
int found;
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;
}
// Untested HNF helpers
// centered mod
void
ibz_vec_4_linear_combination_mod(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,
const ibz_t *mod)
{
ibz_t prod, m;
ibz_vec_4_t sums;
ibz_vec_4_init(&sums);
ibz_init(&prod);
ibz_init(&m);
ibz_copy(&m, mod);
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);
ibz_centered_mod(&(sums[i]), &(sums[i]), &m);
}
for (int i = 0; i < 4; i++) {
ibz_copy(&((*lc)[i]), &(sums[i]));
}
ibz_finalize(&prod);
ibz_finalize(&m);
ibz_vec_4_finalize(&sums);
}
void
ibz_vec_4_copy_mod(ibz_vec_4_t *res, const ibz_vec_4_t *vec, const ibz_t *mod)
{
ibz_t m;
ibz_init(&m);
ibz_copy(&m, mod);
for (int i = 0; i < 4; i++) {
ibz_centered_mod(&((*res)[i]), &((*vec)[i]), &m);
}
ibz_finalize(&m);
}
// no need to center this, and not 0
void
ibz_vec_4_scalar_mul_mod(ibz_vec_4_t *prod, const ibz_t *scalar, const ibz_vec_4_t *vec, const ibz_t *mod)
{
ibz_t m, s;
ibz_init(&m);
ibz_init(&s);
ibz_copy(&s, scalar);
ibz_copy(&m, mod);
for (int i = 0; i < 4; i++) {
ibz_mul(&((*prod)[i]), &((*vec)[i]), &s);
ibz_mod(&((*prod)[i]), &((*prod)[i]), &m);
}
ibz_finalize(&m);
ibz_finalize(&s);
}
// Algorithm used is the one at number 2.4.8 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_4xn_hnf_mod_core(ibz_mat_4x4_t *hnf, int generator_number, const ibz_vec_4_t *generators, const ibz_t *mod)
{
int i = 3;
assert(generator_number > 3);
int n = generator_number;
int j = n - 1;
int k = n - 1;
ibz_t b, u, v, d, q, m, coeff_1, coeff_2, r;
ibz_vec_4_t c;
ibz_vec_4_t a[generator_number];
ibz_vec_4_t w[4];
ibz_init(&b);
ibz_init(&d);
ibz_init(&u);
ibz_init(&v);
ibz_init(&r);
ibz_init(&m);
ibz_init(&q);
ibz_init(&coeff_1);
ibz_init(&coeff_2);
ibz_vec_4_init(&c);
for (int h = 0; h < n; h++) {
if (h < 4)
ibz_vec_4_init(&(w[h]));
ibz_vec_4_init(&(a[h]));
ibz_copy(&(a[h][0]), &(generators[h][0]));
ibz_copy(&(a[h][1]), &(generators[h][1]));
ibz_copy(&(a[h][2]), &(generators[h][2]));
ibz_copy(&(a[h][3]), &(generators[h][3]));
}
assert(ibz_cmp(mod, &ibz_const_zero) > 0);
ibz_copy(&m, mod);
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 also, needs u non 0, but v can be 0 if needed
ibz_xgcd_with_u_not_0(&d, &u, &v, &(a[k][i]), &(a[j][i]));
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_mod(
&(a[j]), &coeff_1, &(a[j]), &coeff_2, &(a[k]), &m); // do lin comb mod m
ibz_vec_4_copy_mod(&(a[k]), &c, &m); // mod m in copy
}
}
ibz_xgcd_with_u_not_0(&d, &u, &v, &(a[k][i]), &m);
ibz_vec_4_scalar_mul_mod(&(w[i]), &u, &(a[k]), &m); // mod m in scalar mult
if (ibz_is_zero(&(w[i][i]))) {
ibz_copy(&(w[i][i]), &m);
}
for (int h = i + 1; h < 4; h++) {
ibz_div_floor(&q, &r, &(w[h][i]), &(w[i][i]));
ibz_neg(&q, &q);
ibz_vec_4_linear_combination(&(w[h]), &ibz_const_one, &(w[h]), &q, &(w[i]));
}
ibz_div(&m, &r, &m, &d);
assert(ibz_is_zero(&r));
if (i != 0) {
k = k - 1;
i = i - 1;
j = k;
if (ibz_is_zero(&(a[k][i])))
ibz_copy(&(a[k][i]), &m);
} else {
k = k - 1;
i = i - 1;
j = k;
}
}
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
ibz_copy(&((*hnf)[i][j]), &(w[j][i]));
}
}
ibz_finalize(&b);
ibz_finalize(&d);
ibz_finalize(&u);
ibz_finalize(&v);
ibz_finalize(&r);
ibz_finalize(&q);
ibz_finalize(&coeff_1);
ibz_finalize(&coeff_2);
ibz_finalize(&m);
ibz_vec_4_finalize(&c);
for (int h = 0; h < n; h++) {
if (h < 4)
ibz_vec_4_finalize(&(w[h]));
ibz_vec_4_finalize(&(a[h]));
}
}

View File

@@ -0,0 +1,182 @@
#include "hnf_internal.h"
#include "internal.h"
// Small helper for integers
void
ibz_mod_not_zero(ibz_t *res, const ibz_t *x, const ibz_t *mod)
{
ibz_t m, t;
ibz_init(&m);
ibz_init(&t);
ibz_mod(&m, x, mod);
ibz_set(&t, ibz_is_zero(&m));
ibz_mul(&t, &t, mod);
ibz_add(res, &m, &t);
ibz_finalize(&m);
ibz_finalize(&t);
}
// centered and rather positive then negative
void
ibz_centered_mod(ibz_t *remainder, const ibz_t *a, const ibz_t *mod)
{
assert(ibz_cmp(mod, &ibz_const_zero) > 0);
ibz_t tmp, d, t;
ibz_init(&tmp);
ibz_init(&d);
ibz_init(&t);
ibz_div_floor(&d, &tmp, mod, &ibz_const_two);
ibz_mod_not_zero(&tmp, a, mod);
ibz_set(&t, ibz_cmp(&tmp, &d) > 0);
ibz_mul(&t, &t, mod);
ibz_sub(remainder, &tmp, &t);
ibz_finalize(&tmp);
ibz_finalize(&d);
ibz_finalize(&t);
}
// if c, res = x, else res = y
void
ibz_conditional_assign(ibz_t *res, const ibz_t *x, const ibz_t *y, int c)
{
ibz_t s, t, r;
ibz_init(&r);
ibz_init(&s);
ibz_init(&t);
ibz_set(&s, c != 0);
ibz_sub(&t, &ibz_const_one, &s);
ibz_mul(&r, &s, x);
ibz_mul(res, &t, y);
ibz_add(res, &r, res);
ibz_finalize(&r);
ibz_finalize(&s);
ibz_finalize(&t);
}
// mpz_gcdext specification specifies unique outputs used here
void
ibz_xgcd_with_u_not_0(ibz_t *d, ibz_t *u, ibz_t *v, const ibz_t *x, const ibz_t *y)
{
if (ibz_is_zero(x) & ibz_is_zero(y)) {
ibz_set(d, 1);
ibz_set(u, 1);
ibz_set(v, 0);
return;
}
ibz_t q, r, x1, y1;
ibz_init(&q);
ibz_init(&r);
ibz_init(&x1);
ibz_init(&y1);
ibz_copy(&x1, x);
ibz_copy(&y1, y);
// xgcd
ibz_xgcd(d, u, v, &x1, &y1);
// make sure u!=0 (v can be 0 if needed)
// following GMP specification, u == 0 implies y|x
if (ibz_is_zero(u)) {
if (!ibz_is_zero(&x1)) {
if (ibz_is_zero(&y1)) {
ibz_set(&y1, 1);
}
ibz_div(&q, &r, &x1, &y1);
assert(ibz_is_zero(&r));
ibz_sub(v, v, &q);
}
ibz_set(u, 1);
}
if (!ibz_is_zero(&x1)) {
// Make sure ux > 0 (and as small as possible)
assert(ibz_cmp(d, &ibz_const_zero) > 0);
ibz_mul(&r, &x1, &y1);
int neg = ibz_cmp(&r, &ibz_const_zero) < 0;
ibz_mul(&q, &x1, u);
while (ibz_cmp(&q, &ibz_const_zero) <= 0) {
ibz_div(&q, &r, &y1, d);
assert(ibz_is_zero(&r));
if (neg) {
ibz_neg(&q, &q);
}
ibz_add(u, u, &q);
ibz_div(&q, &r, &x1, d);
assert(ibz_is_zero(&r));
if (neg) {
ibz_neg(&q, &q);
}
ibz_sub(v, v, &q);
ibz_mul(&q, &x1, u);
}
}
#ifndef NDEBUG
int res = 0;
ibz_t sum, prod, test, cmp;
ibz_init(&sum);
ibz_init(&prod);
ibz_init(&cmp);
ibz_init(&test);
// sign correct
res = res | !(ibz_cmp(d, &ibz_const_zero) >= 0);
if (ibz_is_zero(&x1) && ibz_is_zero(&y1)) {
res = res | !(ibz_is_zero(v) && ibz_is_one(u) && ibz_is_one(d));
} else {
if (!ibz_is_zero(&x1) && !ibz_is_zero(&y1)) {
// GCD divides x
ibz_div(&sum, &prod, &x1, d);
res = res | !ibz_is_zero(&prod);
// Small enough
ibz_mul(&prod, &x1, u);
res = res | !(ibz_cmp(&prod, &ibz_const_zero) > 0);
ibz_mul(&sum, &sum, &y1);
ibz_abs(&sum, &sum);
res = res | !(ibz_cmp(&prod, &sum) <= 0);
// GCD divides y
ibz_div(&sum, &prod, &y1, d);
res = res | !ibz_is_zero(&prod);
// Small enough
ibz_mul(&prod, &y1, v);
res = res | !(ibz_cmp(&prod, &ibz_const_zero) <= 0);
ibz_mul(&sum, &sum, &x1);
ibz_abs(&sum, &sum);
res = res | !(ibz_cmp(&prod, &sum) < 0);
} else {
// GCD divides x
ibz_div(&sum, &prod, &x1, d);
res = res | !ibz_is_zero(&prod);
// GCD divides y
ibz_div(&sum, &prod, &y1, d);
res = res | !ibz_is_zero(&prod);
if (ibz_is_zero(&x1) && !ibz_is_zero(&y1)) {
ibz_abs(&prod, v);
res = res | !(ibz_is_one(&prod));
res = res | !(ibz_is_one(u));
} else {
ibz_abs(&prod, u);
res = res | !(ibz_is_one(&prod));
res = res | !(ibz_is_zero(v));
}
}
// Bezout coeffs
ibz_mul(&sum, &x1, u);
ibz_mul(&prod, &y1, v);
ibz_add(&sum, &sum, &prod);
res = res | !(ibz_cmp(&sum, d) == 0);
}
assert(!res);
ibz_finalize(&sum);
ibz_finalize(&prod);
ibz_finalize(&cmp);
ibz_finalize(&test);
#endif
ibz_finalize(&x1);
ibz_finalize(&y1);
ibz_finalize(&q);
ibz_finalize(&r);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
#include "hnf_internal.h"
#if defined(MINI_GMP)
#include <mini-gmp.h>
#else
#include <gmp.h>
#endif
void
ibz_xgcd(ibz_t *gcd, ibz_t *u, ibz_t *v, const ibz_t *a, const ibz_t *b)
{
mpz_gcdext(*gcd, *u, *v, *a, *b);
}

View File

@@ -2,370 +2,322 @@
#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);
// assumes parent order and lattice correctly set, computes and sets the norm
void
quat_lideal_norm(quat_left_ideal_t *lideal)
{
quat_lattice_index(&(lideal->norm), &(lideal->lattice), (lideal->parent_order));
int ok UNUSED = ibz_sqrt(&(lideal->norm), &(lideal->norm));
assert(ok);
}
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) {
// assumes parent order and lattice correctly set, recomputes and verifies its norm
static int
quat_lideal_norm_verify(const quat_left_ideal_t *lideal)
{
int res;
ibz_t index;
ibz_init(&index);
quat_lattice_index(&index, &(lideal->lattice), (lideal->parent_order));
ibz_sqrt(&index, &index);
res = (ibz_cmp(&(lideal->norm), &index) == 0);
ibz_finalize(&index);
return (res);
}
void
quat_lideal_copy(quat_left_ideal_t *copy, const quat_left_ideal_t *copied)
{
copy->parent_order = copied->parent_order;
ibz_copy(&copy->norm, &copied->norm);
ibz_copy(&copy->lattice.denom, &copied->lattice.denom);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_copy(&copy->lattice.basis[i][j], &copied->lattice.basis[i][j]);
}
}
}
void
quat_lideal_create_principal(quat_left_ideal_t *lideal,
const quat_alg_elem_t *x,
const quat_lattice_t *order,
const quat_alg_t *alg)
{
assert(quat_order_is_maximal(order, alg));
assert(quat_lattice_contains(NULL, order, x));
ibz_t norm_n, norm_d;
ibz_init(&norm_n);
ibz_init(&norm_d);
// Multiply order on the right by x
quat_lattice_alg_elem_mul(&(lideal->lattice), order, x, alg);
// Reduce denominator. This conserves HNF
quat_lattice_reduce_denom(&lideal->lattice, &lideal->lattice);
// Compute norm and check it's integral
quat_alg_norm(&norm_n, &norm_d, x, alg);
assert(ibz_is_one(&norm_d));
ibz_copy(&lideal->norm, &norm_n);
// Set order
lideal->parent_order = order;
ibz_finalize(&norm_n);
ibz_finalize(&norm_d);
}
void
quat_lideal_create(quat_left_ideal_t *lideal,
const quat_alg_elem_t *x,
const ibz_t *N,
const quat_lattice_t *order,
const quat_alg_t *alg)
{
assert(quat_order_is_maximal(order, alg));
assert(!quat_alg_elem_is_zero(x));
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;
// Compute norm
quat_lideal_norm(lideal);
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;
int
quat_lideal_generator(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const quat_alg_t *alg)
{
ibz_t norm_int, norm_n, gcd, r, q, norm_denom;
ibz_vec_4_t vec;
ibz_vec_4_init(&vec);
ibq_init(&norm);
ibz_init(&norm_denom);
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 a, b, c, d;
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++){
int int_norm = 0;
while (1) {
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)
ibz_vec_4_set(&vec, a, b, c, d);
ibz_vec_4_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_int, &norm_denom, gen, alg);
assert(ibz_is_one(&norm_denom));
ibz_div(&q, &r, &norm_int, &(lideal->norm));
assert(ibz_is_zero(&r));
ibz_gcd(&gcd, &(lideal->norm), &q);
found = (0 == ibz_cmp(&gcd, &ibz_const_one));
if (found)
goto fin;
}
}
}
}
}
fin:;
fin:;
ibz_finalize(&r);
ibz_finalize(&q);
ibq_finalize(&norm);
ibz_finalize(&norm_denom);
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);
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];
void
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)
{
assert(quat_order_is_maximal((lideal->parent_order), alg));
ibz_t norm, norm_d;
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_init(&norm_d);
quat_lattice_alg_elem_mul(&(product->lattice), &(lideal->lattice), alpha, alg);
product->parent_order = lideal->parent_order;
quat_alg_norm(&norm, &norm_d, alpha, alg);
ibz_mul(&(product->norm), &(lideal->norm), &norm);
assert(ibz_divides(&(product->norm), &norm_d));
ibz_div(&(product->norm), &norm, &(product->norm), &norm_d);
assert(quat_lideal_norm_verify(lideal));
ibz_finalize(&norm_d);
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);
}
}
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);
assert(quat_order_is_maximal((I2->parent_order), alg));
quat_lattice_add(&sum->lattice, &I1->lattice, &I2->lattice);
sum->parent_order = I1->parent_order;
quat_lideal_norm(sum);
}
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);
assert(quat_order_is_maximal((I2->parent_order), alg));
quat_lattice_intersect(&inter->lattice, &I1->lattice, &I2->lattice);
inter->parent_order = I1->parent_order;
quat_lideal_norm(inter);
}
int
quat_lideal_equals(const quat_left_ideal_t *I1, const quat_left_ideal_t *I2, const quat_alg_t *alg)
{
assert(quat_order_is_maximal((I2->parent_order), alg));
assert(quat_order_is_maximal((I1->parent_order), alg));
return (I1->parent_order == I2->parent_order) & (ibz_cmp(&I1->norm, &I2->norm) == 0) &
quat_lattice_equal(&I1->lattice, &I2->lattice);
}
void
quat_lideal_inverse_lattice_without_hnf(quat_lattice_t *inv, const quat_left_ideal_t *lideal, const quat_alg_t *alg)
{
assert(quat_order_is_maximal((lideal->parent_order), alg));
quat_lattice_conjugate_without_hnf(inv, &(lideal->lattice));
ibz_mul(&(inv->denom), &(inv->denom), &(lideal->norm));
}
// following the implementation of ideal isomorphisms in the code of LearningToSQI's sage
// implementation of SQIsign
void
quat_lideal_right_transporter(quat_lattice_t *trans,
const quat_left_ideal_t *lideal1,
const quat_left_ideal_t *lideal2,
const quat_alg_t *alg)
{
assert(quat_order_is_maximal((lideal1->parent_order), alg));
assert(quat_order_is_maximal((lideal2->parent_order), alg));
assert(lideal1->parent_order == lideal2->parent_order);
quat_lattice_t inv;
quat_lattice_init(&inv);
quat_lideal_inverse_lattice_without_hnf(&inv, lideal1, alg);
quat_lattice_mul(trans, &inv, &(lideal2->lattice), alg);
quat_lattice_finalize(&inv);
}
void
quat_lideal_right_order(quat_lattice_t *order, const quat_left_ideal_t *lideal, const quat_alg_t *alg)
{
assert(quat_order_is_maximal((lideal->parent_order), alg));
quat_lideal_right_transporter(order, lideal, lideal, alg);
}
void
quat_lideal_class_gram(ibz_mat_4x4_t *G, const quat_left_ideal_t *lideal, const quat_alg_t *alg)
{
quat_lattice_gram(G, &(lideal->lattice), alg);
// divide by norm · denominator²
ibz_t divisor, rmd;
ibz_init(&divisor);
ibz_init(&rmd);
ibz_mul(&divisor, &(lideal->lattice.denom), &(lideal->lattice.denom));
ibz_mul(&divisor, &divisor, &(lideal->norm));
for (int i = 0; i < 4; i++) {
for (int j = 0; j <= i; j++) {
ibz_div(&(*G)[i][j], &rmd, &(*G)[i][j], &divisor);
assert(ibz_is_zero(&rmd));
}
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j <= i - 1; j++) {
ibz_copy(&(*G)[j][i], &(*G)[i][j]);
}
}
ibz_finalize(&rmd);
ibz_finalize(&divisor);
}
void
quat_lideal_conjugate_without_hnf(quat_left_ideal_t *conj,
quat_lattice_t *new_parent_order,
const quat_left_ideal_t *lideal,
const quat_alg_t *alg)
{
quat_lideal_right_order(new_parent_order, lideal, alg);
quat_lattice_conjugate_without_hnf(&(conj->lattice), &(lideal->lattice));
conj->parent_order = new_parent_order;
ibz_copy(&(conj->norm), &(lideal->norm));
}
int
quat_order_discriminant(ibz_t *disc, const quat_lattice_t *order, const quat_alg_t *alg)
{
int ok = 0;
ibz_t det, sqr, div;
ibz_mat_4x4_t transposed, norm, prod;
ibz_init(&det);
ibz_init(&sqr);
ibz_init(&div);
ibz_mat_4x4_init(&transposed);
ibz_mat_4x4_init(&norm);
ibz_mat_4x4_init(&prod);
ibz_mat_4x4_transpose(&transposed, &(order->basis));
// multiply gram matrix by 2 because of reduced trace
ibz_mat_4x4_identity(&norm);
ibz_copy(&(norm[2][2]), &(alg->p));
ibz_copy(&(norm[3][3]), &(alg->p));
ibz_mat_4x4_scalar_mul(&norm, &ibz_const_two, &norm);
ibz_mat_4x4_mul(&prod, &transposed, &norm);
ibz_mat_4x4_mul(&prod, &prod, &(order->basis));
ibz_mat_4x4_inv_with_det_as_denom(NULL, &det, &prod);
ibz_mul(&div, &(order->denom), &(order->denom));
ibz_mul(&div, &div, &div);
ibz_mul(&div, &div, &div);
ibz_div(&sqr, &div, &det, &div);
ok = ibz_is_zero(&div);
ok = ok & ibz_sqrt(disc, &sqr);
ibz_finalize(&det);
ibz_finalize(&div);
ibz_finalize(&sqr);
ibz_mat_4x4_finalize(&transposed);
ibz_mat_4x4_finalize(&norm);
ibz_mat_4x4_finalize(&prod);
return (ok);
}
int
quat_order_is_maximal(const quat_lattice_t *order, const quat_alg_t *alg)
{
int res;
ibz_t disc;
ibz_init(&disc);
quat_order_discriminant(&disc, order, alg);
res = (ibz_cmp(&disc, &(alg->p)) == 0);
ibz_finalize(&disc);
return (res);
}

View File

@@ -0,0 +1,303 @@
/** @file
*
* @authors Luca De Feo, Sina Schaeffler
*
* @brief Declarations for big integers in the reference implementation
*/
#ifndef INTBIG_H
#define INTBIG_H
#include <sqisign_namespace.h>
#if defined(MINI_GMP)
#include <mini-gmp.h>
#include <mini-gmp-extra.h>
#else
#include <gmp.h>
#endif
#include <stdint.h>
#include <tutil.h>
/** @ingroup quat_quat
* @defgroup ibz_all Signed big integers (gmp-based)
* @{
*/
/** @defgroup ibz_t Precise number types
* @{
*/
/** @brief Type for signed long integers
*
* @typedef ibz_t
*
* For integers of arbitrary size, used by intbig module, using gmp
*/
typedef mpz_t ibz_t;
/** @}
*/
/** @defgroup ibz_c Constants
* @{
*/
/**
* Constant zero
*/
extern const ibz_t ibz_const_zero;
/**
* Constant one
*/
extern const ibz_t ibz_const_one;
/**
* Constant two
*/
extern const ibz_t ibz_const_two;
/**
* Constant three
*/
extern const ibz_t ibz_const_three;
/** @}
*/
/** @defgroup ibz_finit Constructors and Destructors
* @{
*/
void ibz_init(ibz_t *x);
void ibz_finalize(ibz_t *x);
/** @}
*/
/** @defgroup ibz_za Basic integer arithmetic
* @{
*/
/** @brief sum=a+b
*/
void ibz_add(ibz_t *sum, const ibz_t *a, const ibz_t *b);
/** @brief diff=a-b
*/
void ibz_sub(ibz_t *diff, const ibz_t *a, const ibz_t *b);
/** @brief prod=a*b
*/
void ibz_mul(ibz_t *prod, const ibz_t *a, const ibz_t *b);
/** @brief neg=-a
*/
void ibz_neg(ibz_t *neg, const ibz_t *a);
/** @brief abs=|a|
*/
void ibz_abs(ibz_t *abs, const ibz_t *a);
/** @brief Euclidean division of a by b
*
* Computes quotient, remainder so that remainder+quotient*b = a where 0<=|remainder|<|b|
* The quotient is rounded towards zero.
*/
void ibz_div(ibz_t *quotient, ibz_t *remainder, const ibz_t *a, const ibz_t *b);
/** @brief Euclidean division of a by 2^exp
*
* Computes a right shift of abs(a) by exp bits, then sets sign(quotient) to sign(a).
*
* Division and rounding is as in ibz_div.
*/
void ibz_div_2exp(ibz_t *quotient, const ibz_t *a, uint32_t exp);
/** @brief Two adic valuation computation
*
* Computes the position of the first 1 in the binary representation of the integer given in input
*
* When this number is a power of two this gives the two adic valuation of the integer
*/
int ibz_two_adic(ibz_t *pow);
/** @brief r = a mod b
*
* Assumes valid inputs
* The sign of the divisor is ignored, the result is always non-negative
*/
void ibz_mod(ibz_t *r, const ibz_t *a, const ibz_t *b);
unsigned long int ibz_mod_ui(const mpz_t *n, unsigned long int d);
/** @brief Test if a = 0 mod b
*/
int ibz_divides(const ibz_t *a, const ibz_t *b);
/** @brief pow=x^e
*
* Assumes valid inputs, The case 0^0 yields 1.
*/
void ibz_pow(ibz_t *pow, const ibz_t *x, uint32_t e);
/** @brief pow=(x^e) mod m
*
* Assumes valid inputs
*/
void ibz_pow_mod(ibz_t *pow, const ibz_t *x, const ibz_t *e, const ibz_t *m);
/** @brief Compare a and b
*
* @returns a positive value if a > b, zero if a = b, and a negative value if a < b
*/
int ibz_cmp(const ibz_t *a, const ibz_t *b);
/** @brief Test if x is 0
*
* @returns 1 if x=0, 0 otherwise
*/
int ibz_is_zero(const ibz_t *x);
/** @brief Test if x is 1
*
* @returns 1 if x=1, 0 otherwise
*/
int ibz_is_one(const ibz_t *x);
/** @brief Compare x to y
*
* @returns 0 if x=y, positive if x>y, negative if x<y
*/
int ibz_cmp_int32(const ibz_t *x, int32_t y);
/** @brief Test if x is even
*
* @returns 1 if x is even, 0 otherwise
*/
int ibz_is_even(const ibz_t *x);
/** @brief Test if x is odd
*
* @returns 1 if x is odd, 0 otherwise
*/
int ibz_is_odd(const ibz_t *x);
/** @brief set i to value x
*/
void ibz_set(ibz_t *i, int32_t x);
/** @brief Copy value into target
*/
void ibz_copy(ibz_t *target, const ibz_t *value);
/** @brief Exchange values of a and b
*/
void ibz_swap(ibz_t *a, ibz_t *b);
/** @brief Copy dig array to target, given digits and the length of the dig array
* Restriction: dig represents a non-negative integer
*
* @param target Target ibz_t element
* @param dig array of digits
* @param dig_len length of the digits array
*/
void ibz_copy_digits(ibz_t *target, const digit_t *dig, int dig_len);
#define ibz_copy_digit_array(I, T) \
do { \
ibz_copy_digits((I), (T), sizeof(T) / sizeof(*(T))); \
} while (0)
// void ibz_printf(const char* format, ...);
#define ibz_printf gmp_printf
/** @brief Copy an ibz_t to target digit_t array.
* Restrictions: ibz >= 0 and target must hold sufficient elements to hold ibz
*
* @param target Target digit_t array
* @param ibz ibz source ibz_t element
*/
void ibz_to_digits(digit_t *target, const ibz_t *ibz);
#define ibz_to_digit_array(T, I) \
do { \
memset((T), 0, sizeof(T)); \
ibz_to_digits((T), (I)); \
} while (0)
/** @brief get int32_t equal to the lowest bits of i
*
* Should not be used to get the value of i if its bitsize is close to 32 bit
* It can however be used on any i to get an int32_t of the same parity as i (and same value modulo
* 4)
*
* @param i Input integer
*/
int32_t ibz_get(const ibz_t *i);
/** @brief generate random value in [a, b]
* assumed that a >= 0 and b >= 0 and a < b
* @returns 1 on success, 0 on failiure
*/
int ibz_rand_interval(ibz_t *rand, const ibz_t *a, const ibz_t *b);
/** @brief generate random value in [-m, m]
* assumed that m > 0 and bitlength of m < 32 bit
* @returns 1 on success, 0 on failiure
*/
int ibz_rand_interval_minm_m(ibz_t *rand, int32_t m);
/** @brief Bitsize of a.
*
* @returns Bitsize of a.
*
*/
int ibz_bitsize(const ibz_t *a);
/** @brief Size of a in given base.
*
* @returns Size of a in given base.
*
*/
int ibz_size_in_base(const ibz_t *a, int base);
/** @}
*/
/** @defgroup ibz_n Number theory functions
* @{
*/
/**
* @brief Greatest common divisor
*
* @param gcd Output: Set to the gcd of a and b
* @param a
* @param b
*/
void ibz_gcd(ibz_t *gcd, const ibz_t *a, const ibz_t *b);
/**
* @brief Modular inverse
*
* @param inv Output: Set to the integer in [0,mod[ such that a*inv = 1 mod (mod) if it exists
* @param a
* @param mod
* @returns 1 if inverse exists and was computed, 0 otherwise
*/
int ibz_invmod(ibz_t *inv, const ibz_t *a, const ibz_t *mod);
/**
* @brief Floor of Integer square root
*
* @param sqrt Output: Set to the floor of an integer square root
* @param a number of which a floor of an integer square root is searched
*/
void ibz_sqrt_floor(ibz_t *sqrt, const ibz_t *a);
/** @}
*/
// end of ibz_all
/** @}
*/
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,791 @@
#include "intbig_internal.h"
#include <limits.h>
#include <rng.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <assert.h>
// #define DEBUG_VERBOSE
#ifdef DEBUG_VERBOSE
#define DEBUG_STR_PRINTF(x) printf("%s\n", (x));
static void
DEBUG_STR_FUN_INT_MP(const char *op, int arg1, const ibz_t *arg2)
{
int arg2_size = ibz_size_in_base(arg2, 16);
char arg2_str[arg2_size + 2];
ibz_convert_to_str(arg2, arg2_str, 16);
printf("%s,%x,%s\n", op, arg1, arg2_str);
}
static void
DEBUG_STR_FUN_3(const char *op, const ibz_t *arg1, const ibz_t *arg2, const ibz_t *arg3)
{
int arg1_size = ibz_size_in_base(arg1, 16);
char arg1_str[arg1_size + 2];
ibz_convert_to_str(arg1, arg1_str, 16);
int arg2_size = ibz_size_in_base(arg2, 16);
char arg2_str[arg2_size + 2];
ibz_convert_to_str(arg2, arg2_str, 16);
int arg3_size = ibz_size_in_base(arg3, 16);
char arg3_str[arg3_size + 2];
ibz_convert_to_str(arg3, arg3_str, 16);
printf("%s,%s,%s,%s\n", op, arg1_str, arg2_str, arg3_str);
}
static void
DEBUG_STR_FUN_MP2_INT(const char *op, const ibz_t *arg1, const ibz_t *arg2, int arg3)
{
int arg1_size = ibz_size_in_base(arg1, 16);
char arg1_str[arg1_size + 2];
ibz_convert_to_str(arg1, arg1_str, 16);
int arg2_size = ibz_size_in_base(arg2, 16);
char arg2_str[arg2_size + 2];
ibz_convert_to_str(arg2, arg2_str, 16);
printf("%s,%s,%s,%x\n", op, arg1_str, arg2_str, arg3);
}
static void
DEBUG_STR_FUN_INT_MP2(const char *op, int arg1, const ibz_t *arg2, const ibz_t *arg3)
{
int arg2_size = ibz_size_in_base(arg2, 16);
char arg2_str[arg2_size + 2];
ibz_convert_to_str(arg2, arg2_str, 16);
int arg3_size = ibz_size_in_base(arg3, 16);
char arg3_str[arg3_size + 2];
ibz_convert_to_str(arg3, arg3_str, 16);
if (arg1 >= 0)
printf("%s,%x,%s,%s\n", op, arg1, arg2_str, arg3_str);
else
printf("%s,-%x,%s,%s\n", op, -arg1, arg2_str, arg3_str);
}
static void
DEBUG_STR_FUN_INT_MP_INT(const char *op, int arg1, const ibz_t *arg2, int arg3)
{
int arg2_size = ibz_size_in_base(arg2, 16);
char arg2_str[arg2_size + 2];
ibz_convert_to_str(arg2, arg2_str, 16);
printf("%s,%x,%s,%x\n", op, arg1, arg2_str, arg3);
}
static void
DEBUG_STR_FUN_4(const char *op, const ibz_t *arg1, const ibz_t *arg2, const ibz_t *arg3, const ibz_t *arg4)
{
int arg1_size = ibz_size_in_base(arg1, 16);
char arg1_str[arg1_size + 2];
ibz_convert_to_str(arg1, arg1_str, 16);
int arg2_size = ibz_size_in_base(arg2, 16);
char arg2_str[arg2_size + 2];
ibz_convert_to_str(arg2, arg2_str, 16);
int arg3_size = ibz_size_in_base(arg3, 16);
char arg3_str[arg3_size + 2];
ibz_convert_to_str(arg3, arg3_str, 16);
int arg4_size = ibz_size_in_base(arg4, 16);
char arg4_str[arg4_size + 2];
ibz_convert_to_str(arg4, arg4_str, 16);
printf("%s,%s,%s,%s,%s\n", op, arg1_str, arg2_str, arg3_str, arg4_str);
}
#else
#define DEBUG_STR_PRINTF(x)
#define DEBUG_STR_FUN_INT_MP(op, arg1, arg2)
#define DEBUG_STR_FUN_3(op, arg1, arg2, arg3)
#define DEBUG_STR_FUN_INT_MP2(op, arg1, arg2, arg3)
#define DEBUG_STR_FUN_INT_MP_INT(op, arg1, arg2, arg3)
#define DEBUG_STR_FUN_4(op, arg1, arg2, arg3, arg4)
#endif
/** @defgroup ibz_t Constants
* @{
*/
const __mpz_struct ibz_const_zero[1] = {
{
._mp_alloc = 0,
._mp_size = 0,
._mp_d = (mp_limb_t[]){ 0 },
}
};
const __mpz_struct ibz_const_one[1] = {
{
._mp_alloc = 0,
._mp_size = 1,
._mp_d = (mp_limb_t[]){ 1 },
}
};
const __mpz_struct ibz_const_two[1] = {
{
._mp_alloc = 0,
._mp_size = 1,
._mp_d = (mp_limb_t[]){ 2 },
}
};
const __mpz_struct ibz_const_three[1] = {
{
._mp_alloc = 0,
._mp_size = 1,
._mp_d = (mp_limb_t[]){ 3 },
}
};
void
ibz_init(ibz_t *x)
{
mpz_init(*x);
}
void
ibz_finalize(ibz_t *x)
{
mpz_clear(*x);
}
void
ibz_add(ibz_t *sum, const ibz_t *a, const ibz_t *b)
{
#ifdef DEBUG_VERBOSE
ibz_t a_cp, b_cp;
ibz_init(&a_cp);
ibz_init(&b_cp);
ibz_copy(&a_cp, a);
ibz_copy(&b_cp, b);
#endif
mpz_add(*sum, *a, *b);
#ifdef DEBUG_VERBOSE
DEBUG_STR_FUN_3("ibz_add", sum, &a_cp, &b_cp);
ibz_finalize(&a_cp);
ibz_finalize(&b_cp);
#endif
}
void
ibz_sub(ibz_t *diff, const ibz_t *a, const ibz_t *b)
{
#ifdef DEBUG_VERBOSE
ibz_t a_cp, b_cp;
ibz_init(&a_cp);
ibz_init(&b_cp);
ibz_copy(&a_cp, a);
ibz_copy(&b_cp, b);
#endif
mpz_sub(*diff, *a, *b);
#ifdef DEBUG_VERBOSE
DEBUG_STR_FUN_3("ibz_sub", diff, &a_cp, &b_cp);
ibz_finalize(&a_cp);
ibz_finalize(&b_cp);
#endif
}
void
ibz_mul(ibz_t *prod, const ibz_t *a, const ibz_t *b)
{
#ifdef DEBUG_VERBOSE
ibz_t a_cp, b_cp;
ibz_init(&a_cp);
ibz_init(&b_cp);
ibz_copy(&a_cp, a);
ibz_copy(&b_cp, b);
#endif
mpz_mul(*prod, *a, *b);
#ifdef DEBUG_VERBOSE
DEBUG_STR_FUN_3("ibz_mul", prod, &a_cp, &b_cp);
ibz_finalize(&a_cp);
ibz_finalize(&b_cp);
#endif
}
void
ibz_neg(ibz_t *neg, const ibz_t *a)
{
mpz_neg(*neg, *a);
}
void
ibz_abs(ibz_t *abs, const ibz_t *a)
{
mpz_abs(*abs, *a);
}
void
ibz_div(ibz_t *quotient, ibz_t *remainder, const ibz_t *a, const ibz_t *b)
{
#ifdef DEBUG_VERBOSE
ibz_t a_cp, b_cp;
ibz_init(&a_cp);
ibz_init(&b_cp);
ibz_copy(&a_cp, a);
ibz_copy(&b_cp, b);
#endif
mpz_tdiv_qr(*quotient, *remainder, *a, *b);
#ifdef DEBUG_VERBOSE
DEBUG_STR_FUN_4("ibz_div", quotient, remainder, &a_cp, &b_cp);
ibz_finalize(&a_cp);
ibz_finalize(&b_cp);
#endif
}
void
ibz_div_2exp(ibz_t *quotient, const ibz_t *a, uint32_t exp)
{
#ifdef DEBUG_VERBOSE
ibz_t a_cp;
ibz_init(&a_cp);
ibz_copy(&a_cp, a);
#endif
mpz_tdiv_q_2exp(*quotient, *a, exp);
#ifdef DEBUG_VERBOSE
DEBUG_STR_FUN_MP2_INT("ibz_div_2exp,%Zx,%Zx,%x\n", quotient, &a_cp, exp);
ibz_finalize(&a_cp);
#endif
}
void
ibz_div_floor(ibz_t *q, ibz_t *r, const ibz_t *n, const ibz_t *d)
{
mpz_fdiv_qr(*q, *r, *n, *d);
}
void
ibz_mod(ibz_t *r, const ibz_t *a, const ibz_t *b)
{
mpz_mod(*r, *a, *b);
}
unsigned long int
ibz_mod_ui(const mpz_t *n, unsigned long int d)
{
return mpz_fdiv_ui(*n, d);
}
int
ibz_divides(const ibz_t *a, const ibz_t *b)
{
return mpz_divisible_p(*a, *b);
}
void
ibz_pow(ibz_t *pow, const ibz_t *x, uint32_t e)
{
mpz_pow_ui(*pow, *x, e);
}
void
ibz_pow_mod(ibz_t *pow, const ibz_t *x, const ibz_t *e, const ibz_t *m)
{
mpz_powm(*pow, *x, *e, *m);
DEBUG_STR_FUN_4("ibz_pow_mod", pow, x, e, m);
}
int
ibz_two_adic(ibz_t *pow)
{
return mpz_scan1(*pow, 0);
}
int
ibz_cmp(const ibz_t *a, const ibz_t *b)
{
int ret = mpz_cmp(*a, *b);
DEBUG_STR_FUN_INT_MP2("ibz_cmp", ret, a, b);
return ret;
}
int
ibz_is_zero(const ibz_t *x)
{
int ret = !mpz_cmp_ui(*x, 0);
DEBUG_STR_FUN_INT_MP("ibz_is_zero", ret, x);
return ret;
}
int
ibz_is_one(const ibz_t *x)
{
int ret = !mpz_cmp_ui(*x, 1);
DEBUG_STR_FUN_INT_MP("ibz_is_one", ret, x);
return ret;
}
int
ibz_cmp_int32(const ibz_t *x, int32_t y)
{
int ret = mpz_cmp_si(*x, (signed long int)y);
DEBUG_STR_FUN_INT_MP_INT("ibz_cmp_int32", ret, x, y);
return ret;
}
int
ibz_is_even(const ibz_t *x)
{
int ret = !mpz_tstbit(*x, 0);
DEBUG_STR_FUN_INT_MP("ibz_is_even", ret, x);
return ret;
}
int
ibz_is_odd(const ibz_t *x)
{
int ret = mpz_tstbit(*x, 0);
DEBUG_STR_FUN_INT_MP("ibz_is_odd", ret, x);
return ret;
}
void
ibz_set(ibz_t *i, int32_t x)
{
mpz_set_si(*i, x);
}
int
ibz_convert_to_str(const ibz_t *i, char *str, int base)
{
if (!str || (base != 10 && base != 16))
return 0;
mpz_get_str(str, base, *i);
return 1;
}
void
ibz_print(const ibz_t *num, int base)
{
assert(base == 10 || base == 16);
int num_size = ibz_size_in_base(num, base);
char num_str[num_size + 2];
ibz_convert_to_str(num, num_str, base);
printf("%s", num_str);
}
int
ibz_set_from_str(ibz_t *i, const char *str, int base)
{
return (1 + mpz_set_str(*i, str, base));
}
void
ibz_copy(ibz_t *target, const ibz_t *value)
{
mpz_set(*target, *value);
}
void
ibz_swap(ibz_t *a, ibz_t *b)
{
mpz_swap(*a, *b);
}
int32_t
ibz_get(const ibz_t *i)
{
#if LONG_MAX == INT32_MAX
return (int32_t)mpz_get_si(*i);
#elif LONG_MAX > INT32_MAX
// Extracts the sign bit and the 31 least significant bits
signed long int t = mpz_get_si(*i);
return (int32_t)((t >> (sizeof(signed long int) * 8 - 32)) & INT32_C(0x80000000)) | (t & INT32_C(0x7FFFFFFF));
#else
#error Unsupported configuration: LONG_MAX must be >= INT32_MAX
#endif
}
int
ibz_rand_interval(ibz_t *rand, const ibz_t *a, const ibz_t *b)
{
int randret;
int ret = 1;
mpz_t tmp;
mpz_t bmina;
mpz_init(bmina);
mpz_sub(bmina, *b, *a);
if (mpz_sgn(bmina) == 0) {
mpz_set(*rand, *a);
mpz_clear(bmina);
return 1;
}
size_t len_bits = mpz_sizeinbase(bmina, 2);
size_t len_bytes = (len_bits + 7) / 8;
size_t sizeof_limb = sizeof(mp_limb_t);
size_t sizeof_limb_bits = sizeof_limb * 8;
size_t len_limbs = (len_bytes + sizeof_limb - 1) / sizeof_limb;
mp_limb_t mask = ((mp_limb_t)-1) >> (sizeof_limb_bits - len_bits) % sizeof_limb_bits;
mp_limb_t r[len_limbs];
#ifndef NDEBUG
{
for (size_t i = 0; i < len_limbs; ++i)
r[i] = (mp_limb_t)-1;
r[len_limbs - 1] = mask;
mpz_t check;
mpz_roinit_n(check, r, len_limbs);
assert(mpz_cmp(check, bmina) >= 0); // max sampled value >= b - a
mpz_t bmina2;
mpz_init(bmina2);
mpz_add(bmina2, bmina, bmina);
assert(mpz_cmp(check, bmina2) < 0); // max sampled value < 2 * (b - a)
mpz_clear(bmina2);
}
#endif
do {
randret = randombytes((unsigned char *)r, len_bytes);
if (randret != 0) {
ret = 0;
goto err;
}
#ifdef TARGET_BIG_ENDIAN
for (size_t i = 0; i < len_limbs; ++i)
r[i] = BSWAP_DIGIT(r[i]);
#endif
r[len_limbs - 1] &= mask;
mpz_roinit_n(tmp, r, len_limbs);
if (mpz_cmp(tmp, bmina) <= 0)
break;
} while (1);
mpz_add(*rand, tmp, *a);
err:
mpz_clear(bmina);
return ret;
}
int
ibz_rand_interval_i(ibz_t *rand, int32_t a, int32_t b)
{
uint32_t diff, mask;
int32_t rand32;
if (!(a >= 0 && b >= 0 && b > a)) {
printf("a = %d b = %d\n", a, b);
}
assert(a >= 0 && b >= 0 && b > a);
diff = b - a;
// Create a mask with 1 + ceil(log2(diff)) least significant bits set
#if (defined(__GNUC__) || defined(__clang__)) && INT_MAX == INT32_MAX
mask = (1 << (32 - __builtin_clz((uint32_t)diff))) - 1;
#else
uint32_t diff2 = diff, tmp;
mask = (diff2 > 0xFFFF) << 4;
diff2 >>= mask;
tmp = (diff2 > 0xFF) << 3;
diff2 >>= tmp;
mask |= tmp;
tmp = (diff2 > 0xF) << 2;
diff2 >>= tmp;
mask |= tmp;
tmp = (diff2 > 0x3) << 1;
diff2 >>= tmp;
mask |= tmp;
mask |= diff2 >> 1;
mask = (1 << (mask + 1)) - 1;
#endif
assert(mask >= diff && mask < 2 * diff);
// Rejection sampling
do {
randombytes((unsigned char *)&rand32, sizeof(rand32));
#ifdef TARGET_BIG_ENDIAN
rand32 = BSWAP32(rand32);
#endif
rand32 &= mask;
} while (rand32 > (int32_t)diff);
rand32 += a;
ibz_set(rand, rand32);
return 1;
}
int
ibz_rand_interval_minm_m(ibz_t *rand, int32_t m)
{
int ret = 1;
mpz_t m_big;
// m_big = 2 * m
mpz_init_set_si(m_big, m);
mpz_add(m_big, m_big, m_big);
// Sample in [0, 2*m]
ret = ibz_rand_interval(rand, &ibz_const_zero, &m_big);
// Adjust to range [-m, m]
mpz_sub_ui(*rand, *rand, m);
mpz_clear(m_big);
return ret;
}
int
ibz_rand_interval_bits(ibz_t *rand, uint32_t m)
{
int ret = 1;
mpz_t tmp;
mpz_t low;
mpz_init_set_ui(tmp, 1);
mpz_mul_2exp(tmp, tmp, m);
mpz_init(low);
mpz_neg(low, tmp);
ret = ibz_rand_interval(rand, &low, &tmp);
mpz_clear(tmp);
mpz_clear(low);
if (ret != 1)
goto err;
mpz_sub_ui(*rand, *rand, (unsigned long int)m);
return ret;
err:
mpz_clear(tmp);
mpz_clear(low);
return ret;
}
int
ibz_bitsize(const ibz_t *a)
{
return (int)mpz_sizeinbase(*a, 2);
}
int
ibz_size_in_base(const ibz_t *a, int base)
{
return (int)mpz_sizeinbase(*a, base);
}
void
ibz_copy_digits(ibz_t *target, const digit_t *dig, int dig_len)
{
mpz_import(*target, dig_len, -1, sizeof(digit_t), 0, 0, dig);
}
void
ibz_to_digits(digit_t *target, const ibz_t *ibz)
{
// From the GMP documentation:
// "If op is zero then the count returned will be zero and nothing written to rop."
// The next line ensures zero is written to the first limb of target if ibz is zero;
// target is then overwritten by the actual value if it is not.
target[0] = 0;
mpz_export(target, NULL, -1, sizeof(digit_t), 0, 0, *ibz);
}
int
ibz_probab_prime(const ibz_t *n, int reps)
{
int ret = mpz_probab_prime_p(*n, reps);
DEBUG_STR_FUN_INT_MP_INT("ibz_probab_prime", ret, n, reps);
return ret;
}
void
ibz_gcd(ibz_t *gcd, const ibz_t *a, const ibz_t *b)
{
mpz_gcd(*gcd, *a, *b);
}
int
ibz_invmod(ibz_t *inv, const ibz_t *a, const ibz_t *mod)
{
return (mpz_invert(*inv, *a, *mod) ? 1 : 0);
}
int
ibz_legendre(const ibz_t *a, const ibz_t *p)
{
return mpz_legendre(*a, *p);
}
int
ibz_sqrt(ibz_t *sqrt, const ibz_t *a)
{
if (mpz_perfect_square_p(*a)) {
mpz_sqrt(*sqrt, *a);
return 1;
} else {
return 0;
}
}
void
ibz_sqrt_floor(ibz_t *sqrt, const ibz_t *a)
{
mpz_sqrt(*sqrt, *a);
}
int
ibz_sqrt_mod_p(ibz_t *sqrt, const ibz_t *a, const ibz_t *p)
{
#ifndef NDEBUG
assert(ibz_probab_prime(p, 100));
#endif
// Case a = 0
{
ibz_t test;
ibz_init(&test);
ibz_mod(&test, a, p);
if (ibz_is_zero(&test)) {
ibz_set(sqrt, 0);
}
ibz_finalize(&test);
}
#ifdef DEBUG_VERBOSE
ibz_t a_cp, p_cp;
ibz_init(&a_cp);
ibz_init(&p_cp);
ibz_copy(&a_cp, a);
ibz_copy(&p_cp, p);
#endif
mpz_t amod, tmp, exp, a4, a2, q, z, qnr, x, y, b, pm1;
mpz_init(amod);
mpz_init(tmp);
mpz_init(exp);
mpz_init(a4);
mpz_init(a2);
mpz_init(q);
mpz_init(z);
mpz_init(qnr);
mpz_init(x);
mpz_init(y);
mpz_init(b);
mpz_init(pm1);
int ret = 1;
mpz_mod(amod, *a, *p);
if (mpz_cmp_ui(amod, 0) < 0) {
mpz_add(amod, *p, amod);
}
if (mpz_legendre(amod, *p) != 1) {
ret = 0;
goto end;
}
mpz_sub_ui(pm1, *p, 1);
if (mpz_mod_ui(tmp, *p, 4) == 3) {
// p % 4 == 3
mpz_add_ui(tmp, *p, 1);
mpz_fdiv_q_2exp(tmp, tmp, 2);
mpz_powm(*sqrt, amod, tmp, *p);
} else if (mpz_mod_ui(tmp, *p, 8) == 5) {
// p % 8 == 5
mpz_sub_ui(tmp, *p, 1);
mpz_fdiv_q_2exp(tmp, tmp, 2);
mpz_powm(tmp, amod, tmp, *p); // a^{(p-1)/4} mod p
if (!mpz_cmp_ui(tmp, 1)) {
mpz_add_ui(tmp, *p, 3);
mpz_fdiv_q_2exp(tmp, tmp, 3);
mpz_powm(*sqrt, amod, tmp, *p); // a^{(p+3)/8} mod p
} else {
mpz_sub_ui(tmp, *p, 5);
mpz_fdiv_q_2exp(tmp, tmp, 3); // (p - 5) / 8
mpz_mul_2exp(a4, amod, 2); // 4*a
mpz_powm(tmp, a4, tmp, *p);
mpz_mul_2exp(a2, amod, 1);
mpz_mul(tmp, a2, tmp);
mpz_mod(*sqrt, tmp, *p);
}
} else {
// p % 8 == 1 -> Shanks-Tonelli
int e = 0;
mpz_sub_ui(q, *p, 1);
while (mpz_tstbit(q, e) == 0)
e++;
mpz_fdiv_q_2exp(q, q, e);
// 1. find generator - non-quadratic residue
mpz_set_ui(qnr, 2);
while (mpz_legendre(qnr, *p) != -1)
mpz_add_ui(qnr, qnr, 1);
mpz_powm(z, qnr, q, *p);
// 2. Initialize
mpz_set(y, z);
mpz_powm(y, amod, q, *p); // y = a^q mod p
mpz_add_ui(tmp, q, 1); // tmp = (q + 1) / 2
mpz_fdiv_q_2exp(tmp, tmp, 1);
mpz_powm(x, amod, tmp, *p); // x = a^(q + 1)/2 mod p
mpz_set_ui(exp, 1);
mpz_mul_2exp(exp, exp, e - 2);
for (int i = 0; i < e; ++i) {
mpz_powm(b, y, exp, *p);
if (!mpz_cmp(b, pm1)) {
mpz_mul(x, x, z);
mpz_mod(x, x, *p);
mpz_mul(y, y, z);
mpz_mul(y, y, z);
mpz_mod(y, y, *p);
}
mpz_powm_ui(z, z, 2, *p);
mpz_fdiv_q_2exp(exp, exp, 1);
}
mpz_set(*sqrt, x);
}
#ifdef DEBUG_VERBOSE
DEBUG_STR_FUN_3("ibz_sqrt_mod_p", sqrt, &a_cp, &p_cp);
ibz_finalize(&a_cp);
ibz_finalize(&p_cp);
#endif
end:
mpz_clear(amod);
mpz_clear(tmp);
mpz_clear(exp);
mpz_clear(a4);
mpz_clear(a2);
mpz_clear(q);
mpz_clear(z);
mpz_clear(qnr);
mpz_clear(x);
mpz_clear(y);
mpz_clear(b);
mpz_clear(pm1);
return ret;
}

View File

@@ -1,319 +1,116 @@
#include <quaternion.h>
#include "internal.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.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);
// Random prime generation for tests
int
ibz_generate_random_prime(ibz_t *p, int is3mod4, int bitsize, int probability_test_iterations)
{
assert(bitsize != 0);
int found = 0;
ibz_t two_pow, two_powp;
//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_init(&two_pow);
ibz_init(&two_powp);
ibz_pow(&two_pow, &ibz_const_two, (bitsize - 1) - (0 != is3mod4));
ibz_pow(&two_powp, &ibz_const_two, bitsize - (0 != is3mod4));
int cnt = 0;
while (!found) {
cnt++;
if (cnt % 100000 == 0) {
printf("Random prime generation is still running after %d attempts, this is not "
"normal! The expected number of attempts is %d \n",
cnt,
bitsize);
}
ibz_add(q,q,&sign_q);
}
ibz_finalize(&r);
ibz_finalize(&sign_q);
ibz_finalize(&abs_b);
ibz_rand_interval(p, &two_pow, &two_powp);
ibz_add(p, p, p);
if (is3mod4) {
ibz_add(p, p, p);
ibz_add(p, &ibz_const_two, p);
}
ibz_add(p, &ibz_const_one, p);
found = ibz_probab_prime(p, probability_test_iterations);
}
ibz_finalize(&two_pow);
ibz_finalize(&two_powp);
return found;
}
// 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;
// solves x^2 + n y^2 == p for positive integers x, y
// assumes that p is prime and -n mod p is a square
int
ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p)
{
ibz_t r0, r1, r2, a, prod;
ibz_init(&r0);
ibz_init(&r1);
ibz_init(&r2);
ibz_init(&a);
ibz_init(&prod);
ibz_init(&two);
int res = 0;
// 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);
if (!ibz_cmp(p, &ibz_const_two)) {
if (ibz_is_one(n)) {
ibz_set(x, 1);
ibz_set(y, 1);
res = 1;
} else {
res = 0;
}
goto done;
}
// manage case p = n separately
if (!ibz_cmp(p, n)) {
ibz_set(x, 0);
ibz_set(y, 1);
res = 1;
goto done;
}
//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));
}
}
// test coprimality (should always be ok in our cases)
ibz_gcd(&r2, p, n);
if (!ibz_is_one(&r2))
goto done;
// get sqrt of -n mod p
ibz_neg(&r2, n);
if (!ibz_sqrt_mod_p(&r2, &r2, p))
goto done;
// run loop
ibz_copy(&prod, p);
ibz_copy(&r1, p);
ibz_copy(&r0, 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);
if (!ibz_is_zero(&r2))
goto done;
if (!ibz_sqrt(y, &a))
goto done;
ibz_copy(x, &r0);
ibz_mul(&a, y, y);
ibz_mul(&a, &a, n);
ibz_add(&prod, &prod, &a);
res = !ibz_cmp(&prod, p);
done:
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);
return res;
}

View File

@@ -1,740 +0,0 @@
/** @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,743 @@
/* Copyright (C) 2004-2024 Patrick Pelissier, Paul Zimmermann, LORIA/INRIA.
This file is part of the DPE Library.
The DPE Library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or (at your
option) any later version.
The DPE Library is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the DPE Library; see the file COPYING.LIB.
If not, see <https://www.gnu.org/licenses/>. */
#ifndef __DPE
#define __DPE
#include <stdlib.h> /* For abort */
#include <stdio.h> /* For fprintf */
#include <math.h> /* for round, floor, ceil */
#include <limits.h>
/* if you change the version, please change it in Makefile too */
#define DPE_VERSION_MAJOR 1
#define DPE_VERSION_MINOR 7
#if defined(__GNUC__) && (__GNUC__ >= 3)
# define DPE_LIKELY(x) (__builtin_expect(!!(x),1))
# define DPE_UNLIKELY(x) (__builtin_expect((x),0))
# define DPE_UNUSED_ATTR __attribute__((unused))
#else
# define DPE_LIKELY(x) (x)
# define DPE_UNLIKELY(x) (x)
# define DPE_UNUSED_ATTR
#endif
/* If no user defined mode, define it to double */
#if !defined(DPE_USE_DOUBLE) && !defined(DPE_USE_LONGDOUBLE) && !defined(DPE_USE_FLOAT128)
# define DPE_USE_DOUBLE
#endif
#if defined(DPE_USE_DOUBLE) && defined(DPE_USE_LONGDOUBLE)
# error "Either DPE_USE_DOUBLE or DPE_USE_LONGDOUBLE shall be defined."
#elif defined(DPE_USE_DOUBLE) && defined(DPE_USE_USE_FLOAT128)
# error "Either DPE_USE_DOUBLE or DPE_USE_FLOAT128 shall be defined."
#elif defined(DPE_USE_LONG_DOUBLE) && defined(DPE_USE_USE_FLOAT128)
# error "Either DPE_USE_LONG_DOUBLE or DPE_USE_FLOAT128 shall be defined."
#endif
#if (defined(__i386) || defined (__x86_64)) && !defined(DPE_LITTLEENDIAN32) && defined(DPE_USE_DOUBLE)
# define DPE_LITTLEENDIAN32
#endif
#if (defined(__STDC_VERSION__) && (__STDC_VERSION__>=199901L)) || defined (__GLIBC__)
# define DPE_DEFINE_ROUND_TRUNC
#endif
#if defined(__GNUC__) && (__GNUC__ *10 + __GNUC_MINOR__) >= 43
# define DPE_ISFINITE __builtin_isfinite
#elif defined(isfinite)
# define DPE_ISFINITE isfinite /* new C99 function */
#else
# define DPE_ISFINITE finite /* obsolete BSD function */
#endif
/* DPE_LDEXP(DPE_DOUBLE m, DPEEXP e) return x = m * 2^e */
/* DPE_FREXP(DPE_DOUBLE x, DPEEXP *e) returns m, e such that x = m * 2^e with
1/2 <= m < 1 */
/* DPE_ROUND(DPE_DOUBLE x) returns the nearest integer to x */
#if defined(DPE_USE_DOUBLE)
# define DPE_DOUBLE double /* mantissa type */
# define DPE_BITSIZE 53 /* bitsize of DPE_DOUBLE */
# define DPE_2_POW_BITSIZE 0x1P53
# if defined(__GNUC__) && (__GNUC__ *10 + __GNUC_MINOR__) >= 40
# define DPE_LDEXP __builtin_ldexp
# define DPE_FREXP __builtin_frexp
# define DPE_FLOOR __builtin_floor
# define DPE_CEIL __builtin_ceil
# ifdef DPE_DEFINE_ROUND_TRUNC
# define DPE_ROUND __builtin_round
# define DPE_TRUNC __builtin_trunc
# endif
# else
# define DPE_LDEXP ldexp
# define DPE_FREXP frexp
# define DPE_FLOOR floor
# define DPE_CEIL ceil
# ifdef DPE_DEFINE_ROUND_TRUNC
# define DPE_ROUND round
# define DPE_TRUNC trunc
# endif
# endif
#elif defined(DPE_USE_LONGDOUBLE)
# define DPE_DOUBLE long double
# define DPE_BITSIZE 64
# define DPE_2_POW_BITSIZE 0x1P64
# define DPE_LDEXP ldexpl
# define DPE_FREXP frexpl
# define DPE_FLOOR floorl
# define DPE_CEIL ceill
# ifdef DPE_DEFINE_ROUND_TRUNC
# define DPE_ROUND roundl
# define DPE_TRUNC truncl
# endif
#elif defined(DPE_USE_FLOAT128)
# include "quadmath.h"
# define DPE_DOUBLE __float128
# define DPE_BITSIZE 113
# define DPE_2_POW_BITSIZE 0x1P113
# define DPE_LDEXP ldexpq
# define DPE_FLOOR floorq
# define DPE_CEIL ceilq
# define DPE_FREXP frexpq
# ifdef DPE_DEFINE_ROUND_TRUNC
# define DPE_ROUND roundq
# define DPE_TRUNC truncq
# endif
#else
# error "neither DPE_USE_DOUBLE, nor DPE_USE_LONGDOUBLE, nor DPE_USE_FLOAT128 is defined"
#endif
/* If no C99, do what we can */
#ifndef DPE_DEFINE_ROUND_TRUNC
# define DPE_ROUND(x) ((DPE_DOUBLE) ((long long) ((x) + ((x) >= 0.0 ? 0.5 : -0.5))))
# define DPE_TRUNC(x) ((DPE_DOUBLE) ((long long) ((x) + 0.0)))
#endif
#if defined(DPE_USE_LONG)
# define DPE_EXP_T long /* exponent type */
# define DPE_EXPMIN LONG_MIN /* smallest possible exponent */
#elif defined(DPE_USE_LONGLONG)
# define DPE_EXP_T long long
# define DPE_EXPMIN LLONG_MIN
#else
# define DPE_EXP_T int /* exponent type */
# define DPE_EXPMIN INT_MIN /* smallest possible exponent */
#endif
#ifdef DPE_LITTLEENDIAN32
typedef union
{
double d;
#if INT_MAX == 0x7FFFFFFFL
int i[2];
#elif LONG_MAX == 0x7FFFFFFFL
long i[2];
#elif SHRT_MAX == 0x7FFFFFFFL
short i[2];
#else
# error Cannot find a 32 bits integer type.
#endif
} dpe_double_words;
#endif
typedef struct
{
DPE_DOUBLE d; /* significand */
DPE_EXP_T exp; /* exponent */
} dpe_struct;
typedef dpe_struct dpe_t[1];
#define DPE_MANT(x) ((x)->d)
#define DPE_EXP(x) ((x)->exp)
#define DPE_SIGN(x) ((DPE_MANT(x) < 0.0) ? -1 : (DPE_MANT(x) > 0.0))
#define DPE_INLINE static inline
/* initialize */
DPE_INLINE void
dpe_init (dpe_t x DPE_UNUSED_ATTR)
{
}
/* clear */
DPE_INLINE void
dpe_clear (dpe_t x DPE_UNUSED_ATTR)
{
}
/* set x to y */
DPE_INLINE void
dpe_set (dpe_t x, dpe_t y)
{
DPE_MANT(x) = DPE_MANT(y);
DPE_EXP(x) = DPE_EXP(y);
}
/* set x to -y */
DPE_INLINE void
dpe_neg (dpe_t x, dpe_t y)
{
DPE_MANT(x) = -DPE_MANT(y);
DPE_EXP(x) = DPE_EXP(y);
}
/* set x to |y| */
DPE_INLINE void
dpe_abs (dpe_t x, dpe_t y)
{
DPE_MANT(x) = (DPE_MANT(y) >= 0) ? DPE_MANT(y) : -DPE_MANT(y);
DPE_EXP(x) = DPE_EXP(y);
}
/* set mantissa in [1/2, 1), except for 0 which has minimum exponent */
/* FIXME: don't inline this function yet ? */
static void
dpe_normalize (dpe_t x)
{
if (DPE_UNLIKELY (DPE_MANT(x) == 0.0 || DPE_ISFINITE (DPE_MANT(x)) == 0))
{
if (DPE_MANT(x) == 0.0)
DPE_EXP(x) = DPE_EXPMIN;
/* otherwise let the exponent of NaN, Inf unchanged */
}
else
{
DPE_EXP_T e;
#ifdef DPE_LITTLEENDIAN32 /* 32-bit little endian */
dpe_double_words dw;
dw.d = DPE_MANT(x);
e = (dw.i[1] >> 20) & 0x7FF; /* unbiased exponent, 1022 for m=1/2 */
DPE_EXP(x) += e - 1022;
dw.i[1] = (dw.i[1] & 0x800FFFFF) | 0x3FE00000;
DPE_MANT(x) = dw.d;
#else /* portable code */
double m = DPE_MANT(x);
DPE_MANT(x) = DPE_FREXP (m, &e);
DPE_EXP(x) += e;
#endif
}
}
#if defined(DPE_USE_DOUBLE)
static const double dpe_scale_tab[54] = {
0x1P0, 0x1P-1, 0x1P-2, 0x1P-3, 0x1P-4, 0x1P-5, 0x1P-6, 0x1P-7, 0x1P-8,
0x1P-9, 0x1P-10, 0x1P-11, 0x1P-12, 0x1P-13, 0x1P-14, 0x1P-15, 0x1P-16,
0x1P-17, 0x1P-18, 0x1P-19, 0x1P-20, 0x1P-21, 0x1P-22, 0x1P-23, 0x1P-24,
0x1P-25, 0x1P-26, 0x1P-27, 0x1P-28, 0x1P-29, 0x1P-30, 0x1P-31, 0x1P-32,
0x1P-33, 0x1P-34, 0x1P-35, 0x1P-36, 0x1P-37, 0x1P-38, 0x1P-39, 0x1P-40,
0x1P-41, 0x1P-42, 0x1P-43, 0x1P-44, 0x1P-45, 0x1P-46, 0x1P-47, 0x1P-48,
0x1P-49, 0x1P-50, 0x1P-51, 0x1P-52, 0x1P-53};
#endif
DPE_INLINE DPE_DOUBLE
dpe_scale (DPE_DOUBLE d, int s)
{
/* -DPE_BITSIZE < s <= 0 and 1/2 <= d < 1 */
#if defined(DPE_USE_DOUBLE)
return d * dpe_scale_tab [-s];
#else /* portable code */
return DPE_LDEXP (d, s);
#endif
}
/* set x to y */
DPE_INLINE void
dpe_set_d (dpe_t x, double y)
{
DPE_MANT(x) = (DPE_DOUBLE) y;
DPE_EXP(x) = 0;
dpe_normalize (x);
}
/* set x to y */
DPE_INLINE void
dpe_set_ld (dpe_t x, long double y)
{
DPE_MANT(x) = (DPE_DOUBLE) y;
DPE_EXP(x) = 0;
dpe_normalize (x);
}
/* set x to y */
DPE_INLINE void
dpe_set_ui (dpe_t x, unsigned long y)
{
DPE_MANT(x) = (DPE_DOUBLE) y;
DPE_EXP(x) = 0;
dpe_normalize (x);
}
/* set x to y */
DPE_INLINE void
dpe_set_si (dpe_t x, long y)
{
DPE_MANT(x) = (DPE_DOUBLE) y;
DPE_EXP(x) = 0;
dpe_normalize (x);
}
DPE_INLINE long
dpe_get_si (dpe_t x)
{
DPE_DOUBLE d = DPE_LDEXP (DPE_MANT (x), DPE_EXP (x));
return (long) d;
}
DPE_INLINE unsigned long
dpe_get_ui (dpe_t x)
{
DPE_DOUBLE d = DPE_LDEXP (DPE_MANT (x), DPE_EXP (x));
return (d < 0.0) ? 0 : (unsigned long) d;
}
DPE_INLINE double
dpe_get_d (dpe_t x)
{
return DPE_LDEXP (DPE_MANT (x), DPE_EXP (x));
}
DPE_INLINE long double
dpe_get_ld (dpe_t x)
{
return DPE_LDEXP (DPE_MANT (x), DPE_EXP (x));
}
#if defined(__GMP_H__) || defined(__MINI_GMP_H__)
/* set x to y */
DPE_INLINE void
dpe_set_z (dpe_t x, mpz_t y)
{
long e;
DPE_MANT(x) = mpz_get_d_2exp (&e, y);
DPE_EXP(x) = (DPE_EXP_T) e;
}
/* set x to y, rounded to nearest */
DPE_INLINE void
dpe_get_z (mpz_t x, dpe_t y)
{
DPE_EXP_T ey = DPE_EXP(y);
if (ey >= DPE_BITSIZE) /* y is an integer */
{
DPE_DOUBLE d = DPE_MANT(y) * DPE_2_POW_BITSIZE; /* d is an integer */
mpz_set_d (x, d); /* should be exact */
mpz_mul_2exp (x, x, (unsigned long) ey - DPE_BITSIZE);
}
else /* DPE_EXP(y) < DPE_BITSIZE */
{
if (DPE_UNLIKELY (ey < 0)) /* |y| < 1/2 */
mpz_set_ui (x, 0);
else
{
DPE_DOUBLE d = DPE_LDEXP(DPE_MANT(y), ey);
mpz_set_d (x, (double) DPE_ROUND(d));
}
}
}
/* return e and x such that y = x*2^e */
DPE_INLINE mp_exp_t
dpe_get_z_exp (mpz_t x, dpe_t y)
{
mpz_set_d (x, DPE_MANT (y) * DPE_2_POW_BITSIZE);
return DPE_EXP(y) - DPE_BITSIZE;
}
#endif
/* x <- y + z, assuming y and z are normalized, returns x normalized */
DPE_INLINE void
dpe_add (dpe_t x, dpe_t y, dpe_t z)
{
if (DPE_UNLIKELY (DPE_EXP(y) > DPE_EXP(z) + DPE_BITSIZE))
/* |z| < 1/2*ulp(y), thus o(y+z) = y */
dpe_set (x, y);
else if (DPE_UNLIKELY (DPE_EXP(z) > DPE_EXP(y) + DPE_BITSIZE))
dpe_set (x, z);
else
{
DPE_EXP_T d = DPE_EXP(y) - DPE_EXP(z); /* |d| <= DPE_BITSIZE */
if (d >= 0)
{
DPE_MANT(x) = DPE_MANT(y) + dpe_scale (DPE_MANT(z), -d);
DPE_EXP(x) = DPE_EXP(y);
}
else
{
DPE_MANT(x) = DPE_MANT(z) + dpe_scale (DPE_MANT(y), d);
DPE_EXP(x) = DPE_EXP(z);
}
dpe_normalize (x);
}
}
/* x <- y - z, assuming y and z are normalized, returns x normalized */
DPE_INLINE void
dpe_sub (dpe_t x, dpe_t y, dpe_t z)
{
if (DPE_UNLIKELY (DPE_EXP(y) > DPE_EXP(z) + DPE_BITSIZE))
/* |z| < 1/2*ulp(y), thus o(y-z) = y */
dpe_set (x, y);
else if (DPE_UNLIKELY (DPE_EXP(z) > DPE_EXP(y) + DPE_BITSIZE))
dpe_neg (x, z);
else
{
DPE_EXP_T d = DPE_EXP(y) - DPE_EXP(z); /* |d| <= DPE_BITSIZE */
if (d >= 0)
{
DPE_MANT(x) = DPE_MANT(y) - dpe_scale (DPE_MANT(z), -d);
DPE_EXP(x) = DPE_EXP(y);
}
else
{
DPE_MANT(x) = dpe_scale (DPE_MANT(y), d) - DPE_MANT(z);
DPE_EXP(x) = DPE_EXP(z);
}
dpe_normalize (x);
}
}
/* x <- y * z, assuming y and z are normalized, returns x normalized */
DPE_INLINE void
dpe_mul (dpe_t x, dpe_t y, dpe_t z)
{
DPE_MANT(x) = DPE_MANT(y) * DPE_MANT(z);
DPE_EXP(x) = DPE_EXP(y) + DPE_EXP(z);
dpe_normalize (x);
}
/* x <- sqrt(y), assuming y is normalized, returns x normalized */
DPE_INLINE void
dpe_sqrt (dpe_t x, dpe_t y)
{
DPE_EXP_T ey = DPE_EXP(y);
if (ey % 2)
{
/* since 1/2 <= my < 1, 1/4 <= my/2 < 1 */
DPE_MANT(x) = sqrt (0.5 * DPE_MANT(y));
DPE_EXP(x) = (ey + 1) / 2;
}
else
{
DPE_MANT(x) = sqrt (DPE_MANT(y));
DPE_EXP(x) = ey / 2;
}
}
/* x <- y / z, assuming y and z are normalized, returns x normalized.
Assumes z is not zero. */
DPE_INLINE void
dpe_div (dpe_t x, dpe_t y, dpe_t z)
{
DPE_MANT(x) = DPE_MANT(y) / DPE_MANT(z);
DPE_EXP(x) = DPE_EXP(y) - DPE_EXP(z);
dpe_normalize (x);
}
/* x <- y * z, assuming y normalized, returns x normalized */
DPE_INLINE void
dpe_mul_ui (dpe_t x, dpe_t y, unsigned long z)
{
DPE_MANT(x) = DPE_MANT(y) * (DPE_DOUBLE) z;
DPE_EXP(x) = DPE_EXP(y);
dpe_normalize (x);
}
/* x <- y / z, assuming y normalized, z non-zero, returns x normalized */
DPE_INLINE void
dpe_div_ui (dpe_t x, dpe_t y, unsigned long z)
{
DPE_MANT(x) = DPE_MANT(y) / (DPE_DOUBLE) z;
DPE_EXP(x) = DPE_EXP(y);
dpe_normalize (x);
}
/* x <- y * 2^e */
DPE_INLINE void
dpe_mul_2exp (dpe_t x, dpe_t y, unsigned long e)
{
DPE_MANT(x) = DPE_MANT(y);
DPE_EXP(x) = DPE_EXP(y) + (DPE_EXP_T) e;
}
/* x <- y / 2^e */
DPE_INLINE void
dpe_div_2exp (dpe_t x, dpe_t y, unsigned long e)
{
DPE_MANT(x) = DPE_MANT(y);
DPE_EXP(x) = DPE_EXP(y) - (DPE_EXP_T) e;
}
/* return e and x such that y = x*2^e (equality is not guaranteed if the 'long'
type has fewer bits than the significand in dpe_t) */
DPE_INLINE DPE_EXP_T
dpe_get_si_exp (long *x, dpe_t y)
{
if (sizeof(long) == 4) /* 32-bit word: long has 31 bits */
{
*x = (long) (DPE_MANT(y) * 2147483648.0);
return DPE_EXP(y) - 31;
}
else if (sizeof(long) == 8) /* 64-bit word: long has 63 bits */
{
*x = (long) (DPE_MANT (y) * 9223372036854775808.0);
return DPE_EXP(y) - 63;
}
else
{
fprintf (stderr, "Error, neither 32-bit nor 64-bit word\n");
exit (1);
}
}
static DPE_UNUSED_ATTR int dpe_str_prec = 16;
static int dpe_out_str (FILE *s, int base, dpe_t x) DPE_UNUSED_ATTR;
static int
dpe_out_str (FILE *s, int base, dpe_t x)
{
DPE_DOUBLE d = DPE_MANT(x);
DPE_EXP_T e2 = DPE_EXP(x);
int e10 = 0;
char sign = ' ';
if (DPE_UNLIKELY (base != 10))
{
fprintf (stderr, "Error in dpe_out_str, only base 10 allowed\n");
exit (1);
}
if (d == 0.0)
#ifdef DPE_USE_DOUBLE
return fprintf (s, "%1.*f", dpe_str_prec, d);
#else
return fprintf (s, "%1.*Lf", dpe_str_prec, (long double) d);
#endif
if (d < 0)
{
d = -d;
sign = '-';
}
if (e2 > 0)
{
while (e2 > 0)
{
e2 --;
d *= 2.0;
if (d >= 10.0)
{
d /= 10.0;
e10 ++;
}
}
}
else /* e2 <= 0 */
{
while (e2 < 0)
{
e2 ++;
d /= 2.0;
if (d < 1.0)
{
d *= 10.0;
e10 --;
}
}
}
#ifdef DPE_USE_DOUBLE
return fprintf (s, "%c%1.*f*10^%d", sign, dpe_str_prec, d, e10);
#else
return fprintf (s, "%c%1.*Lf*10^%d", sign, dpe_str_prec, (long double) d, e10);
#endif
}
static size_t dpe_inp_str (dpe_t x, FILE *s, int base) DPE_UNUSED_ATTR;
static size_t
dpe_inp_str (dpe_t x, FILE *s, int base)
{
size_t res;
DPE_DOUBLE d;
if (DPE_UNLIKELY (base != 10))
{
fprintf (stderr, "Error in dpe_out_str, only base 10 allowed\n");
exit (1);
}
#ifdef DPE_USE_DOUBLE
res = fscanf (s, "%lf", &d);
#elif defined(DPE_USE_LONGDOUBLE)
res = fscanf (s, "%Lf", &d);
#else
{
long double d_ld;
res = fscanf (s, "%Lf", &d_ld);
d = d_ld;
}
#endif
dpe_set_d (x, d);
return res;
}
DPE_INLINE void
dpe_dump (dpe_t x)
{
dpe_out_str (stdout, 10, x);
putchar ('\n');
}
DPE_INLINE int
dpe_zero_p (dpe_t x)
{
return DPE_MANT (x) == 0;
}
/* return a positive value if x > y
a negative value if x < y
and 0 otherwise (x=y). */
DPE_INLINE int
dpe_cmp (dpe_t x, dpe_t y)
{
int sx = DPE_SIGN(x);
int d = sx - DPE_SIGN(y);
if (d != 0)
return d;
else if (DPE_EXP(x) > DPE_EXP(y))
return (sx > 0) ? 1 : -1;
else if (DPE_EXP(y) > DPE_EXP(x))
return (sx > 0) ? -1 : 1;
else /* DPE_EXP(x) = DPE_EXP(y) */
return (DPE_MANT(x) < DPE_MANT(y)) ? -1 : (DPE_MANT(x) > DPE_MANT(y));
}
DPE_INLINE int
dpe_cmp_d (dpe_t x, double d)
{
dpe_t y;
dpe_set_d (y, d);
return dpe_cmp (x, y);
}
DPE_INLINE int
dpe_cmp_ui (dpe_t x, unsigned long d)
{
dpe_t y;
dpe_set_ui (y, d);
return dpe_cmp (x, y);
}
DPE_INLINE int
dpe_cmp_si (dpe_t x, long d)
{
dpe_t y;
dpe_set_si (y, d);
return dpe_cmp (x, y);
}
/* set x to integer nearest to y */
DPE_INLINE void
dpe_round (dpe_t x, dpe_t y)
{
if (DPE_EXP(y) < 0) /* |y| < 1/2 */
dpe_set_ui (x, 0);
else if (DPE_EXP(y) >= DPE_BITSIZE) /* y is an integer */
dpe_set (x, y);
else
{
DPE_DOUBLE d;
d = DPE_LDEXP(DPE_MANT(y), DPE_EXP(y));
dpe_set_d (x, DPE_ROUND(d));
}
}
/* set x to the fractional part of y, defined as y - trunc(y), thus the
fractional part has absolute value in [0, 1), and same sign as y */
DPE_INLINE void
dpe_frac (dpe_t x, dpe_t y)
{
/* If |y| is smaller than 1, keep it */
if (DPE_EXP(y) <= 0)
dpe_set (x, y);
else if (DPE_EXP(y) >= DPE_BITSIZE) /* y is an integer */
dpe_set_ui (x, 0);
else
{
DPE_DOUBLE d;
d = DPE_LDEXP(DPE_MANT(y), DPE_EXP(y));
dpe_set_d (x, d - DPE_TRUNC(d));
}
}
/* set x to largest integer <= y */
DPE_INLINE void
dpe_floor (dpe_t x, dpe_t y)
{
if (DPE_EXP(y) <= 0) /* |y| < 1 */
{
if (DPE_SIGN(y) >= 0) /* 0 <= y < 1 */
dpe_set_ui (x, 0);
else /* -1 < y < 0 */
dpe_set_si (x, -1);
}
else if (DPE_EXP(y) >= DPE_BITSIZE) /* y is an integer */
dpe_set (x, y);
else
{
DPE_DOUBLE d;
d = DPE_LDEXP(DPE_MANT(y), DPE_EXP(y));
dpe_set_d (x, DPE_FLOOR(d));
}
}
/* set x to smallest integer >= y */
DPE_INLINE void
dpe_ceil (dpe_t x, dpe_t y)
{
if (DPE_EXP(y) <= 0) /* |y| < 1 */
{
if (DPE_SIGN(y) > 0) /* 0 < y < 1 */
dpe_set_ui (x, 1);
else /* -1 < y <= 0 */
dpe_set_si (x, 0);
}
else if (DPE_EXP(y) >= DPE_BITSIZE) /* y is an integer */
dpe_set (x, y);
else
{
DPE_DOUBLE d;
d = DPE_LDEXP(DPE_MANT(y), DPE_EXP(y));
dpe_set_d (x, DPE_CEIL(d));
}
}
DPE_INLINE void
dpe_swap (dpe_t x, dpe_t y)
{
DPE_EXP_T i = DPE_EXP (x);
DPE_DOUBLE d = DPE_MANT (x);
DPE_EXP (x) = DPE_EXP (y);
DPE_MANT (x) = DPE_MANT (y);
DPE_EXP (y) = i;
DPE_MANT (y) = d;
}
#endif /* __DPE */

View File

@@ -0,0 +1,94 @@
/** @file
*
* @authors Sina Schaeffler
*
* @brief Declarations for functions internal to the HNF computation and its tests
*/
#ifndef QUAT_HNF_HELPERS_H
#define QUAT_HNF_HELPERS_H
#include <quaternion.h>
/** @internal
* @ingroup quat_helpers
* @defgroup quat_hnf_helpers Internal functions for the HNF computation and tests
*/
/** @internal
* @ingroup quat_hnf_helpers
* @defgroup quat_hnf_helpers_ibz Internal renamed GMP functions for the HNF computation
*/
/**
* @brief GCD and Bézout coefficients u, v such that ua + bv = gcd
*
* @param gcd Output: Set to the gcd of a and b
* @param u Output: integer such that ua+bv=gcd
* @param v Output: Integer such that ua+bv=gcd
* @param a
* @param b
*/
void ibz_xgcd(ibz_t *gcd,
ibz_t *u,
ibz_t *v,
const ibz_t *a,
const ibz_t *b); // integers, dim4, test/integers, test/dim4
/** @}
*/
/** @internal
* @ingroup quat_hnf_helpers
* @defgroup quat_hnf_integer_helpers Integer functions internal to the HNF computation and tests
* @{
*/
/** @brief x mod mod, with x in [1,mod]
*
* @param res Output: res = x [mod] and 0<res<mod+1
* @param x
* @param mod integer > 0
*/
void ibz_mod_not_zero(ibz_t *res, const ibz_t *x, const ibz_t *mod);
/** @brief x mod mod, with x in ]-mod/2,mod/2]
*
* Centered and rather positive then negative.
*
* @param remainder Output: remainder = x [mod] and -mod/2<remainder<=mod/2
* @param a
* @param mod integer > 0
*/
void ibz_centered_mod(ibz_t *remainder, const ibz_t *a, const ibz_t *mod);
/** @brief if c then x else y
*
* @param res Output: if c, res = x, else res = y
* @param x
* @param y
* @param c condition: must be 0 or 1
*/
void ibz_conditional_assign(ibz_t *res, const ibz_t *x, const ibz_t *y, int c);
/** @brief d = gcd(x,y)>0 and d = ux+vy and u!= 0 and d>0 and u, v of small absolute value, u not 0
*
* More precisely:
* If x and y are both non 0, -|xy|/d<vy<=0<ux<=|xy|/d.
* If x=0 and y not 0, then u=1 and v=y/d
* If x not 0 and y=0, then u=x/d and v=0
* If y=0 and x=0, d=1, u=1 and v=0
*
* @param d Output: gcd of x and y
* @param u Output: coefficient of x
* @param v Output: coefficient of y
* @param x
* @param y
*/
void ibz_xgcd_with_u_not_0(ibz_t *d, ibz_t *u, ibz_t *v, const ibz_t *x, const ibz_t *y);
/** @}
*/
/** @}
*/
#endif

View File

@@ -0,0 +1,123 @@
/** @file
*
* @authors Sina Schaeffler
*
* @brief Declarations for big integer functions only used in quaternion functions
*/
#ifndef INTBIG_INTERNAL_H
#define INTBIG_INTERNAL_H
#include "intbig.h"
/** @internal
* @ingroup quat_helpers
* @defgroup ibz_helper Internal integer functions (gmp-based)
* @{
*/
/********************************************************************/
/** @brief Euclidean division of a by b
*
* Computes quotient, remainder so that remainder+quotient*b = a where 0<=|remainder|<|b|
* The quotient is rounded towards minus infinity.
*/
void ibz_div_floor(ibz_t *q, ibz_t *r, const ibz_t *n, const ibz_t *d);
/** @brief generate random value in [a, b]
* assumed that a >= 0, b >= 0 and a < b
* @returns 1 on success, 0 on failiure
*/
int ibz_rand_interval_i(ibz_t *rand, int32_t a, int32_t b);
/** @brief generate random value in [-2^m, 2^m]
* assumed that m > 0 and bitlength of m < 32 bit
* @returns 1 on success, 0 on failiure
*/
int ibz_rand_interval_bits(ibz_t *rand, uint32_t m);
/** @brief set str to a string containing the representation of i in base
*
* Base should be 10 or 16
*
* str should be an array of length enough to store the representation of in
* in base, which can be obtained by ibz_sizeinbase(i, base) + 2, where the 2
* is for the sign and the null terminator
*
* Case for base 16 does not matter
*
* @returns 1 if the integer could be converted to a string, 0 otherwise
*/
int ibz_convert_to_str(const ibz_t *i, char *str, int base);
/** @brief print num in base to stdout
*
* Base should be 10 or 16
*/
void ibz_print(const ibz_t *num, int base);
/** @brief set i to integer contained in string when read as number in base
*
* Base should be 10 or 16, and the number should be written without ponctuation or whitespaces
*
* Case for base 16 does not matter
*
* @returns 1 if the string could be converted to an integer, 0 otherwise
*/
int ibz_set_from_str(ibz_t *i, const char *str, int base);
/**
* @brief Probabilistic primality test
*
* @param n The number to test
* @param reps Number of Miller-Rabin repetitions. The more, the slower and the less likely are
* false positives
* @return 1 if probably prime, 0 if certainly not prime, 2 if certainly prime
*
* Using GMP's implementation:
*
* From GMP's documentation: "This function performs some trial divisions, a Baillie-PSW probable
* prime test, then reps-24 Miller-Rabin probabilistic primality tests."
*/
int ibz_probab_prime(const ibz_t *n, int reps);
/**
* @brief Square root modulo a prime
*
* @returns 1 if square root of a mod p exists and was computed, 0 otherwise
* @param sqrt Output: Set to a square root of a mod p if any exist
* @param a number of which a square root mod p is searched
* @param p assumed prime
*/
int ibz_sqrt_mod_p(ibz_t *sqrt, const ibz_t *a, const ibz_t *p);
/**
* @brief Integer square root of a perfect square
*
* @returns 1 if an integer square root of a exists and was computed, 0 otherwise
* @param sqrt Output: Set to a integer square root of a if any exist
* @param a number of which an integer square root is searched
*/
int ibz_sqrt(ibz_t *sqrt, const ibz_t *a);
/**
* @brief Legendre symbol of a mod p
*
* @returns Legendre symbol of a mod p
* @param a
* @param p assumed prime
*
* Uses GMP's implementation
*
* If output is 1, a is a square mod p, if -1, not. If 0, it is divisible by p
*/
int ibz_legendre(const ibz_t *a, const ibz_t *p);
/** @}
*/
// end of ibz_all
/** @}
*/
#endif

View File

@@ -0,0 +1,812 @@
/** @file
*
* @authors Sina Schaeffler
*
* @brief Declarations for helper functions for quaternion algebra implementation
*/
#ifndef QUAT_HELPER_H
#define QUAT_HELPER_H
#include <quaternion.h>
#include <assert.h>
#include "intbig_internal.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.
*/
void quat_alg_init_set_ui(quat_alg_t *alg,
unsigned int p); // test/lattice, test/ideal, test/algebra
/** @brief a*b
*
* Multiply two coordinate vectors as elements of the algebra in basis (1,i,j,ij) with i^2 = -1, j^2
* = -p
*
* @param res Output: Will contain product
* @param a
* @param b
* @param alg The quaternion algebra
*/
void quat_alg_coord_mul(ibz_vec_4_t *res, const ibz_vec_4_t *a, const ibz_vec_4_t *b, const quat_alg_t *alg);
/** @brief a=b
*
* Test if a and b represent the same quaternion algebra element
*
* @param a
* @param b
* @returns 1 if a=b, 0 otherwise
*/
int quat_alg_elem_equal(const quat_alg_elem_t *a, const quat_alg_elem_t *b);
/** @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 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,
int32_t denom,
int32_t coord0,
int32_t coord1,
int32_t coord2,
int32_t coord3);
/**
* @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 a+b for algebra elements
*
* @param res Output
* @param a Algebra element
* @param b Algebra element
*/
void quat_alg_add(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b);
/** @brief a-b for algebra elements
*
* @param res Output
* @param a Algebra element
* @param b Algebra element
*/
void quat_alg_sub(quat_alg_elem_t *res, const quat_alg_elem_t *a, const quat_alg_elem_t *b);
/** @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);
/** @}
*/
/** @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);
/** @}
*/
/** @internal
* @defgroup quat_dim4_lat_helpers Helper functions on vectors and matrices used mainly for lattices
* @{
*/
/** @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 set res to values coord0,coord1,coord2,coord3
*
* @param res Output: Will contain vector (coord0,coord1,coord2,coord3)
* @param coord0
* @param coord1
* @param coord2
* @param coord3
*/
void ibz_vec_4_copy_ibz(ibz_vec_4_t *res,
const ibz_t *coord0,
const ibz_t *coord1,
const ibz_t *coord2,
const ibz_t *coord3);
/** @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, int32_t coord0, int32_t coord1, int32_t coord2, int32_t coord3);
/** @brief a+b
*
* Add two integer 4-vectors
*
* @param res Output: Will contain sum
* @param a
* @param b
*/
void ibz_vec_4_add(ibz_vec_4_t *res, const ibz_vec_4_t *a, const ibz_vec_4_t *b);
/** @brief a-b
*
* Substract two integer 4-vectors
*
* @param res Output: Will contain difference
* @param a
* @param b
*/
void ibz_vec_4_sub(ibz_vec_4_t *res, const ibz_vec_4_t *a, const ibz_vec_4_t *b);
/** @brief x=0
*
* Test if a vector x has only zero coordinates
*
* @returns 0 if x has at least one non-zero coordinates, 1 otherwise
* @param x
*/
int ibz_vec_4_is_zero(const ibz_vec_4_t *x);
/** @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 multiplies all values in vector by same scalar
*
* @param prod Output
* @param scalar
* @param vec
*/
void ibz_vec_4_scalar_mul(ibz_vec_4_t *prod, const ibz_t *scalar, const ibz_vec_4_t *vec);
/** @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 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
*/
void ibz_vec_4_content(ibz_t *content, const ibz_vec_4_t *v);
/** @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 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 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 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 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, computed using a multiple of its
* determinant as modulo
*
* Algorithm used is the one at number 2.4.8 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
* @param generator_number number of generators given
* @param mod integer, must be a multiple of the volume of the lattice generated by the columns of
* generators
*/
void ibz_mat_4xn_hnf_mod_core(ibz_mat_4x4_t *hnf,
int generator_number,
const ibz_vec_4_t *generators,
const ibz_t *mod);
/** @}
*/
/** @}
*/
/** @internal
* @defgroup quat_dim2_helpers Helper functions for dimension 2
* @{
*/
/** @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); // test/dim2
/** @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); // test/dim2
void ibz_mat_2x2_add(ibz_mat_2x2_t *sum, const ibz_mat_2x2_t *a,
const ibz_mat_2x2_t *b); // unused
/** @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); // dim4
/**
* @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); // test/dim2
/** @}
*/
/** @internal
* @defgroup quat_lattice_helper Helper functions for the lattice library (dimension 4)
* @{
*/
/**
* @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); // lattice, test/lattice, test/algebra,
/**
* @brief Lattice equality
*
* Lattice bases are assumed to be under HNF, but denominators are free.
*
* @returns 1 if both lattices are equal, 0 otherwise
* @param lat1
* @param lat2
*/
int quat_lattice_equal(const quat_lattice_t *lat1,
const quat_lattice_t *lat2); // ideal, lattice, test/lattice, test/ideal
/**
* @brief Lattice inclusion test
*
* Lattice bases are assumed to be under HNF, but denominators are free.
*
* @returns 1 if sublat is included in overlat, 0 otherwise
* @param sublat Lattice whose inclusion in overlat will be testes
* @param overlat
*/
int quat_lattice_inclusion(const quat_lattice_t *sublat,
const quat_lattice_t *overlat); // test/lattice, test/ideal
/** @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); // lattice, ideal,
/** @brief a+b for lattices
*
* @param res Output
* @param lat1 Lattice
* @param lat2 Lattice
*/
void quat_lattice_add(quat_lattice_t *res,
const quat_lattice_t *lat1,
const quat_lattice_t *lat2); // ideal, lattice, test/lattice
/** @brief a*b for lattices
*
* @param res Output
* @param lat1 Lattice
* @param lat2 Lattice
* @param alg The quaternion algebra
*/
void quat_lattice_mul(quat_lattice_t *res,
const quat_lattice_t *lat1,
const quat_lattice_t *lat2,
const quat_alg_t *alg); // ideal, lattie, test/ideal, test/lattice
/**
* @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.
*
* Computation 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); // lattice, ideal
/**
* @brief Multiply all columns of lat with coord (as algebra elements)
*
* The columns and coord are seen as algebra elements in basis 1,i,j,ij, i^2 = -1, j^2 = -p). Coord
* is multiplied to the right of lat.
*
* The output matrix is not under HNF.
*
* @param prod Output: Matrix not under HND whose columns represent the algebra elements obtained as
* L*coord for L column of lat.
* @param lat Matrix whose columns are algebra elements in basis (1,i,j,ij)
* @param coord Integer coordinate algebra element in basis (1,i,j,ij)
* @param alg The quaternion algebra
*/
void quat_lattice_mat_alg_coord_mul_without_hnf(ibz_mat_4x4_t *prod,
const ibz_mat_4x4_t *lat,
const ibz_vec_4_t *coord,
const quat_alg_t *alg); // lattice
/** @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); // ideal
/** @brief Compute the Gram matrix of the quaternion trace bilinear form
*
* Given a lattice of the quaternion algebra, computes the Gram matrix
* of the bilinear form
*
* 〈a,b〉 := [lattice->denom^2] Tr(a·conj(b))
*
* multiplied by the square of the denominator of the lattice.
*
* This matrix always has integer entries.
*
* @param G Output: Gram matrix of the trace bilinear form on the lattice, multiplied by the square
* of the denominator of the lattice
* @param lattice A lattice
* @param alg The quaternion algebra
*/
void quat_lattice_gram(ibz_mat_4x4_t *G, const quat_lattice_t *lattice, const quat_alg_t *alg);
/**
* @brief Compute an integer parallelogram containing the ball of
* given radius for the positive definite quadratic form defined by
* the Gram matrix G.
*
* The computed parallelogram is defined by the vectors
*
* (x₁ x₂ x₃ x₄) · U
*
* with x_i ∈ [ -box[i], box[i] ].
*
* @param box Output: bounds of the parallelogram
* @param U Output: Unimodular transformation defining the parallelogram
* @param G Gram matrix of the quadratic form, must be full rank
* @param radius Radius of the ball, must be non-negative
* @returns 0 if the box only contains the origin, 1 otherwise
*/
int quat_lattice_bound_parallelogram(ibz_vec_4_t *box, ibz_mat_4x4_t *U, const ibz_mat_4x4_t *G, const ibz_t *radius);
/** @}
*/
/** @internal
* @defgroup quat_lideal_helper Helper functions for ideals and orders
* @{
*/
/** @brief Set norm of an ideal given its lattice and parent order
*
* @param lideal In/Output: Ideal which has lattice and parent_order correctly set, but not
* necessarily the norm. Will have norm correctly set too.
*/
void quat_lideal_norm(quat_left_ideal_t *lideal); // ideal
/**
* @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_lattice_t *order,
const quat_alg_t *alg); // ideal, test/ideal
/**
* @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); // test/ideal
/**
* @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); // Not used outside
/**
* @brief Left ideal product of left ideal I and element alpha
*
* @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
*
* I*alpha where I is a left-ideal and alpha an element of the algebra
*
* The resulting ideal must have an integer norm
*
*/
void 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); // test/ideal
/** @brief Computes the inverse ideal (for a left ideal of a maximal order) without putting it under
* HNF
*
* This function returns a lattice not under HNF. For careful internal use only
*
* Computes the inverse ideal for lideal as conjugate(lideal)/norm(lideal)
*
* @param inv Output: lattice which is lattice representation of the inverse ideal of lideal
* ATTENTION: is not under HNF. hnf computation must be applied before using lattice functions on it
* @param lideal Left ideal of a maximal order in alg
* @param alg The quaternion algebra
*/
void quat_lideal_inverse_lattice_without_hnf(quat_lattice_t *inv,
const quat_left_ideal_t *lideal,
const quat_alg_t *alg); // ideal
/** @brief Computes the right transporter of two left ideals of the same maximal order
*
* Following the implementation of ideal isomorphisms in the code of LearningToSQI's sage
* implementation of SQIsign. Computes the right transporter of (J:I) as inverse(I)J.
*
* @param trans Output: lattice which is right transporter from lideal1 to lideal2 (lideal2:lideal1)
* @param lideal1 Left ideal of the same maximal order than lideal1 in alg
* @param lideal2 Left ideal of the same maximal order than lideal1 in alg
* @param alg The quaternion algebra
*/
void quat_lideal_right_transporter(quat_lattice_t *trans,
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_lattice_t *order, const quat_left_ideal_t *lideal,
const quat_alg_t *alg); // ideal
/**
* @brief Gram matrix of the trace map of the ideal class
*
* Compute the Gram matrix of the bilinear form
*
* 〈a, b〉 := Tr(a·conj(b)) / norm(lideal)
*
* on the basis of the ideal. This matrix has integer entries and its
* integer congruence class only depends on the ideal class.
*
* @param G Output: Gram matrix of the trace map
* @param lideal left ideal
* @param alg the quaternion algebra
*/
void quat_lideal_class_gram(ibz_mat_4x4_t *G, const quat_left_ideal_t *lideal, const quat_alg_t *alg);
/** @brief Test if order is maximal
*
* Checks if the discriminant of the order equals the prime p defining the quaternion algebra.
*
* It is not verified whether the order is really an order. The output 1 only means that if it is an
* order, then it is maximal.
*
* @returns 1 if order is maximal (assuming it is an order), 0 otherwise
* @param order An order of the quaternion algebra (assumes to be an order, this is not tested)
* @param alg The quaternion algebra
*/
int quat_order_is_maximal(const quat_lattice_t *order,
const quat_alg_t *alg); // ideal (only in asserts)
/** @brief Compute the discriminant of an order as sqrt(det(gram(reduced_norm)))
*
* @param disc: Output: The discriminant sqrt(det(gram(reduced_norm)))
* @param order An order of the quaternion algebra
* @param alg The quaternion algebra
*/
int quat_order_discriminant(ibz_t *disc, const quat_lattice_t *order,
const quat_alg_t *alg); // ideal
/** @}
*/
/** @internal
* @ingroup quat_normeq
* @{
*/
/** @brief Set lattice to O0
*
* @param O0 Lattice to be set to (1,i,(i+j)/2,(1+ij)/2)
*/
void quat_lattice_O0_set(quat_lattice_t *O0);
/** @brief Set p-extremal maximal order to O0
*
* @param O0 p-extremal order to be set to (1,i,(i+j)/2,(1+ij)/2)
*/
void quat_lattice_O0_set_extremal(quat_p_extremal_maximal_order_t *O0);
/**
* @brief Create an element of a extremal maximal order from its coefficients
*
* @param elem Output: the quaternion element
* @param order the order
* @param coeffs the vector of 4 ibz coefficients
* @param Bpoo quaternion algebra
*
* elem = x + z*y + z*u + t*z*v
* where coeffs = [x,y,u,v] and t = order.t z = order.z
*
*/
void quat_order_elem_create(quat_alg_elem_t *elem,
const quat_p_extremal_maximal_order_t *order,
const ibz_vec_4_t *coeffs,
const quat_alg_t *Bpoo); // normeq, untested
/** @}
*/
/** @}
*/
#endif

View File

@@ -0,0 +1,238 @@
#ifndef LLL_INTERNALS_H
#define LLL_INTERNALS_H
/** @file
*
* @authors Sina Schaeffler
*
* @brief Declarations of functions only used for the LLL tets
*/
#include <quaternion.h>
/** @internal
* @ingroup quat_helpers
* @defgroup lll_internal Functions only used for LLL or its tests
* @{
*/
/** @internal
* @ingroup lll_internal
* @defgroup lll_params Parameters used by the L2 implementation (floats) and its tests (ints)
* @{
*/
#define DELTABAR 0.995
#define DELTA_NUM 99
#define DELTA_DENOM 100
#define ETABAR 0.505
#define EPSILON_NUM 1
#define EPSILON_DENOM 100
#define PREC 64
/**
* @}
*/
/** @internal
* @ingroup lll_internal
* @defgroup ibq_t Types for rationals
* @{
*/
/** @brief Type for fractions of integers
*
* @typedef ibq_t
*
* For fractions of integers of arbitrary size, used by intbig module, using gmp
*/
typedef ibz_t ibq_t[2];
typedef ibq_t ibq_vec_4_t[4];
typedef ibq_t ibq_mat_4x4_t[4][4];
/**@}
*/
/** @internal
* @ingroup lll_internal
* @defgroup lll_ibq_c Constructors and Destructors and Printers
* @{
*/
void ibq_init(ibq_t *x);
void ibq_finalize(ibq_t *x);
void ibq_mat_4x4_init(ibq_mat_4x4_t *mat);
void ibq_mat_4x4_finalize(ibq_mat_4x4_t *mat);
void ibq_vec_4_init(ibq_vec_4_t *vec);
void ibq_vec_4_finalize(ibq_vec_4_t *vec);
void ibq_mat_4x4_print(const ibq_mat_4x4_t *mat);
void ibq_vec_4_print(const ibq_vec_4_t *vec);
/** @}
*/
/** @internal
* @ingroup lll_internal
* @defgroup lll_qa Basic fraction arithmetic
* @{
*/
/** @brief sum=a+b
*/
void ibq_add(ibq_t *sum, const ibq_t *a, const ibq_t *b);
/** @brief diff=a-b
*/
void ibq_sub(ibq_t *diff, const ibq_t *a, const ibq_t *b);
/** @brief neg=-x
*/
void ibq_neg(ibq_t *neg, const ibq_t *x);
/** @brief abs=|x|
*/
void ibq_abs(ibq_t *abs, const ibq_t *x);
/** @brief prod=a*b
*/
void ibq_mul(ibq_t *prod, const ibq_t *a, const ibq_t *b);
/** @brief inv=1/x
*
* @returns 0 if x is 0, 1 if inverse exists and was computed
*/
int ibq_inv(ibq_t *inv, const ibq_t *x);
/** @brief Compare a and b
*
* @returns a positive value if a > b, zero if a = b, and a negative value if a < b
*/
int ibq_cmp(const ibq_t *a, const ibq_t *b);
/** @brief Test if x is 0
*
* @returns 1 if x=0, 0 otherwise
*/
int ibq_is_zero(const ibq_t *x);
/** @brief Test if x is 1
*
* @returns 1 if x=1, 0 otherwise
*/
int ibq_is_one(const ibq_t *x);
/** @brief Set q to a/b if b not 0
*
* @returns 1 if b not 0 and q is set, 0 otherwise
*/
int ibq_set(ibq_t *q, const ibz_t *a, const ibz_t *b);
/** @brief Copy value into target
*/
void ibq_copy(ibq_t *target, const ibq_t *value);
/** @brief Checks if q is an integer
*
* @returns 1 if yes, 0 if not
*/
int ibq_is_ibz(const ibq_t *q);
/**
* @brief Converts a fraction q to an integer y, if q is an integer.
*
* @returns 1 if z is an integer, 0 if not
*/
int ibq_to_ibz(ibz_t *z, const ibq_t *q);
/** @}
*/
/** @internal
* @ingroup lll_internal
* @defgroup quat_lll_verify_helpers Helper functions for lll verification in dimension 4
* @{
*/
/** @brief Set ibq to parameters delta and eta = 1/2 + epsilon using L2 constants
*/
void quat_lll_set_ibq_parameters(ibq_t *delta, ibq_t *eta);
/** @brief Set an ibq vector to 4 given integer coefficients
*/
void ibq_vec_4_copy_ibz(ibq_vec_4_t *vec,
const ibz_t *coeff0,
const ibz_t *coeff1,
const ibz_t *coeff2,
const ibz_t *coeff3); // dim4, test/dim4
/** @brief Bilinear form vec00*vec10+vec01*vec11+q*vec02*vec12+q*vec03*vec13 for ibz_q
*/
void quat_lll_bilinear(ibq_t *b, const ibq_vec_4_t *vec0, const ibq_vec_4_t *vec1,
const ibz_t *q); // dim4, test/dim4
/** @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_lll_gram_schmidt_transposed_with_ibq(ibq_mat_4x4_t *orthogonalised_transposed,
const ibz_mat_4x4_t *mat,
const ibz_t *q); // dim4
/** @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_lll_verify(const ibz_mat_4x4_t *mat,
const ibq_t *delta,
const ibq_t *eta,
const quat_alg_t *alg); // test/lattice, test/dim4
/** @}
*/
/** @internal
* @ingroup lll_internal
* @defgroup lll_internal_gram Internal LLL function
* @{
*/
/** @brief In-place L2 reduction core function
*
* Given a lattice basis represented by the columns of a 4x4 matrix
* and the Gram matrix of its bilinear form, L2-reduces the basis
* in-place and updates the Gram matrix accordingly.
*
* Implements the L2 Algorithm of Nguyen-Stehlé, also known as fplll:
* https://iacr.org/archive/eurocrypt2005/34940217/34940217.pdf
*
* Parameters are in lll/lll_internals.h
*
* @param G In/Output: Gram matrix of the lattice basis
* @param basis In/Output: lattice basis
*/
void quat_lll_core(ibz_mat_4x4_t *G, ibz_mat_4x4_t *basis);
/**
* @brief LLL reduction on 4-dimensional lattice
*
* Implements the L2 Algorithm of Nguyen-Stehlé, also known as fplll:
* https://iacr.org/archive/eurocrypt2005/34940217/34940217.pdf
*
* Parameters are in lll/lll_internals.h
*
* @param red Output: LLL reduced basis
* @param lattice In/Output: lattice with 4-dimensional basis
* @param alg The quaternion algebra
*/
int quat_lattice_lll(ibz_mat_4x4_t *red, const quat_lattice_t *lattice, const quat_alg_t *alg);
/**
* @}
*/
// end of lll_internal
/** @}
*/
#endif

View File

@@ -0,0 +1,349 @@
/** @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_helpers
* @defgroup quat_tests Quaternion module test functions
* @{
*/
/** @internal
* @ingroup quat_tests
* @defgroup quat_test_inputs Quaternion module random test input generation
* @{
*/
/**
* @brief Generates list of random ideals of a special extremal order
*
* @param ideals Output: Array of iterations left ideals of the given special extremal order and of
* norms of size norm_bitsize
* @param norm_bitsize Bitsize of the norms of the outut ideals
* @param iterations Number of ideals to sample. Most be smaller than ideals is long
* @param params quat_represent_integer_params_t parameters for the left order of the ideals to be
* sampled.
* @return 0 if success, 1 if failure
*/
int quat_test_input_random_ideal_generation(quat_left_ideal_t *ideals,
int norm_bitsize,
int iterations,
const quat_represent_integer_params_t *params);
/**
* @brief Generates list of random ideals of a special extremal order
*
* @param lattices Output: Array of iterations left ideals of the given special extremal order and
* of norms of size norm_bitsize, given only by their lattices
* @param norms Output: Array which will contain the norms of the sampled ideals, in the same order
* as their lattices are. Can be NULL, in which case no norm is output. Otherwise, it must be an
* array of length at least iterations
* @param norm_bitsize Bitsize of the norms of the outut ideals
* @param iterations Number of ideals to sample. Most be smaller than lattices is long
* @param params quat_represent_integer_params_t parameters for the left order of the ideals to be
* sampled.
* @return 0 if success, 1 if failure
*/
int quat_test_input_random_ideal_lattice_generation(quat_lattice_t *lattices,
ibz_t *norms,
int norm_bitsize,
int iterations,
const quat_represent_integer_params_t *params);
/**
* @brief Generates list of random lattices
*
* @param lattices Output: Array of iterations lattices
* @param bitsize Bitsize of the coefficients of a random basis of the lattices
* @param iterations Number of lattices to sample.Most be smaller than lattices is long
* @param in_hnf If not 0, the lattices are put in HNF before being outputs. Their coefficients in
* this basis might be larger than bitsize.
* @return 0 if success, 1 if failure
*/
int quat_test_input_random_lattice_generation(quat_lattice_t *lattices, int bitsize, int iterations, int in_hnf);
/**
* @}
*/
/** @brief Test for integer functions
*
* void ibz_init(ibz_t *x);
*
* void ibz_finalize(ibz_t *x);
*
* void ibz_add(ibz_t *sum, const ibz_t *a, const ibz_t *b);
*
* void ibz_sub(ibz_t *diff, const ibz_t *a, const ibz_t *b);
*
* void ibz_mul(ibz_t *prod, const ibz_t *a, const ibz_t *b);
*
* void ibz_neg(ibz_t *neg, const ibz_t *a);
*
* void ibz_abs(ibz_t *abs, const ibz_t *a);
*
* void ibz_div(ibz_t *quotient, ibz_t *remainder, const ibz_t *a, const ibz_t *b);
*
* void ibz_div_2exp(ibz_t *quotient, const ibz_t *a, uint32_t exp);
*
* void ibz_mod(ibz_t *r, const ibz_t *a, const ibz_t *b);
*
* unsigned long int ibz_mod_ui(const mpz_t *n, unsigned long int d);
*
* int ibz_divides(const ibz_t *a, const ibz_t *b);
*
* void ibz_pow(ibz_t *pow, const ibz_t *x, uint32_t e);
*
* void ibz_pow_mod(ibz_t *pow, const ibz_t *x, const ibz_t *e, const ibz_t *m);
*
* int ibz_cmp(const ibz_t *a, const ibz_t *b);
*
* int ibz_is_zero(const ibz_t *x);
*
* int ibz_is_one(const ibz_t *x);
*
* int ibz_cmp_int32(const ibz_t *x, int32_t y);
*
* int ibz_is_even(const ibz_t *x);
*
* int ibz_is_odd(const ibz_t *x);
*
* void ibz_set(ibz_t *i, int32_t x);
*
* void ibz_copy(ibz_t *target, const ibz_t *value);
*
* void ibz_swap(ibz_t *a, ibz_t *b);
*
* void ibz_copy_digits(ibz_t *target, const digit_t *dig, int dig_len);
*
* void ibz_to_digits(digit_t *target, const ibz_t *ibz);
*
* int32_t ibz_get(const ibz_t *i);
*
* int ibz_rand_interval(ibz_t *rand, const ibz_t *a, const ibz_t *b);
*
* int ibz_rand_interval_minm_m(ibz_t *rand, int32_t m);
*
* int ibz_bitsize(const ibz_t *a);
*
* void ibz_gcd(ibz_t *gcd, const ibz_t *a, const ibz_t *b);
*
* int ibz_invmod(ibz_t *inv, const ibz_t *a, const ibz_t *mod);
*
* void ibz_sqrt_floor(ibz_t *sqrt, const ibz_t *a);
*/
int ibz_test_intbig(void);
/** @brief Test for implementations of GMP functions missing from the mini-GMP API
*
* int mpz_legendre(const mpz_t a, const mpz_t p);
*
* double mpz_get_d_2exp(signed long int *exp, const mpz_t op);
*/
int mini_gmp_test(void);
/** @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_2_t
*
* ibz_vec_4_t
*
* ibz_mat_2x2_t
*
* ibz_mat_4x4_t
*
* quat_lattice_t
*
* quat_lattice_t
*
* quat_left_ideal_t
*/
int quat_test_finit(void);
/** @brief Test integer, quadratic form and matrix functions for dimension 4 from the quaternion
* module
*
* Runs unit tests for the following functions
*
* 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_vec_4_content(ibz_t *content, const quat_alg_coord_t *v);
*/
int quat_test_dim4(void);
/** @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_copy(ibz_vec_2_t *copy, const ibz_mat_2x2_t *copied);
*
* void ibz_mat_2x2_eval(ibz_vec_2_t *res, const ibz_mat_2x2_t *mat, const ibz_vec_2_t *vec);
*
* void ibz_mat_4x4_eval_t(ibz_vec_4_t *res, const ibz_vec_4_t *vec, const ibz_mat_4x4_t *mat);
*
* 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_mat_2x2_inv_mod(ibz_mat_2x2_t *inv, const ibz_mat_2x2_t *mat, const ibz_t *m);
*/
int quat_test_dim2(void);
/** @brief Test integer functions
*
* Runs unit tests for the following functions
*
* int ibz_generate_random_prime(ibz_t *p, int is3mod4, int bitsize);
*
* int ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p);
*/
int quat_test_integers(void);
/** @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(ibz_t *res_num, ibz_t *res_denom, const quat_alg_elem_t *a, const quat_alg_t
* *alg);
*
* 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_lattice_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);
*
* void quat_alg_elem_copy(quat_alg_elem_t *copy, const quat_alg_elem_t *copied);
*/
int quat_test_algebra(void);
/** @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_conjugate_without_hnf(quat_lattice_t *conj, const quat_lattice_t *lat);
*
* void quat_lattice_alg_elem_mul(quat_lattice_t *prod, const quat_lattice_t *lat, const
* quat_alg_elem_t *elem, const quat_alg_t *alg);
*
* 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(void);
/** @brief Test for lattice reduction and functions based on it
*
* int 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);
*
* int quat_lideal_lideal_mul_reduced(quat_left_ideal_t *prod, ibz_mat_4x4_t *gram, const
* quat_left_ideal_t *lideal1,const quat_left_ideal_t *lideal2, const quat_alg_t *alg);
*
* int quat_lideal_prime_norm_reduced_equivalent(quat_left_ideal_t *lideal, const quat_alg_t *alg,
* const int primality_num_iter, const int equiv_bound_coeff);
*/
int quat_test_lll(void);
/** @brief Test operations on left ideals and their creation
*
* Runs unit tests for the following functions
*
* void quat_lideal_copy(quat_left_ideal_t *copy, const quat_left_ideal_t *copied)
*
* void quat_lideal_create_principal(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const
* quat_lattice_t *order, const quat_alg_t *alg);
*
* void quat_lideal_create(quat_left_ideal_t *lideal, const quat_alg_elem_t *x, const
* ibz_t *N, const quat_lattice_t *order, const quat_alg_t *alg);
*
* int quat_lideal_generator(quat_alg_elem_t *gen, const quat_left_ideal_t *lideal, const
* quat_alg_t);
*
* 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(void);
/** @brief Test operations on represent integer
*
* int quat_sampling_random_ideal_O0_given_norm(quat_left_ideal_t *lideal,const ibz_t *norm,int
* is_prime,const quat_represent_integer_params_t *params,int prime_sampling_attempts);
*
* void quat_change_to_O0_basis(ibz_vec_4_t *vec, const quat_alg_elem_t *el);
*
* int quat_represent_integer(quat_alg_elem_t *gamma, const ibz_t *n_gamma, int non_diag, const
* quat_represent_integer_params_t *params);
*/
int quat_test_normeq(void);
/** @brief Test functions for sampling lattice points of bounded norm
*
* int quat_lattice_sample_from_ball(ibz_vec_4_t *x, const quat_lattice_t *lattice, const quat_alg_t
* *alg, const ibz_t *radius);
*/
int quat_test_lat_ball(void);
/** @brief Test for hnf core and verification
*
*/
int quat_test_hnf(void);
/** @brief Test with randomization for complex functions where this is possible
*
*/
int quat_test_with_randomization(void);
/** @}
*/
#endif

View File

@@ -0,0 +1,139 @@
#include <quaternion.h>
#include <rng.h>
#include <stdio.h>
#include "internal.h"
#include "lll_internals.h"
int
quat_lattice_bound_parallelogram(ibz_vec_4_t *box, ibz_mat_4x4_t *U, const ibz_mat_4x4_t *G, const ibz_t *radius)
{
ibz_t denom, rem;
ibz_init(&denom);
ibz_init(&rem);
ibz_mat_4x4_t dualG;
ibz_mat_4x4_init(&dualG);
// Compute the Gram matrix of the dual lattice
#ifndef NDEBUG
int inv_check = ibz_mat_4x4_inv_with_det_as_denom(&dualG, &denom, G);
assert(inv_check);
#else
(void)ibz_mat_4x4_inv_with_det_as_denom(&dualG, &denom, G);
#endif
// Initialize the dual lattice basis to the identity matrix
ibz_mat_4x4_identity(U);
// Reduce the dual lattice
quat_lll_core(&dualG, U);
// Compute the parallelogram's bounds
int trivial = 1;
for (int i = 0; i < 4; i++) {
ibz_mul(&(*box)[i], &dualG[i][i], radius);
ibz_div(&(*box)[i], &rem, &(*box)[i], &denom);
ibz_sqrt_floor(&(*box)[i], &(*box)[i]);
trivial &= ibz_is_zero(&(*box)[i]);
}
// Compute the transpose transformation matrix
#ifndef NDEBUG
int inv = ibz_mat_4x4_inv_with_det_as_denom(U, &denom, U);
#else
(void)ibz_mat_4x4_inv_with_det_as_denom(U, &denom, U);
#endif
// U is unitary, det(U) = ± 1
ibz_mat_4x4_scalar_mul(U, &denom, U);
#ifndef NDEBUG
assert(inv);
ibz_abs(&denom, &denom);
assert(ibz_is_one(&denom));
#endif
ibz_mat_4x4_finalize(&dualG);
ibz_finalize(&denom);
ibz_finalize(&rem);
return !trivial;
}
int
quat_lattice_sample_from_ball(quat_alg_elem_t *res,
const quat_lattice_t *lattice,
const quat_alg_t *alg,
const ibz_t *radius)
{
assert(ibz_cmp(radius, &ibz_const_zero) > 0);
ibz_vec_4_t box;
ibz_vec_4_init(&box);
ibz_mat_4x4_t U, G;
ibz_mat_4x4_init(&U);
ibz_mat_4x4_init(&G);
ibz_vec_4_t x;
ibz_vec_4_init(&x);
ibz_t rad, tmp;
ibz_init(&rad);
ibz_init(&tmp);
// Compute the Gram matrix of the lattice
quat_lattice_gram(&G, lattice, alg);
// Correct ball radius by the denominator
ibz_mul(&rad, radius, &lattice->denom);
ibz_mul(&rad, &rad, &lattice->denom);
// Correct by 2 (Gram matrix corresponds to twice the norm)
ibz_mul(&rad, &rad, &ibz_const_two);
// Compute a bounding parallelogram for the ball, stop if it only
// contains the origin
int ok = quat_lattice_bound_parallelogram(&box, &U, &G, &rad);
if (!ok)
goto err;
// Rejection sampling from the parallelogram
#ifndef NDEBUG
int cnt = 0;
#endif
do {
// Sample vector
for (int i = 0; i < 4; i++) {
if (ibz_is_zero(&box[i])) {
ibz_copy(&x[i], &ibz_const_zero);
} else {
ibz_add(&tmp, &box[i], &box[i]);
ok &= ibz_rand_interval(&x[i], &ibz_const_zero, &tmp);
ibz_sub(&x[i], &x[i], &box[i]);
if (!ok)
goto err;
}
}
// Map to parallelogram
ibz_mat_4x4_eval_t(&x, &x, &U);
// Evaluate quadratic form
quat_qf_eval(&tmp, &G, &x);
#ifndef NDEBUG
cnt++;
if (cnt % 100 == 0)
printf("Lattice sampling rejected %d times", cnt - 1);
#endif
} while (ibz_is_zero(&tmp) || (ibz_cmp(&tmp, &rad) > 0));
// Evaluate linear combination
ibz_mat_4x4_eval(&(res->coord), &(lattice->basis), &x);
ibz_copy(&(res->denom), &(lattice->denom));
quat_alg_normalize(res);
#ifndef NDEBUG
// Check norm is smaller than radius
quat_alg_norm(&tmp, &rad, res, alg);
ibz_mul(&rad, &rad, radius);
assert(ibz_cmp(&tmp, &rad) <= 0);
#endif
err:
ibz_finalize(&rad);
ibz_finalize(&tmp);
ibz_vec_4_finalize(&x);
ibz_mat_4x4_finalize(&U);
ibz_mat_4x4_finalize(&G);
ibz_vec_4_finalize(&box);
return ok;
}

View File

@@ -2,468 +2,327 @@
#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);
// helper functions
int
quat_lattice_equal(const quat_lattice_t *lat1, const quat_lattice_t *lat2)
{
int equal = 1;
quat_lattice_t a, b;
quat_lattice_init(&a);
quat_lattice_init(&b);
quat_lattice_reduce_denom(&a, lat1);
quat_lattice_reduce_denom(&b, lat2);
ibz_abs(&(a.denom), &(a.denom));
ibz_abs(&(b.denom), &(b.denom));
quat_lattice_hnf(&a);
quat_lattice_hnf(&b);
equal = equal && (ibz_cmp(&(a.denom), &(b.denom)) == 0);
equal = equal && ibz_mat_4x4_equal(&(a.basis), &(b.basis));
quat_lattice_finalize(&a);
quat_lattice_finalize(&b);
return (equal);
}
void quat_lattice_reduce_denom(quat_lattice_t *reduced, const quat_lattice_t *lat){
// sublattice test
int
quat_lattice_inclusion(const quat_lattice_t *sublat, const quat_lattice_t *overlat)
{
int res;
quat_lattice_t sum;
quat_lattice_init(&sum);
quat_lattice_add(&sum, overlat, sublat);
res = quat_lattice_equal(&sum, overlat);
quat_lattice_finalize(&sum);
return (res);
}
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_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_abs(&(reduced->denom), &(reduced->denom));
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){
void
quat_lattice_conjugate_without_hnf(quat_lattice_t *conj, const quat_lattice_t *lat)
{
ibz_mat_4x4_copy(&(conj->basis), &(lat->basis));
ibz_copy(&(conj->denom), &(lat->denom));
for (int row = 1; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
ibz_neg(&(conj->basis[row][col]), &(conj->basis[row][col]));
}
}
}
// 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);
ibz_mat_4x4_inv_with_det_as_denom(&inv, &det, &(lat->basis));
ibz_mat_4x4_transpose(&inv, &inv);
// dual_denom = det/lat_denom
ibz_mat_4x4_scalar_mul(&(dual->basis),&(lat->denom),&inv);
ibz_copy(&(dual-> denom),&det);
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;
void
quat_lattice_add(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2)
{
ibz_vec_4_t generators[8];
ibz_mat_4x4_t tmp;
ibz_t det1, det2, detprod;
ibz_init(&det1);
ibz_init(&det2);
ibz_init(&detprod);
for (int i = 0; i < 8; i++)
ibz_vec_4_init(&(generators[i]));
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, &(lat1->denom), &(lat2->basis));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_copy(&(generators[j][i]), &(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_4x4_inv_with_det_as_denom(NULL, &det1, &tmp);
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(&(generators[4 + j][i]), &(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_inv_with_det_as_denom(NULL, &det2, &tmp);
assert(!ibz_is_zero(&det1));
assert(!ibz_is_zero(&det2));
ibz_gcd(&detprod, &det1, &det2);
ibz_mat_4xn_hnf_mod_core(&(res->basis), 8, generators, &detprod);
ibz_mul(&(res->denom), &(lat1->denom), &(lat2->denom));
quat_lattice_reduce_denom(res, res);
ibz_mat_4x4_finalize(&tmp);
ibz_finalize(&det1);
ibz_finalize(&det2);
ibz_finalize(&detprod);
for (int i = 0; i < 8; i++)
ibz_vec_4_finalize(&(generators[i]));
}
// 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){
// 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_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); // could be removed if we do not expect HNF any more
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)
void
quat_lattice_mat_alg_coord_mul_without_hnf(ibz_mat_4x4_t *prod,
const ibz_mat_4x4_t *lat,
const ibz_vec_4_t *coord,
const quat_alg_t *alg)
{
ibz_vec_4_t p, a;
ibz_vec_4_init(&p);
ibz_vec_4_init(&a);
for (int i = 0; i < 4; i++) {
ibz_mul(index, index, &sublat->basis[i][i]);
ibz_vec_4_copy_ibz(&a, &((*lat)[0][i]), &((*lat)[1][i]), &((*lat)[2][i]), &((*lat)[3][i]));
quat_alg_coord_mul(&p, &a, coord, alg);
ibz_copy(&((*prod)[0][i]), &(p[0]));
ibz_copy(&((*prod)[1][i]), &(p[1]));
ibz_copy(&((*prod)[2][i]), &(p[2]));
ibz_copy(&((*prod)[3][i]), &(p[3]));
}
ibz_vec_4_finalize(&p);
ibz_vec_4_finalize(&a);
}
void
quat_lattice_alg_elem_mul(quat_lattice_t *prod,
const quat_lattice_t *lat,
const quat_alg_elem_t *elem,
const quat_alg_t *alg)
{
quat_lattice_mat_alg_coord_mul_without_hnf(&(prod->basis), &(lat->basis), &(elem->coord), alg);
ibz_mul(&(prod->denom), &(lat->denom), &(elem->denom));
quat_lattice_hnf(prod);
}
void
quat_lattice_mul(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t *lat2, const quat_alg_t *alg)
{
ibz_vec_4_t elem1, elem2, elem_res;
ibz_vec_4_t generators[16];
ibz_mat_4x4_t detmat;
ibz_t det;
quat_lattice_t lat_res;
ibz_init(&det);
ibz_mat_4x4_init(&detmat);
quat_lattice_init(&lat_res);
ibz_vec_4_init(&elem1);
ibz_vec_4_init(&elem2);
ibz_vec_4_init(&elem_res);
for (int i = 0; i < 16; i++)
ibz_vec_4_init(&(generators[i]));
for (int k = 0; k < 4; k++) {
ibz_vec_4_copy_ibz(
&elem1, &(lat1->basis[0][k]), &(lat1->basis[1][k]), &(lat1->basis[2][k]), &(lat1->basis[3][k]));
for (int i = 0; i < 4; i++) {
ibz_vec_4_copy_ibz(
&elem2, &(lat2->basis[0][i]), &(lat2->basis[1][i]), &(lat2->basis[2][i]), &(lat2->basis[3][i]));
quat_alg_coord_mul(&elem_res, &elem1, &elem2, alg);
for (int j = 0; j < 4; j++) {
if (k == 0)
ibz_copy(&(detmat[i][j]), &(elem_res[j]));
ibz_copy(&(generators[4 * k + i][j]), &(elem_res[j]));
}
}
}
ibz_mat_4x4_inv_with_det_as_denom(NULL, &det, &detmat);
ibz_abs(&det, &det);
ibz_mat_4xn_hnf_mod_core(&(res->basis), 16, generators, &det);
ibz_mul(&(res->denom), &(lat1->denom), &(lat2->denom));
quat_lattice_reduce_denom(res, res);
ibz_vec_4_finalize(&elem1);
ibz_vec_4_finalize(&elem2);
ibz_vec_4_finalize(&elem_res);
quat_lattice_finalize(&lat_res);
ibz_finalize(&det);
ibz_mat_4x4_finalize(&(detmat));
for (int i = 0; i < 16; i++)
ibz_vec_4_finalize(&(generators[i]));
}
// lattice assumed of full rank
int
quat_lattice_contains(ibz_vec_4_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t *x)
{
int divisible = 0;
ibz_vec_4_t work_coord;
ibz_mat_4x4_t inv;
ibz_t det, prod;
ibz_init(&prod);
ibz_init(&det);
ibz_vec_4_init(&work_coord);
ibz_mat_4x4_init(&inv);
ibz_mat_4x4_inv_with_det_as_denom(&inv, &det, &(lat->basis));
assert(!ibz_is_zero(&det));
ibz_mat_4x4_eval(&work_coord, &inv, &(x->coord));
ibz_vec_4_scalar_mul(&(work_coord), &(lat->denom), &work_coord);
ibz_mul(&prod, &(x->denom), &det);
divisible = ibz_vec_4_scalar_div(&work_coord, &prod, &work_coord);
// copy result
if (divisible && (coord != NULL)) {
for (int i = 0; i < 4; i++) {
ibz_copy(&((*coord)[i]), &(work_coord[i]));
}
}
ibz_finalize(&prod);
ibz_finalize(&det);
ibz_mat_4x4_finalize(&inv);
ibz_vec_4_finalize(&work_coord);
return (divisible);
}
void
quat_lattice_index(ibz_t *index, const quat_lattice_t *sublat, const quat_lattice_t *overlat)
{
ibz_t tmp, det;
ibz_init(&tmp);
ibz_init(&det);
// det = det(sublat->basis)
ibz_mat_4x4_inv_with_det_as_denom(NULL, &det, &sublat->basis);
// tmp = (overlat->denom)⁴
ibz_mul(&tmp, &overlat->denom, &overlat->denom);
ibz_mul(&tmp, &tmp, &tmp);
// index = (overlat->denom)⁴ · det(sublat->basis)
ibz_mul(index, &det, &tmp);
// tmp = (sublat->denom)⁴
ibz_mul(&tmp, &sublat->denom, &sublat->denom);
ibz_mul(&tmp, &tmp, &tmp);
// det = det(overlat->basis)
ibz_mat_4x4_inv_with_det_as_denom(NULL, &det, &overlat->basis);
// tmp = (sublat->denom)⁴ · det(overlat->basis)
for (int i = 0; i < 4; i++) {
ibz_mul(&tmp, &tmp, &overlat->basis[i][i]);
}
ibz_mul(&tmp, &tmp, &det);
// index = index / tmp
ibz_div(index, &tmp, index, &tmp);
assert(ibz_is_zero(&tmp));
// index = |index|
ibz_abs(index, index);
ibz_finalize(&tmp);
ibz_finalize(&det);
}
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)
void
quat_lattice_hnf(quat_lattice_t *lat)
{
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_t mod;
ibz_vec_4_t generators[4];
ibz_init(&mod);
ibz_mat_4x4_inv_with_det_as_denom(NULL, &mod, &(lat->basis));
ibz_abs(&mod, &mod);
for (int i = 0; i < 4; i++)
ibz_vec_4_init(&(generators[i]));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_copy(&(generators[j][i]), &(lat->basis[i][j]));
}
}
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);
ibz_mat_4xn_hnf_mod_core(&(lat->basis), 4, generators, &mod);
quat_lattice_reduce_denom(lat, lat);
ibz_finalize(&mod);
for (int i = 0; i < 4; i++)
ibz_vec_4_finalize(&(generators[i]));
}
void
quat_lattice_gram(ibz_mat_4x4_t *G, const quat_lattice_t *lattice, const quat_alg_t *alg)
{
ibz_t tmp;
ibz_init(&tmp);
for (int i = 0; i < 4; i++) {
for (int j = 0; j <= i; j++) {
ibz_set(&(*G)[i][j], 0);
for (int k = 0; k < 4; k++) {
ibz_mul(&tmp, &(lattice->basis)[k][i], &(lattice->basis)[k][j]);
if (k >= 2)
ibz_mul(&tmp, &tmp, &alg->p);
ibz_add(&(*G)[i][j], &(*G)[i][j], &tmp);
}
ibz_mul(&(*G)[i][j], &(*G)[i][j], &ibz_const_two);
}
}
for (int i = 0; i < 4; i++) {
for (int j = i + 1; j < 4; j++) {
ibz_copy(&(*G)[i][j], &(*G)[j][i]);
}
}
ibz_finalize(&tmp);
}

View File

@@ -1,366 +0,0 @@
#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,190 @@
#include <quaternion.h>
#include "lll_internals.h"
#include "internal.h"
#include "dpe.h"
// Access entry of symmetric matrix
#define SYM(M, i, j) (i < j ? &M[j][i] : &M[i][j])
void
quat_lll_core(ibz_mat_4x4_t *G, ibz_mat_4x4_t *basis)
{
dpe_t dpe_const_one, dpe_const_DELTABAR;
dpe_init(dpe_const_one);
dpe_set_ui(dpe_const_one, 1);
dpe_init(dpe_const_DELTABAR);
dpe_set_d(dpe_const_DELTABAR, DELTABAR);
// fp variables for Gram-Schmidt orthogonalization and Lovasz' conditions
dpe_t r[4][4], u[4][4], lovasz[4];
for (int i = 0; i < 4; i++) {
dpe_init(lovasz[i]);
for (int j = 0; j <= i; j++) {
dpe_init(r[i][j]);
dpe_init(u[i][j]);
}
}
// threshold for swaps
dpe_t delta_bar;
dpe_init(delta_bar);
dpe_set_d(delta_bar, DELTABAR);
// Other work variables
dpe_t Xf, tmpF;
dpe_init(Xf);
dpe_init(tmpF);
ibz_t X, tmpI;
ibz_init(&X);
ibz_init(&tmpI);
// Main L² loop
dpe_set_z(r[0][0], (*G)[0][0]);
int kappa = 1;
while (kappa < 4) {
// size reduce b_κ
int done = 0;
while (!done) {
// Recompute the κ-th row of the Choleski Factorisation
// Loop invariant:
// r[κ][j] ≈ u[κ][j] ‖b_j*‖² ≈ 〈b_κ, b_j*〉
for (int j = 0; j <= kappa; j++) {
dpe_set_z(r[kappa][j], (*G)[kappa][j]);
for (int k = 0; k < j; k++) {
dpe_mul(tmpF, r[kappa][k], u[j][k]);
dpe_sub(r[kappa][j], r[kappa][j], tmpF);
}
if (j < kappa)
dpe_div(u[kappa][j], r[kappa][j], r[j][j]);
}
done = 1;
// size reduce
for (int i = kappa - 1; i >= 0; i--) {
if (dpe_cmp_d(u[kappa][i], ETABAR) > 0 || dpe_cmp_d(u[kappa][i], -ETABAR) < 0) {
done = 0;
dpe_set(Xf, u[kappa][i]);
dpe_round(Xf, Xf);
dpe_get_z(X, Xf);
// Update basis: b_κ ← b_κ - X·b_i
for (int j = 0; j < 4; j++) {
ibz_mul(&tmpI, &X, &(*basis)[j][i]);
ibz_sub(&(*basis)[j][kappa], &(*basis)[j][kappa], &tmpI);
}
// Update lower half of the Gram matrix
// <b_κ - X·b_i, b_κ - X·b_i> = <b_κ, b_κ> - 2X<b_κ, b_i> + X²<b_i, b_i> =
// <b_κ,b_κ> - X<b_κ,b_i> - X(<b_κ,b_i> - X·<b_i, b_i>)
//// 〈b_κ, b_κ〉 ← 〈b_κ, b_κ〉 - X·〈b_κ, b_i〉
ibz_mul(&tmpI, &X, &(*G)[kappa][i]);
ibz_sub(&(*G)[kappa][kappa], &(*G)[kappa][kappa], &tmpI);
for (int j = 0; j < 4; j++) { // works because i < κ
// 〈b_κ, b_j〉 ← 〈b_κ, b_j〉 - X·〈b_i, b_j〉
ibz_mul(&tmpI, &X, SYM((*G), i, j));
ibz_sub(SYM((*G), kappa, j), SYM((*G), kappa, j), &tmpI);
}
// After the loop:
//// 〈b_κ,b_κ〉 ← 〈b_κ,b_κ〉 - X·〈b_κ,b_i〉 - X·(〈b_κ,b_i〉 - X·〈b_i,
/// b_i〉) = 〈b_κ - X·b_i, b_κ - X·b_i〉
//
// Update u[kappa][j]
for (int j = 0; j < i; j++) {
dpe_mul(tmpF, Xf, u[i][j]);
dpe_sub(u[kappa][j], u[kappa][j], tmpF);
}
}
}
}
// Check Lovasz' conditions
// lovasz[0] = ‖b_κ‖²
dpe_set_z(lovasz[0], (*G)[kappa][kappa]);
// lovasz[i] = lovasz[i-1] - u[κ][i-1]·r[κ][i-1]
for (int i = 1; i < kappa; i++) {
dpe_mul(tmpF, u[kappa][i - 1], r[kappa][i - 1]);
dpe_sub(lovasz[i], lovasz[i - 1], tmpF);
}
int swap;
for (swap = kappa; swap > 0; swap--) {
dpe_mul(tmpF, delta_bar, r[swap - 1][swap - 1]);
if (dpe_cmp(tmpF, lovasz[swap - 1]) < 0)
break;
}
// Insert b_κ before b_swap
if (kappa != swap) {
// Insert b_κ before b_swap in the basis and in the lower half Gram matrix
for (int j = kappa; j > swap; j--) {
for (int i = 0; i < 4; i++) {
ibz_swap(&(*basis)[i][j], &(*basis)[i][j - 1]);
if (i == j - 1)
ibz_swap(&(*G)[i][i], &(*G)[j][j]);
else if (i != j)
ibz_swap(SYM((*G), i, j), SYM((*G), i, j - 1));
}
}
// Copy row u[κ] and r[κ] in swap position, ignore what follows
for (int i = 0; i < swap; i++) {
dpe_set(u[swap][i], u[kappa][i]);
dpe_set(r[swap][i], r[kappa][i]);
}
dpe_set(r[swap][swap], lovasz[swap]);
// swap complete
kappa = swap;
}
kappa += 1;
}
#ifndef NDEBUG
// Check size-reducedness
for (int i = 0; i < 4; i++)
for (int j = 0; j < i; j++) {
dpe_abs(u[i][j], u[i][j]);
assert(dpe_cmp_d(u[i][j], ETABAR) <= 0);
}
// Check Lovasz' conditions
for (int i = 1; i < 4; i++) {
dpe_mul(tmpF, u[i][i - 1], u[i][i - 1]);
dpe_sub(tmpF, dpe_const_DELTABAR, tmpF);
dpe_mul(tmpF, tmpF, r[i - 1][i - 1]);
assert(dpe_cmp(tmpF, r[i][i]) <= 0);
}
#endif
// Fill in the upper half of the Gram matrix
for (int i = 0; i < 4; i++) {
for (int j = i + 1; j < 4; j++)
ibz_copy(&(*G)[i][j], &(*G)[j][i]);
}
// Clearinghouse
ibz_finalize(&X);
ibz_finalize(&tmpI);
dpe_clear(dpe_const_one);
dpe_clear(dpe_const_DELTABAR);
dpe_clear(Xf);
dpe_clear(tmpF);
dpe_clear(delta_bar);
for (int i = 0; i < 4; i++) {
dpe_clear(lovasz[i]);
for (int j = 0; j <= i; j++) {
dpe_clear(r[i][j]);
dpe_clear(u[i][j]);
}
}
}
int
quat_lattice_lll(ibz_mat_4x4_t *red, const quat_lattice_t *lattice, const quat_alg_t *alg)
{
ibz_mat_4x4_t G; // Gram Matrix
ibz_mat_4x4_init(&G);
quat_lattice_gram(&G, lattice, alg);
ibz_mat_4x4_copy(red, &lattice->basis);
quat_lll_core(&G, red);
ibz_mat_4x4_finalize(&G);
return 0;
}

View File

@@ -0,0 +1,127 @@
#include <quaternion.h>
#include <internal.h>
#include "lll_internals.h"
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)
{
assert(quat_order_is_maximal((lideal->parent_order), alg));
ibz_t gram_corrector;
ibz_init(&gram_corrector);
ibz_mul(&gram_corrector, &(lideal->lattice.denom), &(lideal->lattice.denom));
quat_lideal_class_gram(gram, lideal, alg);
ibz_mat_4x4_copy(reduced, &(lideal->lattice.basis));
quat_lll_core(gram, reduced);
ibz_mat_4x4_scalar_mul(gram, &gram_corrector, gram);
for (int i = 0; i < 4; i++) {
ibz_div_2exp(&((*gram)[i][i]), &((*gram)[i][i]), 1);
for (int j = i + 1; j < 4; j++) {
ibz_set(&((*gram)[i][j]), 0);
}
}
ibz_finalize(&gram_corrector);
}
void
quat_lideal_lideal_mul_reduced(quat_left_ideal_t *prod,
ibz_mat_4x4_t *gram,
const quat_left_ideal_t *lideal1,
const quat_left_ideal_t *lideal2,
const quat_alg_t *alg)
{
ibz_mat_4x4_t red;
ibz_mat_4x4_init(&red);
quat_lattice_mul(&(prod->lattice), &(lideal1->lattice), &(lideal2->lattice), alg);
prod->parent_order = lideal1->parent_order;
quat_lideal_norm(prod);
quat_lideal_reduce_basis(&red, gram, prod, alg);
ibz_mat_4x4_copy(&(prod->lattice.basis), &red);
ibz_mat_4x4_finalize(&red);
}
int
quat_lideal_prime_norm_reduced_equivalent(quat_left_ideal_t *lideal,
const quat_alg_t *alg,
const int primality_num_iter,
const int equiv_bound_coeff)
{
ibz_mat_4x4_t gram, red;
ibz_mat_4x4_init(&gram);
ibz_mat_4x4_init(&red);
int found = 0;
// computing the reduced basis
quat_lideal_reduce_basis(&red, &gram, lideal, alg);
quat_alg_elem_t new_alpha;
quat_alg_elem_init(&new_alpha);
ibz_t tmp, remainder, adjusted_norm;
ibz_init(&tmp);
ibz_init(&remainder);
ibz_init(&adjusted_norm);
ibz_mul(&adjusted_norm, &lideal->lattice.denom, &lideal->lattice.denom);
int ctr = 0;
// equiv_num_iter = (2 * equiv_bound_coeff + 1)^4
assert(equiv_bound_coeff < (1 << 20));
int equiv_num_iter = (2 * equiv_bound_coeff + 1);
equiv_num_iter = equiv_num_iter * equiv_num_iter;
equiv_num_iter = equiv_num_iter * equiv_num_iter;
while (!found && ctr < equiv_num_iter) {
ctr++;
// we select our linear combination at random
ibz_rand_interval_minm_m(&new_alpha.coord[0], equiv_bound_coeff);
ibz_rand_interval_minm_m(&new_alpha.coord[1], equiv_bound_coeff);
ibz_rand_interval_minm_m(&new_alpha.coord[2], equiv_bound_coeff);
ibz_rand_interval_minm_m(&new_alpha.coord[3], equiv_bound_coeff);
// computation of the norm of the vector sampled
quat_qf_eval(&tmp, &gram, &new_alpha.coord);
// compute the norm of the equivalent ideal
// can be improved by removing the power of two first and the odd part only if the trial
// division failed (this should always be called on an ideal of norm 2^x * N for some
// big prime N )
ibz_div(&tmp, &remainder, &tmp, &adjusted_norm);
// debug : check that the remainder is zero
assert(ibz_is_zero(&remainder));
// pseudo-primality test
if (ibz_probab_prime(&tmp, primality_num_iter)) {
// computes the generator using a matrix multiplication
ibz_mat_4x4_eval(&new_alpha.coord, &red, &new_alpha.coord);
ibz_copy(&new_alpha.denom, &lideal->lattice.denom);
assert(quat_lattice_contains(NULL, &lideal->lattice, &new_alpha));
quat_alg_conj(&new_alpha, &new_alpha);
ibz_mul(&new_alpha.denom, &new_alpha.denom, &lideal->norm);
quat_lideal_mul(lideal, lideal, &new_alpha, alg);
assert(ibz_probab_prime(&lideal->norm, primality_num_iter));
found = 1;
break;
}
}
assert(found);
ibz_finalize(&tmp);
ibz_finalize(&remainder);
ibz_finalize(&adjusted_norm);
quat_alg_elem_finalize(&new_alpha);
ibz_mat_4x4_finalize(&gram);
ibz_mat_4x4_finalize(&red);
return found;
}

View File

@@ -0,0 +1,377 @@
#include "lll_internals.h"
#include "quaternion_tests.h"
#include <rng.h>
#include <bench.h>
#include <bench_test_arguments.h>
// norms must be either a vector of length iterations of norms or NULL
int
quat_bench_lll_benchmark_lll_core(int bitsize,
int iterations,
int warmup_loops, //(int)(100000 / (bitsize*bitsize)) + 1;
quat_lattice_t *lattices,
const ibz_t *norms,
const quat_alg_t *alg,
int add_tests)
{
int res = 0;
uint64_t start, end, time;
quat_lattice_t test;
ibz_t num, denom;
ibq_t eta, delta;
ibz_mat_4x4_t *reds;
ibz_mat_4x4_t gram;
ibz_init(&num);
ibz_init(&denom);
ibq_init(&eta);
ibq_init(&delta);
quat_lattice_init(&test);
ibz_mat_4x4_init(&gram);
quat_lll_set_ibq_parameters(&delta, &eta);
reds = (ibz_mat_4x4_t *)malloc(iterations * sizeof(ibz_mat_4x4_t));
for (int i = 0; i < iterations; i++)
ibz_mat_4x4_init(&(reds[i]));
// warmup setup
quat_lattice_t *wu;
ibz_mat_4x4_t *wur;
wu = (quat_lattice_t *)malloc(warmup_loops * sizeof(quat_lattice_t));
wur = (ibz_mat_4x4_t *)malloc(warmup_loops * sizeof(ibz_mat_4x4_t));
for (int i = 0; i < warmup_loops; i++) {
quat_lattice_init(&(wu[i]));
ibz_mat_4x4_init(&(wur[i]));
}
quat_test_input_random_lattice_generation(wu, bitsize, warmup_loops, 1);
// Case for unknown norms
if (norms == NULL) {
printf("Start warmup and measures for bitsize %d iterations %d with non-ideals\n", bitsize, iterations);
// warmup loop
for (int iter = 0; iter < warmup_loops; iter++) {
quat_lattice_gram(&gram, &(lattices[iter]), alg);
ibz_mat_4x4_copy(&(reds[iter]), &(lattices[iter].basis));
quat_lll_core(&gram, &(reds[iter]));
}
// benchmark loop
start = cpucycles();
for (int iter = 0; iter < iterations; iter++) {
quat_lattice_gram(&gram, &(lattices[iter]), alg);
ibz_mat_4x4_copy(&(reds[iter]), &(lattices[iter].basis));
quat_lll_core(&gram, &(reds[iter]));
}
end = cpucycles();
} else {
// Using division by norm
quat_lattice_t O0;
quat_left_ideal_t *ideals;
quat_lattice_init(&O0);
quat_lattice_O0_set(&O0);
ideals = (quat_left_ideal_t *)malloc(iterations * sizeof(quat_left_ideal_t));
for (int i = 0; i < iterations; i++) {
quat_left_ideal_init(&(ideals[i]));
ibz_mat_4x4_copy(&(ideals[i].lattice.basis), &(lattices[i].basis));
ibz_copy(&(ideals[i].lattice.denom), &(lattices[i].denom));
ibz_copy(&(ideals[i].norm), &(norms[i]));
(ideals[i].parent_order) = &O0;
}
printf("Start warmup and measures for bitsize %d iterations %d with ideals\n", bitsize, iterations);
// warmup loop
for (int iter = 0; iter < warmup_loops; iter++) {
quat_lideal_class_gram(&gram, &(ideals[iter % iterations]), alg);
ibz_mat_4x4_copy(&(reds[iter % iterations]), &(ideals[iter % iterations].lattice.basis));
quat_lll_core(&gram, &(reds[iter % iterations]));
}
// benchmark loop
start = cpucycles();
for (int iter = 0; iter < iterations; iter++) {
quat_lideal_class_gram(&gram, &(ideals[iter]), alg);
ibz_mat_4x4_copy(&(reds[iter]), &(ideals[iter].lattice.basis));
quat_lll_core(&gram, &(reds[iter]));
}
end = cpucycles();
for (int i = 0; i < iterations; i++)
quat_left_ideal_finalize(&(ideals[i]));
free(ideals);
quat_lattice_finalize(&O0);
}
// results output
time = (end - start);
printf("%" PRIu64 " cycles per lattice\n%d Lattices %" PRIu64 " cycles total\n",
(uint64_t)(time / iterations),
iterations,
time);
// test loop
if (add_tests) {
for (int iter = 0; iter < iterations; iter++) {
// test lll reduced
res = res || !quat_lll_verify(&(reds[iter]), &delta, &eta, alg);
// test lattice equality
ibz_copy(&(test.denom), &((lattices[iter]).denom));
ibz_mat_4x4_copy(&(test.basis), &(reds[iter]));
quat_lattice_hnf(&test);
res = res || !quat_lattice_equal(&test, &(lattices[iter]));
}
if (res != 0) {
printf("Quaternion benchmark tests for lll_core failed\n");
}
}
printf("\n");
ibz_finalize(&num);
ibz_finalize(&denom);
ibq_finalize(&delta);
ibq_finalize(&eta);
quat_lattice_finalize(&test);
ibz_mat_4x4_finalize(&gram);
for (int i = 0; i < warmup_loops; i++) {
quat_lattice_finalize(&(wu[i]));
ibz_mat_4x4_finalize(&(wur[i]));
}
for (int i = 0; i < iterations; i++)
ibz_mat_4x4_finalize(&(reds[i]));
free(reds);
free(wu);
free(wur);
return (res);
}
// ideals determines if generated as ideals: 0 means ideals, 1 lattices without HNF, 2 and any other
// number lattices in HNF tests if tests are run
int
quat_bench_lll_one_level(const ibz_t *prime,
const int *norm_bitsizes,
int nb_bitsizes,
int iterations,
int ideals,
int tests)
{
// initializations
quat_alg_t alg;
quat_p_extremal_maximal_order_t order;
quat_lattice_t *lattices;
ibz_t *norms;
const ibz_t *used_norms;
quat_represent_integer_params_t params;
quat_alg_init_set(&alg, prime);
lattices = malloc(iterations * sizeof(quat_lattice_t));
norms = malloc(iterations * sizeof(ibz_t));
// initialize params:
quat_lattice_init(&(order.order));
quat_alg_elem_init(&(order.t));
quat_alg_elem_init(&(order.z));
quat_lattice_O0_set_extremal(&order);
params.algebra = &alg;
params.order = &order;
params.primality_test_iterations = 30;
int res = 0;
int randret = 0;
if (ideals == 0) {
used_norms = norms;
} else {
used_norms = NULL;
}
// run benchmarks
for (int i = 0; i < iterations; i++) {
quat_lattice_init(&(lattices[i]));
ibz_init(&(norms[i]));
}
for (int i = 0; i < nb_bitsizes; i++) {
int warmups = (int)(100000 / (norm_bitsizes[i] * norm_bitsizes[i])) + 1;
if (ideals == 0) {
randret = randret || quat_test_input_random_ideal_lattice_generation(
lattices, norms, norm_bitsizes[i], iterations, &params);
} else {
randret = randret || quat_test_input_random_lattice_generation(
lattices, norm_bitsizes[i], iterations, ideals - 1); // in HNF
}
if (!randret) {
res = res || quat_bench_lll_benchmark_lll_core(
norm_bitsizes[i], iterations, warmups, lattices, used_norms, &alg, tests);
}
}
quat_alg_finalize(&alg);
for (int i = 0; i < iterations; i++) {
ibz_finalize(&(norms[i]));
quat_lattice_finalize(&(lattices[i]));
}
quat_lattice_finalize(&(order.order));
quat_alg_elem_finalize(&(order.t));
quat_alg_elem_finalize(&(order.z));
free(norms);
free(lattices);
return (res + 2 * (randret));
}
// this function must be adapted if algorithms or parameters change
int
quat_bench_lll_level(int lvl, int iterations, int ideals, int tests)
{
if (!((lvl == 1) || (lvl == 3) || (lvl == 5))) {
printf("Invalid input level to quat_bench_lll_level: %d\n", lvl);
return (1);
}
ibz_t prime;
int norm_bitsizes[4] = { 0 };
int nb_bitsizes = 4;
ibz_init(&prime);
if (lvl == 5) {
ibz_set_from_str(&prime,
"1afffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffff",
16);
norm_bitsizes[0] = 254;
norm_bitsizes[1] = 254;
norm_bitsizes[2] = 747;
norm_bitsizes[3] = 1006;
}
if (lvl == 3) {
ibz_set_from_str(&prime,
"40fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffff",
16);
norm_bitsizes[0] = 193;
norm_bitsizes[1] = 193;
norm_bitsizes[2] = 571;
norm_bitsizes[3] = 761;
}
if (lvl == 1) {
ibz_set_from_str(&prime, "4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 16);
norm_bitsizes[0] = 127;
norm_bitsizes[1] = 127;
norm_bitsizes[2] = 377;
norm_bitsizes[3] = 501;
}
printf("Running benchmarks for lvl %d with bitsizes of [%d, %d, %d, %d] \n",
lvl,
norm_bitsizes[0],
norm_bitsizes[1],
norm_bitsizes[2],
norm_bitsizes[3]);
printf("Using prime ");
ibz_print(&prime, 10);
printf("\n");
printf("With %d iterations, ", iterations);
if (tests == 0)
printf("no ");
printf("tests, and lattices generated as ");
if (ideals == 0)
printf("ideals");
else {
printf("lattices");
if (ideals != 1)
printf("not in hnf");
}
printf("\n\n");
int res = quat_bench_lll_one_level(&prime, norm_bitsizes, nb_bitsizes, iterations, ideals, tests);
ibz_finalize(&prime);
return (res);
}
int
main(int argc, char *argv[])
{
uint32_t seed[12];
int iterations = SQISIGN_TEST_REPS;
int help = 0;
int seed_set = 0;
int tests = 0;
int ideals = 0;
int res = 0;
int level = 1;
int level_set = 0;
#ifndef NDEBUG
fprintf(stderr,
"\x1b[31mIt looks like SQIsign was compiled with assertions enabled.\n"
"This will severely impact performance measurements.\x1b[0m\n");
#endif
for (int i = 1; i < argc; i++) {
if (!tests && strcmp(argv[i], "--tests") == 0) {
tests = 1;
continue;
}
if (!seed_set && !parse_seed(argv[i], seed)) {
seed_set = 1;
continue;
}
if (level_set == 0 && sscanf(argv[i], "--level=%d", &level) == 1) {
level_set = 1;
continue;
}
if (!help && strcmp(argv[i], "--help") == 0) {
help = 1;
continue;
}
if (!help && strcmp(argv[i], "--not_ideals") == 0) {
ideals = 1;
continue;
}
if (sscanf(argv[i], "--iterations=%d", &iterations) == 1) {
continue;
}
}
if (help || (level != 1 && level != 3 && level != 5)) {
printf("Usage: %s [--level=<level>] [--iterations=<iterations>] [--tests] [--seed=<seed>]\n", argv[0]);
printf("Where <level> is either 1 or 3 or 5; if not passed, runs on all levels\n");
printf("Where <iterations> is the number of iterations used for benchmarking; if not "
"present, uses the default: %d)\n",
iterations);
printf("Where <seed> is the random seed to be used; if not present, a random seed is "
"generated\n");
printf("Additional verifications are run on each output if --tests is passed\n");
printf("If --not_ideals is passed, input lattices are not generated as random O0 ideals of "
"norm of a level-specific bitsize, but as random lattices with entries of the same "
"bitsize\n");
printf("Output has last bit set if tests failed, second-to-last if randomness failed\n");
return 1;
}
if (!seed_set) {
randombytes_select((unsigned char *)seed, sizeof(seed));
}
print_seed(seed);
#if defined(TARGET_BIG_ENDIAN)
for (int i = 0; i < 12; i++) {
seed[i] = BSWAP32(seed[i]);
}
#endif
randombytes_init((unsigned char *)seed, NULL, 256);
cpucycles_init();
if (level_set != 0) {
res = quat_bench_lll_level(level, iterations, ideals, tests);
} else {
for (level = 1; level <= 5; level += 2) {
res |= quat_bench_lll_level(level, iterations, ideals, tests);
}
}
return (res);
}
// Notes on realistic benchmarks:
// Denominator is always 2
// Lattice numerators have following maximal bitsizes (when in HNF):
// lvl5: 254, 254, 755-757, ni 1006
// lvl3: 193, 193, 570-571, ni 760-761
// lvl1: 127, 127, 372-377, ni 500-501
// where only the "ni" ones are not calls through the ideal reduction function.
// The largest coeff of all ideals above is about 2 times its norm.
// The quat_bench_lll_level and main functions takes the above into account.
// In particular lattices are always generated as ideals of O0 in HNF.
// Measures obtained by prints in the L2 function and its applications

View File

@@ -0,0 +1,794 @@
#include "lll_internals.h"
#include "quaternion_tests.h"
#include <rng.h>
int
quat_test_lll_ibq_consts(void)
{
int ret = 0;
ibq_t t;
ibz_t tmp1, tmp2, tmp3;
ibz_init(&tmp1);
ibz_init(&tmp2);
ibz_init(&tmp3);
ibq_init(&t);
ibz_set(&tmp1, 123);
ibz_set(&tmp2, -123);
if (!ibq_set(&t, &tmp1, &tmp2)) {
ret = -1;
goto err;
}
if (ibq_is_one(&t)) {
ret = -1;
goto err;
}
if (!ibq_is_ibz(&t)) {
ret = -1;
goto err;
}
if (!ibq_to_ibz(&tmp3, &t)) {
ret = -1;
goto err;
}
if (ibz_is_one(&tmp3)) {
ret = -1;
goto err;
}
ibz_set(&tmp2, 123);
if (!ibq_set(&t, &tmp1, &tmp2)) {
ret = -1;
goto err;
}
if (!ibq_is_one(&t)) {
ret = -1;
goto err;
}
if (!ibq_is_ibz(&t)) {
ret = -1;
goto err;
}
if (!ibq_to_ibz(&tmp3, &t)) {
ret = -1;
goto err;
}
if (!ibz_is_one(&tmp3)) {
ret = -1;
goto err;
}
ibz_set(&tmp1, 0);
ibq_set(&t, &tmp1, &tmp2);
if (!ibq_is_zero(&t)) {
ret = -1;
goto err;
}
if (!ibq_is_ibz(&t)) {
ret = -1;
goto err;
}
if (!ibq_to_ibz(&tmp3, &t)) {
ret = -1;
goto err;
}
if (!ibz_is_zero(&tmp3)) {
ret = -1;
goto err;
}
err:
ibq_finalize(&t);
ibz_finalize(&tmp1);
ibz_finalize(&tmp2);
ibz_finalize(&tmp3);
return ret;
}
// test for lll verification
// void ibq_vec_4_copy_ibz(ibq_vec_4_t *vec, const ibz_t *coeff0, const ibz_t *coeff1,const ibz_t
// *coeff2,const ibz_t *coeff3);
int
quat_test_lll_ibq_vec_4_copy_ibz(void)
{
int res = 0;
ibq_vec_4_t vec;
ibz_vec_4_t vec_z;
ibz_vec_4_init(&vec_z);
ibq_vec_4_init(&vec);
ibz_vec_4_set(&vec_z, 2, 3, 4, 5);
ibq_vec_4_copy_ibz(&vec, &(vec_z[0]), &(vec_z[1]), &(vec_z[2]), &(vec_z[3]));
for (int i = 0; i < 4; i++) {
ibq_to_ibz(&(vec_z[i]), &(vec[i]));
res = res || (ibz_cmp_int32(&(vec_z[i]), i + 2) != 0);
}
if (res != 0) {
printf("Quaternion unit test lll_ibq_vec_4_copy_ibz failed\n");
}
ibz_vec_4_finalize(&vec_z);
ibq_vec_4_finalize(&vec);
return (res);
}
// void quat_lll_bilinear(ibq_t *b, const ibq_vec_4_t *vec0, const ibq_vec_4_t *vec1, const
// ibz_t *q);
int
quat_test_lll_bilinear(void)
{
int res = 0;
ibz_vec_4_t init_helper;
ibq_vec_4_t vec0, vec1;
ibz_t q;
ibq_t cmp, b;
ibz_vec_4_init(&init_helper);
ibq_init(&cmp);
ibq_init(&b);
ibz_init(&q);
ibq_vec_4_init(&vec0);
ibq_vec_4_init(&vec1);
ibz_vec_4_set(&init_helper, 1, 2, 3, 4);
ibq_vec_4_copy_ibz(&vec0, &(init_helper[0]), &(init_helper[1]), &(init_helper[2]), &(init_helper[3]));
ibz_vec_4_set(&init_helper, 9, -8, 7, -6);
ibq_vec_4_copy_ibz(&vec1, &(init_helper[0]), &(init_helper[1]), &(init_helper[2]), &(init_helper[3]));
for (int i = 0; i < 4; i++) {
ibq_inv(&(vec0[i]), &(vec0[i]));
}
ibz_set(&q, 3);
ibz_vec_4_set(&init_helper, 15, 2, 0, 0);
ibq_set(&cmp, &(init_helper[0]), &(init_helper[1]));
quat_lll_bilinear(&b, &vec0, &vec1, &q);
res = res || (ibq_cmp(&b, &cmp));
if (res != 0) {
printf("Quaternion unit test quat_lll_bilinear failed\n");
}
ibq_finalize(&cmp);
ibq_finalize(&b);
ibz_finalize(&q);
ibz_vec_4_finalize(&init_helper);
ibq_vec_4_finalize(&vec0);
ibq_vec_4_finalize(&vec1);
return (res);
}
// void quat_lll_gram_schmidt_transposed_with_ibq(ibq_mat_4x4_t *orthogonalised_transposed, const
// ibz_mat_4x4_t *mat, const ibz_t *q);
int
quat_test_lll_gram_schmidt_transposed_with_ibq(void)
{
int res = 0;
int zero;
ibq_mat_4x4_t ot, cmp;
ibz_mat_4x4_t mat;
ibz_t q, num, denom;
ibq_t b;
ibz_init(&q);
ibz_init(&num);
ibz_init(&denom);
ibq_init(&b);
ibz_mat_4x4_init(&mat);
ibq_mat_4x4_init(&ot);
ibq_mat_4x4_init(&cmp);
ibz_mat_4x4_zero(&mat);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_set(&(mat[i][j]), i * i + (j + 5) * j - 2 + (i == j));
}
}
ibz_set(&q, 3);
quat_lll_gram_schmidt_transposed_with_ibq(&ot, &mat, &q);
// test orthogonality
for (int i = 0; i < 4; i++) {
for (int j = i + 1; j < 4; j++) {
quat_lll_bilinear(&b, &(ot[i]), &(ot[j]), &q);
res = res || !ibq_is_zero(&b);
}
}
// test first vector is identical to mat
for (int i = 0; i < 4; i++) {
ibq_to_ibz(&q, &(ot[0][i]));
res = res || ibz_cmp(&q, &(mat[i][0]));
}
// test no zero vector
for (int i = 0; i < 4; i++) {
zero = 1;
for (int j = 0; j < 4; j++) {
zero = zero && ibq_is_zero(&(ot[i][j]));
}
res = res || zero;
}
ibz_set(&(mat[0][0]), 1);
ibz_set(&(mat[0][1]), 0);
ibz_set(&(mat[0][2]), 1);
ibz_set(&(mat[0][3]), 0);
ibz_set(&(mat[1][0]), 0);
ibz_set(&(mat[1][1]), 1);
ibz_set(&(mat[1][2]), 0);
ibz_set(&(mat[1][3]), 1);
ibz_set(&(mat[2][0]), 1);
ibz_set(&(mat[2][1]), 0);
ibz_set(&(mat[2][2]), 2);
ibz_set(&(mat[2][3]), 0);
ibz_set(&(mat[3][0]), 0);
ibz_set(&(mat[3][1]), 1);
ibz_set(&(mat[3][2]), 0);
ibz_set(&(mat[3][3]), 2);
ibz_set(&denom, 1);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibq_set(&(cmp[i][j]), &(mat[j][i]), &denom);
}
}
ibz_set(&denom, 3);
ibz_set(&num, -2);
ibq_set(&(cmp[2][0]), &num, &denom);
ibq_set(&(cmp[3][1]), &num, &denom);
ibz_set(&num, 1);
ibq_set(&(cmp[2][2]), &num, &denom);
ibq_set(&(cmp[3][3]), &num, &denom);
ibz_set(&q, 2);
quat_lll_gram_schmidt_transposed_with_ibq(&ot, &mat, &q);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
res = res || ibq_cmp(&(cmp[i][j]), &(ot[i][j]));
}
}
ibz_set(&(mat[0][0]), 1);
ibz_set(&(mat[0][1]), 0);
ibz_set(&(mat[0][2]), 1);
ibz_set(&(mat[0][3]), 0);
ibz_set(&(mat[1][0]), 0);
ibz_set(&(mat[1][1]), 1);
ibz_set(&(mat[1][2]), 0);
ibz_set(&(mat[1][3]), 1);
ibz_set(&(mat[2][0]), 1);
ibz_set(&(mat[2][1]), 0);
ibz_set(&(mat[2][2]), 2);
ibz_set(&(mat[2][3]), 1);
ibz_set(&(mat[3][0]), 0);
ibz_set(&(mat[3][1]), 1);
ibz_set(&(mat[3][2]), 0);
ibz_set(&(mat[3][3]), 2);
ibz_set(&denom, 1);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibq_set(&(cmp[i][j]), &(mat[j][i]), &denom);
}
}
ibz_set(&denom, 3);
ibz_set(&num, -2);
ibq_set(&(cmp[2][0]), &num, &denom);
ibq_set(&(cmp[3][1]), &num, &denom);
ibz_set(&num, 1);
ibq_set(&(cmp[2][2]), &num, &denom);
ibq_set(&(cmp[3][3]), &num, &denom);
ibz_set(&num, 0);
ibq_set(&(cmp[3][0]), &num, &denom);
ibq_set(&(cmp[3][2]), &num, &denom);
ibz_set(&q, 2);
quat_lll_gram_schmidt_transposed_with_ibq(&ot, &mat, &q);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
res = res || ibq_cmp(&(cmp[i][j]), &(ot[i][j]));
}
}
if (res != 0) {
printf("Quaternion unit test dim4_gram_schmidt_transposed_with_ibq failed\n");
}
ibz_finalize(&q);
ibz_finalize(&num);
ibz_finalize(&denom);
ibq_finalize(&b);
ibz_mat_4x4_finalize(&mat);
ibq_mat_4x4_finalize(&ot);
ibq_mat_4x4_finalize(&cmp);
return (res);
}
// int quat_lll_verify(const ibz_mat_4x4_t *mat, const ibq_t *delta, const ibq_t *eta, const
// quat_alg_t *alg);
int
quat_test_lll_verify(void)
{
int res = 0;
ibz_mat_4x4_t mat;
ibz_t q, coeff_num, coeff_denom;
ibq_t eta, delta;
quat_alg_t alg;
ibz_mat_4x4_init(&mat);
ibz_init(&q);
ibq_init(&delta);
ibq_init(&eta);
ibz_init(&coeff_num);
ibz_init(&coeff_denom);
// reduced: non-1 norm
ibz_set(&q, 3);
quat_alg_init_set(&alg, &q);
ibq_set(&eta, &ibz_const_one, &ibz_const_two);
ibq_set(&delta, &ibz_const_three, &ibz_const_two);
ibq_mul(&delta, &delta, &eta);
ibz_set(&(mat[0][0]), 0);
ibz_set(&(mat[0][1]), 2);
ibz_set(&(mat[0][2]), 3);
ibz_set(&(mat[0][3]), -14);
ibz_set(&(mat[1][0]), 2);
ibz_set(&(mat[1][1]), -1);
ibz_set(&(mat[1][2]), -4);
ibz_set(&(mat[1][3]), -8);
ibz_set(&(mat[2][0]), 1);
ibz_set(&(mat[2][1]), -2);
ibz_set(&(mat[2][2]), 1);
ibz_set(&(mat[2][3]), 0);
ibz_set(&(mat[3][0]), 1);
ibz_set(&(mat[3][1]), 1);
ibz_set(&(mat[3][2]), 0);
ibz_set(&(mat[3][3]), 7);
res = res || !quat_lll_verify(&mat, &delta, &eta, &alg);
quat_alg_finalize(&alg);
// reduced: non-1 norm
ibz_set(&q, 103);
quat_alg_init_set(&alg, &q);
ibz_set(&coeff_num, 99);
ibz_set(&coeff_denom, 100);
ibq_set(&delta, &coeff_num, &coeff_denom);
ibz_set(&(mat[0][0]), 3);
ibz_set(&(mat[0][1]), 0);
ibz_set(&(mat[0][2]), 90);
ibz_set(&(mat[0][3]), -86);
ibz_set(&(mat[1][0]), 11);
ibz_set(&(mat[1][1]), 15);
ibz_set(&(mat[1][2]), 12);
ibz_set(&(mat[1][3]), 50);
ibz_set(&(mat[2][0]), 1);
ibz_set(&(mat[2][1]), -2);
ibz_set(&(mat[2][2]), 0);
ibz_set(&(mat[2][3]), 3);
ibz_set(&(mat[3][0]), -1);
ibz_set(&(mat[3][1]), 0);
ibz_set(&(mat[3][2]), 5);
ibz_set(&(mat[3][3]), 5);
res = res || !quat_lll_verify(&mat, &delta, &eta, &alg);
quat_alg_finalize(&alg);
if (res != 0) {
printf("Quaternion unit test quat_lll_verify failed\n");
}
ibz_finalize(&q);
ibq_finalize(&delta);
ibq_finalize(&eta);
ibz_finalize(&coeff_num);
ibz_finalize(&coeff_denom);
ibz_mat_4x4_finalize(&mat);
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_lll_lattice_lll(void)
{
int res = 0;
quat_lattice_t lat, test;
ibz_mat_4x4_t red;
ibz_t num, denom, q;
ibq_t eta, delta;
quat_alg_t alg;
ibz_init(&num);
ibz_init(&denom);
ibz_init(&q);
ibq_init(&delta);
ibq_init(&eta);
ibz_mat_4x4_init(&red);
quat_lattice_init(&lat);
quat_lattice_init(&test);
ibz_set(&q, 103);
quat_alg_init_set(&alg, &q);
// 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);
res = res || quat_lattice_lll(&red, &lat, &alg);
// test lll reduced
quat_lll_set_ibq_parameters(&delta, &eta);
res = res || !quat_lll_verify(&red, &delta, &eta, &alg);
// 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 lll_lattice_lll failed\n");
}
ibz_finalize(&num);
ibz_finalize(&denom);
ibz_finalize(&q);
ibq_finalize(&eta);
ibq_finalize(&delta);
ibz_mat_4x4_finalize(&red);
quat_lattice_finalize(&lat);
quat_lattice_finalize(&test);
quat_alg_finalize(&alg);
return (res);
}
// int quat_lattice_lll(ibz_mat_4x4_t *red, const quat_lattice_t *lattice, const quat_alg_t *alg);
int
quat_test_lll_randomized_lattice_lll(void)
{
int res = 0;
quat_lattice_t lat, test;
ibz_mat_4x4_t red;
ibz_t q, det;
ibq_t delta, eta;
int32_t rand[4][4];
int32_t rand_denom;
uint32_t rand_q;
ibz_init(&q);
ibz_init(&det);
ibq_init(&eta);
ibq_init(&delta);
ibz_mat_4x4_init(&red);
quat_lattice_init(&lat);
quat_lattice_init(&test);
quat_lll_set_ibq_parameters(&delta, &eta);
for (int iter = 0; iter < 20; iter++) {
quat_alg_t alg;
rand_denom = 0;
while (rand_denom <= 0) {
int randret = randombytes((unsigned char *)&rand_denom, sizeof(int32_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)) {
randret = randombytes((unsigned char *)rand, sizeof(rand));
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 algebra
ibz_set(&q, 1 + (rand_q % 1023));
quat_alg_init_set(&alg, &q);
// reduce
res = res || quat_lattice_lll(&red, &lat, &alg);
// test lll reduced
res = res || !quat_lll_verify(&red, &delta, &eta, &alg);
// 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);
quat_alg_finalize(&alg);
}
if (res != 0) {
printf("Quaternion unit test of lll with randomization for lattice_lll failed\n");
}
ibz_finalize(&q);
ibz_finalize(&det);
ibq_finalize(&delta);
ibq_finalize(&eta);
ibz_mat_4x4_finalize(&red);
quat_lattice_finalize(&lat);
quat_lattice_finalize(&test);
return (res);
}
// int 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);
int
quat_test_lideal_reduce_basis()
{
int res = 0;
ibz_mat_4x4_t red, gram, prod, gram_norm;
ibz_vec_4_t vec;
quat_left_ideal_t lideal;
quat_lattice_t test;
quat_alg_t alg;
quat_alg_elem_t init_helper;
quat_lattice_t order;
ibq_t delta, eta;
ibz_t num, denom, norm, test_norm;
ibz_init(&num);
ibz_init(&denom);
ibz_init(&norm);
ibz_init(&test_norm);
ibq_init(&delta);
ibq_init(&eta);
ibz_vec_4_init(&vec);
ibz_mat_4x4_init(&prod);
ibz_mat_4x4_init(&gram_norm);
quat_lattice_init(&test);
quat_alg_elem_init(&init_helper);
quat_lattice_init(&order);
quat_alg_init_set_ui(&alg, 19);
ibz_mat_4x4_init(&gram);
ibz_mat_4x4_init(&red);
quat_left_ideal_init(&lideal);
quat_lattice_O0_set(&order);
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);
quat_lll_set_ibq_parameters(&delta, &eta);
res = res || !quat_lll_verify(&red, &delta, &eta, &alg);
// 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_identity(&gram_norm);
ibz_copy(&(gram_norm[2][2]), &(alg.p));
ibz_copy(&(gram_norm[3][3]), &(alg.p));
ibz_mat_4x4_transpose(&prod, &red);
ibz_mat_4x4_mul(&prod, &prod, &gram_norm);
ibz_mat_4x4_mul(&prod, &prod, &red);
for (int i = 0; i < 4; i++) {
ibz_vec_4_set(&vec, (i == 0), (i == 1), (i == 2), (i == 3));
quat_qf_eval(&norm, &gram, &vec);
quat_qf_eval(&test_norm, &prod, &vec);
ibz_mul(&norm, &(lideal.norm), &norm);
res = res || !(ibz_cmp(&norm, &test_norm) == 0);
}
if (res != 0) {
printf("Quaternion unit test lideal_reduce_basis failed\n");
}
ibz_finalize(&num);
ibz_finalize(&denom);
ibz_finalize(&norm);
ibz_finalize(&test_norm);
ibq_finalize(&delta);
ibq_finalize(&eta);
quat_lattice_finalize(&test);
quat_alg_elem_finalize(&init_helper);
ibz_mat_4x4_finalize(&prod);
quat_lattice_finalize(&order);
quat_alg_finalize(&alg);
ibz_vec_4_finalize(&vec);
ibz_mat_4x4_finalize(&gram);
ibz_mat_4x4_finalize(&red);
ibz_mat_4x4_finalize(&gram_norm);
quat_left_ideal_finalize(&lideal);
return (res);
}
// int quat_lideal_lideal_mul_reduced(quat_left_ideal_t *prod, ibz_mat_4x4_t *gram, const
// quat_left_ideal_t *lideal1,const quat_left_ideal_t *lideal2, const quat_alg_t *alg);
int
quat_test_lll_lideal_lideal_mul_reduced()
{
int res = 0;
ibz_t n, norm, test_norm;
ibq_t delta, eta;
ibz_vec_4_t vec;
quat_alg_t alg;
quat_alg_elem_t gen;
quat_lattice_t order, ro;
quat_left_ideal_t lideal1, lideal2, prod, i1, i2;
ibz_mat_4x4_t gram;
ibz_mat_4x4_t gram_test;
ibz_init(&n);
ibz_init(&norm);
ibz_init(&test_norm);
ibq_init(&delta);
ibq_init(&eta);
ibz_vec_4_init(&vec);
quat_alg_elem_init(&gen);
quat_left_ideal_init(&lideal1);
quat_left_ideal_init(&lideal2);
quat_left_ideal_init(&i1);
quat_left_ideal_init(&i2);
quat_left_ideal_init(&prod);
quat_lattice_init(&order);
quat_lattice_init(&ro);
ibz_mat_4x4_init(&gram);
ibz_mat_4x4_init(&gram_test);
quat_alg_init_set_ui(&alg, 103);
quat_lattice_O0_set(&order);
quat_lll_set_ibq_parameters(&delta, &eta);
ibz_set(&n, 113);
quat_alg_elem_set(&gen, 1, 10, 0, 1, 3);
quat_lideal_create(&lideal1, &gen, &n, &order, &alg);
ibz_set(&n, 89);
quat_alg_elem_set(&gen, 2, 2, 5, 1, 4);
quat_lideal_create(&lideal2, &gen, &n, &order, &alg);
quat_lideal_copy(&i1, &lideal1);
quat_lideal_copy(&i2, &lideal2);
quat_lideal_lideal_mul_reduced(&prod, &gram, &lideal1, &lideal2, &alg);
res = res || !quat_lll_verify(&(prod.lattice.basis), &delta, &eta, &alg);
ibz_mat_4x4_identity(&(gram_test));
ibz_copy(&(gram_test[2][2]), &(alg.p));
ibz_copy(&(gram_test[3][3]), &(alg.p));
ibz_mat_4x4_mul(&(gram_test), &(gram_test), &(prod.lattice.basis));
ibz_mat_4x4_transpose(&(gram_test), &(gram_test));
ibz_mat_4x4_mul(&(gram_test), &(gram_test), &(prod.lattice.basis));
for (int i = 0; i < 4; i++) {
ibz_vec_4_set(&vec, (i == 0), (i == 1), (i == 2), (i == 3));
quat_qf_eval(&norm, &gram, &vec);
quat_qf_eval(&test_norm, &gram_test, &vec);
ibz_mul(&norm, &(prod.norm), &norm);
res = res || !(ibz_cmp(&norm, &test_norm) == 0);
}
quat_lattice_hnf(&(prod.lattice));
res = res || !quat_lideal_equals(&i1, &lideal1, &alg);
res = res || !quat_lideal_equals(&i2, &lideal2, &alg);
quat_lattice_mul(&i1.lattice, &i1.lattice, &i2.lattice, &alg);
res = res || !quat_lattice_equal(&i1.lattice, &prod.lattice);
res = res || !(prod.parent_order == lideal1.parent_order);
i1.parent_order = lideal1.parent_order;
quat_lideal_norm(&i1);
res = res || !quat_lideal_equals(&i1, &prod, &alg);
if (res != 0) {
printf("Quaternion unit test lideal_lideal_mul_reduced failed\n");
}
ibz_finalize(&n);
ibz_finalize(&norm);
ibz_finalize(&test_norm);
ibq_finalize(&delta);
ibq_finalize(&eta);
ibz_vec_4_finalize(&vec);
quat_alg_elem_finalize(&gen);
quat_left_ideal_finalize(&lideal1);
quat_left_ideal_finalize(&lideal2);
quat_left_ideal_finalize(&i1);
quat_left_ideal_finalize(&i2);
quat_left_ideal_finalize(&prod);
quat_lattice_finalize(&order);
quat_lattice_finalize(&ro);
quat_alg_finalize(&alg);
ibz_mat_4x4_finalize(&gram);
ibz_mat_4x4_finalize(&gram_test);
return (res);
}
// int quat_lideal_prime_norm_reduced_equivalent(quat_left_ideal_t *lideal, const quat_alg_t *alg,
// const int primality_num_iter, const int equiv_bound_coeff, const int equiv_num_iter);
int
quat_test_lll_lideal_prime_norm_reduced_equivalent()
{
int res = 0;
ibz_t n, d;
quat_alg_t alg;
quat_alg_elem_t gen;
ibz_mat_4x4_t red, gram;
quat_lattice_t order, ro, ro2;
quat_left_ideal_t lideal1, lideal2, i1;
ibz_init(&n);
ibz_init(&d);
quat_alg_elem_init(&gen);
quat_left_ideal_init(&lideal1);
quat_left_ideal_init(&lideal2);
quat_left_ideal_init(&i1);
quat_lattice_init(&order);
quat_lattice_init(&ro);
quat_lattice_init(&ro2);
ibz_mat_4x4_init(&red);
ibz_mat_4x4_init(&gram);
quat_alg_init_set_ui(&alg, 103);
quat_lattice_O0_set(&order);
ibz_set(&n, 113);
quat_alg_elem_set(&gen, 1, 10, 0, 1, 3);
quat_lideal_create(&lideal1, &gen, &n, &order, &alg);
quat_lideal_copy(&i1, &lideal1);
quat_lideal_right_order(&ro, &lideal1, &alg);
quat_lideal_prime_norm_reduced_equivalent(&lideal1, &alg, 20, 20);
// test norm correctness
quat_lattice_hnf(&(lideal1.lattice));
ibz_copy(&n, &(lideal1.norm));
quat_lideal_norm(&lideal1);
res = res || (0 != ibz_cmp(&n, &(lideal1.norm)));
// test norm primality
res = res || !ibz_probab_prime(&n, 20);
// test equivalence
quat_lideal_right_order(&ro2, &lideal1, &alg);
quat_lattice_mul(&(lideal2.lattice), &ro, &ro2, &alg);
ibz_set(&(lideal2.lattice.denom), 1);
lideal2.parent_order = &ro;
quat_lattice_hnf(&(lideal2.lattice));
quat_lideal_norm(&lideal2);
// now lideal2 is a connecting idea of ro and ro2
quat_lideal_reduce_basis(&red, &gram, &lideal2, &alg);
quat_alg_elem_copy_ibz(&gen, &(lideal2.lattice.denom), &(red[0][0]), &(red[1][0]), &(red[2][0]), &(red[3][0]));
quat_alg_norm(&n, &d, &gen, &alg);
assert(ibz_is_one(&d));
res = res || (0 != ibz_cmp(&n, &(lideal2.norm)));
if (res != 0) {
printf("Quaternion unit test lideal_prime_norm_reduced_equivalent failed\n");
}
ibz_finalize(&n);
ibz_finalize(&d);
ibz_mat_4x4_finalize(&red);
ibz_mat_4x4_finalize(&gram);
quat_alg_elem_finalize(&gen);
quat_left_ideal_finalize(&lideal1);
quat_left_ideal_finalize(&lideal2);
quat_left_ideal_finalize(&i1);
quat_lattice_finalize(&order);
quat_lattice_finalize(&ro);
quat_lattice_finalize(&ro2);
quat_alg_finalize(&alg);
return (res);
}
// run all previous tests
int
quat_test_lll(void)
{
int res = 0;
printf("\nRunning quaternion tests of lll and its subfunctions\n");
res = res | quat_test_lll_ibq_consts();
res = res | quat_test_lll_ibq_vec_4_copy_ibz();
res = res | quat_test_lll_bilinear();
res = res | quat_test_lll_gram_schmidt_transposed_with_ibq();
res = res | quat_test_lll_verify();
res = res | quat_test_lll_lattice_lll();
res = res | quat_test_lll_randomized_lattice_lll();
res = res | quat_test_lideal_reduce_basis();
res = res | quat_test_lll_lideal_lideal_mul_reduced();
res = res | quat_test_lll_lideal_prime_norm_reduced_equivalent();
return (res);
}

View File

@@ -0,0 +1,174 @@
#include "lll_internals.h"
// functions to verify lll
void
quat_lll_set_ibq_parameters(ibq_t *delta, ibq_t *eta)
{
ibz_t num, denom;
ibz_init(&num);
ibz_init(&denom);
ibq_set(delta, &ibz_const_one, &ibz_const_two);
ibz_set(&num, EPSILON_NUM);
ibz_set(&denom, EPSILON_DENOM);
ibq_set(eta, &num, &denom);
ibq_add(eta, eta, delta);
ibz_set(&num, DELTA_NUM);
ibz_set(&denom, DELTA_DENOM);
ibq_set(delta, &num, &denom);
ibz_finalize(&num);
ibz_finalize(&denom);
}
void
ibq_vec_4_copy_ibz(ibq_vec_4_t *vec, 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_lll_bilinear(ibq_t *b, const ibq_vec_4_t *vec0, const ibq_vec_4_t *vec1, 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_lll_gram_schmidt_transposed_with_ibq(ibq_mat_4x4_t *orthogonalised_transposed,
const ibz_mat_4x4_t *mat,
const ibz_t *q)
{
ibq_mat_4x4_t work;
ibq_vec_4_t vec;
ibq_t norm, b, coeff, prod;
ibq_init(&norm);
ibq_init(&coeff);
ibq_init(&prod);
ibq_init(&b);
ibq_mat_4x4_init(&work);
ibq_vec_4_init(&vec);
// 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_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_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]));
}
}
ibq_finalize(&norm);
ibq_finalize(&coeff);
ibq_finalize(&prod);
ibq_finalize(&b);
ibq_mat_4x4_finalize(&work);
ibq_vec_4_finalize(&vec);
}
int
quat_lll_verify(const ibz_mat_4x4_t *mat, const ibq_t *delta, const ibq_t *eta, const quat_alg_t *alg)
{
int res = 1;
ibq_mat_4x4_t orthogonalised_transposed;
ibq_vec_4_t tmp_vec;
ibq_t div, tmp, mu, two, norm, b;
ibz_t mu2_floored, num, denom;
ibq_mat_4x4_init(&orthogonalised_transposed);
ibq_vec_4_init(&tmp_vec);
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_lll_gram_schmidt_transposed_with_ibq(&orthogonalised_transposed, mat, &(alg->p));
// 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_lll_bilinear(&b, &(orthogonalised_transposed[j]), &tmp_vec, &(alg->p));
quat_lll_bilinear(&norm, &(orthogonalised_transposed[j]), &(orthogonalised_transposed[j]), &(alg->p));
ibq_inv(&tmp, &norm);
ibq_mul(&mu, &b, &tmp);
ibq_abs(&mu, &mu);
// compare to eta
res = res && (ibq_cmp(&mu, eta) <= 0);
}
}
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_lll_bilinear(&b, &(orthogonalised_transposed[i - 1]), &tmp_vec, &(alg->p));
quat_lll_bilinear(&norm, &(orthogonalised_transposed[i - 1]), &(orthogonalised_transposed[i - 1]), &(alg->p));
ibq_inv(&tmp, &norm);
ibq_mul(&mu, &b, &tmp);
// tmp is mu^2
ibq_mul(&tmp, &mu, &mu);
// mu is delta-mu^2
ibq_sub(&mu, delta, &tmp);
quat_lll_bilinear(&tmp, &(orthogonalised_transposed[i]), &(orthogonalised_transposed[i]), &(alg->p));
// get (delta-mu^2)norm(i-1)
ibq_mul(&div, &norm, &mu);
res = res && (ibq_cmp(&tmp, &div) >= 0);
}
ibq_mat_4x4_finalize(&orthogonalised_transposed);
ibq_vec_4_finalize(&tmp_vec);
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);
}

View File

@@ -0,0 +1,233 @@
#include <stdio.h>
#include "internal.h"
#include "lll_internals.h"
void
ibq_init(ibq_t *x)
{
ibz_init(&((*x)[0]));
ibz_init(&((*x)[1]));
ibz_set(&((*x)[1]), 1);
}
void
ibq_finalize(ibq_t *x)
{
ibz_finalize(&((*x)[0]));
ibz_finalize(&((*x)[1]));
}
void
ibq_mat_4x4_init(ibq_mat_4x4_t *mat)
{
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibq_init(&(*mat)[i][j]);
}
}
}
void
ibq_mat_4x4_finalize(ibq_mat_4x4_t *mat)
{
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibq_finalize(&(*mat)[i][j]);
}
}
}
void
ibq_vec_4_init(ibq_vec_4_t *vec)
{
for (int i = 0; i < 4; i++) {
ibq_init(&(*vec)[i]);
}
}
void
ibq_vec_4_finalize(ibq_vec_4_t *vec)
{
for (int i = 0; i < 4; i++) {
ibq_finalize(&(*vec)[i]);
}
}
void
ibq_mat_4x4_print(const ibq_mat_4x4_t *mat)
{
printf("matrix: ");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_print(&((*mat)[i][j][0]), 10);
printf("/");
ibz_print(&((*mat)[i][j][1]), 10);
printf(" ");
}
printf("\n ");
}
printf("\n");
}
void
ibq_vec_4_print(const ibq_vec_4_t *vec)
{
printf("vector: ");
for (int i = 0; i < 4; i++) {
ibz_print(&((*vec)[i][0]), 10);
printf("/");
ibz_print(&((*vec)[i][1]), 10);
printf(" ");
}
printf("\n\n");
}
void
ibq_reduce(ibq_t *x)
{
ibz_t gcd, r;
ibz_init(&gcd);
ibz_init(&r);
ibz_gcd(&gcd, &((*x)[0]), &((*x)[1]));
ibz_div(&((*x)[0]), &r, &((*x)[0]), &gcd);
assert(ibz_is_zero(&r));
ibz_div(&((*x)[1]), &r, &((*x)[1]), &gcd);
assert(ibz_is_zero(&r));
ibz_finalize(&gcd);
ibz_finalize(&r);
}
void
ibq_add(ibq_t *sum, const ibq_t *a, const ibq_t *b)
{
ibz_t add, prod;
ibz_init(&add);
ibz_init(&prod);
ibz_mul(&add, &((*a)[0]), &((*b)[1]));
ibz_mul(&prod, &((*b)[0]), &((*a)[1]));
ibz_add(&((*sum)[0]), &add, &prod);
ibz_mul(&((*sum)[1]), &((*a)[1]), &((*b)[1]));
ibz_finalize(&add);
ibz_finalize(&prod);
}
void
ibq_neg(ibq_t *neg, const ibq_t *x)
{
ibz_copy(&((*neg)[1]), &((*x)[1]));
ibz_neg(&((*neg)[0]), &((*x)[0]));
}
void
ibq_sub(ibq_t *diff, const ibq_t *a, const ibq_t *b)
{
ibq_t neg;
ibq_init(&neg);
ibq_neg(&neg, b);
ibq_add(diff, a, &neg);
ibq_finalize(&neg);
}
void
ibq_abs(ibq_t *abs, const ibq_t *x) // once
{
ibq_t neg;
ibq_init(&neg);
ibq_neg(&neg, x);
if (ibq_cmp(x, &neg) < 0)
ibq_copy(abs, &neg);
else
ibq_copy(abs, x);
ibq_finalize(&neg);
}
void
ibq_mul(ibq_t *prod, const ibq_t *a, const ibq_t *b)
{
ibz_mul(&((*prod)[0]), &((*a)[0]), &((*b)[0]));
ibz_mul(&((*prod)[1]), &((*a)[1]), &((*b)[1]));
}
int
ibq_inv(ibq_t *inv, const ibq_t *x)
{
int res = !ibq_is_zero(x);
if (res) {
ibz_copy(&((*inv)[0]), &((*x)[0]));
ibz_copy(&((*inv)[1]), &((*x)[1]));
ibz_swap(&((*inv)[1]), &((*inv)[0]));
}
return (res);
}
int
ibq_cmp(const ibq_t *a, const ibq_t *b)
{
ibz_t x, y;
ibz_init(&x);
ibz_init(&y);
ibz_copy(&x, &((*a)[0]));
ibz_copy(&y, &((*b)[0]));
ibz_mul(&y, &y, &((*a)[1]));
ibz_mul(&x, &x, &((*b)[1]));
if (ibz_cmp(&((*a)[1]), &ibz_const_zero) > 0) {
ibz_neg(&y, &y);
ibz_neg(&x, &x);
}
if (ibz_cmp(&((*b)[1]), &ibz_const_zero) > 0) {
ibz_neg(&y, &y);
ibz_neg(&x, &x);
}
int res = ibz_cmp(&x, &y);
ibz_finalize(&x);
ibz_finalize(&y);
return (res);
}
int
ibq_is_zero(const ibq_t *x)
{
return ibz_is_zero(&((*x)[0]));
}
int
ibq_is_one(const ibq_t *x)
{
return (0 == ibz_cmp(&((*x)[0]), &((*x)[1])));
}
int
ibq_set(ibq_t *q, const ibz_t *a, const ibz_t *b)
{
ibz_copy(&((*q)[0]), a);
ibz_copy(&((*q)[1]), b);
return !ibz_is_zero(b);
}
void
ibq_copy(ibq_t *target, const ibq_t *value) // once
{
ibz_copy(&((*target)[0]), &((*value)[0]));
ibz_copy(&((*target)[1]), &((*value)[1]));
}
int
ibq_is_ibz(const ibq_t *q)
{
ibz_t r;
ibz_init(&r);
ibz_mod(&r, &((*q)[0]), &((*q)[1]));
int res = ibz_is_zero(&r);
ibz_finalize(&r);
return (res);
}
int
ibq_to_ibz(ibz_t *z, const ibq_t *q)
{
ibz_t r;
ibz_init(&r);
ibz_div(z, &r, &((*q)[0]), &((*q)[1]));
int res = ibz_is_zero(&r);
ibz_finalize(&r);
return (res);
}

View File

@@ -1,421 +0,0 @@
#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,369 @@
#include <quaternion.h>
#include "internal.h"
/** @file
*
* @authors Antonin Leroux
*
* @brief Functions related to norm equation solving or special extremal orders
*/
void
quat_lattice_O0_set(quat_lattice_t *O0)
{
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_set(&(O0->basis[i][j]), 0);
}
}
ibz_set(&(O0->denom), 2);
ibz_set(&(O0->basis[0][0]), 2);
ibz_set(&(O0->basis[1][1]), 2);
ibz_set(&(O0->basis[2][2]), 1);
ibz_set(&(O0->basis[1][2]), 1);
ibz_set(&(O0->basis[3][3]), 1);
ibz_set(&(O0->basis[0][3]), 1);
}
void
quat_lattice_O0_set_extremal(quat_p_extremal_maximal_order_t *O0)
{
ibz_set(&O0->z.coord[1], 1);
ibz_set(&O0->t.coord[2], 1);
ibz_set(&O0->z.denom, 1);
ibz_set(&O0->t.denom, 1);
O0->q = 1;
quat_lattice_O0_set(&(O0->order));
}
void
quat_order_elem_create(quat_alg_elem_t *elem,
const quat_p_extremal_maximal_order_t *order,
const ibz_vec_4_t *coeffs,
const quat_alg_t *Bpoo)
{
// var dec
quat_alg_elem_t quat_temp;
// var init
quat_alg_elem_init(&quat_temp);
// elem = x
quat_alg_scalar(elem, &(*coeffs)[0], &ibz_const_one);
// quat_temp = i*y
quat_alg_scalar(&quat_temp, &((*coeffs)[1]), &ibz_const_one);
quat_alg_mul(&quat_temp, &order->z, &quat_temp, Bpoo);
// elem = x + i*y
quat_alg_add(elem, elem, &quat_temp);
// quat_temp = z * j
quat_alg_scalar(&quat_temp, &(*coeffs)[2], &ibz_const_one);
quat_alg_mul(&quat_temp, &order->t, &quat_temp, Bpoo);
// elem = x + i* + z*j
quat_alg_add(elem, elem, &quat_temp);
// quat_temp = t * j * i
quat_alg_scalar(&quat_temp, &(*coeffs)[3], &ibz_const_one);
quat_alg_mul(&quat_temp, &order->t, &quat_temp, Bpoo);
quat_alg_mul(&quat_temp, &quat_temp, &order->z, Bpoo);
// elem = x + i*y + j*z + j*i*t
quat_alg_add(elem, elem, &quat_temp);
quat_alg_elem_finalize(&quat_temp);
}
int
quat_represent_integer(quat_alg_elem_t *gamma,
const ibz_t *n_gamma,
int non_diag,
const quat_represent_integer_params_t *params)
{
if (ibz_is_even(n_gamma)) {
return 0;
}
// var dec
int found;
ibz_t cornacchia_target;
ibz_t adjusted_n_gamma, q;
ibz_t bound, sq_bound, temp;
ibz_t test;
ibz_vec_4_t coeffs; // coeffs = [x,y,z,t]
quat_alg_elem_t quat_temp;
if (non_diag)
assert(params->order->q % 4 == 1);
// var init
found = 0;
ibz_init(&bound);
ibz_init(&test);
ibz_init(&temp);
ibz_init(&q);
ibz_init(&sq_bound);
ibz_vec_4_init(&coeffs);
quat_alg_elem_init(&quat_temp);
ibz_init(&adjusted_n_gamma);
ibz_init(&cornacchia_target);
ibz_set(&q, params->order->q);
// this could be removed in the current state
int standard_order = (params->order->q == 1);
// adjusting the norm of gamma (multiplying by 4 to find a solution in an order of odd level)
if (non_diag || standard_order) {
ibz_mul(&adjusted_n_gamma, n_gamma, &ibz_const_two);
ibz_mul(&adjusted_n_gamma, &adjusted_n_gamma, &ibz_const_two);
} else {
ibz_copy(&adjusted_n_gamma, n_gamma);
}
// computation of the first bound = sqrt (adjust_n_gamma / p - q)
ibz_div(&sq_bound, &bound, &adjusted_n_gamma, &((params->algebra)->p));
ibz_set(&temp, params->order->q);
ibz_sub(&sq_bound, &sq_bound, &temp);
ibz_sqrt_floor(&bound, &sq_bound);
// the size of the search space is roughly n_gamma / (p√q)
ibz_t counter;
ibz_init(&counter);
ibz_mul(&temp, &temp, &((params->algebra)->p));
ibz_mul(&temp, &temp, &((params->algebra)->p));
ibz_sqrt_floor(&temp, &temp);
ibz_div(&counter, &temp, &adjusted_n_gamma, &temp);
// entering the main loop
while (!found && ibz_cmp(&counter, &ibz_const_zero) != 0) {
// decreasing the counter
ibz_sub(&counter, &counter, &ibz_const_one);
// we start by sampling the first coordinate
ibz_rand_interval(&coeffs[2], &ibz_const_one, &bound);
// then, we sample the second coordinate
// computing the second bound in temp as sqrt( (adjust_n_gamma - p*coeffs[2]²)/qp )
ibz_mul(&cornacchia_target, &coeffs[2], &coeffs[2]);
ibz_mul(&temp, &cornacchia_target, &(params->algebra->p));
ibz_sub(&temp, &adjusted_n_gamma, &temp);
ibz_mul(&sq_bound, &q, &(params->algebra->p));
ibz_div(&temp, &sq_bound, &temp, &sq_bound);
ibz_sqrt_floor(&temp, &temp);
if (ibz_cmp(&temp, &ibz_const_zero) == 0) {
continue;
}
// sampling the second value
ibz_rand_interval(&coeffs[3], &ibz_const_one, &temp);
// compute cornacchia_target = n_gamma - p * (z² + q*t²)
ibz_mul(&temp, &coeffs[3], &coeffs[3]);
ibz_mul(&temp, &q, &temp);
ibz_add(&cornacchia_target, &cornacchia_target, &temp);
ibz_mul(&cornacchia_target, &cornacchia_target, &((params->algebra)->p));
ibz_sub(&cornacchia_target, &adjusted_n_gamma, &cornacchia_target);
assert(ibz_cmp(&cornacchia_target, &ibz_const_zero) > 0);
// applying cornacchia
if (ibz_probab_prime(&cornacchia_target, params->primality_test_iterations))
found = ibz_cornacchia_prime(&(coeffs[0]), &(coeffs[1]), &q, &cornacchia_target);
else
found = 0;
if (found && non_diag && standard_order) {
// check that we can divide by two at least once
// the treatmeat depends if the basis contains (1+j)/2 or (1+k)/2
// we must have x = t mod 2 and y = z mod 2
// if q=1 we can simply swap x and y
if (ibz_is_odd(&coeffs[0]) != ibz_is_odd(&coeffs[3])) {
ibz_swap(&coeffs[1], &coeffs[0]);
}
// we further check that (x-t)/2 = 1 mod 2 and (y-z)/2 = 1 mod 2 to ensure that the
// resulting endomorphism will behave well for dim 2 computations
found = found && ((ibz_get(&coeffs[0]) - ibz_get(&coeffs[3])) % 4 == 2) &&
((ibz_get(&coeffs[1]) - ibz_get(&coeffs[2])) % 4 == 2);
}
if (found) {
#ifndef NDEBUG
ibz_set(&temp, (params->order->q));
ibz_mul(&temp, &temp, &(coeffs[1]));
ibz_mul(&temp, &temp, &(coeffs[1]));
ibz_mul(&test, &(coeffs[0]), &(coeffs[0]));
ibz_add(&temp, &temp, &test);
assert(0 == ibz_cmp(&temp, &cornacchia_target));
ibz_mul(&cornacchia_target, &(coeffs[3]), &(coeffs[3]));
ibz_mul(&cornacchia_target, &cornacchia_target, &(params->algebra->p));
ibz_mul(&temp, &(coeffs[1]), &(coeffs[1]));
ibz_add(&cornacchia_target, &cornacchia_target, &temp);
ibz_set(&temp, (params->order->q));
ibz_mul(&cornacchia_target, &cornacchia_target, &temp);
ibz_mul(&temp, &(coeffs[0]), &coeffs[0]);
ibz_add(&cornacchia_target, &cornacchia_target, &temp);
ibz_mul(&temp, &(coeffs[2]), &coeffs[2]);
ibz_mul(&temp, &temp, &(params->algebra->p));
ibz_add(&cornacchia_target, &cornacchia_target, &temp);
assert(0 == ibz_cmp(&cornacchia_target, &adjusted_n_gamma));
#endif
// translate x,y,z,t into the quaternion element gamma
quat_order_elem_create(gamma, (params->order), &coeffs, (params->algebra));
#ifndef NDEBUG
quat_alg_norm(&temp, &(coeffs[0]), gamma, (params->algebra));
assert(ibz_is_one(&(coeffs[0])));
assert(0 == ibz_cmp(&temp, &adjusted_n_gamma));
assert(quat_lattice_contains(NULL, &((params->order)->order), gamma));
#endif
// making gamma primitive
// coeffs contains the coefficients of primitivized gamma in the basis of order
quat_alg_make_primitive(&coeffs, &temp, gamma, &((params->order)->order));
if (non_diag || standard_order)
found = (ibz_cmp(&temp, &ibz_const_two) == 0);
else
found = (ibz_cmp(&temp, &ibz_const_one) == 0);
}
}
if (found) {
// new gamma
ibz_mat_4x4_eval(&coeffs, &(((params->order)->order).basis), &coeffs);
ibz_copy(&gamma->coord[0], &coeffs[0]);
ibz_copy(&gamma->coord[1], &coeffs[1]);
ibz_copy(&gamma->coord[2], &coeffs[2]);
ibz_copy(&gamma->coord[3], &coeffs[3]);
ibz_copy(&gamma->denom, &(((params->order)->order).denom));
}
// var finalize
ibz_finalize(&counter);
ibz_finalize(&bound);
ibz_finalize(&temp);
ibz_finalize(&sq_bound);
ibz_vec_4_finalize(&coeffs);
quat_alg_elem_finalize(&quat_temp);
ibz_finalize(&adjusted_n_gamma);
ibz_finalize(&cornacchia_target);
ibz_finalize(&q);
ibz_finalize(&test);
return found;
}
int
quat_sampling_random_ideal_O0_given_norm(quat_left_ideal_t *lideal,
const ibz_t *norm,
int is_prime,
const quat_represent_integer_params_t *params,
const ibz_t *prime_cofactor)
{
ibz_t n_temp, norm_d;
ibz_t disc;
quat_alg_elem_t gen, gen_rerand;
int found = 0;
ibz_init(&n_temp);
ibz_init(&norm_d);
ibz_init(&disc);
quat_alg_elem_init(&gen);
quat_alg_elem_init(&gen_rerand);
// when the norm is prime we can be quite efficient
// by avoiding to run represent integer
// the first step is to generate one ideal of the correct norm
if (is_prime) {
// we find a quaternion element of norm divisible by norm
while (!found) {
// generating a trace-zero element at random
ibz_set(&gen.coord[0], 0);
ibz_sub(&n_temp, norm, &ibz_const_one);
for (int i = 1; i < 4; i++)
ibz_rand_interval(&gen.coord[i], &ibz_const_zero, &n_temp);
// first, we compute the norm of the gen
quat_alg_norm(&n_temp, &norm_d, &gen, (params->algebra));
assert(ibz_is_one(&norm_d));
// and finally the negation mod norm
ibz_neg(&disc, &n_temp);
ibz_mod(&disc, &disc, norm);
// now we check that -n is a square mod norm
// and if the square root exists we compute it
found = ibz_sqrt_mod_p(&gen.coord[0], &disc, norm);
found = found && !quat_alg_elem_is_zero(&gen);
}
} else {
assert(prime_cofactor != NULL);
// if it is not prime or we don't know if it is prime, we may just use represent integer
// and use a precomputed prime as cofactor
assert(!ibz_is_zero(norm));
ibz_mul(&n_temp, prime_cofactor, norm);
found = quat_represent_integer(&gen, &n_temp, 0, params);
found = found && !quat_alg_elem_is_zero(&gen);
}
#ifndef NDEBUG
if (found) {
// first, we compute the norm of the gen
quat_alg_norm(&n_temp, &norm_d, &gen, (params->algebra));
assert(ibz_is_one(&norm_d));
ibz_mod(&n_temp, &n_temp, norm);
assert(ibz_cmp(&n_temp, &ibz_const_zero) == 0);
}
#endif
// now we just have to rerandomize the class of the ideal generated by gen
found = 0;
while (!found) {
for (int i = 0; i < 4; i++) {
ibz_rand_interval(&gen_rerand.coord[i], &ibz_const_one, norm);
}
quat_alg_norm(&n_temp, &norm_d, &gen_rerand, (params->algebra));
assert(ibz_is_one(&norm_d));
ibz_gcd(&disc, &n_temp, norm);
found = ibz_is_one(&disc);
found = found && !quat_alg_elem_is_zero(&gen_rerand);
}
quat_alg_mul(&gen, &gen, &gen_rerand, (params->algebra));
// in both cases, whether norm is prime or not prime,
// gen is not divisible by any integer factor of the target norm
// therefore the call below will yield an ideal of the correct norm
quat_lideal_create(lideal, &gen, norm, &((params->order)->order), (params->algebra));
assert(ibz_cmp(norm, &(lideal->norm)) == 0);
ibz_finalize(&n_temp);
quat_alg_elem_finalize(&gen);
quat_alg_elem_finalize(&gen_rerand);
ibz_finalize(&norm_d);
ibz_finalize(&disc);
return (found);
}
void
quat_change_to_O0_basis(ibz_vec_4_t *vec, const quat_alg_elem_t *el)
{
ibz_t tmp;
ibz_init(&tmp);
ibz_copy(&(*vec)[2], &el->coord[2]);
ibz_add(&(*vec)[2], &(*vec)[2], &(*vec)[2]); // double (not optimal if el->denom is even...)
ibz_copy(&(*vec)[3], &el->coord[3]); // double (not optimal if el->denom is even...)
ibz_add(&(*vec)[3], &(*vec)[3], &(*vec)[3]);
ibz_sub(&(*vec)[0], &el->coord[0], &el->coord[3]);
ibz_sub(&(*vec)[1], &el->coord[1], &el->coord[2]);
assert(ibz_divides(&(*vec)[0], &el->denom));
assert(ibz_divides(&(*vec)[1], &el->denom));
assert(ibz_divides(&(*vec)[2], &el->denom));
assert(ibz_divides(&(*vec)[3], &el->denom));
ibz_div(&(*vec)[0], &tmp, &(*vec)[0], &el->denom);
ibz_div(&(*vec)[1], &tmp, &(*vec)[1], &el->denom);
ibz_div(&(*vec)[2], &tmp, &(*vec)[2], &el->denom);
ibz_div(&(*vec)[3], &tmp, &(*vec)[3], &el->denom);
ibz_finalize(&tmp);
}

View File

@@ -1,158 +1,132 @@
#include <quaternion.h>
#include <stdio.h>
#include "internal.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]));
void
ibz_mat_2x2_print(const ibz_mat_2x2_t *mat)
{
printf("matrix: ");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
ibz_print(&((*mat)[i][j]), 10);
printf(" ");
}
ibz_printf("\n ");
printf("\n ");
}
ibz_printf("\n");
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]));
void
ibz_mat_4x4_print(const ibz_mat_4x4_t *mat)
{
printf("matrix: ");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_print(&((*mat)[i][j]), 10);
printf(" ");
}
ibz_printf("\n ");
printf("\n ");
}
ibz_printf("\n");
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]));
void
ibz_vec_2_print(const ibz_vec_2_t *vec)
{
printf("vector: ");
for (int i = 0; i < 2; i++) {
ibz_print(&((*vec)[i]), 10);
printf(" ");
}
printf("\n\n");
}
void
ibz_vec_4_print(const ibz_vec_4_t *vec)
{
printf("vector: ");
for (int i = 0; i < 4; i++) {
ibz_print(&((*vec)[i]), 10);
printf(" ");
}
printf("\n\n");
}
void
quat_lattice_print(const quat_lattice_t *lat)
{
printf("lattice\n");
printf("denominator: ");
ibz_print(&(lat->denom), 10);
printf("\n");
printf("basis: ");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_print(&((lat->basis)[i][j]), 10);
printf(" ");
}
ibz_printf("\n ");
printf("\n ");
}
ibz_printf("\n");
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]));
void
quat_alg_print(const quat_alg_t *alg)
{
printf("quaternion algebra ramified at ");
ibz_print(&(alg->p), 10);
printf(" and infinity\n\n");
}
void
quat_alg_elem_print(const quat_alg_elem_t *elem)
{
printf("denominator: ");
ibz_print(&(elem->denom), 10);
printf("\n");
printf("coordinates: ");
for (int i = 0; i < 4; i++) {
ibz_print(&((elem->coord)[i]), 10);
printf(" ");
}
printf("\n\n");
}
void
quat_left_ideal_print(const quat_left_ideal_t *lideal)
{
printf("left ideal\n");
printf("norm: ");
ibz_print(&(lideal->norm), 10);
printf("\n");
printf("denominator: ");
ibz_print(&(lideal->lattice.denom), 10);
printf("\n");
printf("basis: ");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_print(&((lideal->lattice.basis)[i][j]), 10);
printf(" ");
}
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]);
if (i != 3) {
printf("\n ");
} else {
printf("\n");
}
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]));
if ((lideal->parent_order) != NULL) {
printf("parent order denominator: ");
ibz_print(&(lideal->parent_order->denom), 10);
printf("\n");
printf("parent order basis: ");
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
ibz_print(&((lideal->parent_order->basis)[i][j]), 10);
printf(" ");
}
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 ");
printf("\n ");
}
} else {
ibz_printf("Parent order not given!\n");
printf("Parent order not given!\n");
}
ibz_printf("\n");
printf("\n");
}

View File

@@ -1,21 +1,40 @@
set(SOURCE_FILES_QUATERNION_GENERIC_REF_TESTS
set(SOURCE_FILES_QUATERNION_GENERIC_REF
intbig.c
algebra.c
ideal.c
dim4.c
dim2.c
integers.c
lattice.c
lat_ball.c
finit.c
matkermod.c
mini-gmp.c
normeq.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} )
if (NOT GMP_LIBRARY STREQUAL "MINI")
list(APPEND SOURCE_FILES_QUATERNION_GENERIC_REF ${PROJECT_SOURCE_DIR}/src/mini-gmp/mini-gmp-extra.c)
endif()
set(SOURCE_FILES_QUATERNION_GENERIC_REF_TESTS
${SOURCE_FILES_QUATERNION_GENERIC_REF}
../hnf/hnf_tests.c
../lll/lll_verification.c
../lll/lll_tests.c
)
add_executable(sqisign_test_quaternion ${SOURCE_FILES_QUATERNION_GENERIC_REF_TESTS} test_quaternions.c)
target_link_libraries(sqisign_test_quaternion ${LIB_QUATERNION} ${GMP} sqisign_common_sys)
target_include_directories(sqisign_test_quaternion PRIVATE ../internal_quaternion_headers ${INC_COMMON} ${INC_QUATERNION} ${INC_PUBLIC} ${PROJECT_SOURCE_DIR}/src/mini-gmp)
add_executable(sqisign_bm_quaternion ${SOURCE_FILES_QUATERNION_GENERIC_REF_TESTS} ../lll/lll_benchmarks.c)
target_link_libraries(sqisign_bm_quaternion ${LIB_QUATERNION} ${GMP} sqisign_common_test)
target_include_directories(sqisign_bm_quaternion PRIVATE ../internal_quaternion_headers ${INC_COMMON} ${INC_QUATERNION} ${INC_PUBLIC} ${PROJECT_SOURCE_DIR}/src/mini-gmp)
# 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()
set(BM_BINS ${BM_BINS} sqisign_bm_quaternion CACHE INTERNAL "List of benchmark executables")

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

@@ -2,276 +2,193 @@
// 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(){
// void quat_alg_init(quat_alg_t *alg);
// void quat_alg_finalize(quat_alg_t *alg);
int
quat_test_finit_alg(void)
{
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);
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){
res = res || ibz_cmp(&(alg.p), &p);
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(){
// 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(void)
{
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])));
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 || (ibz_cmp_int32(&(elem.coord[i]), i) != 0);
}
if (res != 0){
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;
// void ibz_vec_2_init(ibz_vec_2_t *vec);
// void ibz_vec_2_finalize(ibz_vec_2_t *vec);
int
quat_test_finit_ibz_vec_2(void)
{
ibz_vec_2_t vec;
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])));
ibz_vec_2_init(&vec);
for (int i = 0; i < 2; i++) {
ibz_set(&(vec[i]), i);
}
if (res != 0){
printf("Quaternion unit test finit_alg_coord failed\n");
for (int i = 0; i < 2; i++) {
res = res || (ibz_cmp_int32(&(vec[i]), i) != 0);
}
quat_alg_coord_finalize(&coord);
if (res != 0) {
printf("Quaternion unit test finit_ibz_vec_2 failed\n");
}
ibz_vec_2_finalize(&vec);
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(){
// 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(void)
{
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++) {
ibz_set(&(vec[i]), i);
}
for(int i = 0; i <4; i++){
res = res || (i!=ibz_get(&(vec[i])));
for (int i = 0; i < 4; i++) {
res = res || (ibz_cmp_int32(&(vec[i]), i) != 0);
}
if (res != 0){
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(){
// 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(void)
{
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++) {
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])));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
res = res || (ibz_cmp_int32(&(mat[i][j]), i + j) != 0);
}
}
if (res != 0){
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(){
// 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(void)
{
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++) {
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])));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
res = res || (ibz_cmp_int32(&(mat[i][j]), i + j) != 0);
}
}
if (res != 0){
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(){
// void quat_lattice_init(quat_lattice_t *lat);
// void quat_lattice_finalize(quat_lattice_t *lat);
int
quat_test_finit_lattice(void)
{
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);
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])));
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 || (ibz_cmp_int32(&(lat.basis[i][j]), i + j) != 0);
}
}
if (res != 0){
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(){
// 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(void)
{
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);
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);
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 = 1 - (1 == ibz_is_one(&(lideal.lattice.denom)));
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
res = res || (ibz_cmp_int32(&(lideal.lattice.basis[i][j]), i + j) != 0);
}
}
res = res || (5!=ibz_get(&(lideal.norm)));
if (res != 0){
res = res || (ibz_cmp_int32(&(lideal.norm), 5) != 0);
if (res != 0) {
printf("Quaternion unit test finit_alg_lideal failed\n");
}
quat_left_ideal_finalize(&lideal);
@@ -279,21 +196,18 @@ int quat_test_finit_lideal(){
}
// run all previous tests
int quat_test_finit(){
int
quat_test_finit(void)
{
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_2();
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);
return (res);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,977 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rng.h>
#include "intbig_internal.h"
// void ibz_init(ibz_t *x);
// void ibz_finalize(ibz_t *x);
// int ibz_cmp(const ibz_t *a, const ibz_t *b);
// int ibz_is_zero(const ibz_t *x);
// int ibz_is_one(const ibz_t *x);
// int ibz_cmp_int32(const ibz_t *x, int32_t y);
// int ibz_is_even(const ibz_t *x);
// int ibz_is_odd(const ibz_t *x);
// void ibz_set(ibz_t *i, int32_t x);
// void ibz_copy(ibz_t *target, const ibz_t *value);
// void ibz_swap(ibz_t *a, ibz_t *b);
// int32_t ibz_get(const ibz_t *i);
// int ibz_bitsize(const ibz_t *a);
int
ibz_test_init_set_cmp()
{
int res = 0;
ibz_t a, b, c;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
res = res | !ibz_is_zero(&a);
ibz_set(&a, 1);
res = res | !ibz_is_one(&a);
res = res | ibz_is_zero(&a);
res = res | (0 != ibz_cmp(&b, &c));
res = res | (0 == ibz_cmp(&a, &c));
res = res | ibz_is_even(&a);
res = res | !ibz_is_odd(&a);
res = res | !ibz_is_even(&b);
res = res | ibz_is_odd(&b);
ibz_copy(&b, &a);
res = res | !ibz_is_one(&a);
res = res | !ibz_is_one(&b);
res = res | (0 != ibz_cmp(&a, &b));
res = res | (0 == ibz_cmp(&c, &b));
ibz_swap(&b, &c);
res = res | (0 == ibz_cmp(&a, &b));
res = res | (0 == ibz_cmp(&c, &b));
res = res | (0 != ibz_cmp(&a, &c));
res = res | (ibz_bitsize(&a) != 1);
res = res | (ibz_get(&a) != 1);
res = res | (ibz_cmp_int32(&a, 1) != 0);
res = res | (ibz_bitsize(&b) > 1);
res = res | (ibz_get(&b) != 0);
res = res | (ibz_cmp_int32(&b, 0) != 0);
ibz_set(&a, -1);
res = res | !(ibz_cmp(&a, &b) < 0);
res = res | !(ibz_cmp(&b, &c) < 0);
res = res | !(ibz_cmp(&b, &a) > 0);
res = res | !(ibz_cmp(&c, &b) > 0);
ibz_copy(&b, &a);
res = res | (0 != ibz_cmp(&a, &b));
res = res | (0 == ibz_cmp(&c, &b));
ibz_swap(&b, &c);
res = res | (0 == ibz_cmp(&a, &b));
res = res | (0 != ibz_cmp(&c, &a));
ibz_set_from_str(&a, "-10000000000000000011111100000001", 10);
res = res | !(ibz_cmp(&a, &b) < 0);
res = res | !(ibz_cmp(&b, &a) > 0);
ibz_copy(&b, &a);
res = res | (0 != ibz_cmp(&a, &b));
res = res | (0 == ibz_cmp(&c, &b));
ibz_swap(&b, &c);
res = res | (0 == ibz_cmp(&a, &b));
res = res | (0 != ibz_cmp(&c, &a));
ibz_set_from_str(&a, "1aaaa00000000000000000123", 16);
res = res | !(ibz_cmp(&a, &c) > 0);
res = res | !(ibz_cmp(&c, &a) < 0);
res = res | (ibz_bitsize(&a) != 4 * 24 + 1);
res = res | (ibz_get(&a) != 16 * 18 + 3);
if (res) {
printf("Quaternion module integer group test ibz_test_init_set_cmp failed\n");
}
ibz_set_from_str(&a, "deadbeef12345678", 16);
res = res | (ibz_get(&a) != 0x12345678);
// Test INT32_MAX and INT32_MIN
ibz_set_from_str(&a, "-2147483648", 10);
ibz_set(&b, INT32_MIN);
res = res | (ibz_get(&a) != -2147483648);
res = res | (ibz_cmp_int32(&a, -2147483648) != 0);
res = res | (0 != ibz_cmp(&a, &b));
ibz_set_from_str(&a, "2147483647", 10);
ibz_set(&b, INT32_MAX);
res = res | (ibz_get(&a) != 2147483647);
res = res | (ibz_cmp_int32(&a, 2147483647) != 0);
res = res | (0 != ibz_cmp(&a, &b));
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
return (res);
}
// void ibz_add(ibz_t *sum, const ibz_t *a, const ibz_t *b);
// void ibz_sub(ibz_t *diff, const ibz_t *a, const ibz_t *b);
// void ibz_neg(ibz_t *neg, const ibz_t *a);
// void ibz_abs(ibz_t *abs, const ibz_t *a);
int
ibz_test_add_sub_neg_abs()
{
int res = 0;
ibz_t a, b, c, d, r, q, m;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
ibz_init(&d);
ibz_init(&r);
ibz_init(&q);
ibz_init(&m);
ibz_set_from_str(&a, "10000111100002222", 16);
ibz_copy(&c, &a);
ibz_add(&d, &a, &b);
res = res | (0 != ibz_cmp(&b, &q));
res = res | (0 != ibz_cmp(&a, &c));
res = res | (0 != ibz_cmp(&d, &c));
ibz_add(&d, &b, &a);
res = res | (0 != ibz_cmp(&b, &q));
res = res | (0 != ibz_cmp(&a, &c));
res = res | (0 != ibz_cmp(&d, &c));
// add test for adding non-0, sub, mul, ...
ibz_set_from_str(&b, "20000111100002223", 16);
ibz_set_from_str(&c, "30000222200004445", 16);
ibz_add(&d, &a, &b);
res = res | (0 != ibz_cmp(&d, &c));
ibz_neg(&m, &a);
ibz_add(&q, &m, &d);
res = res | (0 != ibz_cmp(&q, &b));
ibz_neg(&m, &m);
res = res | (0 != ibz_cmp(&m, &a));
ibz_add(&q, &m, &b);
res = res | (0 != ibz_cmp(&q, &c));
ibz_neg(&m, &m);
ibz_sub(&q, &b, &m);
res = res | (0 != ibz_cmp(&q, &c));
ibz_neg(&m, &m);
res = res | (0 != ibz_cmp(&a, &m));
ibz_sub(&q, &d, &a);
res = res | (0 != ibz_cmp(&q, &b));
ibz_neg(&d, &d);
ibz_neg(&m, &a);
ibz_neg(&c, &b);
ibz_sub(&q, &d, &m);
res = res | (0 != ibz_cmp(&q, &c));
ibz_sub(&q, &m, &b);
res = res | (0 != ibz_cmp(&q, &d));
ibz_abs(&r, &r);
res = res | !ibz_is_zero(&r);
ibz_abs(&r, &a);
res = res | (0 != ibz_cmp(&a, &r));
ibz_abs(&r, &m);
res = res | (0 != ibz_cmp(&a, &r));
ibz_neg(&m, &m);
res = res | (0 != ibz_cmp(&a, &m));
if (res) {
printf("Quaternion module integer group test ibz_test_add_sub_neg_abs failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
ibz_finalize(&d);
ibz_finalize(&r);
ibz_finalize(&q);
ibz_finalize(&m);
return (res);
}
// void ibz_mul(ibz_t *prod, const ibz_t *a, const ibz_t *b);
// void ibz_sqrt_floor(ibz_t *sqrt, const ibz_t *a);
int
ibz_test_mul_sqrt()
{
int res = 0;
ibz_t a, b, c, d, r, q, m;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
ibz_init(&d);
ibz_init(&r);
ibz_init(&q);
ibz_init(&m);
// zero
ibz_set_from_str(&a, "2113309833171849999003363", 10);
ibz_copy(&c, &a);
ibz_set(&b, 0);
ibz_mul(&m, &a, &b);
res = res | (0 != ibz_cmp(&m, &b));
res = res | (0 != ibz_cmp(&a, &c));
ibz_mul(&m, &b, &a);
res = res | (0 != ibz_cmp(&m, &b));
res = res | (0 != ibz_cmp(&a, &c));
// one
ibz_set(&b, 1);
ibz_mul(&m, &a, &b);
res = res | (0 != ibz_cmp(&m, &a));
res = res | (0 != ibz_cmp(&a, &c));
ibz_mul(&m, &b, &a);
res = res | (0 != ibz_cmp(&m, &a));
res = res | (0 != ibz_cmp(&a, &c));
// -1
ibz_neg(&b, &b);
ibz_neg(&d, &a);
ibz_mul(&m, &a, &b);
res = res | (0 != ibz_cmp(&m, &d));
res = res | (0 != ibz_cmp(&a, &c));
ibz_mul(&m, &b, &a);
res = res | (0 != ibz_cmp(&m, &d));
res = res | (0 != ibz_cmp(&a, &c));
// larger
ibz_set_from_str(&b, "34575345632322576567896", 10);
ibz_set_from_str(&c, "73068417910102676801285574959599851857101834248", 10);
ibz_mul(&m, &a, &b);
res = res | (0 != ibz_cmp(&m, &c));
ibz_neg(&b, &b);
ibz_neg(&a, &a);
ibz_mul(&m, &a, &b);
res = res | (0 != ibz_cmp(&m, &c));
ibz_neg(&a, &a);
ibz_neg(&c, &c);
ibz_mul(&m, &a, &b);
res = res | (0 != ibz_cmp(&m, &c));
ibz_neg(&b, &b);
ibz_neg(&a, &a);
ibz_mul(&m, &a, &b);
res = res | (0 != ibz_cmp(&m, &c));
// sqrt tests
ibz_abs(&b, &b);
ibz_abs(&a, &a);
ibz_mul(&m, &a, &a);
ibz_sqrt_floor(&d, &m);
res = res | (0 != ibz_cmp(&d, &a));
ibz_mul(&m, &b, &b);
ibz_sqrt_floor(&d, &m);
res = res | (0 != ibz_cmp(&d, &b));
ibz_set(&d, 1);
ibz_mul(&m, &b, &b);
ibz_sub(&m, &m, &d);
ibz_sub(&c, &b, &d);
ibz_sqrt_floor(&d, &m);
res = res | (0 != ibz_cmp(&d, &c));
if (res) {
printf("Quaternion module integer group test ibz_test_mul_sqrt failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
ibz_finalize(&d);
ibz_finalize(&r);
ibz_finalize(&q);
ibz_finalize(&m);
return (res);
}
// void ibz_div(ibz_t *quotient, ibz_t *remainder, const ibz_t *a, const ibz_t *b);
// int ibz_divides(const ibz_t *a, const ibz_t *b);
int
ibz_test_div()
{
int res = 0;
ibz_t a, b, c, d, r, q, m;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
ibz_init(&d);
ibz_init(&r);
ibz_init(&q);
ibz_init(&m);
// one
ibz_set_from_str(&a, "2113309833171849999003363", 10);
ibz_copy(&c, &a);
ibz_set(&b, 1);
ibz_copy(&d, &b);
res = res | !ibz_divides(&a, &b);
ibz_div(&q, &r, &a, &b);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &a));
res = res | (0 != ibz_cmp(&c, &a));
res = res | (0 != ibz_cmp(&b, &d));
// not one, zero remainder
ibz_set_from_str(&b, "15678200126527887351125", 10);
ibz_mul(&d, &a, &b);
ibz_copy(&c, &d);
res = res | !ibz_divides(&d, &b);
res = res | !ibz_divides(&d, &a);
res = res | !ibz_divides(&d, &d);
ibz_div(&q, &r, &d, &b);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &a));
ibz_div(&q, &r, &d, &a);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &b));
// flipping signs
ibz_neg(&a, &a);
ibz_neg(&b, &b);
ibz_div(&q, &r, &d, &b);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &a));
ibz_div(&q, &r, &d, &a);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &b));
ibz_neg(&a, &a);
ibz_neg(&d, &d);
ibz_div(&q, &r, &d, &b);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &a));
ibz_div(&q, &r, &d, &a);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &b));
ibz_neg(&a, &a);
ibz_neg(&b, &b);
ibz_div(&q, &r, &d, &b);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &a));
ibz_div(&q, &r, &d, &a);
res = res | !ibz_is_zero(&r);
res = res | (0 != ibz_cmp(&q, &b));
// non-zero remainder
ibz_neg(&a, &a);
ibz_neg(&d, &d);
ibz_set_from_str(&c, "8678205677345432110000", 10);
ibz_add(&d, &d, &c);
ibz_div(&q, &r, &d, &b);
ibz_mul(&m, &q, &b);
ibz_add(&m, &m, &r);
res = res | (0 != ibz_cmp(&d, &m));
ibz_abs(&m, &r);
ibz_abs(&q, &b);
res = res | (0 <= ibz_cmp(&m, &q));
ibz_set(&q, 0);
res =
res | !(((ibz_cmp(&q, &r) <= 0) && (ibz_cmp(&q, &d) < 0)) || ((ibz_cmp(&q, &r) >= 0) && (ibz_cmp(&r, &d) > 0)));
// flip signs
ibz_neg(&d, &d);
ibz_div(&q, &r, &d, &b);
ibz_mul(&m, &q, &b);
ibz_add(&m, &m, &r);
res = res | (0 != ibz_cmp(&d, &m));
ibz_abs(&m, &r);
ibz_abs(&q, &b);
res = res | (0 <= ibz_cmp(&m, &q));
ibz_set(&q, 0);
res =
res | !(((ibz_cmp(&q, &r) <= 0) && (ibz_cmp(&q, &d) < 0)) || ((ibz_cmp(&q, &r) >= 0) && (ibz_cmp(&r, &d) > 0)));
ibz_neg(&b, &b);
ibz_div(&q, &r, &d, &b);
ibz_mul(&m, &q, &b);
ibz_add(&m, &m, &r);
res = res | (0 != ibz_cmp(&d, &m));
ibz_abs(&m, &r);
ibz_abs(&q, &b);
res = res | (0 <= ibz_cmp(&m, &q));
ibz_set(&q, 0);
res =
res | !(((ibz_cmp(&q, &r) <= 0) && (ibz_cmp(&q, &d) < 0)) || ((ibz_cmp(&q, &r) >= 0) && (ibz_cmp(&r, &d) > 0)));
ibz_neg(&d, &d);
ibz_div(&q, &r, &d, &b);
ibz_mul(&m, &q, &b);
ibz_add(&m, &m, &r);
res = res | (0 != ibz_cmp(&d, &m));
ibz_abs(&m, &r);
ibz_abs(&q, &b);
res = res | (0 <= ibz_cmp(&m, &q));
ibz_set(&q, 0);
res =
res | !(((ibz_cmp(&q, &r) <= 0) && (ibz_cmp(&q, &d) < 0)) || ((ibz_cmp(&q, &r) >= 0) && (ibz_cmp(&r, &d) > 0)));
if (res) {
printf("Quaternion module integer group test ibz_test_div failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
ibz_finalize(&d);
ibz_finalize(&r);
ibz_finalize(&q);
ibz_finalize(&m);
return (res);
}
// void ibz_mod(ibz_t *r, const ibz_t *a, const ibz_t *b);
// unsigned long int ibz_mod_ui(const mpz_t *n, unsigned long int d);
int
ibz_test_mod()
{
int res = 0;
ibz_t a, b, c, r, m;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
ibz_init(&r);
ibz_init(&m);
ibz_set_from_str(&a, "2113309833171849999003363", 10);
res = res | (ibz_mod_ui(&a, 3) != 0);
res = res | (ibz_mod_ui(&a, 2) != 1);
ibz_set_from_str(&m, "2113309833171840000000000", 10);
ibz_mod(&r, &a, &m);
ibz_add(&c, &r, &m);
res = res | (0 != ibz_cmp(&c, &a));
ibz_neg(&b, &a);
res = res | (ibz_mod_ui(&b, 3) != 0);
res = res | (ibz_mod_ui(&b, 2) != 1);
ibz_mod(&r, &b, &m);
ibz_sub(&c, &r, &m);
ibz_sub(&c, &c, &m);
res = res | (0 != ibz_cmp(&c, &b));
if (res) {
printf("Quaternion module integer group test ibz_test_mod failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
ibz_finalize(&r);
ibz_finalize(&m);
return (res);
}
// void ibz_pow(ibz_t *pow, const ibz_t *x, uint32_t e);
// void ibz_pow_mod(ibz_t *pow, const ibz_t *x, const ibz_t *e, const ibz_t *m);
// void ibz_div_2exp(ibz_t *quotient, const ibz_t *a, uint32_t exp);
int
ibz_test_pow()
{
int res = 0;
ibz_t a, b, c, d, e, m;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
ibz_init(&d);
ibz_init(&e);
ibz_init(&m);
ibz_set_from_str(&a, "aaaaaaaabbbbbbbb2222221111", 16);
ibz_copy(&c, &a);
int exp = 10;
ibz_set(&b, 1);
for (int i = 1; i < exp + 1; i++)
ibz_mul(&b, &b, &a);
ibz_pow(&d, &a, exp);
res = res | (0 != ibz_cmp(&c, &a));
res = res | (0 != ibz_cmp(&d, &b));
ibz_neg(&a, &a);
ibz_copy(&c, &a);
ibz_pow(&d, &a, exp);
res = res | (0 != ibz_cmp(&c, &a));
res = res | (0 != ibz_cmp(&d, &b));
exp = 9;
ibz_set(&b, 1);
for (int i = 1; i < exp + 1; i++)
ibz_mul(&b, &b, &a);
ibz_pow(&d, &a, exp);
res = res | (0 != ibz_cmp(&c, &a));
res = res | (0 != ibz_cmp(&d, &b));
ibz_set_from_str(&a, "aaaaaaaabbbbbbbb2222221111", 16);
ibz_set_from_str(&m, "cdabde24864122912341", 16);
exp = 10;
ibz_set(&m, exp);
ibz_copy(&c, &a);
ibz_set(&b, 1);
for (int i = 1; i < exp + 1; i++) {
ibz_mul(&b, &b, &a);
ibz_mod(&b, &b, &m);
}
ibz_pow_mod(&d, &a, &e, &m);
res = res | (0 != ibz_cmp(&c, &a));
res = res | (0 != ibz_cmp(&d, &b));
exp = 23;
ibz_set(&b, 2);
ibz_pow(&b, &b, exp);
ibz_div(&m, &b, &a, &b);
ibz_div_2exp(&d, &a, exp);
res = res | (0 != ibz_cmp(&c, &a));
res = res | (0 != ibz_cmp(&m, &d));
if (res) {
printf("Quaternion module integer group test ibz_test_pow failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
ibz_finalize(&d);
ibz_finalize(&e);
ibz_finalize(&m);
return (res);
}
// void ibz_gcd(ibz_t *gcd, const ibz_t *a, const ibz_t *b);
// int ibz_invmod(ibz_t *inv, const ibz_t *a, const ibz_t *mod);ibz_test_mod()
int
ibz_test_gcd()
{
int res = 0;
ibz_t a, b, c, d, ac, bc, m;
ibz_init(&a);
ibz_init(&b);
ibz_init(&c);
ibz_init(&d);
ibz_init(&ac);
ibz_init(&bc);
ibz_init(&m);
// fixed large test
ibz_set_from_str(&c, "25791357069084", 10);
ibz_set_from_str(&a, "6173271838293993987767", 10);
ibz_set_from_str(&b, "89882267321617266071838286", 10);
ibz_mul(&ac, &a, &c);
ibz_mul(&bc, &b, &c);
ibz_gcd(&d, &ac, &bc);
res = res | (0 != ibz_cmp(&c, &d));
ibz_mul(&m, &a, &c);
res = res | (0 != ibz_cmp(&ac, &m));
ibz_mul(&m, &b, &c);
res = res | (0 != ibz_cmp(&bc, &m));
// gcd is positif whatever the inputs are
ibz_neg(&ac, &ac);
ibz_gcd(&d, &ac, &bc);
res = res | (0 != ibz_cmp(&c, &d));
ibz_neg(&bc, &bc);
ibz_gcd(&d, &ac, &bc);
res = res | (0 != ibz_cmp(&c, &d));
ibz_neg(&ac, &ac);
ibz_gcd(&d, &ac, &bc);
res = res | (0 != ibz_cmp(&c, &d));
// different sizes do not matter
ibz_set(&d, 2);
ibz_gcd(&m, &ac, &d);
res = res | (0 != ibz_cmp(&m, &d));
// invmod
ibz_invmod(&d, &a, &b);
res = res | (0 >= ibz_cmp(&b, &d));
ibz_set(&m, 0);
res = res | (0 <= ibz_cmp(&m, &d));
ibz_mul(&m, &d, &a);
ibz_mod(&m, &m, &b);
res = res | !ibz_is_one(&m);
// negative changes nothing
ibz_neg(&a, &a);
ibz_invmod(&d, &a, &b);
res = res | (0 >= ibz_cmp(&b, &d));
ibz_set(&m, 0);
res = res | (0 <= ibz_cmp(&m, &d));
ibz_mul(&m, &d, &a);
ibz_mod(&m, &m, &b);
res = res | !ibz_is_one(&m);
if (res) {
printf("Quaternion module integer group test ibz_test_gcd failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&c);
ibz_finalize(&d);
ibz_finalize(&ac);
ibz_finalize(&bc);
ibz_finalize(&m);
return (res);
}
// Tests ibz_sqrt_mod_p
// Allows to provide the number of repetitions and the bit-size of the primes.
int
ibz_test_sqrt_mod_p(int reps, int prime_n)
{
int res = 0;
// Initialize GMP variables
ibz_t prime, a, prime_minus_a, asq, sqrt, tmp;
ibz_t prime_p4m3, prime_p5m8, prime_p1m8;
ibz_t prime_p4m3_x2, prime_p5m8_x2, prime_p1m8_x2;
ibz_t two_to_the_n_minus_1;
ibz_init(&prime);
ibz_init(&prime_p4m3);
ibz_init(&prime_p5m8);
ibz_init(&prime_p1m8);
ibz_init(&prime_p4m3_x2);
ibz_init(&prime_p5m8_x2);
ibz_init(&prime_p1m8_x2);
ibz_init(&a);
ibz_init(&prime_minus_a);
ibz_init(&asq);
ibz_init(&sqrt);
ibz_init(&tmp);
ibz_init(&two_to_the_n_minus_1);
// Generate random prime number
int n = prime_n; // Number of bits
ibz_set(&two_to_the_n_minus_1, 1);
mpz_mul_2exp(two_to_the_n_minus_1, two_to_the_n_minus_1, n);
ibz_sub(&two_to_the_n_minus_1, &two_to_the_n_minus_1, &ibz_const_one);
for (int r = 0; r < reps; ++r) {
ibz_rand_interval(&prime, &ibz_const_zero, &two_to_the_n_minus_1);
if (ibz_is_even(&prime))
ibz_add(&prime, &prime, &ibz_const_one);
int p4m3 = 0, p5m8 = 0, p1m8 = 0;
while (p4m3 == 0 || p5m8 == 0 || p1m8 == 0) {
do {
ibz_add(&prime, &prime, &ibz_const_two);
} while (!mpz_probab_prime_p(prime, 25));
if (mpz_mod_ui(tmp, prime, 4) == 3) {
ibz_copy(&prime_p4m3, &prime);
p4m3 = 1;
} else if (mpz_mod_ui(tmp, prime, 8) == 5) {
ibz_copy(&prime_p5m8, &prime);
p5m8 = 1;
} else if (mpz_mod_ui(tmp, prime, 8) == 1) {
ibz_copy(&prime_p1m8, &prime);
p1m8 = 1;
} else {
res = 1;
goto err;
}
}
ibz_t *primes[] = { &prime_p4m3, &prime_p5m8, &prime_p1m8 };
ibz_t *primes_x2[] = { &prime_p4m3_x2, &prime_p5m8_x2, &prime_p1m8_x2 };
for (int i = 0; i < 3; ++i) // 2p
mpz_mul_2exp(*primes_x2[i], *primes[i], 1);
// Test sqrt mod p
for (int i = 0; i < 3; ++i) {
ibz_sub(&tmp, primes[i], &ibz_const_one);
ibz_rand_interval(&a, &ibz_const_zero, &tmp);
ibz_sub(&prime_minus_a, (primes[i]), &a);
mpz_powm_ui(asq, a, 2, *primes[i]);
int no_sqrt = !ibz_sqrt_mod_p(&sqrt, &asq, primes[i]);
mpz_powm_ui(tmp, sqrt, 2, *primes[i]);
if (no_sqrt || (ibz_cmp(&sqrt, &a) && ibz_cmp(&sqrt, &prime_minus_a))) {
res = 1;
goto err;
}
}
}
err:
if (res) {
printf("Quaternion module integer test ibz_test_sqrt_mod_p failed\n");
}
ibz_finalize(&prime);
ibz_finalize(&prime_p4m3);
ibz_finalize(&prime_p5m8);
ibz_finalize(&prime_p1m8);
ibz_finalize(&prime_p4m3_x2);
ibz_finalize(&prime_p5m8_x2);
ibz_finalize(&prime_p1m8_x2);
ibz_finalize(&a);
ibz_finalize(&prime_minus_a);
ibz_finalize(&asq);
ibz_finalize(&sqrt);
ibz_finalize(&tmp);
ibz_finalize(&two_to_the_n_minus_1);
return res;
}
int
ibz_test_rand_interval(int reps)
{
int res = 0;
ibz_t low, high, rand;
ibz_init(&low);
ibz_init(&high);
ibz_init(&rand);
ibz_set_from_str(&low, "ffa", 16);
ibz_set_from_str(&high, "eeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", 16);
for (int i = 0; i < reps; ++i) {
res = ibz_rand_interval(&rand, &low, &high);
if (res != 1) {
res = 1;
goto err;
} else {
res = 0;
}
if (ibz_cmp(&rand, &low) < 0 || ibz_cmp(&rand, &high) > 0) {
res = 1;
goto err;
}
}
err:
if (res) {
printf("Quaternion module integer test ibz_test_rand_interval failed\n");
}
ibz_finalize(&low);
ibz_finalize(&high);
ibz_finalize(&rand);
return res;
}
int
ibz_test_rand_interval_i(int reps)
{
int res = 0;
int32_t low, high;
ibz_t rand, cmp;
ibz_init(&rand);
ibz_init(&cmp);
for (int i = 0; i < reps; ++i) {
randombytes((unsigned char *)&low, sizeof(int32_t));
randombytes((unsigned char *)&high, sizeof(int32_t));
if (low < 0)
low = -low;
if (high < 0)
high = -high;
// The function requires a < b, thus a != b
if (low == high) {
continue;
}
if (low > high) {
int32_t tmp = low;
low = high;
high = tmp;
}
res = ibz_rand_interval_i(&rand, low, high);
if (res != 1) {
res = 1;
goto err;
} else {
res = 0;
}
ibz_set(&cmp, low);
if (ibz_cmp(&rand, &cmp) < 0) {
res = 1;
goto err;
}
ibz_set(&cmp, high);
if (ibz_cmp(&rand, &cmp) > 0) {
res = 1;
goto err;
}
}
err:
if (res) {
printf("Quaternion module integer test ibz_test_rand_interval_i failed\n");
}
ibz_finalize(&rand);
ibz_finalize(&cmp);
return res;
}
int
ibz_test_rand_interval_minm_m(int reps)
{
int res = 0;
int32_t m;
ibz_t rand;
ibz_init(&rand);
for (int i = 0; i < reps; ++i) {
randombytes((unsigned char *)&m, sizeof(int32_t));
if (m < 0)
m = -m;
m >>= 1; // less than 32 bit
if (m < 0) {
res = 1;
goto err;
}
res = ibz_rand_interval_minm_m(&rand, m);
if (res != 1) {
res = 1;
goto err;
} else {
res = 0;
}
if (ibz_cmp_int32(&rand, -m) < 0) {
res = 1;
goto err;
}
if (ibz_cmp_int32(&rand, m) > 0) {
res = 1;
goto err;
}
}
err:
if (res) {
printf("Quaternion module integer test ibz_test_rand_interval_minm_m failed\n");
}
ibz_finalize(&rand);
return res;
}
int
ibz_test_copy_digits(void)
{
int res = 0;
const digit_t d1[] = { 0x12345678 };
const digit_t d2[] = { 2, 1 };
const char d1str[] = "12345678";
#if RADIX == 32
const char d2str[] = "100000002";
#elif RADIX == 64
const char d2str[] = "10000000000000002";
#endif
char d1_intbig_str[80] = { 0 };
char d2_intbig_str[80] = { 0 };
ibz_t d1_intbig, d2_intbig;
ibz_init(&d1_intbig);
ibz_init(&d2_intbig);
ibz_copy_digits(&d1_intbig, d1, 1);
ibz_copy_digits(&d2_intbig, d2, 2);
ibz_convert_to_str(&d1_intbig, d1_intbig_str, 16);
ibz_convert_to_str(&d2_intbig, d2_intbig_str, 16);
if (memcmp(d1str, d1_intbig_str, sizeof(d1str))) {
res = 1;
goto err;
}
if (memcmp(d2str, d2_intbig_str, sizeof(d2str))) {
res = 1;
goto err;
}
err:
if (res) {
printf("Quaternion module integer test ibz_test_copy_digits failed\n");
}
ibz_finalize(&d1_intbig);
ibz_finalize(&d2_intbig);
return res;
}
int
ibz_test_to_digits(void)
{
int res = 0;
ibz_t d1_intbig, d2_intbig, zero_intbig;
ibz_t d1_intbig_rec, d2_intbig_rec, zero_intbig_rec, cof, cof2;
const char d1str[] = "12345678";
const char d2str[] = "10000000000000002";
ibz_init(&d1_intbig);
ibz_init(&d2_intbig);
ibz_set_from_str(&d1_intbig, d1str, 16);
ibz_set_from_str(&d2_intbig, d2str, 16);
ibz_init(&zero_intbig);
ibz_init(&d1_intbig_rec);
ibz_init(&d2_intbig_rec);
ibz_init(&zero_intbig_rec);
ibz_init(&cof);
ibz_init(&cof2);
size_t d1_digits = (mpz_sizeinbase(d1_intbig, 2) + sizeof(digit_t) * 8 - 1) / (sizeof(digit_t) * 8);
size_t d2_digits = (mpz_sizeinbase(d2_intbig, 2) + sizeof(digit_t) * 8 - 1) / (sizeof(digit_t) * 8);
digit_t d1[d1_digits];
digit_t d2[d2_digits];
digit_t zero[1];
ibz_to_digits(d1, &d1_intbig);
ibz_to_digits(d2, &d2_intbig);
ibz_to_digits(zero, &zero_intbig);
// A lazy test, but we know that this conversion should be correct from the previous test
ibz_copy_digits(&d1_intbig_rec, d1, d1_digits);
ibz_copy_digits(&d2_intbig_rec, d2, d2_digits);
ibz_copy_digits(&zero_intbig_rec, zero, 1);
if (ibz_cmp(&d1_intbig, &d1_intbig_rec) || ibz_cmp(&zero_intbig, &zero_intbig_rec)) {
res = 1;
goto err;
}
#if RADIX == 64
const digit_t p_cofactor_for_3g[5] = {
0x0000000000000000, 0x74f9dace0d9ec800, 0x63a25b437f655001, 0x0000000000000019, 0
};
digit_t p_cofactor_for_3g_rec[10] = { 0 };
#elif RADIX == 32
const digit_t p_cofactor_for_3g[10] = { 0x00000000, 0x00000000, 0x0d9ec800, 0x74f9dace, 0x7f655001,
0x63a25b43, 0x00000019, 0x00000000, 0, 0 };
digit_t p_cofactor_for_3g_rec[5] = { 0 };
#endif
ibz_copy_digits(&cof, p_cofactor_for_3g, 5);
ibz_to_digits(p_cofactor_for_3g_rec, &cof);
ibz_copy_digits(&cof2, p_cofactor_for_3g_rec, 5);
if (ibz_cmp(&cof, &cof2)) {
res = 1;
goto err;
}
#if RADIX == 32
digit_t da[3] = { 0, 0, 0 };
#elif RADIX == 64
digit_t da[2] = { 0, 0 };
#endif
ibz_t strval, strval_check;
ibz_init(&strval_check);
ibz_init(&strval);
ibz_set_from_str(&strval, "1617406613339667622221321", 10);
ibz_to_digits(da, &strval);
ibz_copy_digits(&strval_check, da, sizeof(da) / sizeof(digit_t));
// ibz_printf("strval: %Zd\nstrval_check: %Zd\n", strval, strval_check);
if (ibz_cmp(&strval, &strval_check)) {
res = 1;
goto err;
}
err:
if (res) {
printf("Quaternion module integer test ibz_test_to_digits failed\n");
}
ibz_finalize(&d1_intbig);
ibz_finalize(&d2_intbig);
ibz_finalize(&zero_intbig);
ibz_finalize(&d1_intbig_rec);
ibz_finalize(&d2_intbig_rec);
ibz_finalize(&zero_intbig_rec);
ibz_finalize(&cof);
ibz_finalize(&cof2);
ibz_finalize(&strval);
ibz_finalize(&strval_check);
return res;
}
int
ibz_test_intbig()
{
int reps = 2;
int prime_n = 103;
int res = 0;
printf("\nRunning quaternion tests of gmp-based integer functions\n");
res = res | ibz_test_init_set_cmp();
res = res | ibz_test_add_sub_neg_abs();
res = res | ibz_test_mul_sqrt();
res = res | ibz_test_div();
res = res | ibz_test_mod();
res = res | ibz_test_pow();
res = res | ibz_test_gcd();
res = res | ibz_test_sqrt_mod_p(reps, prime_n);
res = res | ibz_test_rand_interval(reps);
res = res | ibz_test_rand_interval_i(reps);
res = res | ibz_test_rand_interval_minm_m(reps);
res = res | ibz_test_copy_digits();
res = res | ibz_test_to_digits();
return res;
}

View File

@@ -1,288 +1,44 @@
#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 ibz_generate_random_prime(ibz_t *p, int is3mod4, int bitsize);
int
quat_test_ibz_generate_random_prime()
{
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");
int bitsize, is3mod4, primality_test_attempts;
ibz_t p;
ibz_init(&p);
bitsize = 20;
primality_test_attempts = 30;
is3mod4 = 1;
res = res || !ibz_generate_random_prime(&p, is3mod4, bitsize, primality_test_attempts);
res = res || (ibz_probab_prime(&p, 20) == 0);
res = res || (ibz_bitsize(&p) < bitsize);
res = res || (is3mod4 && (ibz_get(&p) % 4 != 3));
bitsize = 30;
is3mod4 = 0;
res = res || !ibz_generate_random_prime(&p, is3mod4, bitsize, primality_test_attempts);
res = res || (ibz_probab_prime(&p, 20) == 0);
res = res || (ibz_bitsize(&p) < bitsize);
res = res || (is3mod4 && (ibz_get(&p) % 4 != 3));
is3mod4 = 1;
res = res || !ibz_generate_random_prime(&p, is3mod4, bitsize, primality_test_attempts);
res = res || (ibz_probab_prime(&p, 20) == 0);
res = res || (ibz_bitsize(&p) < bitsize);
res = res || (is3mod4 && (ibz_get(&p) % 4 != 3));
if (res) {
printf("Quaternion unit test ibz_generate_random_prime failed\n");
}
ibz_finalize(&a);
ibz_finalize(&b);
ibz_finalize(&q);
return(res);
ibz_finalize(&p);
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 ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p);
int
quat_test_integer_ibz_cornacchia_prime(void)
{
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_t x, y, n, prod, c_res, p;
ibz_init(&x);
ibz_init(&y);
ibz_init(&n);
@@ -293,54 +49,54 @@ int quat_test_integer_ibz_cornacchia_prime(){
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);
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);
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);
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);
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);
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;
}
@@ -348,13 +104,17 @@ int quat_test_integer_ibz_cornacchia_prime(){
ibz_set(&n, 1);
// there is no solution in these cases
ibz_set(&p, 7);
res = res || ibz_cornacchia_prime(&x,&y,&n,&p);
res = res || ibz_cornacchia_prime(&x, &y, &n, &p);
ibz_set(&p, 3);
res = res || ibz_cornacchia_prime(&x,&y,&n,&p);
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){
res = res || ibz_cornacchia_prime(&x, &y, &n, &p);
// This should be solved
ibz_set(&n, 3);
ibz_set(&p, 3);
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);
@@ -366,222 +126,13 @@ int quat_test_integer_ibz_cornacchia_prime(){
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
quat_test_integers(void)
{
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_ibz_generate_random_prime();
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);
return (res);
}

View File

@@ -0,0 +1,266 @@
#include "quaternion_tests.h"
#include <stdlib.h>
// int quat_lattice_bound_parallelogram(ibz_vec_4_t *box, ibz_mat_4x4_t *U, const ibz_mat_4x4_t *G,
// const ibz_t *radius);
int
quat_test_lat_ball_paralellogram_randomized(int iterations, int bitsize)
{
int res = 0;
quat_lattice_t lattice;
ibz_t radius, length, tmp;
ibz_mat_4x4_t U, G;
ibz_vec_4_t box, dbox, x;
quat_lattice_init(&lattice);
ibz_vec_4_init(&box);
ibz_vec_4_init(&dbox);
ibz_vec_4_init(&x);
ibz_mat_4x4_init(&U);
ibz_mat_4x4_init(&G);
ibz_init(&radius);
ibz_init(&length);
ibz_init(&tmp);
for (int it = 0; it < iterations; it++) {
// Create a random positive definite quadatic form
#ifndef NDEBUG
int randret = quat_test_input_random_lattice_generation(&lattice, bitsize, 1, 0);
assert(randret == 0);
#else
(void)quat_test_input_random_lattice_generation(&lattice, bitsize, 1, 0);
#endif
ibz_mat_4x4_transpose(&G, &lattice.basis);
ibz_mat_4x4_mul(&G, &G, &lattice.basis);
// Set radius to 2 × sqrt(lattice volume)
#ifndef NDEBUG
int ok = ibz_mat_4x4_inv_with_det_as_denom(NULL, &radius, &(lattice.basis));
assert(ok);
#else
(void)ibz_mat_4x4_inv_with_det_as_denom(NULL, &radius, &(lattice.basis));
#endif
ibz_abs(&radius, &radius);
ibz_sqrt_floor(&radius, &radius);
ibz_mul(&radius, &radius, &ibz_const_two);
quat_lattice_bound_parallelogram(&box, &U, &G, &radius);
for (int i = 0; i < 4; i++) {
// dbox is a box with sides dbox[i] = 2*box[i] + 1
ibz_add(&dbox[i], &box[i], &box[i]);
ibz_add(&dbox[i], &dbox[i], &ibz_const_one);
// initialize x[i] to the bottom of dbox[i]
ibz_neg(&x[i], &dbox[i]);
}
// Integrate U into the Gram matrix
ibz_mat_4x4_mul(&G, &U, &G);
ibz_mat_4x4_transpose(&U, &U);
ibz_mat_4x4_mul(&G, &G, &U);
// We treat x[0]...x[4] as a counter, incrementing one by one
// but skipping values that are inside the parallelogram defined
// by box.
while (1) {
// x is out of the parallelogram, so its length must be
// greater than the radius
quat_qf_eval(&length, &G, &x);
if (ibz_cmp(&length, &radius) <= 0) {
res = 1;
break;
}
// Increment counter
ibz_add(&x[0], &x[0], &ibz_const_one);
// if x[0] just entered the interval
ibz_add(&tmp, &x[0], &box[0]);
if (ibz_is_zero(&tmp)) {
int inbox = 1;
for (int i = 1; i < 4; i++) {
ibz_abs(&tmp, &x[i]);
inbox &= ibz_cmp(&tmp, &box[i]) <= 0;
}
// if x[1]...x[3] are all in the respective intervals
// jump straight to the end of x[0]'s interval
if (inbox)
ibz_set(&x[0], 1);
}
// if x[0] became positive, loop the counter
if (ibz_is_one(&x[0])) {
ibz_neg(&x[0], &dbox[0]);
int carry = 1;
for (int i = 1; carry && i < 4; i++) {
ibz_add(&x[i], &x[i], &ibz_const_one);
if (ibz_cmp(&x[i], &dbox[i]) > 0) {
ibz_neg(&x[i], &dbox[i]);
} else {
carry = 0;
}
}
// If there still is a carry, we are at the end
if (carry)
break;
}
}
}
quat_lattice_finalize(&lattice);
ibz_vec_4_finalize(&box);
ibz_vec_4_finalize(&dbox);
ibz_vec_4_finalize(&x);
ibz_mat_4x4_finalize(&U);
ibz_mat_4x4_finalize(&G);
ibz_finalize(&radius);
ibz_finalize(&length);
ibz_finalize(&tmp);
if (res != 0) {
printf("Quaternion unit test lat_ball_paralellogram_randomized failed\n");
}
return (res);
}
// helper which tests quat_lattice_sample_from_ball on given input
int
quat_test_lat_ball_sample_helper(const quat_lattice_t *lat, const ibz_t *radius, const quat_alg_t *alg)
{
int res = 0;
quat_alg_elem_t vec;
ibz_t norm_d, norm_n;
ibz_init(&norm_d);
ibz_init(&norm_n);
quat_alg_elem_init(&vec);
// check return value
res |= !quat_lattice_sample_from_ball(&vec, lat, alg, radius);
// check result is in lattice
res |= !quat_lattice_contains(NULL, lat, &vec);
quat_alg_norm(&norm_n, &norm_d, &vec, alg);
// test that n/d <= r so that n <= rd
ibz_mul(&norm_d, &norm_d, radius);
res |= !(ibz_cmp(&norm_n, &norm_d) <= 0);
ibz_finalize(&norm_d);
ibz_finalize(&norm_n);
quat_alg_elem_finalize(&vec);
return res;
}
// int quat_lattice_sample_from_ball(ibz_vec_4_t *x, const quat_lattice_t *lattice, const quat_alg_t
// *alg, const ibz_t *radius);
int
quat_test_lat_ball_sample_from_ball()
{
int res = 0;
ibz_t norm_n, norm_d;
quat_alg_t alg;
quat_alg_elem_t vec;
quat_lattice_t lattice;
ibz_t radius;
quat_alg_init_set_ui(&alg, 11);
ibz_init(&norm_n);
ibz_init(&norm_d);
quat_lattice_init(&lattice);
ibz_init(&radius);
quat_alg_elem_init(&vec);
for (int it = 0; it < 3; it++) {
if (it == 0) {
ibz_mat_4x4_identity(&(lattice.basis)); // Test inner product
} else if (it == 1) {
ibz_set(&lattice.denom, 13); // Test with denominator
} else { // if (it == 2)
// Test a very much non-orthogonal qf
for (int i = 0; i < 4; i++)
for (int j = 0; j < 4; j++)
ibz_add(&(lattice.basis[i][j]), &(lattice.basis[i][j]), &ibz_const_one);
}
for (int i = 0; i < 100; i++) {
ibz_set(&radius, i + 1);
res |= quat_test_lat_ball_sample_helper(&lattice, &radius, &alg);
if (res != 0)
break;
}
}
if (res != 0) {
printf("Quaternion unit test lat_ball_sample_from_ball failed\n");
}
quat_alg_finalize(&alg);
quat_lattice_finalize(&lattice);
ibz_finalize(&radius);
quat_alg_elem_finalize(&vec);
ibz_finalize(&norm_n);
ibz_finalize(&norm_d);
return (res);
}
int
quat_test_lat_ball_sample_from_ball_randomized(int iterations, int bitsize)
{
int res = 0;
ibz_t norm_n, norm_d;
quat_alg_t alg;
quat_alg_elem_t vec;
quat_lattice_t *lattices;
ibz_t radius;
quat_alg_init_set_ui(&alg, 11);
ibz_init(&norm_n);
ibz_init(&norm_d);
lattices = malloc(iterations * sizeof(quat_lattice_t));
for (int i = 0; i < iterations; i++)
quat_lattice_init(&(lattices[i]));
ibz_init(&radius);
quat_alg_elem_init(&vec);
int randret = quat_test_input_random_lattice_generation(lattices, bitsize, iterations, 0);
if (!randret) {
for (int i = 0; i < iterations; i++) {
#ifndef NDEBUG
int ok = ibz_mat_4x4_inv_with_det_as_denom(NULL, &radius, &(lattices[i].basis));
assert(ok);
#else
(void)ibz_mat_4x4_inv_with_det_as_denom(NULL, &radius, &(lattices[i].basis));
#endif
ibz_abs(&radius, &radius);
res |= quat_test_lat_ball_sample_helper(&(lattices[i]), &radius, &alg);
if (res != 0)
break;
}
}
if (res != 0) {
printf("Quaternion unit test lat_ball_sample_from_ball_randomized failed\n");
}
quat_alg_finalize(&alg);
for (int i = 0; i < iterations; i++)
quat_lattice_finalize(&(lattices[i]));
free(lattices);
ibz_finalize(&radius);
quat_alg_elem_finalize(&vec);
ibz_finalize(&norm_n);
ibz_finalize(&norm_d);
return (res);
}
// run all previous tests
int
quat_test_lat_ball(void)
{
int res = 0;
printf("\nRunning quaternion tests for sampling lattice points of bounded norm\n");
res = res | quat_test_lat_ball_sample_from_ball();
res = res | quat_test_lat_ball_sample_from_ball_randomized(100, 10);
res = res | quat_test_lat_ball_paralellogram_randomized(100, 100);
return (res);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,218 +0,0 @@
#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,211 @@
#include <float.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include "mini-gmp-extra.h"
#include "intbig_internal.h"
#include "rng.h"
#define RANDOM_TEST_ITERS 1000
int
mini_gmp_test_mpz_legendre(void)
{
int res = 0;
const int levels = 3;
const int cofactor[] = { 5, 65, 27 };
const int e[] = { 248, 376, 500 };
int as[] = { -2, -1, 0, 1, 2 };
// clang-format off
int legendre[3][5] = {
{ -1, -1, 0, 1, 1, },
{ -1, -1, 0, 1, 1, },
{ -1, -1, 0, 1, 1, },
};
// clang-format on
mpz_t a, p;
mpz_init(a);
mpz_init(p);
for (int i = 0; i < levels; i++) {
// build cofactor*2^e - 1 for the i-th level
mpz_set_ui(p, 1);
mpz_mul_2exp(p, p, e[i]);
mpz_mul_ui(p, p, cofactor[i]);
mpz_sub_ui(p, p, 1);
for (unsigned long j = 0; j < sizeof(as) / sizeof(as[0]); j++) {
mpz_set_si(a, as[j]);
res = res | (mini_mpz_legendre(a, p) != legendre[i][j]);
}
#if defined(__GMP_H__)
for (int j = 0; j < RANDOM_TEST_ITERS; j++) {
ibz_rand_interval(&a, &ibz_const_zero, &p);
// Compare against the full GMP implementation
res = res | (mini_mpz_legendre(a, p) != mpz_legendre(a, p));
}
#endif
}
mpz_clear(a);
mpz_clear(p);
if (res) {
printf("mini-gmp test mpz_legendre failed\n");
}
return res;
}
typedef union
{
double d;
int64_t s64;
unsigned char uc[sizeof(int64_t)];
} double_int64_uchar_t;
int
mini_gmp_test_mpz_get_d_2exp(void)
{
int res = 0;
signed long int e, e2 UNUSED;
double d, d2 UNUSED;
mpz_t op;
mpz_init(op);
// Test 0
mpz_set_si(op, 0);
d = mini_mpz_get_d_2exp(&e, op);
res = res | (e != 0);
res = res | (d != 0.0); // exact floating point comparison
// Test 1
mpz_set_si(op, 1);
d = mini_mpz_get_d_2exp(&e, op);
res = res | (e != 1);
res = res | (d != 0.5); // exact floating point comparison
// Test -1
mpz_set_si(op, -1);
d = mini_mpz_get_d_2exp(&e, op);
res = res | (e != 1);
res = res | (d != -0.5); // exact floating point comparison
// Test a few powers of 2: 2^1, 2^2, 2^4, 2^8, ..., 2^65536, and their negatives
for (int i = 0; i <= 16; i++) {
mpz_set_ui(op, 1);
mpz_mul_2exp(op, op, 1 << i);
d = mini_mpz_get_d_2exp(&e, op);
res = res | (e != (1 << i) + 1);
res = res | (d != 0.5); // exact floating point comparison
mpz_neg(op, op);
d = mini_mpz_get_d_2exp(&e, op);
res = res | (e != (1 << i) + 1);
res = res | (d != -0.5); // exact floating point comparison
}
// Test random doubles with random exponent
double_int64_uchar_t dd;
volatile uint16_t ee;
for (uint32_t i = 0; i < RANDOM_TEST_ITERS; i++) {
randombytes(dd.uc, sizeof(dd.uc));
randombytes((unsigned char *)&ee, sizeof(ee));
#ifdef TARGET_BIG_ENDIAN
// Ensure reproducibility in big-endian systems
dd.s64 = BSWAP64(dd.s64);
ee = BSWAP16(ee);
#endif
dd.s64 &= 0x800fffffffffffff; // clear exponent
dd.s64 |= 0x3fe0000000000000; // set exponent to -1
if (ee >= DBL_MANT_DIG) {
mpz_set_d(op, dd.d * (INT64_C(1) << DBL_MANT_DIG));
mpz_mul_2exp(op, op, ee - DBL_MANT_DIG);
} else {
// Since it fits in a double, round it first to ensure it's an integer
dd.d = round(dd.d * (INT64_C(1) << ee));
mpz_set_d(op, dd.d);
dd.d /= INT64_C(1) << ee;
// These cases (-1, 0, 1) were already tested, and +/- 1 would require special-casing below
if (fabs(dd.d) <= 1.0) {
continue;
}
}
d = mini_mpz_get_d_2exp(&e, op);
res = res | (e != ee);
res = res | (d != dd.d);
#if defined(__GMP_H__)
// Compare against the full GMP implementation
d2 = mpz_get_d_2exp(&e2, op);
res = res | (e != e2);
res = res | (d != d2);
#endif
}
#if defined(__GMP_H__)
for (int exp2 = 1023; exp2 <= 1025; exp2++) {
for (int sign_outer = -1; sign_outer <= 1; sign_outer += 2) {
for (int sign_inner = -1; sign_inner <= 1; sign_inner += 2) {
for (int i = 1; i < 15; i++) {
mpz_set_si(op, i);
mpz_mul_2exp(op, op, 55);
if (sign_inner > 0)
mpz_add_ui(op, op, 1);
else
mpz_sub_ui(op, op, 1);
mpz_mul_2exp(op, op, exp2 - 55);
mpz_mul_si(op, op, sign_outer);
d = mini_mpz_get_d_2exp(&e, op);
d2 = mpz_get_d_2exp(&e2, op);
res = res | (e != e2);
res = res | (d != d2);
}
}
}
}
// Test random integers
for (uint32_t i = 0; i < RANDOM_TEST_ITERS; i++) {
ibz_rand_interval_bits(&op, 8 * (i + 1));
d = mini_mpz_get_d_2exp(&e, op);
d2 = mpz_get_d_2exp(&e2, op);
res = res | (e != e2);
res = res | (d != d2);
}
#endif
if (res) {
printf("mini-gmp test mpz_get_d_2exp failed\n");
}
mpz_clear(op);
return res;
}
int
mini_gmp_test(void)
{
int res = 0;
printf("\nRunning tests for implementations of GMP functions missing from the mini-GMP API\n");
res = res | mini_gmp_test_mpz_legendre();
res = res | mini_gmp_test_mpz_get_d_2exp();
return res;
}

View File

@@ -0,0 +1,384 @@
#include "quaternion_tests.h"
#include <stdlib.h>
#include <assert.h>
// helpers for setting parameters
// test if parameters are such that represent_inter is likely to find a solution
int
quat_test_input_for_repres_bound(const ibz_t *p, const ibz_t *M, int q)
{
ibz_t c, r;
ibz_init(&c);
ibz_init(&r);
ibz_set(&r, q);
ibz_mul(&r, &r, p);
ibz_div(&c, &r, M, &r);
ibz_set(&r, 1 << 20);
int res = (ibz_cmp(&r, &c) <= 0);
ibz_finalize(&c);
ibz_finalize(&r);
return (res);
}
// 1 if ok, 0 if error
int
quat_test_special_extremal_setup(quat_represent_integer_params_t *params, const quat_alg_t *alg)
{ // check the order is maximal and i,j generate a suborder
int res = 1;
ibz_vec_4_t ij;
quat_lattice_t lat;
quat_lattice_t test;
ibz_vec_4_init(&ij);
quat_lattice_init(&lat);
quat_lattice_init(&test);
quat_alg_coord_mul(&ij, &(params->order->z.coord), &(params->order->t.coord), alg);
ibz_copy(&(lat.denom), &(params->order->z.denom));
ibz_copy(&(lat.basis[0][0]), &(lat.denom));
for (int i = 0; i < 4; i++) {
ibz_copy(&(lat.basis[i][3]), &(ij[i]));
ibz_mul(&(lat.basis[i][2]), &(params->order->t.coord[i]), &(lat.denom));
ibz_copy(&(lat.basis[i][1]), &(params->order->z.coord[i]));
}
quat_lattice_hnf(&lat);
quat_lattice_mul(&test, &lat, &lat, alg);
res = res && quat_lattice_inclusion(&test, &lat);
quat_lattice_mul(&test, &(params->order->order), &(params->order->order), alg);
res = res && quat_lattice_inclusion(&test, &(params->order->order));
res = res && quat_order_is_maximal(&(params->order->order), alg);
res = res && quat_lattice_inclusion(&lat, &(params->order->order));
ibz_vec_4_finalize(&ij);
quat_lattice_finalize(&lat);
quat_lattice_finalize(&test);
return (res);
}
void
quat_test_set_params_standard(quat_represent_integer_params_t *params,
quat_p_extremal_maximal_order_t *order,
const quat_alg_t *alg)
{
quat_lattice_O0_set_extremal(order);
params->algebra = alg;
params->order = order;
assert(quat_test_special_extremal_setup(params, alg));
}
void
quat_test_set_params_non_standard(quat_represent_integer_params_t *params,
quat_p_extremal_maximal_order_t *order,
const quat_alg_t *alg)
{
ibz_set_from_str(&order->z.coord[1], "214764116738303679745780048598183569015", 10);
ibz_set(&order->z.coord[2], 0);
ibz_set_from_str(&order->z.coord[3], "1", 10);
ibz_set_from_str(&order->z.denom, "73403150722045993989123427738005336972", 10);
ibz_set(&order->t.coord[2], 1);
ibz_set(&order->t.denom, 1);
order->q = 13;
ibz_set_from_str(&(order->order.basis[0][0]), "73403150722045993989123427738005336972", 10);
ibz_set_from_str(&(order->order.basis[1][1]),
"2694011267961700664357934052637599020390646823337886018360381743577635064392",
10);
ibz_set_from_str(&(order->order.basis[2][2]), "36701575361022996994561713869002668486", 10);
ibz_set(&(order->order.basis[3][3]), 1);
ibz_set_from_str(&(order->order.basis[0][2]), "36701575361022996994561713869002668486", 10);
ibz_set_from_str(&(order->order.basis[0][3]), "0", 10);
ibz_set_from_str(&(order->order.basis[1][2]), "0", 10);
ibz_set_from_str(&(order->order.basis[1][3]), "214764116738303679745780048598183569015", 10);
ibz_set_from_str(&(order->order.denom), "73403150722045993989123427738005336972", 10);
params->algebra = alg;
params->order = order;
assert(quat_test_special_extremal_setup(params, alg));
}
// void quat_order_elem_create(quat_alg_elem_t *elem, const quat_p_extremal_maximal_order_t *order,
// const ibz_vec_4_t *coeffs, const quat_alg_t *Bpoo);
int
quat_test_order_elem_create()
{
int res = 0;
quat_p_extremal_maximal_order_t O0;
ibz_vec_4_t vec, cmp;
quat_alg_t alg;
quat_alg_elem_t elem;
ibz_vec_4_init(&vec);
ibz_vec_4_init(&cmp);
quat_alg_elem_init(&elem);
quat_alg_elem_init(&(O0.z));
quat_alg_elem_init(&(O0.t));
quat_lattice_init(&(O0.order));
quat_alg_init_set_ui(&alg, 103);
quat_lattice_O0_set_extremal(&O0);
ibz_vec_4_set(&vec, 1, 7, 2, -2);
ibz_vec_4_copy(&cmp, &vec);
ibz_neg(&(cmp[3]), &(cmp[3]));
quat_order_elem_create(&elem, &O0, &vec, &alg);
res = res | ibz_cmp(&(elem.denom), &ibz_const_one);
for (int i = 0; i < 4; i++)
res = res | ibz_cmp(&(elem.coord[i]), &cmp[i]);
if (res) {
printf("Quaternion unit test order_elem_create failed\n");
}
ibz_vec_4_finalize(&vec);
ibz_vec_4_finalize(&cmp);
quat_alg_elem_finalize(&elem);
quat_alg_elem_finalize(&(O0.z));
quat_alg_elem_finalize(&(O0.t));
quat_lattice_finalize(&(O0.order));
quat_alg_finalize(&alg);
return (res);
}
// int quat_sampling_random_ideal_O0_given_norm(quat_left_ideal_t *lideal,const ibz_t *norm,int
// is_prime,const quat_represent_integer_params_t *params,int prime_sampling_attempts);
int
quat_test_sampling_random_ideal_O0_given_norm()
{
int res = 0;
quat_left_ideal_t lideal;
quat_lattice_t test;
quat_represent_integer_params_t params;
quat_p_extremal_maximal_order_t O0;
quat_alg_t alg;
ibz_t p, norm, coprime;
quat_lattice_init(&test);
ibz_init(&norm);
ibz_init(&p);
ibz_init(&coprime);
quat_left_ideal_init(&lideal);
quat_alg_elem_init(&(O0.z));
quat_alg_elem_init(&(O0.t));
quat_lattice_init(&(O0.order));
params.primality_test_iterations = 30;
ibz_set(&p, 1533069323);
quat_alg_init_set(&alg, &p);
quat_test_set_params_standard(&params, &O0, &alg);
ibz_set(&p, 1338708463);
res = res || !quat_sampling_random_ideal_O0_given_norm(&lideal, &p, 1, &params, NULL);
res = res || (ibz_cmp(&(lideal.norm), &p) != 0);
quat_lideal_norm(&lideal);
res = res || (ibz_cmp(&(lideal.norm), &p) != 0);
res = res || !ibz_mat_4x4_equal(&(O0.order.basis), &(lideal.parent_order->basis));
res = res || (0 != ibz_cmp(&(O0.order.denom), &(lideal.parent_order->denom)));
quat_lattice_mul(&test, &(lideal.lattice), &(lideal.lattice), &alg);
res = res || !quat_lattice_inclusion(&test, &(lideal.lattice));
ibz_set(&p, 3093 * 59471);
ibz_set(&coprime, 1533069337);
res = res || !quat_sampling_random_ideal_O0_given_norm(&lideal, &p, 0, &params, &coprime);
res = res || (ibz_cmp(&(lideal.norm), &p) != 0);
quat_lideal_norm(&lideal);
res = res || (ibz_cmp(&(lideal.norm), &p) != 0);
res = res || !ibz_mat_4x4_equal(&(O0.order.basis), &(lideal.parent_order->basis));
res = res || (0 != ibz_cmp(&(O0.order.denom), &(lideal.parent_order->denom)));
quat_lattice_mul(&test, &(lideal.lattice), &(lideal.lattice), &alg);
res = res || !quat_lattice_inclusion(&test, &(lideal.lattice));
if (res) {
printf("Quaternion unit test sampling_random_ideal_O0_given_norm failed\n");
}
quat_left_ideal_finalize(&lideal);
quat_alg_elem_finalize(&(O0.z));
quat_alg_elem_finalize(&(O0.t));
quat_lattice_finalize(&(O0.order));
ibz_finalize(&norm);
ibz_finalize(&p);
ibz_finalize(&coprime);
quat_lattice_finalize(&test);
quat_alg_finalize(&alg);
return (res);
}
// void quat_change_to_O0_basis(ibz_vec_4_t *vec, const quat_alg_elem_t *el);
int
quat_test_change_to_O0_basis()
{
int res = 0;
quat_alg_elem_t cmp, elem;
ibz_vec_4_t out;
quat_lattice_t O0;
quat_alg_elem_init(&elem);
quat_alg_elem_init(&cmp);
ibz_vec_4_init(&out);
quat_lattice_init(&O0);
quat_lattice_O0_set(&O0);
quat_alg_elem_set(&cmp, 2, 2, 7, 1, -4);
quat_alg_elem_copy(&elem, &cmp);
quat_change_to_O0_basis(&out, &elem);
res = res || !quat_alg_elem_equal(&elem, &cmp);
quat_alg_elem_set(&elem, 1, 0, 0, 0, 0);
ibz_mat_4x4_eval(&(elem.coord), &(O0.basis), &out);
ibz_copy(&(elem.denom), &(O0.denom));
res = res || !quat_alg_elem_equal(&elem, &cmp);
quat_alg_elem_set(&cmp, 2, 1, 0, 6, -3);
quat_alg_elem_copy(&elem, &cmp);
quat_change_to_O0_basis(&out, &elem);
res = res || !quat_alg_elem_equal(&elem, &cmp);
quat_alg_elem_set(&elem, 1, 0, 0, 0, 0);
ibz_mat_4x4_eval(&(elem.coord), &(O0.basis), &out);
ibz_copy(&(elem.denom), &(O0.denom));
res = res || !quat_alg_elem_equal(&elem, &cmp);
quat_alg_elem_set(&cmp, 1, -8, 2, 1, 3);
quat_alg_elem_copy(&elem, &cmp);
quat_change_to_O0_basis(&out, &elem);
res = res || !quat_alg_elem_equal(&elem, &cmp);
quat_alg_elem_set(&elem, 1, 0, 0, 0, 0);
ibz_mat_4x4_eval(&(elem.coord), &(O0.basis), &out);
ibz_copy(&(elem.denom), &(O0.denom));
res = res || !quat_alg_elem_equal(&elem, &cmp);
if (res) {
printf("Quaternion unit test change_to_O0_basis failed");
}
quat_alg_elem_finalize(&elem);
quat_alg_elem_finalize(&cmp);
ibz_vec_4_finalize(&out);
quat_lattice_finalize(&O0);
return (res);
}
int
quat_test_represent_integer_internal(int gamma_iter,
int prim_iter,
int nbits,
int randomized,
int standard,
int non_diag)
{
int tested = 0;
int rand_ret = 1;
int res = 0;
ibz_t p, norm_n, M, norm_d;
ibz_vec_4_t coord;
quat_alg_t Bpoo;
quat_alg_elem_t gamma;
// must initialize special extremal order, product and primes
quat_represent_integer_params_t params;
quat_p_extremal_maximal_order_t order;
quat_lattice_init(&order.order);
quat_alg_elem_init(&order.z);
quat_alg_elem_init(&order.t);
quat_alg_elem_init(&gamma);
ibz_vec_4_init(&coord);
ibz_init(&M);
ibz_init(&p);
ibz_init(&norm_d);
ibz_init(&norm_n);
quat_alg_init_set_ui(&Bpoo, 101);
// setup
params.primality_test_iterations = gamma_iter;
while (!tested) {
if (standard) {
ibz_set(&p, 0);
rand_ret = !ibz_generate_random_prime(&p, 1, 2 * nbits, 32);
rand_ret = rand_ret || !ibz_rand_interval_bits(&M, 2 * nbits + 24 + 100 * non_diag);
ibz_copy(&(Bpoo.p), &p);
quat_test_set_params_standard(&params, &order, &Bpoo);
} else {
ibz_set_from_str(&p, "23920667128620486487914848107166358953830561597426178123910317653495243603967", 10);
int real_nbits = ibz_bitsize(&p);
rand_ret = !ibz_rand_interval_bits(&M, real_nbits + 24 + 100 * non_diag);
ibz_copy(&(Bpoo.p), &p);
quat_test_set_params_non_standard(&params, &order, &Bpoo);
}
// make test more realistic by keeping some properties always given in SQIsign
tested = (ibz_get(&p) % 8 == 7);
tested = tested && (ibz_get(&M) % 2 == 1);
tested = tested && quat_test_input_for_repres_bound(&(Bpoo.p), &M, params.order->q);
if (rand_ret)
break;
}
if (!rand_ret) {
res = !quat_represent_integer(&gamma, &M, non_diag, &params);
if (!res) {
quat_alg_norm(&norm_n, &norm_d, &gamma, &Bpoo);
res = res || !ibz_is_one(&norm_d);
res = res || !(ibz_cmp(&norm_n, &M) == 0);
res = res || !quat_lattice_contains(NULL, &(params.order->order), &gamma);
if (non_diag) {
if (standard) {
ibz_mul(&norm_n, &ibz_const_two, &ibz_const_two);
// add not sub since basis in quat_order_elem_create is 1,i,j,-ij for O0
ibz_add(&norm_d, &(gamma.coord[0]), &(gamma.coord[3]));
ibz_mod(&norm_d, &norm_d, &norm_n);
res = res || (0 != ibz_cmp(&ibz_const_two, &norm_d));
ibz_sub(&norm_d, &(gamma.coord[1]), &(gamma.coord[2]));
ibz_mod(&norm_d, &norm_d, &norm_n);
res = res || (0 != ibz_cmp(&ibz_const_two, &norm_d));
} else {
quat_lattice_contains(&coord, &(params.order->order), &gamma);
ibz_gcd(&norm_d, &(coord[1]), &(coord[2]));
ibz_gcd(&norm_d, &norm_d, &(coord[3]));
res = res || ibz_is_even(&norm_d);
}
}
}
} else {
printf("Randomness failure in quat_represent_integer test\n");
}
quat_alg_finalize(&Bpoo);
ibz_finalize(&p);
ibz_finalize(&M);
ibz_finalize(&norm_n);
ibz_finalize(&norm_d);
ibz_vec_4_finalize(&coord);
quat_lattice_finalize(&order.order);
quat_alg_elem_finalize(&order.z);
quat_alg_elem_finalize(&order.t);
quat_alg_elem_finalize(&gamma);
return res | (rand_ret);
}
// int quat_represent_integer(quat_alg_elem_t *gamma, const ibz_t *n_gamma, const
// quat_represent_integer_params_t *params);
int
quat_test_represent_integer(void)
{
int res = 0;
int prim_iter = 32;
int gamma_iter = 16384;
res = res | quat_test_represent_integer_internal(prim_iter, gamma_iter, 64, 1, 1, 0);
res = res | quat_test_represent_integer_internal(prim_iter, gamma_iter, 64, 1, 1, 1);
gamma_iter = 32768;
res = res | quat_test_represent_integer_internal(prim_iter, gamma_iter, 128, 1, 1, 0);
res = res | quat_test_represent_integer_internal(prim_iter, gamma_iter, 128, 1, 1, 1);
gamma_iter = 100000;
res = res | quat_test_represent_integer_internal(prim_iter, gamma_iter, 128, 1, 0, 0);
res = res | quat_test_represent_integer_internal(prim_iter, gamma_iter, 128, 1, 0, 1);
if (res) {
printf("Quaternion unit test represent_integer failed\n");
}
return (res);
}
int
quat_test_normeq(void)
{
int res = 0;
printf("\nRunning quaternion tests of functions for special extremal orders\n");
res = res | quat_test_change_to_O0_basis();
res = res | quat_test_order_elem_create();
res = res | quat_test_sampling_random_ideal_O0_given_norm();
res = res | quat_test_represent_integer();
return res;
}

View File

@@ -1,188 +0,0 @@
/** @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,109 @@
#include "quaternion_tests.h"
#include <stdlib.h>
int
quat_test_input_random_ideal_generation(quat_left_ideal_t *ideals,
int norm_bitsize,
int iterations,
const quat_represent_integer_params_t *params)
{
int randret = 0;
ibz_t norm, cofactor;
ibz_init(&norm);
ibz_init(&cofactor);
int size_p = ibz_bitsize(&(params->algebra->p));
randret = !ibz_generate_random_prime(&cofactor,
0,
((norm_bitsize + 5) > size_p) * (norm_bitsize + 5) +
((norm_bitsize + 5) <= size_p) * size_p,
params->primality_test_iterations);
if ((randret != 0) || ibz_is_zero(&cofactor)) {
printf("Random generation failed in quat_test_input_random_ideal_generation\n");
goto fin;
}
for (int iter = 0; iter < iterations; iter++) {
// generate random odd norm
randret = !ibz_rand_interval_bits(&norm, norm_bitsize - 1);
ibz_add(&norm, &norm, &norm);
ibz_add(&norm, &norm, &ibz_const_one);
if ((randret != 0) || ibz_is_zero(&norm)) {
printf("Random generation failed in quat_test_input_random_ideal_generation\n");
goto fin;
}
ibz_abs(&norm, &norm);
// compute ideal
randret = !quat_sampling_random_ideal_O0_given_norm(&(ideals[iter]), &norm, 0, params, &cofactor);
if (randret != 0) {
printf("Random generation failed in quat_test_input_random_ideal_generation\n");
goto fin;
}
}
fin:;
ibz_finalize(&norm);
ibz_finalize(&cofactor);
return (randret);
}
// norms is either of length iterations or NULL
int
quat_test_input_random_ideal_lattice_generation(quat_lattice_t *lattices,
ibz_t *norms,
int norm_bitsize,
int iterations,
const quat_represent_integer_params_t *params)
{
quat_left_ideal_t *ideals;
ideals = malloc(iterations * sizeof(quat_left_ideal_t));
for (int i = 0; i < iterations; i++)
quat_left_ideal_init(&(ideals[i]));
int randret = quat_test_input_random_ideal_generation(ideals, norm_bitsize, iterations, params);
for (int iter = 0; iter < iterations; iter++) {
ibz_mat_4x4_copy(&(lattices[iter].basis), &(ideals[iter].lattice.basis));
ibz_copy(&(lattices[iter].denom), &(ideals[iter].lattice.denom));
if (norms != NULL) {
ibz_copy(&(norms[iter]), &(ideals[iter].norm));
}
}
for (int i = 0; i < iterations; i++)
quat_left_ideal_finalize(&(ideals[i]));
free(ideals);
return randret;
}
int
quat_test_input_random_lattice_generation(quat_lattice_t *lattices, int bitsize, int iterations, int in_hnf)
{
ibz_t det;
ibz_init(&det);
int randret = 0;
for (int iter = 0; iter < iterations; iter++) {
// generate random invertible matrix
ibz_set(&det, 0);
while (ibz_is_zero(&det)) {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
randret = !ibz_rand_interval_bits(&((lattices[iter]).basis[i][j]), bitsize);
if (randret != 0) {
printf("Random generation failed in "
"quat_test_input_random_lattice_generation\n");
goto fin;
}
}
}
randret = !ibz_rand_interval_bits(&((lattices[iter]).denom), bitsize);
if (randret != 0) {
printf("Random generation failed in quat_test_input_random_lattice_generation\n");
goto fin;
}
ibz_mat_4x4_inv_with_det_as_denom(NULL, &det, &((lattices[iter]).basis));
ibz_mul(&det, &det, &(lattices[iter].denom));
if (in_hnf && !ibz_is_zero(&det))
quat_lattice_hnf(&(lattices[iter]));
}
}
fin:;
ibz_finalize(&det);
return (randret);
}

View File

@@ -1,14 +1,14 @@
#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 ibz_mat_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 bitsize_matrix, int bitsize_modulus, int iterations)
{
int randret = 0;
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);
@@ -18,47 +18,46 @@ int quat_test_randomized_ibz_mat_2x2_inv_mod()
ibz_mat_2x2_init(&id);
ibz_mat_2x2_set(&id, 1, 0, 0, 1);
for (int iter = 0; iter < 100; iter++){
for (int iter = 0; iter < iterations; 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;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
randret = randret | !ibz_rand_interval_bits(&(a[i][j]), bitsize_matrix);
}
}
ibz_set(&m,rand_m);
randret = randret | !ibz_rand_interval_bits(&m, bitsize_modulus);
ibz_abs(&m, &m);
ibz_add(&m, &m, &ibz_const_two);
if (randret != 0)
goto fin;
// compute det
ibz_mat_2x2_det_from_ibz(&det,&(a[0][0]),&(a[0][1]),&(a[1][0]),&(a[1][1]));
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)){
ibz_gcd(&gcd, &det, &m);
if (ibz_is_one(&gcd)) {
// matrix should be invertible mod m
if (ibz_2x2_inv_mod(&inv, &a, &m))
{
if (ibz_mat_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
{
} else {
res = 1;
}
} else {
res = res || ibz_2x2_inv_mod(&inv, &a, &m);
res = res || ibz_mat_2x2_inv_mod(&inv, &a, &m);
}
}
if (res != 0)
{
fin:;
if (randret != 0) {
printf("Randomness failed in quaternion unit test with randomization for "
"ibz_mat_2x2_inv_mod\n");
}
if (res != 0) {
printf("Quaternion unit test with randomization for ibz_mat_2x2_inv_mod failed\n");
}
ibz_mat_2x2_finalize(&a);
@@ -71,94 +70,12 @@ int quat_test_randomized_ibz_mat_2x2_inv_mod()
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()
// 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 matrix_bitsize, int iterations)
{
// 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;
int randret = 0;
ibz_t det;
ibz_mat_4x4_t mat, inv;
ibz_mat_4x4_t det_id, prod;
@@ -168,22 +85,31 @@ int quat_test_randomized_ibz_mat_4x4_inv_with_det_as_denom(){
ibz_mat_4x4_init(&mat);
ibz_mat_4x4_init(&inv);
for (int r = 0; r < 10; r++) {
for (int r = 0; r < iterations; 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);
for (int j = 0; j < 4; j++) {
randret = randret | !ibz_rand_interval_bits(&mat[i][j], matrix_bitsize);
if (randret != 0)
goto fin;
}
} 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_scalar_mul(&det_id, &det, &det_id);
ibz_mat_4x4_mul(&prod, &inv, &mat);
res = res || !ibz_mat_4x4_equal(&det_id,&prod);
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);
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");
fin:;
if (randret != 0) {
printf("Randomness failed in quaternion unit test with randomization for "
"ibz_mat_2x2_inv_mod\n");
}
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);
@@ -193,545 +119,155 @@ int quat_test_randomized_ibz_mat_4x4_inv_with_det_as_denom(){
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(){
// int quat_lattice_contains(ibz_vec_4_t *coord, const quat_lattice_t *lat, const quat_alg_elem_t
// *x);
int
quat_test_randomized_lattice_contains(int lattice_bitsize, int coord_bitsize, int iterations)
{
// only tests the case where the element is in the lattice
int res = 0;
int randret = 0;
ibz_t det;
quat_alg_elem_t x, cmp;
quat_alg_coord_t coord;
ibz_vec_4_t coord, set_coord;
quat_lattice_t lat;
uint64_t rand_denom;
int64_t rand[5][4];
ibz_init(&det);
quat_alg_coord_init(&coord);
ibz_vec_4_init(&coord);
ibz_vec_4_init(&set_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));
for (int iter = 0; iter < iterations; iter++) {
randret = quat_test_input_random_lattice_generation(&lat, lattice_bitsize, 1, 1);
for (int i = 0; i < 4; i++) {
randret = randret | !ibz_rand_interval_bits(&(set_coord[i]), coord_bitsize);
}
if (randret != 0)
goto fin;
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);
ibz_mat_4x4_eval(&(x.coord), &(lat.basis), &set_coord);
ibz_copy(&(x.denom), &(lat.denom));
ibz_vec_4_set(&coord, 1, 0, 1, 0);
if (quat_lattice_contains(&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);
ibz_vec_4_sub(&coord, &coord, &set_coord);
res = res || !ibz_vec_4_is_zero(&coord);
} else {
res = 1;
}
}
if (res != 0){
printf("Quaternion unit test with randomization for lattice_contains_without_alg failed\n");
fin:;
if (randret != 0) {
printf("Randomness failed in quaternion unit test with randomization for lattice_contains\n");
}
if (res != 0) {
printf("Quaternion unit test with randomization for lattice_contains failed\n");
}
ibz_finalize(&det);
quat_alg_coord_finalize(&coord);
ibz_vec_4_finalize(&coord);
ibz_vec_4_finalize(&set_coord);
quat_alg_elem_finalize(&x);
quat_alg_elem_finalize(&cmp);
quat_lattice_finalize(&lat);
return(res);
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(){
// void quat_lattice_add(quat_lattice_t *res, const quat_lattice_t *lat1, const quat_lattice_t
// *lat2)
int
quat_test_randomized_lattice_add(int lattice_bitsize, int iterations)
{
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;
int randret = 0;
ibz_t det;
quat_lattice_t lat1, lat2, sum;
ibz_init(&det);
quat_lattice_init(&lat1);
quat_lattice_init(&lat2);
quat_lattice_init(&sum);
for (int iter = 0; iter < iterations; iter++) {
randret = quat_test_input_random_lattice_generation(&lat1, lattice_bitsize, 1, 1);
randret = randret | quat_test_input_random_lattice_generation(&lat2, lattice_bitsize, 1, 1);
if (!randret)
goto fin;
quat_lattice_add(&sum, &lat1, &lat2);
res = res | !quat_lattice_inclusion(&lat1, &sum);
res = res | !quat_lattice_inclusion(&lat2, &sum);
}
fin:;
if (randret != 0) {
printf("Randomness failed in quaternion unit test with randomization for lattice_add\n");
}
if (res != 0) {
printf("Quaternion unit test with randomization for lattice_add failed\n");
}
ibz_finalize(&det);
quat_lattice_finalize(&lat1);
quat_lattice_finalize(&lat2);
quat_lattice_finalize(&sum);
return (res);
}
// int ibz_cornacchia_prime(ibz_t *x, ibz_t *y, const ibz_t *n, const ibz_t *p);
int
quat_test_randomized_ibz_cornacchia_prime(int bitsize, int n_bound, int iterations)
{
int res = 0;
ibz_t x, y, n, prod, c_res, p;
int32_t rand_fact;
int randret = 0;
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
for (int iter = 0; iter < iterations; iter++) {
// Sample small n for cornacchia
rand_fact = 0;
while(rand_fact == 0) {
int randret = randombytes((unsigned char*)&rand_fact, sizeof(uint64_t));
while (rand_fact < 1) {
randret = randret | randombytes((unsigned char *)&rand_fact, sizeof(int32_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;
goto fin;
if (rand_fact < 0)
rand_fact = -rand_fact;
rand_fact = rand_fact % n_bound;
ibz_set(&n, rand_fact);
}
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);
randret = randret | !ibz_generate_random_prime(&p, 0, bitsize, 32);
if (randret != 0)
goto fin;
// If the legendre symbol is ok, Cornacchia should sometimes be able to solve
ibz_neg(&prod, &n);
ibz_mod(&prod, &prod, &p);
if (ibz_legendre(&prod, &p) > -1) {
// If there is output, check the output is correct
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 || (0 != ibz_cmp(&p, &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);
}
// Otherwise Cornacchia should fail
res = res || (ibz_cornacchia_prime(&x, &y, &n, &p));
}
}
fin:;
if (res != 0){
printf("Quaternion unit test with randomization for ibz_cornacchia_extended failed\n");
if (randret != 0) {
printf("Randomness failed in quaternion unit test with randomization for "
"ibz_cornacchia_prime\n");
}
if (res != 0) {
printf("Quaternion unit test with randomization for ibz_cornacchia_prime failed\n");
}
ibz_finalize(&x);
ibz_finalize(&y);
@@ -739,24 +275,19 @@ int quat_test_randomized_ibz_cornacchia_extended(){
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
quat_test_with_randomization(void)
{
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);
res = res | quat_test_randomized_ibz_mat_2x2_inv_mod(370, 270, 100);
res = res | quat_test_randomized_ibz_mat_4x4_inv_with_det_as_denom(1500, 10);
res = res | quat_test_randomized_lattice_contains(250, 250, 10);
res = res | quat_test_randomized_lattice_add(700, 100);
res = res | quat_test_randomized_ibz_cornacchia_prime(128, 6, 10);
return (res);
}

View File

@@ -1,20 +1,70 @@
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "quaternion_tests.h"
#include <rng.h>
#include <bench_test_arguments.h>
// run all tests in module
int main(){
int
main(int argc, char *argv[])
{
uint32_t seed[12] = { 0 };
int help = 0;
int seed_set = 0;
int res = 0;
for (int i = 1; i < argc; i++) {
if (!help && strcmp(argv[i], "--help") == 0) {
help = 1;
continue;
}
if (!seed_set && !parse_seed(argv[i], seed)) {
seed_set = 1;
continue;
}
}
if (help) {
printf("Usage: %s [--seed=<seed>]\n", argv[0]);
printf("Where <seed> is the random seed to be used; if not present, a random seed is "
"generated\n");
return 1;
}
if (!seed_set) {
randombytes_select((unsigned char *)seed, sizeof(seed));
}
print_seed(seed);
#if defined(TARGET_BIG_ENDIAN)
for (int i = 0; i < 12; i++) {
seed[i] = BSWAP32(seed[i]);
}
#endif
printf("Running quaternion module unit tests\n");
res = res | ibz_test_intbig();
res = res | mini_gmp_test();
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_hnf();
res = res | quat_test_algebra();
res = res | quat_test_lattice();
res = res | quat_test_lll();
res = res | quat_test_lideal();
res = res | quat_test_normeq();
res = res | quat_test_lat_ball();
res = res | quat_test_with_randomization();
if(res != 0){
if (res != 0) {
printf("\nSome tests failed!\n");
}
return(res);
return (res);
}