codenano 0.5.1

A library for editing DNA molecular designs
Documentation
//mod domain;
//mod commute;
//use domain::*;
use super::{Design, Domain, Helix, Strand};

#[derive(Clone, Debug)]

/// An atomic modification of an existing design. Patches have operation to permute them or undo
/// them.
pub enum Patch {
    /// ElongateDomain(h, b1, n, b2, n2) elongate the b2 end of domain whose b2 end is (h,
    /// b1, n1) by n2 nucleotides
    ElongateDomain(usize, bool, isize, bool, usize),

    /// ShortenDomain(h, b1, n, b2, n2) shorten the b2 end of domain whose b2 end is (h,
    /// b1, n1) by n2 nucleotides
    ShortenDomain(usize, bool, isize, bool, usize),

    /// AddHelix(h) add helix h
    AddHelix(Helix),

    /// DeleteHelix(n) delete helix with identifier n
    DeleteHelix(usize),

    /// Add Strand (h, b, n) add a strand on helix b at position n on the right strand if b on the
    /// left strand otherwise
    AddStrand(usize, bool, isize),

    /// DeleteStrand(h, b, n) delete the strand containing only the nucleotide (h, b, n)
    DeleteStrand(usize, bool, isize),

    /// CrossOver(s, b1, h, n, b2) the b1 (5' if true, 3' if false) end strand s jumps to helix h
    /// at position n on the right strand if b, on the left strand otherwise
    CrossOver(usize, bool, usize, isize, bool),

    /*
    /// Merge(s1, s2) merge the strands s1 and s2. The end of strand s1 must be adjacent to the
    /// begining of strand s2. The strand at index s2 is deleted, the strand at index s1 is
    /// modified: all the domains of s2 are appenend to the domains of s1. The index s1 is
    /// unchanged if s1 < s2, and decremented by 1 if s1 > s2.
    Merge(usize, usize),
    */

    /*
    /// Split(s, n) split the strand s at its n-th nucleotide
    Split(usize, usize),
    */
    /// MakeBound(h1, b1, n1, h2, b2, n2) Make a bound between the nucleotides (h1, b1, n1)
    /// and (h2, b2, n2)
    MakeBound(usize, bool, isize, usize, bool, isize),

    /// CutBound(h1, b1, n1, h2, b2, n2) Cut the bound between the nucleotides (h1, b1, n1)
    /// and (h2, b2, n2)
    CutBound(usize, bool, isize, usize, bool, isize),
}

impl Patch {
    /*
    pub fn in_domain<S, T>(&self, design: &Design<S, T>) -> bool {
        match self {
            Patch::InsertNucl(s, b, n) => domain_insert_nucl(design, *s, *b, *n),
            Patch::SuppNucl(s, b, n) => domain_suppr_nucl(design, *s, *b, *n),
            Patch::InsertHelix(_) => true, // TODO check intersection of helices
            Patch::DeleteHelix(h) => domain_suppr_helix(design, *h),
            Patch::AddStrand(h, b, n) => domain_add_strand(design, *h, *b, *n),
            Patch::DeleteStrand(s) => domain_delete_strand(design, *s),
            Patch::CrossOver(s, b1, h, n, b2) => domain_cross_over(design, *s, *h, *n, *b2),
            Patch::Merge(s1, s2) => domain_merge(design, *s1, *s2),
            Patch::Split(s, n) => domain_split(design, *s, *n),
        }
    }

    pub fn commute<S, T>(&self, patch: &Patch, design: &Design<S, T>,
                         context: &Design<S, T>) -> Option<(Patch, Patch)> {
        match self {
            Patch::InsertNucl(s, b, n) => commute::insert_nucl(patch, design, context, *s, *b, *n),
            Patch::SuppNucl(s, b, n) => commute::supp_nucl(patch, design, context, *s, *b, *n),
            Patch::InsertHelix(_) => Some((patch.clone(), self.clone())),
            Patch::DeleteHelix(n) => commute::delete_helix(patch, design, *n),
            _ => None
        }
    }
    */

