use std::cmp::Ordering;
use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize};
use crate::{
fragment::Fragment,
sequence::CompoundPeptidoformIon,
spectrum::{PeakSpectrum, RawPeak},
system::{
f64::{Mass, MassOverCharge, Time},
isize::Charge,
},
};
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct AnnotatedSpectrum {
pub title: String,
pub num_scans: u64,
pub rt: Option<Time>,
pub charge: Option<Charge>,
pub mass: Option<Mass>,
pub peptide: CompoundPeptidoformIon,
pub(super) spectrum: Vec<AnnotatedPeak>,
}
impl Extend<AnnotatedPeak> for AnnotatedSpectrum {
fn extend<T: IntoIterator<Item = AnnotatedPeak>>(&mut self, iter: T) {
self.spectrum.extend(iter);
self.spectrum.sort_unstable();
}
}
impl IntoIterator for AnnotatedSpectrum {
type Item = AnnotatedPeak;
type IntoIter = std::vec::IntoIter<AnnotatedPeak>;
fn into_iter(self) -> Self::IntoIter {
self.spectrum.into_iter()
}
}
impl std::ops::Index<usize> for AnnotatedSpectrum {
type Output = AnnotatedPeak;
fn index(&self, index: usize) -> &Self::Output {
&self.spectrum[index]
}
}
impl PeakSpectrum for AnnotatedSpectrum {
type PeakType = AnnotatedPeak;
type Iter<'a> = std::slice::Iter<'a, Self::PeakType>;
fn binary_search(&self, low: MassOverCharge, high: MassOverCharge) -> &[AnnotatedPeak] {
let left_idx = match self
.spectrum
.binary_search_by(|a| a.experimental_mz.value.total_cmp(&low.value))
{
Ok(idx) | Err(idx) => {
let mut idx = idx.saturating_sub(1);
while idx > 0
&& self.spectrum[idx]
.experimental_mz
.value
.total_cmp(&low.value)
!= Ordering::Less
{
idx -= 1;
}
idx
}
};
let right_idx = match self.spectrum[left_idx..]
.binary_search_by(|a| a.experimental_mz.value.total_cmp(&high.value))
{
Ok(idx) | Err(idx) => {
let mut idx = idx + left_idx;
while idx < self.spectrum.len()
&& self.spectrum[idx]
.experimental_mz
.value
.total_cmp(&high.value)
!= Ordering::Greater
{
idx = idx.saturating_add(1);
}
idx.min(self.spectrum.len())
}
};
&self.spectrum[left_idx..right_idx]
}
fn spectrum(&self) -> Self::Iter<'_> {
self.spectrum.iter()
}
fn add_peak(&mut self, item: Self::PeakType) {
let index = self.spectrum.binary_search(&item).unwrap_or_else(|i| i);
self.spectrum.insert(index, item);
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AnnotatedPeak {
pub experimental_mz: MassOverCharge,
pub intensity: OrderedFloat<f64>,
pub annotation: Vec<Fragment>, pub isotope_annotation: Vec<(usize, usize)>,
}
impl AnnotatedPeak {
pub fn new(peak: &RawPeak, annotation: Fragment) -> Self {
Self {
experimental_mz: peak.mz,
intensity: peak.intensity,
annotation: vec![annotation],
isotope_annotation: Vec::new(),
}
}
pub fn background(peak: &RawPeak) -> Self {
Self {
experimental_mz: peak.mz,
intensity: peak.intensity,
annotation: Vec::new(),
isotope_annotation: Vec::new(),
}
}
}
impl PartialOrd for AnnotatedPeak {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for AnnotatedPeak {
fn cmp(&self, other: &Self) -> Ordering {
self.experimental_mz
.value
.total_cmp(&other.experimental_mz.value)
}
}
impl PartialEq for AnnotatedPeak {
fn eq(&self, other: &Self) -> bool {
self.experimental_mz
.value
.total_cmp(&other.experimental_mz.value)
== Ordering::Equal
&& self.intensity.total_cmp(&other.intensity) == Ordering::Equal
&& self.annotation == other.annotation
}
}
impl Eq for AnnotatedPeak {}