2026-01-27 16:21:58 +00:00
|
|
|
//! Advanced Voting Methods
|
|
|
|
|
//!
|
|
|
|
|
//! Implements various voting algorithms as described in the Democracy Design manifesto:
|
|
|
|
|
//! - Schulze Method (Condorcet-consistent pairwise comparison)
|
|
|
|
|
//! - STAR Voting (Score Then Automatic Runoff)
|
|
|
|
|
//! - Quadratic Voting (intensity-weighted preferences)
|
|
|
|
|
//! - Ranked Choice / Instant Runoff
|
|
|
|
|
|
|
|
|
|
pub mod schulze;
|
|
|
|
|
pub mod star;
|
|
|
|
|
pub mod quadratic;
|
|
|
|
|
pub mod ranked_choice;
|
|
|
|
|
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
|
|
|
|
|
/// Voting method types
|
|
|
|
|
/// Used by voting calculation services when tallying results.
|
|
|
|
|
#[allow(dead_code)]
|
2026-01-28 23:46:43 +00:00
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
2026-01-27 16:21:58 +00:00
|
|
|
#[serde(rename_all = "snake_case")]
|
|
|
|
|
pub enum VotingMethod {
|
|
|
|
|
/// Simple approval voting (vote for multiple options)
|
2026-01-28 23:46:43 +00:00
|
|
|
#[default]
|
2026-01-27 16:21:58 +00:00
|
|
|
Approval,
|
|
|
|
|
/// Ranked choice / instant runoff
|
|
|
|
|
RankedChoice,
|
|
|
|
|
/// Schulze method (Condorcet)
|
|
|
|
|
Schulze,
|
|
|
|
|
/// STAR voting (Score Then Automatic Runoff)
|
|
|
|
|
Star,
|
|
|
|
|
/// Quadratic voting (intensity-weighted)
|
|
|
|
|
Quadratic,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::fmt::Display for VotingMethod {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
VotingMethod::Approval => write!(f, "approval"),
|
|
|
|
|
VotingMethod::RankedChoice => write!(f, "ranked_choice"),
|
|
|
|
|
VotingMethod::Schulze => write!(f, "schulze"),
|
|
|
|
|
VotingMethod::Star => write!(f, "star"),
|
|
|
|
|
VotingMethod::Quadratic => write!(f, "quadratic"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Result of a voting calculation
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct VotingResult {
|
|
|
|
|
/// The winning option (if any)
|
|
|
|
|
pub winner: Option<Uuid>,
|
|
|
|
|
/// Full ranking of options
|
|
|
|
|
pub ranking: Vec<RankedOption>,
|
|
|
|
|
/// Method-specific details
|
|
|
|
|
pub details: VotingDetails,
|
|
|
|
|
/// Total number of ballots counted
|
|
|
|
|
pub total_ballots: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// An option with its rank and score
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct RankedOption {
|
|
|
|
|
pub option_id: Uuid,
|
|
|
|
|
pub rank: usize,
|
|
|
|
|
pub score: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Method-specific voting details
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
#[serde(tag = "method")]
|
|
|
|
|
pub enum VotingDetails {
|
|
|
|
|
Approval {
|
|
|
|
|
vote_counts: Vec<(Uuid, i64)>,
|
|
|
|
|
},
|
|
|
|
|
RankedChoice {
|
|
|
|
|
rounds: Vec<RoundResult>,
|
|
|
|
|
eliminated: Vec<Uuid>,
|
|
|
|
|
},
|
|
|
|
|
Schulze {
|
|
|
|
|
pairwise_matrix: Vec<Vec<i32>>,
|
|
|
|
|
strongest_paths: Vec<Vec<i32>>,
|
|
|
|
|
option_ids: Vec<Uuid>,
|
|
|
|
|
},
|
|
|
|
|
Star {
|
|
|
|
|
score_totals: Vec<(Uuid, i64)>,
|
|
|
|
|
finalists: (Uuid, Uuid),
|
|
|
|
|
runoff_votes: (i64, i64),
|
|
|
|
|
},
|
|
|
|
|
Quadratic {
|
|
|
|
|
vote_totals: Vec<(Uuid, i64)>,
|
|
|
|
|
total_credits_spent: i64,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Result of a single round in ranked choice voting
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct RoundResult {
|
|
|
|
|
pub round: usize,
|
|
|
|
|
pub vote_counts: Vec<(Uuid, i64)>,
|
|
|
|
|
pub eliminated: Option<Uuid>,
|
|
|
|
|
}
|