From 7f160903951732e205d3a202bc18b092b4a7778d Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Mon, 19 Jul 2021 18:35:23 +1000 Subject: [PATCH] Fix crash on attempting segmented exclusion of candidate with no votes --- src/stv/gregory.rs | 86 ++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 37 deletions(-) diff --git a/src/stv/gregory.rs b/src/stv/gregory.rs index 6f604dd..45a0b50 100644 --- a/src/stv/gregory.rs +++ b/src/stv/gregory.rs @@ -391,42 +391,49 @@ where } ExclusionMethod::ByValue => { // Exclude by value - let max_value = excluded_candidates.iter() - .map(|c| state.candidates[c].parcels.iter() - .map(|p| p.iter().map(|v| &v.value / &v.ballot.orig_value).max().unwrap()) - .max().unwrap()) - .max().unwrap(); + let excluded_with_votes: Vec<&&Candidate> = excluded_candidates.iter().filter(|c| !state.candidates[*c].parcels.is_empty()).collect(); - votes_remain = false; - - for excluded_candidate in excluded_candidates.iter() { - let count_card = state.candidates.get_mut(excluded_candidate).unwrap(); + if excluded_with_votes.is_empty() { + votes_remain = false; + } else { + // If candidates to exclude still having votes, select only those with the greatest value + let max_value = excluded_with_votes.iter() + .map(|c| state.candidates[*c].parcels.iter() + .map(|p| p.iter().map(|v| &v.value / &v.ballot.orig_value).max().unwrap()) + .max().unwrap()) + .max().unwrap(); - // Filter out just those votes with max_value - let mut remaining_votes = Vec::new(); + votes_remain = false; - let cand_votes = count_card.parcels.concat(); - - let mut votes_transferred = N::new(); - for vote in cand_votes.into_iter() { - if &vote.value / &vote.ballot.orig_value == max_value { - votes_transferred += &vote.value; - votes.push(vote); - } else { - remaining_votes.push(vote); + for excluded_candidate in excluded_with_votes.iter() { + let count_card = state.candidates.get_mut(*excluded_candidate).unwrap(); + + // Filter out just those votes with max_value + let mut remaining_votes = Vec::new(); + + let cand_votes = count_card.parcels.concat(); + + let mut votes_transferred = N::new(); + for vote in cand_votes.into_iter() { + if &vote.value / &vote.ballot.orig_value == max_value { + votes_transferred += &vote.value; + votes.push(vote); + } else { + remaining_votes.push(vote); + } } + + if !remaining_votes.is_empty() { + votes_remain = true; + } + + // Leave remaining votes with candidate (as one parcel) + count_card.parcels = vec![remaining_votes]; + + // Update votes + checksum -= &votes_transferred; + count_card.transfer(&-votes_transferred); } - - if !remaining_votes.is_empty() { - votes_remain = true; - } - - // Leave remaining votes with candidate (as one parcel) - count_card.parcels = vec![remaining_votes]; - - // Update votes - checksum -= &votes_transferred; - count_card.transfer(&-votes_transferred); } } ExclusionMethod::ParcelsByOrder => { @@ -436,13 +443,18 @@ where } let count_card = state.candidates.get_mut(excluded_candidates[0]).unwrap(); - votes = count_card.parcels.remove(0); - votes_remain = !count_card.parcels.is_empty(); - // Update votes - let votes_transferred = votes.iter().fold(N::new(), |acc, v| acc + &v.value); - checksum -= &votes_transferred; - count_card.transfer(&-votes_transferred); + if count_card.parcels.is_empty() { + votes_remain = false; + } else { + votes = count_card.parcels.remove(0); + votes_remain = !count_card.parcels.is_empty(); + + // Update votes + let votes_transferred = votes.iter().fold(N::new(), |acc, v| acc + &v.value); + checksum -= &votes_transferred; + count_card.transfer(&-votes_transferred); + } } _ => panic!() }