use crate::{
alignment::AlignmentIndices,
data::types::cigar::{Cigar, Ciglet},
};
use std::{
iter::FusedIterator,
ops::{ControlFlow, Range},
};
#[derive(Clone, Eq, PartialEq, Default)]
pub struct AlignmentStates(pub(crate) Vec<Ciglet>);
impl AlignmentStates {
#[inline]
#[must_use]
pub fn new() -> Self {
AlignmentStates(Vec::new())
}
#[inline]
#[must_use]
pub fn with_capacity(n: usize) -> Self {
AlignmentStates(Vec::with_capacity(n))
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[Ciglet] {
self.0.as_slice()
}
#[inline]
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [Ciglet] {
self.0.as_mut_slice()
}
#[inline]
#[must_use]
pub fn as_mut_vec(&mut self) -> &mut Vec<Ciglet> {
&mut self.0
}
pub fn add_state(&mut self, op: u8) {
self.add_ciglet(Ciglet { inc: 1, op });
}
pub fn prepend_state(&mut self, op: u8) {
self.prepend_ciglet(Ciglet { inc: 1, op });
}
pub fn add_ciglet(&mut self, ciglet: Ciglet) {
if ciglet.inc > 0 {
if let Some(last) = self.0.last_mut()
&& last.op == ciglet.op
{
last.inc = last.inc.strict_add(ciglet.inc);
} else {
self.0.push(ciglet);
}
}
}
pub fn prepend_ciglet(&mut self, ciglet: Ciglet) {
if ciglet.inc > 0 {
if let Some(first) = self.0.first_mut()
&& first.op == ciglet.op
{
first.inc = first.inc.strict_add(ciglet.inc);
} else {
self.0.insert(0, ciglet);
}
}
}
#[inline]
pub fn add_inc_op(&mut self, inc: usize, op: u8) {
self.add_ciglet(Ciglet { inc, op });
}
#[inline]
pub fn prepend_inc_op(&mut self, inc: usize, op: u8) {
self.prepend_ciglet(Ciglet { inc, op });
}
pub(crate) fn new_no_gaps(aligned_range: Range<usize>, query_len: usize) -> Self {
let mut states = AlignmentStates::with_capacity(3);
states.soft_clip(aligned_range.start);
states.add_inc_op(aligned_range.end - aligned_range.start, b'M');
states.soft_clip(query_len - aligned_range.end);
states
}
pub(crate) fn extend_from_ciglets<I>(&mut self, ciglets: I)
where
I: IntoIterator<Item = Ciglet>, {
let mut ciglets = ciglets.into_iter();
let Some(first_ciglet) = ciglets.next() else { return };
self.add_ciglet(first_ciglet);
self.0.extend(ciglets);
}
pub fn soft_clip(&mut self, inc: usize) {
self.add_ciglet(Ciglet { inc, op: b'S' });
}
pub fn prepend_soft_clip(&mut self, inc: usize) {
self.prepend_ciglet(Ciglet { inc, op: b'S' });
}
#[must_use]
pub fn to_cigar_unchecked(&self) -> Cigar {
Cigar::from_ciglets_unchecked(self.0.iter().copied())
}
#[must_use]
pub fn from_ciglets_unchecked<I: IntoIterator<Item = Ciglet>>(ciglets: I) -> Self {
Self(ciglets.into_iter().collect())
}
#[must_use]
pub fn from_cigar_unchecked(cigar: &Cigar) -> Self {
Self(cigar.iter().collect())
}
#[inline]
pub fn make_reverse(&mut self) {
self.0.reverse();
}
#[inline]
#[must_use]
pub fn to_reverse(&self) -> Self {
Self::from_ciglets_unchecked(self.into_iter().rev())
}
#[inline]
pub fn iter(&self) -> std::slice::Iter<'_, Ciglet> {
self.0.iter()
}
#[must_use]
pub fn to_verbose_sequence_matching(&self, reference: &[u8], query: &[u8], mut ref_index: usize) -> Self {
let mut out = AlignmentStates::with_capacity(self.0.len());
let mut query_index = 0;
for ciglet in self {
if ciglet.op == b'M' {
let query_slice = &query[query_index..query_index + ciglet.inc];
let ref_slice = &reference[ref_index..ref_index + ciglet.inc];
for (query_base, ref_base) in query_slice.iter().zip(ref_slice) {
out.add_state(if query_base == ref_base { b'=' } else { b'X' });
}
query_index += ciglet.inc;
ref_index += ciglet.inc;
} else {
out.add_ciglet(ciglet);
(query_index, ref_index) = (query_index, ref_index).increment_idxs_by(ciglet);
}
}
out
}
}
pub struct AlignmentIter<'a, I>
where
I: Iterator<Item = Ciglet>, {
reference_buffer: &'a [u8],
query_buffer: &'a [u8],
ciglets: I,
inc: usize,
op: u8,
}
impl<'a, I> AlignmentIter<'a, I>
where
I: Iterator<Item = Ciglet>,
{
#[inline]
#[must_use]
pub(crate) fn new(
reference: &'a [u8], query: &'a [u8], ciglets: impl IntoIterator<Item = Ciglet, IntoIter = I>, ref_index: usize,
) -> Self {
let reference_buffer = &reference[ref_index..];
let query_buffer = query;
let mut ciglets = ciglets.into_iter();
let Ciglet { inc, op } = ciglets.next().unwrap_or(Ciglet { inc: 0, op: b'M' });
AlignmentIter {
reference_buffer,
query_buffer,
ciglets,
inc,
op,
}
}
#[inline]
#[must_use]
fn get_next_op(&mut self) -> Option<u8> {
if self.inc > 0 {
self.inc -= 1;
Some(self.op)
} else {
let Ciglet { inc, op } = self.ciglets.next()?;
self.inc = inc;
self.op = op;
self.get_next_op()
}
}
#[inline]
#[must_use]
fn skip_to_next_ciglet(&mut self) -> Option<()> {
let Ciglet { inc, op } = self.ciglets.next()?;
self.inc = inc;
self.op = op;
Some(())
}
#[inline]
#[must_use]
fn advance_reference(&mut self) -> u8 {
let out = self.reference_buffer[0];
self.reference_buffer = &self.reference_buffer[1..];
out
}
#[inline]
#[must_use]
fn advance_query(&mut self) -> u8 {
let out = self.query_buffer[0];
self.query_buffer = &self.query_buffer[1..];
out
}
}
impl<I> Iterator for AlignmentIter<'_, I>
where
I: Iterator<Item = Ciglet>,
{
type Item = (Option<u8>, Option<u8>);
fn next(&mut self) -> Option<Self::Item> {
let op = self.get_next_op()?;
match op {
b'M' | b'=' | b'X' => Some((Some(self.advance_reference()), Some(self.advance_query()))),
b'D' => Some((Some(self.advance_reference()), None)),
b'I' => Some((None, Some(self.advance_query()))),
b'S' => {
self.query_buffer = &self.query_buffer[self.inc + 1..];
self.skip_to_next_ciglet()?;
self.next()
}
b'N' => Some((Some(self.advance_reference()), Some(b'N'))),
b'H' | b'P' => {
self.skip_to_next_ciglet()?;
self.next()
}
_ => panic!("CIGAR op '{op}' not supported.\n"),
}
}
}
pub trait PeekOp {
#[must_use]
fn peek_op(&mut self) -> Option<u8>;
#[must_use]
fn peek_op_back(&mut self) -> Option<u8>;
}
pub trait TakeOp: PeekOp {
#[must_use]
fn take_op(&mut self) -> Option<u8>;
#[must_use]
fn take_op_back(&mut self) -> Option<u8>;
#[inline]
fn take_op_if(&mut self, f: impl FnOnce(u8) -> bool) -> Option<u8> {
if f(self.peek_op()?) { self.take_op() } else { None }
}
#[inline]
fn take_op_back_if(&mut self, f: impl FnOnce(u8) -> bool) -> Option<u8> {
if f(self.peek_op_back()?) { self.take_op_back() } else { None }
}
}
pub trait PeekCiglet {
fn peek_ciglet(&mut self) -> Option<Ciglet>;
fn peek_ciglet_back(&mut self) -> Option<Ciglet>;
}
pub trait PeekCigletMut {
fn peek_ciglet_mut(&mut self) -> Option<&mut Ciglet>;
fn peek_ciglet_back_mut(&mut self) -> Option<&mut Ciglet>;
}
pub trait NextCiglet: PeekOp {
fn next_ciglet(&mut self) -> Option<Ciglet>;
fn next_ciglet_back(&mut self) -> Option<Ciglet>;
#[inline]
fn next_ciglet_if_op(&mut self, f: impl FnOnce(u8) -> bool) -> Option<Ciglet> {
if f(self.peek_op()?) { self.next_ciglet() } else { None }
}
#[inline]
fn next_ciglet_back_if_op(&mut self, f: impl FnOnce(u8) -> bool) -> Option<Ciglet> {
if f(self.peek_op_back()?) {
self.next_ciglet_back()
} else {
None
}
}
#[inline]
fn remove_clipping_front(&mut self) -> usize {
let hard_clipping = self.next_ciglet_if_op(|op| op == b'H').map_or(0, |ciglet| ciglet.inc);
let soft_clipping = self.next_ciglet_if_op(|op| op == b'S').map_or(0, |ciglet| ciglet.inc);
hard_clipping + soft_clipping
}
#[inline]
fn remove_clipping_back(&mut self) -> usize {
let hard_clipping = self.next_ciglet_back_if_op(|op| op == b'H').map_or(0, |ciglet| ciglet.inc);
let soft_clipping = self.next_ciglet_back_if_op(|op| op == b'S').map_or(0, |ciglet| ciglet.inc);
hard_clipping + soft_clipping
}
}
pub trait NextCigletMut<'a>: NextCiglet {
fn next_ciglet_mut(&mut self) -> Option<&'a mut Ciglet>;
fn next_ciglet_back_mut(&mut self) -> Option<&'a mut Ciglet>;
#[inline]
fn next_ciglet_if_op_mut(&mut self, f: impl FnOnce(u8) -> bool) -> Option<&'a mut Ciglet> {
if f(self.peek_op()?) { self.next_ciglet_mut() } else { None }
}
#[inline]
fn next_ciglet_back_if_op_mut(&mut self, f: impl FnOnce(u8) -> bool) -> Option<&'a mut Ciglet> {
if f(self.peek_op_back()?) {
self.next_ciglet_back_mut()
} else {
None
}
}
}
impl PeekOp for &[Ciglet] {
#[inline]
fn peek_op(&mut self) -> Option<u8> {
self.peek_ciglet().map(|ciglet| ciglet.op)
}
#[inline]
fn peek_op_back(&mut self) -> Option<u8> {
self.peek_ciglet_back().map(|ciglet| ciglet.op)
}
}
impl PeekCiglet for &[Ciglet] {
fn peek_ciglet(&mut self) -> Option<Ciglet> {
loop {
let ciglet = self.first()?;
if ciglet.inc == 0 {
self.split_off_first();
} else {
return Some(*ciglet);
}
}
}
fn peek_ciglet_back(&mut self) -> Option<Ciglet> {
loop {
let ciglet = self.last()?;
if ciglet.inc == 0 {
self.split_off_last();
} else {
return Some(*ciglet);
}
}
}
}
impl NextCiglet for &[Ciglet] {
#[inline]
fn next_ciglet(&mut self) -> Option<Ciglet> {
loop {
let ciglet = self.split_off_first()?;
if ciglet.inc > 0 {
return Some(*ciglet);
}
}
}
#[inline]
fn next_ciglet_back(&mut self) -> Option<Ciglet> {
loop {
let ciglet = self.split_off_last()?;
if ciglet.inc > 0 {
return Some(*ciglet);
}
}
}
}
impl PeekOp for &mut [Ciglet] {
#[inline]
fn peek_op(&mut self) -> Option<u8> {
self.peek_ciglet().map(|ciglet| ciglet.op)
}
#[inline]
fn peek_op_back(&mut self) -> Option<u8> {
self.peek_ciglet_back().map(|ciglet| ciglet.op)
}
}
impl TakeOp for &mut [Ciglet] {
fn take_op(&mut self) -> Option<u8> {
loop {
let ciglet = self.first_mut()?;
if let Some(new_inc) = ciglet.inc.checked_sub(1) {
ciglet.inc = new_inc;
return Some(ciglet.op);
}
self.split_off_first_mut();
}
}
fn take_op_back(&mut self) -> Option<u8> {
loop {
let ciglet = self.last_mut()?;
if let Some(new_inc) = ciglet.inc.checked_sub(1) {
ciglet.inc = new_inc;
return Some(ciglet.op);
}
self.split_off_last_mut();
}
}
}
impl PeekCiglet for &mut [Ciglet] {
fn peek_ciglet(&mut self) -> Option<Ciglet> {
loop {
let ciglet = self.first()?;
if ciglet.inc == 0 {
self.split_off_first_mut();
} else {
return Some(*ciglet);
}
}
}
fn peek_ciglet_back(&mut self) -> Option<Ciglet> {
loop {
let ciglet = self.last()?;
if ciglet.inc == 0 {
self.split_off_last_mut();
} else {
return Some(*ciglet);
}
}
}
}
impl NextCiglet for &mut [Ciglet] {
#[inline]
fn next_ciglet(&mut self) -> Option<Ciglet> {
self.next_ciglet_mut().copied()
}
#[inline]
fn next_ciglet_back(&mut self) -> Option<Ciglet> {
self.next_ciglet_back_mut().copied()
}
}
impl PeekCigletMut for &mut [Ciglet] {
fn peek_ciglet_mut(&mut self) -> Option<&mut Ciglet> {
let idx = if self.first()?.inc > 0 {
0
} else {
self.iter().position(|ciglet| ciglet.inc > 0)?
};
let _ = self.split_off_mut(..idx);
self.first_mut()
}
fn peek_ciglet_back_mut(&mut self) -> Option<&mut Ciglet> {
let idx = if self.last()?.inc > 0 {
self.len() - 1
} else {
self.iter().rposition(|ciglet| ciglet.inc > 0)?
};
let _ = self.split_off_mut(idx + 1..);
self.last_mut()
}
}
impl<'a> NextCigletMut<'a> for &'a mut [Ciglet] {
fn next_ciglet_mut(&mut self) -> Option<&'a mut Ciglet> {
loop {
let ciglet = self.split_off_first_mut()?;
if ciglet.inc > 0 {
return Some(ciglet);
}
}
}
fn next_ciglet_back_mut(&mut self) -> Option<&'a mut Ciglet> {
loop {
let ciglet = self.split_off_last_mut()?;
if ciglet.inc > 0 {
return Some(ciglet);
}
}
}
}
impl PeekOp for std::slice::Iter<'_, Ciglet> {
fn peek_op(&mut self) -> Option<u8> {
self.as_slice().peek_op()
}
fn peek_op_back(&mut self) -> Option<u8> {
self.as_slice().peek_op_back()
}
}
impl PeekCiglet for std::slice::Iter<'_, Ciglet> {
fn peek_ciglet(&mut self) -> Option<Ciglet> {
self.as_slice().peek_ciglet()
}
fn peek_ciglet_back(&mut self) -> Option<Ciglet> {
self.as_slice().peek_ciglet_back()
}
}
impl NextCiglet for std::slice::Iter<'_, Ciglet> {
fn next_ciglet(&mut self) -> Option<Ciglet> {
loop {
let ciglet = *self.next()?;
if ciglet.inc > 0 {
return Some(ciglet);
}
}
}
fn next_ciglet_back(&mut self) -> Option<Ciglet> {
loop {
let ciglet = *self.next_back()?;
if ciglet.inc > 0 {
return Some(ciglet);
}
}
}
}
pub struct CigletOpIter<'a> {
ciglets: std::slice::Iter<'a, Ciglet>,
ciglet: Ciglet,
ciglet_back: Ciglet,
}
impl<'a> CigletOpIter<'a> {
pub fn new<A>(ciglets: &'a A) -> Self
where
A: AsRef<[Ciglet]> + ?Sized, {
Self {
ciglets: ciglets.as_ref().iter(),
ciglet: Ciglet { inc: 0, op: b'M' },
ciglet_back: Ciglet { inc: 0, op: b'M' },
}
}
fn load_ciglet(&mut self) -> Option<&mut Ciglet> {
if self.ciglet.inc > 0 {
Some(&mut self.ciglet)
} else if let Some(next_ciglet) = self.ciglets.next_ciglet() {
self.ciglet = next_ciglet;
Some(&mut self.ciglet)
} else if self.ciglet_back.inc > 0 {
Some(&mut self.ciglet_back)
} else {
None
}
}
fn load_ciglet_back(&mut self) -> Option<&mut Ciglet> {
if self.ciglet_back.inc > 0 {
Some(&mut self.ciglet_back)
} else if let Some(next_ciglet) = self.ciglets.next_ciglet_back() {
self.ciglet_back = next_ciglet;
Some(&mut self.ciglet_back)
} else if self.ciglet.inc > 0 {
Some(&mut self.ciglet)
} else {
None
}
}
}
impl PeekOp for CigletOpIter<'_> {
fn peek_op(&mut self) -> Option<u8> {
self.peek_ciglet().map(|ciglet| ciglet.op)
}
fn peek_op_back(&mut self) -> Option<u8> {
self.peek_ciglet_back().map(|ciglet| ciglet.op)
}
}
impl TakeOp for CigletOpIter<'_> {
fn take_op(&mut self) -> Option<u8> {
let ciglet = self.load_ciglet()?;
ciglet.inc -= 1;
Some(ciglet.op)
}
fn take_op_back(&mut self) -> Option<u8> {
let ciglet = self.load_ciglet_back()?;
ciglet.inc -= 1;
Some(ciglet.op)
}
}
impl PeekCiglet for CigletOpIter<'_> {
fn peek_ciglet(&mut self) -> Option<Ciglet> {
self.load_ciglet().copied()
}
fn peek_ciglet_back(&mut self) -> Option<Ciglet> {
self.load_ciglet_back().copied()
}
}
impl NextCiglet for CigletOpIter<'_> {
fn next_ciglet(&mut self) -> Option<Ciglet> {
Some(std::mem::replace(self.load_ciglet()?, Ciglet { inc: 0, op: b'M' }))
}
fn next_ciglet_back(&mut self) -> Option<Ciglet> {
Some(std::mem::replace(self.load_ciglet_back()?, Ciglet { inc: 0, op: b'M' }))
}
}
impl Iterator for CigletOpIter<'_> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
self.take_op()
}
fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
loop {
let ciglet = self.load_ciglet()?;
if n < ciglet.inc {
ciglet.inc -= n + 1;
return Some(ciglet.op);
}
n -= ciglet.inc;
ciglet.inc = 0;
}
}
fn last(mut self) -> Option<Self::Item>
where
Self: Sized, {
self.peek_op_back()
}
fn count(self) -> usize
where
Self: Sized, {
self.ciglet.inc + self.ciglet_back.inc + self.ciglets.map(|ciglet| ciglet.inc).sum::<usize>()
}
fn fold<B, F>(self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B, {
let mut accum = init;
if self.ciglet.inc > 0 {
accum = std::iter::repeat_n(self.ciglet.op, self.ciglet.inc).fold(accum, &mut f);
}
accum = self.ciglets.fold(accum, |accum, ciglet| {
std::iter::repeat_n(ciglet.op, ciglet.inc).fold(accum, &mut f)
});
if self.ciglet_back.inc > 0 {
accum = std::iter::repeat_n(self.ciglet_back.op, self.ciglet_back.inc).fold(accum, &mut f);
}
accum
}
fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
where
Self: Sized,
F: FnMut(B, Self::Item) -> R,
R: std::ops::Try<Output = B>, {
let mut accum = init;
while let Some(new_inc) = self.ciglet.inc.checked_sub(1) {
self.ciglet.inc = new_inc;
accum = f(accum, self.ciglet.op)?;
}
let mut current_ciglet = self.ciglet;
let res = self.ciglets.try_fold(accum, |mut accum, ciglet| {
current_ciglet = *ciglet;
while let Some(new_inc) = current_ciglet.inc.checked_sub(1) {
current_ciglet.inc = new_inc;
accum = f(accum, current_ciglet.op)?;
}
R::from_output(accum)
});
accum = match res.branch() {
ControlFlow::Continue(val) => val,
ControlFlow::Break(val) => {
self.ciglet = current_ciglet;
return R::from_residual(val);
}
};
while let Some(new_inc) = self.ciglet_back.inc.checked_sub(1) {
self.ciglet_back.inc = new_inc;
accum = f(accum, self.ciglet_back.op)?;
}
R::from_output(accum)
}
}
impl DoubleEndedIterator for CigletOpIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.take_op_back()
}
fn nth_back(&mut self, mut n: usize) -> Option<Self::Item> {
loop {
let ciglet = self.load_ciglet_back()?;
if n < ciglet.inc {
ciglet.inc -= n + 1;
return Some(ciglet.op);
}
n -= ciglet.inc;
ciglet.inc = 0;
}
}
fn rfold<B, F>(self, init: B, mut f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B, {
let mut accum = init;
if self.ciglet_back.inc > 0 {
accum = std::iter::repeat_n(self.ciglet_back.op, self.ciglet_back.inc).fold(accum, &mut f);
}
accum = self.ciglets.rfold(accum, |accum, ciglet| {
std::iter::repeat_n(ciglet.op, ciglet.inc).fold(accum, &mut f)
});
if self.ciglet.inc > 0 {
accum = std::iter::repeat_n(self.ciglet.op, self.ciglet.inc).fold(accum, &mut f);
}
accum
}
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
where
Self: Sized,
F: FnMut(B, Self::Item) -> R,
R: std::ops::Try<Output = B>, {
let mut accum = init;
while let Some(new_inc) = self.ciglet_back.inc.checked_sub(1) {
self.ciglet_back.inc = new_inc;
accum = f(accum, self.ciglet_back.op)?;
}
let mut current_ciglet = self.ciglet_back;
let res = self.ciglets.try_rfold(accum, |mut accum, ciglet| {
current_ciglet = *ciglet;
while let Some(new_inc) = current_ciglet.inc.checked_sub(1) {
current_ciglet.inc = new_inc;
accum = f(accum, current_ciglet.op)?;
}
R::from_output(accum)
});
accum = match res.branch() {
ControlFlow::Continue(val) => val,
ControlFlow::Break(val) => {
self.ciglet_back = current_ciglet;
return R::from_residual(val);
}
};
while let Some(new_inc) = self.ciglet.inc.checked_sub(1) {
self.ciglet.inc = new_inc;
accum = f(accum, self.ciglet.op)?;
}
R::from_output(accum)
}
}
impl FusedIterator for CigletOpIter<'_> {}