use crate::{
Nucleotide, Seq, SeqTopology,
restriction_enzyme::{ReMatch, RestrictionEnzyme},
};
pub struct LigationFragment {
pub source_name: String,
pub seq: Seq,
pub re_left: Option<RestrictionEnzyme>,
pub re_right: Option<RestrictionEnzyme>,
}
pub fn digest(
source_name: &str,
selected: &[RestrictionEnzyme],
matches: &[ReMatch],
re_lib: &[RestrictionEnzyme],
seq: &[Nucleotide],
topology: SeqTopology,
) -> Vec<LigationFragment> {
let mut result = Vec::new();
let mut cuts = Vec::new();
for re_match in matches {
if re_match.lib_index >= re_lib.len() {
eprintln!("Invalid RE selected.");
continue;
}
let re = &re_lib[re_match.lib_index];
if !selected.contains(re) {
continue;
}
cuts.push((re_match.seq_index, re.clone()));
}
if cuts.is_empty() {
return result;
}
let mut cut = &cuts[0];
let mut cuts_i = 0;
let mut current_fragment = Vec::new();
for (seq_i, nt) in seq.iter().enumerate().skip(cut.0) {
if seq_i == cut.0 {
if !current_fragment.is_empty() {
result.push(LigationFragment {
source_name: source_name.to_owned(),
seq: current_fragment.clone(),
re_left: Some(cuts[cuts_i - 1].1.clone()),
re_right: Some(cut.1.clone()),
});
}
current_fragment = Vec::new();
if cuts_i + 1 < cuts.len() {
cuts_i += 1;
}
cut = &cuts[cuts_i];
}
current_fragment.push(*nt);
}
match topology {
SeqTopology::Circular => {
for (seq_i, nt) in seq.iter().enumerate().take(cuts[0].0) {
current_fragment.push(*nt);
if seq_i == cuts[0].0 {
break;
}
}
result.push(LigationFragment {
source_name: source_name.to_owned(),
seq: current_fragment,
re_left: Some(cuts[cuts.len() - 1].1.clone()),
re_right: Some(cuts[0].1.clone()),
});
}
SeqTopology::Linear => {
result.push(LigationFragment {
source_name: source_name.to_owned(),
seq: seq[..cuts[0].0].to_vec(),
re_left: None,
re_right: Some(cuts[0].1.clone()),
});
result.push(LigationFragment {
source_name: source_name.to_owned(),
seq: current_fragment,
re_left: Some(cuts[cuts.len() - 1].1.clone()),
re_right: None,
});
}
}
result
}
pub fn ligate(fragments: &[LigationFragment]) -> Vec<Seq> {
let mut result = Vec::new();
for frag in fragments {
if let Some(re_l) = &frag.re_left {
let nt_overhang_a: Vec<_> = re_l
.overhang_top_left(&frag.seq[0..1]) .iter()
.map(|nt| nt.complement())
.collect();
for frag_2 in fragments {
if let Some(re_2_l) = &frag_2.re_left
&& re_2_l.overhang_top_left(&frag.seq[0..1]) == nt_overhang_a
{
let mut ligated = frag.seq.clone();
ligated.extend(frag_2.seq.clone());
result.push(ligated);
}
}
}
}
result
}
pub fn find_common_res<'a>(
re_match_set: &[&Vec<ReMatch>], lib: &'a [RestrictionEnzyme],
sticky_ends_only: bool,
) -> Vec<&'a RestrictionEnzyme> {
let mut result = Vec::new();
for re_matches in re_match_set {
for re_match in *re_matches {
if re_match.lib_index >= lib.len() {
eprintln!("Invalid restriction enzyme");
continue;
}
let re = &lib[re_match.lib_index];
if sticky_ends_only && re.makes_blunt_ends() {
continue;
}
if !result.contains(&re) {
result.push(re);
}
}
}
result
}
pub fn filter_multiple_seqs<'a>(
res: &'a mut Vec<&RestrictionEnzyme>,
re_match_set: &[&Vec<ReMatch>], lib: &'a [RestrictionEnzyme],
) {
if re_match_set.len() < 2 {
return;
}
res.retain(|&re| {
let mut count = 0;
for re_matches in re_match_set {
for re_match in *re_matches {
let re_this = &lib[re_match.lib_index];
if re_this == re {
count += 1;
break;
}
}
}
count >= 2
});
}
pub fn filter_unique_cutters<'a>(
res: &'a mut Vec<&RestrictionEnzyme>,
re_match_set: &[&Vec<ReMatch>], lib: &'a [RestrictionEnzyme],
) {
res.retain(|&re| {
for re_matches in re_match_set {
let mut count = 0; for re_match in *re_matches {
let re_this = &lib[re_match.lib_index];
if re_this == re {
count += 1;
}
}
if count > 1 {
return false;
}
}
true
});
}