use super::topo::{BondOrder, Topo};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Hyb {
S,
Sp,
Sp2,
Sp3,
Other,
}
fn nouter_elecs(atno: u8) -> Option<i32> {
Some(match atno {
1 => 1,
2 => 2,
3 | 11 | 19 => 1,
4 | 12 | 20 => 2,
5 | 13 => 3,
6 | 14 => 4,
7 | 15 => 5,
8 | 16 => 6,
9 | 17 | 35 | 53 => 7,
10 | 18 => 8,
_ => return None,
})
}
fn min_valence(atno: u8) -> i32 {
match atno {
1 => 1,
5 => 3,
6 | 14 => 4,
7 | 15 => 3,
8 | 16 => 2,
9 | 17 | 35 | 53 => 1,
3 | 11 | 19 => 1,
4 | 12 | 20 => 2,
_ => -1,
}
}
fn default_valence(atno: u8) -> i32 {
match atno {
1 => 1,
5 => 3,
6 | 14 => 4,
7 | 15 => 3,
8 | 16 => 2,
9 | 17 | 35 | 53 => 1,
3 | 11 | 19 => 1,
4 | 12 | 20 => 2,
_ => -1,
}
}
fn count_atom_elec(topo: &Topo, i: usize) -> i32 {
let atno = topo.atno[i];
let dv = default_valence(atno);
if dv <= 1 {
return -1;
}
let degree = topo.total_degree(i) as i32; if degree > 3 {
return -1;
}
let nouter = match nouter_elecs(atno) {
Some(v) => v,
None => return -1,
};
let mut nlp = nouter - dv;
nlp = (nlp - topo.formal_charge[i]).max(0);
let mut res = (dv - degree) + nlp; if res > 1 {
let n_unsat = topo.total_bond_order(i) as i32 - topo.degree(i) as i32;
if n_unsat > 1 {
res = 1;
}
}
res
}
fn is_atom_conjug_cand(topo: &Topo, i: usize) -> bool {
let atno = topo.atno[i];
let minv = min_valence(atno);
if topo.formal_charge[i] == 0 && minv >= 0 && (topo.total_bond_order(i) as i32) > minv {
return false;
}
let nouter = match nouter_elecs(atno) {
Some(v) => v,
None => return false,
};
((atno <= 10) || (nouter != 5 && nouter != 6) || (nouter == 6 && topo.total_degree(i) < 2))
&& count_atom_elec(topo, i) > 0
}
fn valence_contrib(o: BondOrder) -> f64 {
match o {
BondOrder::Single => 1.0,
BondOrder::Aromatic => 1.5,
BondOrder::Double => 2.0,
BondOrder::Triple => 3.0,
}
}
fn marks_bond_conjugated(topo: &Topo, at: usize, other: usize) -> bool {
if !is_atom_conjug_cand(topo, at) {
return false;
}
let sbo = topo.degree(at);
if !(2..=3).contains(&sbo) {
return false;
}
for (p1, &n1) in topo.nbrs[at].iter().enumerate() {
if valence_contrib(topo.nbr_order[at][p1]) < 1.5 || !is_atom_conjug_cand(topo, n1) {
continue;
}
for (p2, &n2) in topo.nbrs[at].iter().enumerate() {
if p1 == p2 || topo.degree(n2) > 3 || !is_atom_conjug_cand(topo, n2) {
continue;
}
if n1 == other || n2 == other {
return true;
}
}
}
false
}
fn atom_has_conjugated_bond(topo: &Topo, i: usize) -> bool {
for (p, &j) in topo.nbrs[i].iter().enumerate() {
if topo.nbr_order[i][p] == BondOrder::Aromatic {
return true;
}
if marks_bond_conjugated(topo, i, j) || marks_bond_conjugated(topo, j, i) {
return true;
}
}
false
}
fn num_bonds_plus_lone_pairs(topo: &Topo, i: usize) -> i32 {
let deg = topo.total_degree(i) as i32;
let atno = topo.atno[i];
if atno <= 1 {
return deg;
}
let nouter = match nouter_elecs(atno) {
Some(v) => v,
None => return deg,
};
let total_valence = topo.total_bond_order(i) as i32;
let chg = topo.formal_charge[i];
let num_free = nouter - (total_valence + chg);
let num_lone = num_free / 2;
deg + num_lone
}
pub fn hybridization(topo: &Topo, i: usize) -> Hyb {
if topo.atno[i] == 0 {
return Hyb::Other;
}
let norbs = if topo.atno[i] < 89 {
num_bonds_plus_lone_pairs(topo, i)
} else {
topo.total_degree(i) as i32
};
match norbs {
0 | 1 => Hyb::S,
2 => Hyb::Sp,
3 => Hyb::Sp2,
4 => {
if topo.total_degree(i) > 3 || !atom_has_conjugated_bond(topo, i) {
Hyb::Sp3
} else {
Hyb::Sp2
}
}
5 | 6 => Hyb::Other,
_ => Hyb::Other,
}
}