qsym2/symmetry/
symmetry_element_order.rs

1//! Management of symmetry element order.
2
3use std::cmp::Ordering;
4use std::fmt;
5use std::hash::{Hash, Hasher};
6
7use serde::{Deserialize, Serialize};
8
9use crate::auxiliary::misc::HashableFloat;
10
11/// Enumerated type to handle symmetry element orders.
12#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
13pub enum ElementOrder {
14    /// Positive integer order.
15    Int(u32),
16
17    /// Infinite order.
18    Inf,
19}
20
21impl ElementOrder {
22    /// Creates an integer or infinite element order.
23    ///
24    /// # Arguments
25    ///
26    /// * `order` - A floating-point order value which must be either integral or
27    /// infinite.
28    /// * `thresh` - A threshold for determining the integrality of `order`.
29    ///
30    /// # Returns
31    ///
32    /// An element order.
33    ///
34    /// # Panics
35    ///
36    /// Panics if `order` is not positive.
37    #[must_use]
38    pub fn new(order: f64, thresh: f64) -> Self {
39        assert!(
40            order.is_sign_positive(),
41            "Order value `{order}` is invalid. Order values must be strictly positive.",
42        );
43        if order.is_infinite() {
44            return Self::Inf;
45        }
46        let rounded_order = order.round_factor(thresh);
47        if approx::relative_eq!(
48            rounded_order,
49            rounded_order.round(),
50            epsilon = thresh,
51            max_relative = thresh
52        ) {
53            #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
54            return Self::Int(rounded_order as u32);
55        }
56        panic!("The input order is not an integer.");
57    }
58
59    /// Converts the element order struct to the floating-point representation.
60    ///
61    /// # Returns
62    ///
63    /// The floating-point representation of the current element order.
64    #[must_use]
65    pub fn to_float(&self) -> f64 {
66        match self {
67            Self::Int(s_i) => f64::from(*s_i),
68            Self::Inf => f64::INFINITY,
69        }
70    }
71}
72
73impl PartialEq for ElementOrder {
74    fn eq(&self, other: &Self) -> bool {
75        match &self {
76            Self::Int(s_i) => match &other {
77                Self::Int(o_i) => s_i == o_i,
78                Self::Inf => false,
79            },
80            Self::Inf => match &other {
81                Self::Int(_) => false,
82                Self::Inf => true,
83            },
84        }
85    }
86}
87
88impl Eq for ElementOrder {}
89
90impl PartialOrd for ElementOrder {
91    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92        self.to_float().partial_cmp(&other.to_float())
93    }
94}
95
96impl Ord for ElementOrder {
97    fn cmp(&self, other: &Self) -> Ordering {
98        self.to_float().total_cmp(&other.to_float())
99    }
100}
101
102impl Hash for ElementOrder {
103    fn hash<H: Hasher>(&self, state: &mut H) {
104        match &self {
105            Self::Int(s_i) => {
106                s_i.hash(state);
107            }
108            Self::Inf => {
109                f64::INFINITY.integer_decode().hash(state);
110            }
111        }
112    }
113}
114
115impl fmt::Display for ElementOrder {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        match &self {
118            Self::Int(s_i) => write!(f, "{s_i}"),
119            Self::Inf => write!(f, "∞"),
120        }
121    }
122}
123
124/// Element integer order 1.
125pub const ORDER_1: ElementOrder = ElementOrder::Int(1);
126
127/// Element integer order 2.
128pub const ORDER_2: ElementOrder = ElementOrder::Int(2);
129
130
131/// Element infinite order.
132pub const ORDER_I: ElementOrder = ElementOrder::Inf;