use std::default::Default;
use std::iter;
use std::marker::PhantomData;
use std::ops::Range;
use crate::alignment::AlignmentOperation;
use crate::pattern_matching::myers::{word_size, BitVec, DistType, State};
pub(super) trait StatesHandler<'a, T, D>
where
T: BitVec + 'a,
D: DistType,
{
type TracebackHandler: TracebackHandler<'a, T, D>;
type TracebackColumn: ?Sized;
fn init(&mut self, n: usize, m: D) -> usize;
fn set_max_state(&self, pos: usize, states: &mut [State<T, D>]);
fn add_state(&self, source: &Self::TracebackColumn, pos: usize, states: &mut [State<T, D>]);
fn init_traceback(&self, m: D, pos: usize, states: &'a [State<T, D>])
-> Self::TracebackHandler;
}
pub(super) trait TracebackHandler<'a, T, D>
where
T: BitVec + 'a,
D: DistType,
{
fn block(&self) -> &State<T, D>;
fn block_mut(&mut self) -> &mut State<T, D>;
fn left_block(&self) -> &State<T, D>;
fn left_block_mut(&mut self) -> &mut State<T, D>;
fn pos_bitvec(&self) -> T;
fn move_up(&mut self, adjust_dist: bool);
fn move_up_left(&mut self, adjust_dist: bool);
fn move_to_left(&mut self);
fn move_left_down_if_better(&mut self) -> bool;
fn column_slice(&self) -> &[State<T, D>];
fn finished(&self) -> bool;
fn print_state(&self) {
println!(
"--- TB dist ({:?} <-> {:?})",
self.left_block().dist,
self.block().dist
);
println!(
"{:064b} m\n{:064b} + ({:?}) (left) d={:?}\n{:064b} - ({:?})\n \
{:064b} + ({:?}) (current) d={:?}\n{:064b} - ({:?})\n",
self.pos_bitvec(),
self.left_block().pv,
self.left_block().pv,
self.left_block().dist,
self.left_block().mv,
self.left_block().mv,
self.block().pv,
self.block().pv,
self.block().dist,
self.block().mv,
self.block().mv
);
}
}
#[derive(Clone, Debug)]
pub(super) struct Traceback<'a, T, D, H>
where
T: BitVec + 'a,
D: DistType,
H: StatesHandler<'a, T, D>,
{
m: D,
positions: iter::Cycle<Range<usize>>,
handler: H,
pos: usize,
_t: PhantomData<&'a T>,
}
impl<'a, T, D, H> Traceback<'a, T, D, H>
where
T: BitVec,
D: DistType,
H: StatesHandler<'a, T, D>,
{
#[inline]
pub fn new(
states: &mut Vec<State<T, D>>,
initial_state: &H::TracebackColumn,
num_cols: usize,
m: D,
mut handler: H,
) -> Self {
let num_cols = num_cols + 2;
let n_states = handler.init(num_cols, m);
let mut tb = Traceback {
m,
positions: (0..num_cols).cycle(),
handler,
pos: 0,
_t: PhantomData,
};
let curr_len = states.len();
if n_states > curr_len {
states.reserve(n_states);
states.extend((0..n_states - curr_len).map(|_| State::default()));
} else {
states.truncate(n_states);
states.shrink_to_fit();
}
debug_assert!(states.len() == n_states);
tb.pos = tb.positions.next().unwrap();
tb.handler.set_max_state(tb.pos, states);
tb.add_state(initial_state, states);
tb
}
#[inline]
pub fn add_state(&mut self, column: &H::TracebackColumn, states: &mut [State<T, D>]) {
self.pos = self.positions.next().unwrap();
self.handler.add_state(column, self.pos, states);
}
#[inline]
pub fn traceback(
&self,
ops: Option<&mut Vec<AlignmentOperation>>,
states: &'a [State<T, D>],
) -> (D, D) {
self._traceback_at(self.pos, ops, states)
}
#[inline]
pub fn traceback_at(
&self,
pos: usize,
ops: Option<&mut Vec<AlignmentOperation>>,
states: &'a [State<T, D>],
) -> Option<(D, D)> {
let pos = pos + 2; if pos <= self.pos {
return Some(self._traceback_at(pos, ops, states));
}
None
}
#[inline]
fn _traceback_at(
&self,
pos: usize,
mut ops: Option<&mut Vec<AlignmentOperation>>,
state_slice: &'a [State<T, D>],
) -> (D, D) {
use self::AlignmentOperation::*;
let mut h = self.handler.init_traceback(self.m, pos, state_slice);
let ops = &mut ops;
let mut h_offset = D::zero();
let dist = h.block().dist;
h.move_up_left(true);
while !h.finished() {
let op;
#[allow(clippy::never_loop)]
loop {
if h.left_block().dist.wrapping_add(&D::one()) == h.block().dist {
h.move_up(false);
h.move_up_left(false);
op = Subst;
} else if h.block().pv & h.pos_bitvec() != T::zero() {
h.move_up(true);
h.move_up_left(true);
op = Ins;
break;
} else if h.move_left_down_if_better() {
op = Del;
} else {
h.move_up(false);
h.move_up_left(false);
op = Match;
}
h_offset += D::one();
h.move_to_left();
break;
}
if let Some(o) = ops.as_mut() {
o.push(op);
}
}
(h_offset, dist)
}
#[allow(dead_code)]
fn print_tb_matrix(&self, pos: usize, state_slice: &'a [State<T, D>]) {
let mut h = self.handler.init_traceback(self.m, pos, state_slice);
let m = self.m.to_usize().unwrap();
let mut out = vec![];
for _ in 0..state_slice.len() {
let mut col_out = vec![];
let mut empty = true;
for (i, state) in h.column_slice().iter().enumerate().rev() {
if !(state.is_new() || state.is_max()) {
empty = false;
}
let w = word_size::<T>();
let end = (i + 1) * w;
let n = if end <= m { w } else { m % w };
state.write_dist_column(n, &mut col_out);
}
out.push(col_out);
h.move_to_left();
if empty {
break;
}
}
for j in (0..m).rev() {
print!("{:>4}: ", m - j + 1);
for col in out.iter().rev() {
if let Some(d) = col.get(j) {
if *d >= (D::max_value() >> 1) {
print!(" ");
} else {
print!("{:>4?}", d);
}
} else {
print!(" -");
}
}
println!();
}
}
}