This commit is contained in:
Sam Hadow 2025-05-23 23:55:32 +02:00
parent c107afc342
commit 3dda3528f0
3 changed files with 175 additions and 14 deletions

151
src/deep_lll.rs Normal file
View 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 GramSchmidt 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);
}
}

View File

@ -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;

View File

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