use crate::descriptor::Descriptor;
use crate::error::MordredError;
use crate::molecule::Molecule;
pub struct WienerIndex;
impl Descriptor for WienerIndex {
fn name(&self) -> &str {
"WienerIndex"
}
fn description(&self) -> &str {
"Wiener index (sum of shortest path distances)"
}
fn calculate(&self, mol: &Molecule) -> Result<f64, MordredError> {
if mol.atom_count() < 2 {
return Ok(0.0);
}
let dist = mol.distance_matrix();
let nodes: Vec<_> = mol.graph.node_indices().collect();
let mut sum = 0i64;
for i in 0..nodes.len() {
for j in (i + 1)..nodes.len() {
if let Some(&d) = dist.get(&(nodes[i], nodes[j])) {
sum += d;
}
}
}
Ok(sum as f64)
}
}
pub struct ZagrebIndex1;
impl Descriptor for ZagrebIndex1 {
fn name(&self) -> &str {
"Zagreb1"
}
fn description(&self) -> &str {
"First Zagreb index (sum of squared degrees)"
}
fn calculate(&self, mol: &Molecule) -> Result<f64, MordredError> {
let sum: usize = mol
.graph
.node_indices()
.map(|i| {
let d = mol.degree(i);
d * d
})
.sum();
Ok(sum as f64)
}
}
pub struct ZagrebIndex2;
impl Descriptor for ZagrebIndex2 {
fn name(&self) -> &str {
"Zagreb2"
}
fn description(&self) -> &str {
"Second Zagreb index (sum of degree products over edges)"
}
fn calculate(&self, mol: &Molecule) -> Result<f64, MordredError> {
let sum: usize = mol
.bonds()
.map(|(a, b, _)| mol.degree(a) * mol.degree(b))
.sum();
Ok(sum as f64)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::molecule::parse_smiles;
#[test]
fn test_wiener_ethane() {
let mol = parse_smiles("CC").unwrap();
assert_eq!(WienerIndex.calculate(&mol).unwrap(), 1.0);
}
#[test]
fn test_wiener_propane() {
let mol = parse_smiles("CCC").unwrap();
assert_eq!(WienerIndex.calculate(&mol).unwrap(), 4.0);
}
#[test]
fn test_wiener_butane() {
let mol = parse_smiles("CCCC").unwrap();
assert_eq!(WienerIndex.calculate(&mol).unwrap(), 10.0);
}
#[test]
fn test_zagreb1_ethane() {
let mol = parse_smiles("CC").unwrap();
assert_eq!(ZagrebIndex1.calculate(&mol).unwrap(), 2.0);
}
#[test]
fn test_zagreb1_propane() {
let mol = parse_smiles("CCC").unwrap();
assert_eq!(ZagrebIndex1.calculate(&mol).unwrap(), 6.0);
}
#[test]
fn test_zagreb2_propane() {
let mol = parse_smiles("CCC").unwrap();
assert_eq!(ZagrebIndex2.calculate(&mol).unwrap(), 4.0);
}
}