Skip to main content

chematic_core/
element.rs

1//! Periodic-table elements indexed by atomic number (1–118).
2
3/// An element represented as its atomic number, wrapped in a newtype.
4///
5/// Zero-copy: implements `Copy`; comparison and hashing are O(1).
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
7pub struct Element(u8);
8
9impl Element {
10    // -- commonly used element constants ----------------------------------
11    pub const H: Element = Element(1);
12    pub const HE: Element = Element(2);
13    pub const LI: Element = Element(3);
14    pub const BE: Element = Element(4);
15    pub const B: Element = Element(5);
16    pub const C: Element = Element(6);
17    pub const N: Element = Element(7);
18    pub const O: Element = Element(8);
19    pub const F: Element = Element(9);
20    pub const NE: Element = Element(10);
21    pub const NA: Element = Element(11);
22    pub const MG: Element = Element(12);
23    pub const AL: Element = Element(13);
24    pub const SI: Element = Element(14);
25    pub const P: Element = Element(15);
26    pub const S: Element = Element(16);
27    pub const CL: Element = Element(17);
28    pub const AR: Element = Element(18);
29    pub const K: Element = Element(19);
30    pub const CA: Element = Element(20);
31    pub const SC: Element = Element(21);
32    pub const TI: Element = Element(22);
33    pub const V: Element = Element(23);
34    pub const CR: Element = Element(24);
35    pub const MN: Element = Element(25);
36    pub const FE: Element = Element(26);
37    pub const CO: Element = Element(27);
38    pub const NI: Element = Element(28);
39    pub const CU: Element = Element(29);
40    pub const ZN: Element = Element(30);
41    pub const GA: Element = Element(31);
42    pub const GE: Element = Element(32);
43    pub const AS: Element = Element(33);
44    pub const SE: Element = Element(34);
45    pub const BR: Element = Element(35);
46    pub const KR: Element = Element(36);
47    pub const RB: Element = Element(37);
48    pub const SR: Element = Element(38);
49    pub const Y: Element = Element(39);
50    pub const ZR: Element = Element(40);
51    pub const NB: Element = Element(41);
52    pub const MO: Element = Element(42);
53    pub const TC: Element = Element(43);
54    pub const RU: Element = Element(44);
55    pub const RH: Element = Element(45);
56    pub const PD: Element = Element(46);
57    pub const AG: Element = Element(47);
58    pub const CD: Element = Element(48);
59    pub const IN: Element = Element(49);
60    pub const SN: Element = Element(50);
61    pub const SB: Element = Element(51);
62    pub const TE: Element = Element(52);
63    pub const I: Element = Element(53);
64    pub const XE: Element = Element(54);
65    pub const CS: Element = Element(55);
66    pub const BA: Element = Element(56);
67    pub const LA: Element = Element(57);
68    pub const CE: Element = Element(58);
69    pub const PR: Element = Element(59);
70    pub const ND: Element = Element(60);
71    pub const PM: Element = Element(61);
72    pub const SM: Element = Element(62);
73    pub const EU: Element = Element(63);
74    pub const GD: Element = Element(64);
75    pub const TB: Element = Element(65);
76    pub const DY: Element = Element(66);
77    pub const HO: Element = Element(67);
78    pub const ER: Element = Element(68);
79    pub const TM: Element = Element(69);
80    pub const YB: Element = Element(70);
81    pub const LU: Element = Element(71);
82    pub const HF: Element = Element(72);
83    pub const TA: Element = Element(73);
84    pub const W: Element = Element(74);
85    pub const RE: Element = Element(75);
86    pub const OS: Element = Element(76);
87    pub const IR: Element = Element(77);
88    pub const PT: Element = Element(78);
89    pub const AU: Element = Element(79);
90    pub const HG: Element = Element(80);
91    pub const TL: Element = Element(81);
92    pub const PB: Element = Element(82);
93    pub const BI: Element = Element(83);
94    pub const PO: Element = Element(84);
95    pub const AT: Element = Element(85);
96    pub const RN: Element = Element(86);
97    pub const FR: Element = Element(87);
98    pub const RA: Element = Element(88);
99    pub const AC: Element = Element(89);
100    pub const TH: Element = Element(90);
101    pub const PA: Element = Element(91);
102    pub const U: Element = Element(92);
103    pub const NP: Element = Element(93);
104    pub const PU: Element = Element(94);
105    pub const AM: Element = Element(95);
106    pub const CM: Element = Element(96);
107    pub const BK: Element = Element(97);
108    pub const CF: Element = Element(98);
109    pub const ES: Element = Element(99);
110    pub const FM: Element = Element(100);
111    pub const MD: Element = Element(101);
112    pub const NO: Element = Element(102);
113    pub const LR: Element = Element(103);
114    pub const RF: Element = Element(104);
115    pub const DB: Element = Element(105);
116    pub const SG: Element = Element(106);
117    pub const BH: Element = Element(107);
118    pub const HS: Element = Element(108);
119    pub const MT: Element = Element(109);
120    pub const DS: Element = Element(110);
121    pub const RG: Element = Element(111);
122    pub const CN: Element = Element(112);
123    pub const NH: Element = Element(113);
124    pub const FL: Element = Element(114);
125    pub const MC: Element = Element(115);
126    pub const LV: Element = Element(116);
127    pub const TS: Element = Element(117);
128    pub const OG: Element = Element(118);
129
130    // -- constructors -------------------------------------------------------
131
132    /// Create an Element from an atomic number. Returns `None` if `n` is outside 1–118.
133    #[inline]
134    pub const fn from_atomic_number(n: u8) -> Option<Self> {
135        if n >= 1 && n <= 118 {
136            Some(Self(n))
137        } else {
138            None
139        }
140    }
141
142    /// Create an Element from its symbol (title-case, e.g. "C", "Cl").
143    /// Case-sensitive: "C" is carbon, "c" is not accepted.
144    pub fn from_symbol(s: &str) -> Option<Self> {
145        match s {
146            "H" => Some(Self::H),
147            "He" => Some(Self::HE),
148            "Li" => Some(Self::LI),
149            "Be" => Some(Self::BE),
150            "B" => Some(Self::B),
151            "C" => Some(Self::C),
152            "N" => Some(Self::N),
153            "O" => Some(Self::O),
154            "F" => Some(Self::F),
155            "Ne" => Some(Self::NE),
156            "Na" => Some(Self::NA),
157            "Mg" => Some(Self::MG),
158            "Al" => Some(Self::AL),
159            "Si" => Some(Self::SI),
160            "P" => Some(Self::P),
161            "S" => Some(Self::S),
162            "Cl" => Some(Self::CL),
163            "Ar" => Some(Self::AR),
164            "K" => Some(Self::K),
165            "Ca" => Some(Self::CA),
166            "Sc" => Some(Self::SC),
167            "Ti" => Some(Self::TI),
168            "V" => Some(Self::V),
169            "Cr" => Some(Self::CR),
170            "Mn" => Some(Self::MN),
171            "Fe" => Some(Self::FE),
172            "Co" => Some(Self::CO),
173            "Ni" => Some(Self::NI),
174            "Cu" => Some(Self::CU),
175            "Zn" => Some(Self::ZN),
176            "Ga" => Some(Self::GA),
177            "Ge" => Some(Self::GE),
178            "As" => Some(Self::AS),
179            "Se" => Some(Self::SE),
180            "Br" => Some(Self::BR),
181            "Kr" => Some(Self::KR),
182            "Rb" => Some(Self::RB),
183            "Sr" => Some(Self::SR),
184            "Y" => Some(Self::Y),
185            "Zr" => Some(Self::ZR),
186            "Nb" => Some(Self::NB),
187            "Mo" => Some(Self::MO),
188            "Tc" => Some(Self::TC),
189            "Ru" => Some(Self::RU),
190            "Rh" => Some(Self::RH),
191            "Pd" => Some(Self::PD),
192            "Ag" => Some(Self::AG),
193            "Cd" => Some(Self::CD),
194            "In" => Some(Self::IN),
195            "Sn" => Some(Self::SN),
196            "Sb" => Some(Self::SB),
197            "Te" => Some(Self::TE),
198            "I" => Some(Self::I),
199            "Xe" => Some(Self::XE),
200            "Cs" => Some(Self::CS),
201            "Ba" => Some(Self::BA),
202            "La" => Some(Self::LA),
203            "Ce" => Some(Self::CE),
204            "Pr" => Some(Self::PR),
205            "Nd" => Some(Self::ND),
206            "Pm" => Some(Self::PM),
207            "Sm" => Some(Self::SM),
208            "Eu" => Some(Self::EU),
209            "Gd" => Some(Self::GD),
210            "Tb" => Some(Self::TB),
211            "Dy" => Some(Self::DY),
212            "Ho" => Some(Self::HO),
213            "Er" => Some(Self::ER),
214            "Tm" => Some(Self::TM),
215            "Yb" => Some(Self::YB),
216            "Lu" => Some(Self::LU),
217            "Hf" => Some(Self::HF),
218            "Ta" => Some(Self::TA),
219            "W" => Some(Self::W),
220            "Re" => Some(Self::RE),
221            "Os" => Some(Self::OS),
222            "Ir" => Some(Self::IR),
223            "Pt" => Some(Self::PT),
224            "Au" => Some(Self::AU),
225            "Hg" => Some(Self::HG),
226            "Tl" => Some(Self::TL),
227            "Pb" => Some(Self::PB),
228            "Bi" => Some(Self::BI),
229            "Po" => Some(Self::PO),
230            "At" => Some(Self::AT),
231            "Rn" => Some(Self::RN),
232            "Fr" => Some(Self::FR),
233            "Ra" => Some(Self::RA),
234            "Ac" => Some(Self::AC),
235            "Th" => Some(Self::TH),
236            "Pa" => Some(Self::PA),
237            "U" => Some(Self::U),
238            "Np" => Some(Self::NP),
239            "Pu" => Some(Self::PU),
240            "Am" => Some(Self::AM),
241            "Cm" => Some(Self::CM),
242            "Bk" => Some(Self::BK),
243            "Cf" => Some(Self::CF),
244            "Es" => Some(Self::ES),
245            "Fm" => Some(Self::FM),
246            "Md" => Some(Self::MD),
247            "No" => Some(Self::NO),
248            "Lr" => Some(Self::LR),
249            "Rf" => Some(Self::RF),
250            "Db" => Some(Self::DB),
251            "Sg" => Some(Self::SG),
252            "Bh" => Some(Self::BH),
253            "Hs" => Some(Self::HS),
254            "Mt" => Some(Self::MT),
255            "Ds" => Some(Self::DS),
256            "Rg" => Some(Self::RG),
257            "Cn" => Some(Self::CN),
258            "Nh" => Some(Self::NH),
259            "Fl" => Some(Self::FL),
260            "Mc" => Some(Self::MC),
261            "Lv" => Some(Self::LV),
262            "Ts" => Some(Self::TS),
263            "Og" => Some(Self::OG),
264            _ => None,
265        }
266    }
267
268    /// Return the IUPAC element symbol (title-case).
269    #[inline]
270    pub fn symbol(self) -> &'static str {
271        SYMBOLS[(self.0 as usize) - 1]
272    }
273
274    /// Return the atomic number (1–118).
275    #[inline]
276    pub fn atomic_number(self) -> u8 {
277        self.0
278    }
279
280    /// Returns true if this element is in the OpenSMILES organic subset
281    /// (B, C, N, O, P, S, F, Cl, Br, I) — these may carry implicit H without brackets.
282    #[inline]
283    pub fn is_organic_subset(self) -> bool {
284        matches!(self.0, 5 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 35 | 53)
285    }
286
287    /// van der Waals radius in Å (Bondi 1964 + Alvarez 2013).
288    /// Unknown/synthetic elements return 1.70 (carbon fallback).
289    #[inline]
290    pub fn vdw_radius(self) -> f32 {
291        VDW_RADII[(self.0 as usize) - 1]
292    }
293
294    /// Single-bond covalent radius in Å (Alvarez 2008).
295    /// Unknown elements return 0.77 (sp3 carbon fallback).
296    #[inline]
297    pub fn covalent_radius(self) -> f32 {
298        COVALENT_RADII[(self.0 as usize) - 1]
299    }
300
301    /// Monoisotopic (exact) mass of the most abundant isotope (u).
302    /// Used as CIP rule 4 tiebreaker: higher atomic mass has higher priority.
303    pub fn atomic_mass(self) -> f64 {
304        ATOMIC_MASSES[(self.0 as usize) - 1]
305    }
306
307    /// Normal valence list (ascending). Empty = undefined (transition metals, etc.).
308    /// Used for computing implicit H counts in organic-subset atoms.
309    pub fn normal_valences(self) -> &'static [u8] {
310        match self.0 {
311            1 => &[1],           // H
312            5 => &[3],           // B
313            6 => &[4],           // C
314            7 => &[3, 5],        // N
315            8 => &[2],           // O
316            9 => &[1],           // F
317            14 => &[4],          // Si
318            15 => &[3, 5],       // P
319            16 => &[2, 4, 6],    // S
320            17 => &[1, 3, 5, 7], // Cl
321            33 => &[3, 5],       // As
322            34 => &[2, 4, 6],    // Se
323            35 => &[1, 3, 5, 7], // Br
324            53 => &[1, 3, 5, 7], // I
325            _ => &[],
326        }
327    }
328}
329
330impl core::fmt::Display for Element {
331    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
332        f.write_str(self.symbol())
333    }
334}
335
336/// Element symbol table indexed by (atomic_number - 1), covering elements 1–118.
337static SYMBOLS: [&str; 118] = [
338    "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl",
339    "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As",
340    "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In",
341    "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb",
342    "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl",
343    "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk",
344    "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh",
345    "Fl", "Mc", "Lv", "Ts", "Og",
346];
347
348/// van der Waals radii (Å) indexed by (atomic_number - 1).
349/// Sources: Bondi J.Phys.Chem. 1964; Alvarez Dalton Trans. 2013, 10.1039/c3dt50599e.
350/// Elements without published values use 1.70 (C fallback).
351#[rustfmt::skip]
352static VDW_RADII: [f32; 118] = [
353    // H     He    Li    Be    B     C     N     O     F     Ne
354    1.20, 1.40, 1.82, 1.53, 1.92, 1.70, 1.55, 1.52, 1.47, 1.54,
355    // Na    Mg    Al    Si    P     S     Cl    Ar    K     Ca
356    2.27, 1.73, 1.84, 2.10, 1.80, 1.80, 1.75, 1.88, 2.75, 2.31,
357    // Sc    Ti    V     Cr    Mn    Fe    Co    Ni    Cu    Zn
358    2.11, 2.00, 1.92, 1.93, 1.97, 1.96, 2.00, 1.63, 1.40, 1.39,
359    // Ga    Ge    As    Se    Br    Kr    Rb    Sr    Y     Zr
360    1.87, 2.11, 1.85, 1.90, 1.85, 2.02, 3.03, 2.49, 2.34, 2.23,
361    // Nb    Mo    Tc    Ru    Rh    Pd    Ag    Cd    In    Sn
362    2.18, 2.17, 2.16, 2.13, 2.10, 1.63, 1.72, 1.58, 1.93, 2.17,
363    // Sb    Te    I     Xe    Cs    Ba    La    Ce    Pr    Nd
364    2.06, 2.06, 1.98, 2.16, 3.43, 2.68, 2.57, 2.56, 2.54, 2.51,
365    // Pm    Sm    Eu    Gd    Tb    Dy    Ho    Er    Tm    Yb
366    2.50, 2.48, 2.45, 2.43, 2.42, 2.40, 2.38, 2.37, 2.36, 2.35,
367    // Lu    Hf    Ta    W     Re    Os    Ir    Pt    Au    Hg
368    2.34, 2.23, 2.22, 2.18, 2.16, 2.16, 2.13, 1.75, 1.66, 1.55,
369    // Tl    Pb    Bi    Po    At    Rn    Fr    Ra    Ac    Th
370    1.96, 2.02, 2.07, 1.97, 2.02, 2.20, 3.48, 2.83, 2.64, 2.54,
371    // Pa    U     Np    Pu    Am    Cm    Bk    Cf    Es    Fm
372    2.52, 2.41, 2.39, 2.43, 2.44, 2.45, 2.44, 2.45, 2.45, 1.70,
373    // Md    No    Lr    Rf    Db    Sg    Bh    Hs    Mt    Ds
374    1.70, 1.70, 1.70, 1.70, 1.70, 1.70, 1.70, 1.70, 1.70, 1.70,
375    // Rg    Cn    Nh    Fl    Mc    Lv    Ts    Og
376    1.70, 1.70, 1.70, 1.70, 1.70, 1.70, 1.70, 1.70,
377];
378
379/// Single-bond covalent radii (Å) indexed by (atomic_number - 1).
380/// Source: Alvarez et al. Dalton Trans. 2008, 2832–2838, 10.1039/b801115j.
381/// Elements 104–118 use computational estimates; others default to 0.77.
382#[rustfmt::skip]
383static COVALENT_RADII: [f32; 118] = [
384    // H     He    Li    Be    B     C     N     O     F     Ne
385    0.31, 0.28, 1.28, 0.96, 0.84, 0.77, 0.71, 0.66, 0.57, 0.58,
386    // Na    Mg    Al    Si    P     S     Cl    Ar    K     Ca
387    1.66, 1.41, 1.21, 1.11, 1.07, 1.05, 1.02, 1.06, 2.03, 1.76,
388    // Sc    Ti    V     Cr    Mn    Fe    Co    Ni    Cu    Zn
389    1.70, 1.60, 1.53, 1.39, 1.50, 1.52, 1.38, 1.24, 1.32, 1.22,
390    // Ga    Ge    As    Se    Br    Kr    Rb    Sr    Y     Zr
391    1.22, 1.20, 1.19, 1.20, 1.20, 1.16, 2.20, 1.95, 1.90, 1.75,
392    // Nb    Mo    Tc    Ru    Rh    Pd    Ag    Cd    In    Sn
393    1.64, 1.54, 1.47, 1.46, 1.42, 1.39, 1.45, 1.44, 1.42, 1.39,
394    // Sb    Te    I     Xe    Cs    Ba    La    Ce    Pr    Nd
395    1.39, 1.38, 1.39, 1.40, 2.44, 2.15, 2.07, 2.04, 2.03, 2.01,
396    // Pm    Sm    Eu    Gd    Tb    Dy    Ho    Er    Tm    Yb
397    1.99, 1.98, 1.98, 1.96, 1.94, 1.92, 1.92, 1.89, 1.90, 1.87,
398    // Lu    Hf    Ta    W     Re    Os    Ir    Pt    Au    Hg
399    1.87, 1.75, 1.70, 1.62, 1.51, 1.44, 1.41, 1.36, 1.36, 1.32,
400    // Tl    Pb    Bi    Po    At    Rn    Fr    Ra    Ac    Th
401    1.45, 1.46, 1.48, 1.40, 1.50, 1.50, 2.60, 2.21, 2.15, 2.06,
402    // Pa    U     Np    Pu    Am    Cm    Bk    Cf    Es    Fm
403    2.00, 1.96, 1.90, 1.87, 1.80, 1.69, 1.68, 1.68, 1.65, 1.67,
404    // Md    No    Lr    Rf    Db    Sg    Bh    Hs    Mt    Ds
405    1.73, 1.76, 1.61, 1.57, 1.49, 1.43, 1.41, 1.34, 1.29, 1.28,
406    // Rg    Cn    Nh    Fl    Mc    Lv    Ts    Og
407    1.21, 1.22, 1.36, 1.43, 1.62, 1.75, 1.65, 1.57,
408];
409
410/// Monoisotopic atomic masses (u) indexed by (atomic_number - 1).
411/// Source: NIST, exact masses of most abundant isotopes, used for CIP rule 4 (atomic mass tiebreaker).
412#[rustfmt::skip]
413static ATOMIC_MASSES: [f64; 118] = [
414    // H        He       Li       Be       B        C        N        O        F        Ne
415    1.007825, 4.002603, 7.016003, 9.012182, 11.009305, 12.000000, 14.003074, 15.994915, 18.998403, 19.992440,
416    // Na       Mg       Al       Si       P        S        Cl       Ar       K        Ca
417    22.989770, 23.985042, 26.981538, 27.976927, 30.973762, 31.972071, 34.968853, 39.962383, 38.963707, 39.962591,
418    // Sc       Ti       V        Cr       Mn       Fe       Co       Ni       Cu       Zn
419    44.955910, 47.947947, 50.943963, 51.940512, 54.938045, 55.934840, 58.933195, 57.935343, 62.929598, 63.929145,
420    // Ga       Ge       As       Se       Br       Kr       Rb       Sr       Y        Zr
421    68.925581, 73.921218, 74.921596, 79.916521, 78.918337, 83.911507, 84.911789, 87.905614, 88.905848, 89.904704,
422    // Nb       Mo       Tc       Ru       Rh       Pd       Ag       Cd       In       Sn
423    92.906378, 97.905408, 98.906255, 101.904435, 102.905504, 105.903483, 106.905093, 113.903358, 114.903878, 119.902197,
424    // Sb       Te       I        Xe       Cs       Ba       La       Ce       Pr       Nd
425    120.903818, 129.906224, 126.904477, 131.904154, 132.905447, 137.905241, 138.906349, 139.905434, 140.907648, 141.907720,
426    // Pm       Sm       Eu       Gd       Tb       Dy       Ho       Er       Tm       Yb
427    145.000000, 151.919729, 152.921227, 157.924103, 158.925343, 163.929171, 164.930319, 165.930292, 168.934211, 173.938858,
428    // Lu       Hf       Ta       W        Re       Os       Ir       Pt       Au       Hg
429    174.940768, 179.946549, 180.947885, 183.950931, 186.955751, 191.961479, 192.960837, 194.964791, 196.966569, 201.972326,
430    // Tl       Pb       Bi       Po       At       Rn       Fr       Ra       Ac       Th
431    202.972329, 207.982019, 208.980384, 209.000000, 210.000000, 222.000000, 223.000000, 226.000000, 227.000000, 232.038054,
432    // Pa       U        Np       Pu       Am       Cm       Bk       Cf       Es       Fm
433    231.035884, 238.028915, 237.000000, 244.000000, 243.000000, 247.000000, 247.000000, 251.000000, 252.000000, 257.000000,
434    // Md       No       Lr       Rf       Db       Sg       Bh       Hs       Mt       Ds
435    258.000000, 259.000000, 266.000000, 267.000000, 268.000000, 269.000000, 270.000000, 270.000000, 278.000000, 281.000000,
436    // Rg       Cn       Nh       Fl       Mc       Lv       Ts       Og
437    280.000000, 285.000000, 286.000000, 289.000000, 290.000000, 293.000000, 294.000000, 294.000000,
438];
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443
444    #[test]
445    fn test_roundtrip_symbol() {
446        for n in 1u8..=118 {
447            let elem = Element::from_atomic_number(n).unwrap();
448            let sym = elem.symbol();
449            let back =
450                Element::from_symbol(sym).unwrap_or_else(|| panic!("no elem for symbol {sym}"));
451            assert_eq!(elem, back, "roundtrip failed for atomic number {n}");
452        }
453    }
454
455    #[test]
456    fn test_organic_subset() {
457        for sym in &["B", "C", "N", "O", "P", "S", "F", "Cl", "Br", "I"] {
458            assert!(
459                Element::from_symbol(sym).unwrap().is_organic_subset(),
460                "{sym} should be in organic subset"
461            );
462        }
463        assert!(!Element::H.is_organic_subset());
464        assert!(!Element::FE.is_organic_subset());
465    }
466
467    #[test]
468    fn test_vdw_radii() {
469        assert!((Element::H.vdw_radius() - 1.20).abs() < 1e-4);
470        assert!((Element::C.vdw_radius() - 1.70).abs() < 1e-4);
471        assert!((Element::N.vdw_radius() - 1.55).abs() < 1e-4);
472        assert!((Element::O.vdw_radius() - 1.52).abs() < 1e-4);
473        assert!((Element::S.vdw_radius() - 1.80).abs() < 1e-4);
474        assert!((Element::CL.vdw_radius() - 1.75).abs() < 1e-4);
475        assert!((Element::BR.vdw_radius() - 1.85).abs() < 1e-4);
476        assert!((Element::I.vdw_radius() - 1.98).abs() < 1e-4);
477        // Heavy elements fall back to 1.70
478        assert!((Element::OG.vdw_radius() - 1.70).abs() < 1e-4);
479    }
480
481    #[test]
482    fn test_covalent_radii() {
483        assert!((Element::H.covalent_radius() - 0.31).abs() < 1e-4);
484        assert!((Element::C.covalent_radius() - 0.77).abs() < 1e-4);
485        assert!((Element::N.covalent_radius() - 0.71).abs() < 1e-4);
486        assert!((Element::O.covalent_radius() - 0.66).abs() < 1e-4);
487        assert!((Element::CL.covalent_radius() - 1.02).abs() < 1e-4);
488    }
489
490    #[test]
491    fn test_valences() {
492        assert_eq!(Element::C.normal_valences(), &[4]);
493        assert_eq!(Element::N.normal_valences(), &[3, 5]);
494        assert_eq!(Element::S.normal_valences(), &[2, 4, 6]);
495    }
496}