rustyms 0.11.0

A library to handle proteomic mass spectrometry data and match peptides to spectra.
Documentation
use std::num::NonZeroU16;

use rand::distr::{Distribution, StandardUniform};

use crate::{
    chemistry::{Element, MolecularFormula},
    glycan::{BaseSugar, GlycanStructure, GlycanSubstituent, MonoSaccharide},
    sequence::SimpleModificationInner,
    system::{Mass, OrderedMass, dalton},
};

impl Distribution<SimpleModificationInner> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> SimpleModificationInner {
        match rng.random_range(0..=3) {
            0 => SimpleModificationInner::Mass(rng.random()),
            1 => SimpleModificationInner::Formula(rng.random()),
            2 => {
                let mut glycans: Vec<(MonoSaccharide, i8)> = Vec::new();
                for _ in 0..rng.random_range(0..32) {
                    glycans.push(rng.random());
                }
                SimpleModificationInner::Glycan(
                    glycans
                        .into_iter()
                        .map(|(ms, number)| (ms, number as isize))
                        .collect(),
                )
            }
            3 => SimpleModificationInner::GlycanStructure(rng.random()),
            _ => todo!(),
        }
    }
}

impl Distribution<GlycanStructure> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> GlycanStructure {
        let mut branches = Vec::new();
        for _ in 0..rng.random_range(0..=2) {
            branches.push(rng.random());
        }
        GlycanStructure::new(rng.random(), branches)
    }
}

impl Distribution<MonoSaccharide> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> MonoSaccharide {
        let mut substituents = Vec::new();
        for _ in 0..rng.random_range(0..5) {
            substituents.push(rng.random());
        }
        MonoSaccharide::new(rng.random(), &substituents)
    }
}

impl Distribution<MolecularFormula> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> MolecularFormula {
        let mut formula = MolecularFormula::default();
        for _ in 0..rng.random_range(0..32) {
            let element: Element = rng.random();
            let isotope: u16 = rng.random();
            let isotope = if rng.random::<f32>() > 0.9 {
                None
            } else {
                NonZeroU16::new(isotope)
            };

            if element.is_valid(isotope) {
                let _ = formula.add((element, isotope, rng.random()));
            }
        }
        formula
    }
}

impl Distribution<GlycanSubstituent> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> GlycanSubstituent {
        match rng.random_range(1..=36) {
            1 => GlycanSubstituent::Acetimidoyl,
            2 => GlycanSubstituent::Acetyl,
            3 => GlycanSubstituent::Acid,
            4 => GlycanSubstituent::Alanyl,
            5 => GlycanSubstituent::Alcohol,
            6 => GlycanSubstituent::Amino,
            7 => GlycanSubstituent::Aric,
            8 => GlycanSubstituent::CargoxyEthylidene,
            9 => GlycanSubstituent::Deoxy,
            10 => GlycanSubstituent::Didehydro,
            11 => GlycanSubstituent::DiMethyl,
            12 => GlycanSubstituent::Element(rng.random()),
            13 => GlycanSubstituent::Ethanolamine,
            14 => GlycanSubstituent::EtOH,
            15 => GlycanSubstituent::Formyl,
            16 => GlycanSubstituent::Glyceryl,
            17 => GlycanSubstituent::Glycolyl,
            18 => GlycanSubstituent::Glycyl,
            19 => GlycanSubstituent::HydroxyButyryl,
            20 => GlycanSubstituent::Lac,
            21 => GlycanSubstituent::Lactyl,
            22 => GlycanSubstituent::Methyl,
            23 => GlycanSubstituent::NAcetyl,
            24 => GlycanSubstituent::NDiMe,
            25 => GlycanSubstituent::NFo,
            26 => GlycanSubstituent::NGlycolyl,
            27 => GlycanSubstituent::OCarboxyEthyl,
            28 => GlycanSubstituent::PCholine,
            29 => GlycanSubstituent::Phosphate,
            30 => GlycanSubstituent::Pyruvyl,
            31 => GlycanSubstituent::Suc,
            32 => GlycanSubstituent::Sulfate,
            33 => GlycanSubstituent::Tauryl,
            34 => GlycanSubstituent::Ulo,
            35 => GlycanSubstituent::Ulof,
            _ => GlycanSubstituent::Water,
        }
    }
}

impl Distribution<BaseSugar> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> BaseSugar {
        match rng.random_range(0..=9) {
            0 => BaseSugar::None,
            1 => BaseSugar::Sugar,
            2 => BaseSugar::Triose,
            3 => BaseSugar::Tetrose(None),
            4 => BaseSugar::Pentose(None),
            5 => BaseSugar::Hexose(None),
            6 => BaseSugar::Heptose(None),
            7 => BaseSugar::Octose,
            8 => BaseSugar::Nonose(None),
            _ => BaseSugar::Decose,
        }
    }
}

impl Distribution<Element> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> Element {
        Element::try_from(rng.random_range(Element::Electron as usize..Element::Og as usize))
            .unwrap()
    }
}

impl Distribution<OrderedMass> for StandardUniform {
    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> OrderedMass {
        Mass::new::<dalton>(rng.random_range(f64::MIN..f64::MAX)).into()
    }
}