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 bkz;
|
||||
mod deep_lll;
|
||||
mod file;
|
||||
mod lll;
|
||||
mod matrix;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::vector::IntVector;
|
||||
use rug::ops::Pow;
|
||||
use rug::Integer;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use crate::vector::IntVector;
|
||||
|
||||
macro_rules! int {
|
||||
($x:expr) => {
|
||||
@ -9,11 +9,11 @@ macro_rules! int {
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Matrix {
|
||||
pub n: usize, // rows
|
||||
pub m: usize, // columns
|
||||
columns: Vec<IntVector>,
|
||||
pub n: usize, // number of columns
|
||||
pub m: usize, // number of rows
|
||||
pub columns: Vec<IntVector>,
|
||||
}
|
||||
|
||||
impl Matrix {
|
||||
@ -43,11 +43,11 @@ impl Matrix {
|
||||
}
|
||||
|
||||
for i in 1..n {
|
||||
for j in 0..n {
|
||||
for (j, column) in columns.iter_mut().enumerate().take(n) {
|
||||
if i == j {
|
||||
columns[j].push(ciphertexts[0].clone());
|
||||
column.push(ciphertexts[0].clone());
|
||||
} 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[(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)]");
|
||||
|
||||
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)]");
|
||||
}
|
||||
|
||||
@ -138,9 +142,15 @@ mod tests {
|
||||
// 2nd column = [ciphertexts[1], ciphertexts[0], 0] = [8,5,0]
|
||||
// 3rd column = [ciphertexts[2], 0, ciphertexts[0]] = [12,0,5]
|
||||
let expected_flat = vec![
|
||||
int!(8), int!(8), int!(12),
|
||||
int!(0), int!(5), int!(0),
|
||||
int!(0), int!(0), int!(5),
|
||||
int!(8),
|
||||
int!(8),
|
||||
int!(12),
|
||||
int!(0),
|
||||
int!(5),
|
||||
int!(0),
|
||||
int!(0),
|
||||
int!(0),
|
||||
int!(5),
|
||||
];
|
||||
|
||||
let mut actual_flat = Vec::with_capacity(9);
|
||||
@ -153,4 +163,3 @@ mod tests {
|
||||
assert_eq!(actual_flat, expected_flat);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user