deep lll
This commit is contained in:
		
							
								
								
									
										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); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user