1use std::{fmt::Display, str::FromStr};
4
5use serde::{Deserialize, Serialize};
6
7use crate::{quantum::types::Sign, LadduError};
8
9mod types;
10pub use types::{AngularMomentum, Charge, OrbitalAngularMomentum, Parity, Projection, Statistics};
11
12mod state;
13pub use state::{AllowedPartialWave, Isospin, PartialWave, ParticleProperties, SpinState};
14
15mod rules;
16pub use rules::{RuleSet, SelectionRules};
17
18pub fn allowed_projections(spin: AngularMomentum) -> Vec<Projection> {
20 SpinState::allowed_projections(spin)
21 .into_iter()
22 .map(SpinState::projection)
23 .collect()
24}
25
26#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub enum Frame {
29 Helicity,
33 GottfriedJackson,
37 Adair,
39}
40impl Display for Frame {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 match self {
43 Frame::Helicity => write!(f, "Helicity"),
44 Frame::GottfriedJackson => write!(f, "Gottfried-Jackson"),
45 Frame::Adair => write!(f, "Adair"),
46 }
47 }
48}
49impl FromStr for Frame {
50 type Err = LadduError;
51
52 fn from_str(s: &str) -> Result<Self, Self::Err> {
53 match s.to_lowercase().as_str() {
54 "helicity" | "hx" | "hel" => Ok(Self::Helicity),
55 "gottfriedjackson" | "gottfried jackson" | "gj" | "gottfried-jackson" => {
56 Ok(Self::GottfriedJackson)
57 }
58 "adair" => Ok(Self::Adair),
59 _ => Err(LadduError::ParseError {
60 name: s.to_string(),
61 object: "Frame".to_string(),
62 }),
63 }
64 }
65}
66
67#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
69pub enum Reflectivity {
70 Positive,
72 Negative,
74}
75impl Display for Reflectivity {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match self {
78 Reflectivity::Positive => write!(f, "+"),
79 Reflectivity::Negative => write!(f, "-"),
80 }
81 }
82}
83
84impl FromStr for Reflectivity {
85 type Err = LadduError;
86
87 fn from_str(s: &str) -> Result<Self, Self::Err> {
88 match parse_sign_value(s, "Reflectivity")? {
89 Sign::Positive => Ok(Self::Positive),
90 Sign::Negative => Ok(Self::Negative),
91 }
92 }
93}
94
95pub(crate) fn parse_sign_value(s: &str, object: &str) -> Result<Sign, LadduError> {
96 match s.to_lowercase().as_ref() {
97 "+" | "plus" | "pos" | "positive" => Ok(Sign::Positive),
98 "-" | "minus" | "neg" | "negative" => Ok(Sign::Negative),
99 _ => Err(LadduError::ParseError {
100 name: s.to_string(),
101 object: object.to_string(),
102 }),
103 }
104}
105
106#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
108pub enum Channel {
109 S,
111 T,
113 U,
115}
116
117impl Display for Channel {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 match self {
120 Channel::S => write!(f, "s"),
121 Channel::T => write!(f, "t"),
122 Channel::U => write!(f, "u"),
123 }
124 }
125}
126
127impl FromStr for Channel {
128 type Err = LadduError;
129
130 fn from_str(s: &str) -> Result<Self, Self::Err> {
131 match s.to_lowercase().as_ref() {
132 "s" => Ok(Self::S),
133 "t" => Ok(Self::T),
134 "u" => Ok(Self::U),
135 _ => Err(LadduError::ParseError {
136 name: s.to_string(),
137 object: "Channel".to_string(),
138 }),
139 }
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn spin_state_accepts_integer_and_half_integer_values() {
149 let spin_one = AngularMomentum::integer(1);
150 let spin_half = AngularMomentum::half_integer(1);
151 assert_eq!(
152 SpinState::new(spin_one, Projection::integer(0))
153 .unwrap()
154 .projection()
155 .value(),
156 0
157 );
158 assert_eq!(
159 SpinState::new(spin_half, Projection::half_integer(-1))
160 .unwrap()
161 .projection()
162 .value(),
163 -1
164 );
165 }
166
167 #[test]
168 fn spin_state_rejects_invalid_projection() {
169 let spin_one = AngularMomentum::integer(1);
170 assert!(SpinState::new(spin_one, Projection::half_integer(4)).is_err());
171 assert!(SpinState::new(spin_one, Projection::half_integer(1)).is_err());
172 }
173
174 #[test]
175 fn spin_state_enumerates_allowed_projections() {
176 let spin_zero = SpinState::allowed_projections(AngularMomentum::integer(0));
177 assert_eq!(
178 spin_zero
179 .iter()
180 .map(|state| state.projection().value())
181 .collect::<Vec<_>>(),
182 vec![0]
183 );
184
185 let spin_half = SpinState::allowed_projections(AngularMomentum::half_integer(1));
186 assert_eq!(
187 spin_half
188 .iter()
189 .map(|state| state.projection().value())
190 .collect::<Vec<_>>(),
191 vec![-1, 1]
192 );
193
194 let spin_one = SpinState::allowed_projections(AngularMomentum::integer(1));
195 assert_eq!(
196 spin_one
197 .iter()
198 .map(|state| state.projection().value())
199 .collect::<Vec<_>>(),
200 vec![-2, 0, 2]
201 );
202
203 let spin_three_halves = SpinState::allowed_projections(AngularMomentum::half_integer(3));
204 assert_eq!(
205 spin_three_halves
206 .iter()
207 .map(|state| state.projection().value())
208 .collect::<Vec<_>>(),
209 vec![-3, -1, 1, 3]
210 );
211 }
212
213 #[test]
214 fn allowed_projection_helper_returns_projection_values() {
215 assert_eq!(
216 allowed_projections(AngularMomentum::integer(1)),
217 vec![
218 Projection::integer(-1),
219 Projection::integer(0),
220 Projection::integer(1),
221 ]
222 );
223 }
224
225 #[test]
226 fn enum_displays() {
227 assert_eq!(format!("{}", Frame::Helicity), "Helicity");
228 assert_eq!(format!("{}", Frame::GottfriedJackson), "Gottfried-Jackson");
229 assert_eq!(format!("{}", Frame::Adair), "Adair");
230 assert_eq!(format!("{}", Reflectivity::Positive), "+");
231 assert_eq!(format!("{}", Reflectivity::Negative), "-");
232 assert_eq!(format!("{}", Channel::S), "s");
233 assert_eq!(format!("{}", Channel::T), "t");
234 assert_eq!(format!("{}", Channel::U), "u");
235 }
236
237 #[test]
238 fn enum_from_str() {
239 assert_eq!(Frame::from_str("Helicity").unwrap(), Frame::Helicity);
240 assert_eq!(Frame::from_str("HX").unwrap(), Frame::Helicity);
241 assert_eq!(Frame::from_str("HEL").unwrap(), Frame::Helicity);
242 assert_eq!(
243 Frame::from_str("GottfriedJackson").unwrap(),
244 Frame::GottfriedJackson
245 );
246 assert_eq!(Frame::from_str("GJ").unwrap(), Frame::GottfriedJackson);
247 assert_eq!(
248 Frame::from_str("Gottfried-Jackson").unwrap(),
249 Frame::GottfriedJackson
250 );
251 assert_eq!(
252 Frame::from_str("Gottfried Jackson").unwrap(),
253 Frame::GottfriedJackson
254 );
255 assert_eq!(Frame::from_str("Adair").unwrap(), Frame::Adair);
256 assert_eq!(Reflectivity::from_str("+").unwrap(), Reflectivity::Positive);
257 assert_eq!(
258 Reflectivity::from_str("pos").unwrap(),
259 Reflectivity::Positive
260 );
261 assert_eq!(
262 Reflectivity::from_str("plus").unwrap(),
263 Reflectivity::Positive
264 );
265 assert_eq!(
266 Reflectivity::from_str("Positive").unwrap(),
267 Reflectivity::Positive
268 );
269 assert_eq!(Reflectivity::from_str("-").unwrap(), Reflectivity::Negative);
270 assert_eq!(
271 Reflectivity::from_str("minus").unwrap(),
272 Reflectivity::Negative
273 );
274 assert_eq!(
275 Reflectivity::from_str("neg").unwrap(),
276 Reflectivity::Negative
277 );
278 assert_eq!(
279 Reflectivity::from_str("Negative").unwrap(),
280 Reflectivity::Negative
281 );
282 assert_eq!(Channel::from_str("S").unwrap(), Channel::S);
283 assert_eq!(Channel::from_str("s").unwrap(), Channel::S);
284 assert_eq!(Channel::from_str("T").unwrap(), Channel::T);
285 assert_eq!(Channel::from_str("t").unwrap(), Channel::T);
286 assert_eq!(Channel::from_str("U").unwrap(), Channel::U);
287 assert_eq!(Channel::from_str("u").unwrap(), Channel::U);
288 }
289}