use std::ffi::CString;
use std::os::raw::{c_char, c_double, c_int};
use std::sync::Mutex;
use mafft_align::{msalignmm, GapModel, Profile};
use mafft_scoring::build_context;
use mafft_types::{ScoringModel, SeqType};
static C_MUTEX: Mutex<()> = Mutex::new(());
unsafe fn init_c_protein_blosum62() {
unsafe {
mafft_sys::initglobalvariables();
std::ptr::addr_of_mut!(mafft_sys::ppenalty).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::ppenalty_ex).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::ppenalty_EX).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::ppenalty_OP).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::ppenalty_dist).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::poffset).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::kimuraR).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::pamN).write(mafft_sys::NOTSPECIFIED);
std::ptr::addr_of_mut!(mafft_sys::dorp).write(b'p' as i32);
std::ptr::addr_of_mut!(mafft_sys::scoremtx).write(1);
std::ptr::addr_of_mut!(mafft_sys::nblosum).write(62);
std::ptr::addr_of_mut!(mafft_sys::fmodel).write(0);
std::ptr::addr_of_mut!(mafft_sys::outgap).write(1);
let seq_data = b"ACDEFGHIKLMNPQRSTVWY\0";
let mut seq_ptr = seq_data.as_ptr() as *mut i8;
let seq_arr: *mut *mut i8 = &mut seq_ptr;
mafft_sys::constants(1, seq_arr);
}
}
fn align_via_both(
s1: &[u8],
s2: &[u8],
head_gap: bool,
tail_gap: bool,
) -> (Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>) {
let scoring = build_context(ScoringModel::Blosum(62), SeqType::Protein);
let amino_map = &scoring.amino_map;
let nalpha = scoring.nalphabets;
let prof1 = Profile::from_aligned(&[s1], &[1.0], amino_map, nalpha);
let prof2 = Profile::from_aligned(&[s2], &[1.0], amino_map, nalpha);
let gap = GapModel::new(scoring.gap.open as f64, scoring.gap.extend as f64);
let rust_aln = msalignmm(&prof1, &prof2, &scoring.consweight_matrix, &gap, head_gap, tail_gap);
let mut rust_s1 = Vec::with_capacity(rust_aln.operations.len());
let mut rust_s2 = Vec::with_capacity(rust_aln.operations.len());
let (mut p, mut q) = (0usize, 0usize);
for op in &rust_aln.operations {
match op {
mafft_align::AlignOp::Match => {
rust_s1.push(s1[p]); p += 1;
rust_s2.push(s2[q]); q += 1;
}
mafft_align::AlignOp::Delete => {
rust_s1.push(s1[p]); p += 1;
rust_s2.push(b'-');
}
mafft_align::AlignOp::Insert => {
rust_s1.push(b'-');
rust_s2.push(s2[q]); q += 1;
}
}
}
let _guard = C_MUTEX.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let (c_s1, c_s2) = unsafe {
init_c_protein_blosum62();
let alloclen = (s1.len() + s2.len() + 1000) as c_int;
let c_seq1 = CString::new(s1).unwrap();
let c_seq2 = CString::new(s2).unwrap();
let mut buf1: Vec<u8> = c_seq1.as_bytes().to_vec();
buf1.resize(alloclen as usize + 1, 0);
let mut buf2: Vec<u8> = c_seq2.as_bytes().to_vec();
buf2.resize(alloclen as usize + 1, 0);
let mut p1 = buf1.as_mut_ptr() as *mut c_char;
let mut p2 = buf2.as_mut_ptr() as *mut c_char;
let mut eff1: c_double = 1.0;
let mut eff2: c_double = 1.0;
let nalpha_c = scoring.substitution_matrix.len() as c_int;
let n_dyn = mafft_sys::AllocateDoubleMtx(nalpha_c, nalpha_c);
for i in 0..scoring.substitution_matrix.len() {
for j in 0..scoring.substitution_matrix[i].len() {
*(*n_dyn.add(i)).add(j) = scoring.substitution_matrix[i][j] as f64;
}
}
let _c_score = mafft_sys::MSalignmm(
n_dyn,
&mut p1, &mut p2,
&mut eff1, &mut eff2,
1, 1,
alloclen,
std::ptr::null_mut(), std::ptr::null_mut(),
std::ptr::null_mut(), std::ptr::null_mut(),
std::ptr::null_mut(), 0, std::ptr::null_mut(),
head_gap as c_int, tail_gap as c_int,
std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut(),
1.0, 1.0,
);
let c_width = {
let mut k = 0; while *p1.add(k) != 0 { k += 1; } k
};
let c_s1: Vec<u8> = (0..c_width).map(|k| *p1.add(k) as u8).collect();
let c_s2: Vec<u8> = (0..c_width).map(|k| *p2.add(k) as u8).collect();
mafft_sys::freeconstants();
(c_s1, c_s2)
};
(rust_s1, rust_s2, c_s1, c_s2)
}
#[test]
fn msalign_recursive_matches_c() {
let s1 = b"MKTIIALSYIFCLVFAKEDFREEKSPELLVNVPILTPVAGTHKAGKLITGSTMKAKEGNCGRDLLINGTGRLILSSSGKLPHRMNAIPRTNKPGSEDYTKVVNFLSGNLDRGQLSYLKLELKM";
let s2 = b"MKTIIALSYIFCLVFAKEDFREEKSPELLVNVPILTPVAGTHKAGKLITGSTMKAKEGNCGRDPQLLLAGKSDESQRWSAALLINGTGRLILSSSGKLPHRMNAIPRTNKPGSEDYTKVVNFLSGNLDRGQLSYLKLELKM";
assert!(s1.len() > 100, "test input must exercise Hirschberg recursion");
assert!(s2.len() > 100);
let (rust_s1, rust_s2, c_s1, c_s2) = align_via_both(s1, s2, true, true);
let rust_str1 = String::from_utf8_lossy(&rust_s1);
let rust_str2 = String::from_utf8_lossy(&rust_s2);
let c_str1 = String::from_utf8_lossy(&c_s1);
let c_str2 = String::from_utf8_lossy(&c_s2);
eprintln!("Rust width: {}", rust_s1.len());
eprintln!("C width: {}", c_s1.len());
eprintln!("Rust seq1: {}", rust_str1);
eprintln!("C seq1: {}", c_str1);
eprintln!("Rust seq2: {}", rust_str2);
eprintln!("C seq2: {}", c_str2);
assert_eq!(rust_s1.len(), c_s1.len(),
"alignment width mismatch: rust={} c={}", rust_s1.len(), c_s1.len());
assert_eq!(rust_s1, c_s1, "seq1 aligned output differs");
assert_eq!(rust_s2, c_s2, "seq2 aligned output differs");
}
#[test]
fn msalign_base_case_matches_c() {
let s1 = b"MKTIIALSYIFCLVFAKEDFREEK";
let s2 = b"MKTIIALSYIFCLVFAKEDFREEK";
assert!(s1.len() < 100);
let (rust_s1, rust_s2, c_s1, c_s2) = align_via_both(s1, s2, true, true);
assert_eq!(rust_s1, c_s1, "base-case seq1 differs");
assert_eq!(rust_s2, c_s2, "base-case seq2 differs");
}
#[test]
fn msalign_identical_long_matches_c() {
let s = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSIEGFFATLGGEVALWSLVVLAIERYIVIC";
assert!(s.len() > 100);
let (rust_s1, rust_s2, c_s1, c_s2) = align_via_both(s, s, true, true);
assert_eq!(rust_s1, c_s1, "identical-long seq1 differs");
assert_eq!(rust_s2, c_s2, "identical-long seq2 differs");
}
#[test]
fn msalign_freetail_matches_c() {
let s1 = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSIEGFFATLGGEVALWSLV";
let s2 = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSIEGFFAT";
let (rust_s1, rust_s2, c_s1, c_s2) = align_via_both(s1, s2, false, false);
assert_eq!(rust_s1, c_s1, "freetail seq1 differs");
assert_eq!(rust_s2, c_s2, "freetail seq2 differs");
}
#[test]
fn msalign_mid_state_matches_c() {
let s1 = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let s2 = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNGGRTLSEVMKWPFSDQIANLPTQRDLELFQKLMSARTVTNLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let lgth1 = s1.len();
let lgth2 = s2.len();
let scoring = build_context(ScoringModel::Blosum(62), SeqType::Protein);
let _guard = C_MUTEX.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let (c_imid, c_jmid, c_jumpi, c_jumpj, c_midw, c_midm, c_midn,
c_jumpbacki, c_jumpbackj, c_jumpforwi, c_jumpforwj) = unsafe {
init_c_protein_blosum62();
let alloclen = (lgth1 + lgth2 + 1000) as c_int;
let c_seq1 = CString::new(&s1[..]).unwrap();
let c_seq2 = CString::new(&s2[..]).unwrap();
let mut buf1: Vec<u8> = c_seq1.as_bytes().to_vec();
buf1.resize(alloclen as usize + 1, 0);
let mut buf2: Vec<u8> = c_seq2.as_bytes().to_vec();
buf2.resize(alloclen as usize + 1, 0);
let p1 = buf1.as_mut_ptr() as *mut c_char;
let p2 = buf2.as_mut_ptr() as *mut c_char;
let nalpha_c = scoring.substitution_matrix.len() as c_int;
let n_dyn = mafft_sys::AllocateDoubleMtx(nalpha_c, nalpha_c);
for i in 0..scoring.substitution_matrix.len() {
for j in 0..scoring.substitution_matrix[i].len() {
*(*n_dyn.add(i)).add(j) = scoring.substitution_matrix[i][j] as f64;
}
}
let out_size = lgth2 + 2;
let mut out_imid: c_int = 0;
let mut out_jmid: c_int = 0;
let mut out_jumpi: c_int = 0;
let mut out_jumpj: c_int = 0;
let mut out_midw = vec![0.0f64; out_size];
let mut out_midm = vec![0.0f64; out_size];
let mut out_midn = vec![0.0f64; out_size];
let mut out_jumpbacki = vec![0 as c_int; out_size];
let mut out_jumpbackj = vec![0 as c_int; out_size];
let mut out_jumpforwi = vec![0 as c_int; out_size];
let mut out_jumpforwj = vec![0 as c_int; out_size];
mafft_sys::rs_msalignmm_capture_top(
n_dyn, p1, p2,
lgth1 as c_int, lgth2 as c_int,
1, 1,
&mut out_imid, &mut out_jmid, &mut out_jumpi, &mut out_jumpj,
out_midw.as_mut_ptr(),
out_midm.as_mut_ptr(),
out_midn.as_mut_ptr(),
out_jumpbacki.as_mut_ptr(),
out_jumpbackj.as_mut_ptr(),
out_jumpforwi.as_mut_ptr(),
out_jumpforwj.as_mut_ptr(),
);
mafft_sys::freeconstants();
(out_imid as usize, out_jmid as usize, out_jumpi as usize, out_jumpj as usize,
out_midw, out_midm, out_midn,
out_jumpbacki, out_jumpbackj, out_jumpforwi, out_jumpforwj)
};
eprintln!("C: imid={} jmid={} jumpi={} jumpj={}",
c_imid, c_jmid, c_jumpi, c_jumpj);
eprintln!("C: midw[95]={:.2} midw[99]={:.2} midw[100]={:.2}",
c_midw[95], c_midw[99], c_midw[100]);
eprintln!("C: midm[95]={:.2} midm[99]={:.2} midm[100]={:.2}",
c_midm[95], c_midm[99], c_midm[100]);
eprintln!("C: midn[94]={:.2} midn[98]={:.2} midn[99]={:.2}",
c_midn[94], c_midn[98], c_midn[99]);
let mut c_max_midw = (0, f64::NEG_INFINITY);
for j in 1..lgth2 {
if c_midw[j] > c_max_midw.1 { c_max_midw = (j, c_midw[j]); }
}
eprintln!("C: argmax(midw) = {} (val {:.2})", c_max_midw.0, c_max_midw.1);
eprintln!("C: midn[95]={:.2} midw[96]={:.2}", c_midn[95], c_midw[96]);
eprintln!("C: jumpbacki[96]={} jumpbackj[96]={}", c_jumpbacki[96], c_jumpbackj[96]);
eprintln!("C: jumpforwi[95]={} jumpforwj[95]={}", c_jumpforwi[95], c_jumpforwj[95]);
let _ = c_jumpbacki;
let _ = c_jumpbackj;
let _ = c_jumpforwi;
let _ = c_jumpforwj;
let prof1 = Profile::from_aligned(&[s1.as_slice()], &[1.0], &scoring.amino_map, scoring.nalphabets);
let prof2 = Profile::from_aligned(&[s2.as_slice()], &[1.0], &scoring.amino_map, scoring.nalphabets);
let gap = GapModel::new(scoring.gap.open as f64, scoring.gap.extend as f64);
let aln = msalignmm(&prof1, &prof2, &scoring.consweight_matrix, &gap, true, true);
eprintln!("Rust msalignmm width: {}", aln.operations.len());
}
#[test]
fn msalign_subregion_top_matches_c() {
let s1_full = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let s2_full = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNGGRTLSEVMKWPFSDQIANLPTQRDLELFQKLMSARTVTNLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let s1_top: &[u8] = &s1_full[0..56];
let s2_top: &[u8] = &s2_full[0..96];
assert_eq!(s1_top.len(), 56);
assert_eq!(s2_top.len(), 96);
let (rust_s1, rust_s2, c_s1, c_s2) = align_via_both(s1_top, s2_top, true, true);
eprintln!("Rust top width: {}", rust_s1.len());
eprintln!("C top width: {}", c_s1.len());
eprintln!("Rust s1: {}", String::from_utf8_lossy(&rust_s1));
eprintln!("C s1: {}", String::from_utf8_lossy(&c_s1));
eprintln!("Rust s2: {}", String::from_utf8_lossy(&rust_s2));
eprintln!("C s2: {}", String::from_utf8_lossy(&c_s2));
let s1_bot: &[u8] = &s1_full[57..=111];
let s2_bot: &[u8] = &s2_full[96..=150];
assert_eq!(s1_bot.len(), 55);
assert_eq!(s2_bot.len(), 55);
let (rs1, rs2, cs1, cs2) = align_via_both(s1_bot, s2_bot, true, true);
eprintln!("Bottom — Rust width: {}, C width: {}", rs1.len(), cs1.len());
eprintln!("Bottom Rust s1: {}", String::from_utf8_lossy(&rs1));
eprintln!("Bottom C s1: {}", String::from_utf8_lossy(&cs1));
eprintln!("Bottom Rust s2: {}", String::from_utf8_lossy(&rs2));
eprintln!("Bottom C s2: {}", String::from_utf8_lossy(&cs2));
}
#[test]
fn msalign_tanni_in_context_matches_c() {
let s1_full = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let s2_full = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNGGRTLSEVMKWPFSDQIANLPTQRDLELFQKLMSARTVTNLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let lgth1 = s1_full.len();
let lgth2 = s2_full.len();
let ist = 0;
let ien = 55;
let jst = 0;
let jen = 95;
let scoring = build_context(ScoringModel::Blosum(62), SeqType::Protein);
let _guard = C_MUTEX.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let (c_s1, c_s2, c_width) = unsafe {
init_c_protein_blosum62();
let alloclen = (lgth1 + lgth2 + 1000) as c_int;
let c_seq1 = CString::new(&s1_full[..]).unwrap();
let c_seq2 = CString::new(&s2_full[..]).unwrap();
let mut buf1: Vec<u8> = c_seq1.as_bytes().to_vec();
buf1.resize(alloclen as usize + 1, 0);
let mut buf2: Vec<u8> = c_seq2.as_bytes().to_vec();
buf2.resize(alloclen as usize + 1, 0);
let p1 = buf1.as_mut_ptr() as *mut c_char;
let p2 = buf2.as_mut_ptr() as *mut c_char;
let nalpha_c = scoring.substitution_matrix.len() as c_int;
let n_dyn = mafft_sys::AllocateDoubleMtx(nalpha_c, nalpha_c);
for i in 0..scoring.substitution_matrix.len() {
for j in 0..scoring.substitution_matrix[i].len() {
*(*n_dyn.add(i)).add(j) = scoring.substitution_matrix[i][j] as f64;
}
}
let out_size = ien - ist + jen - jst + 100 + 10;
let mut out_s1 = vec![0u8; out_size];
let mut out_s2 = vec![0u8; out_size];
let mut out_width: c_int = 0;
mafft_sys::rs_msalignmm_tanni_capture(
n_dyn, p1, p2,
lgth1 as c_int, lgth2 as c_int,
ist as c_int, ien as c_int, jst as c_int, jen as c_int,
1, 1,
out_s1.as_mut_ptr() as *mut c_char,
out_s2.as_mut_ptr() as *mut c_char,
&mut out_width,
);
mafft_sys::freeconstants();
let w = out_width as usize;
let s1: Vec<u8> = out_s1[..w].to_vec();
let s2: Vec<u8> = out_s2[..w].to_vec();
(s1, s2, w)
};
let prof1_full = Profile::from_aligned(&[s1_full.as_slice()], &[1.0], &scoring.amino_map, scoring.nalphabets);
let prof2_full = Profile::from_aligned(&[s2_full.as_slice()], &[1.0], &scoring.amino_map, scoring.nalphabets);
let sub1 = prof1_full.sub_profile(ist, ien + 1);
let sub2 = prof2_full.sub_profile(jst, jen + 1);
let effective_head = true || ist != 0 || jst != 0;
let effective_tail = true || ien + 1 != prof1_full.length || jen + 1 != prof2_full.length;
let head1 = if ist > 0 { prof1_full.nongap_freq[ist - 1] } else { 1.0 };
let head2 = if jst > 0 { prof2_full.nongap_freq[jst - 1] } else { 1.0 };
let tail1 = if ien + 1 < prof1_full.length { prof1_full.nongap_freq[ien + 1] } else { 1.0 };
let tail2 = if jen + 1 < prof2_full.length { prof2_full.nongap_freq[jen + 1] } else { 1.0 };
let gap = GapModel::new(scoring.gap.open as f64, scoring.gap.extend as f64);
let rust_aln = mafft_align::profile_align_imp_with_boundary(
&sub1, &sub2, &scoring.consweight_matrix, &gap,
effective_head, effective_tail, None, false,
mafft_align::BoundaryFreqs { head1, head2, tail1, tail2 },
);
let mut rust_s1 = Vec::new();
let mut rust_s2 = Vec::new();
let (mut p, mut q) = (0usize, 0usize);
let sub_s1 = &s1_full[ist..=ien];
let sub_s2 = &s2_full[jst..=jen];
for op in &rust_aln.operations {
match op {
mafft_align::AlignOp::Match => {
rust_s1.push(sub_s1[p]); p += 1;
rust_s2.push(sub_s2[q]); q += 1;
}
mafft_align::AlignOp::Delete => {
rust_s1.push(sub_s1[p]); p += 1;
rust_s2.push(b'-');
}
mafft_align::AlignOp::Insert => {
rust_s1.push(b'-');
rust_s2.push(sub_s2[q]); q += 1;
}
}
}
eprintln!("C in-context tanni: width={} ({} ops)", c_width, c_s1.len());
eprintln!("C s1: {}", String::from_utf8_lossy(&c_s1));
eprintln!("C s2: {}", String::from_utf8_lossy(&c_s2));
eprintln!("Rust full DP : width={}", rust_s1.len());
eprintln!("Rust s1: {}", String::from_utf8_lossy(&rust_s1));
eprintln!("Rust s2: {}", String::from_utf8_lossy(&rust_s2));
assert_eq!(rust_s1.len(), c_s1.len(),
"in-context width differs: rust={} c={}", rust_s1.len(), c_s1.len());
assert_eq!(rust_s1, c_s1, "in-context seq1 differs");
assert_eq!(rust_s2, c_s2, "in-context seq2 differs");
}
#[test]
fn msalign_full_trace_c_reimpl_symmetric_matches() {
let s1 = b"MKTIIALSYIFCLVFAKEDFREEKSPELLVNVPILTPVAGTHKAGKLITGSTMKAKEGNCGRDLLINGTGRLILSSSGKLPHRMNAIPRTNKPGSEDYTKVVNFLSGNLDRGQLSYLKLELKM";
let s2 = b"MKTIIALSYIFCLVFAKEDFREEKSPELLVNVPILTPVAGTHKAGKLITGSTMKAKEGNCGRDPQLLLAGKSDESQRWSAALLINGTGRLILSSSGKLPHRMNAIPRTNKPGSEDYTKVVNFLSGNLDRGQLSYLKLELKM";
let scoring = build_context(ScoringModel::Blosum(62), SeqType::Protein);
let _guard = C_MUTEX.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let (trace_w, real_w) = unsafe {
init_c_protein_blosum62();
let alloclen = (s1.len() + s2.len() + 1000) as c_int;
let cs1 = CString::new(&s1[..]).unwrap();
let cs2 = CString::new(&s2[..]).unwrap();
let nalpha_c = scoring.substitution_matrix.len() as c_int;
let n_dyn = mafft_sys::AllocateDoubleMtx(nalpha_c, nalpha_c);
for i in 0..scoring.substitution_matrix.len() {
for j in 0..scoring.substitution_matrix[i].len() {
*(*n_dyn.add(i)).add(j) = scoring.substitution_matrix[i][j] as f64;
}
}
let out_size = s1.len() + s2.len() + 200;
let mut t1 = vec![0u8; out_size];
let mut t2 = vec![0u8; out_size];
let mut tw: c_int = 0;
let mut b1: Vec<u8> = cs1.as_bytes().to_vec(); b1.resize(alloclen as usize + 1, 0);
let mut b2: Vec<u8> = cs2.as_bytes().to_vec(); b2.resize(alloclen as usize + 1, 0);
mafft_sys::rs_msalignmm_full_trace(
n_dyn, b1.as_mut_ptr() as *mut c_char, b2.as_mut_ptr() as *mut c_char,
s1.len() as c_int, s2.len() as c_int, 1, 1,
t1.as_mut_ptr() as *mut c_char, t2.as_mut_ptr() as *mut c_char, &mut tw);
let mut rb1: Vec<u8> = cs1.as_bytes().to_vec(); rb1.resize(alloclen as usize + 1, 0);
let mut rb2: Vec<u8> = cs2.as_bytes().to_vec(); rb2.resize(alloclen as usize + 1, 0);
let mut rp1 = rb1.as_mut_ptr() as *mut c_char;
let mut rp2 = rb2.as_mut_ptr() as *mut c_char;
let mut e1: c_double = 1.0; let mut e2: c_double = 1.0;
mafft_sys::MSalignmm(n_dyn, &mut rp1, &mut rp2, &mut e1, &mut e2, 1, 1, alloclen,
std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut(),
std::ptr::null_mut(), 0, std::ptr::null_mut(), 1, 1,
std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut(), 1.0, 1.0);
let rw = { let mut k = 0; while *rp1.add(k) != 0 { k += 1; } k };
mafft_sys::freeconstants();
(tw as usize, rw)
};
eprintln!("symmetric: faithful={} real={}", trace_w, real_w);
assert_eq!(trace_w, real_w, "faithful re-impl differs from real C on symmetric");
}
#[test]
fn msalign_full_trace_c_reimpl_matches_real_c() {
let s1_full = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let s2_full = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNGGRTLSEVMKWPFSDQIANLPTQRDLELFQKLMSARTVTNLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let lgth1 = s1_full.len();
let lgth2 = s2_full.len();
let scoring = build_context(ScoringModel::Blosum(62), SeqType::Protein);
let _guard = C_MUTEX.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
let (trace_s1, trace_s2, trace_width, real_s1, real_s2, real_width) = unsafe {
init_c_protein_blosum62();
let alloclen = (lgth1 + lgth2 + 1000) as c_int;
let c_seq1 = CString::new(&s1_full[..]).unwrap();
let c_seq2 = CString::new(&s2_full[..]).unwrap();
let nalpha_c = scoring.substitution_matrix.len() as c_int;
let n_dyn = mafft_sys::AllocateDoubleMtx(nalpha_c, nalpha_c);
for i in 0..scoring.substitution_matrix.len() {
for j in 0..scoring.substitution_matrix[i].len() {
*(*n_dyn.add(i)).add(j) = scoring.substitution_matrix[i][j] as f64;
}
}
let out_size = lgth1 + lgth2 + 200;
let mut trace_out_s1 = vec![0u8; out_size];
let mut trace_out_s2 = vec![0u8; out_size];
let mut trace_out_width: c_int = 0;
let mut buf1: Vec<u8> = c_seq1.as_bytes().to_vec();
buf1.resize(alloclen as usize + 1, 0);
let mut buf2: Vec<u8> = c_seq2.as_bytes().to_vec();
buf2.resize(alloclen as usize + 1, 0);
mafft_sys::rs_msalignmm_full_trace(
n_dyn,
buf1.as_mut_ptr() as *mut c_char,
buf2.as_mut_ptr() as *mut c_char,
lgth1 as c_int, lgth2 as c_int,
1, 1,
trace_out_s1.as_mut_ptr() as *mut c_char,
trace_out_s2.as_mut_ptr() as *mut c_char,
&mut trace_out_width,
);
let tw = trace_out_width as usize;
let ts1 = trace_out_s1[..tw].to_vec();
let ts2 = trace_out_s2[..tw].to_vec();
let mut real_buf1: Vec<u8> = c_seq1.as_bytes().to_vec();
real_buf1.resize(alloclen as usize + 1, 0);
let mut real_buf2: Vec<u8> = c_seq2.as_bytes().to_vec();
real_buf2.resize(alloclen as usize + 1, 0);
let mut rp1 = real_buf1.as_mut_ptr() as *mut c_char;
let mut rp2 = real_buf2.as_mut_ptr() as *mut c_char;
let mut eff1: c_double = 1.0;
let mut eff2: c_double = 1.0;
let _ = mafft_sys::MSalignmm(
n_dyn, &mut rp1, &mut rp2, &mut eff1, &mut eff2,
1, 1, alloclen,
std::ptr::null_mut(), std::ptr::null_mut(),
std::ptr::null_mut(), std::ptr::null_mut(),
std::ptr::null_mut(), 0, std::ptr::null_mut(),
1, 1,
std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut(),
1.0, 1.0,
);
let rw = {
let mut k = 0; while *rp1.add(k) != 0 { k += 1; } k
};
let rs1: Vec<u8> = (0..rw).map(|k| *rp1.add(k) as u8).collect();
let rs2: Vec<u8> = (0..rw).map(|k| *rp2.add(k) as u8).collect();
mafft_sys::freeconstants();
(ts1, ts2, tw, rs1, rs2, rw)
};
eprintln!("FAITHFUL C re-impl width: {}", trace_width);
eprintln!("REAL C MSalignmm width: {}", real_width);
eprintln!("Faithful s1: {}", String::from_utf8_lossy(&trace_s1));
eprintln!("Real s1: {}", String::from_utf8_lossy(&real_s1));
eprintln!("Faithful s2: {}", String::from_utf8_lossy(&trace_s2));
eprintln!("Real s2: {}", String::from_utf8_lossy(&real_s2));
assert_eq!(trace_width, real_width,
"faithful C re-impl width ({}) differs from real C MSalignmm ({})",
trace_width, real_width);
}
#[test]
fn msalign_asymmetric_lengths_matches_c() {
let s1 = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNFLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let s2 = b"MNGTEGDNFYVPFSNKTGLARSPYEYPQYYLAEPWKYSALAAYMFFLILVGFPVNGGRTLSEVMKWPFSDQIANLPTQRDLELFQKLMSARTVTNLTLFVTVQHKKLRTPLNYILLNLAMANLFMVLFGFTVTMYTSMNGYFVFGPTMCSI";
let (rust_s1, rust_s2, c_s1, c_s2) = align_via_both(s1, s2, true, true);
eprintln!("Rust s1: {}", String::from_utf8_lossy(&rust_s1));
eprintln!("C s1: {}", String::from_utf8_lossy(&c_s1));
eprintln!("Rust s2: {}", String::from_utf8_lossy(&rust_s2));
eprintln!("C s2: {}", String::from_utf8_lossy(&c_s2));
assert_eq!(rust_s1.len(), c_s1.len(),
"width differs: rust={} c={}", rust_s1.len(), c_s1.len());
assert_eq!(rust_s1, c_s1, "asymmetric seq1 differs");
assert_eq!(rust_s2, c_s2, "asymmetric seq2 differs");
}