use crate::bond::BondOrder;
use crate::descriptors::hybridization::{Hybridization, all_hybridizations};
use crate::molecule::Molecule;
pub fn is_conjugated_bond(mol: &Molecule, bond_idx: usize) -> bool {
let bond = match mol.bonds.get(bond_idx) {
Some(b) => b,
None => return false,
};
if bond.is_aromatic() {
return true;
}
let hybs = all_hybridizations(mol);
if bond.order == BondOrder::Single {
let hyb1 = hybs
.get(bond.atom1)
.copied()
.unwrap_or(Hybridization::Other);
let hyb2 = hybs
.get(bond.atom2)
.copied()
.unwrap_or(Hybridization::Other);
if hyb1 == Hybridization::SP2 && hyb2 == Hybridization::SP2 {
return true;
}
}
if bond.order == BondOrder::Double || bond.order == BondOrder::Triple {
let hybs = all_hybridizations(mol);
let hyb1 = hybs
.get(bond.atom1)
.copied()
.unwrap_or(Hybridization::Other);
let hyb2 = hybs
.get(bond.atom2)
.copied()
.unwrap_or(Hybridization::Other);
let is_sp2_or_sp = |h: Hybridization| h == Hybridization::SP2 || h == Hybridization::SP;
if is_sp2_or_sp(hyb1) && is_sp2_or_sp(hyb2) {
for (other_idx, other_bond) in mol.bonds.iter().enumerate() {
if other_idx == bond_idx {
continue;
}
let shares_atom =
other_bond.contains_atom(bond.atom1) || other_bond.contains_atom(bond.atom2);
if shares_atom
&& (other_bond.order == BondOrder::Double
|| other_bond.order == BondOrder::Triple
|| other_bond.is_aromatic()
|| {
if other_bond.order == BondOrder::Single {
let other_end = if other_bond.contains_atom(bond.atom1) {
other_bond.other_atom(bond.atom1).unwrap_or(usize::MAX)
} else {
other_bond.other_atom(bond.atom2).unwrap_or(usize::MAX)
};
let other_hyb =
hybs.get(other_end).copied().unwrap_or(Hybridization::Other);
is_sp2_or_sp(other_hyb)
} else {
false
}
})
{
return true;
}
}
}
}
false
}
pub fn all_conjugated_bonds(mol: &Molecule) -> Vec<bool> {
let m = mol.bond_count();
if m == 0 {
return vec![];
}
let hybs = all_hybridizations(mol);
let mut result = vec![false; m];
for (bond_idx, bond) in mol.bonds.iter().enumerate() {
if bond.is_aromatic() {
result[bond_idx] = true;
continue;
}
if bond.order == BondOrder::Single {
let hyb1 = hybs
.get(bond.atom1)
.copied()
.unwrap_or(Hybridization::Other);
let hyb2 = hybs
.get(bond.atom2)
.copied()
.unwrap_or(Hybridization::Other);
if hyb1 == Hybridization::SP2 && hyb2 == Hybridization::SP2 {
result[bond_idx] = true;
continue;
}
}
if bond.order == BondOrder::Double || bond.order == BondOrder::Triple {
let hyb1 = hybs
.get(bond.atom1)
.copied()
.unwrap_or(Hybridization::Other);
let hyb2 = hybs
.get(bond.atom2)
.copied()
.unwrap_or(Hybridization::Other);
let is_sp2_or_sp = |h: Hybridization| h == Hybridization::SP2 || h == Hybridization::SP;
if is_sp2_or_sp(hyb1) && is_sp2_or_sp(hyb2) {
let mut is_conj = false;
for (other_idx, other_bond) in mol.bonds.iter().enumerate() {
if other_idx == bond_idx {
continue;
}
let shares_atom = other_bond.contains_atom(bond.atom1)
|| other_bond.contains_atom(bond.atom2);
if shares_atom
&& (other_bond.order == BondOrder::Double
|| other_bond.order == BondOrder::Triple
|| other_bond.is_aromatic()
|| {
if other_bond.order == BondOrder::Single {
let other_end = if other_bond.contains_atom(bond.atom1) {
other_bond.other_atom(bond.atom1).unwrap_or(usize::MAX)
} else {
other_bond.other_atom(bond.atom2).unwrap_or(usize::MAX)
};
let other_hyb = hybs
.get(other_end)
.copied()
.unwrap_or(Hybridization::Other);
is_sp2_or_sp(other_hyb)
} else {
false
}
})
{
is_conj = true;
break;
}
}
if is_conj {
result[bond_idx] = true;
}
}
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::atom::Atom;
use crate::bond::{Bond, BondOrder};
#[test]
fn test_benzene_conjugated() {
let mut mol = Molecule::new("benzene");
for i in 0..6 {
mol.atoms.push(Atom::new(i, "C", 0.0, 0.0, 0.0));
}
for i in 0..6 {
mol.bonds
.push(Bond::new(i, (i + 1) % 6, BondOrder::Aromatic));
}
let conj = all_conjugated_bonds(&mol);
assert!(conj.iter().all(|&c| c));
}
#[test]
fn test_butadiene() {
let mut mol = Molecule::new("butadiene");
mol.atoms.push(Atom::new(0, "C", 0.0, 0.0, 0.0));
mol.atoms.push(Atom::new(1, "C", 1.3, 0.0, 0.0));
mol.atoms.push(Atom::new(2, "C", 2.5, 0.0, 0.0));
mol.atoms.push(Atom::new(3, "C", 3.8, 0.0, 0.0));
mol.bonds.push(Bond::new(0, 1, BondOrder::Double));
mol.bonds.push(Bond::new(1, 2, BondOrder::Single));
mol.bonds.push(Bond::new(2, 3, BondOrder::Double));
let conj = all_conjugated_bonds(&mol);
assert!(conj[0], "First C=C should be conjugated");
assert!(conj[1], "C-C between SP2 atoms should be conjugated");
assert!(conj[2], "Second C=C should be conjugated");
}
#[test]
fn test_ethane_not_conjugated() {
let mut mol = Molecule::new("ethane");
mol.atoms.push(Atom::new(0, "C", 0.0, 0.0, 0.0));
mol.atoms.push(Atom::new(1, "C", 1.5, 0.0, 0.0));
mol.bonds.push(Bond::new(0, 1, BondOrder::Single));
let conj = all_conjugated_bonds(&mol);
assert!(!conj[0]);
}
#[test]
fn test_isolated_double_bond() {
let mut mol = Molecule::new("ethylene");
mol.atoms.push(Atom::new(0, "C", 0.0, 0.0, 0.0));
mol.atoms.push(Atom::new(1, "C", 1.3, 0.0, 0.0));
mol.bonds.push(Bond::new(0, 1, BondOrder::Double));
let conj = all_conjugated_bonds(&mol);
assert!(!conj[0], "Isolated C=C should not be conjugated");
}
#[test]
fn test_empty() {
let mol = Molecule::new("empty");
let conj = all_conjugated_bonds(&mol);
assert!(conj.is_empty());
}
#[test]
fn test_is_conjugated_bond() {
let mut mol = Molecule::new("benzene");
for i in 0..6 {
mol.atoms.push(Atom::new(i, "C", 0.0, 0.0, 0.0));
}
for i in 0..6 {
mol.bonds
.push(Bond::new(i, (i + 1) % 6, BondOrder::Aromatic));
}
assert!(is_conjugated_bond(&mol, 0));
}
}