use core::fmt::Display;
use crate::{
ChargeLike, ChargedMolecularTree, ChemicalTree, CountLike, MolecularTree,
errors::{CountError, NumericError},
subscript_digits_ltr,
};
#[derive(Debug, PartialEq, Clone, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "mem_size", derive(mem_dbg::MemSize))]
#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg))]
pub struct RepeatNode<Count, T> {
pub(crate) count: Count,
pub(crate) node: T,
}
impl<Count, T> AsRef<T> for RepeatNode<Count, T> {
fn as_ref(&self) -> &T {
&self.node
}
}
impl<Count: CountLike, Charge: ChargeLike, Extension, N> From<RepeatNode<Count, N>>
for ChemicalTree<Count, Charge, Extension>
where
ChemicalTree<Count, Charge, Extension>: From<N>,
{
fn from(repeat: RepeatNode<Count, N>) -> Self {
if repeat.count == Count::ONE {
return ChemicalTree::from(repeat.node);
}
ChemicalTree::Repeat(RepeatNode {
count: repeat.count,
node: alloc::boxed::Box::new(repeat.node.into()),
})
}
}
impl<Count: CountLike, T: MolecularTree<Count>> MolecularTree<Count> for RepeatNode<Count, T> {
type ElementIter<'a>
= core::iter::FlatMap<
core::iter::RepeatN<&'a T>,
T::ElementIter<'a>,
fn(&'a T) -> T::ElementIter<'a>,
>
where
Self: 'a;
type NonHydrogenElementIter<'a>
= core::iter::FlatMap<
core::iter::RepeatN<&'a T>,
T::NonHydrogenElementIter<'a>,
fn(&'a T) -> T::NonHydrogenElementIter<'a>,
>
where
Self: 'a;
#[inline]
fn elements(&self) -> Self::ElementIter<'_> {
let count: usize = self
.count
.try_into()
.ok()
.expect("Count too large for usize - do you have an extremely large repeat count?");
core::iter::repeat_n(&self.node, count).flat_map(T::elements)
}
#[inline]
fn non_hydrogens(&self) -> Self::NonHydrogenElementIter<'_> {
let count: usize = self
.count
.try_into()
.ok()
.expect("Count too large for usize - do you have an extremely large repeat count?");
core::iter::repeat_n(&self.node, count).flat_map(T::non_hydrogens)
}
#[inline]
fn contains_elements(&self) -> bool {
self.node.contains_elements()
}
#[inline]
fn contains_non_hydrogens(&self) -> bool {
self.node.contains_non_hydrogens()
}
#[inline]
fn contains_isotopes(&self) -> bool {
self.node.contains_isotopes()
}
#[inline]
fn contains_element(&self, element: elements_rs::Element) -> bool {
self.node.contains_element(element)
}
#[inline]
fn contains_isotope(&self, isotope: elements_rs::Isotope) -> bool {
self.node.contains_isotope(isotope)
}
#[inline]
fn number_of_elements(&self) -> usize {
let count: usize = self.count.try_into().ok().expect("Count too large for usize");
count * self.node.number_of_elements()
}
#[inline]
fn count_of_element<C>(&self, element: elements_rs::Element) -> Result<C, CountError>
where
C: From<Count>
+ num_traits::CheckedAdd
+ num_traits::CheckedMul
+ num_traits::ConstZero
+ num_traits::ConstOne,
{
let node_count = self.node.count_of_element::<C>(element)?;
let count_as_c: C = C::from(self.count);
Ok(node_count.checked_mul(&count_as_c).ok_or(NumericError::PositiveOverflow)?)
}
#[inline]
fn count_of_isotope<C>(&self, isotope: elements_rs::Isotope) -> Result<C, CountError>
where
C: From<Count>
+ num_traits::CheckedAdd
+ num_traits::CheckedMul
+ num_traits::ConstZero
+ num_traits::ConstOne,
{
let node_count = self.node.count_of_isotope::<C>(isotope)?;
let count_as_c: C = C::from(self.count);
Ok(node_count.checked_mul(&count_as_c).ok_or(NumericError::PositiveOverflow)?)
}
#[inline]
fn isotopologue_mass(&self) -> f64 {
let count: f64 = self.count.into();
self.node.isotopologue_mass() * count
}
#[inline]
fn is_noble_gas_compound(&self) -> bool {
self.node.is_noble_gas_compound()
}
fn isotopic_normalization(&self) -> Self {
Self { node: self.node.isotopic_normalization(), count: self.count }
}
fn check_hill_ordering(
&self,
predecessor: Option<elements_rs::Element>,
has_carbon: bool,
) -> Result<Option<elements_rs::Element>, ()> {
self.node.check_hill_ordering(predecessor, has_carbon)
}
}
impl<Count: CountLike, T: Display> Display for RepeatNode<Count, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.node)?;
for digit in subscript_digits_ltr(self.count) {
write!(f, "{digit}")?;
}
Ok(())
}
}
impl<Count: CountLike, T> RepeatNode<Count, T> {
pub(crate) fn new(count: Count, node: T) -> Self {
assert_ne!(count, Count::ZERO, "RepeatNode count cannot be zero");
Self { count, node }
}
pub fn count(&self) -> &Count {
&self.count
}
pub fn node(&self) -> &T {
&self.node
}
}
impl<Count: CountLike, Charge, T: ChargedMolecularTree<Count, Charge>>
ChargedMolecularTree<Count, Charge> for RepeatNode<Count, T>
{
fn charge(&self) -> f64 {
let count: f64 = self.count.into();
self.node.charge() * count
}
fn isotopologue_mass_with_charge(&self) -> f64 {
let count: f64 = self.count.into();
self.node.isotopologue_mass_with_charge() * count
}
fn molar_mass(&self) -> f64 {
let count: f64 = self.count.into();
self.node.molar_mass() * count
}
}