use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Trit {
Reject,
Tend,
Affirm,
}
impl Trit {
pub fn as_i8(self) -> i8 {
match self {
Trit::Reject => -1,
Trit::Tend => 0,
Trit::Affirm => 1,
}
}
pub fn from_i8(v: i8) -> Self {
match v {
v if v < 0 => Trit::Reject,
0 => Trit::Tend,
_ => Trit::Affirm,
}
}
pub fn label(self) -> &'static str {
match self {
Trit::Reject => "reject",
Trit::Tend => "tend",
Trit::Affirm => "affirm",
}
}
pub fn consensus(a: Trit, b: Trit) -> Trit {
match (a, b) {
(Trit::Affirm, Trit::Affirm) => Trit::Affirm,
(Trit::Reject, Trit::Reject) => Trit::Reject,
(Trit::Tend, x) | (x, Trit::Tend) => x,
_ => Trit::Tend, }
}
pub fn from_evidence(mean: f64) -> Trit {
if mean > 0.33 {
Trit::Affirm
} else if mean < -0.33 {
Trit::Reject
} else {
Trit::Tend
}
}
}
pub fn decide(evidence: &[f64]) -> (Trit, f64) {
if evidence.is_empty() {
return (Trit::Tend, 0.0);
}
let mean: f64 = evidence.iter().sum::<f64>() / evidence.len() as f64;
let trit = Trit::from_evidence(mean);
let confidence = (mean.abs()).min(1.0);
(trit, confidence)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn consensus_matches_observed_runbook_result() {
assert_eq!(Trit::consensus(Trit::Reject, Trit::Affirm), Trit::Tend);
}
#[test]
fn consensus_agreement_holds() {
assert_eq!(Trit::consensus(Trit::Affirm, Trit::Affirm), Trit::Affirm);
assert_eq!(Trit::consensus(Trit::Reject, Trit::Reject), Trit::Reject);
}
#[test]
fn consensus_tend_is_absorbing_into_the_other_side() {
assert_eq!(Trit::consensus(Trit::Tend, Trit::Affirm), Trit::Affirm);
assert_eq!(Trit::consensus(Trit::Reject, Trit::Tend), Trit::Reject);
}
#[test]
fn decide_zones() {
assert_eq!(decide(&[0.9, 0.8]).0, Trit::Affirm);
assert_eq!(decide(&[-0.9]).0, Trit::Reject);
assert_eq!(decide(&[0.1, -0.1]).0, Trit::Tend);
assert_eq!(decide(&[]).0, Trit::Tend);
}
}