use crate::index::ResidueIndex;
use crate::topology::ResidueTable;
use std::mem::size_of_val;
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub enum ParentMapping {
#[default]
OffsetsOnly,
Explicit(Vec<u32>),
BlockIndexed {
block: u16,
parents: Vec<u32>,
},
}
impl ParentMapping {
pub const DEFAULT_BLOCK: u16 = 128;
#[must_use]
pub fn block_indexed(parents_in_order: &[u32]) -> Self {
let block = Self::DEFAULT_BLOCK;
let parents = parents_in_order
.iter()
.step_by(usize::from(block))
.copied()
.collect();
Self::BlockIndexed { block, parents }
}
#[must_use]
pub fn explicit(parents_in_order: &[u32]) -> Self {
Self::Explicit(parents_in_order.to_vec())
}
#[must_use]
pub fn bytes(&self) -> usize {
match self {
Self::OffsetsOnly => 0,
Self::Explicit(parents) | Self::BlockIndexed { parents, .. } => {
size_of_val(parents.as_slice())
}
}
}
#[must_use]
pub fn resolve(
&self,
local: u32,
first_atom: u32,
residues: &ResidueTable,
) -> Option<ResidueIndex> {
match self {
Self::OffsetsOnly => Self::resolve_from_offsets(local, first_atom, residues),
Self::Explicit(parents) => Self::resolve_explicit(local, parents),
Self::BlockIndexed { block, parents } => {
Self::resolve_block_indexed(local, first_atom, *block, parents, residues)
}
}
}
fn resolve_from_offsets(
local: u32,
first_atom: u32,
residues: &ResidueTable,
) -> Option<ResidueIndex> {
first_atom
.checked_add(local)
.and_then(|atom| residues.containing(atom))
}
fn resolve_explicit(local: u32, parents: &[u32]) -> Option<ResidueIndex> {
usize::try_from(local)
.ok()
.and_then(|index| parents.get(index))
.copied()
.map(ResidueIndex::new)
}
fn resolve_block_indexed(
local: u32,
first_atom: u32,
block: u16,
parents: &[u32],
residues: &ResidueTable,
) -> Option<ResidueIndex> {
let start = Self::block_parent(local, block, parents)?;
let atom = first_atom.checked_add(local)?;
Self::scan_forward(start, atom, residues)
}
fn block_parent(local: u32, block: u16, parents: &[u32]) -> Option<u32> {
let block = u32::from(block).max(1);
let block_index = local / block;
usize::try_from(block_index)
.ok()
.and_then(|index| parents.get(index))
.copied()
}
fn scan_forward(start: u32, atom: u32, residues: &ResidueTable) -> Option<ResidueIndex> {
let start = usize::try_from(start).ok()?;
(start..residues.len())
.map_while(|candidate| {
let candidate = u32::try_from(candidate).ok()?;
let residue = ResidueIndex::new(candidate);
let range = residues.atoms(residue)?;
(atom >= range.start).then_some((residue, range))
})
.find_map(|(residue, range)| range.contains(&atom).then_some(residue))
}
}
#[cfg(test)]
#[path = "parent_tests.rs"]
mod tests;