utomid_rs/
compounds.rs

1use crate::{decode_utomid, ELEMENTS};
2use std::collections::HashMap;
3
4/// Compute the total mass of a compound represented as a flat list of element IDs
5/// Example: [1, 1, 8] = H2O
6pub fn compound_mass(arr: &[u8]) -> f64 {
7    arr.iter()
8        .map(|n| ELEMENTS.get(n).unwrap().atomic_mass)
9        .sum()
10}
11
12/// Convert a flat list of element IDs into a chemical formula string
13/// Example: [1, 1, 8] -> "H2O"
14pub fn compound_formula(arr: &[u8]) -> String {
15    let mut counts: HashMap<u8, usize> = HashMap::new();
16    for &n in arr {
17        *counts.entry(n).or_default() += 1;
18    }
19    let mut parts: Vec<String> = counts
20        .into_iter()
21        .map(|(num, count)| {
22            let sym = ELEMENTS.get(&num).unwrap().symbol;
23            if count > 1 {
24                format!("{}{}", sym, count)
25            } else {
26                sym.to_string()
27            }
28        })
29        .collect();
30    parts.sort(); // canonical ordering
31    parts.join("")
32}
33
34/// A more compact compound representation: [(element_id, count)]
35/// Example: H2O -> [(1, 2), (8, 1)]
36#[derive(Debug, Clone)]
37pub struct CompactCompound {
38    pub pairs: Vec<(u8, u8)>, // (element_id, count)
39}
40
41impl CompactCompound {
42    pub fn new(pairs: Vec<(u8, u8)>) -> Self {
43        Self { pairs }
44    }
45
46    /// Return the chemical formula as a string (e.g. "H2O")
47    pub fn formula(&self) -> String {
48        self.pairs
49            .iter()
50            .map(|(id, count)| {
51                if let Some(el) = decode_utomid(*id) {
52                    if *count > 1 {
53                        format!("{}{}", el.symbol, count)
54                    } else {
55                        el.symbol.to_string()
56                    }
57                } else {
58                    "?".to_string()
59                }
60            })
61            .collect::<Vec<_>>()
62            .join("")
63    }
64
65    /// Compute the compound mass based on stored pairs
66    pub fn mass(&self) -> f64 {
67        self.pairs
68            .iter()
69            .map(|(id, count)| {
70                if let Some(el) = decode_utomid(*id) {
71                    el.atomic_mass * f64::from(*count)
72                } else {
73                    0.0
74                }
75            })
76            .sum()
77    }
78}
79
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_compact_water() {
87        let water = CompactCompound::new(vec![(1,2), (8,1)]);
88        assert_eq!(water.formula(), "H2O");
89        assert!((water.mass() - 18.015).abs() < 0.01);
90    }
91}