86 lines
3.0 KiB
Rust
86 lines
3.0 KiB
Rust
|
use fuzzytags::{FuzzyDetectionKey, FuzzyPublicKey, FuzzyTag};
|
||
|
use hashbrown::HashMap;
|
||
|
|
||
|
pub struct SimulatedServer {
|
||
|
keybase: Vec<(FuzzyDetectionKey, FuzzyPublicKey)>,
|
||
|
messages: Vec<FuzzyTag>,
|
||
|
tags_to_keys_cache: HashMap<String, Vec<FuzzyDetectionKey>>,
|
||
|
keys_to_tags_cache: HashMap<String, Vec<FuzzyTag>>,
|
||
|
}
|
||
|
|
||
|
pub struct PartyStatistics {
|
||
|
pub ideal_rate: f64,
|
||
|
pub expected_messages: f64,
|
||
|
pub observed_messages: usize,
|
||
|
pub observed_rate: f64,
|
||
|
pub observed_skew_messages: f64,
|
||
|
pub observed_skew: f64,
|
||
|
pub trivial_breaks: usize,
|
||
|
}
|
||
|
|
||
|
impl SimulatedServer {
|
||
|
pub fn new() -> SimulatedServer {
|
||
|
SimulatedServer {
|
||
|
keybase: vec![],
|
||
|
messages: vec![],
|
||
|
tags_to_keys_cache: HashMap::new(),
|
||
|
keys_to_tags_cache: HashMap::new(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn register_key(&mut self, detection_key: &FuzzyDetectionKey, public_key: &FuzzyPublicKey) {
|
||
|
self.keybase.push((detection_key.clone(), public_key.clone()));
|
||
|
self.keys_to_tags_cache.insert(detection_key.id(), vec![]);
|
||
|
}
|
||
|
|
||
|
pub fn add_message(&mut self, tag: FuzzyTag) {
|
||
|
self.messages.push(tag.clone());
|
||
|
self.tags_to_keys_cache.insert(tag.to_string(), vec![]);
|
||
|
}
|
||
|
|
||
|
pub fn test_messages(&mut self) {
|
||
|
for message in self.messages.iter() {
|
||
|
for (detection_key, _) in self.keybase.iter() {
|
||
|
if detection_key.test_tag(message) {
|
||
|
self.tags_to_keys_cache.get_mut(message.to_string().as_str()).unwrap().push((*detection_key).clone());
|
||
|
self.keys_to_tags_cache.get_mut(detection_key.id().as_str()).unwrap().push((*message).clone());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn statistics(&self) -> HashMap<String, PartyStatistics> {
|
||
|
let mut stats = HashMap::new();
|
||
|
for (party, pub_key) in self.keybase.iter() {
|
||
|
let matched = self.keys_to_tags_cache[party.id().as_str()].clone();
|
||
|
let observed_messages = matched.len();
|
||
|
let ideal_rate = party.false_positive_probability();
|
||
|
let observed_rate = (observed_messages as f64) / (self.messages.len() as f64);
|
||
|
let expected_messages = ideal_rate * (self.messages.len() as f64);
|
||
|
let observed_skew_messages = expected_messages - (observed_messages as f64);
|
||
|
let observed_skew = observed_rate / ideal_rate;
|
||
|
|
||
|
let mut trivial_breaks = 0;
|
||
|
for tag in matched.iter() {
|
||
|
if self.tags_to_keys_cache[tag.to_string().as_str()].len() == 1 {
|
||
|
trivial_breaks += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stats.insert(
|
||
|
pub_key.id(),
|
||
|
PartyStatistics {
|
||
|
ideal_rate,
|
||
|
expected_messages,
|
||
|
observed_messages,
|
||
|
observed_rate,
|
||
|
observed_skew_messages,
|
||
|
observed_skew,
|
||
|
trivial_breaks,
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
stats
|
||
|
}
|
||
|
}
|