From 1a1153b6664c95bd07c5cc5880e4be5ceda76d64 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 20 Nov 2022 18:58:32 +1100 Subject: [PATCH] Defer surplus distribution if not affecting bulk exclusion, even if otherwise affecting trailing 2 candidates --- docs/FnSpecs.tex | 12 ++++++------ docs/options.md | 13 +++++++------ src/stv/mod.rs | 20 ++++++++++++-------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/docs/FnSpecs.tex b/docs/FnSpecs.tex index 204d6f1..7c715cf 100644 --- a/docs/FnSpecs.tex +++ b/docs/FnSpecs.tex @@ -87,7 +87,7 @@ \fancypagestyle{plain}{\fancyhf{}} \pagestyle{fancy}\fancyhf{}\renewcommand{\headrulewidth}{0pt} \lhead{\textsf{\scriptsize OpenTally Functional Specifications}} - \lfoot{\textsf{\scriptsize Draft 2022-11-06}} + \lfoot{\textsf{\scriptsize Draft 2022-11-20}} \rfoot{\textsf{\scriptsize\thepage}} \pagenumbering{roman} @@ -226,13 +226,13 @@ \subsection If there are fewer than 2 continuing candidates, the distribution of surpluses must be deferred. - \subsection\label{bulk-exclude-xref1-s}Otherwise, if: + \subsection\label{bulk-exclude-xref1}Otherwise, if \textit{--bulk-exclude} is enabled and a bulk exclusion could be performed under section~\ref{bulk-exclude}: - \subsectionc\paragraph the total of all undistributed surpluses is less than the difference between the progress totals of the 2 continuing candidates with the lowest progress totals, and + \subsectionc\paragraph If the total of all undistributed surpluses is less than the difference between the sum of the progress totals of the continuing candidates who could be bulk excluded, and the progress total of the continuing candidate with the next lowest progress total, then the distribution of surpluses must be deferred. - \subsectionc\paragraph\label{bulk-exclude-xref1}if \textit{--bulk-exclude} is enabled and a bulk exclusion could be performed under section~\ref{bulk-exclude}, the total of all undistributed surpluses is less than the difference between the sum of the progress totals of the continuing candidates who could be bulk excluded, and the progress total of the continuing candidate with the next lowest progress total, + \subsectionc\paragraph Otherwise, the distribution of surpluses must not be deferred. - \subsectionc the distribution of surpluses must be deferred. + \subsection Otherwise, if the total of all undistributed surpluses is less than the difference between the progress totals of the 2 continuing candidates with the lowest progress totals, the distribution of surpluses must be deferred. \subsection Otherwise, the distribution of surpluses must not be deferred. @@ -867,7 +867,7 @@ \section{Bulk exclusion}\label{bulk-exclude} - \subsection This section applies in subsections~\ref{bulk-exclude-xref1-s}\ref{bulk-exclude-xref1}, \ref{bulk-exclude-xref2-s}\ref{bulk-exclude-xref2}, \ref{bulk-exclude-xref4-s}\ref{bulk-exclude-xref4} and \ref{bulk-exclude-xref3-s}\ref{bulk-exclude-xref3}, if \textit{--bulk-exclude} is enabled, to determine which candidates (if any) can be bulk excluded. + \subsection This section applies in subsections~\ref{bulk-exclude-xref1}, \ref{bulk-exclude-xref2-s}\ref{bulk-exclude-xref2}, \ref{bulk-exclude-xref4-s}\ref{bulk-exclude-xref4} and \ref{bulk-exclude-xref3-s}\ref{bulk-exclude-xref3}, if \textit{--bulk-exclude} is enabled, to determine which candidates (if any) can be bulk excluded. \subsection In a bulk exclusion, select for exclusion as many of the continuing candidates with the lowest progress totals as possible, provided that: diff --git a/docs/options.md b/docs/options.md index c94e02a..7a41598 100644 --- a/docs/options.md +++ b/docs/options.md @@ -23,9 +23,9 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV | Dáil Éireann STV | Rules from the [*Electoral Act 1992* (Ireland)](http://www.irishstatutebook.ie/eli/1992/act/23/enacted/en/print), using stratified random sample transfers. | [E4] [E7] | ✓ | | [van der Craats (‘Wright’) STV](https://www.aph.gov.au/Parliamentary_Business/Committees/House_of_Representatives_Committees?url=em/elect07/subs/sub051.1.pdf) | Rules proposed by Anthony van der Craats designed for computer counting, involving reset and re-iteration of the count after each candidate exclusion. | | ✓ | | [PRSA 1977](https://www.prsa.org.au/rule1977.htm) | Simple rules designed for hand counting, using the exclusive Gregory method, with counting performed in thousandths of a vote. | | ✓ | -| [ERS97](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/) | More complex rules designed for hand counting, using the exclusive Gregory method. | [E8] [E9] | ✓ | -| • ERS76 | Former rules from the 1976 2nd edition. | [E8] [E9] [E10] | ✓ | -| • ERS73 | Former rules from the 1973 1st edition. | [E8] [E9] [E10] | | +| [ERS97](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/) | More complex rules designed for hand counting, using the exclusive Gregory method. | [E8] [E9] [E10] | ✓ | +| • ERS76 | Former rules from the 1976 2nd edition. | [E8] [E9] [E10] [E11] | ✓ | +| • ERS73 | Former rules from the 1973 1st edition. | [E8] [E9] [E10] [E11] | | | Church of England | Rules from the Church of England [*Single Transferable Vote Rules 2020*](https://www.churchofengland.org/sites/default/files/2020-02/STV%20Rules%202020%20-%20final.pdf), similar to ERS73. | [E8] | ✓ | Exceptions: @@ -35,11 +35,12 @@ Exceptions: * [E3] A tie between 2 candidates for the final vacancy will be broken backwards then at random, rather than the method described in the legislation. * [E4] Bulk exclusion is not performed, as the prescribed rules are more conservative than OpenTally's. See also the section on *Bulk exclusion* for further discussion. * [E5] The legislation is drafted such that a consistent interpretation is impossible – see Conway for a discussion. In practice, the New South Wales Electoral Commission has applied the ‘by parcel’ method of rounding subtransfers, which OpenTally follows. -* [E6] The ‘mathematically eliminated by the sum of all ranked-choice votes comparison’ is not implemented. +* [E6] The ‘mathematically eliminated by the sum of all ranked-choice votes comparison’ is not implemented, and undeclared write-in candidates are not distinguished. * [E7] The ‘quarter of a quota’ provision is disregarded when determining whether to defer surplus distributions. * [E8] The distribution of a surplus is not deferred if it exactly equals the difference between the 2 trailing continuing candidates. -* [E9] No distinction is made between stages and substages (during exclusion). This affects only the numbering of stages and not the result. -* [E10] By default, the quota is always calculated to 2 decimal places. For full ERS76 (ERS73) compliance, set *Round quota to 0 d.p.* when the quota is more than 100 (100 or more). +* [E9] The distribution of a surplus is not deferred if a bulk exclusion could be performed and it could not change the bulk exclusion, even if it could change which candidate is last. +* [E10] No distinction is made between stages and substages (during exclusion). This affects only the numbering of stages and not the result. +* [E11] By default, the quota is always calculated to 2 decimal places. For full ERS76 (ERS73) compliance, set *Round quota to 0 d.p.* when the quota is more than 100 (100 or more). For details of validation, see [validation.md](https://yingtongli.me/git/OpenTally/about/docs/validation.md). diff --git a/src/stv/mod.rs b/src/stv/mod.rs index ab8f89c..13c4dc2 100644 --- a/src/stv/mod.rs +++ b/src/stv/mod.rs @@ -675,18 +675,14 @@ fn can_defer_surpluses(state: &CountState, opts: &STVOptions, tota where for<'r> &'r N: ops::Sub<&'r N, Output=N> { - // Do not defer if this could change the last 2 candidates let mut hopefuls: Vec<(&Candidate, &CountCard)> = state.candidates.iter() .filter(|(_, cc)| cc.state == CandidateState::Hopeful || cc.state == CandidateState::Guarded) .collect(); - - if hopefuls.len() < 2 { - return true; - } - hopefuls.sort_unstable_by(|(_, cc1), (_, cc2)| cc1.votes.cmp(&cc2.votes)); - if total_surpluses >= &(&hopefuls[1].1.votes - &hopefuls[0].1.votes) { - return false; + + // Surpluses can always be deferred if there is only 1 continuing candidate + if hopefuls.len() <= 1 { + return true; } // Do not defer if this could affect a bulk exclusion @@ -697,9 +693,17 @@ where let total_excluded = state.total_votes_of(to_exclude.into_iter().map(|c| &state.candidates[c])); if total_surpluses >= &(&hopefuls[num_to_exclude].1.votes - &total_excluded) { return false; + } else { + return true; } } } + + // Do not defer if this could change the last 2 candidates + if total_surpluses >= &(&hopefuls[1].1.votes - &hopefuls[0].1.votes) { + return false; + } + return true; }