Skip to content

Commit

Permalink
Each CVR must keep track of whether it's been fully ranked or not
Browse files Browse the repository at this point in the history
  • Loading branch information
artoonie committed Jul 1, 2024
1 parent de4d00b commit a595e69
Show file tree
Hide file tree
Showing 108 changed files with 534 additions and 429 deletions.
16 changes: 14 additions & 2 deletions src/main/java/network/brightspots/rcv/CastVoteRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class CastVoteRecord {
private final String precinct;
// which precinct portion this ballot came from
private final String precinctPortion;
// is the last-used ranking the last-allowed ranking in the CVR?
private final boolean usesLastAllowedRanking;
// records winners to whom some fraction of this vote has been allocated
private final Map<String, BigDecimal> winnerToFractionalValue = new HashMap<>();
// If CVR CDF output is enabled, we store the necessary info here: for each round, the list of
Expand Down Expand Up @@ -65,8 +67,10 @@ class CastVoteRecord {
String suppliedId,
String precinct,
String precinctPortion,
boolean usesLastAllowedRanking,
List<Pair<Integer, String>> rankings) {
this(contestId, tabulatorId, batchId, suppliedId, null, precinct, precinctPortion, rankings);
this(contestId, tabulatorId, batchId, suppliedId, null, precinct, precinctPortion,
usesLastAllowedRanking, rankings);
}

CastVoteRecord(
Expand All @@ -77,6 +81,7 @@ class CastVoteRecord {
String computedId,
String precinct,
String precinctPortion,
boolean usesLastAllowedRanking,
List<Pair<Integer, String>> rankings) {
this.contestId = contestId;
this.tabulatorId = tabulatorId;
Expand All @@ -85,6 +90,7 @@ class CastVoteRecord {
this.computedId = computedId;
this.precinct = precinct;
this.precinctPortion = precinctPortion;
this.usesLastAllowedRanking = usesLastAllowedRanking;
this.candidateRankings = new CandidateRankingsList(rankings);
}

Expand All @@ -93,8 +99,10 @@ class CastVoteRecord {
String suppliedId,
String precinct,
String batchId,
boolean usesLastAllowedRanking,
List<Pair<Integer, String>> rankings) {
this(null, null, batchId, suppliedId, computedId, precinct, null, rankings);
this(null, null, batchId, suppliedId, computedId, precinct, null,
usesLastAllowedRanking, rankings);
}

String getContestId() {
Expand All @@ -116,6 +124,10 @@ String getPrecinctPortion() {
return precinctPortion;
}

boolean doesUseLastAllowedRanking() {
return usesLastAllowedRanking;
}

String getId() {
return suppliedId != null ? suppliedId : computedId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ void readCastVoteRecords(List<CastVoteRecord> castVoteRecords)
}
}
// create the cast vote record
boolean usesLastAllowedRanking = !isRankingAllowed(rankings.size() + 1, null);
CastVoteRecord castVoteRecord =
new CastVoteRecord(
source.getContestId(),
Expand All @@ -110,6 +111,7 @@ void readCastVoteRecords(List<CastVoteRecord> castVoteRecords)
cvrData[CvrColumnField.BallotID.ordinal()],
cvrData[CvrColumnField.PrecinctID.ordinal()],
null,
usesLastAllowedRanking,
rankings);

castVoteRecords.add(castVoteRecord);
Expand Down
18 changes: 15 additions & 3 deletions src/main/java/network/brightspots/rcv/CommonDataFormatReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,14 @@ void parseXml(List<CastVoteRecord> castVoteRecords) throws CvrParseException, IO

String computedCastVoteRecordId = String.format("%s(%d)", fileName, ++cvrIndex);
// create the new CastVoteRecord
boolean usesLastAllowedRanking = isRankingAllowed(rankings.size() + 1, null);
CastVoteRecord newRecord = new CastVoteRecord(
computedCastVoteRecordId, cvr.UniqueId, precinctId, cvr.BatchSequenceId, rankings);
computedCastVoteRecordId,
cvr.UniqueId,
precinctId,
cvr.BatchSequenceId,
usesLastAllowedRanking,
rankings);
castVoteRecords.add(newRecord);

// provide some user feedback on the CVR count
Expand Down Expand Up @@ -527,8 +533,14 @@ void parseJson(List<CastVoteRecord> castVoteRecords) throws CvrParseException {
String batchId = (String) cvr.get("BatchSequenceId");
String computedCastVoteRecordId = String.format("%s(%d)", fileName, ++cvrIndex);
// create the new CastVoteRecord
CastVoteRecord newRecord =
new CastVoteRecord(computedCastVoteRecordId, ballotId, precinctId, batchId, rankings);
boolean usesLastAllowedRanking = !isRankingAllowed(rankings.size() + 1, null);
CastVoteRecord newRecord = new CastVoteRecord(
computedCastVoteRecordId,
ballotId,
precinctId,
batchId,
usesLastAllowedRanking,
rankings);
castVoteRecords.add(newRecord);
// provide some user feedback on the CVR count
if (castVoteRecords.size() % 50000 == 0) {
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/network/brightspots/rcv/CsvCvrReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,14 @@ void readCastVoteRecords(List<CastVoteRecord> castVoteRecords)
}

// create the new CastVoteRecord
CastVoteRecord newCvr =
new CastVoteRecord(
Integer.toString(index), "no supplied ID", "no precinct", "no batch ID", rankings);
boolean usesLastAllowedRanking = !isRankingAllowed(rankings.size() + 1, null);
CastVoteRecord newCvr = new CastVoteRecord(
Integer.toString(index),
"no supplied ID",
"no precinct",
"no batch ID",
usesLastAllowedRanking,
rankings);
castVoteRecords.add(newCvr);
}
} catch (IOException exception) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/network/brightspots/rcv/DominionCvrReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,10 @@ private int parseCvrFile(
rankings.add(ranking);
}
// create the new cvr
boolean usesLastAllowedRanking = !isRankingAllowed(rankings.size() + 1, contestId);
CastVoteRecord newCvr =
new CastVoteRecord(contestId, tabulatorId, batchId, suppliedId,
computedId, precinct, precinctPortion, rankings);
computedId, precinct, precinctPortion, usesLastAllowedRanking, rankings);
castVoteRecords.add(newCvr);
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/network/brightspots/rcv/HartCvrReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ private void readCastVoteRecord(List<CastVoteRecord> castVoteRecords, Path path)
}
}