    /// Apply self on a design
    pub fn apply<S: serde::Serialize, T: serde::Serialize>(&self, design: &mut Design<S, T>) {
        /*
        if !self.in_domain(design) {
            panic!("Patch domain error");
        }
        */
        match self {
            Patch::ElongateDomain(h, b1, n1, b2, n2) => {
                let s_id = design
                    .get_strand_nucl(*h as isize, *n1, *b1)
                    .expect("get nucl elgonate domain");
                let strand = &mut design.strands[s_id];
                let mut dom_num = 0;
                for i in 0..strand.domains.len() {
                    if strand.domains[i].contains(*h as isize, *n1, *b1) {
                        dom_num = i;
                        break;
                    }
                }
                let domain = &mut strand.domains[dom_num];
                if !domain.contains(*h as isize, *n1, *b1) {
                    panic!("domain does not contains");
                }
                if *b2 == domain.forward {
                    domain.end += *n2 as isize
                } else {
                    domain.start -= *n2 as isize
                };
            }
            Patch::ShortenDomain(h, b1, n1, b2, n2) => {
                let s_id = design
                    .get_strand_nucl(*h as isize, *n1, *b1)
                    .expect("get nucl shorten domain");
                let strand = &mut design.strands[s_id];
                let mut dom_num = 0;
                for i in 0..strand.domains.len() {
                    if strand.domains[i].contains(*h as isize, *n1, *b1) {
                        dom_num = i;
                        break;
                    }
                }
                let domain = &mut strand.domains[dom_num];
                if !domain.contains(*h as isize, *n1, *b1) {
                    panic!("domain does not contains");
                }
                if *b2 == domain.forward {
                    domain.end -= *n2 as isize
                } else {
                    domain.start += *n2 as isize
                };
            }
            Patch::AddHelix(h) => {
                design.add_helix(h.position.x, h.position.y, h.position.z, h.roll, h.pitch, h.yaw);
            }
            Patch::DeleteHelix(h) => {
                design.helices.remove(*h);
            }
            Patch::AddStrand(h, b, n) => {
                design.strands.push(Strand {
                    domains: vec![Domain {
                        helix: *h as isize,
                        start: *n,
                        end: *n + 1,
                        forward: *b,
                        label: None,
                        sequence: None,
                    }],
                    label: None,
                    sequence: None,
                    cyclic: false,
                    color: None,
                });
            }
            Patch::DeleteStrand(h, b, n) => {
                if let Some(s_id) = design.get_strand_nucl(*h as isize, *n, *b) {
                    design.strands.remove(s_id);
                }
            }
            Patch::CrossOver(s, b1, h, n, b2) => {
                let new_dom = Domain {
                    helix: *h as isize,
                    start: *n,
                    end: *n + 1,
                    forward: *b2,
                    label: None,
                    sequence: None,
                };
                if *b1 {
                    design.strands[*s].domains.push(new_dom);
                } else {
                    design.strands[*s].domains.insert(0, new_dom);
                }
            }
            Patch::MakeBound(h1, b1, n1, h2, b2, n2) => {
                //let strand1 = &design.strands[*s1];
                let s1 = design
                    .get_strand_nucl(*h1 as isize, *n1, *b1)
                    .expect("bounding unexisting nucl");
                let s2 = design
                    .get_strand_nucl(*h2 as isize, *n2, *b2)
                    .expect("bounding unexisting nucl");
                if s1 != s2 {
                    for i in 0..design.strands[s2].domains.len() {
                        let domain = design.strands[s2].domains[i].pseudo_copy();
                        design.strands[s1].domains.push(domain);
                    }
                    design.strands.remove(s2);
                } else {
                    let builder = design.get_strand_ref(s1);
                    builder.cycle();
                }
            }
            Patch::CutBound(h1, b1, n1, h2, b2, n2) => {
                let h1 = *h1 as isize;
                let s1 = design
                    .get_strand_nucl(h1, *n1, *b1)
                    .expect("cuting on unexisting nucl");
                let strand = &mut design.strands[s1];
                if !strand.cyclic {
                    let mut dom = 0;
                    let mut old_start = 0;
                    let mut old_end = 0;
                    let mut split_dom = true;
                    for i in 0..strand.domains.len() {
                        let domain = &mut strand.domains[i];
                        if domain.contains(h1, *n1, *b1) {
                            //shorten the strand
                            old_start = domain.start;
                            old_end = domain.end;
                            let contains = domain.contains(*h2 as isize, *n2, *b2);
                            if domain.forward {
                                domain.end = *n1 + 1;
                            } else {
                                domain.start = *n1;
                            }

                            // set the start of the new strand
                            if !contains {
                                old_start = strand.domains[i + 1].start;
                                old_end = strand.domains[i + 1].end;
                                dom = i + 1;
                                split_dom = false;
                            } else {
                                dom = i;
                            }
                            break;
                        }
                    }

                    let old_domain = &strand.domains[dom];
                    let new_domain = Domain {
                        start: {
                            if old_domain.forward {
                                *n2
                            } else {
                                old_start
                            }
                        },
                        end: {
                            if old_domain.forward {
                                old_end
                            } else {
                                *n2 + 1
                            }
                        },
                        label: None,
                        sequence: None,
                        ..*old_domain
                    };
                    let nb_dom = strand.domains.len();
                    let mut domains = vec![new_domain];
                    for i in (dom + 1)..nb_dom {
                        let domain = strand.domains[i].pseudo_copy();
                        domains.push(domain);
                    }
                    if !split_dom {
                        dom -= 1;
                    }
                    for _ in (dom + 1)..nb_dom {
                        strand.domains.pop();
                    }
                    design.strands.push(Strand {
                        domains,
                        label: None,
                        sequence: None,
                        cyclic: false,
                        color: None,
                    });
                } else {
                    let mut domains = Vec::new();
                    let mut added_dom = None;
                    for i in 0..strand.domains.len() {
                        if strand.domains[i].contains(*h2 as isize, *n2, *b2) {
                            let contains = strand.domains[i].contains(h1, *n1, *b1);
                            let old_start = strand.domains[i].start;
                            let old_end = strand.domains[i].end;
                            if strand.domains[i].forward {
                                strand.domains[i].start = *n2;
                            } else {
                                strand.domains[i].end = *n2 + 1;
                            }
                            if i == 0 {
                                let last_dom = strand.domains.last_mut().unwrap();
                                if last_dom.forward {
                                    last_dom.end = *n2;
                                } else {
                                    last_dom.start = *n2 + 1;
                                }
                            } else {
                                if contains {
                                    let dom = strand.domains[i].pseudo_copy();
                                    let start = if dom.forward { old_start } else { *n1 };
                                    let end = if dom.forward { *n1 + 1 } else { old_end };
                                    added_dom = Some(Domain { start, end, ..dom });
                                }
                                let last_dom = strand.domains.last_mut().unwrap();
                                if last_dom.length() == 1 {
                                    strand.domains.pop();
                                } else {
                                    if last_dom.forward {
                                        last_dom.end -= 1;
                                    } else {
                                        last_dom.start += 1;
                                    }
                                }
                                for j in i..strand.domains.len() + i {
                                    let dom =
                                        strand.domains[j % strand.domains.len()].pseudo_copy();
                                    domains.push(dom);
                                }
                                if let Some(ref dom) = added_dom {
                                    domains.push(dom.pseudo_copy());
                                }
                            }
                            break;
                        }
                    }
                    if domains.len() > 0 {
                        strand.domains = domains;
                    }
                    strand.cyclic = false;
                }
            }
        }
    }

