1use nalgebra::{DMatrix, DVector};
19
20pub const PSI: f64 = 0.618;
22const S_PSI: f64 = 1.0 - PSI;
23
24#[derive(Debug, Clone, Default, PartialEq, PartialOrd)]
26pub struct AdloVector {
27 pub coords: DVector<i64>,
28 pub f64_coords: DVector<f64>,
29 pub norm_sq: i128,
30}
31
32impl AdloVector {
33 pub fn new(coords: DVector<i64>) -> Self {
35 let f64_coords = coords.clone().cast::<f64>();
36 let norm_sq = Self::calculate_norm_sq(&coords);
37 AdloVector {
38 coords,
39 f64_coords,
40 norm_sq,
41 }
42 }
43 fn calculate_norm_sq(coords: &DVector<i64>) -> i128 {
44 let mut sum: i128 = 0;
45 for &coord in coords.iter() {
46 let coord_i128 = coord as i128;
47 sum += coord_i128 * coord_i128;
48 }
49 sum
50 }
51 fn norm_squared(&self) -> i128 {
52 self.norm_sq
53 }
54 pub fn norm(&self) -> f64 {
55 (self.norm_squared() as f64).sqrt()
56 }
57 pub fn set_f64_coords(&mut self) {
58 self.f64_coords = self.coords.clone().cast::<f64>();
59 }
60}
61
62fn calculate_local_density(basis: &[AdloVector], k: usize) -> f64 {
63 let n = basis.len();
64 let mut count = 0;
65 let radius = basis[k].norm() * 1.5;
67
68 for i in 0..n {
69 if i != k {
70 let dist = basis[k].norm() - basis[i].norm();
71 if dist <= radius {
72 count += 1;
73 }
74 }
75 }
76 (count as f64) / (n as f64 - 1.0)
78}
79
80fn calculate_lovasz_factor(density: f64) -> f64 {
81 PSI + S_PSI * density.min(1.0) }
85
86pub fn adaptive_lll(basis: &mut [AdloVector]) {
102 let n = basis.len();
103 let mut b_star: Vec<AdloVector> = Vec::with_capacity(n);
104 let mut mu: DMatrix<f64> = DMatrix::from_element(n, n, 0.0);
105
106 for i in 0..n {
108 b_star.push(basis[i].clone());
109 for j in 0..i {
110 mu[(i, j)] = b_star[i].f64_coords.dot(&b_star[j].f64_coords) / b_star[j].norm().powi(2);
111 let mut new_coords_f64 = DVector::<f64>::zeros(n);
113 for dim in 0..n {
114 let mu_val = mu[(i, j)];
115 let b_star_j_coord = b_star[j].f64_coords[dim];
116 let intermediate_val = (mu_val * b_star_j_coord) as f64; new_coords_f64[dim] = intermediate_val;
118 }
119 b_star[i].f64_coords -= new_coords_f64.clone();
120 b_star[i].coords = new_coords_f64.map(|x| x.round() as i64);
121 }
122 }
123
124 let mut k = 1;
125 while k < n {
126 for i in (0..k).rev() {
128 let r_f64 = mu[(k, i)];
129 let r_i128 = r_f64.round() as i128; let mut change_basis = DVector::<i64>::zeros(n);
131
132 for dim in 0..n {
133 change_basis[dim] = (r_i128 * basis[i].coords[dim] as i128).try_into().unwrap(); }
135
136 basis[k].coords -= change_basis;
137 basis[k].set_f64_coords();
138
139 for l in 0..=i {
140 mu[(k, l)] -= r_f64 * mu[(i, l)];
141 }
142 }
143 let local_density = calculate_local_density(basis, k);
145 let lovasz_factor = calculate_lovasz_factor(local_density);
146 if b_star[k].norm().powi(2) + (mu[(k, k - 1)].round() as f64 * b_star[k - 1].norm().powi(2))
148 < lovasz_factor * b_star[k - 1].norm().powi(2)
149 {
150 basis.swap(k - 1, k);
151 b_star.clear();
153 mu = DMatrix::zeros(n, n);
154 for i in 0..n {
155 b_star.push(basis[i].clone());
156 for j in 0..i {
157 mu[(i, j)] =
158 b_star[i].f64_coords.dot(&b_star[j].f64_coords) / b_star[j].norm().powi(2);
159 let new_coords_f64 = mu[(i, j)] * &b_star[j].f64_coords;
160 b_star[i].f64_coords -= new_coords_f64.clone();
161 b_star[i].coords = new_coords_f64.map(|x| x.round() as i64);
162 }
163 }
164 k = 1;
165 } else {
166 k += 1;
167 }
168 }
169}
170
171pub fn create_rotation_matrix(n: usize, i: usize, j: usize, theta: f64) -> DMatrix<f64> {
172 let mut matrix = DMatrix::identity(n, n);
173 let cos_theta = theta.cos();
174 let sin_theta = theta.sin();
175
176 matrix[(i, i)] = cos_theta;
177 matrix[(j, j)] = cos_theta;
178 matrix[(i, j)] = -sin_theta;
179 matrix[(j, i)] = sin_theta;
180
181 matrix
182}
183
184pub fn rotate_vector(v: &DVector<f64>, i: usize, j: usize, theta: f64) -> DVector<f64> {
185 let n = v.len();
186 let rotation_matrix = create_rotation_matrix(n, i, j, theta);
187 rotation_matrix * v
188}
189
190pub fn multi_frame_search(basis: &mut Vec<AdloVector>) {
194 let mut best_basis = basis.to_vec();
195 let mut best_norm = best_basis[0].norm();
196 let n = basis.len();
197 for angle in (0..360).step_by(45) {
199 let angle_rad = (angle as f64).to_radians();
200 for i in 0..n {
202 for j in i + 1..n {
203 let mut rotated_basis = basis.to_vec();
205 for vec in &mut rotated_basis {
206 let rotated_f64_coords = rotate_vector(&vec.f64_coords, i, j, angle_rad);
208 let rotated_i64_coords = rotated_f64_coords.map(|x| x.round() as i64);
209 vec.f64_coords = rotated_f64_coords;
210 vec.coords = rotated_i64_coords;
211 }
212 adaptive_lll(&mut rotated_basis);
213 if rotated_basis[0].norm() < best_norm {
214 best_norm = rotated_basis[0].norm();
215 best_basis = rotated_basis;
216 }
217 }
218 }
219 }
220 *basis = best_basis;
221}
222
223#[cfg(test)]
226mod tests {
227
228 use super::*;
229
230 #[test]
231 fn basis_2d_test() {
232 let mut basis_2d = vec![
233 AdloVector::new(DVector::from_vec(vec![5, 3])),
234 AdloVector::new(DVector::from_vec(vec![2, 2])),
235 ];
236 multi_frame_search(&mut basis_2d);
237 let mut expected = AdloVector::new(DVector::from_vec(vec![1, -1]));
238 expected.norm_sq = 34;
239 assert_eq!(expected, basis_2d[basis_2d.len() - 1]);
240 }
241
242 #[test]
243 fn basis_5d_test() {
244 let mut basis_5d = vec![
245 AdloVector::new(DVector::from_vec(vec![1, 1, 1])),
246 AdloVector::new(DVector::from_vec(vec![-1, 0, 2])),
247 AdloVector::new(DVector::from_vec(vec![3, 5, 6])),
248 ];
249 multi_frame_search(&mut basis_5d);
250 let mut expected = AdloVector::new(DVector::from_vec(vec![-1, 0, 2]));
251 expected.norm_sq = 5;
252 assert_eq!(expected, basis_5d[basis_5d.len() - 2]);
253 }
254}