lattice_qcd_rs/
lattice.rs

1//! Defines lattices and lattice component.
2//!
3//! [`LatticeCyclic`] is the structure that encode the lattice information like
4//! the lattice spacing, the number of point and the dimension.
5//! It is used to do operation on [`LatticePoint`], [`LatticeLink`] and
6//! [`LatticeLinkCanonical`].
7//! Or to get an iterator over these elements.
8//!
9//! [`LatticePoint`], [`LatticeLink`] and [`LatticeLinkCanonical`] are elements on the lattice.
10//! They encode where a field element is situated.
11
12use std::cmp::Ordering;
13use std::convert::TryInto;
14use std::iter::FusedIterator;
15use std::ops::{Index, IndexMut, Neg};
16
17use lattice_qcd_rs_procedural_macro::{implement_direction_from, implement_direction_list};
18use na::{SVector, Vector4};
19#[cfg(feature = "serde-serialize")]
20use serde::{Deserialize, Serialize};
21
22use super::field::{EField, LinkMatrix};
23use super::{error::LatticeInitializationError, Real};
24
25/// A Cyclic lattice in space. Does not store point and links but is used to generate them.
26///
27/// The generic parameter `D` is the dimension.
28///
29/// This lattice is Cyclic more precisely if the lattice has N points in each direction.
30/// Then we can move alongside a direction going though point 0, 1, ... N-1. The next step in
31/// the same direction goes back to the point at 0.
32///
33/// This structure is used for operation on [`LatticePoint`], [`LatticeLink`] and
34/// [`LatticeLinkCanonical`].
35// For example, theses three structures are abstract and are in general use to
36/// access data on the lattice. These data are stored [`LinkMatrix`] and [`EField`] which are just
37/// a wrapper around a [`Vec`]. `LatticeCyclic` is used to convert the lattice element to
38/// an index to access these data.
39///
40/// This contain very few data and can be cloned at almost no cost even though
41/// it does not implement [`Copy`].
42#[derive(Clone, Debug, PartialEq)]
43#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
44pub struct LatticeCyclic<const D: usize> {
45    /// The lattice spacing.
46    size: Real,
47    /// The number of point *per* dimension.
48    dim: usize,
49}
50
51impl<const D: usize> LatticeCyclic<D> {
52    /// Number space + time dimension, this is the `D` parameter.
53    ///
54    /// Not to confuse with [`LatticeCyclic::dim`] which is the number of point per dimension.
55    pub const fn dim_st() -> usize {
56        D
57    }
58
59    /// see [`LatticeLinkCanonical`], a conical link is a link whose direction is always positive.
60    /// That means that a link form `[x, y, z, t]` with direction `-x`
61    /// the link return is `[x - 1, y, z, t]` (modulo the `lattice::dim()``) with direction `+x`
62    /// # Example
63    /// ```
64    /// # use lattice_qcd_rs::lattice::{LatticeCyclic, DirectionEnum, LatticePoint, LatticeLinkCanonical};
65    /// # use lattice_qcd_rs::error::ImplementationError;
66    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
67    /// let lattice = LatticeCyclic::<4>::new(1_f64, 4)?;
68    /// let point = LatticePoint::<4>::new([1, 0, 2, 0].into());
69    /// assert_eq!(
70    ///     lattice.link_canonical(point, DirectionEnum::XNeg.into()),
71    ///     LatticeLinkCanonical::new(LatticePoint::new([0, 0, 2, 0].into()), DirectionEnum::XPos.into()).ok_or(ImplementationError::OptionWithUnexpectedNone)?
72    /// );
73    /// assert_eq!(
74    ///     lattice.link_canonical(point, DirectionEnum::XPos.into()),
75    ///     LatticeLinkCanonical::new(LatticePoint::new([1, 0, 2, 0].into()), DirectionEnum::XPos.into()).ok_or(ImplementationError::OptionWithUnexpectedNone)?
76    /// );
77    /// assert_eq!(
78    ///     lattice.link_canonical(point, DirectionEnum::YNeg.into()),
79    ///     LatticeLinkCanonical::new(LatticePoint::new([1, 3, 2, 0].into()), DirectionEnum::YPos.into()).ok_or(ImplementationError::OptionWithUnexpectedNone)?
80    /// );
81    /// # Ok(())
82    /// # }
83    /// ```
84    pub fn link_canonical(
85        &self,
86        pos: LatticePoint<D>,
87        dir: Direction<D>,
88    ) -> LatticeLinkCanonical<D> {
89        let mut pos_link = pos;
90        if !dir.is_positive() {
91            pos_link = self.add_point_direction(pos_link, &dir);
92        }
93        for i in 0..pos.len() {
94            pos_link[i] %= self.dim();
95        }
96        LatticeLinkCanonical::new(pos_link, dir.to_positive()).unwrap()
97    }
98
99    /// Return a link build from `pos` and `dir`.
100    ///
101    /// It is similar to [`LatticeLink::new`]. It however enforce that the point is inside the bounds.
102    /// If it is not, it will use the modulus of the bound.
103    /// # Example
104    /// ```
105    /// # use lattice_qcd_rs::{lattice::{LatticeCyclic, Direction, LatticePoint}, error::ImplementationError};
106    /// # use nalgebra::SVector;
107    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
108    /// let l = LatticeCyclic::<3>::new(1_f64, 4)?;
109    /// let dir = Direction::new(0, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
110    /// let pt = LatticePoint::new(SVector::<_, 3>::new(1, 2, 5));
111    /// let link = l.link(pt, dir);
112    /// assert_eq!(
113    ///     *link.pos(),
114    ///     LatticePoint::new(SVector::<_, 3>::new(1, 2, 1))
115    /// );
116    /// # Ok(())
117    /// # }
118    /// ```
119    pub fn link(&self, pos: LatticePoint<D>, dir: Direction<D>) -> LatticeLink<D> {
120        let mut pos_link = LatticePoint::new_zero();
121        for i in 0..pos.len() {
122            pos_link[i] = pos[i] % self.dim();
123        }
124        LatticeLink::new(pos_link, dir)
125    }
126
127    /// Transform a [`LatticeLink`] into a [`LatticeLinkCanonical`].
128    ///
129    /// See [`LatticeCyclic::link_canonical`] and [`LatticeLinkCanonical`].
130    pub fn into_canonical(&self, l: LatticeLink<D>) -> LatticeLinkCanonical<D> {
131        self.link_canonical(*l.pos(), *l.dir())
132    }
133
134    /// Get the number of points in a single direction.
135    ///
136    /// use [`LatticeCyclic::number_of_points`] for the total number of points.
137    /// Not to confuse with [`LatticeCyclic::dim_st`] which is the dimension of space-time.
138    pub const fn dim(&self) -> usize {
139        self.dim
140    }
141
142    /// Get an Iterator over all points of the lattice.
143    ///
144    /// # Example
145    /// ```
146    /// # use lattice_qcd_rs::lattice::LatticeCyclic;
147    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
148    /// for i in [4, 8, 16, 32, 64].into_iter() {
149    ///     let l = LatticeCyclic::<4>::new(1_f64, i)?;
150    ///     assert_eq!(l.get_points().size_hint().0, l.number_of_points());
151    /// }
152    /// # Ok(())
153    /// # }
154    /// ```
155    pub fn get_points(&self) -> IteratorLatticePoint<'_, D> {
156        IteratorLatticePoint::new(self, LatticePoint::new_zero())
157    }
158
159    /// Get an Iterator over all canonical link of the lattice.
160    ///
161    /// # Example
162    /// ```
163    /// # use lattice_qcd_rs::lattice::LatticeCyclic;
164    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
165    /// for i in [4, 8, 16, 32, 64].into_iter() {
166    ///     let l = LatticeCyclic::<4>::new(1_f64, i)?;
167    ///     assert_eq!(
168    ///         l.get_links().size_hint().0,
169    ///         l.number_of_canonical_links_space()
170    ///     );
171    /// }
172    /// # Ok(())
173    /// # }
174    /// ```
175    pub fn get_links(&self) -> IteratorLatticeLinkCanonical<'_, D> {
176        return IteratorLatticeLinkCanonical::new(
177            self,
178            self.link_canonical(
179                LatticePoint::new_zero(),
180                *Direction::positive_directions().first().unwrap(),
181            ),
182        );
183    }
184
185    /// create a new lattice with `size` the lattice size parameter, and `dim` the number of
186    /// points in each spatial dimension.
187    ///
188    /// # Errors
189    /// Size should be greater than 0 and dim greater or equal to 2, otherwise return an error.
190    pub fn new(size: Real, dim: usize) -> Result<Self, LatticeInitializationError> {
191        if D == 0 {
192            return Err(LatticeInitializationError::ZeroDimension);
193        }
194        if size <= 0_f64 || size.is_nan() || size.is_infinite() {
195            return Err(LatticeInitializationError::NonPositiveSize);
196        }
197        if dim < 2 {
198            return Err(LatticeInitializationError::DimTooSmall);
199        }
200        Ok(Self { size, dim })
201    }
202
203    /// Total number of canonical links oriented in space for a set time.
204    ///
205    /// Basically the number of element return by [`LatticeCyclic::get_links`].
206    ///
207    /// # Example
208    /// ```
209    /// # use lattice_qcd_rs::lattice::LatticeCyclic;
210    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
211    /// let l = LatticeCyclic::<4>::new(1_f64, 8)?;
212    /// assert_eq!(l.number_of_canonical_links_space(), 8_usize.pow(4) * 4);
213    ///
214    /// let l = LatticeCyclic::<4>::new(1_f64, 16)?;
215    /// assert_eq!(l.number_of_canonical_links_space(), 16_usize.pow(4) * 4);
216    ///
217    /// let l = LatticeCyclic::<3>::new(1_f64, 8)?;
218    /// assert_eq!(l.number_of_canonical_links_space(), 8_usize.pow(3) * 3);
219    /// # Ok(())
220    /// # }
221    /// ```
222    pub fn number_of_canonical_links_space(&self) -> usize {
223        self.number_of_points() * D
224    }
225
226    /// Total number of point in the lattice for a set time.
227    ///
228    /// # Example
229    /// ```
230    /// # use lattice_qcd_rs::lattice::LatticeCyclic;
231    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
232    /// let l = LatticeCyclic::<4>::new(1_f64, 8)?;
233    /// assert_eq!(l.number_of_points(), 8_usize.pow(4));
234    ///
235    /// let l = LatticeCyclic::<4>::new(1_f64, 16)?;
236    /// assert_eq!(l.number_of_points(), 16_usize.pow(4));
237    ///
238    /// let l = LatticeCyclic::<3>::new(1_f64, 8)?;
239    /// assert_eq!(l.number_of_points(), 8_usize.pow(3));
240    /// # Ok(())
241    /// # }
242    /// ```
243    pub fn number_of_points(&self) -> usize {
244        self.dim().pow(D.try_into().unwrap())
245    }
246
247    /// Return the lattice size factor.
248    pub const fn size(&self) -> Real {
249        self.size
250    }
251
252    /// Get the next point in the lattice following the direction `dir`.
253    /// It follows the Cyclic property of the lattice.
254    ///
255    /// # Example
256    /// ```
257    /// # use lattice_qcd_rs::lattice::{LatticeCyclic, DirectionEnum, LatticePoint};
258    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
259    /// let lattice = LatticeCyclic::<4>::new(1_f64, 4)?;
260    /// let point = LatticePoint::<4>::from([1, 0, 2, 0]);
261    /// assert_eq!(
262    ///     lattice.add_point_direction(point, &DirectionEnum::XPos.into()),
263    ///     LatticePoint::from([2, 0, 2, 0])
264    /// );
265    /// // In the following case we get [_, 3, _, _] because `dim = 4`, and this lattice is Cyclic.
266    /// assert_eq!(
267    ///     lattice.add_point_direction(point, &DirectionEnum::YNeg.into()),
268    ///     LatticePoint::from([1, 3, 2, 0])
269    /// );
270    /// # Ok(())
271    /// # }
272    /// ```
273    pub fn add_point_direction(
274        &self,
275        point: LatticePoint<D>,
276        dir: &Direction<D>,
277    ) -> LatticePoint<D> {
278        self.add_point_direction_n(point, dir, 1)
279    }
280
281    /// Returns the point given y moving `shift_number` times in direction `dir` from position `point`.
282    /// It follows the Cyclic property of the lattice.
283    ///
284    /// It is equivalent of doing [`Self::add_point_direction`] n times.
285    /// # Example
286    /// ```
287    /// # use lattice_qcd_rs::lattice::{LatticeCyclic, DirectionEnum, LatticePoint};
288    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
289    /// let lattice = LatticeCyclic::<4>::new(1_f64, 4)?;
290    /// let point = LatticePoint::<4>::from([1, 0, 2, 0]);
291    /// assert_eq!(
292    ///     lattice.add_point_direction_n(point, &DirectionEnum::XPos.into(), 2),
293    ///     LatticePoint::from([3, 0, 2, 0])
294    /// );
295    /// // In the following case we get [_, 1, _, _] because `dim = 4`, and this lattice is Cyclic.
296    /// assert_eq!(
297    ///     lattice.add_point_direction_n(point, &DirectionEnum::YNeg.into(), 3),
298    ///     LatticePoint::from([1, 1, 2, 0])
299    /// );
300    /// # Ok(())
301    /// # }
302    /// ```
303    pub fn add_point_direction_n(
304        &self,
305        mut point: LatticePoint<D>,
306        dir: &Direction<D>,
307        shift_number: usize,
308    ) -> LatticePoint<D> {
309        let shift_number = shift_number % self.dim(); // we ensure that shift_number < % self.dim()
310        if dir.is_positive() {
311            point[dir.index()] = (point[dir.index()] + shift_number) % self.dim();
312        }
313        else {
314            let dir_pos = dir.to_positive();
315            if point[dir_pos.index()] < shift_number {
316                point[dir_pos.index()] = self.dim() - (shift_number - point[dir_pos.index()]);
317            }
318            else {
319                point[dir_pos.index()] = (point[dir_pos.index()] - shift_number) % self.dim();
320            }
321        }
322        point
323    }
324
325    /// Returns whether the number of canonical link is the same as the length of `links`.
326    pub fn has_compatible_length_links(&self, links: &LinkMatrix) -> bool {
327        self.number_of_canonical_links_space() == links.len()
328    }
329
330    /// Returns wether the number of point is the same as the length of `e_field`.
331    pub fn has_compatible_length_e_field(&self, e_field: &EField<D>) -> bool {
332        self.number_of_points() == e_field.len()
333    }
334
335    /// Returns the length is compatible for both `links` and `e_field`.
336    /// See [`Self::has_compatible_length_links`] and [`Self::has_compatible_length_e_field`].
337    pub fn has_compatible_length(&self, links: &LinkMatrix, e_field: &EField<D>) -> bool {
338        self.has_compatible_length_links(links) && self.has_compatible_length_e_field(e_field)
339    }
340}
341
342impl<const D: usize> std::fmt::Display for LatticeCyclic<D> {
343    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344        write!(
345            f,
346            "Cyclic lattice with {}^{} points and spacing {}",
347            self.dim, D, self.size
348        )
349    }
350}
351
352/// Iterator over [`LatticeLinkCanonical`] associated to a particular [`LatticeCyclic`].
353#[derive(Clone, Debug, PartialEq)]
354pub struct IteratorLatticeLinkCanonical<'a, const D: usize> {
355    lattice: &'a LatticeCyclic<D>,
356    element: Option<LatticeLinkCanonical<D>>,
357}
358
359impl<'a, const D: usize> IteratorLatticeLinkCanonical<'a, D> {
360    /// create a new iterator. The first [`IteratorLatticeLinkCanonical::next()`] will return `first_el`.
361    /// # Example
362    /// ```
363    /// # use lattice_qcd_rs::lattice::{IteratorLatticeLinkCanonical, LatticeCyclic, LatticeLinkCanonical, LatticePoint, DirectionEnum};
364    /// # use lattice_qcd_rs::error::ImplementationError;
365    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
366    /// let lattice = LatticeCyclic::<4>::new(1_f64, 4)?;
367    /// let first_el = LatticeLinkCanonical::<4>::new(LatticePoint::from([1, 0, 2, 0]), DirectionEnum::YPos.into()).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
368    /// let mut iter = IteratorLatticeLinkCanonical::new(&lattice, first_el);
369    /// assert_eq!(iter.next().ok_or(ImplementationError::OptionWithUnexpectedNone)?, first_el);
370    /// # Ok(())
371    /// # }
372    /// ```
373    pub const fn new(lattice: &'a LatticeCyclic<D>, first_el: LatticeLinkCanonical<D>) -> Self {
374        Self {
375            lattice,
376            element: Some(first_el),
377        }
378    }
379}
380
381impl<'a, const D: usize> Iterator for IteratorLatticeLinkCanonical<'a, D> {
382    type Item = LatticeLinkCanonical<D>;
383
384    // TODO improve
385    fn next(&mut self) -> Option<Self::Item> {
386        let previous_el = self.element;
387        if let Some(ref mut element) = self.element {
388            let mut iter_dir = IteratorDirection::<D, true>::new(Some(element.dir)).unwrap();
389            let new_dir = iter_dir.next();
390            match new_dir {
391                Some(dir) => element.set_dir(dir),
392                None => {
393                    element.set_dir(Direction::new(0, true).unwrap());
394                    let mut iter = IteratorLatticePoint::new(self.lattice, *element.pos());
395                    match iter.nth(1) {
396                        // get the second element
397                        Some(array) => *element.pos_mut() = array,
398                        None => {
399                            self.element = None;
400                            return previous_el;
401                        }
402                    }
403                }
404            }
405        }
406        previous_el
407    }
408
409    fn size_hint(&self) -> (usize, Option<usize>) {
410        match self.element {
411            None => (0, Some(0)),
412            Some(element) => {
413                let val =
414                    self.lattice.number_of_canonical_links_space() - element.to_index(self.lattice);
415                (val, Some(val))
416            }
417        }
418    }
419}
420
421impl<'a, const D: usize> FusedIterator for IteratorLatticeLinkCanonical<'a, D> {}
422
423impl<'a, const D: usize> ExactSizeIterator for IteratorLatticeLinkCanonical<'a, D> {}
424
425/// Enum for internal use of iterator. It store the previous element returned by `next`
426#[derive(Clone, Debug, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)]
427#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
428pub enum IteratorElement<T> {
429    /// First element of the iterator
430    FirstElement,
431    /// An element of the iterator
432    Element(T),
433    /// The Iterator is exhausted
434    LastElement,
435}
436
437impl<T: std::fmt::Display> std::fmt::Display for IteratorElement<T> {
438    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
439        match self {
440            Self::FirstElement => write!(f, "first element"),
441            Self::Element(t) => write!(f, "element {}", t),
442            Self::LastElement => write!(f, "last element"),
443        }
444    }
445}
446
447impl<T> Default for IteratorElement<T> {
448    fn default() -> Self {
449        Self::FirstElement
450    }
451}
452
453impl<T> From<IteratorElement<T>> for Option<T> {
454    fn from(element: IteratorElement<T>) -> Self {
455        match element {
456            IteratorElement::Element(el) => Some(el),
457            IteratorElement::LastElement | IteratorElement::FirstElement => None,
458        }
459    }
460}
461
462/// Iterator over [`Direction`] with the same sign.
463/// # Example
464/// ```
465/// # use lattice_qcd_rs::lattice::{IteratorDirection, Direction, IteratorElement};
466/// # use lattice_qcd_rs::error::ImplementationError;
467/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
468/// let mut iter = IteratorDirection::<4, true>::new(None)
469///     .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
470/// assert_eq!(
471///     iter.next()
472///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
473///     Direction::new(0, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
474/// );
475/// assert_eq!(
476///     iter.next()
477///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
478///     Direction::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
479/// );
480/// assert_eq!(
481///     iter.next()
482///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
483///     Direction::new(2, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
484/// );
485/// assert_eq!(
486///     iter.next()
487///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
488///     Direction::new(3, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
489/// );
490/// assert_eq!(iter.next(), None);
491/// assert_eq!(iter.next(), None);
492/// # Ok(())
493/// # }
494/// ```
495#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Hash)]
496#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
497pub struct IteratorDirection<const D: usize, const IS_POSITIVE_DIRECTION: bool> {
498    element: IteratorElement<Direction<D>>,
499}
500
501impl<const D: usize, const IS_POSITIVE_DIRECTION: bool>
502    IteratorDirection<D, IS_POSITIVE_DIRECTION>
503{
504    /// Create an iterator where the first element upon calling [`Self::next`] is the direction
505    /// after `element`. Giving `None` results in the first element being the direction with index 0
506    /// # Example
507    /// ```
508    /// # use lattice_qcd_rs::lattice::{IteratorDirection, Direction, IteratorElement};
509    /// # use lattice_qcd_rs::error::ImplementationError;
510    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
511    /// let mut iter = IteratorDirection::<4, true>::new(None)
512    ///     .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
513    /// assert_eq!(
514    ///     iter.next()
515    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
516    ///     Direction::new(0, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
517    /// );
518    /// assert_eq!(
519    ///     iter.next()
520    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
521    ///     Direction::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
522    /// );
523    /// let element =
524    ///     Direction::<4>::new(0, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
525    /// let mut iter = IteratorDirection::<4, false>::new(Some(element))
526    ///     .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
527    /// assert_eq!(
528    ///     iter.next()
529    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
530    ///     Direction::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?
531    /// );
532    /// assert_eq!(
533    ///     iter.next()
534    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
535    ///     Direction::new(2, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?
536    /// );
537    /// # Ok(())
538    /// # }
539    /// ```
540    /// ```
541    /// # use lattice_qcd_rs::lattice::{IteratorDirection, Direction, IteratorElement};
542    /// # use lattice_qcd_rs::error::ImplementationError;
543    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
544    /// let iter = IteratorDirection::<0, true>::new(None);
545    /// // 0 is invalid
546    /// assert!(iter.is_none());
547    /// let iter = IteratorDirection::<4, true>::new(Some(
548    ///     Direction::new(2, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?,
549    /// ));
550    /// // the sign of the direction is invalid
551    /// assert!(iter.is_none());
552    /// # Ok(())
553    /// # }
554    /// ```
555    pub const fn new(element: Option<Direction<D>>) -> Option<Self> {
556        match element {
557            None => Self::new_from_element(IteratorElement::FirstElement),
558            Some(dir) => Self::new_from_element(IteratorElement::Element(dir)),
559        }
560    }
561
562    /// create a new iterator. The first [`IteratorLatticeLinkCanonical::next()`] the element just after the one given
563    /// or the first element if [`IteratorElement::FirstElement`] is given.
564    /// # Example
565    /// ```
566    /// # use lattice_qcd_rs::lattice::{IteratorDirection, Direction, IteratorElement};
567    /// # use lattice_qcd_rs::error::ImplementationError;
568    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
569    /// let mut iter = IteratorDirection::<4, true>::new_from_element(IteratorElement::FirstElement)
570    ///     .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
571    /// assert_eq!(
572    ///     iter.next()
573    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
574    ///     Direction::new(0, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
575    /// );
576    /// assert_eq!(
577    ///     iter.next()
578    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
579    ///     Direction::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
580    /// );
581    /// let element =
582    ///     Direction::<4>::new(0, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
583    /// let mut iter =
584    ///     IteratorDirection::<4, false>::new_from_element(IteratorElement::Element(element))
585    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
586    /// assert_eq!(
587    ///     iter.next()
588    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
589    ///     Direction::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?
590    /// );
591    /// assert_eq!(
592    ///     iter.next()
593    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?,
594    ///     Direction::new(2, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?
595    /// );
596    /// # Ok(())
597    /// # }
598    /// ```
599    pub const fn new_from_element(element: IteratorElement<Direction<D>>) -> Option<Self> {
600        if D == 0 {
601            return None;
602        }
603        if let IteratorElement::Element(ref el) = element {
604            if el.is_positive() != IS_POSITIVE_DIRECTION {
605                return None;
606            }
607        }
608        Some(Self { element })
609    }
610}
611
612impl<const D: usize, const IS_POSITIVE_DIRECTION: bool> Iterator
613    for IteratorDirection<D, IS_POSITIVE_DIRECTION>
614{
615    type Item = Direction<D>;
616
617    fn next(&mut self) -> Option<Self::Item> {
618        let next_element = match self.element {
619            IteratorElement::FirstElement => {
620                IteratorElement::Element(Direction::new(0, IS_POSITIVE_DIRECTION).unwrap())
621            }
622            IteratorElement::Element(ref dir) => {
623                if let Some(dir) = Direction::new(dir.index() + 1, IS_POSITIVE_DIRECTION) {
624                    IteratorElement::Element(dir)
625                }
626                else {
627                    IteratorElement::LastElement
628                }
629            }
630            IteratorElement::LastElement => IteratorElement::LastElement,
631        };
632        self.element = next_element;
633        next_element.into()
634    }
635
636    fn size_hint(&self) -> (usize, Option<usize>) {
637        let size = match self.element {
638            IteratorElement::FirstElement => D,
639            IteratorElement::Element(ref dir) => D - (dir.index() + 1),
640            IteratorElement::LastElement => 0,
641        };
642        (size, Some(size))
643    }
644}
645
646impl<const D: usize, const IS_POSITIVE_DIRECTION: bool> FusedIterator
647    for IteratorDirection<D, IS_POSITIVE_DIRECTION>
648{
649}
650
651impl<const D: usize, const IS_POSITIVE_DIRECTION: bool> ExactSizeIterator
652    for IteratorDirection<D, IS_POSITIVE_DIRECTION>
653{
654}
655
656/// Iterator over [`LatticePoint`]
657#[derive(Clone, Debug, PartialEq)]
658pub struct IteratorLatticePoint<'a, const D: usize> {
659    lattice: &'a LatticeCyclic<D>,
660    element: Option<LatticePoint<D>>,
661}
662
663impl<'a, const D: usize> IteratorLatticePoint<'a, D> {
664    /// create a new iterator. The first [`IteratorLatticePoint::next()`] will return `first_el`.
665    /// # Example
666    /// ```
667    /// # use lattice_qcd_rs::lattice::{IteratorLatticePoint, LatticeCyclic, LatticePoint, Direction};
668    /// # use lattice_qcd_rs::error::ImplementationError;
669    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
670    /// let lattice = LatticeCyclic::new(1_f64, 4)?;
671    /// let first_el = LatticePoint::from([1, 0, 2, 0]);
672    /// let mut iter = IteratorLatticePoint::new(&lattice, first_el);
673    /// assert_eq!(iter.next().ok_or(ImplementationError::OptionWithUnexpectedNone)?, first_el);
674    /// # Ok(())
675    /// # }
676    /// ```
677    pub const fn new(lattice: &'a LatticeCyclic<D>, first_el: LatticePoint<D>) -> Self {
678        Self {
679            lattice,
680            element: Some(first_el),
681        }
682    }
683}
684
685impl<'a, const D: usize> Iterator for IteratorLatticePoint<'a, D> {
686    type Item = LatticePoint<D>;
687
688    // TODO improve
689    fn next(&mut self) -> Option<Self::Item> {
690        let previous_el = self.element;
691        if let Some(ref mut el) = &mut self.element {
692            el[0] += 1;
693            for i in 0..el.len() {
694                while el[i] >= self.lattice.dim() {
695                    if i < el.len() - 1 {
696                        // every element except the last one
697                        el[i + 1] += 1;
698                    }
699                    else {
700                        self.element = None;
701                        return previous_el;
702                    }
703                    el[i] -= self.lattice.dim();
704                }
705            }
706        }
707        previous_el
708    }
709
710    fn size_hint(&self) -> (usize, Option<usize>) {
711        match self.element {
712            None => (0, Some(0)),
713            Some(element) => {
714                let val = self.lattice.number_of_points() - element.to_index(self.lattice);
715                (val, Some(val))
716            }
717        }
718    }
719}
720
721impl<'a, const D: usize> FusedIterator for IteratorLatticePoint<'a, D> {}
722
723impl<'a, const D: usize> ExactSizeIterator for IteratorLatticePoint<'a, D> {}
724
725/// Represents point on a (any) lattice.
726#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Hash)]
727#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
728pub struct LatticePoint<const D: usize> {
729    data: na::SVector<usize, D>,
730}
731
732impl<const D: usize> LatticePoint<D> {
733    /// Create a new lattice point.
734    ///
735    /// It can be outside a lattice.
736    pub const fn new(data: SVector<usize, D>) -> Self {
737        Self { data }
738    }
739
740    /// Create a point at the origin
741    pub fn new_zero() -> Self {
742        Self {
743            data: SVector::zeros(),
744        }
745    }
746
747    /// Create a point using the closure generate elements with the index as input.
748    ///
749    /// See [`nalgebra::base::Matrix::from_fn`].
750    pub fn from_fn<F>(mut f: F) -> Self
751    where
752        F: FnMut(usize) -> usize,
753    {
754        Self::new(SVector::from_fn(|index, _| f(index)))
755    }
756
757    /// Number of elements in [`LatticePoint`]. This is `D`.
758    #[allow(clippy::unused_self)]
759    pub const fn len(&self) -> usize {
760        // this is in order to have a const function.
761        // we could have called self.data.len()
762        D
763    }
764
765    /// Return if LatticePoint contain no data. True when the dimension is 0, false otherwise.
766    #[allow(clippy::unused_self)]
767    pub const fn is_empty(&self) -> bool {
768        D == 0
769    }
770
771    /// Get an iterator on the data.
772    pub fn iter(&self) -> impl Iterator<Item = &usize> + ExactSizeIterator + FusedIterator {
773        self.data.iter()
774    }
775
776    /// Get an iterator on the data as mutable.
777    pub fn iter_mut(
778        &mut self,
779    ) -> impl Iterator<Item = &mut usize> + ExactSizeIterator + FusedIterator {
780        self.data.iter_mut()
781    }
782
783    /// Get the point as as [`nalgebra::SVector<usize, D>`]
784    /// # Example
785    /// ```
786    /// # use lattice_qcd_rs::lattice::LatticePoint;
787    /// #
788    /// # let point = LatticePoint::<4>::default();
789    /// let max = point.as_svector().max();
790    /// let min = point.as_ref().min();
791    /// ```
792    pub const fn as_svector(&self) -> &SVector<usize, D> {
793        &self.data
794    }
795
796    /// Get the point as a mut ref to [`nalgebra::SVector<usize, D>`]
797    /// # Example
798    /// ```
799    /// # use lattice_qcd_rs::lattice::LatticePoint;
800    /// #
801    /// # let mut point = LatticePoint::<4>::new_zero();
802    /// point.as_svector_mut()[2] = 2;
803    /// point.as_mut()[0] = 1;
804    /// ```
805    pub fn as_svector_mut(&mut self) -> &mut SVector<usize, D> {
806        &mut self.data
807    }
808}
809
810impl<const D: usize> Default for LatticePoint<D> {
811    fn default() -> Self {
812        Self::new_zero()
813    }
814}
815
816impl<const D: usize> std::fmt::Display for LatticePoint<D> {
817    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
818        write!(f, "{}", self.data)
819    }
820}
821
822impl<'a, const D: usize> IntoIterator for &'a LatticePoint<D> {
823    type IntoIter = <&'a SVector<usize, D> as IntoIterator>::IntoIter;
824    type Item = &'a usize;
825
826    fn into_iter(self) -> Self::IntoIter {
827        self.data.iter()
828    }
829}
830
831impl<'a, const D: usize> IntoIterator for &'a mut LatticePoint<D> {
832    type IntoIter = <&'a mut SVector<usize, D> as IntoIterator>::IntoIter;
833    type Item = &'a mut usize;
834
835    fn into_iter(self) -> Self::IntoIter {
836        self.data.iter_mut()
837    }
838}
839
840impl<const D: usize> Index<usize> for LatticePoint<D> {
841    type Output = usize;
842
843    /// Get the element at position `pos`
844    /// # Panic
845    /// Panics if the position is out of bound
846    /// ```should_panic
847    /// # use lattice_qcd_rs::lattice::LatticePoint;
848    /// let point = LatticePoint::new([0; 4].into());
849    /// let _ = point[4];
850    /// ```
851    fn index(&self, pos: usize) -> &Self::Output {
852        &self.data[pos]
853    }
854}
855
856impl<const D: usize> IndexMut<usize> for LatticePoint<D> {
857    /// Get the element at position `pos`
858    /// # Panic
859    /// Panics if the position is out of bound
860    /// ```should_panic
861    /// # use lattice_qcd_rs::lattice::LatticePoint;
862    /// let mut point = LatticePoint::new([0; 4].into());
863    /// point[4] += 1;
864    /// ```
865    fn index_mut(&mut self, pos: usize) -> &mut Self::Output {
866        &mut self.data[pos]
867    }
868}
869
870impl<T, const D: usize> From<T> for LatticePoint<D>
871where
872    SVector<usize, D>: From<T>,
873{
874    fn from(data: T) -> Self {
875        LatticePoint::new(SVector::from(data))
876    }
877}
878
879impl<const D: usize> From<LatticePoint<D>> for [usize; D]
880where
881    SVector<usize, D>: Into<[usize; D]>,
882{
883    fn from(data: LatticePoint<D>) -> [usize; D] {
884        data.data.into()
885    }
886}
887
888impl<const D: usize> AsRef<SVector<usize, D>> for LatticePoint<D> {
889    fn as_ref(&self) -> &SVector<usize, D> {
890        self.as_svector()
891    }
892}
893
894impl<const D: usize> AsMut<SVector<usize, D>> for LatticePoint<D> {
895    fn as_mut(&mut self) -> &mut SVector<usize, D> {
896        self.as_svector_mut()
897    }
898}
899
900/// Trait to convert an element on a lattice to an [`usize`].
901///
902/// Used mainly to index field on the lattice using [`std::vec::Vec`]
903// TODO change name ? make it less confusing
904pub trait LatticeElementToIndex<const D: usize> {
905    /// Given a lattice return an index from the element
906    fn to_index(&self, l: &LatticeCyclic<D>) -> usize;
907}
908
909impl<const D: usize> LatticeElementToIndex<D> for LatticePoint<D> {
910    fn to_index(&self, l: &LatticeCyclic<D>) -> usize {
911        self.iter()
912            .enumerate()
913            .map(|(index, pos)| (pos % l.dim()) * l.dim().pow(index.try_into().unwrap()))
914            .sum()
915    }
916}
917
918impl<const D: usize> LatticeElementToIndex<D> for Direction<D> {
919    /// equivalent to [`Direction::to_index()`]
920    fn to_index(&self, _: &LatticeCyclic<D>) -> usize {
921        self.index()
922    }
923}
924
925impl<const D: usize> LatticeElementToIndex<D> for LatticeLinkCanonical<D> {
926    fn to_index(&self, l: &LatticeCyclic<D>) -> usize {
927        self.pos().to_index(l) * D + self.dir().index()
928    }
929}
930
931/// This is just the identity.
932///
933/// It is implemented for compatibility reason
934/// such that function that require a [`LatticeElementToIndex`] can also accept [`usize`].
935impl<const D: usize> LatticeElementToIndex<D> for usize {
936    /// return self
937    fn to_index(&self, _l: &LatticeCyclic<D>) -> usize {
938        *self
939    }
940}
941
942/// A canonical link of a lattice. It contain a position and a direction.
943///
944/// The direction should always be positive.
945/// By itself the link does not store data about the lattice. Hence most function require a [`LatticeCyclic`].
946/// It also means that there is no guarantee that the object is inside a lattice.
947/// You can use modulus over the elements to use inside a lattice.
948///
949/// This object can be used to safely index in a [`std::collections::HashMap`]
950#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
951#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
952pub struct LatticeLinkCanonical<const D: usize> {
953    from: LatticePoint<D>,
954    dir: Direction<D>,
955}
956
957impl<const D: usize> LatticeLinkCanonical<D> {
958    /// Try create a LatticeLinkCanonical. If the dir is negative it fails.
959    ///
960    /// To guaranty creating an element see [LatticeCyclic::link_canonical].
961    /// The creation of an element this ways does not guaranties that the element is inside a lattice.
962    ///
963    /// # Example
964    /// ```
965    /// # use lattice_qcd_rs::lattice::{LatticeLinkCanonical, LatticePoint, DirectionEnum};
966    /// let l = LatticeLinkCanonical::new(LatticePoint::new([0; 4].into()), DirectionEnum::XNeg.into());
967    /// assert_eq!(l, None);
968    ///
969    /// let l = LatticeLinkCanonical::new(LatticePoint::new([0; 4].into()), DirectionEnum::XPos.into());
970    /// assert!(l.is_some());
971    /// ```
972    pub const fn new(from: LatticePoint<D>, dir: Direction<D>) -> Option<Self> {
973        if dir.is_negative() {
974            return None;
975        }
976        Some(Self { from, dir })
977    }
978
979    /// Position of the link.
980    pub const fn pos(&self) -> &LatticePoint<D> {
981        &self.from
982    }
983
984    /// Get a mutable reference on the position of the link.
985    pub fn pos_mut(&mut self) -> &mut LatticePoint<D> {
986        &mut self.from
987    }
988
989    /// Direction of the link.
990    pub const fn dir(&self) -> &Direction<D> {
991        &self.dir
992    }
993
994    /// Set the direction to dir
995    ///
996    /// # Example
997    /// ```
998    /// # use lattice_qcd_rs::lattice::{LatticeLinkCanonical, LatticePoint, DirectionEnum};
999    /// # use lattice_qcd_rs::error::ImplementationError;
1000    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1001    /// let mut lattice_link_canonical =
1002    ///     LatticeLinkCanonical::new(LatticePoint::new([0; 4].into()), DirectionEnum::YPos.into())
1003    ///         .ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1004    /// lattice_link_canonical.set_dir(DirectionEnum::XPos.into());
1005    /// assert_eq!(*lattice_link_canonical.dir(), DirectionEnum::XPos.into());
1006    /// # Ok(())
1007    /// # }
1008    /// ```
1009    ///
1010    /// # Panic
1011    /// panic if a negative direction is given.
1012    /// ```should_panic
1013    /// # use lattice_qcd_rs::lattice::{LatticeLinkCanonical, LatticePoint, DirectionEnum};
1014    /// # use lattice_qcd_rs::error::ImplementationError;
1015    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1016    /// # let mut lattice_link_canonical = LatticeLinkCanonical::new(LatticePoint::new([0; 4].into()), DirectionEnum::XPos.into()).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1017    /// lattice_link_canonical.set_dir(DirectionEnum::XNeg.into()); // Panics !
1018    /// # Ok(())
1019    /// # }
1020    /// ```
1021    pub fn set_dir(&mut self, dir: Direction<D>) {
1022        if dir.is_negative() {
1023            panic!("Cannot set a negative direction to a canonical link.");
1024        }
1025        self.dir = dir;
1026    }
1027
1028    /// Set the direction using positive direction. i.e. if a direction `-x` is passed
1029    /// the direction assigned will be `+x`.
1030    ///
1031    /// This is equivalent to `link.set_dir(dir.to_positive())`.
1032    pub fn set_dir_positive(&mut self, dir: Direction<D>) {
1033        self.dir = dir.to_positive();
1034    }
1035}
1036
1037impl<const D: usize> std::fmt::Display for LatticeLinkCanonical<D> {
1038    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1039        write!(
1040            f,
1041            "canonical link [position {}, direction {}]",
1042            self.from, self.dir
1043        )
1044    }
1045}
1046
1047impl<const D: usize> From<LatticeLinkCanonical<D>> for LatticeLink<D> {
1048    fn from(l: LatticeLinkCanonical<D>) -> Self {
1049        LatticeLink::new(l.from, l.dir)
1050    }
1051}
1052
1053impl<const D: usize> From<&LatticeLinkCanonical<D>> for LatticeLink<D> {
1054    fn from(l: &LatticeLinkCanonical<D>) -> Self {
1055        LatticeLink::new(l.from, l.dir)
1056    }
1057}
1058
1059/// A lattice link, contrary to [`LatticeLinkCanonical`] the direction can be negative.
1060///
1061/// This means that multiple link can be equivalent but does not have the same data
1062/// and therefore hash (hopefully).
1063///
1064/// By itself the link does not store data about the lattice. Hence most function require a [`LatticeCyclic`].
1065/// It also means that there is no guarantee that the object is inside a lattice.
1066/// You can use modulus over the elements to use inside a lattice.
1067#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
1068#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1069pub struct LatticeLink<const D: usize> {
1070    from: LatticePoint<D>,
1071    dir: Direction<D>,
1072}
1073
1074impl<const D: usize> LatticeLink<D> {
1075    /// Create a link from position `from` and direction `dir`.
1076    pub const fn new(from: LatticePoint<D>, dir: Direction<D>) -> Self {
1077        Self { from, dir }
1078    }
1079
1080    /// Get the position of the link.
1081    pub const fn pos(&self) -> &LatticePoint<D> {
1082        &self.from
1083    }
1084
1085    /// Get a mutable reference to the position of the link.
1086    pub fn pos_mut(&mut self) -> &mut LatticePoint<D> {
1087        &mut self.from
1088    }
1089
1090    /// Get the direction of the link.
1091    pub const fn dir(&self) -> &Direction<D> {
1092        &self.dir
1093    }
1094
1095    /// Get a mutable reference to the direction of the link.
1096    pub fn dir_mut(&mut self) -> &mut Direction<D> {
1097        &mut self.dir
1098    }
1099
1100    /// Get if the direction of the link is positive.
1101    pub const fn is_dir_positive(&self) -> bool {
1102        self.dir.is_positive()
1103    }
1104
1105    /// Get if the direction of the link is negative.
1106    pub const fn is_dir_negative(&self) -> bool {
1107        self.dir.is_negative()
1108    }
1109}
1110
1111impl<const D: usize> std::fmt::Display for LatticeLink<D> {
1112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1113        write!(f, "link [position {}, direction {}]", self.from, self.dir)
1114    }
1115}
1116
1117/// Represent a cardinal direction
1118#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
1119#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1120pub struct Direction<const D: usize> {
1121    index_dir: usize,
1122    is_positive: bool,
1123}
1124
1125impl<const D: usize> Direction<D> {
1126    /// New direction from a direction as an idex and wether it is in the positive direction.
1127    pub const fn new(index_dir: usize, is_positive: bool) -> Option<Self> {
1128        if index_dir >= D {
1129            return None;
1130        }
1131        Some(Self {
1132            index_dir,
1133            is_positive,
1134        })
1135    }
1136
1137    /// List of all positives directions.
1138    /// This is very slow use [`Self::positive_directions`] instead.
1139    pub fn positives_vec() -> Vec<Self> {
1140        let mut x = Vec::with_capacity(D);
1141        for i in 0..D {
1142            x.push(Self::new(i, true).unwrap());
1143        }
1144        x
1145    }
1146
1147    /// List all directions.
1148    /// This is very slow use [`DirectionList::directions`] instead.
1149    pub fn directions_vec() -> Vec<Self> {
1150        let mut x = Vec::with_capacity(2 * D);
1151        for i in 0..D {
1152            x.push(Self::new(i, true).unwrap());
1153            x.push(Self::new(i, false).unwrap());
1154        }
1155        x
1156    }
1157
1158    // TODO add const function for all direction once operation on const generic are added
1159    /// Get all direction with the sign `IS_POSITIVE`.
1160    pub const fn directions_array<const IS_POSITIVE: bool>() -> [Self; D] {
1161        // TODO use unsafe code to avoid the allocation
1162        let mut i = 0_usize;
1163        let mut array = [Direction {
1164            index_dir: 0,
1165            is_positive: IS_POSITIVE,
1166        }; D];
1167        while i < D {
1168            array[i] = Direction {
1169                index_dir: i,
1170                is_positive: IS_POSITIVE,
1171            };
1172            i += 1;
1173        }
1174        array
1175    }
1176
1177    /// Get all negative direction
1178    pub const fn negative_directions() -> [Self; D] {
1179        Self::directions_array::<false>()
1180    }
1181
1182    /// Get all positive direction
1183    pub const fn positive_directions() -> [Self; D] {
1184        Self::directions_array::<true>()
1185    }
1186
1187    /// Get if the position is positive.
1188    pub const fn is_positive(&self) -> bool {
1189        self.is_positive
1190    }
1191
1192    /// Get if the position is Negative. see [`Direction::is_positive`]
1193    pub const fn is_negative(&self) -> bool {
1194        !self.is_positive()
1195    }
1196
1197    /// Return the positive direction associated, for example `-x` gives `+x`
1198    /// and `+x` gives `+x`.
1199    /// # Example
1200    /// ```
1201    /// # use lattice_qcd_rs::lattice::Direction;
1202    /// assert_eq!(
1203    ///     Direction::<4>::new(1, false).unwrap().to_positive(),
1204    ///     Direction::<4>::new(1, true).unwrap()
1205    /// );
1206    /// assert_eq!(
1207    ///     Direction::<4>::new(1, true).unwrap().to_positive(),
1208    ///     Direction::<4>::new(1, true).unwrap()
1209    /// );
1210    /// ```
1211    pub const fn to_positive(mut self) -> Self {
1212        self.is_positive = true;
1213        self
1214    }
1215
1216    /// Get a index associated to the direction.
1217    /// # Example
1218    /// ```
1219    /// # use lattice_qcd_rs::lattice::Direction;
1220    /// assert_eq!(Direction::<4>::new(1, false).unwrap().index(), 1);
1221    /// ```
1222    pub const fn index(&self) -> usize {
1223        self.index_dir
1224    }
1225
1226    /// Convert the direction into a vector of norm `a`;
1227    pub fn to_vector(self, a: f64) -> SVector<Real, D> {
1228        self.to_unit_vector() * a
1229    }
1230
1231    /// Returns the dimension
1232    pub const fn dim() -> usize {
1233        D
1234    }
1235
1236    /// Convert the direction into a vector of norm `1`;
1237    pub fn to_unit_vector(self) -> SVector<Real, D> {
1238        let mut v = SVector::zeros();
1239        v[self.index_dir] = 1_f64;
1240        v
1241    }
1242
1243    /// Find the direction the vector point the most.
1244    /// For a zero vector return [`DirectionEnum::XPos`].
1245    /// # Example
1246    /// ```
1247    /// # use lattice_qcd_rs::lattice::Direction;
1248    /// # use lattice_qcd_rs::error::ImplementationError;
1249    /// # fn main() -> Result<(), Box< dyn std::error::Error>> {
1250    /// assert_eq!(
1251    ///     Direction::from_vector(&nalgebra::Vector4::new(1_f64, 0_f64, 0_f64, 0_f64)),
1252    ///     Direction::<4>::new(0, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
1253    /// );
1254    /// assert_eq!(
1255    ///     Direction::from_vector(&nalgebra::Vector4::new(0_f64, -1_f64, 0_f64, 0_f64)),
1256    ///     Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?
1257    /// );
1258    /// assert_eq!(
1259    ///     Direction::from_vector(&nalgebra::Vector4::new(0.5_f64, 1_f64, 0_f64, 2_f64)),
1260    ///     Direction::<4>::new(3, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?
1261    /// );
1262    /// # Ok(())
1263    /// # }
1264    /// ```
1265    pub fn from_vector(v: &SVector<Real, D>) -> Self {
1266        let mut max = 0_f64;
1267        let mut index_max: usize = 0;
1268        let mut is_positive = true;
1269        for (i, dir) in Self::positive_directions().iter().enumerate() {
1270            let scalar_prod = v.dot(&dir.to_vector(1_f64));
1271            if scalar_prod.abs() > max {
1272                max = scalar_prod.abs();
1273                index_max = i;
1274                is_positive = scalar_prod > 0_f64;
1275            }
1276        }
1277        Self::new(index_max, is_positive).expect("Unreachable")
1278    }
1279}
1280
1281// TODO default when condition on const generic is available
1282
1283/*
1284impl<const D: usize> Default for LatticeLinkCanonical<D>
1285where
1286    Direction<D>: Default,
1287{
1288    fn default() -> Self {
1289        Self {
1290            from: LatticePoint::default(),
1291            dir: Direction::default(),
1292        }
1293    }
1294}
1295
1296impl<const D: usize> Default for LatticeLink<D>
1297where
1298    Direction<D>: Default,
1299{
1300    fn default() -> Self {
1301        Self {
1302            from: LatticePoint::default(),
1303            dir: Direction::default(),
1304        }
1305    }
1306}
1307*/
1308
1309impl<const D: usize> std::fmt::Display for Direction<D> {
1310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1311        write!(
1312            f,
1313            "[index {}, is positive {}]",
1314            self.index(),
1315            self.is_positive()
1316        )
1317    }
1318}
1319
1320/// List all possible direction
1321pub trait DirectionList: Sized {
1322    /// List all directions.
1323    fn directions() -> &'static [Self];
1324    /// List all positive directions.
1325    fn positive_directions() -> &'static [Self];
1326}
1327
1328implement_direction_list!();
1329
1330implement_direction_from!();
1331
1332/// Partial ordering is set as follows: two directions can be compared if they have the same index
1333/// or the same direction sign. In the first case a positive direction is greater than a negative direction
1334/// In the latter case the ordering is done on the index.
1335/// # Example
1336/// ```
1337/// # use lattice_qcd_rs::lattice::Direction;
1338/// # use lattice_qcd_rs::error::ImplementationError;
1339/// use std::cmp::Ordering;
1340/// # fn main() -> Result<(), Box< dyn std::error::Error>> {
1341///
1342/// let dir_1 =
1343///     Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1344/// let dir_2 =
1345///     Direction::<4>::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1346/// assert!(dir_1 < dir_2);
1347/// assert_eq!(dir_1.partial_cmp(&dir_2), Some(Ordering::Less));
1348/// //--------
1349/// let dir_3 =
1350///     Direction::<4>::new(3, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1351/// let dir_4 =
1352///     Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1353/// assert!(dir_3 > dir_4);
1354/// assert_eq!(dir_3.partial_cmp(&dir_4), Some(Ordering::Greater));
1355/// //--------
1356/// let dir_5 =
1357///     Direction::<4>::new(3, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1358/// let dir_6 =
1359///     Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1360/// assert_eq!(dir_5.partial_cmp(&dir_6), None);
1361/// //--------
1362/// let dir_5 =
1363///     Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1364/// let dir_6 =
1365///     Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?;
1366/// assert_eq!(dir_5.partial_cmp(&dir_6), Some(Ordering::Equal));
1367/// # Ok(())
1368/// # }
1369/// ```
1370impl<const D: usize> PartialOrd for Direction<D> {
1371    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1372        if self == other {
1373            Some(Ordering::Equal)
1374        }
1375        else if self.is_positive() == other.is_positive() {
1376            self.index().partial_cmp(&other.index())
1377        }
1378        else if self.index() == other.index() {
1379            self.is_positive().partial_cmp(&other.is_positive())
1380        }
1381        else {
1382            None
1383        }
1384    }
1385}
1386
1387impl<const D: usize> Neg for Direction<D> {
1388    type Output = Self;
1389
1390    fn neg(mut self) -> Self::Output {
1391        self.is_positive = !self.is_positive;
1392        self
1393    }
1394}
1395
1396impl<const D: usize> Neg for &Direction<D> {
1397    type Output = Direction<D>;
1398
1399    fn neg(self) -> Self::Output {
1400        -*self
1401    }
1402}
1403
1404/// Return [`Direction::to_index`].
1405impl<const D: usize> From<Direction<D>> for usize {
1406    fn from(d: Direction<D>) -> Self {
1407        d.index()
1408    }
1409}
1410
1411/// Return [`Direction::to_index`].
1412impl<const D: usize> From<&Direction<D>> for usize {
1413    fn from(d: &Direction<D>) -> Self {
1414        d.index()
1415    }
1416}
1417
1418/// Return [`DirectionEnum::from_vector`].
1419impl<const D: usize> From<SVector<Real, D>> for Direction<D> {
1420    fn from(v: SVector<Real, D>) -> Self {
1421        Direction::from_vector(&v)
1422    }
1423}
1424
1425/// Return [`DirectionEnum::from_vector`].
1426impl<const D: usize> From<&SVector<Real, D>> for Direction<D> {
1427    fn from(v: &SVector<Real, D>) -> Self {
1428        Direction::<D>::from_vector(v)
1429    }
1430}
1431
1432/// Return [`Direction::to_unit_vector`].
1433impl<const D: usize> From<Direction<D>> for SVector<Real, D> {
1434    fn from(d: Direction<D>) -> Self {
1435        d.to_unit_vector()
1436    }
1437}
1438
1439/// Return [`Direction::to_unit_vector`].
1440impl<const D: usize> From<&Direction<D>> for SVector<Real, D> {
1441    fn from(d: &Direction<D>) -> Self {
1442        d.to_unit_vector()
1443    }
1444}
1445
1446impl From<DirectionEnum> for Direction<4> {
1447    fn from(d: DirectionEnum) -> Self {
1448        Self::new(d.to_index(), d.is_positive()).expect("unreachable")
1449    }
1450}
1451
1452impl From<&DirectionEnum> for Direction<4> {
1453    fn from(d: &DirectionEnum) -> Self {
1454        Self::new(d.to_index(), d.is_positive()).expect("unreachable")
1455    }
1456}
1457
1458/// Represent a cardinal direction
1459#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
1460#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1461pub enum DirectionEnum {
1462    /// Positive x direction.
1463    XPos,
1464    /// Negative x direction.
1465    XNeg,
1466    /// Positive y direction.
1467    YPos,
1468    /// Negative y direction.
1469    YNeg,
1470    /// Positive z direction.
1471    ZPos,
1472    /// Negative z direction.
1473    ZNeg,
1474    /// Positive t direction.
1475    TPos,
1476    /// Negative t direction.
1477    TNeg,
1478}
1479
1480impl DirectionEnum {
1481    /// List all directions.
1482    pub const DIRECTIONS: [Self; 8] = [
1483        DirectionEnum::XPos,
1484        DirectionEnum::YPos,
1485        DirectionEnum::ZPos,
1486        DirectionEnum::TPos,
1487        DirectionEnum::XNeg,
1488        DirectionEnum::YNeg,
1489        DirectionEnum::ZNeg,
1490        DirectionEnum::TNeg,
1491    ];
1492    /// List all spatial directions.
1493    pub const DIRECTIONS_SPACE: [Self; 6] = [
1494        DirectionEnum::XPos,
1495        DirectionEnum::YPos,
1496        DirectionEnum::ZPos,
1497        DirectionEnum::XNeg,
1498        DirectionEnum::YNeg,
1499        DirectionEnum::ZNeg,
1500    ];
1501    /// List of all positives directions.
1502    pub const POSITIVES: [Self; 4] = [
1503        DirectionEnum::XPos,
1504        DirectionEnum::YPos,
1505        DirectionEnum::ZPos,
1506        DirectionEnum::TPos,
1507    ];
1508    /// List spatial positive direction.
1509    pub const POSITIVES_SPACE: [Self; 3] = [
1510        DirectionEnum::XPos,
1511        DirectionEnum::YPos,
1512        DirectionEnum::ZPos,
1513    ];
1514
1515    /// Convert the direction into a vector of norm `a`;
1516    pub fn to_vector(self, a: f64) -> Vector4<Real> {
1517        self.to_unit_vector() * a
1518    }
1519
1520    /// Convert the direction into a vector of norm `1`;
1521    pub const fn to_unit_vector(self) -> Vector4<Real> {
1522        match self {
1523            DirectionEnum::XPos => Vector4::<Real>::new(1_f64, 0_f64, 0_f64, 0_f64),
1524            DirectionEnum::XNeg => Vector4::<Real>::new(-1_f64, 0_f64, 0_f64, 0_f64),
1525            DirectionEnum::YPos => Vector4::<Real>::new(0_f64, 1_f64, 0_f64, 0_f64),
1526            DirectionEnum::YNeg => Vector4::<Real>::new(0_f64, -1_f64, 0_f64, 0_f64),
1527            DirectionEnum::ZPos => Vector4::<Real>::new(0_f64, 0_f64, 1_f64, 0_f64),
1528            DirectionEnum::ZNeg => Vector4::<Real>::new(0_f64, 0_f64, -1_f64, 0_f64),
1529            DirectionEnum::TPos => Vector4::<Real>::new(0_f64, 0_f64, 0_f64, 1_f64),
1530            DirectionEnum::TNeg => Vector4::<Real>::new(0_f64, 0_f64, 0_f64, -1_f64),
1531        }
1532    }
1533
1534    /// Get if the position is positive.
1535    /// # Example
1536    /// ```
1537    /// # use lattice_qcd_rs::lattice::DirectionEnum;
1538    /// assert_eq!(DirectionEnum::XPos.is_positive(), true);
1539    /// assert_eq!(DirectionEnum::TPos.is_positive(), true);
1540    /// assert_eq!(DirectionEnum::YNeg.is_positive(), false);
1541    /// ```
1542    pub const fn is_positive(self) -> bool {
1543        match self {
1544            DirectionEnum::XPos
1545            | DirectionEnum::YPos
1546            | DirectionEnum::ZPos
1547            | DirectionEnum::TPos => true,
1548            DirectionEnum::XNeg
1549            | DirectionEnum::YNeg
1550            | DirectionEnum::ZNeg
1551            | DirectionEnum::TNeg => false,
1552        }
1553    }
1554
1555    /// Get if the position is Negative. see [`DirectionEnum::is_positive`]
1556    pub const fn is_negative(self) -> bool {
1557        !self.is_positive()
1558    }
1559
1560    /// Find the direction the vector point the most.
1561    /// For a zero vector return [`DirectionEnum::XPos`].
1562    /// # Example
1563    /// ```
1564    /// # use lattice_qcd_rs::lattice::DirectionEnum;
1565    /// # extern crate nalgebra;
1566    /// assert_eq!(
1567    ///     DirectionEnum::from_vector(&nalgebra::Vector4::new(1_f64, 0_f64, 0_f64, 0_f64)),
1568    ///     DirectionEnum::XPos
1569    /// );
1570    /// assert_eq!(
1571    ///     DirectionEnum::from_vector(&nalgebra::Vector4::new(0_f64, -1_f64, 0_f64, 0_f64)),
1572    ///     DirectionEnum::YNeg
1573    /// );
1574    /// assert_eq!(
1575    ///     DirectionEnum::from_vector(&nalgebra::Vector4::new(0.5_f64, 1_f64, 0_f64, 2_f64)),
1576    ///     DirectionEnum::TPos
1577    /// );
1578    /// ```
1579    #[allow(clippy::needless_return)] // for readability
1580    pub fn from_vector(v: &Vector4<Real>) -> Self {
1581        let mut max = 0_f64;
1582        let mut index_max: usize = 0;
1583        let mut is_positive = true;
1584        for i in 0..DirectionEnum::POSITIVES.len() {
1585            let scalar_prod = v.dot(&DirectionEnum::POSITIVES[i].to_vector(1_f64));
1586            if scalar_prod.abs() > max {
1587                max = scalar_prod.abs();
1588                index_max = i;
1589                is_positive = scalar_prod > 0_f64;
1590            }
1591        }
1592        match index_max {
1593            0 => {
1594                if is_positive {
1595                    return DirectionEnum::XPos;
1596                }
1597                else {
1598                    return DirectionEnum::XNeg;
1599                }
1600            }
1601            1 => {
1602                if is_positive {
1603                    return DirectionEnum::YPos;
1604                }
1605                else {
1606                    return DirectionEnum::YNeg;
1607                }
1608            }
1609            2 => {
1610                if is_positive {
1611                    return DirectionEnum::ZPos;
1612                }
1613                else {
1614                    return DirectionEnum::ZNeg;
1615                }
1616            }
1617            3 => {
1618                if is_positive {
1619                    return DirectionEnum::TPos;
1620                }
1621                else {
1622                    return DirectionEnum::TNeg;
1623                }
1624            }
1625            _ => {
1626                // the code should attain this code. and therefore panicking is not expected.
1627                unreachable!("Implementation error : invalid index")
1628            }
1629        }
1630    }
1631
1632    /// Return the positive direction associated, for example `-x` gives `+x`
1633    /// and `+x` gives `+x`.
1634    /// # Example
1635    /// ```
1636    /// # use lattice_qcd_rs::lattice::DirectionEnum;
1637    /// assert_eq!(DirectionEnum::XNeg.to_positive(), DirectionEnum::XPos);
1638    /// assert_eq!(DirectionEnum::YPos.to_positive(), DirectionEnum::YPos);
1639    /// ```
1640    pub const fn to_positive(self) -> Self {
1641        match self {
1642            DirectionEnum::XNeg => DirectionEnum::XPos,
1643            DirectionEnum::YNeg => DirectionEnum::YPos,
1644            DirectionEnum::ZNeg => DirectionEnum::ZPos,
1645            DirectionEnum::TNeg => DirectionEnum::TPos,
1646            _ => self,
1647        }
1648    }
1649
1650    /// Get a index associated to the direction.
1651    /// # Example
1652    /// ```
1653    /// # use lattice_qcd_rs::lattice::DirectionEnum;
1654    /// assert_eq!(DirectionEnum::XPos.to_index(), 0);
1655    /// assert_eq!(DirectionEnum::XNeg.to_index(), 0);
1656    /// assert_eq!(DirectionEnum::YPos.to_index(), 1);
1657    /// assert_eq!(DirectionEnum::YNeg.to_index(), 1);
1658    /// assert_eq!(DirectionEnum::ZPos.to_index(), 2);
1659    /// assert_eq!(DirectionEnum::ZNeg.to_index(), 2);
1660    /// assert_eq!(DirectionEnum::TPos.to_index(), 3);
1661    /// assert_eq!(DirectionEnum::TNeg.to_index(), 3);
1662    /// ```
1663    pub const fn to_index(self) -> usize {
1664        match self {
1665            DirectionEnum::XPos | DirectionEnum::XNeg => 0,
1666            DirectionEnum::YPos | DirectionEnum::YNeg => 1,
1667            DirectionEnum::ZPos | DirectionEnum::ZNeg => 2,
1668            DirectionEnum::TPos | DirectionEnum::TNeg => 3,
1669        }
1670    }
1671}
1672
1673/// Return [`DirectionEnum::XPos`]
1674impl Default for DirectionEnum {
1675    ///Return [`DirectionEnum::XPos`]
1676    fn default() -> Self {
1677        Self::XPos
1678    }
1679}
1680
1681impl std::fmt::Display for DirectionEnum {
1682    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1683        match self {
1684            DirectionEnum::XPos => write!(f, "positive X direction"),
1685            DirectionEnum::XNeg => write!(f, "negative X direction"),
1686            DirectionEnum::YPos => write!(f, "positive Y direction"),
1687            DirectionEnum::YNeg => write!(f, "negative Y direction"),
1688            DirectionEnum::ZPos => write!(f, "positive Z direction"),
1689            DirectionEnum::ZNeg => write!(f, "negative Z direction"),
1690            DirectionEnum::TPos => write!(f, "positive T direction"),
1691            DirectionEnum::TNeg => write!(f, "negative T direction"),
1692        }
1693    }
1694}
1695
1696impl DirectionList for DirectionEnum {
1697    fn directions() -> &'static [Self] {
1698        &Self::DIRECTIONS
1699    }
1700
1701    fn positive_directions() -> &'static [Self] {
1702        &Self::POSITIVES
1703    }
1704}
1705
1706impl PartialOrd for DirectionEnum {
1707    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1708        Direction::<4>::from(self).partial_cmp(&other.into())
1709    }
1710}
1711
1712/// Return the negative of a direction
1713/// # Example
1714/// ```
1715/// # use lattice_qcd_rs::lattice::DirectionEnum;
1716/// assert_eq!(-DirectionEnum::XNeg, DirectionEnum::XPos);
1717/// assert_eq!(-DirectionEnum::YPos, DirectionEnum::YNeg);
1718/// ```
1719impl Neg for DirectionEnum {
1720    type Output = Self;
1721
1722    fn neg(self) -> Self::Output {
1723        match self {
1724            DirectionEnum::XPos => DirectionEnum::XNeg,
1725            DirectionEnum::XNeg => DirectionEnum::XPos,
1726            DirectionEnum::YPos => DirectionEnum::YNeg,
1727            DirectionEnum::YNeg => DirectionEnum::YPos,
1728            DirectionEnum::ZPos => DirectionEnum::ZNeg,
1729            DirectionEnum::ZNeg => DirectionEnum::ZPos,
1730            DirectionEnum::TPos => DirectionEnum::TNeg,
1731            DirectionEnum::TNeg => DirectionEnum::TPos,
1732        }
1733    }
1734}
1735
1736impl Neg for &DirectionEnum {
1737    type Output = Self;
1738
1739    fn neg(self) -> Self::Output {
1740        match self {
1741            DirectionEnum::XPos => &DirectionEnum::XNeg,
1742            DirectionEnum::XNeg => &DirectionEnum::XPos,
1743            DirectionEnum::YPos => &DirectionEnum::YNeg,
1744            DirectionEnum::YNeg => &DirectionEnum::YPos,
1745            DirectionEnum::ZPos => &DirectionEnum::ZNeg,
1746            DirectionEnum::ZNeg => &DirectionEnum::ZPos,
1747            DirectionEnum::TPos => &DirectionEnum::TNeg,
1748            DirectionEnum::TNeg => &DirectionEnum::TPos,
1749        }
1750    }
1751}
1752
1753/// Return [`DirectionEnum::to_index`].
1754impl From<DirectionEnum> for usize {
1755    fn from(d: DirectionEnum) -> Self {
1756        d.to_index()
1757    }
1758}
1759
1760/// Return [`DirectionEnum::to_index`].
1761impl From<&DirectionEnum> for usize {
1762    fn from(d: &DirectionEnum) -> Self {
1763        d.to_index()
1764    }
1765}
1766
1767/// Return [`DirectionEnum::from_vector`].
1768impl From<Vector4<Real>> for DirectionEnum {
1769    fn from(v: Vector4<Real>) -> Self {
1770        DirectionEnum::from_vector(&v)
1771    }
1772}
1773
1774/// Return [`DirectionEnum::from_vector`].
1775impl From<&Vector4<Real>> for DirectionEnum {
1776    fn from(v: &Vector4<Real>) -> Self {
1777        DirectionEnum::from_vector(v)
1778    }
1779}
1780
1781/// Return [`DirectionEnum::to_unit_vector`].
1782impl From<DirectionEnum> for Vector4<Real> {
1783    fn from(d: DirectionEnum) -> Self {
1784        d.to_unit_vector()
1785    }
1786}
1787
1788/// Return [`DirectionEnum::to_unit_vector`].
1789impl From<&DirectionEnum> for Vector4<Real> {
1790    fn from(d: &DirectionEnum) -> Self {
1791        d.to_unit_vector()
1792    }
1793}
1794
1795#[cfg(test)]
1796mod test {
1797    use super::*;
1798
1799    #[test]
1800    fn directions_list_cst() {
1801        let dirs = Direction::<3>::positive_directions();
1802        for (index, dir) in dirs.iter().enumerate() {
1803            assert_eq!(*dir, Direction::new(index, true).unwrap());
1804        }
1805        let dirs = Direction::<3>::negative_directions();
1806        for (index, dir) in dirs.iter().enumerate() {
1807            assert_eq!(*dir, Direction::new(index, false).unwrap());
1808        }
1809
1810        let dirs = Direction::<8>::positive_directions();
1811        for (index, dir) in dirs.iter().enumerate() {
1812            assert_eq!(*dir, Direction::new(index, true).unwrap());
1813        }
1814        let dirs = Direction::<8>::negative_directions();
1815        for (index, dir) in dirs.iter().enumerate() {
1816            assert_eq!(*dir, Direction::new(index, false).unwrap());
1817        }
1818
1819        let dirs = Direction::<0>::positive_directions();
1820        assert_eq!(dirs.len(), 0);
1821        let dirs = Direction::<0>::negative_directions();
1822        assert_eq!(dirs.len(), 0);
1823
1824        let dirs = Direction::<128>::positive_directions();
1825        for (index, dir) in dirs.iter().enumerate() {
1826            assert_eq!(*dir, Direction::new(index, true).unwrap());
1827        }
1828        let dirs = Direction::<128>::negative_directions();
1829        for (index, dir) in dirs.iter().enumerate() {
1830            assert_eq!(*dir, Direction::new(index, false).unwrap());
1831        }
1832
1833        let dirs = Direction::<128>::positive_directions();
1834        let dir_vec = Direction::<128>::positives_vec();
1835        assert_eq!(dirs.to_vec(), dir_vec);
1836
1837        let dir_vec = Direction::<128>::directions_vec();
1838        assert_eq!(dir_vec.len(), 256);
1839    }
1840
1841    #[test]
1842    #[allow(clippy::explicit_counter_loop)]
1843    fn lattice_pt() {
1844        let array = [1, 2, 3, 4];
1845        let mut pt = LatticePoint::from(array);
1846        assert_eq!(LatticePoint::from(array), LatticePoint::new(array.into()));
1847
1848        assert_eq!(<[usize; 4]>::from(pt), array);
1849        assert!(!pt.is_empty());
1850        assert_eq!(pt.iter().count(), 4);
1851        assert_eq!(pt.iter_mut().count(), 4);
1852        assert_eq!(pt.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3, 4]);
1853        assert_eq!(pt.iter_mut().collect::<Vec<_>>(), vec![&1, &2, &3, &4]);
1854
1855        let mut j = 0;
1856        for i in &pt {
1857            assert_eq!(*i, array[j]);
1858            j += 1;
1859        }
1860
1861        let mut j = 0;
1862        for i in &mut pt {
1863            assert_eq!(*i, array[j]);
1864            j += 1;
1865        }
1866
1867        pt.iter_mut().for_each(|el| *el = 0);
1868        assert_eq!(pt, LatticePoint::new_zero());
1869        assert_eq!(LatticePoint::<4>::default(), LatticePoint::new_zero());
1870
1871        let mut pt_2 = LatticePoint::<0>::new_zero();
1872        assert!(pt_2.is_empty());
1873        assert_eq!(pt_2.iter().count(), 0);
1874        assert_eq!(pt_2.iter_mut().count(), 0);
1875
1876        assert_eq!(pt.to_string(), pt.as_ref().to_string());
1877    }
1878
1879    #[test]
1880    #[should_panic]
1881    fn set_dir_neg() {
1882        let mut lattice_link_canonical =
1883            LatticeLinkCanonical::new(LatticePoint::new([0; 4].into()), DirectionEnum::XPos.into())
1884                .unwrap();
1885
1886        lattice_link_canonical.set_dir(DirectionEnum::XNeg.into());
1887    }
1888
1889    #[test]
1890    #[allow(clippy::cognitive_complexity)]
1891    fn dir() {
1892        assert!(Direction::<4>::new(4, true).is_none());
1893        assert!(Direction::<4>::new(32, true).is_none());
1894        assert!(Direction::<4>::new(3, true).is_some());
1895        assert!(Direction::<127>::new(128, true).is_none());
1896        assert_eq!(Direction::<4>::dim(), 4);
1897        assert_eq!(Direction::<124>::dim(), 124);
1898
1899        assert!(DirectionEnum::TNeg.is_negative());
1900        assert!(!DirectionEnum::ZPos.is_negative());
1901
1902        assert_eq!(-&DirectionEnum::TNeg, &DirectionEnum::TPos);
1903        assert_eq!(-&DirectionEnum::TPos, &DirectionEnum::TNeg);
1904        assert_eq!(-&DirectionEnum::ZNeg, &DirectionEnum::ZPos);
1905        assert_eq!(-&DirectionEnum::ZPos, &DirectionEnum::ZNeg);
1906        assert_eq!(-&DirectionEnum::YNeg, &DirectionEnum::YPos);
1907        assert_eq!(-&DirectionEnum::YPos, &DirectionEnum::YNeg);
1908        assert_eq!(-&DirectionEnum::XNeg, &DirectionEnum::XPos);
1909        assert_eq!(-&DirectionEnum::XPos, &DirectionEnum::XNeg);
1910
1911        assert_eq!(-DirectionEnum::TNeg, DirectionEnum::TPos);
1912        assert_eq!(-DirectionEnum::TPos, DirectionEnum::TNeg);
1913        assert_eq!(-DirectionEnum::ZNeg, DirectionEnum::ZPos);
1914        assert_eq!(-DirectionEnum::ZPos, DirectionEnum::ZNeg);
1915        assert_eq!(-DirectionEnum::YNeg, DirectionEnum::YPos);
1916        assert_eq!(-DirectionEnum::YPos, DirectionEnum::YNeg);
1917        assert_eq!(-DirectionEnum::XNeg, DirectionEnum::XPos);
1918        assert_eq!(-DirectionEnum::XPos, DirectionEnum::XNeg);
1919
1920        assert_eq!(DirectionEnum::directions().len(), 8);
1921        assert_eq!(DirectionEnum::positive_directions().len(), 4);
1922
1923        assert_eq!(Direction::<4>::directions().len(), 8);
1924        assert_eq!(Direction::<4>::positive_directions().len(), 4);
1925
1926        let l = LatticeCyclic::new(1_f64, 4).unwrap();
1927        for dir in Direction::<3>::directions() {
1928            assert_eq!(
1929                <Direction<3> as LatticeElementToIndex<3>>::to_index(dir, &l),
1930                dir.index()
1931            );
1932        }
1933
1934        let array_dir_name = ["X", "Y", "Z", "T"];
1935        let array_pos = ["positive", "negative"];
1936        for (i, dir) in DirectionEnum::directions().iter().enumerate() {
1937            assert_eq!(
1938                dir.to_string(),
1939                format!("{} {} direction", array_pos[i / 4], array_dir_name[i % 4])
1940            );
1941        }
1942    }
1943
1944    #[test]
1945    fn lattice_link() {
1946        let pt = [0, 0, 0, 0].into();
1947        let dir = DirectionEnum::XNeg.into();
1948        let mut link = LatticeLinkCanonical::new(pt, DirectionEnum::YPos.into()).unwrap();
1949        link.set_dir_positive(dir);
1950        assert_eq!(
1951            link,
1952            LatticeLinkCanonical::new(pt, dir.to_positive()).unwrap()
1953        );
1954
1955        let mut link = LatticeLink::new(pt, DirectionEnum::YPos.into());
1956        let pos = *link.pos();
1957        let dir = *link.dir();
1958        assert_eq!(pos, *link.pos_mut());
1959        assert_eq!(dir, *link.dir_mut());
1960        assert!(link.is_dir_positive());
1961        *link.dir_mut() = DirectionEnum::YNeg.into();
1962        assert!(!link.is_dir_positive());
1963
1964        assert_eq!(
1965            link.to_string(),
1966            format!(
1967                "link [position {}, direction [index 1, is positive false]]",
1968                SVector::<usize, 4>::zeros()
1969            )
1970        );
1971
1972        let vector = SVector::<usize, 5>::from([1, 0, 0, 0, 0]);
1973        let canonical_link = LatticeLinkCanonical::<5>::new(
1974            LatticePoint::new(vector),
1975            Direction::new(0, true).unwrap(),
1976        )
1977        .unwrap();
1978        println!("{}", canonical_link);
1979        assert_eq!(
1980            canonical_link.to_string(),
1981            format!(
1982                "canonical link [position {}, direction [index 0, is positive true]]",
1983                vector
1984            )
1985        );
1986    }
1987
1988    #[allow(clippy::cognitive_complexity)]
1989    #[test]
1990    fn iterator() {
1991        let l = LatticeCyclic::<2>::new(1_f64, 4).unwrap();
1992        let mut iterator = l.get_points();
1993        assert_eq!(
1994            iterator.size_hint(),
1995            (l.number_of_points(), Some(l.number_of_points()))
1996        );
1997        assert_eq!(iterator.size_hint(), (16, Some(16)));
1998        iterator.nth(9);
1999        assert_eq!(
2000            iterator.size_hint(),
2001            (
2002                l.number_of_points() - 10,       // 6
2003                Some(l.number_of_points() - 10)  // 6
2004            )
2005        );
2006        assert!(iterator.nth(4).is_some());
2007        assert_eq!(iterator.size_hint(), (1, Some(1)));
2008        assert!(iterator.next().is_some());
2009        assert_eq!(iterator.size_hint(), (0, Some(0)));
2010        assert!(iterator.next().is_none());
2011        assert_eq!(iterator.size_hint(), (0, Some(0)));
2012
2013        let mut iterator = l.get_links();
2014        assert_eq!(
2015            iterator.size_hint(),
2016            (
2017                l.number_of_canonical_links_space(),
2018                Some(l.number_of_canonical_links_space())
2019            )
2020        );
2021        assert_eq!(iterator.size_hint(), (16 * 2, Some(16 * 2)));
2022        iterator.nth(9);
2023        assert_eq!(
2024            iterator.size_hint(),
2025            (
2026                l.number_of_canonical_links_space() - 10,       // 6
2027                Some(l.number_of_canonical_links_space() - 10)  // 6
2028            )
2029        );
2030        assert!(iterator.nth(20).is_some());
2031        assert_eq!(iterator.size_hint(), (1, Some(1)));
2032        assert!(iterator.next().is_some());
2033        assert_eq!(iterator.size_hint(), (0, Some(0)));
2034        assert!(iterator.next().is_none());
2035        assert_eq!(iterator.size_hint(), (0, Some(0)));
2036
2037        let mut iterator = IteratorDirection::<2, true>::new(None).unwrap();
2038        assert_eq!(iterator.size_hint(), (2, Some(2)));
2039        assert!(iterator.next().is_some());
2040        assert_eq!(iterator.size_hint(), (1, Some(1)));
2041        assert!(iterator.next().is_some());
2042        assert_eq!(iterator.size_hint(), (0, Some(0)));
2043        assert!(iterator.next().is_none());
2044        assert!(iterator.next().is_none());
2045
2046        //----
2047
2048        assert_eq!(
2049            IteratorElement::<i32>::FirstElement.to_string(),
2050            "first element"
2051        );
2052        assert_eq!(IteratorElement::Element(0_i32).to_string(), "element 0");
2053        assert_eq!(
2054            IteratorElement::<i32>::LastElement.to_string(),
2055            "last element"
2056        );
2057        assert_eq!(
2058            IteratorElement::<i32>::default(),
2059            IteratorElement::<i32>::FirstElement,
2060        );
2061    }
2062
2063    #[test]
2064    fn lattice() {
2065        let lattice = LatticeCyclic::<3>::new(1_f64, 8).unwrap();
2066        assert_eq!(
2067            lattice.to_string(),
2068            "Cyclic lattice with 8^3 points and spacing 1"
2069        );
2070    }
2071}