This commit is contained in:
chaos 2024-11-23 14:09:40 +00:00
parent 313507f60e
commit a2b24cd586
13 changed files with 247 additions and 546 deletions

View file

@ -1,4 +1,4 @@
use super::{from_unix_millis, AdministrationRoute, Dose, Estimation, StandardDeviation}; use super::{from_unix_millis, AdministrationRoute, Dose};
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
#[derive(Default, PartialEq, Debug, Clone)] #[derive(Default, PartialEq, Debug, Clone)]
@ -27,31 +27,17 @@ impl From<psychonaut_journal_types::CustomUnit> for CustomUnit {
original_unit: custom_unit.original_unit, original_unit: custom_unit.original_unit,
dose: { dose: {
if let Some(dose) = custom_unit.dose { if custom_unit.estimate_standard_deviation.is_some() {
Dose { Dose::new_deviation(
value: dose, custom_unit.dose.unwrap(),
contains_unknown: false, custom_unit.estimate_standard_deviation.unwrap(),
estimation: { )
if custom_unit.is_estimate { } else if custom_unit.is_estimate && custom_unit.dose.is_some() {
if let Some(deviation) = custom_unit.estimate_standard_deviation { Dose::new_estimate(custom_unit.dose.unwrap())
Estimation::StandardDeviation(StandardDeviation { } else if custom_unit.dose.is_some() {
expectation: custom_unit.dose.unwrap_or_default(), Dose::new_precise(custom_unit.dose.unwrap())
deviation,
})
} else { } else {
Estimation::Estimate Dose::new_unknown()
}
} else {
Estimation::Precise
}
},
}
} else {
Dose {
value: 0.0,
contains_unknown: true,
estimation: Estimation::Estimate,
}
} }
}, },
@ -65,9 +51,7 @@ impl From<psychonaut_journal_types::CustomUnit> for CustomUnit {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::{ use crate::types::{from_unix_millis, AdministrationRoute, Dose};
from_unix_millis, AdministrationRoute, Dose, Estimation, StandardDeviation,
};
use super::CustomUnit; use super::CustomUnit;
use psychonaut_journal_types::CustomUnit as PsychonautCustomUnit; use psychonaut_journal_types::CustomUnit as PsychonautCustomUnit;
@ -94,14 +78,7 @@ mod tests {
substance_name: "Caffeine".to_string(), substance_name: "Caffeine".to_string(),
unit: "sip".to_string(), unit: "sip".to_string(),
original_unit: "mg".to_string(), original_unit: "mg".to_string(),
dose: Dose { dose: Dose::new_deviation(10.0, 10.0),
value: 10.0,
contains_unknown: false,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 10.0,
deviation: 10.0
})
},
administration_route: AdministrationRoute::Oral, administration_route: AdministrationRoute::Oral,
creation_time: from_unix_millis(0), creation_time: from_unix_millis(0),
is_archived: false is_archived: false

View file

@ -1,11 +1,21 @@
use super::Estimation;
#[derive(Default, PartialEq, Debug, Clone)] #[derive(Default, PartialEq, Debug, Clone)]
pub struct Dose { pub struct Dose {
pub value: f64, pub value: Option<f64>,
pub is_estimate: bool,
pub contains_unknown: bool, pub contains_unknown: bool,
pub estimation: Estimation, pub deviation: Option<f64>,
}
#[derive(PartialEq, Debug, Copy, Clone, Default)]
pub enum DoseKind {
Unknown,
#[default]
Precise,
Estimate,
StandardDeviation,
} }
mod add; mod add;
mod kind;
mod multiply; mod multiply;
mod new;

View file

@ -1,4 +1,4 @@
use super::{Dose, Estimation}; use super::Dose;
use std::ops::Add; use std::ops::Add;
@ -30,46 +30,45 @@ impl Add<&Dose> for Dose {
type Output = Self; type Output = Self;
fn add(self, rhs: &Self) -> Self::Output { fn add(self, rhs: &Self) -> Self::Output {
let contains_unknown = self.contains_unknown || rhs.contains_unknown;
let estimation = match self.estimation + &rhs.estimation {
Estimation::Precise => {
if contains_unknown {
Estimation::Estimate
} else {
Estimation::Precise
}
}
value => value,
};
Dose { Dose {
value: self.value + rhs.value, value: {
contains_unknown, match (self.value, rhs.value) {
estimation, (None, None) => None,
(lhs, rhs) => Some(lhs.unwrap_or_default() + rhs.unwrap_or_default()),
}
},
is_estimate: self.is_estimate || rhs.is_estimate,
deviation: {
match (self.deviation, rhs.deviation) {
(None, None) => None,
(Some(lhs), Some(rhs)) => Some(lhs + rhs),
(Some(deviation), None) | (_, Some(deviation)) => Some(deviation),
}
},
contains_unknown: {
match (
self.contains_unknown,
rhs.contains_unknown,
self.value,
rhs.value,
) {
(true, _, _, _) | (_, true, _, _) | (_, _, None, _) | (_, _, _, None) => true,
_ => false,
}
},
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::StandardDeviation; use super::Dose;
use super::{Dose, Estimation};
#[test] #[test]
fn add() { fn add() {
let lhs = Dose { let lhs = Dose::new_precise(10.0);
value: 10.0, let rhs = Dose::new_precise(10.0);
..Dose::default() let result = Dose::new_precise(20.0);
};
let rhs = Dose {
value: 10.0,
..Dose::default()
};
let result = Dose {
value: 20.0,
..Dose::default()
};
assert_eq!(lhs.clone() + rhs.clone(), result); assert_eq!(lhs.clone() + rhs.clone(), result);
assert_eq!(lhs.clone() + &rhs.clone(), result); assert_eq!(lhs.clone() + &rhs.clone(), result);
@ -80,63 +79,24 @@ mod tests {
#[test] #[test]
fn add_with_estimate() { fn add_with_estimate() {
assert_eq!( assert_eq!(
Dose { Dose::new_precise(10.0) + Dose::new_estimate(10.0),
value: 10.0, Dose::new_estimate(20.0)
..Dose::default()
} + Dose {
value: 10.0,
estimation: Estimation::Estimate,
..Dose::default()
},
Dose {
value: 20.0,
estimation: Estimation::Estimate,
..Dose::default()
}
); );
} }
#[test] #[test]
fn add_with_unknown() { fn add_with_unknown() {
assert_eq!( assert_eq!(
Dose { Dose::new_precise(10.0) + Dose::new_unknown(),
value: 10.0, Dose::new_estimate_with_unknown(10.0)
..Dose::default()
} + Dose {
value: 10.0,
contains_unknown: true,
..Dose::default()
},
Dose {
value: 20.0,
contains_unknown: true,
estimation: Estimation::Estimate
}
); );
} }
#[test] #[test]
fn add_with_standard_deviation() { fn add_with_deviation() {
assert_eq!( assert_eq!(
Dose { Dose::new_deviation(10.0, 10.0) + Dose::new_precise(10.0),
value: 10.0, Dose::new_deviation(20.0, 10.0),
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 10.0,
deviation: 10.0,
}),
..Dose::default()
} + Dose {
value: 10.0,
..Dose::default()
},
Dose {
value: 20.0,
contains_unknown: false,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 10.0,
deviation: 10.0,
}),
}
); );
} }
} }

View file

@ -0,0 +1,37 @@
use super::{Dose, DoseKind};
impl Dose {
pub fn kind(&self) -> DoseKind {
if self.value.is_none() {
assert!(self.contains_unknown);
return DoseKind::Unknown;
}
if self.is_estimate {
if self.deviation.is_some() {
DoseKind::StandardDeviation
} else {
DoseKind::Estimate
}
} else if self.contains_unknown {
DoseKind::Estimate
} else {
DoseKind::Precise
}
}
}
impl DoseKind {
pub fn unknown(&self) -> bool {
matches!(&self, DoseKind::Unknown)
}
pub fn precise(&self) -> bool {
matches!(&self, DoseKind::Precise)
}
pub fn estimate(&self) -> bool {
matches!(&self, DoseKind::Estimate | DoseKind::StandardDeviation)
}
pub fn standard_deviation(&self) -> bool {
matches!(&self, DoseKind::StandardDeviation)
}
}

View file

@ -1,6 +1,4 @@
use crate::types::StandardDeviation; use super::Dose;
use super::{Dose, Estimation};
use std::ops::Mul; use std::ops::Mul;
@ -32,66 +30,68 @@ impl Mul<&Dose> for Dose {
type Output = Self; type Output = Self;
fn mul(self, rhs: &Self) -> Self::Output { fn mul(self, rhs: &Self) -> Self::Output {
let contains_unknown = self.contains_unknown || rhs.contains_unknown;
let estimation = match (self.estimation, &rhs.estimation) {
(Estimation::StandardDeviation(_), Estimation::StandardDeviation(_)) => {
self.estimation * &rhs.estimation
}
(_, Estimation::StandardDeviation(deviation)) => {
Estimation::StandardDeviation(StandardDeviation {
expectation: self.value * deviation.expectation,
deviation: deviation.deviation,
})
}
(Estimation::StandardDeviation(deviation), _) => {
Estimation::StandardDeviation(StandardDeviation {
expectation: deviation.expectation * rhs.value,
deviation: deviation.deviation,
})
}
_ => self.estimation * &rhs.estimation,
};
let estimation = match estimation {
Estimation::Precise => {
if contains_unknown {
Estimation::Estimate
} else {
Estimation::Precise
}
}
value => value,
};
Dose { Dose {
value: self.value * rhs.value, value: {
contains_unknown, match (self.value, rhs.value) {
estimation, (Some(lhs), Some(rhs)) => Some(lhs * rhs),
_ => None,
}
},
is_estimate: self.is_estimate || rhs.is_estimate,
deviation: {
match (self.deviation, &rhs.deviation) {
(Some(_), Some(_)) => {
let lhs_deviation = self.deviation.unwrap_or_default();
let lhs_expectation = self.value.unwrap_or_default();
let rhs_deviation = rhs.deviation.unwrap_or_default();
let rhs_expectation = rhs.value.unwrap_or_default();
let lhs_sum = lhs_deviation.powi(2) + lhs_expectation.powi(2);
let rhs_sum = rhs_deviation.powi(2) + rhs_expectation.powi(2);
let expectations = lhs_expectation.powi(2) * rhs_expectation.powi(2);
let product_variance = (lhs_sum * rhs_sum) - expectations;
if product_variance > 0.0000001 {
let deviation = product_variance.sqrt();
Some(deviation.round())
} else {
None
}
}
(Some(lhs), _) => Some(lhs * rhs.value.unwrap_or_default()),
(_, Some(rhs)) => Some(rhs * self.value.unwrap_or_default()),
_ => None,
}
},
contains_unknown: {
match (
self.contains_unknown,
rhs.contains_unknown,
self.value,
rhs.value,
) {
(true, _, _, _) | (_, true, _, _) | (_, _, None, _) | (_, _, _, None) => true,
_ => false,
}
},
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::StandardDeviation; use super::Dose;
use super::{Dose, Estimation};
#[test] #[test]
fn mul() { fn mul() {
let lhs = Dose { let lhs = Dose::new_precise(10.0);
value: 10.0, let rhs = Dose::new_precise(10.0);
..Dose::default()
}; let result = Dose::new_precise(100.0);
let rhs = Dose {
value: 10.0,
..Dose::default()
};
let result = Dose {
value: 100.0,
..Dose::default()
};
assert_eq!(lhs.clone() * rhs.clone(), result); assert_eq!(lhs.clone() * rhs.clone(), result);
assert_eq!(lhs.clone() * &rhs.clone(), result); assert_eq!(lhs.clone() * &rhs.clone(), result);
@ -102,119 +102,40 @@ mod tests {
#[test] #[test]
fn mul_with_estimate() { fn mul_with_estimate() {
assert_eq!( assert_eq!(
Dose { Dose::new_precise(10.0) * Dose::new_estimate(10.0),
value: 10.0, Dose::new_estimate(100.0)
..Dose::default()
} * Dose {
value: 10.0,
estimation: Estimation::Estimate,
..Dose::default()
},
Dose {
value: 100.0,
estimation: Estimation::Estimate,
..Dose::default()
}
); );
} }
#[test] #[test]
fn mul_with_lhs_standard_deviation() { fn mul_with_lhs_standard_deviation() {
assert_eq!( assert_eq!(
Dose { Dose::new_deviation(10.0, 10.0) * Dose::new_precise(10.0),
value: 10.0, Dose::new_deviation(100.0, 100.0)
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 10.0,
deviation: 10.0
}),
..Dose::default()
} * Dose {
value: 10.0,
estimation: Estimation::Precise,
..Dose::default()
},
Dose {
value: 100.0,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 100.0,
deviation: 10.0
}),
..Dose::default()
}
); );
} }
#[test] #[test]
fn mul_with_rhs_standard_deviation() { fn mul_with_rhs_standard_deviation() {
assert_eq!( assert_eq!(
Dose { Dose::new_precise(10.0) * Dose::new_deviation(10.0, 10.0),
value: 10.0, Dose::new_deviation(100.0, 100.0)
estimation: Estimation::Precise,
..Dose::default()
} * Dose {
value: 10.0,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 10.0,
deviation: 10.0
}),
..Dose::default()
},
Dose {
value: 100.0,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 100.0,
deviation: 10.0
}),
..Dose::default()
}
); );
} }
#[test] #[test]
fn mul_with_standard_deviations() { fn mul_with_standard_deviations() {
assert_eq!( assert_eq!(
Dose { Dose::new_deviation(10.0, 10.0) * Dose::new_deviation(10.0, 10.0),
value: 10.0, Dose::new_deviation(100.0, 173.0)
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 10.0,
deviation: 10.0
}),
..Dose::default()
} * Dose {
value: 10.0,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 10.0,
deviation: 10.0
}),
..Dose::default()
},
Dose {
value: 100.0,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 100.0,
deviation: 173.0
}),
..Dose::default()
}
); );
} }
#[test] #[test]
fn mul_with_unknown() { fn mul_with_unknown() {
assert_eq!( assert_eq!(
Dose { Dose::new_precise(10.0) * Dose::new_unknown(),
value: 10.0, Dose::new_unknown()
..Dose::default()
} * Dose {
value: 10.0,
contains_unknown: true,
..Dose::default()
},
Dose {
value: 100.0,
contains_unknown: true,
estimation: Estimation::Estimate
}
); );
} }
} }

