elements_rs 0.2.8

A comprehensive library for chemical elements and their isotopes with rich metadata
Documentation
//! Submodule providing a bitmask based on a `u128` to represent sets of
//! elements observed in a molecule.

use crate::Element;

/// Struct representing a set of elements as a bitmask.
///
/// Each bit in the `u128` corresponds to an element in the periodic table,
/// with the least significant bit representing Hydrogen (H, atomic number 1)
/// and the most significant bit representing Oganesson (Og, atomic number 118).
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "mem_size", derive(mem_dbg::MemSize))]
#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg))]
#[cfg_attr(feature = "mem_size", mem_size(flat))]
pub struct ElementMask(u128);

impl From<Element> for ElementMask {
    /// # Examples
    ///
    /// ```rust
    /// use elements_rs::{Element, ElementMask};
    ///
    /// let mask: ElementMask = Element::H.into();
    /// assert!(mask.contains(Element::H));
    /// ```
    fn from(element: Element) -> Self {
        let mut mask = ElementMask::default();
        mask.insert(element);
        mask
    }
}

impl ElementMask {
    /// Returns whether the bitmask contains the specified element.
    ///
    /// # Arguments
    ///
    /// * `element` - The element to check for in the bitmask.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use elements_rs::{Element, ElementMask};
    ///
    /// let mut mask = ElementMask::default();
    /// mask.insert(Element::H);
    /// assert!(mask.contains(Element::H));
    /// assert!(!mask.contains(Element::He));
    /// ```
    #[must_use]
    pub fn contains(&self, element: Element) -> bool {
        let atomic_number: u8 = element.into();
        let index = u32::from(atomic_number - 1);
        (self.0 & (1 << index)) != 0
    }

    /// Inserts an element into the bitmask and returns
    /// whether the element was not already present.
    ///
    /// # Arguments
    ///
    /// * `element` - The element to insert into the bitmask.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use elements_rs::{Element, ElementMask};
    ///
    /// let mut mask = ElementMask::default();
    /// assert!(mask.insert(Element::H)); // Was not present
    /// assert!(!mask.insert(Element::H)); // Already present
    /// ```
    pub fn insert(&mut self, element: Element) -> bool {
        let atomic_number: u8 = element.into();
        let index = u32::from(atomic_number - 1_u8);
        let was_present = self.contains(element);
        self.0 |= 1 << index;
        !was_present
    }
}

impl FromIterator<Element> for ElementMask {
    /// # Examples
    ///
    /// ```rust
    /// # extern crate alloc;
    /// use elements_rs::{Element, ElementMask};
    ///
    /// let elements = alloc::vec![Element::H, Element::He];
    /// let mask: ElementMask = elements.into_iter().collect();
    /// assert!(mask.contains(Element::H));
    /// assert!(mask.contains(Element::He));
    /// ```
    fn from_iter<T: IntoIterator<Item = Element>>(iter: T) -> Self {
        let mut mask = ElementMask::default();
        for element in iter {
            mask.insert(element);
        }
        mask
    }
}

impl core::iter::IntoIterator for ElementMask {
    type Item = Element;
    type IntoIter = ElementMaskIterator;

    /// # Examples
    ///
    /// ```rust
    /// # extern crate alloc;
    /// use elements_rs::{Element, ElementMask};
    ///
    /// let mut mask = ElementMask::default();
    /// mask.insert(Element::H);
    /// mask.insert(Element::He);
    /// let elements: Vec<Element> = mask.into_iter().collect();
    /// assert_eq!(elements, alloc::vec![Element::H, Element::He]);
    /// ```
    fn into_iter(self) -> Self::IntoIter {
        ElementMaskIterator { mask: self.0 }
    }
}

/// Iterator over the elements in an `ElementMask`.
#[cfg_attr(feature = "mem_size", derive(mem_dbg::MemSize))]
#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg))]
#[cfg_attr(feature = "mem_size", mem_size(flat))]
pub struct ElementMaskIterator {
    mask: u128,
}

impl Iterator for ElementMaskIterator {
    type Item = Element;

    fn next(&mut self) -> Option<Self::Item> {
        if self.mask == 0 {
            return None;
        }
        // The atomic number is the bit position plus one. A non-zero mask has a
        // trailing-zero count in `0..128`, so `+ 1` always fits in a `u8`.
        let atomic_number =
            u8::try_from(self.mask.trailing_zeros() + 1).expect("bit position fits in a u8");
        // Clear the lowest set bit so the next call yields the following element.
        self.mask &= self.mask - 1;
        Some(Element::try_from(atomic_number).unwrap())
    }
}