1use crate::algorithms::matmul::MatmulAlgorithm;
2use crate::algorithms::matmul::STANDARD_MATMUL;
3use crate::field::*;
4use crate::integer::*;
5use crate::homomorphism::*;
6use crate::matrix::*;
7use crate::matrix::transform::TransformTarget;
8use crate::primitive_int::*;
9use crate::rings::float_real::Real64;
10use crate::rings::float_real::Real64Base;
11use crate::rings::rational::*;
12use crate::ordered::{OrderedRing, OrderedRingStore};
13use crate::ring::*;
14
15use std::alloc::Allocator;
16use std::cmp::max;
17use std::marker::PhantomData;
18
19#[stability::unstable(feature = "enable")]
27pub trait LLLRealField<I>: OrderedRing + Field
28 where I: ?Sized + IntegerRing
29{
30 fn from_integer(&self, x: I::Element, ZZ: &I) -> Self::Element;
31 fn round_to_integer(&self, x: &Self::Element, ZZ: &I) -> I::Element;
32}
33
34impl<I, J> LLLRealField<J> for RationalFieldBase<I>
35 where I: IntegerRingStore,
36 I::Type: IntegerRing,
37 J: ?Sized + IntegerRing
38{
39 fn from_integer(&self, x: J::Element, ZZ: &J) -> Self::Element {
40 RingRef::new(self).inclusion().map(int_cast(x, self.base_ring(), RingRef::new(ZZ)))
41 }
42
43 fn round_to_integer(&self, x: &Self::Element, ZZ: &J) -> J::Element {
44 int_cast(self.base_ring().rounded_div(self.base_ring().clone_el(self.num(x)), self.den(x)), RingRef::new(ZZ), self.base_ring())
45 }
46}
47
48impl<J> LLLRealField<J> for Real64Base
49 where J: ?Sized + IntegerRing
50{
51 fn from_integer(&self, x: J::Element, ZZ: &J) -> Self::Element {
52 ZZ.to_float_approx(&x)
53 }
54
55 fn round_to_integer(&self, x: &Self::Element, ZZ: &J) -> J::Element {
56 int_cast(x.round() as i64, RingRef::new(ZZ), StaticRing::<i64>::RING)
57 }
58}
59
60fn size_reduce<R, I, V, T>(ring: R, int_ring: I, mut target: SubmatrixMut<V, El<R>>, target_j: usize, matrix: Submatrix<V, El<R>>, col_ops: &mut T)
61 where R: RingStore,
62 R::Type: LLLRealField<I::Type>,
63 I: IntegerRingStore + Copy,
64 I::Type: IntegerRing,
65 V: AsPointerToSlice<El<R>>,
66 T: TransformTarget<I::Type>
67{
68 for j in (0..matrix.col_count()).rev() {
69 let factor = ring.get_ring().round_to_integer(target.as_const().at(j, 0), int_ring.get_ring());
70 col_ops.subtract(int_ring, j, target_j, &factor);
71 let factor = ring.get_ring().from_integer(factor, int_ring.get_ring());
72 ring.sub_assign_ref(target.at_mut(j, 0), &factor);
73 for k in 0..j {
74 ring.sub_assign(target.at_mut(k, 0), ring.mul_ref(matrix.at(k, j), &factor));
75 }
76 }
77}
78
79fn swap_gso_cols<R, V>(ring: R, mut gso: SubmatrixMut<V, El<R>>, i: usize, j: usize)
97 where R: RingStore,
98 R::Type: OrderedRing + Field,
99 V: AsPointerToSlice<El<R>>
100{
101 assert!(j == i + 1);
102
103 let col_count = gso.col_count();
104
105 let (mut col_i, mut col_i1) = gso.reborrow().restrict_cols(i..(i + 2)).split_cols(0..1, 1..2);
107 for k in 0..i {
108 std::mem::swap(col_i.at_mut(k, 0), col_i1.at_mut(k, 0));
109 }
110
111 let bi_star_norm_sqr = ring.clone_el(gso.at(i, i));
115 let bi1_star_norm_sqr = ring.clone_el(gso.at(i + 1, i + 1));
117 let mu = ring.clone_el(gso.at(i, i + 1));
119 let mu_sqr = ring.pow(ring.clone_el(&mu), 2);
120
121 let new_bi_star_norm_sqr = ring.add_ref_fst(&bi1_star_norm_sqr, ring.mul_ref(&mu_sqr, &bi_star_norm_sqr));
122 let gamma_sqr = ring.div(&bi_star_norm_sqr, &new_bi_star_norm_sqr);
124 let new_bi1_star_norm_sqr = ring.mul_ref(&gamma_sqr, &bi1_star_norm_sqr);
125 let new_mu = ring.mul_ref(&gamma_sqr, &mu);
126
127 let lin_transform_muki = [ring.mul_ref(&gamma_sqr, &mu), ring.sub(ring.one(), ring.mul_ref(&gamma_sqr, &mu_sqr))];
129 let (mut row_i, mut row_i1) = gso.reborrow().restrict_rows(i..(i + 2)).split_rows(0..1, 1..2);
130 for k in (i + 2)..col_count {
131 let mu_ki = ring.clone_el(row_i.at(0, k));
132 std::mem::swap(row_i.at_mut(0, k), row_i1.at_mut(0, k));
133 ring.sub_assign(row_i1.at_mut(0, k), ring.mul_ref(&mu, row_i.at(0, k)));
134 ring.mul_assign_ref(row_i.at_mut(0, k), &lin_transform_muki[1]);
135 ring.add_assign(row_i.at_mut(0, k), ring.mul_ref_fst(&lin_transform_muki[0], mu_ki));
136 }
137
138 *gso.at_mut(i, i) = new_bi_star_norm_sqr;
139 *gso.at_mut(i, i + 1) = new_mu;
140 *gso.at_mut(i + 1, i + 1) = new_bi1_star_norm_sqr;
141}
142
143fn lll_base<R, I, V, T>(ring: R, int_ring: I, mut gso: SubmatrixMut<V, El<R>>, mut col_ops: T, delta: &El<R>)
148 where R: RingStore + Copy,
149 R::Type: LLLRealField<I::Type>,
150 I: IntegerRingStore + Copy,
151 I::Type: IntegerRing,
152 V: AsPointerToSlice<El<R>>,
153 T: TransformTarget<I::Type>
154{
155 let mut i = 0;
156 while i + 1 < gso.col_count() {
157 let (target, matrix) = gso.reborrow().split_cols((i + 1)..(i + 2), 0..(i + 1));
158 size_reduce(ring, int_ring, target, i + 1, matrix.as_const(), &mut col_ops);
159 if ring.is_gt(
160 &ring.mul_ref_snd(
161 ring.sub_ref_fst(delta, ring.mul_ref(gso.as_const().at(i, i + 1), gso.as_const().at(i, i + 1))),
162 gso.as_const().at(i, i)
163 ),
164 gso.as_const().at(i + 1, i + 1)
165 ) {
166 col_ops.swap(int_ring, i, i + 1);
167 swap_gso_cols(ring, gso.reborrow(), i, i + 1);
168 i = max(i, 1) - 1;
169 } else {
170 i += 1;
171 }
172 }
173}
174
175fn ldl<R, V>(ring: R, mut matrix: SubmatrixMut<V, El<R>>)
186 where R: RingStore,
187 R::Type: Field,
188 V: AsPointerToSlice<El<R>>
189{
190 assert_eq!(matrix.row_count(), matrix.col_count());
192 let n = matrix.row_count();
193 for i in 0..n {
194 let pivot = ring.clone_el(matrix.at(i, i));
195 let pivot_inv = ring.div(&ring.one(), matrix.at(i, i));
196 for j in (i + 1)..n {
197 ring.mul_assign_ref(matrix.at_mut(i, j), &pivot_inv);
198 }
199 for k in (i + 1)..n {
200 for l in k..n {
201 let subtract = ring.mul_ref_snd(ring.mul_ref(matrix.as_const().at(i, k), matrix.as_const().at(i, l)), &pivot);
202 ring.sub_assign(matrix.at_mut(k, l), subtract);
203 }
204 }
205 }
206}
207
208#[stability::unstable(feature = "enable")]
229pub fn lll_float<I, V1, V2, A>(ring: I, quadratic_form: Submatrix<V1, El<Real64>>, matrix: SubmatrixMut<V2, El<I>>, delta: f64, allocator: A)
230 where I: IntegerRingStore,
231 I::Type: IntegerRing,
232 V1: AsPointerToSlice<El<Real64>>,
233 V2: AsPointerToSlice<El<I>>,
234 A: Allocator
235{
236 assert!(delta < 1.);
237 assert!(delta > 0.25);
238 assert_eq!(quadratic_form.row_count(), quadratic_form.col_count());
239 assert_eq!(matrix.col_count(), quadratic_form.col_count());
240
241 let lll_reals = Real64::RING;
242 let hom = lll_reals.can_hom(&ring).unwrap();
243
244 let n = matrix.col_count();
245 let mut gso: OwnedMatrix<f64, &A> = OwnedMatrix::zero_in(n, n, &lll_reals, &allocator);
246 let mut tmp: OwnedMatrix<f64, &A> = OwnedMatrix::zero_in(n, n, &lll_reals, &allocator);
247 let matrix_RR: OwnedMatrix<f64, &A> = OwnedMatrix::from_fn_in(matrix.row_count(), matrix.col_count(), |i, j| hom.map_ref(matrix.at(i, j)), &allocator);
248 STANDARD_MATMUL.matmul(TransposableSubmatrix::from(matrix_RR.data()).transpose(), TransposableSubmatrix::from(quadratic_form), TransposableSubmatrixMut::from(tmp.data_mut()), lll_reals);
249 STANDARD_MATMUL.matmul(TransposableSubmatrix::from(tmp.data()), TransposableSubmatrix::from(matrix_RR.data()), TransposableSubmatrixMut::from(gso.data_mut()), lll_reals);
250
251 ldl(&lll_reals, gso.data_mut());
252 lll_base::<_, _, _, TransformLatticeBasis<I::Type, I::Type, _, _>>(&lll_reals, &ring, gso.data_mut(), TransformLatticeBasis { basis: matrix, hom: ring.identity(), int_ring: PhantomData }, &delta);
253}
254
255#[stability::unstable(feature = "enable")]
278pub fn lll_exact<I, V, A>(ring: I, mut matrix: SubmatrixMut<V, El<I>>, delta: f64, allocator: A)
279 where I: IntegerRingStore,
280 I::Type: IntegerRing,
281 V: AsPointerToSlice<El<I>>,
282 A: Allocator
283{
284 assert!(delta < 1.);
285 assert!(delta > 0.25);
286 lll_float(&ring, OwnedMatrix::<f64>::identity(matrix.col_count(), matrix.col_count(), Real64::RING).data(), matrix.reborrow(), delta, &allocator);
287
288 let lll_reals = RationalField::new(BigIntRing::RING);
289 let delta_int = ring.from_float_approx(delta * 2f64.powi(20)).unwrap();
290 let delta = lll_reals.div(&lll_reals.get_ring().from_integer(delta_int, ring.get_ring()), &lll_reals.get_ring().from_integer(ring.power_of_two(20), ring.get_ring()));
291
292 let n = matrix.col_count();
293 let mut gso = OwnedMatrix::zero_in(n, n, &lll_reals, &allocator);
294 let hom = lll_reals.inclusion().compose(BigIntRing::RING.can_hom(&ring).unwrap());
295 let matrix_RR: OwnedMatrix<_, &A> = OwnedMatrix::from_fn_in(matrix.row_count(), matrix.col_count(), |i, j| hom.map_ref(matrix.at(i, j)), &allocator);
296 STANDARD_MATMUL.matmul(TransposableSubmatrix::from(matrix_RR.data()).transpose(), TransposableSubmatrix::from(matrix_RR.data()), TransposableSubmatrixMut::from(gso.data_mut()), &lll_reals);
297
298 ldl(&lll_reals, gso.data_mut());
299 lll_base::<_, _, _, TransformLatticeBasis<I::Type, I::Type, _, _>>(&lll_reals, &ring, gso.data_mut(), TransformLatticeBasis { basis: matrix, hom: ring.identity(), int_ring: PhantomData }, &delta);
300}
301
302struct TransformLatticeBasis<'a, R, I, V, H>
303 where R: ?Sized + RingBase,
304 I: ?Sized + IntegerRing,
305 H: Homomorphism<I, R>,
306 V: AsPointerToSlice<R::Element>
307{
308 basis: SubmatrixMut<'a, V, R::Element>,
309 int_ring: PhantomData<I>,
310 hom: H
311}
312
313impl<'a, R, I, V, H> TransformTarget<I> for TransformLatticeBasis<'a, R, I, V, H>
314 where R: ?Sized + RingBase,
315 I: ?Sized + IntegerRing,
316 H: Homomorphism<I, R>,
317 V: AsPointerToSlice<R::Element>
318{
319 fn transform<S: Copy + RingStore<Type = I>>(&mut self, ring: S, i: usize, j: usize, transform: &[I::Element; 4]) {
320 assert!(ring.get_ring() == self.hom.domain().get_ring());
321 assert!(i != j);
322 let ring = self.hom.codomain();
323 for k in 0..self.basis.row_count() {
324 let a = ring.clone_el(self.basis.at(k, i));
325 let b = ring.clone_el(self.basis.at(k, j));
326 *self.basis.at_mut(k, i) = ring.add(self.hom.mul_ref_map(&a, &transform[0]), self.hom.mul_ref_map(&b, &transform[1]));
327 *self.basis.at_mut(k, i) = ring.add(self.hom.mul_ref_snd_map(a, &transform[2]), self.hom.mul_ref_snd_map(b, &transform[3]));
328 }
329 }
330
331 fn subtract<S: Copy + RingStore<Type = I>>(&mut self, ring: S, src: usize, dst: usize, factor: &I::Element) {
332 assert!(ring.get_ring() == self.hom.domain().get_ring());
333 assert!(src != dst);
334 let ring = self.hom.codomain();
335 for k in 0..self.basis.row_count() {
336 let subtract = self.hom.mul_ref_map(self.basis.at(k, src), factor);
337 ring.sub_assign(self.basis.at_mut(k, dst), subtract);
338 }
339 }
340
341 fn swap<S: Copy + RingStore<Type = I>>(&mut self, ring: S, i: usize, j: usize) {
342 assert!(ring.get_ring() == self.hom.domain().get_ring());
343 if i == j {
344 return;
345 }
346 let col_count = self.basis.col_count();
347 let (mut col_i, mut col_j) = self.basis.reborrow().split_cols(i..(i + 1), j..(j + 1));
348 for k in 0..col_count {
349 std::mem::swap(col_i.at_mut(k, 0), col_j.at_mut(k, 0));
350 }
351 }
352}
353
354#[cfg(test)]
355use crate::seq::*;
356#[cfg(test)]
357use test::Bencher;
358#[cfg(test)]
359use std::alloc::Global;
360#[cfg(test)]
361use crate::assert_matrix_eq;
362
363#[cfg(test)]
364const QQ: RationalField<StaticRing<i64>> = RationalField::new(StaticRing::<i64>::RING);
365
366#[cfg(test)]
367macro_rules! in_QQ {
368 ($hom:expr; $num:literal) => {
369 ($hom).map($num)
370 };
371 ($hom:expr; $num:literal, $den:literal) => {
372 ($hom).codomain().div(&($hom).map($num), &($hom).map($den))
373 };
374 ($([$($num:literal $(/ $den:literal)?),*]),*) => {
375 {
376 let ZZ_to_QQ = QQ.inclusion();
377 [
378 $([$(
379 in_QQ!(ZZ_to_QQ; $num $(, $den)?)
380 ),*]),*
381 ]
382 }
383 };
384 ($(DerefArray::from([$($num:literal $(/ $den:literal)?),*])),*) => {
385 {
386 let ZZ_to_QQ = QQ.inclusion();
387 [
388 $(DerefArray::from([$(
389 in_QQ!(ZZ_to_QQ; $num $(, $den)?)
390 ),*])),*
391 ]
392 }
393 };
394}
395
396#[test]
397fn test_ldl() {
398 let mut data = in_QQ![
399 DerefArray::from([1, 2, 1]),
400 DerefArray::from([2, 5, 0]),
401 DerefArray::from([1, 0, 7])
402 ];
403 let mut matrix = SubmatrixMut::<DerefArray<_, 3>, _>::from_2d(&mut data);
404 let mut expected = in_QQ![
405 [1, 2, 1],
406 [0, 1, -2],
407 [0, 0, 2]
408 ];
409 ldl(QQ, matrix.reborrow());
410
411 expected[1][0] = *matrix.at(1, 0);
413 expected[2][0] = *matrix.at(2, 0);
414 expected[2][1] = *matrix.at(2, 1);
415
416 assert_matrix_eq!(&QQ, &expected, &matrix);
417}
418
419#[test]
420fn test_swap_gso_cols() {
421 let mut matrix = in_QQ![
422 DerefArray::from([2, 1/2, 2/5]),
423 DerefArray::from([0, 3/2, 1/4]),
424 DerefArray::from([0, 0, 1])
425 ];
426 let expected = in_QQ![
427 [2, 1/2, 31/80],
428 [0, 3/2, 11/40],
429 [0, 0, 1]
430 ];
431 let matrix_view = SubmatrixMut::<DerefArray<_, 3>, _>::from_2d(&mut matrix);
432
433 swap_gso_cols(&QQ, matrix_view, 0, 1);
434
435 assert_matrix_eq!(&QQ, &expected, &matrix);
436}
437
438#[cfg(test)]
439fn norm_squared<V>(col: &Column<V, i64>) -> i64
440 where V: AsPointerToSlice<i64>
441{
442 StaticRing::<i64>::RING.sum((0..col.len()).map(|i| col.at(i) * col.at(i)))
443}
444
445#[cfg(test)]
446fn assert_lattice_isomorphic<V, const N: usize, const M: usize>(lhs: &[DerefArray<i64, M>; N], rhs: &Submatrix<V, i64>)
447 where V: AsPointerToSlice<i64>
448{
449 use crate::algorithms::linsolve::smith;
450
451 assert_eq!(rhs.row_count(), N);
452 assert_eq!(rhs.col_count(), M);
453 let ZZbig = BigIntRing::RING;
454 let mut A: OwnedMatrix<_> = OwnedMatrix::zero(N, M, ZZbig);
455 let mut B: OwnedMatrix<_> = OwnedMatrix::zero(N, M, ZZbig);
456 let int_to_ZZbig: CanHom<&RingValue<StaticRingBase<i64>>, &BigIntRing> = ZZbig.can_hom(&StaticRing::<i64>::RING).unwrap();
457 for i in 0..N {
458 for j in 0..M {
459 *A.at_mut(i, j) = int_to_ZZbig.map(lhs[i][j]);
460 *B.at_mut(i, j) = int_to_ZZbig.map(*rhs.at(i, j));
461 }
462 }
463 let mut U: OwnedMatrix<_> = OwnedMatrix::zero(N, M, ZZbig);
464 assert!(smith::solve_right_using_pre_smith(&ZZbig, A.clone_matrix(&ZZbig).data_mut(), B.clone_matrix(&ZZbig).data_mut(), U.data_mut(), Global).is_solved());
465 assert!(smith::solve_right_using_pre_smith(&ZZbig, B.clone_matrix(&ZZbig).data_mut(), A.clone_matrix(&ZZbig).data_mut(), U.data_mut(), Global).is_solved());
466}
467
468#[test]
469fn test_lll_float_2d() {
470 let ZZ = StaticRing::<i64>::RING;
471 let original = [
472 DerefArray::from([5, 9]),
473 DerefArray::from([11, 20])
474 ];
475 let mut reduced = original;
476 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 2>, _>::from_2d(&mut reduced);
477 lll_float(&ZZ, OwnedMatrix::<_>::identity(2, 2, Real64::RING).data(), reduced_matrix.reborrow(), 0.9, Global);
478
479 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
480 assert_eq!(1, norm_squared(&reduced_matrix.as_const().col_at(0)));
481 assert_eq!(1, norm_squared(&reduced_matrix.as_const().col_at(1)));
482
483 let original = [
484 DerefArray::from([10, 8]),
485 DerefArray::from([27, 22])
486 ];
487 let mut reduced = original;
488 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 2>, _>::from_2d(&mut reduced);
489 lll_float(&ZZ, OwnedMatrix::<_>::identity(2, 2, Real64::RING).data(), reduced_matrix.reborrow(), 0.9, Global);
490
491 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
492 assert_eq!(4, norm_squared(&reduced_matrix.as_const().col_at(0)));
493 assert_eq!(5, norm_squared(&reduced_matrix.as_const().col_at(1)));
494}
495
496#[test]
497fn test_lll_float_3d() {
498 let ZZ = StaticRing::<i64>::RING;
499 let original = [
502 DerefArray::from([72, 0, 0]),
503 DerefArray::from([0, 9, 0]),
504 DerefArray::from([8432, 7344, 16864])
505 ];
506 let _expected = [
507 [144, 72, 72],
508 [0, 279, -72],
509 [0, 0, 272]
510 ];
511
512 let mut reduced = original;
513 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 3>, _>::from_2d(&mut reduced);
514 lll_float(&ZZ, OwnedMatrix::<_>::identity(3, 3, Real64::RING).data(), reduced_matrix.reborrow(), 0.999, Global);
515
516 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
517 assert_eq!(144 * 144, norm_squared(&reduced_matrix.as_const().col_at(0)));
518 assert_eq!(72 * 72 + 279 * 279, norm_squared(&reduced_matrix.as_const().col_at(1)));
519 assert_eq!(72 * 72 * 2 + 272 * 272, norm_squared(&reduced_matrix.as_const().col_at(2)));
520}
521
522#[bench]
523fn bench_lll_float_10d(bencher: &mut Bencher) {
524 let ZZ = StaticRing::<i64>::RING;
525
526 let _expected = [
527 [ 2, 0, 0, -2, -6, -2, -3, 1, -1, -1],
528 [ 0, 0, 1, -2, -1, 2, -7, -8, 8, 1],
529 [ -1, 1, 0, 4, -1, 1, -1, -5, 1, -11],
530 [ 3, 1, -2, 0, 2, 1, -2, 1, 5, -11],
531 [ -1, 5, 3, -1, -1, -2, -3, 1, -3, 5],
532 [ 1, -1, 3, 1, 1, 2, -1, 0, -6, 2],
533 [ 1, 1, 0, 3, 0, -2, 1, -1, 4, 6],
534 [ 1, 1, 2, -1, 0, 2, 7, 1, 2, 2],
535 [ 1, 0, -4, 2, 2, 4, -1, 3, -3, 8],
536 [ -1, -2, 1, 1, 0, 3, 0, 7, 5, -2]
537 ];
538 bencher.iter(|| {
539 let original = [
540 DerefArray::from([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
541 DerefArray::from([ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]),
542 DerefArray::from([ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]),
543 DerefArray::from([ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]),
544 DerefArray::from([ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]),
545 DerefArray::from([ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]),
546 DerefArray::from([ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]),
547 DerefArray::from([ 2, 2, 2, 2, 0, 0, 1, 4, 0, 0]),
548 DerefArray::from([ 4, 3, 3, 3, 1, 2, 1, 0, 5, 0]),
549 DerefArray::from([ 3433883, 14315221, 24549008, 6570781, 32725387, 33674813, 27390657, 15726308, 43003827, 43364304])
550 ];
551 let mut reduced = original;
552 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 10>, _>::from_2d(&mut reduced);
553 lll_float(&ZZ, OwnedMatrix::<_>::identity(10, 10, Real64::RING).data(), reduced_matrix.reborrow(), 0.9, Global);
554
555 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
556 assert!(16 * 16 > norm_squared(&reduced_matrix.as_const().col_at(0)));
557 });
558}
559
560#[test]
561fn test_lll_exact_2d() {
562 let ZZ = StaticRing::<i64>::RING;
563 let original = [
564 DerefArray::from([5, 9]),
565 DerefArray::from([11, 20])
566 ];
567 let mut reduced = original;
568 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 2>, _>::from_2d(&mut reduced);
569 lll_exact(&ZZ, reduced_matrix.reborrow(), 0.9, Global);
570
571 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
572 assert_eq!(1, norm_squared(&reduced_matrix.as_const().col_at(0)));
573 assert_eq!(1, norm_squared(&reduced_matrix.as_const().col_at(1)));
574
575 let original = [
576 DerefArray::from([10, 8]),
577 DerefArray::from([27, 22])
578 ];
579 let mut reduced = original;
580 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 2>, _>::from_2d(&mut reduced);
581 lll_exact(&ZZ, reduced_matrix.reborrow(), 0.9, Global);
582
583 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
584 assert_eq!(4, norm_squared(&reduced_matrix.as_const().col_at(0)));
585 assert_eq!(5, norm_squared(&reduced_matrix.as_const().col_at(1)));
586}
587
588#[test]
589fn test_lll_exact_3d() {
590 let ZZ = StaticRing::<i64>::RING;
591 let original = [
594 DerefArray::from([72, 0, 0]),
595 DerefArray::from([0, 9, 0]),
596 DerefArray::from([8432, 7344, 16864])
597 ];
598 let _expected = [
599 [144, 72, 72],
600 [0, 279, -72],
601 [0, 0, 272]
602 ];
603
604 let mut reduced = original;
605 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 3>, _>::from_2d(&mut reduced);
606 lll_exact(&ZZ, reduced_matrix.reborrow(), 0.999, Global);
607
608 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
609 assert_eq!(144 * 144, norm_squared(&reduced_matrix.as_const().col_at(0)));
610 assert_eq!(72 * 72 + 279 * 279, norm_squared(&reduced_matrix.as_const().col_at(1)));
611 assert_eq!(72 * 72 * 2 + 272 * 272, norm_squared(&reduced_matrix.as_const().col_at(2)));
612}
613
614#[bench]
615fn bench_lll_exact_10d(bencher: &mut Bencher) {
616 let ZZ = StaticRing::<i64>::RING;
617
618 let _expected = [
619 [ 2, 0, 0, -2, -6, -2, -3, 1, -1, -1],
620 [ 0, 0, 1, -2, -1, 2, -7, -8, 8, 1],
621 [ -1, 1, 0, 4, -1, 1, -1, -5, 1, -11],
622 [ 3, 1, -2, 0, 2, 1, -2, 1, 5, -11],
623 [ -1, 5, 3, -1, -1, -2, -3, 1, -3, 5],
624 [ 1, -1, 3, 1, 1, 2, -1, 0, -6, 2],
625 [ 1, 1, 0, 3, 0, -2, 1, -1, 4, 6],
626 [ 1, 1, 2, -1, 0, 2, 7, 1, 2, 2],
627 [ 1, 0, -4, 2, 2, 4, -1, 3, -3, 8],
628 [ -1, -2, 1, 1, 0, 3, 0, 7, 5, -2]
629 ];
630 bencher.iter(|| {
631 let original = [
632 DerefArray::from([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
633 DerefArray::from([ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]),
634 DerefArray::from([ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]),
635 DerefArray::from([ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0]),
636 DerefArray::from([ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]),
637 DerefArray::from([ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0]),
638 DerefArray::from([ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]),
639 DerefArray::from([ 2, 2, 2, 2, 0, 0, 1, 4, 0, 0]),
640 DerefArray::from([ 4, 3, 3, 3, 1, 2, 1, 0, 5, 0]),
641 DerefArray::from([ 3433883, 14315221, 24549008, 6570781, 32725387, 33674813, 27390657, 15726308, 43003827, 43364304])
642 ];
643 let mut reduced = original;
644 let mut reduced_matrix = SubmatrixMut::<DerefArray<_, 10>, _>::from_2d(&mut reduced);
645 lll_exact(&ZZ, reduced_matrix.reborrow(), 0.9, Global);
646
647 assert_lattice_isomorphic(&original, &reduced_matrix.as_const());
648 assert!(16 * 16 > norm_squared(&reduced_matrix.as_const().col_at(0)));
649 });
650}