#include #include "internal.h" //internal helpers, also for other files void ibz_vec_2_set(ibz_vec_2_t *vec, int a0, int a1){ ibz_set(&((*vec)[0]),a0); ibz_set(&((*vec)[1]),a1); } void ibz_mat_2x2_set(ibz_mat_2x2_t *mat, int a00, int a01, int a10, int a11){ ibz_set(&((*mat)[0][0]),a00); ibz_set(&((*mat)[0][1]),a01); ibz_set(&((*mat)[1][0]),a10); ibz_set(&((*mat)[1][1]),a11); } void ibz_mat_2x2_det_from_ibz(ibz_t *det, const ibz_t *a11, const ibz_t *a12, const ibz_t *a21, const ibz_t *a22){ ibz_t prod; ibz_init(&prod); ibz_mul(&prod,a12,a21); ibz_mul(det,a11,a22); ibz_sub(det,det,&prod); ibz_finalize(&prod); } void ibz_mat_2x2_eval(ibz_vec_2_t *res, const ibz_mat_2x2_t *mat, const ibz_vec_2_t *vec){ ibz_t prod; ibz_vec_2_t matvec; ibz_init(&prod); ibz_vec_2_init(&matvec); ibz_mul(&prod,&((*mat)[0][0]),&((*vec)[0])); ibz_copy(&(matvec[0]), &prod); ibz_mul(&prod,&((*mat)[0][1]),&((*vec)[1])); ibz_add(&(matvec[0]),&(matvec[0]), &prod); ibz_mul(&prod,&((*mat)[1][0]),&((*vec)[0])); ibz_copy(&(matvec[1]), &prod); ibz_mul(&prod,&((*mat)[1][1]),&((*vec)[1])); ibz_add(&(matvec[1]),&(matvec[1]), &prod); ibz_copy(&((*res)[0]), &(matvec[0])); ibz_copy(&((*res)[1]), &(matvec[1])); ibz_finalize(&prod); ibz_vec_2_finalize(&matvec); } // modular 2x2 operations void ibz_2x2_mul_mod(ibz_mat_2x2_t *prod, const ibz_mat_2x2_t *mat_a, const ibz_mat_2x2_t *mat_b, const ibz_t *m){ ibz_t mul; ibz_mat_2x2_t sums; ibz_init(&mul); ibz_mat_2x2_init(&sums); for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ ibz_set(&(sums[i][j]),0); } } for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ for(int k = 0; k < 2; k++){ ibz_mul(&mul,&((*mat_a)[i][k]), &((*mat_b)[k][j])); ibz_add(&(sums[i][j]),&(sums[i][j]), &mul); ibz_mod(&(sums[i][j]),&(sums[i][j]), m); } } } for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ ibz_copy(&((*prod)[i][j]),&(sums[i][j])); } } ibz_finalize(&mul); ibz_mat_2x2_finalize(&sums); } int ibz_2x2_inv_mod(ibz_mat_2x2_t *inv, const ibz_mat_2x2_t *mat, const ibz_t *m){ ibz_t det, prod; ibz_init(&det); ibz_init(&prod); ibz_mul(&det,&((*mat)[0][0]),&((*mat)[1][1])); ibz_mod(&det,&det,m); ibz_mul(&prod,&((*mat)[0][1]),&((*mat)[1][0])); ibz_sub(&det,&det,&prod); ibz_mod(&det,&det,m); int res = ibz_invmod(&det,&det,m); if(res){ ibz_copy(&prod,&((*mat)[0][0])); ibz_copy(&((*inv)[0][0]), &((*mat)[1][1])); ibz_copy(&((*inv)[1][1]), &prod); ibz_neg(&((*inv)[1][0]), &((*mat)[1][0])); ibz_neg(&((*inv)[0][1]), &((*mat)[0][1])); for(int i = 0; i<2;i++){ for(int j = 0; j<2;j++){ ibz_mul(&((*inv)[i][j]),&((*inv)[i][j]),&det); ibz_mod(&((*inv)[i][j]),&((*inv)[i][j]),m); } } } ibz_finalize(&det); ibz_finalize(&prod); return(res); } //helper for cvp int quat_dim2_lattice_contains(ibz_mat_2x2_t *basis, ibz_t *coord1, ibz_t *coord2){ int res = 1; ibz_t prod, sum, det, r; ibz_init(&det); ibz_init(&r); ibz_init(&sum); ibz_init(&prod); // compute det, then both coordinates (inverse*det)*vec, where vec is (coord1, coord2) and check wthether det divides both results ibz_mat_2x2_det_from_ibz(&det, &((*basis)[0][0]), &((*basis)[0][1]), &((*basis)[1][0]), &((*basis)[1][1])); ibz_mul(&sum,coord1,&((*basis)[1][1])); ibz_mul(&prod,coord2,&((*basis)[0][1])); ibz_sub(&sum,&sum,&prod); ibz_div(&prod,&r,&sum,&det); res = res && ibz_is_zero(&r); ibz_mul(&sum,coord2,&((*basis)[0][0])); ibz_mul(&prod,coord1,&((*basis)[1][0])); ibz_sub(&sum,&sum,&prod); ibz_div(&prod,&r,&sum,&det); res = res && ibz_is_zero(&r); ibz_finalize(&det); ibz_finalize(&r); ibz_finalize(&sum); ibz_finalize(&prod); return(res); } void quat_dim2_lattice_norm(ibz_t *norm, const ibz_t *coord1, const ibz_t *coord2, const ibz_t *norm_q){ ibz_t prod, sum; ibz_init(&prod); ibz_init(&sum); ibz_mul(&sum,coord1,coord1); ibz_mul(&prod,coord2,coord2); ibz_mul(&prod,&prod,norm_q); ibz_add(norm,&sum,&prod); ibz_finalize(&prod); ibz_finalize(&sum); } void quat_dim2_lattice_bilinear(ibz_t *res, const ibz_t *v11, const ibz_t *v12,const ibz_t *v21, const ibz_t *v22, const ibz_t *norm_q){ ibz_t prod, sum; ibz_init(&prod); ibz_init(&sum); ibz_mul(&sum,v11,v21); ibz_mul(&prod,v12,v22); ibz_mul(&prod,&prod,norm_q); ibz_add(res,&sum,&prod); ibz_finalize(&prod); ibz_finalize(&sum); } // algo 3.1.14 Cohen (exact solution for shortest vector in dimension 2, than take a second, orthogonal vector) void quat_dim2_lattice_short_basis(ibz_mat_2x2_t *reduced, const ibz_mat_2x2_t *basis, const ibz_t *norm_q){ ibz_vec_2_t a,b,t; ibz_t prod,sum, norm_a, norm_b, r, norm_t, n; ibz_vec_2_init(&a); ibz_vec_2_init(&b); ibz_vec_2_init(&t); ibz_init(&prod); ibz_init(&sum); ibz_init(&r); ibz_init(&n); ibz_init(&norm_t); ibz_init(&norm_a); ibz_init(&norm_b); // init a,b ibz_copy(&(a[0]),&((*basis)[0][0])); ibz_copy(&(a[1]),&((*basis)[1][0])); ibz_copy(&(b[0]),&((*basis)[0][1])); ibz_copy(&(b[1]),&((*basis)[1][1])); // compute initial norms quat_dim2_lattice_norm(&norm_a,&(a[0]),&(a[1]), norm_q); quat_dim2_lattice_norm(&norm_b,&(b[0]),&(b[1]), norm_q); // exchange if needed if(ibz_cmp(&norm_a,&norm_b)<0){ ibz_copy(&sum,&(a[0])); ibz_copy(&(a[0]),&(b[0])); ibz_copy(&(b[0]),&sum); ibz_copy(&sum,&(a[1])); ibz_copy(&(a[1]),&(b[1])); ibz_copy(&(b[1]),&sum); ibz_copy(&sum,&norm_a); ibz_copy(&norm_a,&norm_b); ibz_copy(&norm_b,&sum); } int test = 1; while(test){ //compute n quat_dim2_lattice_bilinear(&n,&(a[0]),&(a[1]),&(b[0]),&(b[1]),norm_q); // set r ibz_rounded_div(&r,&n,&norm_b); // compute t_norm ibz_set(&prod,2); ibz_mul(&prod,&prod,&n); ibz_mul(&prod,&prod,&r); ibz_sub(&sum,&norm_a,&prod); ibz_mul(&prod,&r,&r); ibz_mul(&prod,&prod,&norm_b); ibz_add(&norm_t,&sum,&prod); // test: if(ibz_cmp(&norm_b,&norm_t)>0){ // compute t, a, b ibz_copy(&norm_a,&norm_b); ibz_copy(&norm_b,&norm_t); // t is a -rb, a is b, b is t ibz_mul(&prod,&r,&(b[0])); ibz_sub(&(t[0]),&(a[0]),&prod); ibz_mul(&prod,&r,&(b[1])); ibz_sub(&(t[1]),&(a[1]),&prod); ibz_copy(&(a[0]),&(b[0])); ibz_copy(&(a[1]),&(b[1])); ibz_copy(&(b[0]),&(t[0])); ibz_copy(&(b[1]),&(t[1])); } else { test = 0; } } // output : now b is short: need to get 2nd short vector: idea: take shortest among t and a if(ibz_cmp(&norm_t,&norm_a)<0){ ibz_mul(&prod,&r,&(b[0])); ibz_sub(&(a[0]),&(a[0]),&prod); ibz_mul(&prod,&r,&(b[1])); ibz_sub(&(a[1]),&(a[1]),&prod); } ibz_copy(&((*reduced)[0][0]),&(b[0])); ibz_copy(&((*reduced)[1][0]),&(b[1])); ibz_copy(&((*reduced)[0][1]),&(a[0])); ibz_copy(&((*reduced)[1][1]),&(a[1])); ibz_finalize(&prod); ibz_finalize(&sum); ibz_finalize(&norm_a); ibz_finalize(&norm_b); ibz_finalize(&norm_t); ibz_vec_2_finalize(&a); ibz_vec_2_finalize(&b); ibz_vec_2_finalize(&t); ibz_finalize(&r); ibz_finalize(&n); } // compute the rounded value of /, 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