use super::{
balancing, setup_inputs, BalancingConfig, CandidatePtr, ElectionResult, ExtendedBalance,
IdentifierT, PerThing128, VoteWeight, Voter,
};
use crate::arithmetic::{
helpers_128bit::multiply_by_rational_with_rounding,
traits::{Bounded, Zero},
Rational128, Rounding,
};
use alloc::vec::Vec;
const DEN: ExtendedBalance = ExtendedBalance::max_value();
pub fn seq_phragmen<AccountId: IdentifierT, P: PerThing128>(
to_elect: usize,
candidates: Vec<AccountId>,
voters: Vec<(AccountId, VoteWeight, impl IntoIterator<Item = AccountId>)>,
balancing: Option<BalancingConfig>,
) -> Result<ElectionResult<AccountId, P>, super::Error> {
let (candidates, voters) = setup_inputs(candidates, voters);
let (candidates, mut voters) = seq_phragmen_core::<AccountId>(to_elect, candidates, voters)?;
if let Some(ref config) = balancing {
let _iters = balancing::balance::<AccountId>(&mut voters, config);
}
let mut winners = candidates
.into_iter()
.filter(|c_ptr| c_ptr.borrow().elected)
.take(to_elect)
.collect::<Vec<_>>();
winners.sort_by_key(|c_ptr| c_ptr.borrow().round);
let mut assignments =
voters.into_iter().filter_map(|v| v.into_assignment()).collect::<Vec<_>>();
assignments
.iter_mut()
.try_for_each(|a| a.try_normalize().map_err(|_| super::Error::ArithmeticError))?;
let winners = winners
.into_iter()
.map(|w_ptr| (w_ptr.borrow().who.clone(), w_ptr.borrow().backed_stake))
.collect();
Ok(ElectionResult { winners, assignments })
}
pub fn seq_phragmen_core<AccountId: IdentifierT>(
to_elect: usize,
candidates: Vec<CandidatePtr<AccountId>>,
mut voters: Vec<Voter<AccountId>>,
) -> Result<(Vec<CandidatePtr<AccountId>>, Vec<Voter<AccountId>>), super::Error> {
let to_elect = to_elect.min(candidates.len());
for round in 0..to_elect {
for c_ptr in &candidates {
let mut candidate = c_ptr.borrow_mut();
if !candidate.elected {
if candidate.approval_stake.is_zero() {
candidate.score = Bounded::max_value();
} else {
candidate.score = Rational128::from(DEN / candidate.approval_stake, DEN);
}
}
}
for voter in &voters {
for edge in &voter.edges {
let mut candidate = edge.candidate.borrow_mut();
if !candidate.elected && !candidate.approval_stake.is_zero() {
let temp_n = multiply_by_rational_with_rounding(
voter.load.n(),
voter.budget,
candidate.approval_stake,
Rounding::Down,
)
.unwrap_or(Bounded::max_value());
let temp_d = voter.load.d();
let temp = Rational128::from(temp_n, temp_d);
candidate.score = candidate.score.lazy_saturating_add(temp);
}
}
}
if let Some(winner_ptr) = candidates
.iter()
.filter(|c| !c.borrow().elected)
.min_by_key(|c| c.borrow().score)
{
let mut winner = winner_ptr.borrow_mut();
winner.elected = true;
winner.round = round;
for voter in &mut voters {
for edge in &mut voter.edges {
if edge.who == winner.who {
edge.load = winner.score.lazy_saturating_sub(voter.load);
voter.load = winner.score;
}
}
}
} else {
break;
}
}
for voter in &mut voters {
for edge in &mut voter.edges {
if edge.candidate.borrow().elected {
edge.weight = multiply_by_rational_with_rounding(
voter.budget,
edge.load.n(),
voter.load.n(),
Rounding::Down,
)
.unwrap_or(Bounded::max_value());
} else {
edge.weight = 0
}
let mut candidate = edge.candidate.borrow_mut();
candidate.backed_stake = candidate.backed_stake.saturating_add(edge.weight);
}
voter.edges.retain(|e| e.weight > 0);
debug_assert!(voter.edges.iter().all(|e| e.candidate.borrow().elected));
voter.try_normalize_elected().map_err(|_| super::Error::ArithmeticError)?;
}
Ok((candidates, voters))
}