phasesmith_core/
radiation.rs1use std::error::Error;
4use std::fmt::{Display, Formatter};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum WavelengthComponentsError {
9 LengthMismatch,
11 Empty,
13 InvalidWavelength {
15 component: usize,
17 },
18 InvalidRelativeIntensity {
20 component: usize,
22 },
23 NonPositiveReferenceIntensity,
25}
26
27impl Display for WavelengthComponentsError {
28 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
29 match self {
30 Self::LengthMismatch => write!(
31 formatter,
32 "wavelength and relative-intensity arrays must have equal length"
33 ),
34 Self::Empty => write!(formatter, "at least one wavelength component is required"),
35 Self::InvalidWavelength { component } => write!(
36 formatter,
37 "wavelength component {component} must be positive and finite"
38 ),
39 Self::InvalidRelativeIntensity { component } => write!(
40 formatter,
41 "relative intensity component {component} must be non-negative and finite"
42 ),
43 Self::NonPositiveReferenceIntensity => write!(
44 formatter,
45 "reference component relative intensity must be positive"
46 ),
47 }
48 }
49}
50
51impl Error for WavelengthComponentsError {}
52
53#[derive(Clone, Copy, Debug, PartialEq)]
59pub struct WavelengthComponentsView<'a> {
60 wavelengths_angstrom: &'a [f64],
61 relative_intensities: &'a [f64],
62}
63
64impl<'a> WavelengthComponentsView<'a> {
65 pub fn new(
72 wavelengths_angstrom: &'a [f64],
73 relative_intensities: &'a [f64],
74 ) -> Result<Self, WavelengthComponentsError> {
75 if wavelengths_angstrom.len() != relative_intensities.len() {
76 return Err(WavelengthComponentsError::LengthMismatch);
77 }
78 if wavelengths_angstrom.is_empty() {
79 return Err(WavelengthComponentsError::Empty);
80 }
81 for (component, wavelength) in wavelengths_angstrom.iter().copied().enumerate() {
82 if !wavelength.is_finite() || wavelength <= 0.0 {
83 return Err(WavelengthComponentsError::InvalidWavelength { component });
84 }
85 }
86 for (component, intensity) in relative_intensities.iter().copied().enumerate() {
87 if !intensity.is_finite() || intensity < 0.0 {
88 return Err(WavelengthComponentsError::InvalidRelativeIntensity { component });
89 }
90 }
91 if relative_intensities[0] <= 0.0 {
92 return Err(WavelengthComponentsError::NonPositiveReferenceIntensity);
93 }
94 Ok(Self {
95 wavelengths_angstrom,
96 relative_intensities,
97 })
98 }
99
100 #[must_use]
102 pub const fn len(self) -> usize {
103 self.wavelengths_angstrom.len()
104 }
105
106 #[must_use]
108 pub const fn is_empty(self) -> bool {
109 self.wavelengths_angstrom.is_empty()
110 }
111
112 pub(crate) const fn wavelength(self, component: usize) -> f64 {
113 self.wavelengths_angstrom[component]
114 }
115
116 pub(crate) const fn relative_intensity(self, component: usize) -> f64 {
117 self.relative_intensities[component]
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn validates_component_arrays_and_reference_intensity() {
127 let wavelengths = [1.540_56, 1.544_39];
128 let intensities = [1.0, 0.5];
129 let model = WavelengthComponentsView::new(&wavelengths, &intensities).expect("valid");
130 assert_eq!(model.len(), 2);
131 assert_eq!(model.wavelength(1).to_bits(), 1.544_39_f64.to_bits());
132 assert_eq!(model.relative_intensity(1).to_bits(), 0.5_f64.to_bits());
133 assert_eq!(
134 WavelengthComponentsView::new(&wavelengths, &[0.0, 1.0]),
135 Err(WavelengthComponentsError::NonPositiveReferenceIntensity)
136 );
137 }
138}