Skip to main content

readcon_core/
helpers.rs

1//! Element symbol and atomic number lookup.
2//!
3//! Both lookups cover hydrogen through uranium (Z = 1..=92). Unknown
4//! inputs return a stable sentinel: [`symbol_to_atomic_number`] returns
5//! 0 for unknown symbols and [`atomic_number_to_symbol`] returns "X"
6//! for unknown atomic numbers. The same lookup is exposed to C/C++ via
7//! [`crate::ffi::rkr_symbol_to_z`] and [`crate::ffi::rkr_z_to_symbol`]
8//! so downstream tools can drop their own copies of the periodic table.
9
10/// Returns the atomic number for a chemical symbol, or 0 if unknown.
11pub fn symbol_to_atomic_number(symbol: &str) -> u64 {
12    match symbol {
13        "H" => 1,
14        "He" => 2,
15        "Li" => 3,
16        "Be" => 4,
17        "B" => 5,
18        "C" => 6,
19        "N" => 7,
20        "O" => 8,
21        "F" => 9,
22        "Ne" => 10,
23        "Na" => 11,
24        "Mg" => 12,
25        "Al" => 13,
26        "Si" => 14,
27        "P" => 15,
28        "S" => 16,
29        "Cl" => 17,
30        "Ar" => 18,
31        "K" => 19,
32        "Ca" => 20,
33        "Sc" => 21,
34        "Ti" => 22,
35        "V" => 23,
36        "Cr" => 24,
37        "Mn" => 25,
38        "Fe" => 26,
39        "Co" => 27,
40        "Ni" => 28,
41        "Cu" => 29,
42        "Zn" => 30,
43        "Ga" => 31,
44        "Ge" => 32,
45        "As" => 33,
46        "Se" => 34,
47        "Br" => 35,
48        "Kr" => 36,
49        "Rb" => 37,
50        "Sr" => 38,
51        "Y" => 39,
52        "Zr" => 40,
53        "Nb" => 41,
54        "Mo" => 42,
55        "Tc" => 43,
56        "Ru" => 44,
57        "Rh" => 45,
58        "Pd" => 46,
59        "Ag" => 47,
60        "Cd" => 48,
61        "In" => 49,
62        "Sn" => 50,
63        "Sb" => 51,
64        "Te" => 52,
65        "I" => 53,
66        "Xe" => 54,
67        "Cs" => 55,
68        "Ba" => 56,
69        "La" => 57,
70        "Ce" => 58,
71        "Pr" => 59,
72        "Nd" => 60,
73        "Pm" => 61,
74        "Sm" => 62,
75        "Eu" => 63,
76        "Gd" => 64,
77        "Tb" => 65,
78        "Dy" => 66,
79        "Ho" => 67,
80        "Er" => 68,
81        "Tm" => 69,
82        "Yb" => 70,
83        "Lu" => 71,
84        "Hf" => 72,
85        "Ta" => 73,
86        "W" => 74,
87        "Re" => 75,
88        "Os" => 76,
89        "Ir" => 77,
90        "Pt" => 78,
91        "Au" => 79,
92        "Hg" => 80,
93        "Tl" => 81,
94        "Pb" => 82,
95        "Bi" => 83,
96        "Po" => 84,
97        "At" => 85,
98        "Rn" => 86,
99        "Fr" => 87,
100        "Ra" => 88,
101        "Ac" => 89,
102        "Th" => 90,
103        "Pa" => 91,
104        "U" => 92,
105        _ => 0, // Unknown
106    }
107}
108
109/// Returns the chemical symbol for an atomic number, or "X" if unknown.
110pub fn atomic_number_to_symbol(atomic_number: u64) -> &'static str {
111    match atomic_number {
112        1 => "H",
113        2 => "He",
114        3 => "Li",
115        4 => "Be",
116        5 => "B",
117        6 => "C",
118        7 => "N",
119        8 => "O",
120        9 => "F",
121        10 => "Ne",
122        11 => "Na",
123        12 => "Mg",
124        13 => "Al",
125        14 => "Si",
126        15 => "P",
127        16 => "S",
128        17 => "Cl",
129        18 => "Ar",
130        19 => "K",
131        20 => "Ca",
132        21 => "Sc",
133        22 => "Ti",
134        23 => "V",
135        24 => "Cr",
136        25 => "Mn",
137        26 => "Fe",
138        27 => "Co",
139        28 => "Ni",
140        29 => "Cu",
141        30 => "Zn",
142        31 => "Ga",
143        32 => "Ge",
144        33 => "As",
145        34 => "Se",
146        35 => "Br",
147        36 => "Kr",
148        37 => "Rb",
149        38 => "Sr",
150        39 => "Y",
151        40 => "Zr",
152        41 => "Nb",
153        42 => "Mo",
154        43 => "Tc",
155        44 => "Ru",
156        45 => "Rh",
157        46 => "Pd",
158        47 => "Ag",
159        48 => "Cd",
160        49 => "In",
161        50 => "Sn",
162        51 => "Sb",
163        52 => "Te",
164        53 => "I",
165        54 => "Xe",
166        55 => "Cs",
167        56 => "Ba",
168        57 => "La",
169        58 => "Ce",
170        59 => "Pr",
171        60 => "Nd",
172        61 => "Pm",
173        62 => "Sm",
174        63 => "Eu",
175        64 => "Gd",
176        65 => "Tb",
177        66 => "Dy",
178        67 => "Ho",
179        68 => "Er",
180        69 => "Tm",
181        70 => "Yb",
182        71 => "Lu",
183        72 => "Hf",
184        73 => "Ta",
185        74 => "W",
186        75 => "Re",
187        76 => "Os",
188        77 => "Ir",
189        78 => "Pt",
190        79 => "Au",
191        80 => "Hg",
192        81 => "Tl",
193        82 => "Pb",
194        83 => "Bi",
195        84 => "Po",
196        85 => "At",
197        86 => "Rn",
198        87 => "Fr",
199        88 => "Ra",
200        89 => "Ac",
201        90 => "Th",
202        91 => "Pa",
203        92 => "U",
204        _ => "X", // Represents an unknown element
205    }
206}
207
208#[cfg(test)]
209mod tests {
210    use super::*;
211
212    #[test]
213    fn known_round_trip() {
214        for z in 1u64..=92 {
215            let symbol = atomic_number_to_symbol(z);
216            assert_eq!(
217                symbol_to_atomic_number(symbol),
218                z,
219                "round-trip failed for Z={z} (symbol={symbol})"
220            );
221        }
222    }
223
224    #[test]
225    fn unknown_symbol_returns_zero() {
226        assert_eq!(symbol_to_atomic_number(""), 0);
227        assert_eq!(symbol_to_atomic_number("Xx"), 0);
228        assert_eq!(symbol_to_atomic_number("h"), 0); // case-sensitive
229    }
230
231    #[test]
232    fn unknown_z_returns_x() {
233        assert_eq!(atomic_number_to_symbol(0), "X");
234        assert_eq!(atomic_number_to_symbol(93), "X");
235        assert_eq!(atomic_number_to_symbol(u64::MAX), "X");
236    }
237}