use super::csr::stored_range;
use crate::column::BitVec;
use crate::index::ResidueIndex;
use crate::limits::{CapacityError, TableError};
use crate::optional::{OptionalI32, OptionalSymbol};
use crate::symbol::SymbolId;
use std::ops::Range;
use std::sync::Arc;
#[derive(Clone, Debug, Default)]
pub struct ResidueTable {
first_atom: Arc<Vec<u32>>,
atom_count: Arc<Vec<u32>>,
label_comp_id: Arc<Vec<SymbolId>>,
auth_comp_id: Arc<Vec<OptionalSymbol>>,
label_seq_id: Arc<Vec<OptionalI32>>,
auth_seq_id: Arc<Vec<OptionalI32>>,
ins_code: Arc<Vec<OptionalSymbol>>,
het: BitVec,
}
#[derive(Clone, Copy, Debug)]
pub struct ResidueRecord {
pub label_comp_id: SymbolId,
pub auth_comp_id: OptionalSymbol,
pub label_seq_id: OptionalI32,
pub auth_seq_id: OptionalI32,
pub ins_code: OptionalSymbol,
pub het: bool,
}
impl ResidueTable {
#[must_use]
pub fn len(&self) -> usize {
self.label_comp_id.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.label_comp_id.is_empty()
}
pub fn push(
&mut self,
record: ResidueRecord,
atoms: Range<u32>,
) -> Result<ResidueIndex, TableError> {
let capacity = || TableError::Capacity(CapacityError::new("residue table"));
let position = u32::try_from(self.label_comp_id.len()).map_err(|_| capacity())?;
let atom_count = atoms
.end
.checked_sub(atoms.start)
.ok_or(TableError::ReversedRange { table: "residue" })?;
self.het.try_push(record.het).map_err(|_| capacity())?;
Arc::make_mut(&mut self.first_atom).push(atoms.start);
Arc::make_mut(&mut self.atom_count).push(atom_count);
Arc::make_mut(&mut self.label_comp_id).push(record.label_comp_id);
Arc::make_mut(&mut self.auth_comp_id).push(record.auth_comp_id);
Arc::make_mut(&mut self.label_seq_id).push(record.label_seq_id);
Arc::make_mut(&mut self.auth_seq_id).push(record.auth_seq_id);
Arc::make_mut(&mut self.ins_code).push(record.ins_code);
Ok(ResidueIndex::new(position))
}
pub fn set_atoms(
&mut self,
residue: ResidueIndex,
atoms: Range<u32>,
) -> Result<(), TableError> {
let atom_count = atoms
.end
.checked_sub(atoms.start)
.ok_or(TableError::ReversedRange { table: "residue" })?;
let Some(first) = Arc::make_mut(&mut self.first_atom).get_mut(residue.as_usize()) else {
return Err(TableError::MissingIndex { table: "residue" });
};
*first = atoms.start;
if let Some(count) = Arc::make_mut(&mut self.atom_count).get_mut(residue.as_usize()) {
*count = atom_count;
return Ok(());
}
Err(TableError::MissingIndex { table: "residue" })
}
pub(crate) fn last_atom_end(&self) -> Option<u32> {
self.first_atom
.last()
.copied()?
.checked_add(self.atom_count.last().copied()?)
}
#[must_use]
pub fn atoms(&self, residue: ResidueIndex) -> Option<Range<u32>> {
stored_range(&self.first_atom, &self.atom_count, residue.as_usize())
}
#[must_use]
pub fn label_comp_id(&self, residue: ResidueIndex) -> Option<SymbolId> {
self.label_comp_id.get(residue.as_usize()).copied()
}
#[must_use]
pub fn auth_comp_id(&self, residue: ResidueIndex) -> Option<SymbolId> {
self.auth_comp_id
.get(residue.as_usize())
.copied()
.and_then(OptionalSymbol::get)
}
#[must_use]
pub fn label_seq_id(&self, residue: ResidueIndex) -> Option<i32> {
self.label_seq_id
.get(residue.as_usize())
.copied()
.and_then(OptionalI32::get)
}
pub fn set_label_seq_id(&mut self, residue: ResidueIndex, seq: i32) -> Result<(), TableError> {
let slot = Arc::make_mut(&mut self.label_seq_id)
.get_mut(residue.as_usize())
.ok_or(TableError::MissingIndex { table: "residue" })?;
*slot = OptionalI32::some(seq);
Ok(())
}
#[must_use]
pub fn auth_seq_id(&self, residue: ResidueIndex) -> Option<i32> {
self.auth_seq_id
.get(residue.as_usize())
.copied()
.and_then(OptionalI32::get)
}
#[must_use]
pub fn ins_code(&self, residue: ResidueIndex) -> Option<SymbolId> {
self.ins_code
.get(residue.as_usize())
.copied()
.and_then(OptionalSymbol::get)
}
#[must_use]
pub fn is_het(&self, residue: ResidueIndex) -> bool {
self.het.test(residue.get())
}
#[must_use]
pub fn containing(&self, atom: u32) -> Option<ResidueIndex> {
let insertion = self.first_atom.partition_point(|first| *first <= atom);
let candidate = insertion.checked_sub(1)?;
let first = *self.first_atom.get(candidate)?;
let count = *self.atom_count.get(candidate)?;
let end = first.checked_add(count)?;
if atom < first || atom >= end {
return None;
}
let position = u32::try_from(candidate).ok()?;
Some(ResidueIndex::new(position))
}
}