boolean usesLastAllowedRanking = !isRankingAllowed(rankings.size() + 1, null);
CastVoteRecord cvr =
new CastVoteRecord(
contest.Id,
Expand All @@ -135,6 +136,7 @@ private void readCastVoteRecord(List<CastVoteRecord> castVoteRecords, Path path)
xmlCvr.CvrGuid,
xmlCvr.PrecinctSplit.Name,
xmlCvr.PrecinctSplit.Id,
usesLastAllowedRanking,
rankings);
castVoteRecords.add(cvr);

Expand Down
8 changes: 4 additions & 4 deletions src/main/java/network/brightspots/rcv/ResultsWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -479,14 +479,14 @@ private List<Pair<String, StatusForRound>> getStatusesToPrintInCsv(RoundTallies
RoundTally lastRound = roundTallies.get(numRounds);
BigDecimal numFullyRankedLastRound = lastRound.getBallotStatusTally(
StatusForRound.EXHAUSTED_CHOICE_FULLY_RANKED);
if (config.isMaxRankingsSetToMaximum() && !numFullyRankedLastRound.equals(BigDecimal.ZERO)) {
if (config.isMaxRankingsSetToMaximum() && numFullyRankedLastRound.equals(BigDecimal.ZERO)) {
statusesToPrint.add(new Pair<>("Exhausted Choices",
StatusForRound.EXHAUSTED_CHOICE_PARTIALLY_RANKED));
} else {
statusesToPrint.add(new Pair<>("Exhausted Choices (Fully Ranked)",
StatusForRound.EXHAUSTED_CHOICE_FULLY_RANKED));
statusesToPrint.add(new Pair<>("Exhausted Choices (Partially Ranked)",
StatusForRound.EXHAUSTED_CHOICE_PARTIALLY_RANKED));
} else {
statusesToPrint.add(new Pair<>("Exhausted Choices",
StatusForRound.EXHAUSTED_CHOICE_PARTIALLY_RANKED));
}

return statusesToPrint;
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/network/brightspots/rcv/StreamingCvrReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -216,11 +216,13 @@ private void endCvr() {
Logger.auditable("[Raw Data]: " + currentCvrData.toString());

// create new cast vote record
boolean usesLastAllowedRanking = !isRankingAllowed(currentRankings.size() + 1, null);
CastVoteRecord newRecord = new CastVoteRecord(
computedCastVoteRecordId,
currentSuppliedCvrId,
currentPrecinct,
currentBatch,
usesLastAllowedRanking,
currentRankings);
cvrList.add(newRecord);

Expand Down
18 changes: 8 additions & 10 deletions src/main/java/network/brightspots/rcv/Tabulator.java
Original file line number Diff line number Diff line change
Expand Up @@ -1138,8 +1138,10 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
if (rank == cvr.candidateRankings.maxRankingNumber()) {
// If the final ranking is an overvote, even if we're trying to skip to the next rank,
// we consider this inactive by exhausted choices -- not an overvote.
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.EXHAUSTED_CHOICE_FULLY_RANKED, "");
StatusForRound status = cvr.doesUseLastAllowedRanking()
? StatusForRound.EXHAUSTED_CHOICE_FULLY_RANKED
: StatusForRound.EXHAUSTED_CHOICE_PARTIALLY_RANKED;
recordSelectionForCastVoteRecord(cvr, roundTally, null, status, "");
}
continue;
}
Expand Down Expand Up @@ -1182,14 +1184,10 @@ && isCandidateContinuing(cvr.getCurrentRecipientOfVote())) {
int maxAllowedRanking = config.isMaxRankingsSetToMaximum()
? config.getNumDeclaredCandidates()
: config.getMaxRankingsAllowedWhenNotSetToMaximum();
if (config.getMaxSkippedRanksAllowed() != Integer.MAX_VALUE
&& maxAllowedRanking - rank > config.getMaxSkippedRanksAllowed()) {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.EXHAUSTED_CHOICE_PARTIALLY_RANKED, "");
} else {
recordSelectionForCastVoteRecord(
cvr, roundTally, null, StatusForRound.EXHAUSTED_CHOICE_FULLY_RANKED, "");
}
StatusForRound status = cvr.doesUseLastAllowedRanking()
? StatusForRound.EXHAUSTED_CHOICE_FULLY_RANKED
: StatusForRound.EXHAUSTED_CHOICE_PARTIALLY_RANKED;
recordSelectionForCastVoteRecord(cvr, roundTally, null, status, "");
}
} // end looping over the rankings within one ballot
} // end looping over all ballots
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@ Current Round Threshold,39657,,,39638,,,39635,,,39633,,,39627,,,39622,,,39614,,,
Inactive Ballots by Overvotes,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Skipped Rankings,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Repeated Rankings,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices (Fully Ranked),0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices (Partially Ranked),0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots Total,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,6 @@ Current Round Threshold,39657,,,39638,,,39635,,,39633,,,39627,,,39622,,,39614,,,
Inactive Ballots by Overvotes,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Skipped Rankings,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Repeated Rankings,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices (Fully Ranked),0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices (Partially Ranked),0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots Total,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ Current Round Threshold,39657,,,39638,,,39635,,,39633,,,39627,,,39622,,,39614,,,
Inactive Ballots by Overvotes,103,,1,104,,0,104,,0,104,,0,104,,0,104,,0,104,,0,104,,0,104,,1,105,,1,106,,1,107,,0,107,,0,107,,0,107,,0,107,,0,107,,0,107,,0,107,,0,107,,0,107,,1,108,,1,109,,1,110,,0,110,,0,110,,1,111,,1,112,,2,114,,1,115,,2,117,,6,123,,17,140,,0
Inactive Ballots by Skipped Rankings,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0,47,,0
Inactive Ballots by Repeated Rankings,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0,0,,0
Inactive Ballots by Exhausted Choices,0,,18,18,,6,24,,4,28,,10,38,,6,44,,12,56,,7,63,,15,78,,17,95,,14,109,,17,126,,26,152,,6,158,,13,171,,30,201,,8,209,,6,215,,44,259,,7,266,,10,276,,9,285,,22,307,,108,415,,42,457,,26,483,,202,685,,33,718,,195,913,,63,976,,196,1172,,559,1731,,1981,3712,,0
Inactive Ballots by Exhausted Choices (Fully Ranked),0,,17,17,,0,17,,1,18,,1,19,,3,22,,4,26,,3,29,,2,31,,9,40,,12,52,,10,62,,7,69,,13,82,,22,104,,17,121,,19,140,,15,155,,27,182,,40,222,,40,262,,70,332,,92,424,,133,557,,88,645,,90,735,,272,1007,,303,1310,,227,1537,,217,1754,,479,2233,,1238,3471,,6537,10008,,0
Inactive Ballots by Exhausted Choices (Partially Ranked),0,,19,19,,6,25,,4,29,,10,39,,7,46,,13,59,,9,68,,15,83,,23,106,,16,122,,22,144,,30,174,,9,183,,18,201,,34,235,,10,245,,10,255,,45,300,,14,314,,16,330,,28,358,,31,389,,136,525,,54,579,,31,610,,236,846,,66,912,,229,1141,,82,1223,,252,1475,,727,2202,,3271,5473,,0
Inactive Ballots Total,150,,37,187,,6,193,,5,198,,11,209,,10,219,,17,236,,12,248,,17,265,,33,298,,29,327,,33,360,,37,397,,22,419,,40,459,,51,510,,29,539,,25,564,,72,636,,54,690,,56,746,,99,845,,124,969,,270,1239,,142,1381,,121,1502,,509,2011,,370,2381,,458,2839,,300,3139,,733,3872,,1971,5843,,9825,15668,,0
Loading

0 comments on commit a595e69

Please sign in to comment.