use crate::domain::hfbi::{AnagraficaHFBI, CampionamentoHFBI, GruppoEcoHFBI};
use crate::domain::posf32::PositiveF32;
pub fn calc_bbent(
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 biobent = 0.0;
for specie in campione {
match specie.specie.gruppo_eco {
GruppoEcoHFBI::Diadromi
| GruppoEcoHFBI::MigratoriMarini
| GruppoEcoHFBI::ResidentiDiEstuario => {
biobent += specie.peso * (specie.specie.gruppo_trofico.microbentivori)
+ specie.peso * (specie.specie.gruppo_trofico.macrobentivori)
}
_ => {}
}
}
let epsilon: f32 = 1e-6;
if biobent.abs() < epsilon {
return Ok(0.0);
}
let bbent = ((biobent / area) * 100.0 + 1.0).ln();
Ok((1000.0 * bbent).round() / 1000.0)
}
#[cfg(test)]
mod bbent_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: "TestRegione".to_string(),
provincia: "TestProvincia".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_calc_bbent_empty_campionamento() {
let anagrafica = create_test_anagrafica(100.0, 5.0);
let campione = CampionamentoHFBI::new(vec![]);
let result = calc_bbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
let expected = 0.0;
assert!(
(result - expected).abs() < EPSILON,
"Failed empty campionamento test. Expected: {}, Got: {}",
expected,
result
);
}
#[test]
fn test_calc_bbent_with_irrelevant_species() {
let anagrafica = create_test_anagrafica(100.0, 5.0);
let campione = CampionamentoHFBI::new(vec![create_specie_record(
GruppoEcoHFBI::OccasionaliMarini,
0.5,
0.5,
100.0,
)]);
let result = calc_bbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
let expected = 0.0;
assert!(
(result - expected).abs() < EPSILON,
"Failed irrelevant species test. Expected: {}, Got: {}",
expected,
result
);
}
#[test]
fn test_calc_bbent_with_single_relevant_specie() {
let anagrafica = create_test_anagrafica(100.0, 5.0);
let campione = CampionamentoHFBI::new(vec![create_specie_record(
GruppoEcoHFBI::ResidentiDiEstuario,
0.4,
0.6,
200.0,
)]);
let result = calc_bbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
let expected = (1000.0 * 41.0_f32.ln()).round() / 1000.0;
assert!(
(result - expected).abs() < EPSILON,
"Failed single relevant specie test. Expected: {}, Got: {}",
expected,
result
);
}
#[test]
fn test_calc_bbent_with_mixed_gruppoeco() {
let anagrafica = create_test_anagrafica(80.0, 5.0);
let campione = CampionamentoHFBI::new(vec![
create_specie_record(GruppoEcoHFBI::ResidentiDiEstuario, 0.5, 0.5, 150.0),
create_specie_record(GruppoEcoHFBI::MigratoriMarini, 0.2, 0.3, 100.0),
create_specie_record(GruppoEcoHFBI::OccasionaliDiAcqueDolci, 1.0, 0.0, 500.0),
create_specie_record(GruppoEcoHFBI::Diadromi, 0.8, 0.2, 200.0),
]);
let result = calc_bbent(&campione, &anagrafica)
.expect("Area fields of anagrafica should be positive and finite");
let expected = (1000.0 * 101.0_f32.ln()).round() / 1000.0;
assert!(
(result - expected).abs() < EPSILON,
"Failed mixed species test. Expected: {}, Got: {}",
expected,
result
);
}
#[test]
fn test_calc_bbent_division_by_zero_area() {
let anagrafica = create_test_anagrafica(100.0, 0.0);
let campione = CampionamentoHFBI::new(vec![create_specie_record(
GruppoEcoHFBI::ResidentiDiEstuario,
1.0,
0.0,
100.0,
)]);
let result = calc_bbent(&campione, &anagrafica);
assert!(result.is_err());
}
#[test]
fn test_calc_bbent_division_by_zero_area_and_zero_biobent() {
let anagrafica = create_test_anagrafica(0.0, 10.0);
let campione = CampionamentoHFBI::new(vec![]);
let result = calc_bbent(&campione, &anagrafica);
assert!(result.is_err());
}
}