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}