    /// Return the inverse of `self`
    pub fn undo(&self) -> Self {
        match self {
            Patch::ElongateDomain(h, b1, n1, b2, n2) if *b1 == *b2 => {
                Patch::ShortenDomain(*h, *b1, *n1 + *n2 as isize, *b2, *n2)
            }
            Patch::ElongateDomain(h, b1, n1, b2, n2) => {
                Patch::ShortenDomain(*h, *b1, *n1 - *n2 as isize, *b2, *n2)
            }
            Patch::ShortenDomain(h, b1, n1, b2, n2) if *b1 == *b2 => {
                Patch::ElongateDomain(*h, *b1, *n1 - *n2 as isize, *b2, *n2)
            }
            Patch::ShortenDomain(h, b1, n1, b2, n2) => {
                Patch::ElongateDomain(*h, *b1, *n1 + *n2 as isize, *b2, *n2)
            }
            Patch::AddStrand(h, b, n) => Patch::DeleteStrand(*h, *b, *n),
            Patch::DeleteStrand(h, b, n) => Patch::AddStrand(*h, *b, *n),
            Patch::MakeBound(h1, b1, n1, h2, b2, n2) => {
                Patch::CutBound(*h1, *b1, *n1, *h2, *b2, *n2)
            }
            Patch::CutBound(h1, b1, n1, h2, b2, n2) => {
                Patch::MakeBound(*h1, *b1, *n1, *h2, *b2, *n2)
            }
            _ => unimplemented!(),
        }
    }
}

/*
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
}
*/