use i32;
use std::cmp::max;
use std::iter::repeat;
use crate::alignment::{Alignment, AlignmentMode, AlignmentOperation};
use crate::utils::TextSlice;
pub mod banded;
pub const MIN_SCORE: i32 = -858_993_459;
pub trait MatchFunc {
fn score(&self, a: u8, b: u8) -> i32;
}
#[derive(
Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize,
)]
pub struct MatchParams {
pub match_score: i32,
pub mismatch_score: i32,
}
impl MatchParams {
pub fn new(match_score: i32, mismatch_score: i32) -> Self {
assert!(match_score >= 0, "match_score can't be negative");
assert!(mismatch_score <= 0, "mismatch_score can't be positive");
MatchParams {
match_score,
mismatch_score,
}
}
}
impl MatchFunc for MatchParams {
#[inline]
fn score(&self, a: u8, b: u8) -> i32 {
if a == b {
self.match_score
} else {
self.mismatch_score
}
}
}
impl<F> MatchFunc for F
where
F: Fn(u8, u8) -> i32,
{
fn score(&self, a: u8, b: u8) -> i32 {
(self)(a, b)
}
}
#[derive(
Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize,
)]
pub struct Scoring<F: MatchFunc> {
pub gap_open: i32,
pub gap_extend: i32,
pub match_fn: F,
pub match_scores: Option<(i32, i32)>,
pub xclip_prefix: i32,
pub xclip_suffix: i32,
pub yclip_prefix: i32,
pub yclip_suffix: i32,
}
impl Scoring<MatchParams> {
pub fn from_scores(
gap_open: i32,
gap_extend: i32,
match_score: i32,
mismatch_score: i32,
) -> Self {
assert!(gap_open <= 0, "gap_open can't be positive");
assert!(gap_extend <= 0, "gap_extend can't be positive");
Scoring {
gap_open,
gap_extend,
match_fn: MatchParams::new(match_score, mismatch_score),
match_scores: Some((match_score, mismatch_score)),
xclip_prefix: MIN_SCORE,
xclip_suffix: MIN_SCORE,
yclip_prefix: MIN_SCORE,
yclip_suffix: MIN_SCORE,
}
}
}
impl<F: MatchFunc> Scoring<F> {
pub fn new(gap_open: i32, gap_extend: i32, match_fn: F) -> Self {
assert!(gap_open <= 0, "gap_open can't be positive");
assert!(gap_extend <= 0, "gap_extend can't be positive");
Scoring {
gap_open,
gap_extend,
match_fn,
match_scores: None,
xclip_prefix: MIN_SCORE,
xclip_suffix: MIN_SCORE,
yclip_prefix: MIN_SCORE,
yclip_suffix: MIN_SCORE,
}
}
pub fn xclip(mut self, penalty: i32) -> Self {
assert!(penalty <= 0, "Clipping penalty can't be positive");
self.xclip_prefix = penalty;
self.xclip_suffix = penalty;
self
}
pub fn xclip_prefix(mut self, penalty: i32) -> Self {
assert!(penalty <= 0, "Clipping penalty can't be positive");
self.xclip_prefix = penalty;
self
}
pub fn xclip_suffix(mut self, penalty: i32) -> Self {
assert!(penalty <= 0, "Clipping penalty can't be positive");
self.xclip_suffix = penalty;
self
}
pub fn yclip(mut self, penalty: i32) -> Self {
assert!(penalty <= 0, "Clipping penalty can't be positive");
self.yclip_prefix = penalty;
self.yclip_suffix = penalty;
self
}
pub fn yclip_prefix(mut self, penalty: i32) -> Self {
assert!(penalty <= 0, "Clipping penalty can't be positive");
self.yclip_prefix = penalty;
self
}
pub fn yclip_suffix(mut self, penalty: i32) -> Self {
assert!(penalty <= 0, "Clipping penalty can't be positive");
self.yclip_suffix = penalty;
self
}
}
#[allow(non_snake_case)]
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
pub struct Aligner<F: MatchFunc> {
I: [Vec<i32>; 2],
D: [Vec<i32>; 2],
S: [Vec<i32>; 2],
Lx: Vec<usize>,
Ly: Vec<usize>,
Sn: Vec<i32>,
traceback: Traceback,
scoring: Scoring<F>,
}
const DEFAULT_ALIGNER_CAPACITY: usize = 200;
impl<F: MatchFunc> Aligner<F> {
pub fn new(gap_open: i32, gap_extend: i32, match_fn: F) -> Self {
Aligner::with_capacity(
DEFAULT_ALIGNER_CAPACITY,
DEFAULT_ALIGNER_CAPACITY,
gap_open,
gap_extend,
match_fn,
)
}
pub fn with_capacity(m: usize, n: usize, gap_open: i32, gap_extend: i32, match_fn: F) -> Self {
assert!(gap_open <= 0, "gap_open can't be positive");
assert!(gap_extend <= 0, "gap_extend can't be positive");
Aligner {
I: [Vec::with_capacity(m + 1), Vec::with_capacity(m + 1)],
D: [Vec::with_capacity(m + 1), Vec::with_capacity(m + 1)],
S: [Vec::with_capacity(m + 1), Vec::with_capacity(m + 1)],
Lx: Vec::with_capacity(n + 1),
Ly: Vec::with_capacity(m + 1),
Sn: Vec::with_capacity(m + 1),
traceback: Traceback::with_capacity(m, n),
scoring: Scoring::new(gap_open, gap_extend, match_fn),
}
}
pub fn with_scoring(scoring: Scoring<F>) -> Self {
Aligner::with_capacity_and_scoring(
DEFAULT_ALIGNER_CAPACITY,
DEFAULT_ALIGNER_CAPACITY,
scoring,
)
}
pub fn with_capacity_and_scoring(m: usize, n: usize, scoring: Scoring<F>) -> Self {
assert!(scoring.gap_open <= 0, "gap_open can't be positive");
assert!(scoring.gap_extend <= 0, "gap_extend can't be positive");
assert!(
scoring.xclip_prefix <= 0,
"Clipping penalty (x prefix) can't be positive"
);
assert!(
scoring.xclip_suffix <= 0,
"Clipping penalty (x suffix) can't be positive"
);
assert!(
scoring.yclip_prefix <= 0,
"Clipping penalty (y prefix) can't be positive"
);
assert!(
scoring.yclip_suffix <= 0,
"Clipping penalty (y suffix) can't be positive"
);
Aligner {
I: [Vec::with_capacity(m + 1), Vec::with_capacity(m + 1)],
D: [Vec::with_capacity(m + 1), Vec::with_capacity(m + 1)],
S: [Vec::with_capacity(m + 1), Vec::with_capacity(m + 1)],
Lx: Vec::with_capacity(n + 1),
Ly: Vec::with_capacity(m + 1),
Sn: Vec::with_capacity(m + 1),
traceback: Traceback::with_capacity(m, n),
scoring,
}
}
pub fn custom(&mut self, x: TextSlice<'_>, y: TextSlice<'_>) -> Alignment {
let (m, n) = (x.len(), y.len());
self.traceback.init(m, n);
for k in 0..2 {
self.I[k].clear();
self.D[k].clear();
self.S[k].clear();
self.D[k].extend(repeat(MIN_SCORE).take(m + 1));
self.I[k].extend(repeat(MIN_SCORE).take(m + 1));
self.S[k].extend(repeat(MIN_SCORE).take(m + 1));
self.S[k][0] = 0;
if k == 0 {
let mut tb = TracebackCell::new();
tb.set_all(TB_START);
self.traceback.set(0, 0, tb);
self.Lx.clear();
self.Lx.extend(repeat(0usize).take(n + 1));
self.Ly.clear();
self.Ly.extend(repeat(0usize).take(m + 1));
self.Sn.clear();
self.Sn.extend(repeat(MIN_SCORE).take(m + 1));
self.Sn[0] = self.scoring.yclip_suffix;
self.Ly[0] = n;
}
for i in 1..=m {
let mut tb = TracebackCell::new();
tb.set_all(TB_START);
if i == 1 {
self.I[k][i] = self.scoring.gap_open + self.scoring.gap_extend;
tb.set_i_bits(TB_START);
} else {
let i_score = self.scoring.gap_open + self.scoring.gap_extend * (i as i32);
let c_score =
self.scoring.xclip_prefix + self.scoring.gap_open + self.scoring.gap_extend; if i_score > c_score {
self.I[k][i] = i_score;
tb.set_i_bits(TB_INS);
} else {
self.I[k][i] = c_score;
tb.set_i_bits(TB_XCLIP_PREFIX);
}
}
if i == m {
tb.set_s_bits(TB_XCLIP_SUFFIX);
} else {
self.S[k][i] = MIN_SCORE;
}
if self.I[k][i] > self.S[k][i] {
self.S[k][i] = self.I[k][i];
tb.set_s_bits(TB_INS);
}
if self.scoring.xclip_prefix > self.S[k][i] {
self.S[k][i] = self.scoring.xclip_prefix;
tb.set_s_bits(TB_XCLIP_PREFIX);
}
if i != m && self.S[k][i] + self.scoring.xclip_suffix > self.S[k][m] {
self.S[k][m] = self.S[k][i] + self.scoring.xclip_suffix;
self.Lx[0] = m - i;
}
if k == 0 {
self.traceback.set(i, 0, tb);
}
if self.S[k][i] + self.scoring.yclip_suffix > self.Sn[i] {
self.Sn[i] = self.S[k][i] + self.scoring.yclip_suffix;
self.Ly[i] = n;
}
}
}
for j in 1..=n {
let curr = j % 2;
let prev = 1 - curr;
{
let mut tb = TracebackCell::new();
self.I[curr][0] = MIN_SCORE;
if j == 1 {
self.D[curr][0] = self.scoring.gap_open + self.scoring.gap_extend;
tb.set_d_bits(TB_START);
} else {
let d_score = self.scoring.gap_open + self.scoring.gap_extend * (j as i32);
let c_score =
self.scoring.yclip_prefix + self.scoring.gap_open + self.scoring.gap_extend;
if d_score > c_score {
self.D[curr][0] = d_score;
tb.set_d_bits(TB_DEL);
} else {
self.D[curr][0] = c_score;
tb.set_d_bits(TB_YCLIP_PREFIX);
}
}
if self.D[curr][0] > self.scoring.yclip_prefix {
self.S[curr][0] = self.D[curr][0];
tb.set_s_bits(TB_DEL);
} else {
self.S[curr][0] = self.scoring.yclip_prefix;
tb.set_s_bits(TB_YCLIP_PREFIX);
}
if j == n && self.Sn[0] > self.S[curr][0] {
self.S[curr][0] = self.Sn[0];
tb.set_s_bits(TB_YCLIP_SUFFIX);
} else if self.S[curr][0] + self.scoring.yclip_suffix > self.Sn[0] {
self.Sn[0] = self.S[curr][0] + self.scoring.yclip_suffix;
self.Ly[0] = n - j;
}
self.traceback.set(0, j, tb);
}
for i in 1..=m {
self.S[curr][i] = MIN_SCORE;
}
let q = y[j - 1];
let xclip_score = self.scoring.xclip_prefix
+ max(
self.scoring.yclip_prefix,
self.scoring.gap_open + self.scoring.gap_extend * (j as i32),
);
for i in 1..m + 1 {
let p = x[i - 1];
let mut tb = TracebackCell::new();
let m_score = self.S[prev][i - 1] + self.scoring.match_fn.score(p, q);
let i_score = self.I[curr][i - 1] + self.scoring.gap_extend;
let s_score = self.S[curr][i - 1] + self.scoring.gap_open + self.scoring.gap_extend;
let best_i_score;
if i_score > s_score {
best_i_score = i_score;
tb.set_i_bits(TB_INS);
} else {
best_i_score = s_score;
tb.set_i_bits(self.traceback.get(i - 1, j).get_s_bits());
}
let d_score = self.D[prev][i] + self.scoring.gap_extend;
let s_score = self.S[prev][i] + self.scoring.gap_open + self.scoring.gap_extend;
let best_d_score;
if d_score > s_score {
best_d_score = d_score;
tb.set_d_bits(TB_DEL);
} else {
best_d_score = s_score;
tb.set_d_bits(self.traceback.get(i, j - 1).get_s_bits());
}
tb.set_s_bits(TB_XCLIP_SUFFIX);
let mut best_s_score = self.S[curr][i];
if m_score > best_s_score {
best_s_score = m_score;
tb.set_s_bits(if p == q { TB_MATCH } else { TB_SUBST });
}
if best_i_score > best_s_score {
best_s_score = best_i_score;
tb.set_s_bits(TB_INS);
}
if best_d_score > best_s_score {
best_s_score = best_d_score;
tb.set_s_bits(TB_DEL);
}
if xclip_score > best_s_score {
best_s_score = xclip_score;
tb.set_s_bits(TB_XCLIP_PREFIX);
}
let yclip_score = self.scoring.yclip_prefix
+ self.scoring.gap_open
+ self.scoring.gap_extend * (i as i32);
if yclip_score > best_s_score {
best_s_score = yclip_score;
tb.set_s_bits(TB_YCLIP_PREFIX);
}
self.S[curr][i] = best_s_score;
self.I[curr][i] = best_i_score;
self.D[curr][i] = best_d_score;
if self.S[curr][i] + self.scoring.xclip_suffix > self.S[curr][m] {
self.S[curr][m] = self.S[curr][i] + self.scoring.xclip_suffix;
self.Lx[j] = m - i;
}
if self.S[curr][i] + self.scoring.yclip_suffix > self.Sn[i] {
self.Sn[i] = self.S[curr][i] + self.scoring.yclip_suffix;
self.Ly[i] = n - j;
}
self.traceback.set(i, j, tb);
}
}
for i in 0..=m {
let j = n;
let curr = j % 2;
if self.Sn[i] > self.S[curr][i] {
self.S[curr][i] = self.Sn[i];
self.traceback.get_mut(i, j).set_s_bits(TB_YCLIP_SUFFIX);
}
if self.S[curr][i] + self.scoring.xclip_suffix > self.S[curr][m] {
self.S[curr][m] = self.S[curr][i] + self.scoring.xclip_suffix;
self.Lx[j] = m - i;
self.traceback.get_mut(m, j).set_s_bits(TB_XCLIP_SUFFIX);
}
}
for i in 1..=m {
let j = n;
let curr = j % 2;
let s_score = self.S[curr][i - 1] + self.scoring.gap_open + self.scoring.gap_extend;
if s_score > self.I[curr][i] {
self.I[curr][i] = s_score;
let s_bit = self.traceback.get(i - 1, j).get_s_bits();
self.traceback.get_mut(i, j).set_i_bits(s_bit);
}
if s_score > self.S[curr][i] {
self.S[curr][i] = s_score;
self.traceback.get_mut(i, j).set_s_bits(TB_INS);
if self.S[curr][i] + self.scoring.xclip_suffix > self.S[curr][m] {
self.S[curr][m] = self.S[curr][i] + self.scoring.xclip_suffix;
self.Lx[j] = m - i;
self.traceback.get_mut(m, j).set_s_bits(TB_XCLIP_SUFFIX);
}
}
}
let mut i = m;
let mut j = n;
let mut operations = Vec::with_capacity(x.len());
let mut xstart: usize = 0usize;
let mut ystart: usize = 0usize;
let mut xend = m;
let mut yend = n;
let mut last_layer = self.traceback.get(i, j).get_s_bits();
loop {
let next_layer: u16;
match last_layer {
TB_START => break,
TB_INS => {
operations.push(AlignmentOperation::Ins);
next_layer = self.traceback.get(i, j).get_i_bits();
i -= 1;
}
TB_DEL => {
operations.push(AlignmentOperation::Del);
next_layer = self.traceback.get(i, j).get_d_bits();
j -= 1;
}
TB_MATCH => {
operations.push(AlignmentOperation::Match);
next_layer = self.traceback.get(i - 1, j - 1).get_s_bits();
i -= 1;
j -= 1;
}
TB_SUBST => {
operations.push(AlignmentOperation::Subst);
next_layer = self.traceback.get(i - 1, j - 1).get_s_bits();
i -= 1;
j -= 1;
}
TB_XCLIP_PREFIX => {
operations.push(AlignmentOperation::Xclip(i));
xstart = i;
i = 0;
next_layer = self.traceback.get(0, j).get_s_bits();
}
TB_XCLIP_SUFFIX => {
operations.push(AlignmentOperation::Xclip(self.Lx[j]));
i -= self.Lx[j];
xend = i;
next_layer = self.traceback.get(i, j).get_s_bits();
}
TB_YCLIP_PREFIX => {
operations.push(AlignmentOperation::Yclip(j));
ystart = j;
j = 0;
next_layer = self.traceback.get(i, 0).get_s_bits();
}
TB_YCLIP_SUFFIX => {
operations.push(AlignmentOperation::Yclip(self.Ly[i]));
j -= self.Ly[i];
yend = j;
next_layer = self.traceback.get(i, j).get_s_bits();
}
_ => panic!("Dint expect this!"),
}
last_layer = next_layer;
}
operations.reverse();
Alignment {
score: self.S[n % 2][m],
ystart,
xstart,
yend,
xend,
ylen: n,
xlen: m,
operations,
mode: AlignmentMode::Custom,
}
}
pub fn global(&mut self, x: TextSlice<'_>, y: TextSlice<'_>) -> Alignment {
let clip_penalties = [
self.scoring.xclip_prefix,
self.scoring.xclip_suffix,
self.scoring.yclip_prefix,
self.scoring.yclip_suffix,
];
self.scoring.xclip_prefix = MIN_SCORE;
self.scoring.xclip_suffix = MIN_SCORE;
self.scoring.yclip_prefix = MIN_SCORE;
self.scoring.yclip_suffix = MIN_SCORE;
let mut alignment = self.custom(x, y);
alignment.mode = AlignmentMode::Global;
self.scoring.xclip_prefix = clip_penalties[0];
self.scoring.xclip_suffix = clip_penalties[1];
self.scoring.yclip_prefix = clip_penalties[2];
self.scoring.yclip_suffix = clip_penalties[3];
alignment
}
pub fn semiglobal(&mut self, x: TextSlice<'_>, y: TextSlice<'_>) -> Alignment {
let clip_penalties = [
self.scoring.xclip_prefix,
self.scoring.xclip_suffix,
self.scoring.yclip_prefix,
self.scoring.yclip_suffix,
];
self.scoring.xclip_prefix = MIN_SCORE;
self.scoring.xclip_suffix = MIN_SCORE;
self.scoring.yclip_prefix = 0;
self.scoring.yclip_suffix = 0;
let mut alignment = self.custom(x, y);
alignment.mode = AlignmentMode::Semiglobal;
alignment.filter_clip_operations();
self.scoring.xclip_prefix = clip_penalties[0];
self.scoring.xclip_suffix = clip_penalties[1];
self.scoring.yclip_prefix = clip_penalties[2];
self.scoring.yclip_suffix = clip_penalties[3];
alignment
}
pub fn local(&mut self, x: TextSlice<'_>, y: TextSlice<'_>) -> Alignment {
let clip_penalties = [
self.scoring.xclip_prefix,
self.scoring.xclip_suffix,
self.scoring.yclip_prefix,
self.scoring.yclip_suffix,
];
self.scoring.xclip_prefix = 0;
self.scoring.xclip_suffix = 0;
self.scoring.yclip_prefix = 0;
self.scoring.yclip_suffix = 0;
let mut alignment = self.custom(x, y);
alignment.mode = AlignmentMode::Local;
alignment.filter_clip_operations();
self.scoring.xclip_prefix = clip_penalties[0];
self.scoring.xclip_suffix = clip_penalties[1];
self.scoring.yclip_prefix = clip_penalties[2];
self.scoring.yclip_suffix = clip_penalties[3];
alignment
}
}
#[derive(
Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize,
)]
pub struct TracebackCell {
v: u16,
}
const I_POS: u8 = 0; const D_POS: u8 = 4;
const S_POS: u8 = 8;
const TB_START: u16 = 0b0000;
const TB_INS: u16 = 0b0001;
const TB_DEL: u16 = 0b0010;
const TB_SUBST: u16 = 0b0011;
const TB_MATCH: u16 = 0b0100;
const TB_XCLIP_PREFIX: u16 = 0b0101; const TB_XCLIP_SUFFIX: u16 = 0b0110; const TB_YCLIP_PREFIX: u16 = 0b0111; const TB_YCLIP_SUFFIX: u16 = 0b1000;
const TB_MAX: u16 = 0b1000;
impl TracebackCell {
#[inline(always)]
pub fn new() -> TracebackCell {
Default::default()
}
#[inline(always)]
fn set_bits(&mut self, pos: u8, value: u16) {
let bits: u16 = (0b1111) << pos;
assert!(
value <= TB_MAX,
"Expected a value <= TB_MAX while setting traceback bits"
);
self.v = (self.v & !bits) | (value << pos) }
#[inline(always)]
pub fn set_i_bits(&mut self, value: u16) {
self.set_bits(I_POS, value);
}
#[inline(always)]
pub fn set_d_bits(&mut self, value: u16) {
self.set_bits(D_POS, value);
}
#[inline(always)]
pub fn set_s_bits(&mut self, value: u16) {
self.set_bits(S_POS, value);
}
#[inline(always)]
fn get_bits(self, pos: u8) -> u16 {
(self.v >> pos) & (0b1111)
}
#[inline(always)]
pub fn get_i_bits(self) -> u16 {
self.get_bits(I_POS)
}
#[inline(always)]
pub fn get_d_bits(self) -> u16 {
self.get_bits(D_POS)
}
#[inline(always)]
pub fn get_s_bits(self) -> u16 {
self.get_bits(S_POS)
}
pub fn set_all(&mut self, value: u16) {
self.set_i_bits(value);
self.set_d_bits(value);
self.set_s_bits(value);
}
}
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize, Deserialize)]
struct Traceback {
rows: usize,
cols: usize,
matrix: Vec<TracebackCell>,
}
impl Traceback {
fn with_capacity(m: usize, n: usize) -> Self {
let rows = m + 1;
let cols = n + 1;
Traceback {
rows,
cols,
matrix: Vec::with_capacity(rows * cols),
}
}
fn init(&mut self, m: usize, n: usize) {
self.matrix.clear();
let mut start = TracebackCell::new();
start.set_all(TB_START);
self.resize(m, n, start);
}
#[inline(always)]
fn set(&mut self, i: usize, j: usize, v: TracebackCell) {
debug_assert!(i < self.rows);
debug_assert!(j < self.cols);
self.matrix[i * self.cols + j] = v;
}
#[inline(always)]
fn get(&self, i: usize, j: usize) -> &TracebackCell {
debug_assert!(i < self.rows);
debug_assert!(j < self.cols);
&self.matrix[i * self.cols + j]
}
fn get_mut(&mut self, i: usize, j: usize) -> &mut TracebackCell {
debug_assert!(i < self.rows);
debug_assert!(j < self.cols);
&mut self.matrix[i * self.cols + j]
}
fn resize(&mut self, m: usize, n: usize, v: TracebackCell) {
self.rows = m + 1;
self.cols = n + 1;
self.matrix.resize(self.rows * self.cols, v);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::alignment::AlignmentOperation::*;
use crate::scores::blosum62;
#[test]
fn traceback_cell() {
let mut tb = TracebackCell::new();
tb.set_all(TB_SUBST);
assert_eq!(tb.get_i_bits(), TB_SUBST);
assert_eq!(tb.get_d_bits(), TB_SUBST);
assert_eq!(tb.get_s_bits(), TB_SUBST);
tb.set_d_bits(TB_INS);
assert_eq!(tb.get_d_bits(), TB_INS);
tb.set_i_bits(TB_XCLIP_PREFIX);
assert_eq!(tb.get_d_bits(), TB_INS);
assert_eq!(tb.get_i_bits(), TB_XCLIP_PREFIX);
tb.set_d_bits(TB_DEL);
assert_eq!(tb.get_d_bits(), TB_DEL);
assert_eq!(tb.get_i_bits(), TB_XCLIP_PREFIX);
tb.set_s_bits(TB_YCLIP_SUFFIX);
assert_eq!(tb.get_d_bits(), TB_DEL);
assert_eq!(tb.get_i_bits(), TB_XCLIP_PREFIX);
assert_eq!(tb.get_s_bits(), TB_YCLIP_SUFFIX);
}
#[test]
fn test_semiglobal() {
let x = b"ACCGTGGAT";
let y = b"AAAAACCGTTGAT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(alignment.ystart, 4);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Match, Match, Match, Match, Match, Subst, Match, Match, Match,]
);
}
#[test]
fn test_semiglobal_gap_open_lt_mismatch() {
let x = b"ACCGTGGAT";
let y = b"AAAAACCGTTGAT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -5i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -1, -1, score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(alignment.ystart, 4);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Match, Match, Match, Match, Del, Match, Ins, Match, Match, Match,]
);
}
#[test]
fn test_global_affine_ins() {
let x = b"ACGAGAACA";
let y = b"ACGACA";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -3i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
let alignment = aligner.global(x, y);
println!("aln:\n{}", alignment.pretty(x, y, 80));
assert_eq!(
alignment.operations,
[Match, Match, Match, Ins, Ins, Ins, Match, Match, Match]
);
}
#[test]
fn test_global_affine_ins2() {
let x = b"AGATAGATAGATAGGGAGTTGTGTAGATGATCCACAGT";
let y = b"AGATAGATAGATGTAGATGATCCACAGT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
let alignment = aligner.global(x, y);
println!("aln:\n{}", alignment.pretty(x, y, 80));
let mut correct = Vec::new();
correct.extend(repeat(Match).take(11));
correct.extend(repeat(Ins).take(10));
correct.extend(repeat(Match).take(17));
assert_eq!(alignment.operations, correct);
}
#[test]
fn test_local_affine_ins2() {
let x = b"ACGTATCATAGATAGATAGGGTTGTGTAGATGATCCACAG";
let y = b"CGTATCATAGATAGATGTAGATGATCCACAGT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
let alignment = aligner.local(x, y);
assert_eq!(alignment.xstart, 1);
assert_eq!(alignment.ystart, 0);
}
#[test]
fn test_local() {
let x = b"ACCGTGGAT";
let y = b"AAAAACCGTTGAT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
let alignment = aligner.local(x, y);
assert_eq!(alignment.ystart, 4);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Match, Match, Match, Match, Match, Subst, Match, Match, Match,]
);
}
#[test]
fn test_global() {
let x = b"ACCGTGGAT";
let y = b"AAAAACCGTTGAT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
let alignment = aligner.global(x, y);
println!("\naln:\n{}", alignment.pretty(x, y, 80));
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Del, Del, Del, Del, Match, Match, Match, Match, Match, Subst, Match, Match, Match,]
);
}
#[test]
fn test_blosum62() {
let x = b"AAAA";
let y = b"AAAA";
let score = &blosum62;
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, score);
let alignment = aligner.global(x, y);
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.xstart, 0);
assert_eq!(alignment.score, 16);
assert_eq!(alignment.operations, [Match, Match, Match, Match]);
}
#[test]
fn test_blosum62_local() {
let x = b"LSPADKTNVKAA";
let y = b"PEEKSAV";
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -10, -1, &blosum62);
let alignment = aligner.local(x, y);
assert_eq!(alignment.xstart, 2);
assert_eq!(alignment.xend, 9);
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.yend, 7);
assert_eq!(
alignment.operations,
[Match, Subst, Subst, Match, Subst, Subst, Match]
);
assert_eq!(alignment.score, 16);
}
#[test]
fn test_issue11() {
let y = b"TACC"; let x = b"AAAAACC"; let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
let alignment = aligner.global(x, y);
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Ins, Ins, Ins, Subst, Match, Match, Match]
);
}
#[test]
fn test_issue12_1() {
let x = b"CCGGCA";
let y = b"ACCGTTGACGC";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(alignment.xstart, 0);
assert_eq!(alignment.ystart, 1);
assert_eq!(
alignment.operations,
[Match, Match, Match, Subst, Subst, Subst]
);
}
#[test]
fn test_issue12_2() {
let y = b"CCGGCA";
let x = b"ACCGTTGACGC";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(alignment.xstart, 0);
assert_eq!(alignment.ystart, 0);
assert_eq!(
alignment.operations,
[Subst, Match, Ins, Ins, Ins, Ins, Ins, Ins, Subst, Match, Match,]
);
}
#[test]
fn test_issue12_3() {
let y = b"CCGTCCGGCAA";
let x = b"AAAAACCGTTGACGCAA";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[
Ins, Ins, Ins, Ins, Ins, Ins, Match, Subst, Subst, Match, Subst, Subst, Subst,
Match, Match, Match, Match,
]
);
let mut aligner = Aligner::with_capacity(y.len(), x.len(), -5, -1, &score);
let alignment = aligner.semiglobal(y, x);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Match, Subst, Subst, Match, Subst, Subst, Subst, Match, Match, Match, Match,]
);
}
#[test]
fn test_left_aligned_del() {
let x = b"GTGCATCATGTG";
let y = b"GTGCATCATCATGTG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
let alignment = aligner.global(x, y);
println!("\naln:\n{}", alignment.pretty(x, y, 80));
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[
Match, Match, Match, Del, Del, Del, Match, Match, Match, Match, Match, Match,
Match, Match, Match,
]
);
}
#[test]
fn test_global_right_del() {
let x = b"AACCACGTACGTGGGGGGA";
let y = b"CCACGTACGT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
let alignment = aligner.global(x, y);
println!("\naln:\n{}", alignment.pretty(x, y, 80));
println!("score:{}", alignment.score);
assert_eq!(alignment.score, -9);
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[
Ins, Ins, Match, Match, Match, Match, Match, Match, Match, Match, Match, Match,
Ins, Ins, Ins, Ins, Ins, Ins, Ins,
]
);
}
#[test]
fn test_left_aligned_ins() {
let x = b"GTGCATCATCATGTG";
let y = b"GTGCATCATGTG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
let alignment = aligner.global(x, y);
println!("\naln:\n{}", alignment.pretty(x, y, 80));
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[
Match, Match, Match, Ins, Ins, Ins, Match, Match, Match, Match, Match, Match,
Match, Match, Match,
]
);
}
#[test]
fn test_aligner_new() {
let x = b"ACCGTGGAT";
let y = b"AAAAACCGTTGAT";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::new(-5, -1, &score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(alignment.ystart, 4);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Match, Match, Match, Match, Match, Subst, Match, Match, Match,]
);
let alignment = aligner.local(x, y);
assert_eq!(alignment.ystart, 4);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Match, Match, Match, Match, Match, Subst, Match, Match, Match,]
);
let alignment = aligner.global(x, y);
assert_eq!(alignment.ystart, 0);
assert_eq!(alignment.xstart, 0);
assert_eq!(
alignment.operations,
[Del, Del, Del, Del, Match, Match, Match, Match, Match, Subst, Match, Match, Match,]
);
}
#[test]
fn test_semiglobal_simple() {
let x = b"GAAAACCGTTGAT";
let y = b"ACCGTGGATGGG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let mut aligner = Aligner::new(-5, -1, &score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(
alignment.operations,
[Ins, Ins, Ins, Ins, Match, Match, Match, Match, Match, Subst, Match, Match, Match,]
);
}
#[test]
fn test_insert_only_semiglobal() {
let x = b"TTTT";
let y = b"AAAA";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -3i32 };
let mut aligner = Aligner::new(-5, -1, &score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(alignment.operations, [Ins, Ins, Ins, Ins]);
}
#[test]
fn test_insert_in_between_semiglobal() {
let x = b"GGGGG";
let y = b"GGTAGGG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -3i32 };
let mut aligner = Aligner::new(-5, -1, &score);
let alignment = aligner.semiglobal(x, y);
assert_eq!(
alignment.operations,
[Match, Match, Del, Del, Match, Match, Match]
);
}
#[test]
fn test_xclip_prefix_custom() {
let x = b"GGGGGGATG";
let y = b"ATG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let scoring = Scoring::new(-5, -1, &score).xclip(-5);
let mut aligner = Aligner::with_scoring(scoring);
let alignment = aligner.custom(x, y);
assert_eq!(alignment.operations, [Xclip(6), Match, Match, Match]);
}
#[test]
fn test_yclip_prefix_custom() {
let y = b"GGGGGGATG";
let x = b"ATG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let scoring = Scoring::new(-5, -1, &score).yclip(-5);
let mut aligner = Aligner::with_scoring(scoring);
let alignment = aligner.custom(x, y);
assert_eq!(alignment.operations, [Yclip(6), Match, Match, Match]);
}
#[test]
fn test_xclip_suffix_custom() {
let x = b"GAAAA";
let y = b"CG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let scoring = Scoring::new(-5, -1, &score).xclip(-5).yclip(0);
let mut aligner = Aligner::with_scoring(scoring);
let alignment = aligner.custom(x, y);
assert_eq!(alignment.operations, [Yclip(1), Match, Xclip(4)]);
}
#[test]
fn test_yclip_suffix_custom() {
let y = b"GAAAA";
let x = b"CG";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -3i32 };
let scoring = Scoring::new(-5, -1, &score).yclip(-5).xclip(0);
let mut aligner = Aligner::with_scoring(scoring);
let alignment = aligner.custom(x, y);
assert_eq!(alignment.operations, [Xclip(1), Match, Yclip(4)]);
}
#[test]
fn test_xclip_prefix_suffix() {
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let scoring1 = Scoring::new(-5, -1, &score).xclip(-5);
let scoring2 = Scoring::new(-5, -1, &score)
.xclip_prefix(-5)
.xclip_suffix(-5);
assert_eq!(scoring1.xclip_prefix, scoring2.xclip_prefix);
assert_eq!(scoring1.xclip_suffix, scoring2.xclip_suffix);
}
#[test]
fn test_yclip_prefix_suffix() {
let score = |a: u8, b: u8| if a == b { 1i32 } else { -1i32 };
let scoring1 = Scoring::new(-5, -1, &score).yclip(-5);
let scoring2 = Scoring::new(-5, -1, &score)
.yclip_prefix(-5)
.yclip_suffix(-5);
assert_eq!(scoring1.yclip_prefix, scoring2.yclip_prefix);
assert_eq!(scoring1.yclip_suffix, scoring2.yclip_suffix);
}
#[test]
fn test_longer_string_all_operations() {
let x = b"TTTTTGGGGGGATGGCCCCCCTTTTTTTTTTGGGAAAAAAAAAGGGGGG";
let y = b"GGGGGGATTTCCCCCCCCCTTTTTTTTTTAAAAAAAAA";
let score = |a: u8, b: u8| if a == b { 1i32 } else { -3i32 };
let scoring = Scoring::new(-5, -1, &score).xclip(-5).yclip(0);
let mut aligner = Aligner::with_scoring(scoring);
let alignment = aligner.custom(x, y);
println!("{}", alignment.pretty(x, y, 80));
assert_eq!(alignment.score, 7);
}
#[test]
fn test_scoring_from_scores() {
let y = b"GGGGGGATG";
let x = b"ATG";
let scoring = Scoring::from_scores(-5, -1, 1, -1).yclip(-5);
let mut aligner = Aligner::with_scoring(scoring);
let alignment = aligner.custom(x, y);
assert_eq!(alignment.operations, [Yclip(6), Match, Match, Match]);
}
#[test]
fn test_only_clips() {
let x = b"GGAAAAAAAAAAAAA";
let y = b"TTTTAATTTGTGTAAAAAATAATA";
let base_score = Scoring::from_scores(-4, -4, 4, -7);
let scoring = Scoring {
xclip_prefix: 0,
xclip_suffix: 0,
yclip_suffix: 0,
..base_score
};
let mut al = Aligner::with_scoring(scoring);
let alignment = al.custom(x, y);
assert_eq!(alignment.score, 0);
}
#[test]
fn test_zero_score_clips() {
let x = b"AA";
let y = b"CC";
let base_score = Scoring::from_scores(-1, -1, 1, -1);
{
let scoring = Scoring {
xclip_prefix: 0,
yclip_prefix: 0,
..base_score
};
let mut al = Aligner::with_scoring(scoring);
let alignment = al.custom(x, y);
assert_eq!(alignment.score, 0);
}
{
let scoring = Scoring {
xclip_prefix: 0,
yclip_suffix: 0,
..base_score
};
let mut al = Aligner::with_scoring(scoring);
let alignment = al.custom(x, y);
assert_eq!(alignment.score, 0);
}
{
let scoring = Scoring {
xclip_suffix: 0,
yclip_prefix: 0,
..base_score
};
let mut al = Aligner::with_scoring(scoring);
let alignment = al.custom(x, y);
assert_eq!(alignment.score, 0);
}
{
let scoring = Scoring {
xclip_suffix: 0,
yclip_suffix: 0,
..base_score
};
let mut al = Aligner::with_scoring(scoring);
let alignment = al.custom(x, y);
assert_eq!(alignment.score, 0);
}
}
}