use anyhow::{anyhow, ensure, Result};
#[derive(Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FeedbackCounters {
counters: Vec<FeedbackCounter>,
total_correct: u64,
total_incorrect: u64,
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FeedbackCounter {
correct: u64,
incorrect: u64,
}
impl FeedbackCounters {
pub fn from_instance(mut read_global: impl FnMut(&str) -> Option<u64>) -> Result<Self> {
let mut counters = vec![];
let mut total_correct = 0_u64;
let mut total_incorrect = 0_u64;
for i in 0.. {
let correct = match read_global(&format!("__winliner_counter_{i}_correct")) {
Some(x) => x,
None => break,
};
total_correct = total_correct.saturating_add(correct);
let incorrect =
read_global(&format!("__winliner_counter_{i}_incorrect")).ok_or_else(|| {
anyhow!("Failed to read `__winliner_counter_{i}_incorrect` global")
})?;
total_incorrect = total_incorrect.saturating_add(incorrect);
counters.push(FeedbackCounter { correct, incorrect });
}
Ok(FeedbackCounters {
counters,
total_correct,
total_incorrect,
})
}
pub fn merge(&mut self, other: &Self) -> Result<()> {
ensure!(
self.counters.len() == other.counters.len(),
"incompatible counters: generated from different Wasm modules"
);
for (me, them) in self.counters.iter_mut().zip(&other.counters) {
me.correct = me.correct.saturating_add(them.correct);
me.incorrect += me.incorrect.saturating_add(them.incorrect);
}
self.total_correct = self.total_correct.saturating_add(other.total_correct);
self.total_incorrect = self.total_incorrect.saturating_add(other.total_incorrect);
Ok(())
}
pub fn counters(&self) -> &[FeedbackCounter] {
&self.counters
}
pub fn total(&self) -> u64 {
self.total_correct.saturating_add(self.total_incorrect)
}
pub fn total_correct(&self) -> u64 {
self.total_correct
}
pub fn total_incorrect(&self) -> u64 {
self.total_incorrect
}
pub fn total_correct_ratio(&self) -> Option<f64> {
if self.total() > 0 {
Some(self.total_correct as f64 / self.total() as f64)
} else {
None
}
}
pub fn total_incorrect_ratio(&self) -> Option<f64> {
if self.total() > 0 {
Some(self.total_incorrect as f64 / self.total() as f64)
} else {
None
}
}
}
impl FeedbackCounter {
pub fn correct(&self) -> u64 {
self.correct
}
pub fn incorrect(&self) -> u64 {
self.incorrect
}
pub fn total(&self) -> u64 {
self.correct.saturating_add(self.incorrect)
}
pub fn correct_ratio(&self) -> Option<f64> {
if self.total() > 0 {
Some(self.correct as f64 / self.total() as f64)
} else {
None
}
}
pub fn incorrect_ratio(&self) -> Option<f64> {
if self.total() > 0 {
Some(self.incorrect as f64 / self.total() as f64)
} else {
None
}
}
}