diff --git a/CHANGELOG.md b/CHANGELOG.md index a7199d33..e3005ab0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ - [`LatticeIterator`] and [`LatticeParIter`] and be easily converted into each other. - implemented [`LatticeElementToIndex`] for [`DirectionEnum`]. - added [`LatticeCyclic::par_iter_links`] and [`LatticeCyclic::par_iter_points`] to get parallel iterator on the links and points respectively. +- Added [`CardinalDirection`]. +- Added [`Axis`]. # v0.2.1 diff --git a/Cargo.toml b/Cargo.toml index c7c020da..5152afff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,24 @@ license = "MIT OR Apache-2.0" rust-version = "1.72" +[lints.clippy] +as_ptr_cast_mut = "warn" # mistake +derive_partial_eq_without_eq = "warn" # mistake +future_not_send = "warn" #async +mutex_integer = "warn" # perf, multithreading +needless_pass_by_ref_mut = "warn" # mistake +read_zero_byte_vec = "warn" # mistake + + +[workspace.lints.clippy] +as_ptr_cast_mut = "warn" # mistake +derive_partial_eq_without_eq = "warn" # mistake +future_not_send = "warn" # async +mutex_integer = "warn" # perf, multithreading +needless_pass_by_ref_mut = "warn" # mistake +read_zero_byte_vec = "warn" # mistake + + [lib] path = "src/lib.rs" crate-type = ["lib"] diff --git a/bacon.toml b/bacon.toml index a59d38cd..97c31f5c 100644 --- a/bacon.toml +++ b/bacon.toml @@ -29,6 +29,26 @@ command = [ ] need_stdout = false +[jobs.clippy-fix] +command = [ + "cargo", + "clippy", + "--fix", + "--allow-staged", + "--all", + "--all-targets", + "--all-features", + "--color", + "always", + "--", + # "--no-deps", +] +need_stdout = false +on_success = "back" +background = true +allow_warnings = true +allow_failures = true + # This job lets you run # - all tests: bacon test # - a specific test: bacon test -- config::test_default_files @@ -160,3 +180,5 @@ alt-d = "job:doc-open" shift-d = "job:doc-pub" r = "rerun" f5 = "rerun" +c = "job:clippy" +shift-c = "job:clippy-fix" diff --git a/makefile b/makefile index 0f147b54..637134b0 100644 --- a/makefile +++ b/makefile @@ -20,6 +20,7 @@ cargo_crate_macro := -p $(name)-procedural_macro # clippy cargo_clippy := clippy cargo_clippy_flag := -- -D warnings +fix_flags := --fix --allow-staged rust_release_flag := --release # doc @@ -33,6 +34,8 @@ rust_doc_targets = --doc rust_example_flag := --examples # feature rust_coverage_feature := --no-default-features --features="serde-serialize" +#nextest +nextest_run := nextest run rust_stable := +stable rust_nightly := +nightly @@ -51,6 +54,10 @@ git_hooks := $(foreach file, $(notdir $(wildcard tools/git_hook/*)), .git/hooks/ .PHONY: all all: clippy +.PHONY: fix +fix: $(source_files) + - $(cargo) $(cargo_clippy) $(cargo_all_flag) $(rust_all_targets) $(fix_flags) + .PHONY: build build: target/debug/$(lib_name) @@ -62,6 +69,11 @@ test: $(source_files) $(cargo) $(cargo_test) $(cargo_all_flag) $(rust_doc_targets) +.PHONY: nextest +nextest: $(source_files) + $(cargo) $(nextest_run) $(cargo_all_flag) $(rust_all_targets) + + .PHONY: test_all test_all: fmt_check test doc_check | clippy diff --git a/procedural_macro/src/lib.rs b/procedural_macro/src/lib.rs index ab39eb8c..915fbf02 100644 --- a/procedural_macro/src/lib.rs +++ b/procedural_macro/src/lib.rs @@ -161,12 +161,13 @@ pub fn implement_direction_list(_item: TokenStream) -> TokenStream { let mut array_direction = Vec::with_capacity(MAX_DIM); let mut array_direction_positives = Vec::with_capacity(MAX_DIM); for j in 0..i { + // SAFETY: #j is smaller than the dimension = #i (later in the proc macro) array_direction.push(quote! { - Direction { index_dir: #j, is_positive: true }, - Direction { index_dir: #j, is_positive: false } + Direction { axis: unsafe{Axis::new_unchecked(#j)}, is_positive: true }, + Direction { axis: unsafe{Axis::new_unchecked(#j)}, is_positive: false } }); array_direction_positives.push(quote! { - Direction { index_dir: #j, is_positive: true } + Direction { axis: unsafe{Axis::new_unchecked(#j)}, is_positive: true } }); } @@ -184,6 +185,8 @@ pub fn implement_direction_list(_item: TokenStream) -> TokenStream { #[doc=#comment_pos] const #u_dir_pos_ident: [Direction<#i>; #i] = [ #(#array_direction_positives),* ]; + #[allow(unsafe_code)] + #[allow(undocumented_unsafe_blocks)] impl DirectionList for Direction<#i> { #[inline] fn directions() -> &'static [Self] { diff --git a/src/lattice/direction/axis.rs b/src/lattice/direction/axis.rs new file mode 100644 index 00000000..d7277f23 --- /dev/null +++ b/src/lattice/direction/axis.rs @@ -0,0 +1,138 @@ +//--------------------------------------- +// uses + +#[cfg(feature = "serde-serialize")] +use serde::{Deserialize, Serialize}; +use utils_lib::{Getter, Sealed}; + +use super::DirectionIndexing; + +//--------------------------------------- +// struct definition + +/// Represent an axis in the space. There is `D` axis in dimension D. Contrary to +/// [`super::Direction`] and [`super::OrientedDirection`], an [`Axis`] +/// does not have an orientation. +#[derive(Sealed, Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[derive(Getter)] +pub struct Axis { + /// index of the axis + #[get(Pub, Const, Copy, self_ty = "value")] + index: usize, +} + +//--------------------------------------- +// main impl block + +impl Axis { + /// Create a new axis. The index should be strictly smaller than `D` to return [`Some`]. + /// # Example + /// ``` + /// use lattice_qcd_rs::{error::ImplementationError, lattice::Axis}; + /// # fn main() -> Result<(), ImplementationError> { + /// assert!(Axis::<0>::new(0).is_none()); + /// assert!(Axis::<0>::new(4).is_none()); + /// + /// let axis = Axis::<1>::new(0).ok_or(ImplementationError::OptionWithUnexpectedNone)?; + /// assert_eq!(axis.index(), 0); + /// assert!(Axis::<1>::new(1).is_none()); + /// + /// let axis = Axis::<4>::new(3).ok_or(ImplementationError::OptionWithUnexpectedNone)?; + /// assert_eq!(axis.index(), 3); + /// assert!(Axis::<4>::new(4).is_none()); + /// assert!(Axis::<4>::new(6).is_none()); + /// # Ok(()) + /// # } + /// ``` + #[inline] + #[must_use] + #[allow(clippy::if_then_some_else_none)] // not possible for const fn + pub const fn new(index: usize) -> Option { + if index < D { + Some(Self { index }) + } else { + None + } + } + + #[inline] + #[must_use] + #[allow(unsafe_code)] + pub(super) const unsafe fn new_unchecked(index: usize) -> Self { + Self { index } + } +} + +//--------------------------------------- +// conversion + +// with usize + +impl From> for usize { + #[inline] + fn from(value: Axis) -> Self { + value.index() + } +} + +impl AsRef for Axis { + #[inline] + fn as_ref(&self) -> &usize { + &self.index + } +} + +// there is no AsMut as is it not safe to give a mut ref to the inner index + +//--------------------------------------- +// lattice indexing + +impl DirectionIndexing for Axis { + #[inline] + fn to_index(&self) -> usize { + self.index() + } + + #[inline] + fn from_index(index: usize) -> Option { + Self::new(index) + } + + #[inline] + fn number_of_elements() -> usize { + D + } +} + +#[cfg(test)] +mod test { + use crate::{error::ImplementationError, lattice::Axis}; + + #[test] + fn axis() -> Result<(), ImplementationError> { + for i in 0..25 { + assert!(Axis::<0>::new(i).is_none()); + } + //------------------ + assert_eq!( + Axis::<1>::new(0).ok_or(ImplementationError::OptionWithUnexpectedNone)?, + Axis { index: 0 } + ); + for i in 1..25 { + assert!(Axis::<1>::new(i).is_none()); + } + //------------------ + for i in 0..4 { + assert_eq!( + Axis::<4>::new(i).ok_or(ImplementationError::OptionWithUnexpectedNone)?, + Axis { index: i } + ); + } + for i in 4..25 { + assert!(Axis::<4>::new(i).is_none()); + } + + Ok(()) + } +} diff --git a/src/lattice/direction/direction_enum.rs b/src/lattice/direction/direction_enum.rs new file mode 100644 index 00000000..d549e298 --- /dev/null +++ b/src/lattice/direction/direction_enum.rs @@ -0,0 +1,402 @@ +//--------------------------------------- +// uses + +use std::cmp::Ordering; +use std::fmt::{self, Display}; +use std::ops::Neg; + +use nalgebra::Vector4; +#[cfg(feature = "serde-serialize")] +use serde::{Deserialize, Serialize}; +use utils_lib::Sealed; + +use super::{Direction, DirectionList}; +use crate::lattice::{LatticeCyclic, LatticeElementToIndex}; +use crate::Real; + +//--------------------------------------- +// struct definition + +// TODO depreciate ? +/// Represent a cardinal direction +#[allow(clippy::exhaustive_enums)] +#[derive(Sealed, Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +pub enum DirectionEnum { + /// Positive x direction. + XPos, + /// Negative x direction. + XNeg, + /// Positive y direction. + YPos, + /// Negative y direction. + YNeg, + /// Positive z direction. + ZPos, + /// Negative z direction. + ZNeg, + /// Positive t direction. + TPos, + /// Negative t direction. + TNeg, +} + +//--------------------------------------- +// main impl block + +impl DirectionEnum { + /// List all directions. + pub const DIRECTIONS: [Self; 8] = [ + Self::XPos, + Self::YPos, + Self::ZPos, + Self::TPos, + Self::XNeg, + Self::YNeg, + Self::ZNeg, + Self::TNeg, + ]; + /// List all spatial directions. + pub const DIRECTIONS_SPACE: [Self; 6] = [ + Self::XPos, + Self::YPos, + Self::ZPos, + Self::XNeg, + Self::YNeg, + Self::ZNeg, + ]; + /// List of all positives directions. + pub const POSITIVES: [Self; 4] = [Self::XPos, Self::YPos, Self::ZPos, Self::TPos]; + /// List spatial positive direction. + pub const POSITIVES_SPACE: [Self; 3] = [Self::XPos, Self::YPos, Self::ZPos]; + + /// Convert the direction into a vector of norm `a`; + #[must_use] + #[inline] + pub fn to_vector(self, a: f64) -> Vector4 { + self.to_unit_vector() * a + } + + /// Convert the direction into a vector of norm `1`; + #[must_use] + #[inline] + pub const fn to_unit_vector(self) -> Vector4 { + match self { + Self::XPos => Vector4::::new(1_f64, 0_f64, 0_f64, 0_f64), + Self::XNeg => Vector4::::new(-1_f64, 0_f64, 0_f64, 0_f64), + Self::YPos => Vector4::::new(0_f64, 1_f64, 0_f64, 0_f64), + Self::YNeg => Vector4::::new(0_f64, -1_f64, 0_f64, 0_f64), + Self::ZPos => Vector4::::new(0_f64, 0_f64, 1_f64, 0_f64), + Self::ZNeg => Vector4::::new(0_f64, 0_f64, -1_f64, 0_f64), + Self::TPos => Vector4::::new(0_f64, 0_f64, 0_f64, 1_f64), + Self::TNeg => Vector4::::new(0_f64, 0_f64, 0_f64, -1_f64), + } + } + + /// Get if the position is positive. + /// # Example + /// ``` + /// # use lattice_qcd_rs::lattice::DirectionEnum; + /// assert_eq!(DirectionEnum::XPos.is_positive(), true); + /// assert_eq!(DirectionEnum::TPos.is_positive(), true); + /// assert_eq!(DirectionEnum::YNeg.is_positive(), false); + /// ``` + #[must_use] + #[inline] + pub const fn is_positive(self) -> bool { + match self { + Self::XPos | Self::YPos | Self::ZPos | Self::TPos => true, + Self::XNeg | Self::YNeg | Self::ZNeg | Self::TNeg => false, + } + } + + /// Get if the position is Negative. see [`DirectionEnum::is_positive`] + #[must_use] + #[inline] + pub const fn is_negative(self) -> bool { + !self.is_positive() + } + + /// Find the direction the vector point the most. + /// For a zero vector return [`DirectionEnum::XPos`]. + /// # Example + /// ``` + /// # use lattice_qcd_rs::lattice::DirectionEnum; + /// # extern crate nalgebra; + /// assert_eq!( + /// DirectionEnum::from_vector(&nalgebra::Vector4::new(1_f64, 0_f64, 0_f64, 0_f64)), + /// DirectionEnum::XPos + /// ); + /// assert_eq!( + /// DirectionEnum::from_vector(&nalgebra::Vector4::new(0_f64, -1_f64, 0_f64, 0_f64)), + /// DirectionEnum::YNeg + /// ); + /// assert_eq!( + /// DirectionEnum::from_vector(&nalgebra::Vector4::new(0.5_f64, 1_f64, 0_f64, 2_f64)), + /// DirectionEnum::TPos + /// ); + /// ``` + #[inline] + #[must_use] + pub fn from_vector(v: &Vector4) -> Self { + let mut max = 0_f64; + let mut index_max: usize = 0; + let mut is_positive = true; + for i in 0..Self::POSITIVES.len() { + let scalar_prod = v.dot(&Self::POSITIVES[i].to_vector(1_f64)); + if scalar_prod.abs() > max { + max = scalar_prod.abs(); + index_max = i; + is_positive = scalar_prod > 0_f64; + } + } + match index_max { + 0 => { + if is_positive { + Self::XPos + } else { + Self::XNeg + } + } + 1 => { + if is_positive { + Self::YPos + } else { + Self::YNeg + } + } + 2 => { + if is_positive { + Self::ZPos + } else { + Self::ZNeg + } + } + 3 => { + if is_positive { + Self::TPos + } else { + Self::TNeg + } + } + _ => { + // the code should attain this code. and therefore panicking is not expected. + unreachable!("Implementation error : invalid index") + } + } + } + + /// Return the positive direction associated, for example `-x` gives `+x` + /// and `+x` gives `+x`. + /// # Example + /// ``` + /// # use lattice_qcd_rs::lattice::DirectionEnum; + /// assert_eq!(DirectionEnum::XNeg.to_positive(), DirectionEnum::XPos); + /// assert_eq!(DirectionEnum::YPos.to_positive(), DirectionEnum::YPos); + /// ``` + #[inline] + #[must_use] + pub const fn to_positive(self) -> Self { + match self { + Self::XNeg => Self::XPos, + Self::YNeg => Self::YPos, + Self::ZNeg => Self::ZPos, + Self::TNeg => Self::TPos, + _ => self, + } + } + + /// Get a index associated to the direction. + /// # Example + /// ``` + /// # use lattice_qcd_rs::lattice::DirectionEnum; + /// assert_eq!(DirectionEnum::XPos.index(), 0); + /// assert_eq!(DirectionEnum::XNeg.index(), 0); + /// assert_eq!(DirectionEnum::YPos.index(), 1); + /// assert_eq!(DirectionEnum::YNeg.index(), 1); + /// assert_eq!(DirectionEnum::ZPos.index(), 2); + /// assert_eq!(DirectionEnum::ZNeg.index(), 2); + /// assert_eq!(DirectionEnum::TPos.index(), 3); + /// assert_eq!(DirectionEnum::TNeg.index(), 3); + /// ``` + #[inline] + #[must_use] + pub const fn index(self) -> usize { + match self { + Self::XPos | Self::XNeg => 0, + Self::YPos | Self::YNeg => 1, + Self::ZPos | Self::ZNeg => 2, + Self::TPos | Self::TNeg => 3, + } + } +} + +//--------------------------------------- +// Common traits + +/// Return [`DirectionEnum::XPos`] +impl Default for DirectionEnum { + ///Return [`DirectionEnum::XPos`] + #[inline] + fn default() -> Self { + Self::XPos + } +} + +impl Display for DirectionEnum { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::XPos => write!(f, "positive X direction"), + Self::XNeg => write!(f, "negative X direction"), + Self::YPos => write!(f, "positive Y direction"), + Self::YNeg => write!(f, "negative Y direction"), + Self::ZPos => write!(f, "positive Z direction"), + Self::ZNeg => write!(f, "negative Z direction"), + Self::TPos => write!(f, "positive T direction"), + Self::TNeg => write!(f, "negative T direction"), + } + } +} + +impl PartialOrd for DirectionEnum { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Direction::<4>::from(self).partial_cmp(&other.into()) + } +} + +//--------------------------------------- +// DirectionList trait + +impl DirectionList for DirectionEnum { + #[inline] + fn directions() -> &'static [Self] { + &Self::DIRECTIONS + } + + #[inline] + fn positive_directions() -> &'static [Self] { + &Self::POSITIVES + } +} + +//--------------------------------------- +// Ops trait + +/// Return the negative of a direction +/// # Example +/// ``` +/// # use lattice_qcd_rs::lattice::DirectionEnum; +/// assert_eq!(-DirectionEnum::XNeg, DirectionEnum::XPos); +/// assert_eq!(-DirectionEnum::YPos, DirectionEnum::YNeg); +/// ``` +impl Neg for DirectionEnum { + type Output = Self; + + #[inline] + fn neg(self) -> Self::Output { + match self { + Self::XPos => Self::XNeg, + Self::XNeg => Self::XPos, + Self::YPos => Self::YNeg, + Self::YNeg => Self::YPos, + Self::ZPos => Self::ZNeg, + Self::ZNeg => Self::ZPos, + Self::TPos => Self::TNeg, + Self::TNeg => Self::TPos, + } + } +} + +impl Neg for &DirectionEnum { + type Output = Self; + + #[inline] + fn neg(self) -> Self::Output { + match self { + DirectionEnum::XPos => &DirectionEnum::XNeg, + DirectionEnum::XNeg => &DirectionEnum::XPos, + DirectionEnum::YPos => &DirectionEnum::YNeg, + DirectionEnum::YNeg => &DirectionEnum::YPos, + DirectionEnum::ZPos => &DirectionEnum::ZNeg, + DirectionEnum::ZNeg => &DirectionEnum::ZPos, + DirectionEnum::TPos => &DirectionEnum::TNeg, + DirectionEnum::TNeg => &DirectionEnum::TPos, + } + } +} + +//--------------------------------------- +// Conversion + +/// Return [`DirectionEnum::to_index`]. +impl From for usize { + #[inline] + fn from(d: DirectionEnum) -> Self { + d.index() + } +} + +/// Return [`DirectionEnum::to_index`]. +impl From<&DirectionEnum> for usize { + #[inline] + fn from(d: &DirectionEnum) -> Self { + DirectionEnum::index(*d) + } +} + +/// Return [`DirectionEnum::from_vector`]. +impl From> for DirectionEnum { + #[inline] + fn from(v: Vector4) -> Self { + Self::from_vector(&v) + } +} + +/// Return [`DirectionEnum::from_vector`]. +impl From<&Vector4> for DirectionEnum { + #[inline] + fn from(v: &Vector4) -> Self { + Self::from_vector(v) + } +} + +/// Return [`DirectionEnum::to_unit_vector`]. +impl From for Vector4 { + #[inline] + fn from(d: DirectionEnum) -> Self { + d.to_unit_vector() + } +} + +/// Return [`DirectionEnum::to_unit_vector`]. +impl From<&DirectionEnum> for Vector4 { + #[inline] + fn from(d: &DirectionEnum) -> Self { + d.to_unit_vector() + } +} + +//--------------------------------------- +// Lattice index traits + +impl LatticeElementToIndex<4> for DirectionEnum { + #[inline] + fn to_index(&self, l: &LatticeCyclic<4>) -> usize { + Direction::<4>::from(self).to_index(l) + } +} + +// impl NumberOfLatticeElement<4> for DirectionEnum { +// #[inline] +// fn number_of_elements(lattice: &LatticeCyclic<4>) -> usize { +// Direction::<4>::number_of_elements(lattice) +// } +// } + +// impl IndexToElement<4> for DirectionEnum { +// fn index_to_element(lattice: &LatticeCyclic<4>, index: usize) -> Option { +// Direction::<4>::index_to_element(lattice, index).map(Into::into) +// } +// } diff --git a/src/lattice/direction/mod.rs b/src/lattice/direction/mod.rs new file mode 100644 index 00000000..7e018a1f --- /dev/null +++ b/src/lattice/direction/mod.rs @@ -0,0 +1,571 @@ +//--------------------------------------- +// mod + +mod axis; +mod direction_enum; +mod oriented; + +//--------------------------------------- +// uses +use std::cmp::Ordering; +use std::convert::TryInto; +use std::error::Error; +use std::fmt::{self, Display}; +use std::ops::Neg; + +use lattice_qcd_rs_procedural_macro::{implement_direction_from, implement_direction_list}; +use nalgebra::SVector; +#[cfg(feature = "serde-serialize")] +use serde::{Deserialize, Serialize}; +use utils_lib::{Getter, Sealed}; + +pub use self::axis::Axis; +pub use self::direction_enum::DirectionEnum; +pub use self::oriented::OrientedDirection; +use super::{IndexToElement, LatticeCyclic, LatticeElementToIndex, NumberOfLatticeElement}; +use crate::{private::Sealed, Real}; + +//--------------------------------------- +// struct definition + +/// Represent a cardinal direction +#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy, Sealed)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[derive(Getter)] +pub struct Direction { + /// Axis of the direction + #[get(Pub, Const, copy)] + #[get_mut(Pub)] + axis: Axis, + /// Orientation + // TODO fix doc about orientation + is_positive: bool, +} + +//--------------------------------------- +// main impl block + +impl Direction { + /// New direction from a direction as an idex and wether it is in the positive direction. + #[must_use] + #[inline] + pub const fn new(index_dir: usize, is_positive: bool) -> Option { + // TODO return error ? + if let Some(axis) = Axis::new(index_dir) { + Some(Self { axis, is_positive }) + } else { + None + } + } + + /// List of all positives directions. + /// This is very slow use [`Self::positive_directions`] instead. + #[allow(clippy::missing_panics_doc)] // it nevers panic + #[must_use] + #[inline] + pub fn positives_vec() -> Vec { + let mut x = Vec::with_capacity(D); + for i in 0..D { + x.push(Self::new(i, true).expect("unreachable")); + } + x + } + + /// List all directions. + /// This is very slow use [`DirectionList::directions`] instead. + #[allow(clippy::missing_panics_doc)] // it nevers panic + #[must_use] + #[inline] + pub fn directions_vec() -> Vec { + let mut x = Vec::with_capacity(2 * D); + for i in 0..D { + x.push(Self::new(i, true).expect("unreachable")); + x.push(Self::new(i, false).expect("unreachable")); + } + x + } + + // TODO add const function for all direction once operation on const generic are added + /// Get all direction with the sign `IS_POSITIVE`. + #[must_use] + #[inline] + #[allow(unsafe_code)] + pub const fn directions_array() -> [Self; D] { + // TODO use unsafe code to avoid the allocation + let mut i = 0_usize; + let mut array = [Self { + // SAFETY: if D = 0 the array is empty so no axis is created + axis: unsafe { Axis::new_unchecked(0) }, + is_positive: IS_POSITIVE, + }; D]; + while i < D { + array[i] = Self { + // SAFETY: i < D + axis: unsafe { Axis::new_unchecked(i) }, + is_positive: IS_POSITIVE, + }; + i += 1; + } + array + } + + /// Get all negative direction + #[must_use] + #[inline] + pub const fn negative_directions() -> [Self; D] { + Self::directions_array::() + } + + /// Get all positive direction + #[allow(clippy::same_name_method)] + #[must_use] + #[inline] + pub const fn positive_directions() -> [Self; D] { + Self::directions_array::() + } + + /// Get if the orientation is positive. + #[must_use] + #[inline] + pub const fn is_positive(&self) -> bool { + self.is_positive + } + + /// Get if the orientation is negative, see [`Self::is_positive`]. + #[must_use] + #[inline] + pub const fn is_negative(&self) -> bool { + !self.is_positive() + } + + /// Return the direction with positive orientation, for example `-x` gives `+x` + /// and `+x` gives `+x`. + /// # Example + /// ``` + /// # use lattice_qcd_rs::{lattice::Direction, error::ImplementationError}; + /// # fn main() -> Result<(), ImplementationError> { + /// assert_eq!( + /// Direction::<4>::new(1, false) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// .to_positive(), + /// Direction::<4>::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// ); + /// assert_eq!( + /// Direction::<4>::new(1, true) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// .to_positive(), + /// Direction::<4>::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// ); + /// # Ok(()) + /// # } + /// ``` + #[must_use] + #[inline] + pub const fn to_positive(mut self) -> Self { + self.is_positive = true; + self + } + + /// Get a index associated to the direction. + /// # Example + /// ``` + /// # use lattice_qcd_rs::{lattice::Direction, error::ImplementationError}; + /// # fn main() -> Result<(), ImplementationError> { + /// assert_eq!( + /// Direction::<4>::new(1, false) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// .index(), + /// 1 + /// ); + /// assert_eq!( + /// Direction::<4>::new(3, true) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// .index(), + /// 3 + /// ); + /// assert_eq!( + /// Direction::<6>::new(5, false) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// .index(), + /// 5 + /// ); + /// # Ok(()) + /// # } + /// ``` + #[must_use] + #[inline] + pub const fn index(&self) -> usize { + self.axis().index() + } + + /// Convert the direction into a vector of norm `a`; + #[must_use] + #[inline] + pub fn to_vector(self, a: f64) -> SVector { + self.to_unit_vector() * a + } + + /// Returns the dimension. + #[must_use] + #[inline] + pub const fn dim() -> usize { + D + } + + /// Convert the direction into a vector of norm `1` + // TODO example + #[must_use] + #[inline] + pub fn to_unit_vector(self) -> SVector { + let mut v = SVector::zeros(); + v[self.index()] = 1_f64; + v + } + + /// Find the direction the vector point the most. + /// For a zero vector return [`DirectionEnum::XPos`]. + /// # Example + /// ``` + /// # use lattice_qcd_rs::lattice::Direction; + /// # use lattice_qcd_rs::error::ImplementationError; + /// # fn main() -> Result<(), Box< dyn std::error::Error>> { + /// assert_eq!( + /// Direction::from_vector(&nalgebra::Vector4::new(1_f64, 0_f64, 0_f64, 0_f64)), + /// Direction::<4>::new(0, true).ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// ); + /// assert_eq!( + /// Direction::from_vector(&nalgebra::Vector4::new(0_f64, -1_f64, 0_f64, 0_f64)), + /// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// ); + /// assert_eq!( + /// Direction::from_vector(&nalgebra::Vector4::new(0.5_f64, 1_f64, 0_f64, 2_f64)), + /// Direction::<4>::new(3, true).ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// ); + /// # Ok(()) + /// # } + /// ``` + #[allow(clippy::missing_panics_doc)] // it nevers panic + #[must_use] + #[inline] + pub fn from_vector(v: &SVector) -> Self { + let mut max = 0_f64; + let mut index_max: usize = 0; + let mut is_positive = true; + for (i, dir) in Self::positive_directions().iter().enumerate() { + let scalar_prod = v.dot(&dir.to_vector(1_f64)); + if scalar_prod.abs() > max { + max = scalar_prod.abs(); + index_max = i; + is_positive = scalar_prod > 0_f64; + } + } + Self::new(index_max, is_positive).expect("Unreachable") + } +} + +//--------------------------------------- +// Common trait implementation + +impl Display for Direction { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "[index {}, is positive {}]", + self.index(), + self.is_positive() + ) + } +} + +// /// TODO impl doc +// impl NumberOfLatticeElement for Direction { +// #[inline] +// fn number_of_elements(_lattice: &LatticeCyclic) -> usize { +// D +// } +// } + +// // TODO impl doc +// impl IndexToElement for Direction { +// fn index_to_element(_lattice: &LatticeCyclic, index: usize) -> Option { +// Self::new(index, true) +// } +// } + +/// Partial ordering is set as follows: two directions can be compared if they have the same index +/// or the same direction sign. In the first case a positive direction is greater than a negative direction +/// In the latter case the ordering is done on the index. +/// # Example +/// ``` +/// # use lattice_qcd_rs::lattice::Direction; +/// # use lattice_qcd_rs::error::ImplementationError; +/// use std::cmp::Ordering; +/// # fn main() -> Result<(), Box< dyn std::error::Error>> { +/// +/// let dir_1 = +/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// let dir_2 = +/// Direction::<4>::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// assert!(dir_1 < dir_2); +/// assert_eq!(dir_1.partial_cmp(&dir_2), Some(Ordering::Less)); +/// //-------- +/// let dir_3 = +/// Direction::<4>::new(3, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// let dir_4 = +/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// assert!(dir_3 > dir_4); +/// assert_eq!(dir_3.partial_cmp(&dir_4), Some(Ordering::Greater)); +/// //-------- +/// let dir_5 = +/// Direction::<4>::new(3, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// let dir_6 = +/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// assert_eq!(dir_5.partial_cmp(&dir_6), None); +/// //-------- +/// let dir_5 = +/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// let dir_6 = +/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; +/// assert_eq!(dir_5.partial_cmp(&dir_6), Some(Ordering::Equal)); +/// # Ok(()) +/// # } +/// ``` +impl PartialOrd for Direction { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + if self == other { + Some(Ordering::Equal) + } else if self.is_positive() == other.is_positive() { + self.index().partial_cmp(&other.index()) + } else if self.index() == other.index() { + self.is_positive().partial_cmp(&other.is_positive()) + } else { + None + } + } +} + +//--------------------------------------- +// Ops traits + +impl Neg for Direction { + type Output = Self; + + #[inline] + fn neg(mut self) -> Self::Output { + self.is_positive = !self.is_positive; + self + } +} + +impl Neg for &Direction { + type Output = Direction; + + #[inline] + fn neg(self) -> Self::Output { + -*self + } +} + +//--------------------------------------- +// Conversion + +/// Return [`Direction::index`]. +impl From> for usize { + #[inline] + fn from(d: Direction) -> Self { + d.index() + } +} + +/// Return [`Direction::index`]. +impl From<&Direction> for usize { + #[inline] + fn from(d: &Direction) -> Self { + d.index() + } +} + +/// Return [`DirectionEnum::from_vector`]. +impl From> for Direction { + #[inline] + fn from(v: SVector) -> Self { + Self::from_vector(&v) + } +} + +/// Return [`DirectionEnum::from_vector`]. +impl From<&SVector> for Direction { + #[inline] + fn from(v: &SVector) -> Self { + Self::from_vector(v) + } +} + +/// Return [`Direction::to_unit_vector`]. +impl From> for SVector { + #[inline] + fn from(d: Direction) -> Self { + d.to_unit_vector() + } +} + +/// Return [`Direction::to_unit_vector`]. +impl From<&Direction> for SVector { + #[inline] + fn from(d: &Direction) -> Self { + d.to_unit_vector() + } +} + +impl From for Direction<4> { + #[inline] + fn from(d: DirectionEnum) -> Self { + Self::new(DirectionEnum::index(d), d.is_positive()).expect("unreachable") + } +} + +impl From> for DirectionEnum { + #[inline] + fn from(d: Direction<4>) -> Self { + d.into() + } +} + +impl From<&Direction<4>> for DirectionEnum { + #[inline] + fn from(d: &Direction<4>) -> Self { + match (d.index(), d.is_positive()) { + (0, true) => Self::XPos, + (0, false) => Self::XNeg, + (1, true) => Self::YPos, + (1, false) => Self::YNeg, + (2, true) => Self::ZPos, + (2, false) => Self::ZNeg, + (3, true) => Self::TPos, + (3, false) => Self::TNeg, + _ => unreachable!(), + } + } +} + +impl From<&DirectionEnum> for Direction<4> { + #[inline] + fn from(d: &DirectionEnum) -> Self { + Self::new(DirectionEnum::index(*d), d.is_positive()).expect("unreachable") + } +} + +// with Direction + +/// create a [`Direction`] with positive orientation of the same axis. +impl From> for Direction { + #[inline] + fn from(value: Axis) -> Self { + Self::new(value.index(), true).expect("always exists") + } +} + +impl From> for Axis { + #[inline] + fn from(value: Direction) -> Self { + value.axis() + } +} + +impl AsRef> for Direction { + #[inline] + fn as_ref(&self) -> &Axis { + &self.axis + } +} + +impl AsMut> for Direction { + #[inline] + fn as_mut(&mut self) -> &mut Axis { + self.axis_mut() + } +} + +//--------------------------------------- +// Procedural macro implementation + +/// List all possible direction +pub trait DirectionList: Sized { + /// List all directions. + #[must_use] + fn directions() -> &'static [Self]; + /// List all positive directions. + #[must_use] + fn positive_directions() -> &'static [Self]; +} + +/// Error return by [`TryFrom`] for [`Direction`]. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum DirectionConversionError { + /// The index is out of bound, i.e. the direction axis does not exist in the lower space dimension. + IndexOutOfBound, // more error info like dim and index +} + +impl Display for DirectionConversionError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::IndexOutOfBound => write!(f, "the index is out of bound, the direction axis does not exist in the lower space dimension"), + } + } +} + +impl Error for DirectionConversionError { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::IndexOutOfBound => None, + } + } +} + +implement_direction_list!(); + +implement_direction_from!(); + +//--------------------------------------- +// trait DirectionIndexing + +pub trait DirectionIndexing: Sealed + Sized { + #[must_use] + fn to_index(&self) -> usize; + + #[must_use] + fn from_index(index: usize) -> Option; + + #[must_use] + fn number_of_elements() -> usize; +} + +//--------------------------------------- +// trait auto impl + +impl LatticeElementToIndex for T { + #[inline] + fn to_index(&self, _lattice: &LatticeCyclic) -> usize { + self.to_index() + } +} + +impl NumberOfLatticeElement for T { + #[inline] + fn number_of_elements(_lattice: &LatticeCyclic) -> usize { + T::number_of_elements() + } +} + +impl IndexToElement for T { + #[inline] + fn index_to_element(_lattice: &LatticeCyclic, index: usize) -> Option { + T::from_index(index) + } +} diff --git a/src/lattice/direction/oriented.rs b/src/lattice/direction/oriented.rs new file mode 100644 index 00000000..d79a2df1 --- /dev/null +++ b/src/lattice/direction/oriented.rs @@ -0,0 +1,309 @@ +//--------------------------------------- +// uses + +use std::{ + error::Error, + fmt::{self, Display}, + ops::Neg, + usize, +}; + +use nalgebra::SVector; +#[cfg(feature = "serde-serialize")] +use serde::{Deserialize, Serialize}; +use utils_lib::{Getter, Sealed}; + +use super::{Axis, Direction, DirectionIndexing}; +use crate::Real; + +//--------------------------------------- +// struct definition + +/// A cardinal direction whose orientation is set in the type. +/// It is similar to [`Direction`] and to [`Axis`]. +#[derive(Sealed, Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[derive(Getter)] +pub struct OrientedDirection { + /// axis of the direction + #[get(Pub, Const, Copy, self_ty = "value")] + #[get_mut(Pub)] + axis: Axis, +} + +//--------------------------------------- +// main impl block + +impl OrientedDirection { + /// Create a new oriented direction. It returns [`Some`] only if `index < D` + /// # Example + /// ``` + /// use lattice_qcd_rs::{error::ImplementationError, lattice::OrientedDirection}; + /// # fn main() -> Result<(), ImplementationError> { + /// assert!(OrientedDirection::<0, true>::new(0).is_none()); + /// assert!(OrientedDirection::<0, false>::new(4).is_none()); + /// + /// let o_dir = OrientedDirection::<1, true>::new(0) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)?; + /// assert_eq!(o_dir.index(), 0); + /// assert!(OrientedDirection::<1, false>::new(1).is_none()); + /// + /// let o_dir = OrientedDirection::<4, true>::new(3) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)?; + /// assert_eq!(o_dir.index(), 3); + /// assert!(OrientedDirection::<4, false>::new(4).is_none()); + /// assert!(OrientedDirection::<4, true>::new(6).is_none()); + /// # Ok(()) + /// # } + /// ``` + #[inline] + #[must_use] + pub const fn new(index: usize) -> Option { + if let Some(axis) = Axis::new(index) { + Some(Self { axis }) + } else { + None + } + } + + #[inline] + #[must_use] + pub const fn index(self) -> usize { + self.axis().index() + } + + /// Get if the orientation is positive. + #[inline] + #[must_use] + pub const fn is_positive() -> bool { + ORIENTATION + } + + /// Get if the orientation is negative, see [`Self::is_positive`]. + #[inline] + #[must_use] + pub const fn is_negative() -> bool { + !ORIENTATION + } + + /// Returns the dimension. + #[inline] + #[must_use] + pub const fn dim() -> usize { + D + } + + /// Return the cardinal direction with positive orientation, for example `-x` gives `+x` + /// and `+x` gives `+x`. + /// # Example + /// ``` + /// use lattice_qcd_rs::{error::ImplementationError, lattice::OrientedDirection}; + /// # fn main() -> Result<(), ImplementationError> { + /// assert_eq!( + /// OrientedDirection::<4, false>::new(1) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// .to_positive(), + /// OrientedDirection::<4, true>::new(1) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// ); + /// assert_eq!( + /// OrientedDirection::<4, true>::new(1) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// .to_positive(), + /// OrientedDirection::<4, true>::new(1) + /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? + /// ); + /// # Ok(()) + /// # } + /// ``` + #[must_use] + #[inline] + pub const fn to_positive(self) -> OrientedDirection { + // TODO use expect when it is constant + if let Some(c) = OrientedDirection::new(self.index()) { + c + } else { + unreachable!() + } + } + + /// Convert the direction into a vector of norm `1`; + #[must_use] + #[inline] + pub fn to_unit_vector(self) -> SVector { + Direction::from(self).to_unit_vector() + } +} + +//--------------------------------------- +// conversion + +// with self + +impl From> for OrientedDirection { + #[inline] + fn from(value: OrientedDirection) -> Self { + Self::new(value.axis().index()).expect("always exist") + } +} + +impl From> for OrientedDirection { + #[inline] + fn from(value: OrientedDirection) -> Self { + Self::new(value.axis().index()).expect("always exist") + } +} + +// with axis + +impl From> for OrientedDirection { + #[inline] + fn from(value: Axis) -> Self { + Self::new(value.index()).expect("always exists") + } +} + +impl From> for Axis { + #[inline] + fn from(value: OrientedDirection) -> Self { + value.axis() + } +} + +impl AsRef> for OrientedDirection { + #[inline] + fn as_ref(&self) -> &Axis { + &self.axis + } +} + +impl AsMut> for OrientedDirection { + #[inline] + fn as_mut(&mut self) -> &mut Axis { + &mut self.axis + } +} + +// with direction + +impl From> for Direction { + #[inline] + fn from(value: OrientedDirection) -> Self { + Self::new(value.index(), DIR).expect("always exist") + } +} + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +#[non_exhaustive] +pub enum CardinalDirectionConversionError { + WrongOrientation, + IndexOutOfBound, +} + +impl Display for CardinalDirectionConversionError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "conversion error: ")?; + match self { + Self::WrongOrientation => write!(f, "wrong orientation"), + Self::IndexOutOfBound => write!(f, "the index is out of bound"), + } + } +} + +impl Error for CardinalDirectionConversionError { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::WrongOrientation | Self::IndexOutOfBound => None, + } + } +} + +impl TryFrom> for OrientedDirection { + type Error = CardinalDirectionConversionError; + + #[inline] + fn try_from(dir: Direction) -> Result { + if dir.is_positive() && DIR { + Self::new(dir.index()).ok_or(CardinalDirectionConversionError::IndexOutOfBound) + } else { + Err(CardinalDirectionConversionError::WrongOrientation) + } + } +} + +// with usize + +impl From> for usize { + #[inline] + fn from(value: OrientedDirection) -> Self { + value.index() + } +} + +impl AsRef for OrientedDirection { + #[inline] + fn as_ref(&self) -> &usize { + >>::as_ref(self).as_ref() + } +} + +//--------------------------------------- +// operator + +impl Neg for OrientedDirection { + type Output = OrientedDirection; + + #[inline] + fn neg(self) -> Self::Output { + self.into() + } +} + +impl Neg for OrientedDirection { + type Output = OrientedDirection; + + #[inline] + fn neg(self) -> Self::Output { + self.into() + } +} + +//--------------------------------------- +// lattice indexing + +impl DirectionIndexing + for OrientedDirection +{ + #[inline] + fn to_index(&self) -> usize { + self.index() + } + + #[inline] + fn from_index(index: usize) -> Option { + Self::new(index) + } + + #[inline] + fn number_of_elements() -> usize { + D + } +} + +// /// TODO impl doc +// impl NumberOfLatticeElement for Direction { +// #[inline] +// fn number_of_elements(_lattice: &LatticeCyclic) -> usize { +// D +// } +// } + +// // TODO impl doc +// impl IndexToElement for Direction { +// fn index_to_element(_lattice: &LatticeCyclic, index: usize) -> Option { +// Self::new(index, true) +// } +// } diff --git a/src/lattice/iterator/mod.rs b/src/lattice/iterator/mod.rs index 6b743701..45cb6d12 100644 --- a/src/lattice/iterator/mod.rs +++ b/src/lattice/iterator/mod.rs @@ -20,6 +20,7 @@ mod point; use std::{ fmt::{self, Display}, iter::FusedIterator, + mem, }; use rayon::iter::{ @@ -28,7 +29,7 @@ use rayon::iter::{ }; #[cfg(feature = "serde-serialize")] use serde::{Deserialize, Serialize}; -use utils_lib::Getter; +use utils_lib::{Getter, Sealed}; pub use self::direction::IteratorDirection; pub use self::link_canonical::{IteratorLatticeLinkCanonical, ParIterLatticeLinkCanonical}; @@ -40,7 +41,7 @@ use crate::private::Sealed; /// /// It has a notion of dimension as it is link to the notion of lattice element. /// And a lattice takes a dimension. -pub trait RandomAccessIterator: Sealed { +pub trait RandomAccessIterator: Sealed { /// Type of element return by the iterator type Item; @@ -48,26 +49,30 @@ pub trait RandomAccessIterator: Sealed { #[must_use] fn iter_len(&self) -> usize; - /// Increase the given front element by the given number . + /// Increase the given front element by the given number and return the result + /// without modifying the iterator. #[must_use] - fn increase_element_by( - lattice: &LatticeCyclic, - front_element: &IteratorElement, - advance_by: usize, - ) -> IteratorElement; + fn increase_front_element_by(&self, advance_by: usize) -> IteratorElement; - /// decrease the back element by a given number. + /// Decrease the given end element by the given number and return the result + /// without modifying the iterator. #[must_use] - fn decrease_element_by( - lattice: &LatticeCyclic, - end_element: &IteratorElement, - back_by: usize, - ) -> IteratorElement; + fn decrease_end_element_by(&self, back_by: usize) -> IteratorElement; + + // /// Increase the given front element by the given number and modify the iterator. + // fn increase_front_by(&mut self, advance_by: usize) { + // *self.as_mut().front_mut() = self.increase_front_element_by(advance_by); + // } + + // /// Decrease the end element by a given number and modify the iterator. + // fn decrease_end_by(&mut self, back_by: usize) { + // *self.as_mut().end_mut() = self.decrease_end_element_by(back_by); + // } } /// Enum for internal use of iterator. It store the previous element returned by `next` #[allow(clippy::exhaustive_enums)] -#[derive(Debug, Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)] +#[derive(Sealed, Debug, Clone, Copy, Hash, PartialOrd, Ord, PartialEq, Eq)] #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] pub enum IteratorElement { /// First element of the iterator @@ -145,9 +150,6 @@ impl IteratorElement { } } -//#[doc(hidden)] -impl Sealed for IteratorElement {} - impl Display for IteratorElement { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -211,8 +213,8 @@ impl + NumberOfLatticeElement> /// to track the front and the back. [`Iterator`] traits are not (yet ?) implemented /// on this type. #[derive(Getter, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -struct DoubleEndedCounter { +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +pub struct DoubleEndedCounter { /// Front element of the iterator. The state need to be increased before /// being returned by the next [`Iterator::next`] call. #[get(Const)] @@ -297,7 +299,7 @@ impl Display for DoubleEndedCounter { /// todo!() /// ``` // TODO bound explanation -#[derive(Getter, Debug, Clone, PartialEq)] +#[derive(Sealed, Getter, Debug, Clone, PartialEq)] pub struct LatticeIterator<'a, const D: usize, T> { /// ref to the lattice #[get(Const, copy)] @@ -374,7 +376,7 @@ impl<'a, const D: usize, T> LatticeIterator<'a, D, T> { #[inline] pub const fn new(lattice: &'a LatticeCyclic) -> Self where - Self: RandomAccessIterator, + Self: RandomAccessIterator, T: Clone, { Self { @@ -453,16 +455,28 @@ impl<'a, const D: usize, T> LatticeIterator<'a, D, T> { #[inline] pub fn new_with_first_element(lattice: &'a LatticeCyclic, first_el: T) -> Self where - Self: RandomAccessIterator, + Self: RandomAccessIterator, T: Clone, { - Self { + //TODO / FIXME + + // we can't really decrease the first element so we use some trick. + let mut s = Self { lattice, counter: DoubleEndedCounter { - front: Self::decrease_element_by(lattice, &IteratorElement::Element(first_el), 1), - end: IteratorElement::LastElement, + // we define the front and end reversed. + // we will swap both value afterward + front: IteratorElement::LastElement, + end: IteratorElement::Element(first_el), }, - } + }; + + // Then we decrease `end`. Also this is fine we don't verify both end of + // the iterator so we don't care that the iter should produce None. + *s.end_mut() = s.decrease_end_element_by(1); + // we then swap the value to get a properly define iterator + mem::swap(&mut s.counter.front, &mut s.counter.end); + s } /// Convert the iterator into an [`IndexedParallelIterator`]. @@ -470,7 +484,7 @@ impl<'a, const D: usize, T> LatticeIterator<'a, D, T> { #[inline] pub const fn par_iter(self) -> LatticeParIter<'a, D, T> where - Self: RandomAccessIterator, + Self: RandomAccessIterator, { LatticeParIter::new(self) } @@ -506,9 +520,6 @@ impl<'a, const D: usize, T: Display> Display for LatticeIterator<'a, D, T> { } } -//#[doc(hidden)] -impl<'a, const D: usize, T> Sealed for LatticeIterator<'a, D, T> {} - impl<'a, const D: usize, T> AsRef> for LatticeIterator<'a, D, T> { #[inline] fn as_ref(&self) -> &DoubleEndedCounter { @@ -544,7 +555,7 @@ impl<'a, const D: usize, T> From<&'a LatticeIterator<'a, D, T>> for &'a LatticeC } } -impl<'a, const D: usize, T> RandomAccessIterator for LatticeIterator<'a, D, T> +impl<'a, const D: usize, T> RandomAccessIterator for LatticeIterator<'a, D, T> where T: LatticeElementToIndex + NumberOfLatticeElement + IndexToElement, { @@ -558,14 +569,10 @@ where .saturating_sub(self.end().size_position(self.lattice())) } - fn increase_element_by( - lattice: &LatticeCyclic, - front_element: &IteratorElement, - advance_by: usize, - ) -> IteratorElement { - let index = match front_element { + fn increase_front_element_by(&self, advance_by: usize) -> IteratorElement { + let index = match self.front() { IteratorElement::FirstElement => 0, - IteratorElement::Element(ref element) => element.to_index(lattice) + 1, + IteratorElement::Element(ref element) => element.to_index(self.lattice()) + 1, IteratorElement::LastElement => { // early return return IteratorElement::LastElement; @@ -574,27 +581,23 @@ where let new_index = index + advance_by; IteratorElement::index_to_element(new_index, |index| { - Self::Item::index_to_element(lattice, index) + Self::Item::index_to_element(self.lattice(), index) }) } - fn decrease_element_by( - lattice: &LatticeCyclic, - end_element: &IteratorElement, - back_by: usize, - ) -> IteratorElement { - let index = match end_element { + fn decrease_end_element_by(&self, back_by: usize) -> IteratorElement { + let index = match self.end() { IteratorElement::FirstElement => { // early return return IteratorElement::FirstElement; } - IteratorElement::Element(ref element) => element.to_index(lattice) + 1, - IteratorElement::LastElement => lattice.number_of_points() + 1, + IteratorElement::Element(ref element) => element.to_index(self.lattice()) + 1, + IteratorElement::LastElement => self.lattice().number_of_points() + 1, }; let new_index = index.saturating_sub(back_by); IteratorElement::index_to_element(new_index, |index| { - Self::Item::index_to_element(lattice, index) + Self::Item::index_to_element(self.lattice(), index) }) } } @@ -602,7 +605,7 @@ where /// TODO DOC impl<'a, const D: usize, T> Iterator for LatticeIterator<'a, D, T> where - Self: RandomAccessIterator, + Self: RandomAccessIterator, T: Clone, { type Item = T; @@ -645,7 +648,7 @@ where } return None; } - let next_element = Self::increase_element_by(self.lattice(), self.front(), n + 1); + let next_element = self.increase_front_element_by(n + 1); *self.front_mut() = next_element.clone(); next_element.into() } @@ -672,7 +675,7 @@ where /// TODO DOC impl<'a, const D: usize, T> DoubleEndedIterator for LatticeIterator<'a, D, T> where - Self: RandomAccessIterator, + Self: RandomAccessIterator, T: Clone, { #[inline] @@ -691,7 +694,7 @@ where } return None; } - let previous_element = Self::decrease_element_by(self.lattice(), self.end(), n + 1); + let previous_element = self.decrease_end_element_by(n + 1); *self.end_mut() = previous_element.clone(); previous_element.into() } @@ -699,14 +702,14 @@ where impl<'a, const D: usize, T> FusedIterator for LatticeIterator<'a, D, T> where - LatticeIterator<'a, D, T>: RandomAccessIterator, + LatticeIterator<'a, D, T>: RandomAccessIterator, T: Clone, { } impl<'a, const D: usize, T> ExactSizeIterator for LatticeIterator<'a, D, T> where - LatticeIterator<'a, D, T>: RandomAccessIterator, + LatticeIterator<'a, D, T>: RandomAccessIterator, T: Clone, { #[inline] @@ -731,7 +734,7 @@ impl<'a, const D: usize, T> From> for LatticeIterator< impl<'a, const D: usize, T> IntoParallelIterator for LatticeIterator<'a, D, T> where - Self: RandomAccessIterator, + Self: RandomAccessIterator, T: Clone + Send, { type Iter = LatticeParIter<'a, D, T>; @@ -787,7 +790,7 @@ impl<'a, const D: usize, T> From> for LatticeProducer<' impl<'a, const D: usize, T> Producer for LatticeProducer<'a, D, T> where - LatticeIterator<'a, D, T>: RandomAccessIterator, + LatticeIterator<'a, D, T>: RandomAccessIterator, T: Clone + Send, { type Item = ::Item; @@ -800,11 +803,7 @@ where #[inline] fn split_at(self, index: usize) -> (Self, Self) { - let splinting = Self::IntoIter::increase_element_by( - self.as_iter().lattice(), - self.as_iter().front(), - index, - ); + let splinting = self.as_iter().increase_front_element_by(index); ( Self(Self::IntoIter { lattice: self.0.lattice, @@ -840,7 +839,7 @@ impl<'a, const D: usize, T> AsMut> for LatticeProducer impl<'a, const D: usize, T> IntoIterator for LatticeProducer<'a, D, T> where - LatticeIterator<'a, D, T>: RandomAccessIterator, + LatticeIterator<'a, D, T>: RandomAccessIterator, T: Clone, { type Item = ::Item; @@ -900,7 +899,7 @@ impl<'a, const D: usize, T> LatticeParIter<'a, D, T> { impl<'a, const D: usize, T> ParallelIterator for LatticeParIter<'a, D, T> where - LatticeIterator<'a, D, T>: RandomAccessIterator, + LatticeIterator<'a, D, T>: RandomAccessIterator, T: Clone + Send, { type Item = T; @@ -947,7 +946,7 @@ where impl<'a, const D: usize, T> IndexedParallelIterator for LatticeParIter<'a, D, T> where - LatticeIterator<'a, D, T>: RandomAccessIterator, + LatticeIterator<'a, D, T>: RandomAccessIterator, T: Clone + Send, { #[inline] @@ -1010,7 +1009,7 @@ impl<'a, const D: usize, T> AsMut> for LatticeIterator< impl<'a, const D: usize, T> IntoIterator for LatticeParIter<'a, D, T> where - LatticeIterator<'a, D, T>: RandomAccessIterator, + LatticeIterator<'a, D, T>: RandomAccessIterator, T: Clone, { type Item = ::Item; diff --git a/src/lattice/mod.rs b/src/lattice/mod.rs index cf83ed70..7c026d62 100644 --- a/src/lattice/mod.rs +++ b/src/lattice/mod.rs @@ -9,29 +9,29 @@ //! [`LatticePoint`], [`LatticeLink`] and [`LatticeLinkCanonical`] are elements on the lattice. //! They encode where a field element is situated. +mod direction; mod iterator; mod lattice_cyclic; -use std::cmp::Ordering; use std::convert::TryInto; -use std::error::Error; use std::fmt::{self, Display}; use std::iter::FusedIterator; -use std::ops::{Index, IndexMut, Neg}; +use std::ops::{Index, IndexMut}; -use lattice_qcd_rs_procedural_macro::{implement_direction_from, implement_direction_list}; -use nalgebra::{SVector, Vector4}; +use nalgebra::SVector; #[cfg(feature = "serde-serialize")] use serde::{Deserialize, Serialize}; use utils_lib::Sealed; +pub use self::direction::{ + Axis, Direction, DirectionConversionError, DirectionEnum, DirectionList, OrientedDirection, +}; // TODO remove IteratorElement from public interface ? pub use self::iterator::{ IteratorDirection, IteratorElement, IteratorLatticeLinkCanonical, IteratorLatticePoint, LatticeIterator, LatticeParIter, ParIterLatticeLinkCanonical, ParIterLatticePoint, }; pub use self::lattice_cyclic::LatticeCyclic; -use super::Real; use crate::private::Sealed; /// Represents point on a (any) lattice. @@ -349,6 +349,79 @@ impl IndexToElement for () { } } +/// A lattice link, contrary to [`LatticeLinkCanonical`] the direction can be negative. +/// +/// This means that multiple link can be equivalent but does not have the same data +/// and therefore hash (hopefully). +/// +/// By itself the link does not store data about the lattice. Hence most function require a [`LatticeCyclic`]. +/// It also means that there is no guarantee that the object is inside a lattice. +/// You can use modulus over the elements to use inside a lattice. +#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] +#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +pub struct LatticeLink { + from: LatticePoint, + dir: Direction, +} + +impl LatticeLink { + /// Create a link from position `from` and direction `dir`. + #[must_use] + #[inline] + pub const fn new(from: LatticePoint, dir: Direction) -> Self { + Self { from, dir } + } + + /// Get the position of the link. + #[must_use] + #[inline] + pub const fn pos(&self) -> &LatticePoint { + &self.from + } + + /// Get a mutable reference to the position of the link. + #[must_use] + #[inline] + pub fn pos_mut(&mut self) -> &mut LatticePoint { + &mut self.from + } + + /// Get the direction of the link. + #[must_use] + #[inline] + pub const fn dir(&self) -> &Direction { + &self.dir + } + + /// Get a mutable reference to the direction of the link. + #[must_use] + #[inline] + pub fn dir_mut(&mut self) -> &mut Direction { + &mut self.dir + } + + /// Get if the direction of the link is positive. + #[must_use] + #[inline] + pub const fn is_dir_positive(&self) -> bool { + self.dir.is_positive() + } + + /// Get if the direction of the link is negative. + #[must_use] + #[inline] + pub const fn is_dir_negative(&self) -> bool { + self.dir.is_negative() + } +} + +impl Display for LatticeLink { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "link [position {}, direction {}]", self.from, self.dir) + } +} + /// A canonical link of a lattice. It contain a position and a direction. /// /// The direction should always be positive. @@ -504,296 +577,6 @@ impl IndexToElement for LatticeLinkCanonical { } } -/// A lattice link, contrary to [`LatticeLinkCanonical`] the direction can be negative. -/// -/// This means that multiple link can be equivalent but does not have the same data -/// and therefore hash (hopefully). -/// -/// By itself the link does not store data about the lattice. Hence most function require a [`LatticeCyclic`]. -/// It also means that there is no guarantee that the object is inside a lattice. -/// You can use modulus over the elements to use inside a lattice. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -pub struct LatticeLink { - from: LatticePoint, - dir: Direction, -} - -impl LatticeLink { - /// Create a link from position `from` and direction `dir`. - #[must_use] - #[inline] - pub const fn new(from: LatticePoint, dir: Direction) -> Self { - Self { from, dir } - } - - /// Get the position of the link. - #[must_use] - #[inline] - pub const fn pos(&self) -> &LatticePoint { - &self.from - } - - /// Get a mutable reference to the position of the link. - #[must_use] - #[inline] - pub fn pos_mut(&mut self) -> &mut LatticePoint { - &mut self.from - } - - /// Get the direction of the link. - #[must_use] - #[inline] - pub const fn dir(&self) -> &Direction { - &self.dir - } - - /// Get a mutable reference to the direction of the link. - #[must_use] - #[inline] - pub fn dir_mut(&mut self) -> &mut Direction { - &mut self.dir - } - - /// Get if the direction of the link is positive. - #[must_use] - #[inline] - pub const fn is_dir_positive(&self) -> bool { - self.dir.is_positive() - } - - /// Get if the direction of the link is negative. - #[must_use] - #[inline] - pub const fn is_dir_negative(&self) -> bool { - self.dir.is_negative() - } -} - -impl Display for LatticeLink { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "link [position {}, direction {}]", self.from, self.dir) - } -} - -/// Represent a cardinal direction -#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy, Sealed)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -pub struct Direction { - index_dir: usize, - is_positive: bool, -} - -impl Direction { - /// New direction from a direction as an idex and wether it is in the positive direction. - #[must_use] - #[inline] - pub const fn new(index_dir: usize, is_positive: bool) -> Option { - // TODO return error ? - if index_dir >= D { - return None; - } - Some(Self { - index_dir, - is_positive, - }) - } - - /// List of all positives directions. - /// This is very slow use [`Self::positive_directions`] instead. - #[allow(clippy::missing_panics_doc)] // it nevers panic - #[must_use] - #[inline] - pub fn positives_vec() -> Vec { - let mut x = Vec::with_capacity(D); - for i in 0..D { - x.push(Self::new(i, true).expect("unreachable")); - } - x - } - - /// List all directions. - /// This is very slow use [`DirectionList::directions`] instead. - #[allow(clippy::missing_panics_doc)] // it nevers panic - #[must_use] - #[inline] - pub fn directions_vec() -> Vec { - let mut x = Vec::with_capacity(2 * D); - for i in 0..D { - x.push(Self::new(i, true).expect("unreachable")); - x.push(Self::new(i, false).expect("unreachable")); - } - x - } - - // TODO add const function for all direction once operation on const generic are added - /// Get all direction with the sign `IS_POSITIVE`. - #[must_use] - #[inline] - pub const fn directions_array() -> [Self; D] { - // TODO use unsafe code to avoid the allocation - let mut i = 0_usize; - let mut array = [Self { - index_dir: 0, - is_positive: IS_POSITIVE, - }; D]; - while i < D { - array[i] = Self { - index_dir: i, - is_positive: IS_POSITIVE, - }; - i += 1; - } - array - } - - /// Get all negative direction - #[must_use] - #[inline] - pub const fn negative_directions() -> [Self; D] { - Self::directions_array::() - } - - /// Get all positive direction - #[allow(clippy::same_name_method)] - #[must_use] - #[inline] - pub const fn positive_directions() -> [Self; D] { - Self::directions_array::() - } - - /// Get if the position is positive. - #[must_use] - #[inline] - pub const fn is_positive(&self) -> bool { - self.is_positive - } - - /// Get if the position is Negative. see [`Direction::is_positive`] - #[must_use] - #[inline] - pub const fn is_negative(&self) -> bool { - !self.is_positive() - } - - /// Return the positive direction associated, for example `-x` gives `+x` - /// and `+x` gives `+x`. - /// # Example - /// ``` - /// # use lattice_qcd_rs::lattice::Direction; - /// assert_eq!( - /// Direction::<4>::new(1, false).unwrap().to_positive(), - /// Direction::<4>::new(1, true).unwrap() - /// ); - /// assert_eq!( - /// Direction::<4>::new(1, true).unwrap().to_positive(), - /// Direction::<4>::new(1, true).unwrap() - /// ); - /// ``` - #[must_use] - #[inline] - pub const fn to_positive(mut self) -> Self { - self.is_positive = true; - self - } - - /// Get a index associated to the direction. - /// # Example - /// ``` - /// # use lattice_qcd_rs::{lattice::Direction, error::ImplementationError}; - /// # fn main() -> Result<(), ImplementationError> { - /// assert_eq!( - /// Direction::<4>::new(1, false) - /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? - /// .index(), - /// 1 - /// ); - /// assert_eq!( - /// Direction::<4>::new(3, true) - /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? - /// .index(), - /// 3 - /// ); - /// assert_eq!( - /// Direction::<6>::new(5, false) - /// .ok_or(ImplementationError::OptionWithUnexpectedNone)? - /// .index(), - /// 5 - /// ); - /// # Ok(()) - /// # } - /// ``` - #[must_use] - #[inline] - pub const fn index(&self) -> usize { - self.index_dir - } - - /// Convert the direction into a vector of norm `a`; - #[must_use] - #[inline] - pub fn to_vector(self, a: f64) -> SVector { - self.to_unit_vector() * a - } - - /// Returns the dimension - #[must_use] - #[inline] - pub const fn dim() -> usize { - D - } - - /// Convert the direction into a vector of norm `1`; - #[must_use] - #[inline] - pub fn to_unit_vector(self) -> SVector { - let mut v = SVector::zeros(); - v[self.index_dir] = 1_f64; - v - } - - /// Find the direction the vector point the most. - /// For a zero vector return [`DirectionEnum::XPos`]. - /// # Example - /// ``` - /// # use lattice_qcd_rs::lattice::Direction; - /// # use lattice_qcd_rs::error::ImplementationError; - /// # fn main() -> Result<(), Box< dyn std::error::Error>> { - /// assert_eq!( - /// Direction::from_vector(&nalgebra::Vector4::new(1_f64, 0_f64, 0_f64, 0_f64)), - /// Direction::<4>::new(0, true).ok_or(ImplementationError::OptionWithUnexpectedNone)? - /// ); - /// assert_eq!( - /// Direction::from_vector(&nalgebra::Vector4::new(0_f64, -1_f64, 0_f64, 0_f64)), - /// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)? - /// ); - /// assert_eq!( - /// Direction::from_vector(&nalgebra::Vector4::new(0.5_f64, 1_f64, 0_f64, 2_f64)), - /// Direction::<4>::new(3, true).ok_or(ImplementationError::OptionWithUnexpectedNone)? - /// ); - /// # Ok(()) - /// # } - /// ``` - #[allow(clippy::missing_panics_doc)] // it nevers panic - #[must_use] - #[inline] - pub fn from_vector(v: &SVector) -> Self { - let mut max = 0_f64; - let mut index_max: usize = 0; - let mut is_positive = true; - for (i, dir) in Self::positive_directions().iter().enumerate() { - let scalar_prod = v.dot(&dir.to_vector(1_f64)); - if scalar_prod.abs() > max { - max = scalar_prod.abs(); - index_max = i; - is_positive = scalar_prod > 0_f64; - } - } - Self::new(index_max, is_positive).expect("Unreachable") - } -} - // TODO default when condition on const generic is available /* @@ -822,600 +605,6 @@ where } */ -impl Display for Direction { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "[index {}, is positive {}]", - self.index(), - self.is_positive() - ) - } -} - -// /// TODO impl doc -// impl NumberOfLatticeElement for Direction { -// #[inline] -// fn number_of_elements(_lattice: &LatticeCyclic) -> usize { -// D -// } -// } - -// // TODO impl doc -// impl IndexToElement for Direction { -// fn index_to_element(_lattice: &LatticeCyclic, index: usize) -> Option { -// Self::new(index, true) -// } -// } - -/// List all possible direction -pub trait DirectionList: Sized { - /// List all directions. - #[must_use] - fn directions() -> &'static [Self]; - /// List all positive directions. - #[must_use] - fn positive_directions() -> &'static [Self]; -} - -/// Error return by [`TryFrom`] for [`Direction`]. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -#[non_exhaustive] -pub enum DirectionConversionError { - /// The index is out of bound, i.e. the direction axis does not exist in the lower space dimension. - IndexOutOfBound, // more error info like dim and index -} - -impl Display for DirectionConversionError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::IndexOutOfBound => write!(f, "the index is out of bound, the direction axis does not exist in the lower space dimension"), - } - } -} - -impl Error for DirectionConversionError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match self { - Self::IndexOutOfBound => None, - } - } -} - -implement_direction_list!(); - -implement_direction_from!(); - -/// Partial ordering is set as follows: two directions can be compared if they have the same index -/// or the same direction sign. In the first case a positive direction is greater than a negative direction -/// In the latter case the ordering is done on the index. -/// # Example -/// ``` -/// # use lattice_qcd_rs::lattice::Direction; -/// # use lattice_qcd_rs::error::ImplementationError; -/// use std::cmp::Ordering; -/// # fn main() -> Result<(), Box< dyn std::error::Error>> { -/// -/// let dir_1 = -/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// let dir_2 = -/// Direction::<4>::new(1, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// assert!(dir_1 < dir_2); -/// assert_eq!(dir_1.partial_cmp(&dir_2), Some(Ordering::Less)); -/// //-------- -/// let dir_3 = -/// Direction::<4>::new(3, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// let dir_4 = -/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// assert!(dir_3 > dir_4); -/// assert_eq!(dir_3.partial_cmp(&dir_4), Some(Ordering::Greater)); -/// //-------- -/// let dir_5 = -/// Direction::<4>::new(3, true).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// let dir_6 = -/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// assert_eq!(dir_5.partial_cmp(&dir_6), None); -/// //-------- -/// let dir_5 = -/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// let dir_6 = -/// Direction::<4>::new(1, false).ok_or(ImplementationError::OptionWithUnexpectedNone)?; -/// assert_eq!(dir_5.partial_cmp(&dir_6), Some(Ordering::Equal)); -/// # Ok(()) -/// # } -/// ``` -impl PartialOrd for Direction { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - if self == other { - Some(Ordering::Equal) - } else if self.is_positive() == other.is_positive() { - self.index().partial_cmp(&other.index()) - } else if self.index() == other.index() { - self.is_positive().partial_cmp(&other.is_positive()) - } else { - None - } - } -} - -impl Neg for Direction { - type Output = Self; - - #[inline] - fn neg(mut self) -> Self::Output { - self.is_positive = !self.is_positive; - self - } -} - -impl Neg for &Direction { - type Output = Direction; - - #[inline] - fn neg(self) -> Self::Output { - -*self - } -} - -/// Return [`Direction::to_index`]. -impl From> for usize { - #[inline] - fn from(d: Direction) -> Self { - d.index() - } -} - -/// Return [`Direction::to_index`]. -impl From<&Direction> for usize { - #[inline] - fn from(d: &Direction) -> Self { - d.index() - } -} - -/// Return [`DirectionEnum::from_vector`]. -impl From> for Direction { - #[inline] - fn from(v: SVector) -> Self { - Self::from_vector(&v) - } -} - -/// Return [`DirectionEnum::from_vector`]. -impl From<&SVector> for Direction { - #[inline] - fn from(v: &SVector) -> Self { - Self::from_vector(v) - } -} - -/// Return [`Direction::to_unit_vector`]. -impl From> for SVector { - #[inline] - fn from(d: Direction) -> Self { - d.to_unit_vector() - } -} - -/// Return [`Direction::to_unit_vector`]. -impl From<&Direction> for SVector { - #[inline] - fn from(d: &Direction) -> Self { - d.to_unit_vector() - } -} - -impl From for Direction<4> { - #[inline] - fn from(d: DirectionEnum) -> Self { - Self::new(DirectionEnum::index(d), d.is_positive()).expect("unreachable") - } -} - -impl From> for DirectionEnum { - #[inline] - fn from(d: Direction<4>) -> Self { - d.into() - } -} - -impl From<&Direction<4>> for DirectionEnum { - #[inline] - fn from(d: &Direction<4>) -> Self { - match (d.index(), d.is_positive()) { - (0, true) => Self::XPos, - (0, false) => Self::XNeg, - (1, true) => Self::YPos, - (1, false) => Self::YNeg, - (2, true) => Self::ZPos, - (2, false) => Self::ZNeg, - (3, true) => Self::TPos, - (3, false) => Self::TNeg, - _ => unreachable!(), - } - } -} - -impl From<&DirectionEnum> for Direction<4> { - #[inline] - fn from(d: &DirectionEnum) -> Self { - Self::new(DirectionEnum::index(*d), d.is_positive()).expect("unreachable") - } -} - -// TODO depreciate ? -/// Represent a cardinal direction -#[allow(clippy::exhaustive_enums)] -#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)] -#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] -pub enum DirectionEnum { - /// Positive x direction. - XPos, - /// Negative x direction. - XNeg, - /// Positive y direction. - YPos, - /// Negative y direction. - YNeg, - /// Positive z direction. - ZPos, - /// Negative z direction. - ZNeg, - /// Positive t direction. - TPos, - /// Negative t direction. - TNeg, -} - -impl DirectionEnum { - /// List all directions. - pub const DIRECTIONS: [Self; 8] = [ - Self::XPos, - Self::YPos, - Self::ZPos, - Self::TPos, - Self::XNeg, - Self::YNeg, - Self::ZNeg, - Self::TNeg, - ]; - /// List all spatial directions. - pub const DIRECTIONS_SPACE: [Self; 6] = [ - Self::XPos, - Self::YPos, - Self::ZPos, - Self::XNeg, - Self::YNeg, - Self::ZNeg, - ]; - /// List of all positives directions. - pub const POSITIVES: [Self; 4] = [Self::XPos, Self::YPos, Self::ZPos, Self::TPos]; - /// List spatial positive direction. - pub const POSITIVES_SPACE: [Self; 3] = [Self::XPos, Self::YPos, Self::ZPos]; - - /// Convert the direction into a vector of norm `a`; - #[must_use] - #[inline] - pub fn to_vector(self, a: f64) -> Vector4 { - self.to_unit_vector() * a - } - - /// Convert the direction into a vector of norm `1`; - #[must_use] - #[inline] - pub const fn to_unit_vector(self) -> Vector4 { - match self { - Self::XPos => Vector4::::new(1_f64, 0_f64, 0_f64, 0_f64), - Self::XNeg => Vector4::::new(-1_f64, 0_f64, 0_f64, 0_f64), - Self::YPos => Vector4::::new(0_f64, 1_f64, 0_f64, 0_f64), - Self::YNeg => Vector4::::new(0_f64, -1_f64, 0_f64, 0_f64), - Self::ZPos => Vector4::::new(0_f64, 0_f64, 1_f64, 0_f64), - Self::ZNeg => Vector4::::new(0_f64, 0_f64, -1_f64, 0_f64), - Self::TPos => Vector4::::new(0_f64, 0_f64, 0_f64, 1_f64), - Self::TNeg => Vector4::::new(0_f64, 0_f64, 0_f64, -1_f64), - } - } - - /// Get if the position is positive. - /// # Example - /// ``` - /// # use lattice_qcd_rs::lattice::DirectionEnum; - /// assert_eq!(DirectionEnum::XPos.is_positive(), true); - /// assert_eq!(DirectionEnum::TPos.is_positive(), true); - /// assert_eq!(DirectionEnum::YNeg.is_positive(), false); - /// ``` - #[must_use] - #[inline] - pub const fn is_positive(self) -> bool { - match self { - Self::XPos | Self::YPos | Self::ZPos | Self::TPos => true, - Self::XNeg | Self::YNeg | Self::ZNeg | Self::TNeg => false, - } - } - - /// Get if the position is Negative. see [`DirectionEnum::is_positive`] - #[must_use] - #[inline] - pub const fn is_negative(self) -> bool { - !self.is_positive() - } - - /// Find the direction the vector point the most. - /// For a zero vector return [`DirectionEnum::XPos`]. - /// # Example - /// ``` - /// # use lattice_qcd_rs::lattice::DirectionEnum; - /// # extern crate nalgebra; - /// assert_eq!( - /// DirectionEnum::from_vector(&nalgebra::Vector4::new(1_f64, 0_f64, 0_f64, 0_f64)), - /// DirectionEnum::XPos - /// ); - /// assert_eq!( - /// DirectionEnum::from_vector(&nalgebra::Vector4::new(0_f64, -1_f64, 0_f64, 0_f64)), - /// DirectionEnum::YNeg - /// ); - /// assert_eq!( - /// DirectionEnum::from_vector(&nalgebra::Vector4::new(0.5_f64, 1_f64, 0_f64, 2_f64)), - /// DirectionEnum::TPos - /// ); - /// ``` - #[inline] - #[must_use] - pub fn from_vector(v: &Vector4) -> Self { - let mut max = 0_f64; - let mut index_max: usize = 0; - let mut is_positive = true; - for i in 0..Self::POSITIVES.len() { - let scalar_prod = v.dot(&Self::POSITIVES[i].to_vector(1_f64)); - if scalar_prod.abs() > max { - max = scalar_prod.abs(); - index_max = i; - is_positive = scalar_prod > 0_f64; - } - } - match index_max { - 0 => { - if is_positive { - Self::XPos - } else { - Self::XNeg - } - } - 1 => { - if is_positive { - Self::YPos - } else { - Self::YNeg - } - } - 2 => { - if is_positive { - Self::ZPos - } else { - Self::ZNeg - } - } - 3 => { - if is_positive { - Self::TPos - } else { - Self::TNeg - } - } - _ => { - // the code should attain this code. and therefore panicking is not expected. - unreachable!("Implementation error : invalid index") - } - } - } - - /// Return the positive direction associated, for example `-x` gives `+x` - /// and `+x` gives `+x`. - /// # Example - /// ``` - /// # use lattice_qcd_rs::lattice::DirectionEnum; - /// assert_eq!(DirectionEnum::XNeg.to_positive(), DirectionEnum::XPos); - /// assert_eq!(DirectionEnum::YPos.to_positive(), DirectionEnum::YPos); - /// ``` - #[inline] - #[must_use] - pub const fn to_positive(self) -> Self { - match self { - Self::XNeg => Self::XPos, - Self::YNeg => Self::YPos, - Self::ZNeg => Self::ZPos, - Self::TNeg => Self::TPos, - _ => self, - } - } - - /// Get a index associated to the direction. - /// # Example - /// ``` - /// # use lattice_qcd_rs::lattice::DirectionEnum; - /// assert_eq!(DirectionEnum::XPos.index(), 0); - /// assert_eq!(DirectionEnum::XNeg.index(), 0); - /// assert_eq!(DirectionEnum::YPos.index(), 1); - /// assert_eq!(DirectionEnum::YNeg.index(), 1); - /// assert_eq!(DirectionEnum::ZPos.index(), 2); - /// assert_eq!(DirectionEnum::ZNeg.index(), 2); - /// assert_eq!(DirectionEnum::TPos.index(), 3); - /// assert_eq!(DirectionEnum::TNeg.index(), 3); - /// ``` - #[inline] - #[must_use] - pub const fn index(self) -> usize { - match self { - Self::XPos | Self::XNeg => 0, - Self::YPos | Self::YNeg => 1, - Self::ZPos | Self::ZNeg => 2, - Self::TPos | Self::TNeg => 3, - } - } -} - -/// Return [`DirectionEnum::XPos`] -impl Default for DirectionEnum { - ///Return [`DirectionEnum::XPos`] - #[inline] - fn default() -> Self { - Self::XPos - } -} - -impl Display for DirectionEnum { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::XPos => write!(f, "positive X direction"), - Self::XNeg => write!(f, "negative X direction"), - Self::YPos => write!(f, "positive Y direction"), - Self::YNeg => write!(f, "negative Y direction"), - Self::ZPos => write!(f, "positive Z direction"), - Self::ZNeg => write!(f, "negative Z direction"), - Self::TPos => write!(f, "positive T direction"), - Self::TNeg => write!(f, "negative T direction"), - } - } -} - -impl DirectionList for DirectionEnum { - #[inline] - fn directions() -> &'static [Self] { - &Self::DIRECTIONS - } - - #[inline] - fn positive_directions() -> &'static [Self] { - &Self::POSITIVES - } -} - -impl PartialOrd for DirectionEnum { - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - Direction::<4>::from(self).partial_cmp(&other.into()) - } -} - -/// Return the negative of a direction -/// # Example -/// ``` -/// # use lattice_qcd_rs::lattice::DirectionEnum; -/// assert_eq!(-DirectionEnum::XNeg, DirectionEnum::XPos); -/// assert_eq!(-DirectionEnum::YPos, DirectionEnum::YNeg); -/// ``` -impl Neg for DirectionEnum { - type Output = Self; - - #[inline] - fn neg(self) -> Self::Output { - match self { - Self::XPos => Self::XNeg, - Self::XNeg => Self::XPos, - Self::YPos => Self::YNeg, - Self::YNeg => Self::YPos, - Self::ZPos => Self::ZNeg, - Self::ZNeg => Self::ZPos, - Self::TPos => Self::TNeg, - Self::TNeg => Self::TPos, - } - } -} - -impl Neg for &DirectionEnum { - type Output = Self; - - #[inline] - fn neg(self) -> Self::Output { - match self { - DirectionEnum::XPos => &DirectionEnum::XNeg, - DirectionEnum::XNeg => &DirectionEnum::XPos, - DirectionEnum::YPos => &DirectionEnum::YNeg, - DirectionEnum::YNeg => &DirectionEnum::YPos, - DirectionEnum::ZPos => &DirectionEnum::ZNeg, - DirectionEnum::ZNeg => &DirectionEnum::ZPos, - DirectionEnum::TPos => &DirectionEnum::TNeg, - DirectionEnum::TNeg => &DirectionEnum::TPos, - } - } -} - -/// Return [`DirectionEnum::to_index`]. -impl From for usize { - #[inline] - fn from(d: DirectionEnum) -> Self { - d.index() - } -} - -/// Return [`DirectionEnum::to_index`]. -impl From<&DirectionEnum> for usize { - #[inline] - fn from(d: &DirectionEnum) -> Self { - DirectionEnum::index(*d) - } -} - -/// Return [`DirectionEnum::from_vector`]. -impl From> for DirectionEnum { - #[inline] - fn from(v: Vector4) -> Self { - Self::from_vector(&v) - } -} - -/// Return [`DirectionEnum::from_vector`]. -impl From<&Vector4> for DirectionEnum { - #[inline] - fn from(v: &Vector4) -> Self { - Self::from_vector(v) - } -} - -/// Return [`DirectionEnum::to_unit_vector`]. -impl From for Vector4 { - #[inline] - fn from(d: DirectionEnum) -> Self { - d.to_unit_vector() - } -} - -/// Return [`DirectionEnum::to_unit_vector`]. -impl From<&DirectionEnum> for Vector4 { - #[inline] - fn from(d: &DirectionEnum) -> Self { - d.to_unit_vector() - } -} - -impl LatticeElementToIndex<4> for DirectionEnum { - #[inline] - fn to_index(&self, l: &LatticeCyclic<4>) -> usize { - Direction::<4>::from(self).to_index(l) - } -} - -// impl Sealed for DirectionEnum {} - -// impl NumberOfLatticeElement<4> for DirectionEnum { -// #[inline] -// fn number_of_elements(lattice: &LatticeCyclic<4>) -> usize { -// Direction::<4>::number_of_elements(lattice) -// } -// } - -// impl IndexToElement<4> for DirectionEnum { -// fn index_to_element(lattice: &LatticeCyclic<4>, index: usize) -> Option { -// Direction::<4>::index_to_element(lattice, index).map(Into::into) -// } -// } - #[cfg(test)] mod test { use super::*; diff --git a/src/lib.rs b/src/lib.rs index e05fb46e..50684225 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] #![doc(html_root_url = "https://docs.rs/lattice_qcd_rs/0.3.0")] -// +//--------------------------------------- +// lints #![warn(clippy::pedantic)] #![warn(clippy::cargo)] // @@ -117,8 +118,8 @@ #![warn(clippy::verbose_file_reads)] #![warn(unsafe_code)] // there is used of some unsafe code // -//--------------- -// doc +//--------------------------------------- +// doc lints #![warn(missing_docs)] // doc //#![warn(clippy::missing_docs_in_private_items)] // doc #![deny(rustdoc::broken_intra_doc_links)] // cspell: ignore rustdoc @@ -129,12 +130,14 @@ #![deny(rustdoc::bare_urls)] #![deny(rustdoc::unescaped_backticks)] #![deny(rustdoc::redundant_explicit_links)] -//--------------- -// allow +//--------------------------------------- +// allow lints #![allow(clippy::module_name_repetitions)] -// +//--------------------------------------- // #![cfg_attr(feature = "experimental", feature(is_sorted))] +//--------------------------------------- +// module #[macro_use] mod macro_def; pub mod dim; @@ -154,16 +157,29 @@ pub mod utils; #[cfg(test)] mod test; +//--------------------------------------- +// uses + pub use nalgebra::ComplexField; pub use rand::{Rng, SeedableRng}; pub use rand_distr::Distribution; use utils_lib::trait_sealed; +//--------------------------------------- +// + +// #[cfg(feature = "serde-serialize")] +// use serde::{Deserialize, Serialize}; // #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Default)] -// #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +// #[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))] +//--------------------------------------- +// trait trait_sealed!(); +//--------------------------------------- +// type aliases + /// alias for [`f64`] pub type Real = f64; /// easy to use alias for [`nalgebra::Complex::`] diff --git a/tools/git_hook/pre-commit b/tools/git_hook/pre-commit index f62ff266..4618a8a0 100644 --- a/tools/git_hook/pre-commit +++ b/tools/git_hook/pre-commit @@ -1,4 +1,4 @@ #!/bin/sh cargo +nightly fmt --all --check -cargo clippy --all +cargo clippy --all --all-targets