/* OpenTally: Open-source election vote counting
* Copyright © 2021 Lee Yingtong Li (RunasSudo)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use super::{ExclusionMethod, NextPreferencesEntry, STVError, STVOptions, SumSurplusTransfersMode, SurplusMethod, SurplusOrder};
use super::sample;
use crate::constraints;
use crate::election::{Candidate, CandidateState, CountState, Parcel, Vote};
use crate::numbers::Number;
use crate::ties;
use std::cmp::max;
use std::collections::HashMap;
use std::ops;
/// Distribute first preference votes according to the Gregory method
pub fn distribute_first_preferences(state: &mut CountState) {
let votes = state.election.ballots.iter().map(|b| Vote {
ballot: b,
up_to_pref: 0,
}).collect();
let result = super::next_preferences(state, votes);
// Transfer candidate votes
for (candidate, entry) in result.candidates.into_iter() {
let parcel = Parcel {
votes: entry.votes,
value_fraction: N::one(),
source_order: 0,
};
let count_card = state.candidates.get_mut(candidate).unwrap();
count_card.parcels.push(parcel);
count_card.transfer(&entry.num_ballots);
count_card.ballot_transfers += entry.num_ballots;
}
// Transfer exhausted votes
let parcel = Parcel {
votes: result.exhausted.votes,
value_fraction: N::one(),
source_order: 0,
};
state.exhausted.parcels.push(parcel);
state.exhausted.transfer(&result.exhausted.num_ballots);
state.exhausted.ballot_transfers += result.exhausted.num_ballots;
state.kind = None;
state.title = "First preferences".to_string();
state.logger.log_literal("First preferences distributed.".to_string());
}
/// Distribute the largest surplus according to the Gregory or random subset method, based on [STVOptions::surplus]
///
/// Returns `true` if any surpluses were distributed.
pub fn distribute_surpluses(state: &mut CountState, opts: &STVOptions) -> Result
where
for<'r> &'r N: ops::Add<&'r N, Output=N>,
for<'r> &'r N: ops::Sub<&'r N, Output=N>,
for<'r> &'r N: ops::Mul<&'r N, Output=N>,
for<'r> &'r N: ops::Div<&'r N, Output=N>,
for<'r> &'r N: ops::Neg