use super::Element;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum SubshellLabel {
S,
P,
D,
F,
G,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Subshell {
pub shell_number: u32,
pub subshell_label: SubshellLabel,
pub number_of_electrons: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ElectronicConfiguration {
pub noble_gas: Option<Element>,
pub valence_subshells: &'static [Subshell],
}
impl core::fmt::Display for SubshellLabel {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
SubshellLabel::S => "s",
SubshellLabel::P => "p",
SubshellLabel::D => "d",
SubshellLabel::F => "f",
SubshellLabel::G => "g",
})
}
}
impl core::fmt::Display for Subshell {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.number_of_electrons == 1 {
formatter.write_fmt(format_args!("{}{}", self.shell_number, self.subshell_label))?;
} else {
formatter.write_fmt(format_args!(
"{}{}{}",
self.shell_number,
self.subshell_label,
crate::superscript::Superscript::new(self.number_of_electrons)
))?;
}
Ok(())
}
}
#[cfg(feature = "symbol")]
impl core::fmt::Display for ElectronicConfiguration {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut first = true;
if let Some(element) = self.noble_gas {
formatter.write_fmt(format_args!("[{}]", element.symbol()))?;
first = false;
}
for subshell in self.valence_subshells {
if !first {
formatter.write_str(" ")?;
}
formatter.write_fmt(format_args!("{}", subshell,))?;
first = false;
}
Ok(())
}
}
macro_rules! ec {
($subshells:expr) => {
ElectronicConfiguration {
noble_gas: None,
valence_subshells: &$subshells,
}
};
($gas:ident, $subshells:expr) => {
ElectronicConfiguration {
noble_gas: Some(Element::$gas),
valence_subshells: &$subshells,
}
};
}
macro_rules! s {
($n:expr, $t:ident, $e:expr) => {
Subshell {
shell_number: $n,
subshell_label: SubshellLabel::$t,
number_of_electrons: $e,
}
};
}
impl Element {
pub const fn electronic_configuration(&self) -> ElectronicConfiguration {
use Element as E;
match self {
E::H => ec!([s!(1, S, 1),]),
E::He => ec!([s!(1, S, 2),]),
E::Li => ec!(He, [s!(2, S, 1),]),
E::Be => ec!(He, [s!(2, S, 2),]),
E::B => ec!(He, [s!(2, S, 2), s!(2, P, 1),]),
E::C => ec!(He, [s!(2, S, 2), s!(2, P, 2),]),
E::N => ec!(He, [s!(2, S, 2), s!(2, P, 3),]),
E::O => ec!(He, [s!(2, S, 2), s!(2, P, 4),]),
E::F => ec!(He, [s!(2, S, 2), s!(2, P, 5),]),
E::Ne => ec!(He, [s!(2, S, 2), s!(2, P, 6),]),
E::Na => ec!(Ne, [s!(3, S, 1),]),
E::Mg => ec!(Ne, [s!(3, S, 2),]),
E::Al => ec!(Ne, [s!(3, S, 2), s!(3, P, 1),]),
E::Si => ec!(Ne, [s!(3, S, 2), s!(3, P, 2),]),
E::P => ec!(Ne, [s!(3, S, 2), s!(3, P, 3),]),
E::S => ec!(Ne, [s!(3, S, 2), s!(3, P, 4),]),
E::Cl => ec!(Ne, [s!(3, S, 2), s!(3, P, 5),]),
E::Ar => ec!(Ne, [s!(3, S, 2), s!(3, P, 6),]),
E::K => ec!(Ar, [s!(4, S, 1),]),
E::Ca => ec!(Ar, [s!(4, S, 2),]),
E::Sc => ec!(Ar, [s!(3, D, 1), s!(4, S, 2),]),
E::Ti => ec!(Ar, [s!(3, D, 2), s!(4, S, 2),]),
E::V => ec!(Ar, [s!(3, D, 3), s!(4, S, 2),]),
E::Cr => ec!(Ar, [s!(3, D, 5), s!(4, S, 1),]),
E::Mn => ec!(Ar, [s!(3, D, 5), s!(4, S, 2),]),
E::Fe => ec!(Ar, [s!(3, D, 6), s!(4, S, 2),]),
E::Co => ec!(Ar, [s!(3, D, 7), s!(4, S, 2),]),
E::Ni => ec!(Ar, [s!(3, D, 8), s!(4, S, 2),]),
E::Cu => ec!(Ar, [s!(3, D, 10), s!(4, S, 1),]),
E::Zn => ec!(Ar, [s!(3, D, 10), s!(4, S, 2),]),
E::Ga => ec!(Ar, [s!(3, D, 10), s!(4, S, 2), s!(4, P, 1),]),
E::Ge => ec!(Ar, [s!(3, D, 10), s!(4, S, 2), s!(4, P, 2),]),
E::As => ec!(Ar, [s!(3, D, 10), s!(4, S, 2), s!(4, P, 3),]),
E::Se => ec!(Ar, [s!(3, D, 10), s!(4, S, 2), s!(4, P, 4),]),
E::Br => ec!(Ar, [s!(3, D, 10), s!(4, S, 2), s!(4, P, 5),]),
E::Kr => ec!(Ar, [s!(3, D, 10), s!(4, S, 2), s!(4, P, 6),]),
E::Rb => ec!(Kr, [s!(5, S, 1),]),
E::Sr => ec!(Kr, [s!(5, S, 2),]),
E::Y => ec!(Kr, [s!(4, D, 1), s!(5, S, 2),]),
E::Zr => ec!(Kr, [s!(4, D, 2), s!(5, S, 2),]),
E::Nb => ec!(Kr, [s!(4, D, 4), s!(5, S, 1),]),
E::Mo => ec!(Kr, [s!(4, D, 5), s!(5, S, 1),]),
E::Tc => ec!(Kr, [s!(4, D, 5), s!(5, S, 2),]),
E::Ru => ec!(Kr, [s!(4, D, 7), s!(5, S, 1),]),
E::Rh => ec!(Kr, [s!(4, D, 8), s!(5, S, 1),]),
E::Pd => ec!(Kr, [s!(4, D, 10),]),
E::Ag => ec!(Kr, [s!(4, D, 10), s!(5, S, 1),]),
E::Cd => ec!(Kr, [s!(4, D, 10), s!(5, S, 2),]),
E::In => ec!(Kr, [s!(4, D, 10), s!(5, S, 2), s!(5, P, 1),]),
E::Sn => ec!(Kr, [s!(4, D, 10), s!(5, S, 2), s!(5, P, 2),]),
E::Sb => ec!(Kr, [s!(4, D, 10), s!(5, S, 2), s!(5, P, 3),]),
E::Te => ec!(Kr, [s!(4, D, 10), s!(5, S, 2), s!(5, P, 4),]),
E::I => ec!(Kr, [s!(4, D, 10), s!(5, S, 2), s!(5, P, 5),]),
E::Xe => ec!(Kr, [s!(4, D, 10), s!(5, S, 2), s!(5, P, 6),]),
E::Cs => ec!(Xe, [s!(6, S, 1),]),
E::Ba => ec!(Xe, [s!(6, S, 2),]),
E::La => ec!(Xe, [s!(5, D, 1), s!(6, S, 2),]),
E::Ce => ec!(Xe, [s!(4, F, 1), s!(5, D, 1), s!(6, S, 2),]),
E::Pr => ec!(Xe, [s!(4, F, 3), s!(6, S, 2),]),
E::Nd => ec!(Xe, [s!(4, F, 4), s!(6, S, 2),]),
E::Pm => ec!(Xe, [s!(4, F, 5), s!(6, S, 2),]),
E::Sm => ec!(Xe, [s!(4, F, 6), s!(6, S, 2),]),
E::Eu => ec!(Xe, [s!(4, F, 7), s!(6, S, 2),]),
E::Gd => ec!(Xe, [s!(4, F, 7), s!(5, D, 1), s!(6, S, 2),]),
E::Tb => ec!(Xe, [s!(4, F, 9), s!(6, S, 2),]),
E::Dy => ec!(Xe, [s!(4, F, 10), s!(6, S, 2),]),
E::Ho => ec!(Xe, [s!(4, F, 11), s!(6, S, 2),]),
E::Er => ec!(Xe, [s!(4, F, 12), s!(6, S, 2),]),
E::Tm => ec!(Xe, [s!(4, F, 13), s!(6, S, 2),]),
E::Yb => ec!(Xe, [s!(4, F, 14), s!(6, S, 2),]),
E::Lu => ec!(Xe, [s!(4, F, 14), s!(5, D, 1), s!(6, S, 2),]),
E::Hf => ec!(Xe, [s!(4, F, 14), s!(5, D, 2), s!(6, S, 2),]),
E::Ta => ec!(Xe, [s!(4, F, 14), s!(5, D, 3), s!(6, S, 2),]),
E::W => ec!(Xe, [s!(4, F, 14), s!(5, D, 4), s!(6, S, 2),]),
E::Re => ec!(Xe, [s!(4, F, 14), s!(5, D, 5), s!(6, S, 2),]),
E::Os => ec!(Xe, [s!(4, F, 14), s!(5, D, 6), s!(6, S, 2),]),
E::Ir => ec!(Xe, [s!(4, F, 14), s!(5, D, 7), s!(6, S, 2),]),
E::Pt => ec!(Xe, [s!(4, F, 14), s!(5, D, 9), s!(6, S, 1),]),
E::Au => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 1),]),
E::Hg => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 2),]),
E::Tl => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 2), s!(6, P, 1),]),
E::Pb => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 2), s!(6, P, 2),]),
E::Bi => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 2), s!(6, P, 3),]),
E::Po => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 2), s!(6, P, 4),]),
E::At => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 2), s!(6, P, 5),]),
E::Rn => ec!(Xe, [s!(4, F, 14), s!(5, D, 10), s!(6, S, 2), s!(6, P, 6),]),
E::Fr => ec!(Rn, [s!(7, S, 1),]),
E::Ra => ec!(Rn, [s!(7, S, 2),]),
E::Ac => ec!(Rn, [s!(6, D, 1), s!(7, S, 2),]),
E::Th => ec!(Rn, [s!(6, D, 2), s!(7, S, 2),]),
E::Pa => ec!(Rn, [s!(5, F, 2), s!(6, D, 1), s!(7, S, 2),]),
E::U => ec!(Rn, [s!(5, F, 3), s!(6, D, 1), s!(7, S, 2),]),
E::Np => ec!(Rn, [s!(5, F, 4), s!(6, D, 1), s!(7, S, 2),]),
E::Pu => ec!(Rn, [s!(5, F, 6), s!(7, S, 2),]),
E::Am => ec!(Rn, [s!(5, F, 7), s!(7, S, 2),]),
E::Cm => ec!(Rn, [s!(5, F, 7), s!(6, D, 1), s!(7, S, 2),]),
E::Bk => ec!(Rn, [s!(5, F, 9), s!(7, S, 2),]),
E::Cf => ec!(Rn, [s!(5, F, 10), s!(7, S, 2),]),
E::Es => ec!(Rn, [s!(5, F, 11), s!(7, S, 2),]),
E::Fm => ec!(Rn, [s!(5, F, 12), s!(7, S, 2),]),
E::Md => ec!(Rn, [s!(5, F, 13), s!(7, S, 2),]),
E::No => ec!(Rn, [s!(5, F, 14), s!(7, S, 2),]),
E::Lr => ec!(Rn, [s!(5, F, 14), s!(7, S, 2), s!(7, P, 1),]),
E::Rf => ec!(Rn, [s!(5, F, 14), s!(6, D, 2), s!(7, S, 2),]),
E::Db => ec!(Rn, [s!(5, F, 14), s!(6, D, 3), s!(7, S, 2),]),
E::Sg => ec!(Rn, [s!(5, F, 14), s!(6, D, 4), s!(7, S, 2),]),
E::Bh => ec!(Rn, [s!(5, F, 14), s!(6, D, 5), s!(7, S, 2),]),
E::Hs => ec!(Rn, [s!(5, F, 14), s!(6, D, 6), s!(7, S, 2),]),
E::Mt => ec!(Rn, [s!(5, F, 14), s!(6, D, 7), s!(7, S, 2),]),
E::Ds => ec!(Rn, [s!(5, F, 14), s!(6, D, 9), s!(7, S, 1),]),
E::Rg => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 1),]),
E::Cn => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 2),]),
E::Nh => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 2), s!(7, P, 1),]),
E::Fl => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 2), s!(7, P, 2),]),
E::Mc => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 2), s!(7, P, 3),]),
E::Lv => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 2), s!(7, P, 4),]),
E::Ts => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 2), s!(7, P, 5),]),
E::Og => ec!(Rn, [s!(5, F, 14), s!(6, D, 10), s!(7, S, 2), s!(7, P, 6),]),
}
}
}
#[cfg(test)]
mod tests {
use crate::{ElectronicConfiguration, Element};
fn electron_count(element: Element) -> u32 {
let ElectronicConfiguration {
noble_gas,
valence_subshells,
} = element.electronic_configuration();
valence_subshells
.iter()
.map(|s| s.number_of_electrons)
.sum::<u32>()
+ noble_gas.map_or(0, electron_count)
}
#[test]
fn electron_count_matches_atomic_number() {
for element in Element::iter() {
assert_eq!(element.atomic_number(), electron_count(element));
}
}
}