use std::sync::Arc;
use rand::distr::weighted::WeightedIndex;
use rand::prelude::*;
use crate::core::{Ballot, Candidate, Preference, Tactic};
use crate::tactics::Identity;
use crate::core::Profile;
#[derive(Debug)]
pub struct VotingBloc<B: Ballot> {
preference: Arc<dyn Preference<B>>,
strategy: Vec<(Arc<dyn Tactic<B>>, f32)>,
members: usize,
}
impl<B: Ballot> VotingBloc<B> {
pub fn builder(
preference: impl Preference<B> + 'static,
members: usize,
) -> VotingBlocBuilder<B> {
VotingBlocBuilder::new(preference, members)
}
fn new(
preference: Arc<dyn Preference<B>>,
strategy: Vec<(Arc<dyn Tactic<B>>, f32)>,
members: usize,
) -> Self {
Self {
preference,
strategy,
members,
}
}
#[must_use]
pub fn preferences(&self) -> Arc<dyn Preference<B>> {
self.preference.clone()
}
#[must_use]
pub fn strategy(&self) -> &[(Arc<dyn Tactic<B>>, f32)] {
&self.strategy
}
#[must_use]
pub const fn members(&self) -> usize {
self.members
}
pub fn realize(&self, candidates: &[Candidate], rng: &mut StdRng) -> Profile<B> {
(0..self.members())
.map(|_| self.preferences().draw(candidates, rng))
.collect()
}
pub fn vote(&self, candidates: &[Candidate], rng: &mut StdRng) -> Profile<B> {
let weights: Vec<f32> = self.strategy.iter().map(|(_, weight)| *weight).collect();
let dist = WeightedIndex::new(&weights).expect("Weights should be valid");
(0..self.members())
.map(|_| {
let honest_ballot = self.preferences().draw(candidates, rng);
let tactic = &self.strategy[dist.sample(rng)].0;
tactic.apply(honest_ballot)
})
.collect()
}
}
pub struct VotingBlocBuilder<B: Ballot> {
preference: Arc<dyn Preference<B>>,
strategy: Vec<(Arc<dyn Tactic<B>>, f32)>,
members: usize,
}
impl<B: Ballot> VotingBlocBuilder<B> {
pub fn new(preference: impl Preference<B> + 'static, members: usize) -> Self {
Self {
preference: Arc::new(preference),
strategy: Vec::new(),
members,
}
}
pub fn add_tactic(mut self, tactic: impl Tactic<B> + 'static, weight: f32) -> Self {
self.strategy.push((Arc::new(tactic), weight));
self
}
pub fn build(mut self) -> VotingBloc<B> {
if self.strategy.is_empty() {
self.strategy.push((Arc::new(Identity), 1.0f32));
}
VotingBloc::new(self.preference, self.strategy, self.members)
}
}