chemcore 0.4.1

A cheminformatics toolkit.
Documentation
use purr::{ graph };
use purr::parts::BondKind;

use crate::molecule::{ Bond };
use super::{ Error, trigonal_parity };

pub fn to_bond(
    sid: usize,
    bond: &graph::Bond,
    atoms: &[graph::Atom],
    trace: &[usize]
) -> Result<Bond, Error> {
    let (electrons, parity) = match &bond.kind {
        BondKind::Elided |
        BondKind::Aromatic |
        BondKind::Single => (2, None),
        BondKind::Up |
        BondKind::Down => if has_double(sid, bond, atoms) {
            (2, None)
        } else {
            return Err(Error::BondKind(trace[bond.tid]))
        },
        BondKind::Double => {
            let left_parity = match trigonal_parity(&atoms[sid].bonds) {
                Ok(parity) => parity,
                Err(()) => return Err(Error::BondKind(trace[sid]))
            };
            let right_parity = match trigonal_parity(&atoms[bond.tid].bonds) {
                Ok(parity) => parity,
                Err(()) => return Err(Error::BondKind(trace[bond.tid]))
            };

            if let Some(left) = left_parity {
                if let Some(right) = &right_parity {
                    let neg_right = right.negate();

                    (4, Some(left.multiply(&neg_right)))
                } else {
                    (4, None)
                }
            } else {
                (4, None)
            }
        },
        BondKind::Triple => (6, None),
        BondKind::Quadruple => (8, None)
    };

    Ok(Bond {
        electrons,
        parity,
        tid: bond.tid
    })
}

fn has_double(sid: usize, bond: &graph::Bond, atoms: &[graph::Atom]) -> bool {
    let source = &atoms[sid];

    if source.bonds.iter().any(|bond| bond.kind == BondKind::Double) {
        return true
    }

    let target = &atoms[bond.tid];

    target.bonds.iter().any(|bond| bond.kind == BondKind::Double)
}

#[cfg(test)]
mod tests {
    use pretty_assertions::assert_eq;
    use purr::read::{ Reading, read };
    use purr::graph::{ from_tree, Bond as PurrBond };
    use crate::molecule::Parity;
    use super::*;

    #[test]
    fn elided() {
        let Reading { root, trace } = read("CC").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Elided, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(2, None, 1)))
    }

    #[test]
    fn single() {
        let Reading { root, trace } = read("C-C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Single, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(2, None, 1)))
    }

    #[test]
    fn double() {
        let Reading { root, trace } = read("C=C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, None, 1)))
    }

    #[test]
    fn double_up() {
        let Reading { root, trace } = read("C=C/C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, None, 1)))
    }

    #[test]
    fn double_up_down() {
        let Reading { root, trace } = read("C=C(/C)\\C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, None, 1)))
    }

    #[test]
    fn double_up_up_elided() {
        let Reading { root, trace } = read("C=P(/C)(/C)C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, None, 1)))
    }

    #[test]
    fn up_double_up() {
        let Reading { root, trace } = read("C/C=C/C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 2);
        let bond = to_bond(1, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, Some(Parity::Negative), 2)))
    }

    #[test]
    fn up_double_down() {
        let Reading { root, trace } = read("C/C=C\\C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 2);
        let bond = to_bond(1, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, Some(Parity::Positive), 2)))
    }

    #[test]
    fn up_up_double_down() {
        let Reading { root, trace } = read("C/C(/C)=C\\C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 3);
        let bond = to_bond(1, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, Some(Parity::Negative), 3)))
    }

    #[test]
    fn up_up_double_up() {
        let Reading { root, trace } = read("C/C(/C)=C/C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 3);
        let bond = to_bond(1, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, Some(Parity::Positive), 3)))
    }

    #[test]
    fn up() {
        let Reading { root, trace } = read("C/C=C/C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, None, 1)))
    }

    #[test]
    fn down() {
        let Reading { root, trace } = read("C\\C=C/C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Double, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(4, None, 1)))
    }

    #[test]
    fn triple() {
        let Reading { root, trace } = read("C#C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Triple, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(6, None, 1)))
    }

    #[test]
    fn quadruple() {
        let Reading { root, trace } = read("C#C").unwrap();
        let atoms = from_tree(root).unwrap();
        let input = PurrBond::new(BondKind::Quadruple, 1);
        let bond = to_bond(0, &input, &atoms, &trace);

        assert_eq!(bond, Ok(Bond::new(8, None, 1)))
    }
}