deep lll
This commit is contained in:
parent
c107afc342
commit
3dda3528f0
151
src/deep_lll.rs
Normal file
151
src/deep_lll.rs
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
use crate::matrix::Matrix;
|
||||||
|
use rug::{Integer, Rational};
|
||||||
|
|
||||||
|
/// Perform DeepLLL reduction on a given lattice basis represented by Matrix.
|
||||||
|
/// 1/4 < delta < 1.
|
||||||
|
pub fn deep_lll(mut mat: Matrix, delta: Rational) -> Option<Matrix> {
|
||||||
|
let n = mat.n;
|
||||||
|
let (mut mu, mut b_star_sq) = gramm_schmidt(&mat);
|
||||||
|
let mut k = 2;
|
||||||
|
|
||||||
|
while k <= n {
|
||||||
|
size_reduce(&mut mat, &mut mu, &mut b_star_sq, k);
|
||||||
|
let mut c = norm_sq(&mat, k);
|
||||||
|
let mut i = 1;
|
||||||
|
while i < k {
|
||||||
|
if c >= delta.clone() * b_star_sq[i - 1].clone() {
|
||||||
|
let mu_ki = mu[k - 1][i - 1].clone();
|
||||||
|
c -= mu_ki.clone() * mu_ki * b_star_sq[i - 1].clone();
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
deep_insert(&mut mat, i, k);
|
||||||
|
let (new_mu, new_bs) = gramm_schmidt(&mat);
|
||||||
|
mu = new_mu;
|
||||||
|
b_star_sq = new_bs;
|
||||||
|
k = if i > 2 { i } else { 2 } - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(mat)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute Gram–Schmidt coefficients and squared norms of orthogonal vectors b*_i as Rationals.
|
||||||
|
fn gramm_schmidt(mat: &Matrix) -> (Vec<Vec<Rational>>, Vec<Rational>) {
|
||||||
|
let n = mat.n;
|
||||||
|
let m = mat.m;
|
||||||
|
let mut mu = vec![vec![Rational::from((0, 1)); n]; n];
|
||||||
|
let mut b_star = vec![vec![Rational::from((0, 1)); m]; n];
|
||||||
|
let mut b_star_sq = vec![Rational::from((0, 1)); n];
|
||||||
|
|
||||||
|
for i in 0..n {
|
||||||
|
for j in 0..m {
|
||||||
|
b_star[i][j] = Rational::from(mat[(i, j)].clone());
|
||||||
|
}
|
||||||
|
for j in 0..i {
|
||||||
|
let mut numer = Rational::from((0, 1));
|
||||||
|
for k in 0..m {
|
||||||
|
let prod = b_star[j][k].clone() * Rational::from(mat[(i, k)].clone());
|
||||||
|
numer += prod;
|
||||||
|
}
|
||||||
|
mu[i][j] = numer.clone() / b_star_sq[j].clone();
|
||||||
|
for k in 0..m {
|
||||||
|
let tmp = mu[i][j].clone() * b_star[j][k].clone();
|
||||||
|
b_star[i][k] -= tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut sum = Rational::from((0, 1));
|
||||||
|
for k in 0..m {
|
||||||
|
let sq = b_star[i][k].clone() * b_star[i][k].clone();
|
||||||
|
sum += sq;
|
||||||
|
}
|
||||||
|
b_star_sq[i] = sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
(mu, b_star_sq)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Size-reduce column k in-place
|
||||||
|
fn size_reduce(mat: &mut Matrix, mu: &mut [Vec<Rational>], b_star_sq: &mut [Rational], k: usize) {
|
||||||
|
let mut updated = true;
|
||||||
|
while updated {
|
||||||
|
updated = false;
|
||||||
|
for j in (1..k).rev() {
|
||||||
|
let mu_kj = mu[k - 1][j - 1].clone();
|
||||||
|
let q = mu_kj.round();
|
||||||
|
if q != 0 {
|
||||||
|
for row in 0..mat.n {
|
||||||
|
let t = mat[(k - 1, row)].clone();
|
||||||
|
let prod = &mat[(j - 1, row)] * q.clone();
|
||||||
|
mat[(k - 1, row)] = (t - prod).round_ref().into();
|
||||||
|
}
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if updated {
|
||||||
|
let (new_mu, new_bs) = gramm_schmidt(mat);
|
||||||
|
mu.clone_from_slice(&new_mu);
|
||||||
|
b_star_sq.clone_from_slice(&new_bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deep insertion: move column k into position i (1-based), shifting intermediate columns right.
|
||||||
|
fn deep_insert(mat: &mut Matrix, i: usize, k: usize) {
|
||||||
|
let col = mat.columns.remove(k - 1);
|
||||||
|
mat.columns.insert(i - 1, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute squared Euclidean norm of column k as a Rational.
|
||||||
|
fn norm_sq(mat: &Matrix, k: usize) -> Rational {
|
||||||
|
let mut sum = Rational::from((0, 1));
|
||||||
|
for row in 0..mat.n {
|
||||||
|
let v = mat[(k - 1, row)].clone();
|
||||||
|
sum += Rational::from(v.clone() * v);
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod deep_lll_tests {
|
||||||
|
use super::*;
|
||||||
|
use rug::{Integer, Rational};
|
||||||
|
#[test]
|
||||||
|
fn test_identity_basis() {
|
||||||
|
let values = vec![
|
||||||
|
Integer::from(1),
|
||||||
|
Integer::from(0),
|
||||||
|
Integer::from(0),
|
||||||
|
Integer::from(1),
|
||||||
|
];
|
||||||
|
let mat = Matrix::new(2, 2, values.clone()).unwrap();
|
||||||
|
let delta = Rational::from((3, 4));
|
||||||
|
let reduced = deep_lll(mat.clone(), delta).unwrap();
|
||||||
|
for col in 0..2 {
|
||||||
|
for row in 0..2 {
|
||||||
|
assert_eq!(mat[(col, row)], reduced[(col, row)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_determinant_preservation() {
|
||||||
|
let values = vec![
|
||||||
|
Integer::from(4),
|
||||||
|
Integer::from(1),
|
||||||
|
Integer::from(1),
|
||||||
|
Integer::from(3),
|
||||||
|
];
|
||||||
|
let mat = Matrix::new(2, 2, values).unwrap();
|
||||||
|
let delta = Rational::from((51, 100));
|
||||||
|
let reduced = deep_lll(mat.clone(), delta).unwrap();
|
||||||
|
let det_orig =
|
||||||
|
mat[(0, 0)].clone() * mat[(1, 1)].clone() - mat[(1, 0)].clone() * mat[(0, 1)].clone();
|
||||||
|
let det_red = reduced[(0, 0)].clone() * reduced[(1, 1)].clone()
|
||||||
|
- reduced[(1, 0)].clone() * reduced[(0, 1)].clone();
|
||||||
|
assert_eq!(det_orig, det_red);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
mod agcd;
|
mod agcd;
|
||||||
mod bkz;
|
mod bkz;
|
||||||
|
mod deep_lll;
|
||||||
mod file;
|
mod file;
|
||||||
mod lll;
|
mod lll;
|
||||||
mod matrix;
|
mod matrix;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
use crate::vector::IntVector;
|
||||||
use rug::ops::Pow;
|
use rug::ops::Pow;
|
||||||
use rug::Integer;
|
use rug::Integer;
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
use crate::vector::IntVector;
|
|
||||||
|
|
||||||
macro_rules! int {
|
macro_rules! int {
|
||||||
($x:expr) => {
|
($x:expr) => {
|
||||||
@ -9,11 +9,11 @@ macro_rules! int {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct Matrix {
|
pub struct Matrix {
|
||||||
pub n: usize, // rows
|
pub n: usize, // number of columns
|
||||||
pub m: usize, // columns
|
pub m: usize, // number of rows
|
||||||
columns: Vec<IntVector>,
|
pub columns: Vec<IntVector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Matrix {
|
impl Matrix {
|
||||||
@ -43,11 +43,11 @@ impl Matrix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i in 1..n {
|
for i in 1..n {
|
||||||
for j in 0..n {
|
for (j, column) in columns.iter_mut().enumerate().take(n) {
|
||||||
if i == j {
|
if i == j {
|
||||||
columns[j].push(ciphertexts[0].clone());
|
column.push(ciphertexts[0].clone());
|
||||||
} else {
|
} else {
|
||||||
columns[j].push(int!(0));
|
column.push(int!(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,10 +118,14 @@ mod tests {
|
|||||||
assert_eq!(m2[(0, 2)], int!(3));
|
assert_eq!(m2[(0, 2)], int!(3));
|
||||||
assert_eq!(m2[(1, 0)], int!(4));
|
assert_eq!(m2[(1, 0)], int!(4));
|
||||||
|
|
||||||
let result = panic::catch_unwind(|| { let _ = m2[(2, 0)]; });
|
let result = panic::catch_unwind(|| {
|
||||||
|
let _ = m2[(2, 0)];
|
||||||
|
});
|
||||||
assert!(result.is_err(), "Expected panic on m2[(2, 0)]");
|
assert!(result.is_err(), "Expected panic on m2[(2, 0)]");
|
||||||
|
|
||||||
let result2 = panic::catch_unwind(|| { let _ = m2[(0, 3)]; });
|
let result2 = panic::catch_unwind(|| {
|
||||||
|
let _ = m2[(0, 3)];
|
||||||
|
});
|
||||||
assert!(result2.is_err(), "Expected panic on m2[(0, 3)]");
|
assert!(result2.is_err(), "Expected panic on m2[(0, 3)]");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,9 +142,15 @@ mod tests {
|
|||||||
// 2nd column = [ciphertexts[1], ciphertexts[0], 0] = [8,5,0]
|
// 2nd column = [ciphertexts[1], ciphertexts[0], 0] = [8,5,0]
|
||||||
// 3rd column = [ciphertexts[2], 0, ciphertexts[0]] = [12,0,5]
|
// 3rd column = [ciphertexts[2], 0, ciphertexts[0]] = [12,0,5]
|
||||||
let expected_flat = vec![
|
let expected_flat = vec![
|
||||||
int!(8), int!(8), int!(12),
|
int!(8),
|
||||||
int!(0), int!(5), int!(0),
|
int!(8),
|
||||||
int!(0), int!(0), int!(5),
|
int!(12),
|
||||||
|
int!(0),
|
||||||
|
int!(5),
|
||||||
|
int!(0),
|
||||||
|
int!(0),
|
||||||
|
int!(0),
|
||||||
|
int!(5),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut actual_flat = Vec::with_capacity(9);
|
let mut actual_flat = Vec::with_capacity(9);
|
||||||
@ -153,4 +163,3 @@ mod tests {
|
|||||||
assert_eq!(actual_flat, expected_flat);
|
assert_eq!(actual_flat, expected_flat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user