Complete ERS76 implementation and add test case
This commit is contained in:
parent
5024496f61
commit
9f1476da63
@ -20,7 +20,7 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV
|
|||||||
| [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. | | ✓ |
|
| [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. | | ✓ |
|
| [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. | | ✓ |
|
| [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. | | ✓ |
|
||||||
| • ERS76 | Former rules from the 1976 2nd edition. | [E6] | |
|
| • ERS76 | Former rules from the 1976 2nd edition. | [E6] | ✓ |
|
||||||
| • ERS73 | Former rules from the 1973 1st edition. | [E6] | |
|
| • ERS73 | Former rules from the 1973 1st edition. | [E6] | |
|
||||||
| 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. | |
|
| 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. | |
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ 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.
|
* [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. See the section on *Bulk exclusion* for further discussion.
|
* [E4] Bulk exclusion is not performed. See the section on *Bulk exclusion* for further discussion.
|
||||||
* [E5] The ‘mathematically eliminated by the sum of all ranked-choice votes comparison’ is not implemented.
|
* [E5] The ‘mathematically eliminated by the sum of all ranked-choice votes comparison’ is not implemented.
|
||||||
* [E6] 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).
|
* [E6] 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](validation.md).
|
For details of validation, see [validation.md](validation.md).
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ STV-counting software is frequently validated empirically by comparing the resul
|
|||||||
| PRSA 1977 | [*Proportional Representation Manual*](https://www.prsa.org.au/publicat.htm#p2) [example 1](https://www.prsa.org.au/utopiatc.pdf) | [Model result](https://www.prsa.org.au/example1.pdf) (official) | ✓ |
|
| PRSA 1977 | [*Proportional Representation Manual*](https://www.prsa.org.au/publicat.htm#p2) [example 1](https://www.prsa.org.au/utopiatc.pdf) | [Model result](https://www.prsa.org.au/example1.pdf) (official) | ✓ |
|
||||||
| ERS97 | [Reverse engineered ballot papers for the ERS97 model election](https://yingtongli.me/blog/2021/01/04/ers97.html) | [Model result](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/#sub-section-24) (official) | ✓ |
|
| ERS97 | [Reverse engineered ballot papers for the ERS97 model election](https://yingtongli.me/blog/2021/01/04/ers97.html) | [Model result](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/#sub-section-24) (official) | ✓ |
|
||||||
| ERS97 | [Joe Otten/eSTV ballot papers for the ERS97 model election](https://web.archive.org/web/20020606014623/http://estv.otten.co.uk/) | [Model result](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/#sub-section-24) (official) | ✓ |
|
| ERS97 | [Joe Otten/eSTV ballot papers for the ERS97 model election](https://web.archive.org/web/20020606014623/http://estv.otten.co.uk/) | [Model result](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/#sub-section-24) (official) | ✓ |
|
||||||
|
| ERS76 | Ballot papers adapted from Joe Otten/eSTV ERS97 papers | Model result (official) | ✓ |
|
||||||
|
|
||||||
# References
|
# References
|
||||||
|
|
||||||
|
@ -785,8 +785,14 @@ fn total_to_quota<N: Number>(mut total: N, seats: usize, opts: &STVOptions) -> N
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update vote required for election according to ERS97 rules
|
/// Update vote required for election according to ERS97/ERS76 rules
|
||||||
fn update_vre<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
fn update_vre<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
||||||
|
if opts.quota_mode == QuotaMode::ERS76 && state.num_excluded == 0 && (state.num_elected == 0 || state.candidates.values().all(|cc| !cc.finalised)) {
|
||||||
|
// ERS76 rules: Do not update VRE until a surplus is distributed or candidate is excluded
|
||||||
|
state.vote_required_election = state.quota.clone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut log = String::new();
|
let mut log = String::new();
|
||||||
|
|
||||||
// Calculate active vote
|
// Calculate active vote
|
||||||
@ -1023,8 +1029,11 @@ fn elect_hopefuls<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOption
|
|||||||
count_card.state = CandidateState::Elected;
|
count_card.state = CandidateState::Elected;
|
||||||
state.num_elected += 1;
|
state.num_elected += 1;
|
||||||
count_card.order_elected = state.num_elected as isize;
|
count_card.order_elected = state.num_elected as isize;
|
||||||
|
|
||||||
|
let elected_on_quota;
|
||||||
if cmp_quota_criterion(state.quota.as_ref().unwrap(), count_card, opts) {
|
if cmp_quota_criterion(state.quota.as_ref().unwrap(), count_card, opts) {
|
||||||
// Elected with a quota
|
// Elected with a quota
|
||||||
|
elected_on_quota = true;
|
||||||
state.logger.log_smart(
|
state.logger.log_smart(
|
||||||
"{} meets the quota and is elected.",
|
"{} meets the quota and is elected.",
|
||||||
"{} meet the quota and are elected.",
|
"{} meet the quota and are elected.",
|
||||||
@ -1032,6 +1041,7 @@ fn elect_hopefuls<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOption
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Elected with vote required
|
// Elected with vote required
|
||||||
|
elected_on_quota = false;
|
||||||
state.logger.log_smart(
|
state.logger.log_smart(
|
||||||
"{} meets the vote required and is elected.",
|
"{} meets the vote required and is elected.",
|
||||||
"{} meet the vote required and are elected.",
|
"{} meet the vote required and are elected.",
|
||||||
@ -1051,9 +1061,10 @@ fn elect_hopefuls<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOption
|
|||||||
cands_meeting_quota.remove(cands_meeting_quota.iter().position(|c| *c == candidate).unwrap());
|
cands_meeting_quota.remove(cands_meeting_quota.iter().position(|c| *c == candidate).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.quota_mode == QuotaMode::ERS97 {
|
if opts.quota_mode == QuotaMode::ERS97 || (opts.quota_mode == QuotaMode::ERS76 && elected_on_quota) {
|
||||||
// Vote required for election may have changed
|
// Vote required for election may have changed
|
||||||
// This is not performed in ERS76 - TODO: Check this
|
// ERS97: Check this after every elected candidate (cf. model election)
|
||||||
|
// ERS76: Check this after every candidate elected on a quota, but all at once for candidates elected on VRE (cf. model election)
|
||||||
calculate_quota(state, opts);
|
calculate_quota(state, opts);
|
||||||
|
|
||||||
// Repeat in case vote required for election has changed
|
// Repeat in case vote required for election has changed
|
||||||
|
1
tests/data/.~lock.ers76.ods#
Normal file
1
tests/data/.~lock.ers76.ods#
Normal file
@ -0,0 +1 @@
|
|||||||
|
Yingtong Li,runassudo,NEXUS.localdomain,09.08.2021 19:43,file:///home/runassudo/.config/libreoffice/4;
|
71
tests/data/ers76.blt
Normal file
71
tests/data/ers76.blt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# Comment: Ballot papers for the ERS76 model election - all votes - ERS76
|
||||||
|
# Source: Adapted by RunasSudo from ers97.blt (Joe Otten)
|
||||||
|
# Contributor: RunasSudo
|
||||||
|
11 6
|
||||||
|
8 1 2 0
|
||||||
|
3 1 3 0
|
||||||
|
14 1 4 0
|
||||||
|
34 1 5 0
|
||||||
|
1 1 2 0 # Changed from "1 6"
|
||||||
|
4 1 7 0
|
||||||
|
1 1 8 0
|
||||||
|
3 1 9 3 0 # One of these changed to "1 9 6"
|
||||||
|
3 1 9 4 0
|
||||||
|
3 1 9 6 0
|
||||||
|
3 1 9 7 0
|
||||||
|
4 1 9 8 0
|
||||||
|
9 1 9 0
|
||||||
|
4 1 10 3 0
|
||||||
|
5 1 10 4 0
|
||||||
|
3 1 10 6 0
|
||||||
|
3 1 10 7 0
|
||||||
|
6 1 10 8 0
|
||||||
|
10 1 10 0
|
||||||
|
1 1 11 5 0
|
||||||
|
1 1 11 0
|
||||||
|
11 1 0
|
||||||
|
105 2 0
|
||||||
|
91 3 0
|
||||||
|
90 4 0
|
||||||
|
81 5 0
|
||||||
|
64 6 0
|
||||||
|
11 7 6 0
|
||||||
|
36 7 8 0
|
||||||
|
12 7 0
|
||||||
|
55 8 0
|
||||||
|
3 9 3 0
|
||||||
|
2 9 4 0
|
||||||
|
2 9 5 3 0
|
||||||
|
1 9 5 4 0
|
||||||
|
15 9 5 0
|
||||||
|
2 9 6 0
|
||||||
|
2 9 8 0
|
||||||
|
2 10 3 0
|
||||||
|
2 10 4 0
|
||||||
|
2 10 5 6 0
|
||||||
|
1 10 5 7 6 0
|
||||||
|
2 10 5 8 0
|
||||||
|
11 10 5 0
|
||||||
|
1 10 6 0
|
||||||
|
1 10 7 4 0
|
||||||
|
1 10 8 0
|
||||||
|
1 10 0
|
||||||
|
2 11 2 0
|
||||||
|
4 11 3 0
|
||||||
|
1 11 4 0
|
||||||
|
5 11 7 8 0
|
||||||
|
10 11 8 0
|
||||||
|
1 11 0
|
||||||
|
0
|
||||||
|
"Smith"
|
||||||
|
"Duke"
|
||||||
|
"Prince"
|
||||||
|
"Freeman"
|
||||||
|
"Carpenter"
|
||||||
|
"Baron"
|
||||||
|
"Abbot"
|
||||||
|
"Vicar"
|
||||||
|
"Wright"
|
||||||
|
"Glazier"
|
||||||
|
"Monk"
|
||||||
|
"ERS Model - 1997 Edition"
|
15
tests/data/ers76.csv
Normal file
15
tests/data/ers76.csv
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Stage:,1,,2,,4,,6,,7,,8,
|
||||||
|
Comment:,First preferences,,Surplus of Smith,,Exclusion of Monk,,"Exclusion of Glazier, Wright",,Surplus of Carpenter,,Exclusion of Abbot,
|
||||||
|
Smith,134,EL,108,EL,108,EL,108,EL,108,EL,108,EL
|
||||||
|
Duke,105,H,106.89,H,108.89,EL,108.89,EL,108.89,EL,108.89,EL
|
||||||
|
Prince,91,H,91.63,H,95.63,H,102.1,H,104.1,EL,104.1,EL
|
||||||
|
Freeman,90,H,92.94,H,93.94,H,99.62,H,100.62,H,101.62,EL
|
||||||
|
Carpenter,81,H,88.14,H,88.35,H,122.35,EL,108,EL,108,EL
|
||||||
|
Baron,64,H,64,H,64,H,68.26,H,70.26,H,82.26,H
|
||||||
|
Abbot,59,H,59.84,H,64.84,H,67.1,H,68.1,H,2.1,EX
|
||||||
|
Vicar,55,H,55.21,H,65.21,H,70.31,H,72.31,H,113.31,EL
|
||||||
|
Wright,27,H,32.25,H,32.25,H,0,EX,0,EX,0,EX
|
||||||
|
Glazier,24,H,30.51,H,30.51,H,0,EX,0,EX,0,EX
|
||||||
|
Monk,23,H,23.42,H,0,EX,0,EX,0,EX,0,EX
|
||||||
|
Non-transferable,0,,0.17,,1.38,,6.37,,12.72,,24.72,
|
||||||
|
Votes required,108,,,,,,,,104.07,,96.09,
|
|
BIN
tests/data/ers76.ods
Normal file
BIN
tests/data/ers76.ods
Normal file
Binary file not shown.
@ -61,3 +61,24 @@ fn ers97_rational() {
|
|||||||
|
|
||||||
utils::read_validate_election::<Rational>("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, None, &["nt", "vre"]);
|
utils::read_validate_election::<Rational>("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, None, &["nt", "vre"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ers76_rational() {
|
||||||
|
let stv_opts = stv::STVOptionsBuilder::default()
|
||||||
|
.round_surplus_fractions(Some(2))
|
||||||
|
.round_values(Some(2))
|
||||||
|
.round_votes(Some(2))
|
||||||
|
.round_quota(Some(0))
|
||||||
|
.quota(stv::QuotaType::DroopExact)
|
||||||
|
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
|
||||||
|
.quota_mode(stv::QuotaMode::ERS76)
|
||||||
|
.surplus(stv::SurplusMethod::EG)
|
||||||
|
.transferable_only(true)
|
||||||
|
.exclusion(stv::ExclusionMethod::ByValue)
|
||||||
|
.early_bulk_elect(false)
|
||||||
|
.bulk_exclude(true)
|
||||||
|
.defer_surpluses(true)
|
||||||
|
.build().unwrap();
|
||||||
|
|
||||||
|
utils::read_validate_election::<Rational>("tests/data/ers76.csv", "tests/data/ers76.blt", stv_opts, None, &["nt", "vre"]);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user