use std::sync::OnceLock;
use crate::system::{da, r, Ratio};
include!("shared/element.rs");
impl Element {
pub fn isotopes(self) -> &'static [(u16, Mass, f64)] {
&elemental_data()[self as usize].2
}
pub fn mass(&self, isotope: u16) -> Option<Mass> {
if *self == Self::Electron {
return Some(da(5.485_799_090_65e-4));
}
Some(if isotope == 0 {
elemental_data()[*self as usize - 1].0?
} else {
elemental_data()[*self as usize - 1]
.2
.iter()
.find(|(ii, _, _)| *ii == isotope)
.map(|(_, m, _)| *m)?
})
}
pub fn average_weight(&self, isotope: u16) -> Option<Mass> {
if *self == Self::Electron {
return Some(da(5.485_799_090_65e-4));
}
Some(if isotope == 0 {
elemental_data()[*self as usize - 1].1?
} else {
elemental_data()[*self as usize - 1]
.2
.iter()
.find(|(ii, _, _)| *ii == isotope)
.map(|(_, m, _)| *m)?
})
}
pub fn most_abundant_mass(&self, n: i16, isotope: u16) -> Option<Mass> {
if *self == Self::Electron {
return Some(da(5.485_799_090_65e-4) * Ratio::new::<r>(f64::from(n)));
}
Some(
if isotope == 0 {
let mut max = None;
for iso in &elemental_data()[*self as usize - 1].2 {
let chance = iso.2 * f64::from(n);
if max.map_or(true, |m: (Mass, f64)| chance > m.1) {
max = Some((iso.1, chance));
}
}
max?.0
} else {
elemental_data()[*self as usize - 1]
.2
.iter()
.find(|(ii, _, _)| *ii == isotope)
.map(|(_, m, _)| *m)?
} * Ratio::new::<r>(f64::from(n)),
)
}
}
pub fn elemental_data() -> &'static ElementalData {
ELEMENTAL_DATA_CELL.get_or_init(|| {
bincode::deserialize(include_bytes!(concat!(env!("OUT_DIR"), "/elements.dat"))).unwrap()
})
}
static ELEMENTAL_DATA_CELL: OnceLock<ElementalData> = OnceLock::new();
#[cfg(test)]
mod test {
use crate::{Element, MolecularFormula};
#[test]
fn hill_notation() {
assert_eq!(
molecular_formula!(C 6 O 5 H 10).hill_notation(),
"C6H10O5".to_string()
);
}
}