#![warn(missing_docs)]
use std::collections::HashMap;
#[derive(Debug, Default)]
pub struct CircleJerk {
opinions: HashMap<String, usize>,
total: usize,
}
impl CircleJerk {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn agree(&mut self, opinion: impl Into<String>) {
*self.opinions.entry(opinion.into()).or_insert(0) += 1;
self.total += 1;
}
#[must_use]
pub fn consensus(&self) -> bool {
if self.total == 0 {
return false;
}
self.opinions
.values()
.copied()
.max()
.is_some_and(|count| count == self.total)
}
#[must_use]
pub fn consensus_ratio(&self) -> f64 {
if self.total == 0 {
return 0.0;
}
let max = self.opinions.values().copied().max().unwrap_or(0);
max as f64 / self.total as f64
}
#[must_use]
pub fn dominant_opinion(&self) -> Option<&str> {
self.opinions
.iter()
.max_by_key(|(_, count)| *count)
.map(|(opinion, _)| opinion.as_str())
}
#[must_use]
pub fn participants(&self) -> usize {
self.total
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn perfect_circlejerk() {
let mut cj = CircleJerk::new();
cj.agree("Rust is the best language");
cj.agree("Rust is the best language");
cj.agree("Rust is the best language");
assert!(cj.consensus());
assert_eq!(cj.consensus_ratio(), 1.0);
assert_eq!(cj.dominant_opinion(), Some("Rust is the best language"));
}
#[test]
fn dissent_detected() {
let mut cj = CircleJerk::new();
cj.agree("Rust is the best language");
cj.agree("Rust is the best language");
cj.agree("C++ is fine actually");
assert!(!cj.consensus());
assert_eq!(cj.consensus_ratio(), 2.0 / 3.0);
}
}