diff --git a/docs/options.md b/docs/options.md
index 771c215..c54dc42 100644
--- a/docs/options.md
+++ b/docs/options.md
@@ -261,8 +261,7 @@ When *Surplus method* is set to *Meek method*:
When *Surplus method* is set to a Gregory method, this option allows you to specify how the numbers of votes credited to candidates in a surplus transfer is calculated. In each case, votes are grouped according to the next available preference for a continuing candidate. Subsequently:
-* *Single step*: The total value of all votes expressing a next available preference for that candidate is multiplied by the surplus fraction. The product is credited to that candidate.
-* *By value*: The votes expressing a next available preference for that candidate are further divided according to value. For each group of votes at a particular value, the total value of all such votes is multiplied by the surplus fraction. The product is credited to that candidate. This is distinct to *Single step* only for weighted inclusive Gregory.
+* *By value*: The votes expressing a next available preference for that candidate are further divided according to value. For each group of votes at a particular value, the total value of all such votes is multiplied by the surplus fraction. The product is credited to that candidate.
* *Per ballot*: For each individual vote expressing a next available preference for that candidate, the value of the vote is multiplied by the surplus fraction. The product is credited to that candidate.
This option affects the result only as far as rounding (due to use of fixed-precision/floating-point arithmetic, or an explicit rounding option) is concerned.
diff --git a/html/index.html b/html/index.html
index 47fdd58..454bdf4 100644
--- a/html/index.html
+++ b/html/index.html
@@ -282,8 +282,8 @@
Gregory
Sum surplus transfers:
diff --git a/html/index.js b/html/index.js
index fd277b8..42e3d4f 100644
--- a/html/index.js
+++ b/html/index.js
@@ -379,7 +379,7 @@ function changePreset() {
document.getElementById('chkRoundVotes').checked = false;
document.getElementById('chkRoundSFs').checked = false;
document.getElementById('chkRoundValues').checked = false;
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selMethod').value = 'wig';
document.getElementById('selPapers').value = 'both';
@@ -511,7 +511,7 @@ function changePreset() {
document.getElementById('txtRoundVotes').value = '0';
document.getElementById('chkRoundSFs').checked = false;
document.getElementById('chkRoundValues').checked = false;
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_order';
document.getElementById('selMethod').value = 'uig';
document.getElementById('selPapers').value = 'both';
@@ -559,7 +559,7 @@ function changePreset() {
document.getElementById('txtRoundVotes').value = '6';
document.getElementById('chkRoundSFs').checked = false;
document.getElementById('chkRoundValues').checked = false;
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_order';
document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable';
@@ -584,7 +584,7 @@ function changePreset() {
document.getElementById('chkRoundSFs').checked = true;
document.getElementById('txtRoundSFs').value = '4';
document.getElementById('chkRoundValues').checked = false;
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selMethod').value = 'wig';
document.getElementById('selPapers').value = 'both';
@@ -606,7 +606,7 @@ function changePreset() {
document.getElementById('chkNormaliseBallots').checked = true;
document.getElementById('chkRoundQuota').checked = true;
document.getElementById('txtRoundQuota').value = '0';
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selMethod').value = 'cincinnati';
document.getElementById('selPapers').value = 'transferable';
document.getElementById('selExclusion').value = 'single_stage';
@@ -629,7 +629,7 @@ function changePreset() {
document.getElementById('chkRoundVotes').checked = false;
document.getElementById('chkRoundSFs').checked = false;
document.getElementById('chkRoundValues').checked = false;
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selMethod').value = 'wig';
document.getElementById('selPapers').value = 'both';
@@ -656,7 +656,7 @@ function changePreset() {
document.getElementById('txtRoundSFs').value = '3';
document.getElementById('chkRoundValues').checked = true;
document.getElementById('txtRoundValues').value = '3';
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_order';
document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable';
@@ -683,7 +683,7 @@ function changePreset() {
document.getElementById('txtRoundSFs').value = '2';
document.getElementById('chkRoundValues').checked = true;
document.getElementById('txtRoundValues').value = '2';
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable';
@@ -710,7 +710,7 @@ function changePreset() {
document.getElementById('txtRoundSFs').value = '2';
document.getElementById('chkRoundValues').checked = true;
document.getElementById('txtRoundValues').value = '2';
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable';
@@ -737,7 +737,7 @@ function changePreset() {
document.getElementById('txtRoundSFs').value = '2';
document.getElementById('chkRoundValues').checked = true;
document.getElementById('txtRoundValues').value = '2';
- document.getElementById('selSumTransfers').value = 'single_step';
+ document.getElementById('selSumTransfers').value = 'by_value';
document.getElementById('selSurplus').value = 'by_size';
document.getElementById('selMethod').value = 'eg';
document.getElementById('selPapers').value = 'transferable';
diff --git a/src/election.rs b/src/election.rs
index 830835d..2e25ca4 100644
--- a/src/election.rs
+++ b/src/election.rs
@@ -352,17 +352,29 @@ impl<'a, N: Number> CountCard<'a, N> {
pub struct Parcel<'a, N> {
/// [Vote]s in this parcel
pub votes: Vec>,
+ /// Accumulated relative value of each [Vote] in this parcel
+ pub value_fraction: N,
/// Order for sorting with [crate::stv::ExclusionMethod::BySource]
pub source_order: usize,
}
+impl<'a, N: Number> Parcel<'a, N> {
+ /// Return the number of ballots in this parcel
+ pub fn num_ballots(&self) -> N {
+ return self.votes.iter().fold(N::new(), |acc, v| acc + &v.ballot.orig_value);
+ }
+
+ /// Return the value of the votes in this parcel
+ pub fn num_votes(&self) -> N {
+ return self.num_ballots() * &self.value_fraction;
+ }
+}
+
/// Represents a [Ballot] with an associated value
#[derive(Clone)]
pub struct Vote<'a, N> {
/// Ballot from which the vote is derived
pub ballot: &'a Ballot,
- /// Current value of the ballot
- pub value: N,
/// Index of the next preference to examine
pub up_to_pref: usize,
}
diff --git a/src/main.rs b/src/main.rs
index 0eac96d..5ae4e56 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -86,7 +86,7 @@ struct STV {
round_quota: Option,
/// (Gregory STV) How to calculate votes to credit to candidates in surplus transfers
- #[clap(help_heading=Some("ROUNDING"), long, possible_values=&["single_step", "by_value", "per_ballot"], default_value="single_step", value_name="mode")]
+ #[clap(help_heading=Some("ROUNDING"), long, possible_values=&["by_value", "per_ballot"], default_value="by_value", value_name="mode")]
sum_surplus_transfers: String,
/// (Meek STV) Limit for stopping iteration of surplus distribution
@@ -259,6 +259,7 @@ fn maybe_load_constraints(election: &mut Election, constraints: &O
fn count_election(mut election: Election, cmd_opts: STV) -> Result<(), i32>
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>,
diff --git a/src/stv/gregory.rs b/src/stv/gregory.rs
index 7f664d2..4b54a21 100644
--- a/src/stv/gregory.rs
+++ b/src/stv/gregory.rs
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-use super::{ExclusionMethod, NextPreferencesEntry, NextPreferencesResult, STVError, STVOptions, SumSurplusTransfersMode, SurplusMethod, SurplusOrder};
+use super::{ExclusionMethod, NextPreferencesEntry, STVError, STVOptions, SumSurplusTransfersMode, SurplusMethod, SurplusOrder};
use super::sample;
use crate::constraints;
@@ -23,16 +23,14 @@ use crate::election::{Candidate, CandidateState, CountState, Parcel, Vote};
use crate::numbers::Number;
use crate::ties;
-use itertools::Itertools;
-
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,
- value: b.orig_value.clone(),
up_to_pref: 0,
}).collect();
@@ -42,20 +40,22 @@ pub fn distribute_first_preferences(state: &mut CountState) {
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_votes);
+ count_card.transfer(&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_votes);
+ state.exhausted.transfer(&result.exhausted.num_ballots);
state.kind = None;
state.title = "First preferences".to_string();
@@ -67,7 +67,9 @@ pub fn distribute_first_preferences(state: &mut CountState) {
/// 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