use crate::domain::hfbi::{AnagraficaHFBI, CampionamentoHFBI, GruppoEcoHFBI};
use crate::domain::posf32::PositiveF32;
pub fn calc_dbent(
campione: &CampionamentoHFBI,
anagrafica: &AnagraficaHFBI,
) -> Result<f32, String> {
let width = anagrafica.get_larghezza_media();
let length = anagrafica.get_lunghezza_media();
let width_checked = PositiveF32::new(width).map_err(|e| e.to_string())?;
let length_checked = PositiveF32::new(length).map_err(|e| e.to_string())?;
let area: f32 = *width_checked * *length_checked;
let mut sbent = 0.0;
let mut densita_biomassa;
let mut specie_sbent;
let mut bbent = 0.0;
for specie in campione {
match specie.specie.gruppo_eco {
GruppoEcoHFBI::Diadromi
| GruppoEcoHFBI::MigratoriMarini
| GruppoEcoHFBI::ResidentiDiEstuario => {
specie_sbent = specie.specie.gruppo_trofico.microbentivori;
specie_sbent += specie.specie.gruppo_trofico.macrobentivori;
densita_biomassa = (specie.peso / area) * 100.0;
bbent += densita_biomassa * specie_sbent;
sbent += specie_sbent;
}
_ => {}
}
}
let epsilon: f32 = 1e-6;
if sbent.abs() < epsilon {
return Ok(0.0);
}
if (sbent - 0.2).abs() < epsilon {
return Ok(0.01);
}
let dbent = (((sbent - 1.0) / bbent.ln()) + 1.0).ln();
Ok((1000.0 * dbent).round() / 1000.0)
}
#[cfg(test)]
mod dbent_private_tests {
use super::*;
use crate::domain::hfbi::{
AnagraficaHFBI, CampionamentoHFBI, GruppoEcoHFBI, GruppoTrofHFBI, HabitatHFBI, RecordHFBI,
SpecieHFBI, StagioneHFBI, TipoLagunaCostieraHFBI,
};
use crate::domain::location::Location;
const EPSILON: f32 = 1e-6;
fn create_test_anagrafica(lunghezza: f32, larghezza: f32) -> AnagraficaHFBI {
AnagraficaHFBI::new_raw_unchecked(
"TestStazione".to_string(),
"TestCorpoIdrico".to_string(),
Location {
regione: "Test".to_string(),
provincia: "Test".to_string(),
},
"01/01/2025".to_string(),
TipoLagunaCostieraHFBI::MAt1,
StagioneHFBI::Primavera,
HabitatHFBI::NonVegetato,
lunghezza,
larghezza,
)
}
fn create_specie_record(
gruppo_eco: GruppoEcoHFBI,
microb: f32,
macrob: f32,
peso: f32,
) -> RecordHFBI {
RecordHFBI {
specie: SpecieHFBI {
nome_comune: "Test Specie".to_string(),
codice_specie: "TS".to_string(),
autoctono: true,
gruppo_eco,
gruppo_trofico: GruppoTrofHFBI {
microbentivori: microb,
macrobentivori: macrob,
iperbentivori: 0.0,
erbivori: 0.0,
detritivori: 0.0,
planctivori: 0.0,
onnivori: 0.0,
},
},
numero_individui: 1,
peso,
}
}
#[test]
fn test_dbent_empty_input() {
let anagrafica = create_test_anagrafica(100.0, 5.0);
let campione = CampionamentoHFBI::new(vec![]);
let result = calc_dbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
assert!(
(result - 0.0).abs() < EPSILON,
"Expected 0.0 for empty input, got {}",
result
);
}
#[test]
fn test_dbent_sbent_is_near_zero() {
let anagrafica = create_test_anagrafica(100.0, 5.0);
let campione = CampionamentoHFBI::new(vec![create_specie_record(
GruppoEcoHFBI::ResidentiDiEstuario,
0.0,
0.0,
100.0,
)]);
let result = calc_dbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
assert!(
(result - 0.0).abs() < EPSILON,
"Expected 0.0 for sbent near zero, got {}",
result
);
}
#[test]
fn test_dbent_sbent_is_near_point_two() {
let anagrafica = create_test_anagrafica(100.0, 5.0);
let campione = CampionamentoHFBI::new(vec![
create_specie_record(GruppoEcoHFBI::Diadromi, 0.15, 0.05, 100.0),
create_specie_record(GruppoEcoHFBI::MigratoriMarini, 0.0, 0.0, 50.0),
]);
let result = calc_dbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
assert!(
(result - 0.01).abs() < EPSILON,
"Expected 0.01 for sbent near 0.2, got {}",
result
);
}
#[test]
fn test_dbent_standard_calculation() {
let anagrafica = create_test_anagrafica(100.0, 5.0);
let campione = CampionamentoHFBI::new(vec![
create_specie_record(GruppoEcoHFBI::MigratoriMarini, 0.8, 0.7, 200.0),
create_specie_record(GruppoEcoHFBI::ResidentiDiEstuario, 0.5, 0.0, 150.0),
create_specie_record(GruppoEcoHFBI::OccasionaliMarini, 1.0, 1.0, 50.0),
]);
let sbent = 2.0;
let bbent = 75.0_f32.ln();
let expected = (1000.0 * (((sbent - 1.0) / bbent) + 1.0).ln()).round() / 1000.0;
let result = calc_dbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
assert!(
(result - expected).abs() < EPSILON,
"Standard calculation failed. Expected: {}, Got: {}",
expected,
result
);
}
}