use std::fmt;
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
pub enum Operation {
Sentinel = 0u8,
M = 1u8,
Eq = 2u8,
X = 3u8,
I = 4u8,
D = 5u8
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct OpLen {
pub op: Operation,
pub len: usize
}
pub struct Cigar {
s: Vec<OpLen>,
idx: usize
}
impl Cigar {
pub fn new(query_len: usize, reference_len: usize) -> Self {
let s = vec![OpLen { op: Operation::Sentinel, len: 0 }; query_len + reference_len + 5];
let idx = 1;
Cigar { s, idx }
}
#[allow(dead_code)]
pub(crate) fn clear(&mut self, query_len: usize, reference_len: usize) {
self.s[..query_len + reference_len + 5].fill(OpLen { op: Operation::Sentinel, len: 0 });
self.idx = 1;
}
#[allow(dead_code)]
pub(crate) unsafe fn add(&mut self, op: Operation) {
debug_assert!(self.idx < self.s.len());
let add = (op != (*self.s.as_ptr().add(self.idx - 1)).op) as usize;
self.idx += add;
(*self.s.as_mut_ptr().add(self.idx - 1)).op = op;
(*self.s.as_mut_ptr().add(self.idx - 1)).len += 1;
}
pub fn reverse(&mut self) {
self.s[1..self.idx].reverse();
}
pub fn len(&self) -> usize {
self.idx - 1
}
pub fn get(&self, i: usize) -> OpLen {
self.s[self.idx - 1 - i]
}
pub fn format(&self, q: &[u8], r: &[u8]) -> (String, String) {
let mut a = String::with_capacity(self.idx);
let mut b = String::with_capacity(self.idx);
let mut i = 0;
let mut j = 0;
for &op_len in self.s[1..self.idx].iter().rev() {
match op_len.op {
Operation::M | Operation::Eq | Operation::X => {
for _k in 0..op_len.len {
a.push(q[i] as char);
b.push(r[j] as char);
i += 1;
j += 1;
}
},
Operation::I => {
for _k in 0..op_len.len {
a.push(q[i] as char);
b.push('-');
i += 1;
}
},
Operation::D => {
for _k in 0..op_len.len {
a.push('-');
b.push(r[j] as char);
j += 1;
}
},
_ => continue
}
}
(a, b)
}
pub fn to_vec(&self) -> Vec<OpLen> {
self.s[1..self.idx]
.iter()
.rev()
.map(|&op_len| op_len)
.collect::<Vec<OpLen>>()
}
}
impl fmt::Display for Cigar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for &op_len in self.s[1..self.idx].iter().rev() {
let c = match op_len.op {
Operation::M => 'M',
Operation::Eq => '=',
Operation::X => 'X',
Operation::I => 'I',
Operation::D => 'D',
_ => continue
};
write!(f, "{}{}", op_len.len, c)?;
}
Ok(())
}
}