View file

@ -0,0 +1,51 @@
use super::Dose;
impl Dose {
pub fn new_unknown() -> Dose {
let value = Dose {
value: None,
is_estimate: true,
contains_unknown: true,
..Dose::default()
};
assert!(value.kind().unknown());
value
}
pub fn new_precise(value: f64) -> Dose {
let value = Dose {
value: Some(value),
..Dose::default()
};
assert!(value.kind().precise());
value
}
pub fn new_estimate(value: f64) -> Dose {
let value = Dose {
value: Some(value),
is_estimate: true,
..Dose::default()
};
assert!(value.kind().estimate());
value
}
pub fn new_estimate_with_unknown(value: f64) -> Dose {
let value = Dose {
value: Some(value),
is_estimate: true,
contains_unknown: true,
..Dose::default()
};
assert!(value.kind().estimate());
value
}
pub fn new_deviation(value: f64, deviation: f64) -> Dose {
let value = Dose {
value: Some(value),
is_estimate: true,
deviation: Some(deviation),
..Dose::default()
};
assert!(value.kind().standard_deviation());
value
}
}

View file

@ -1,6 +1,6 @@
use std::fmt::Display; use std::fmt::Display;
use super::{Dose, Estimation, Unit}; use super::{Dose, Unit};
pub fn format_dose(dose: &Dose, unit: &Unit) -> String { pub fn format_dose(dose: &Dose, unit: &Unit) -> String {
format!("{}", DoseWithUnitRefs { dose, unit }) format!("{}", DoseWithUnitRefs { dose, unit })
@ -36,8 +36,8 @@ impl Display for DoseWithUnitRefs<'_> {
match unit { match unit {
Unit::Simple(unit) => { Unit::Simple(unit) => {
let is_unknown = dose.contains_unknown && dose.value == 0.0; let is_unknown = dose.kind().unknown();
let is_estimate = dose.estimation.is_estimate() && !is_unknown; let is_estimate = dose.kind().estimate();
if is_estimate { if is_estimate {
f.write_str("~")?; f.write_str("~")?;
@ -46,7 +46,7 @@ impl Display for DoseWithUnitRefs<'_> {
if is_unknown { if is_unknown {
f.write_str("Unknown")? f.write_str("Unknown")?
} else { } else {
let dose_rounded = (dose.value * 100.0).round() / 100.0; let dose_rounded = (dose.value.unwrap() * 100.0).round() / 100.0;
write!(f, "{}", dose_rounded)?; write!(f, "{}", dose_rounded)?;
if dose.contains_unknown { if dose.contains_unknown {
@ -54,8 +54,8 @@ impl Display for DoseWithUnitRefs<'_> {
} }
} }
if let Estimation::StandardDeviation(deviation) = dose.estimation { if let Some(deviation) = dose.deviation {
write!(f, "±{}", (deviation.deviation * 100.0).round() / 100.0)?; write!(f, "±{}", (deviation * 100.0).round() / 100.0)?;
} }
f.write_str((" ".to_string() + unit).as_str()) f.write_str((" ".to_string() + unit).as_str())
@ -109,20 +109,13 @@ impl Display for DoseWithUnitRefs<'_> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::{AdministrationRoute, CustomUnit, StandardDeviation}; use crate::types::{AdministrationRoute, CustomUnit};
use super::*; use super::*;
#[test] #[test]
fn format_dose() { fn format_dose() {
let result = super::format_dose( let result = super::format_dose(&Dose::new_precise(0.0), &Unit::Simple("mg".to_string()));
&Dose {
value: 0.0,
contains_unknown: false,
estimation: Estimation::Precise,
},
&Unit::Simple("mg".to_string()),
);
assert_eq!(result, "0 mg"); assert_eq!(result, "0 mg");
} }
@ -131,11 +124,7 @@ mod tests {
let result = format!( let result = format!(
"{}", "{}",
DoseWithUnit { DoseWithUnit {
dose: Dose { dose: Dose::new_precise(0.0),
value: 0.0,
contains_unknown: false,
estimation: Estimation::Precise,
},
unit: Unit::Simple("mg".to_string()) unit: Unit::Simple("mg".to_string())
} }
); );
@ -144,25 +133,14 @@ mod tests {
#[test] #[test]
fn format_dose_estimate() { fn format_dose_estimate() {
let result = super::format_dose( let result = super::format_dose(&Dose::new_estimate(0.0), &Unit::Simple("mg".to_string()));
&Dose {
value: 0.0,
contains_unknown: false,
estimation: Estimation::Estimate,
},
&Unit::Simple("mg".to_string()),
);
assert_eq!(result, "~0 mg"); assert_eq!(result, "~0 mg");
} }
#[test] #[test]
fn format_dose_contains_unknown() { fn format_dose_contains_unknown() {
let result = super::format_dose( let result = super::format_dose(
&Dose { &(Dose::new_precise(10.0) + Dose::new_unknown()),
value: 10.0,
contains_unknown: true,
estimation: Estimation::Estimate,
},
&Unit::Simple("mg".to_string()), &Unit::Simple("mg".to_string()),
); );
assert_eq!(result, "~10+Unknown mg"); assert_eq!(result, "~10+Unknown mg");
@ -171,14 +149,7 @@ mod tests {
#[test] #[test]
fn format_dose_standard_deviation() { fn format_dose_standard_deviation() {
let result = super::format_dose( let result = super::format_dose(
&Dose { &Dose::new_deviation(0.0, 10.0),
value: 0.0,
contains_unknown: false,
estimation: Estimation::StandardDeviation(StandardDeviation {
expectation: 0.0,
deviation: 10.0,
}),
},
&Unit::Simple("mg".to_string()), &Unit::Simple("mg".to_string()),
); );
assert_eq!(result, "~0±10 mg"); assert_eq!(result, "~0±10 mg");
@ -186,25 +157,14 @@ mod tests {
#[test] #[test]
fn format_unknown_dose() { fn format_unknown_dose() {
let result = super::format_dose( let result = super::format_dose(&Dose::new_unknown(), &Unit::Simple("mg".to_string()));
&Dose {
value: 0.0,
contains_unknown: true,
estimation: Estimation::Estimate,
},
&Unit::Simple("mg".to_string()),
);
assert_eq!(result, "Unknown mg"); assert_eq!(result, "Unknown mg");
} }
#[test] #[test]
fn format_unknown_dose_with_custom_unit() { fn format_unknown_dose_with_custom_unit() {
let result = super::format_dose( let result = super::format_dose(
&Dose { &Dose::new_unknown(),
value: 0.0,
contains_unknown: true,
estimation: Estimation::Estimate,
},
&Unit::Custom { &Unit::Custom {
id: 0, id: 0,
unit: Some(CustomUnit { unit: Some(CustomUnit {
@ -212,11 +172,7 @@ mod tests {
substance_name: "Caffeine".to_string(), substance_name: "Caffeine".to_string(),
unit: "sip".to_string(), unit: "sip".to_string(),
original_unit: "mg".to_string(), original_unit: "mg".to_string(),
dose: Dose { dose: Dose::new_estimate(10.0),
value: 10.0,
contains_unknown: false,
estimation: Estimation::Estimate,
},
administration_route: AdministrationRoute::Oral, administration_route: AdministrationRoute::Oral,
..CustomUnit::default() ..CustomUnit::default()
}), }),

View file

@ -1,17 +0,0 @@
#[derive(PartialEq, Debug, Copy, Clone, Default)]
pub enum Estimation {
#[default]
Precise,
Estimate,
StandardDeviation(StandardDeviation),
}
#[derive(PartialEq, Default, Debug, Copy, Clone)]
pub struct StandardDeviation {
pub expectation: f64,
pub deviation: f64,
}
mod add;
mod is;
mod multiply;

View file

@ -1,57 +0,0 @@
use std::ops::Add;
use super::{Estimation, StandardDeviation};
impl Add<&Estimation> for Estimation {
type Output = Self;
fn add(self, rhs: &Self) -> Self::Output {
match (self, rhs) {
(Estimation::StandardDeviation(lhs), Estimation::StandardDeviation(rhs)) => {
Estimation::StandardDeviation(StandardDeviation {
expectation: lhs.expectation + rhs.expectation,
deviation: lhs.deviation + rhs.deviation,
})
}
(Estimation::StandardDeviation(deviation), _) => {
Estimation::StandardDeviation(deviation)
}
(_, Estimation::StandardDeviation(deviation)) => {
Estimation::StandardDeviation(*deviation)
}
(Estimation::Estimate, _) | (_, Estimation::Estimate) => Estimation::Estimate,
(Estimation::Precise, Estimation::Precise) => Estimation::Precise,
}
}
}
#[cfg(test)]
mod tests {
use super::{Estimation::*, StandardDeviation as Deviation};
#[test]
fn add() {
let deviation = StandardDeviation(Deviation {
expectation: 10.0,
deviation: 10.0,
});
assert_eq!(Precise + &Precise, Precise);
assert_eq!(Precise + &Estimate, Estimate);
assert_eq!(Precise + &deviation, deviation);
assert_eq!(Estimate + &Precise, Estimate);
assert_eq!(Estimate + &Estimate, Estimate);
assert_eq!(Estimate + &deviation, deviation);
assert_eq!(deviation + &Precise, deviation);
assert_eq!(deviation + &Estimate, deviation);
assert_eq!(
deviation + &deviation,
StandardDeviation(Deviation {
expectation: 20.0,
deviation: 20.0,
})
);
}
}

View file

@ -1,36 +0,0 @@
use super::Estimation;
impl Estimation {
pub fn is_precise(&self) -> bool {
matches!(self, Estimation::Precise)
}
pub fn is_estimate(&self) -> bool {
!matches!(self, Estimation::Precise)
}
pub fn is_standard_deviation(&self) -> bool {
matches!(self, Estimation::StandardDeviation(_))
}
}
#[cfg(test)]
mod tests {
use super::Estimation::*;
use crate::types::StandardDeviation as Deviation;
#[test]
fn is() {
let deviation = StandardDeviation(Deviation::default());
assert!(Precise.is_precise());
assert!(!Precise.is_estimate());
assert!(!Precise.is_standard_deviation());
assert!(!Estimate.is_precise());
assert!(Estimate.is_estimate());
assert!(!Estimate.is_standard_deviation());
assert!(!deviation.is_precise());
assert!(deviation.is_estimate());
assert!(deviation.is_standard_deviation());
}
}

View file

@ -1,82 +0,0 @@
use std::ops::Mul;
use super::{Estimation, StandardDeviation};
impl Mul<&Estimation> for Estimation {
type Output = Self;
fn mul(self, rhs: &Self) -> Self::Output {
match (self, rhs) {
(Estimation::StandardDeviation(lhs), Estimation::StandardDeviation(rhs)) => {
let lhs_sum = lhs.deviation.powi(2) + lhs.expectation.powi(2);
let rhs_sum = rhs.deviation.powi(2) + rhs.expectation.powi(2);
let expectations = lhs.expectation.powi(2) * rhs.expectation.powi(2);
let product_variance = (lhs_sum * rhs_sum) - expectations;
if product_variance > 0.0000001 {
let deviation = product_variance.sqrt();
let expectation = lhs.expectation * rhs.expectation;
Estimation::StandardDeviation(StandardDeviation {
expectation,
deviation: deviation.round(),
})
} else {
Estimation::Estimate
}
}
(Estimation::StandardDeviation(deviation), _) => {
Estimation::StandardDeviation(deviation)
}
(_, Estimation::StandardDeviation(deviation)) => {
Estimation::StandardDeviation(*deviation)
}
(Estimation::Estimate, _) | (_, Estimation::Estimate) => Estimation::Estimate,
(Estimation::Precise, Estimation::Precise) => Estimation::Precise,
}
}
}
#[cfg(test)]
mod tests {
use super::{Estimation::*, StandardDeviation as Deviation};
#[test]
fn multiply() {
let deviation = StandardDeviation(Deviation {
expectation: 10.0,
deviation: 10.0,
});
assert_eq!(Precise * &Precise, Precise);
assert_eq!(Precise * &Estimate, Estimate);
assert_eq!(Precise * &deviation, deviation);
assert_eq!(Estimate * &Precise, Estimate);
assert_eq!(Estimate * &Estimate, Estimate);
assert_eq!(Estimate * &deviation, deviation);
assert_eq!(deviation * &Precise, deviation);
assert_eq!(deviation * &Estimate, deviation);
assert_eq!(
deviation * &deviation,
StandardDeviation(Deviation {
expectation: 100.0,
deviation: 173.0,
})
);
assert_eq!(
StandardDeviation(Deviation {
expectation: 100.0,
deviation: 0.0,
}) * &StandardDeviation(Deviation {
expectation: 100.0,
deviation: 0.0,
}),
Estimate
)
}
}

View file

@ -1,9 +1,6 @@
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use super::{ use super::{dose::Dose, from_unix_millis, AdministrationRoute, Consumer, Unit};
dose::Dose, from_unix_millis, AdministrationRoute, Consumer, Estimation, StandardDeviation,
Unit,
};
#[derive(PartialEq, Default, Debug, Clone)] #[derive(PartialEq, Default, Debug, Clone)]
pub struct Ingestion { pub struct Ingestion {
@ -25,30 +22,20 @@ impl From<psychonaut_journal_types::Ingestion> for Ingestion {
ingestion_time: from_unix_millis(ingestion.ingestion_time), ingestion_time: from_unix_millis(ingestion.ingestion_time),
creation_time: from_unix_millis(ingestion.creation_time), creation_time: from_unix_millis(ingestion.creation_time),
dose: match ingestion.dose { dose: {
Some(value) => Dose { if ingestion.estimate_standard_deviation.is_some() {
value, Dose::new_deviation(
contains_unknown: false, ingestion.dose.unwrap(),
estimation: { ingestion.estimate_standard_deviation.unwrap(),
if !ingestion.is_estimate { )
Estimation::Precise } else if ingestion.is_estimate && ingestion.dose.is_some() {
} else if let Some(deviation) = ingestion.estimate_standard_deviation { Dose::new_estimate(ingestion.dose.unwrap())
Estimation::StandardDeviation(StandardDeviation { } else if ingestion.dose.is_some() {
expectation: ingestion.dose.unwrap_or_default(), Dose::new_precise(ingestion.dose.unwrap())
deviation,
})
} else { } else {
Estimation::Estimate Dose::new_unknown()
} }
}, },
},
None => Dose {
value: 0.0,
contains_unknown: true,
estimation: Estimation::Estimate,
},
},
unit: match ingestion.custom_unit_id { unit: match ingestion.custom_unit_id {
Some(id) => Unit::Custom { id, unit: None }, Some(id) => Unit::Custom { id, unit: None },
None => Unit::Simple(ingestion.unit), None => Unit::Simple(ingestion.unit),
@ -69,7 +56,7 @@ impl From<psychonaut_journal_types::Ingestion> for Ingestion {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::types::{from_unix_millis, AdministrationRoute, Consumer, Dose, Estimation, Unit}; use crate::types::{from_unix_millis, AdministrationRoute, Consumer, Dose, Unit};
use super::Ingestion; use super::Ingestion;
use psychonaut_journal_types::Ingestion as PsychonautIngestion; use psychonaut_journal_types::Ingestion as PsychonautIngestion;
@ -95,11 +82,7 @@ mod tests {
substance_name: "Caffeine".to_string(), substance_name: "Caffeine".to_string(),
ingestion_time: from_unix_millis(0), ingestion_time: from_unix_millis(0),
creation_time: from_unix_millis(0), creation_time: from_unix_millis(0),
dose: Dose { dose: Dose::new_precise(10.0),
value: 10.0,
contains_unknown: false,
estimation: Estimation::Precise
},
unit: Unit::Simple("mg".to_string()), unit: Unit::Simple("mg".to_string()),
roa: AdministrationRoute::Oral, roa: AdministrationRoute::Oral,
consumer: Consumer::Default, consumer: Consumer::Default,

View file

@ -1,9 +1,7 @@
mod estimate;
use chrono::{DateTime, TimeZone, Utc}; use chrono::{DateTime, TimeZone, Utc};
pub use estimate::{Estimation, StandardDeviation};
mod dose; mod dose;
pub use dose::Dose; pub use dose::{Dose, DoseKind};
mod unit; mod unit;
pub use unit::Unit; pub use unit::Unit;