#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
match mat[i][j].direction {
Direction::Match => {
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<_> = "what".chars().collect();
let mut vec2: Vec<_> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
let mut vec1: Vec<_> = "what".chars().collect();
let mut vec2: Vec<_> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
// first col fnall test_align_mat()go {up
let mfor =i init_align_matrix(2,in 3);1..len1 {
let m_should_bemat[i][0] = vec![
vec![ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(i 0,as Direction::Undefinedi64)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_fill_mattest_align_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let mut matm = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity2, -13); use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_bem_should_be = vec![
vec![ResultEntry::new(0, udDirection::Undefined), ResultEntry::new(-1, ltDirection::Left), ResultEntry::new(-2, ltDirection::Left), ResultEntry::new(-3, ltDirection::Left)],
vec![ResultEntry::new(-1, upDirection::Up), ResultEntry::new(1 0, mtDirection::Undefined), ResultEntry::new(0, ltDirection::Undefined), ResultEntry::new(-1 0, ltDirection::Undefined)],
vec![ResultEntry::new(-2, upDirection::Up), ResultEntry::new(0, upDirection::Undefined), ResultEntry::new(0, mtDirection::Undefined), ResultEntry::new(1 0, mtDirection::Undefined)],
];
assert_eq!(matm, res_should_bem_should_be);
}
#[test]
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fntest_fill_mat(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, ic1: usize&char, jc2: usize&char) -> i64 {
if i == 0 || jc1 == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefinedc2 {
mat[i][j].score1
} else {
-1
}
}
let up_leftmut vec1: Vec<_> = get_score_set_direction"ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, similarity&similarity, gap_penalty-1);
use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, iud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, jup), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
let up assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
#[cfg(feature = "verbose")]
{
print_mat(mat);
println!("Node: {:?}", (i, j));
println!("up_left, up, left: {:?}", (up_left, up, left));
println!("Match, up, left: {:?}", (match_score, up_score, left_score));
println!("Max score: {}", m);
}
if m == match_score {
#[cfg(feature = "verbose")]
{
print_mat(mat);
println!("Node: {:?}", (i, j));
println!("up_left, up, left: {:?}", (up_left, up, left));
"Go Up and printlnLeft!("Match, up, left: {:?}", (match_score, up_score, left_score)); println!("Max score: {}", m);
}
if m == match_score {
#[cfg(feature = "verbose")]
{
println!("Go Up and Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
#[cfg(feature = "verbose")]
{
println!("Go Up!");
}
mat[i][j] = ResultEntry::new(m, Direction::Up);
} else {
#[cfg(feature = "verbose")]
{
println!("Go Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
mat[i][j] = ResultEntry::new(m, Direction::Up);
} else {
} #[cfg(feature = "verbose")]
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len println!("Go Left!"); + 1) {
get_score_set_direction(mat,}
vec1, vec2, similarity, gap_penalty, i mat[i][j] = ResultEntry::new(m, jDirection::Left);
}
m
}
}
#[cfgfor i in 1..(featurevec1.len() =+ "verbose"1)] {
for j in 1..(vec2.len() + 1) {
print_matget_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
#[cfg(feature = "verbose")]
{
print_mat(mat);
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.',
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
match mat[i][j].direction {
Direction::Match => {
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<_> = "what".chars().collect();
let mut vec2: Vec<_> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mat() {
let m = init_align_matrix(2, 3);
let m_should_be = vec![
vec![ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
#[test]
fn test_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1); use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
#[cfg(feature = "verbose")]
{
print_mat(mat);
println!("Node: {:?}", (i, j));
println!("up_left, up, left: {:?}", (up_left, up, left));
println!("Match, up, left: {:?}", (match_score, up_score, left_score)); println!("Max score: {}", m);
}
if m == match_score {
#[cfg(feature = "verbose")]
{
println!("Go Up and Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
#[cfg(feature = "verbose")]
{
println!("Go Up!");
}
mat[i][j] = ResultEntry::new(m, Direction::Up);
} else {
#[cfg(feature = "verbose")]
{
println!("Go Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len() + 1) {
get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
#[cfg(feature = "verbose")]
{
print_mat(mat);
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.',
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
match mat[i][j].direction {
Direction::Match => {
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<_> = "what".chars().collect();
let mut vec2: Vec<_> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mat() {
let m = init_align_matrix(2, 3);
let m_should_be = vec![
vec![ResultEntry::new(0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new(0, Direction::Undefined), ResultEntry::new(0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
#[test]
fn test_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1);
use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
#[cfg(feature = "verbose")]
{
print_mat(mat);
println!("Node: {:?}", (i, j));
println!("up_left, up, left: {:?}", (up_left, up, left));
println!("Match, up, left: {:?}", (match_score, up_score, left_score));
println!("Max score: {}", m);
}
if m == match_score {
#[cfg(feature = "verbose")]
{
println!("Go Up and Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
#[cfg(feature = "verbose")]
{
println!("Go Up!");
}
mat[i][j] = ResultEntry::new(m, Direction::Up);
} else {
#[cfg(feature = "verbose")]
{
println!("Go Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len() + 1) {
get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
#[cfg(feature = "verbose")]
{
print_mat(mat);
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.',
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
loop print_mat(mat);{
// println!("Node: {:?}, Node direction: {:?}", (i, j),match mat[i][j].direction);
{
match mat[i][j].direction {
Direction::Match => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
// println!("Tracing {:?}", (i, j));
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<_> = "what".chars().collect();
let mut vec2: Vec<_> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
let mut vec1: Vec<char> = "what".chars().collect();
let mut vec2: Vec<char> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
//#[test]
first col all gofn uptest_align_mat() {
for i in 1..len1 {let m = init_align_matrix(2, 3);
mat[i][0]let m_should_be = vec![
vec![ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(i-3, asDirection::Left)],
i64 vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mattest_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mmut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let mut mat = init_align_matrix(2vec1.len(), 3vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1); use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let m_should_beres_should_be = vec![
vec![ResultEntry::new(0, Direction::Undefinedud), ResultEntry::new(-1, Direction::Leftlt), ResultEntry::new(-2, Direction::Leftlt), ResultEntry::new(-3, Direction::Leftlt)],
vec![ResultEntry::new(-1, Direction::Upup), ResultEntry::new( 01, Direction::Undefinedmt), ResultEntry::new(0, Direction::Undefinedlt), ResultEntry::new( 0-1, Direction::Undefinedlt)],
vec![ResultEntry::new(-2, Direction::Upup), ResultEntry::new(0, Direction::Undefinedup), ResultEntry::new(0, Direction::Undefinedmt), ResultEntry::new( 01, Direction::Undefinedmt)],
];
assert_eq!(mmat, m_should_beres_should_be);
}
#[test]
fn test_fill_matfill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(c1&U, &U) -> i64, gap_penalty: &chari64, c2i: &charusize, j: usize) -> i64 {
if c1i == c20 || j == 0 {
1mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i -1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
#[cfg(feature = "verbose")]
{
print_mat(mat);
println!("Node: {:?}", (i, j));
println!("up_left, up, left: {:?}", (up_left, up, left));
println!("Match, up, left: {:?}", (match_score, up_score, left_score));
println!("Max score: {}", m);
}
if m == match_score {
#[cfg(feature = "verbose")]
{
println!("Go Up and Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
#[cfg(feature = "verbose")]
{
println!("Go Up!");
}
mat[i][j] = ResultEntry::new(m, Direction::Up);
} else {
#[cfg(feature = "verbose")]
{
println!("Go Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char>for i =in "aeb"1.chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1);
use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
+ vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],{
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up),for ResultEntry::new(0,j mt),in ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
// print_mat(mat);
// println!("Node: {:?}", (i, j));
// println!("up_left, up, left: {:?}", (up_left, up, left));
// println!("Match, up, left: {:?}", (match_score, up_score, left_score));
// println!("Max score: {}", m);
if m == match_score {
// println!("Go Up and Left!");
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
mat[i][j] = ResultEntry::new(m, Direction::Up);
// println!("Go Up!");
} else {
// println!("Go Left!");
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len#[cfg()feature += 1"verbose")] {
for j in 1..(vec2.len() + 1) {
get_score_set_directionprint_mat(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.', }
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
print_mat(mat);
// println!("Node: {:?}, Node direction: {:?}", (i, j), mat[i][j].direction);
match mat[i][j].direction {
Direction::Match => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
// println!("Tracing {:?}", (i, j));
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<char> = "what".chars().collect();
let mut vec2: Vec<char> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mat() {
let m = init_align_matrix(2, 3);
let m_should_be = vec![
vec![ResultEntry::new(0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new(0, Direction::Undefined), ResultEntry::new(0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
#[test]
fn test_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1);
use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
// print_mat(mat);
// println!("Node: {:?}", (i, j));
// println!("up_left, up, left: {:?}", (up_left, up, left));
// println!("Match, up, left: {:?}", (match_score, up_score, left_score));
// println!("Max score: {}", m);
if m == match_score {
// println!("Go Up and Left!");
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
mat[i][j] = ResultEntry::new(m, Direction::Up);
// println!("Go Up!");
} else {
// println!("Go Left!");
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len() + 1) {
get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.',
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
match mat[i][j].direction {
Direction::Match => {
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<_> = "what".chars().collect();
let mut vec2: Vec<_> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mat() {
let m = init_align_matrix(2, 3);
let m_should_be = vec![
vec![ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
#[test]
fn test_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<_> = "ab".chars().collect();
let mut vec2: Vec<_> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1); use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i -1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
#[cfg(feature = "verbose")]
{
print_mat(mat);
println!("Node: {:?}", (i, j));
println!("up_left, up, left: {:?}", (up_left, up, left));
println!("Match, up, left: {:?}", (match_score, up_score, left_score));
println!("Max score: {}", m);
}
if m == match_score {
#[cfg(feature = "verbose")]
{
println!("Go Up and Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
#[cfg(feature = "verbose")]
{
println!("Go Up!");
}
mat[i][j] = ResultEntry::new(m, Direction::Up);
} else {
#[cfg(feature = "verbose")]
{
println!("Go Left!");
}
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len() + 1) {
get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
#[cfg(feature = "verbose")]
{
print_mat(mat);
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.', }
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
print_mat(mat);
// println!("Node: {:?}, Node direction: {:?}", (i, j), mat[i][j].direction);
match mat[i][j].direction {
Direction::Match => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
// println!("Tracing {:?}", (i, j));
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<char> = "what".chars().collect();
let mut vec2: Vec<char> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mat() {
let m = init_align_matrix(2, 3);
let m_should_be = vec![
vec![ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
#[test]
fn test_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1);
use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
// print_mat(mat);
// println!("Node: {:?}", (i, j));
// println!("up_left, up, left: {:?}", (up_left, up, left));
// println!("Match, up, left: {:?}", (match_score, up_score, left_score));
// println!("Max score: {}", m);
if m == match_score {
// println!("Go Up and Left!");
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
mat[i][j] = ResultEntry::new(m, Direction::Up);
// println!("Go Up!");
} else {
// println!("Go Left!");
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len() + 1) {
get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.',
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
print_mat(mat);
// println!("Node: {:?}, Node direction: {:?}", (i, j), mat[i][j].direction);
match mat[i][j].direction {
Direction::Match => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
// println!("Tracing {:?}", (i, j));
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<char> = "what".chars().collect();
let mut vec2: Vec<char> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mat() {
let m = init_align_matrix(2, 3);
let m_should_be = vec![
vec![ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
#[test]
fn test_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1);
use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
// print_mat(mat);
// println!("Node: {:?}", (i, j));
// println!("up_left, up, left: {:?}", (up_left, up, left));
// println!("Match, up, left: {:?}", (match_score, up_score, left_score));
// println!("Max score: {}", m);
if m == match_score {
// println!("Go Up and Left!");
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
mat[i][j] = ResultEntry::new(m, Direction::Up);
// println!("Go Up!");
} else {
// println!("Go Left!");
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len() + 1) {
get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.',
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Direction {
Match, // go diagonally, left and up
Left,
Up,
Undefined,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ResultEntry {
pub score: i64,
pub direction: Direction,
}
impl ResultEntry {
fn new(s: i64, d: Direction) -> ResultEntry {
ResultEntry {
score: s,
direction: d,
}
}
}
pub trait NeedlemanWunsch<T> {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>);
}
impl<T> NeedlemanWunsch<T> for Vec<T> where T: Eq + Copy + Clone {
fn align(&mut self, vec: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) -> (Vec<Option<T>>, Vec<Option<T>>) {
let mut mat = init_align_matrix(self.len() , vec.len());
fill_align_matrix(&mut mat, self, vec, similarity, gap_penalty);
let mat = &mat; // no need to mutate mat any more.
let mut vec1: Vec<Option<T>> = Vec::new();
let mut vec2: Vec<Option<T>> = Vec::new();
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let mut i = self.len();
let mut j = vec.len();
loop {
print_mat(mat);
// println!("Node: {:?}, Node direction: {:?}", (i, j), mat[i][j].direction);
match mat[i][j].direction {
Direction::Match => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
// println!("Tracing {:?}", (i, j));
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, Some(vec[j-1]));
i -= 1;
j -= 1;
},
Direction::Left => {
vec1.insert(0, None);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec2.insert(0, Some(vec[j-1]));
j -= 1;
},
Direction::Up => {
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
vec1.insert(0, Some(self[i-1]));
vec2.insert(0, None);
i -= 1;
},
Direction::Undefined => {
break;
}
}
}
return (vec1, vec2);
}
}
#[test]
fn test_align() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 { 1 } else { -1 }
}
let mut vec1: Vec<char> = "what".chars().collect();
let mut vec2: Vec<char> = "white".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('w'), Some('h'), Some('a'), Some('t'), None],
vec![Some('w'), Some('h'), Some('i'), Some('t'), Some('e')],
);
assert_eq!(res, res_should_be);
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let res = vec1.align(&mut vec2, &similarity, -1);
let res_should_be = (
vec![Some('a'), None, Some('b')],
vec![Some('a'), Some('e'), Some('b')],
);
assert_eq!(res, res_should_be);
}
type AlignMatrix = Vec<Vec<ResultEntry>>;
/// len1: Length of the first vector, or number of rows for the alignment matrix.
/// len2: Length of the second vector, or number of cols for the alignment matrix.
fn init_align_matrix(len1: usize, len2: usize) -> AlignMatrix {
let len1 = len1 + 1;
let len2 = len2 + 1;
// starting point has undefined direction
let mut mat: AlignMatrix = vec![vec![ResultEntry::new(0, Direction::Undefined); len2]; len1];
// first row all go left
for i in 1..len2 {
mat[0][i] = ResultEntry::new(-(i as i64), Direction::Left);
}
// first col all go up
for i in 1..len1 {
mat[i][0] = ResultEntry::new(-(i as i64), Direction::Up);
}
mat[0][0] = ResultEntry::new(0, Direction::Undefined);
mat
}
#[test]
fn test_align_mat() {
let m = init_align_matrix(2, 3);
let m_should_be = vec![
vec![ResultEntry::new( 0, Direction::Undefined), ResultEntry::new(-1, Direction::Left), ResultEntry::new(-2, Direction::Left), ResultEntry::new(-3, Direction::Left)],
vec![ResultEntry::new(-1, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
vec![ResultEntry::new(-2, Direction::Up), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined), ResultEntry::new( 0, Direction::Undefined)],
];
assert_eq!(m, m_should_be);
}
#[test]
fn test_fill_mat() {
fn similarity(c1: &char, c2: &char) -> i64 {
if c1 == c2 {
1
} else {
-1
}
}
let mut vec1: Vec<char> = "ab".chars().collect();
let mut vec2: Vec<char> = "aeb".chars().collect();
let mut mat = init_align_matrix(vec1.len(), vec2.len());
fill_align_matrix(&mut mat, &mut vec1, &mut vec2, &similarity, -1);
use self::Direction::Undefined as ud;
use self::Direction::Left as lt;
use self::Direction::Up as up;
use self::Direction::Match as mt;
let res_should_be = vec![
vec![ResultEntry::new(0, ud), ResultEntry::new(-1, lt), ResultEntry::new(-2, lt), ResultEntry::new(-3, lt)],
vec![ResultEntry::new(-1, up), ResultEntry::new(1, mt), ResultEntry::new(0, lt), ResultEntry::new(-1, lt)],
vec![ResultEntry::new(-2, up), ResultEntry::new(0, up), ResultEntry::new(0, mt), ResultEntry::new(1, mt)],
];
assert_eq!(mat, res_should_be);
}
fn fill_align_matrix<T>(mat: &mut AlignMatrix, vec1: &mut Vec<T>, vec2: &mut Vec<T>, similarity: &Fn(&T, &T) -> i64, gap_penalty: i64) {
assert_eq!(mat.len(), vec1.len() + 1);
assert_eq!(mat[0].len(), vec2.len() + 1);
fn get_score_set_direction<U>(mat: &mut AlignMatrix, vec1: &Vec<U>, vec2: &Vec<U>, similarity: &Fn(&U, &U) -> i64, gap_penalty: i64, i: usize, j: usize) -> i64 {
if i == 0 || j == 0 {
mat[i][j].score
} else if mat[i][j].direction != Direction::Undefined {
mat[i][j].score
} else {
let up_left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j - 1);
let up = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i - 1, j);
let left = get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j - 1);
// the dimension of the alignment matrix is one larger than vec1.len by vec2.len
let similarity_score = similarity(&vec1[i-1], &vec2[j-1]);
let match_score = up_left + similarity_score;
let up_score = up + gap_penalty;
let left_score = left + gap_penalty;
let m = *[match_score, up_score, left_score].iter().max().unwrap();
// print_mat(mat);
// println!("Node: {:?}", (i, j));
// println!("up_left, up, left: {:?}", (up_left, up, left));
// println!("Match, up, left: {:?}", (match_score, up_score, left_score));
// println!("Max score: {}", m);
if m == match_score {
// println!("Go Up and Left!");
mat[i][j] = ResultEntry::new(m, Direction::Match);
} else if m == up_score {
mat[i][j] = ResultEntry::new(m, Direction::Up);
// println!("Go Up!");
} else {
// println!("Go Left!");
mat[i][j] = ResultEntry::new(m, Direction::Left);
}
m
}
}
for i in 1..(vec1.len() + 1) {
for j in 1..(vec2.len() + 1) {
get_score_set_direction(mat, vec1, vec2, similarity, gap_penalty, i, j);
}
}
}
fn print_mat(mat: &AlignMatrix) {
use std::fmt::Write;
fn direction_to_arrow(d: Direction) -> char {
match d {
Direction::Match => '↖',
Direction::Up => '↑',
Direction::Left => '←',
Direction::Undefined => '.',
}
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", el.score).unwrap();
}
println!("{}", s);
}
for row in mat {
let mut s = String::new();
for el in row {
write!(&mut s, "{:>4}", direction_to_arrow(el.direction)).unwrap();
}
println!("{}", s);
}
}
lang-rust