STAR++ v0.2.2
C++ implementation of STAR voting
Loading...
Searching...
No Matches
calculator.h
1#ifndef CALCULATOR_H
2#define CALCULATOR_H
3
4// Shared Library Support
5#include "star/star_base_export.h"
6
7// Qt Includes
8#include <QObject>
9#include <QFlags>
10
11// Project Includes
12#include "star/election.h"
13#include "star/electionresult.h"
14#include "star/qualifierresult.h"
15
16namespace Star
17{
18
19// Forward Declarations
20class HeadToHeadResults;
21
22class STAR_BASE_EXPORT Calculator : public QObject
23{
24 Q_OBJECT
25//-Class Enums------------------------------------------------------------------------------------------------------
26public:
27 enum Option
28 {
29 NoOptions = 0x00,
30 AllowTrueTies = 0x01,
31 CondorcetProtocol = 0x02,
32 DefactoWinner = 0x04
33 };
34 Q_DECLARE_FLAGS(Options, Option);
35
36//-Class Variables------------------------------------------------------------------------------------------------------
37private:
38 // Logging - Intro
39 static inline const QString LOG_EVENT_INVALID_ELECTION = QStringLiteral("The provided election is invalid.");
40 static inline const QString LOG_EVENT_CALC_START = QStringLiteral("Calculating results of election - %1");
41 static inline const QString LOG_EVENT_INPUT_COUNTS = QStringLiteral("There are %1 candidates, %2 ballots, and %3 seats to fill.");
42 static inline const QString LOG_EVENT_INITAL_RAW_RANKINGS = QStringLiteral("Initial score rankings:");
43 static inline const QString LOG_EVENT_CALC_HEAD_TO_HEAD = QStringLiteral("Pre-calculating head-to-head matchup results...");
44
45 // Logging - Perform Runoff Qualifier
46 static inline const QString LOG_EVENT_QUALIFIER = QStringLiteral("Performing runoff qualifier to seed candidates for the runoff.");
47 static inline const QString LOG_EVENT_QUALIFIER_TOP = QStringLiteral("Trying to determine remaining %1 candidate(s) to advance between:");
48 static inline const QString LOG_EVENT_QUALIFIER_ADVANCE_CANDIDATES = QStringLiteral("Advancing candidate(s):");
49 static inline const QString LOG_EVENT_QUALIFIER_CUT_CANDIDATES = QStringLiteral("Cutting candidate(s):");
50 static inline const QString LOG_EVENT_QUALIFIER_NO_RANDOM = QStringLiteral("Random tiebreaker is disabled.");
51 static inline const QString LOG_EVENT_QUALIFIER_UNSUCCESSFUL = QStringLiteral("Unable to resolve scoring round tie to reach the target number of candidates.");
52 static inline const QString LOG_EVENT_QUALIFIER_RESULT = QStringLiteral(
53 "Qualifier Result:\n"
54 "First Seed: %1\n"
55 "Second Seed: %2\n"
56 "Simultaneous: %3\n"
57 "Overflow: {%4}\n"
58 );
59
60 // Logging - Defacto Winner Check
61 static inline const QString LOG_EVENT_DEFACTO_WINNER_CHECK = QStringLiteral(R"(Simulating runoff for potential defacto winner "%1" between:)");
62 static inline const QString LOG_EVENT_DEFACTO_WINNER_CHECK_WIN = QStringLiteral(R"(The first seed won against "%1".)");
63 static inline const QString LOG_EVENT_DEFACTO_WINNER_CHECK_FAIL = QStringLiteral(R"(The first seed lost to "%1".)");
64 static inline const QString LOG_EVENT_DEFACTO_WINNER_CHECK_SUCCESS = QStringLiteral("The first seed wins all simulated runoffs.");
65
66 // Logging - Runoff
67 static inline const QString LOG_EVENT_RUNOFF = QStringLiteral(R"(Observing runoff between "%1" and "%2".)");
68 static inline const QString LOG_EVENT_RUNOFF_HEAD_TO_HEAD_WINNER_CHECK = QStringLiteral("Checking for clear winner of head-to-head.");
69 static inline const QString LOG_EVENT_RUNOFF_TIE = QStringLiteral("The candidates in the runoff are tied in terms of preference.");
70 static inline const QString LOG_EVENT_RUNOFF_HIGHER_SCORE_CHECK = QStringLiteral("Checking for the candidate with the higher score.");
71 static inline const QString LOG_EVENT_RUNOFF_MORE_FIVE_STAR_CHECK = QStringLiteral("Checking for the candidate with more five star votes.");
72 static inline const QString LOG_EVENT_RUNOFF_CHOOSING_RANDOM_WINNER = QStringLiteral("Choosing runoff winner randomly.");
73 static inline const QString LOG_EVENT_RUNOFF_NO_RANDOM = QStringLiteral("Random tiebreaker is disabled, the runoff candidates remained tied.");
74 static inline const QString LOG_EVENT_RUNOFF_WINNER = QStringLiteral(R"(The runoff resulted in a win for: "%1")");
75 static inline const QString LOG_EVENT_RUNOFF_UNRESOLVED = QStringLiteral("The runoff tie could not be broken.");
76
77 // Logging - Ranking
78 static inline const QString LOG_EVENT_RANK_BY_SCORE = QStringLiteral("Ranking relevant candidates by score (%1)...");
79 static inline const QString LOG_EVENT_RANKINGS_SCORE = QStringLiteral("Score rankings:");
80 static inline const QString LOG_EVENT_RANK_BY_VOTES_OF_MAX_SCORE = QStringLiteral("Ranking relevant candidates by votes of max score (%1)...");
81 static inline const QString LOG_EVENT_RANKINGS_VOTES_OF_MAX_SCORE = QStringLiteral("Votes of Max Score rankings:");
82 static inline const QString LOG_EVENT_RANK_BY_HEAD_TO_HEAD_LOSSES = QStringLiteral("Ranking relevant candidates by head-to-head losses (%1)...");
83 static inline const QString LOG_EVENT_RANKINGS_HEAD_TO_HEAD_LOSSES = QStringLiteral("Head-to-head losses rankings:");
84 static inline const QString LOG_EVENT_RANK_BY_HEAD_TO_HEAD_PREFERENCES = QStringLiteral("Ranking relevant candidates by head-to-head preference count (%1)...");
85 static inline const QString LOG_EVENT_RANKINGS_HEAD_TO_HEAD_PREFERENCES = QStringLiteral("Head-to-head preference count rankings:");
86 static inline const QString LOG_EVENT_RANK_BY_HEAD_TO_HEAD_MARGIN = QStringLiteral("Ranking relevant candidates by head-to-head margin (%1)...");
87 static inline const QString LOG_EVENT_RANKINGS_HEAD_TO_HEAD_MARGIN = QStringLiteral("Head-to-head margin rankings:");
88
89 // Logging - Tiebreak
90 static inline const QString LOG_EVENT_BREAK_TIE_MOST_FIVE_STAR = QStringLiteral("Breaking %1-way tie according to most five star votes...");
91 static inline const QString LOG_EVENT_BREAK_TIE_HIGHEST_SCORE = QStringLiteral("Breaking %1-way tie according to highest score...");
92 static inline const QString LOG_EVENT_BREAK_TIE_RANDOM = QStringLiteral("Breaking %1-way tie randomly...");
93 static inline const QString LOG_EVENT_BREAK_RESULT = QStringLiteral("Tie Break Winner(s) - { %1 }");
94
95 // Logging - Main
96 static inline const QString LOG_EVENT_FILLING_SEAT = QStringLiteral("Filling seat %1...");
97 static inline const QString LOG_EVENT_DIRECT_SEAT_FILL = QStringLiteral("Only one candidate remains, seat can be filled directly.");
98 static inline const QString LOG_EVENT_RUNOFF_CANDIDATES = QStringLiteral(R"("%1" & "%2" advance to the runoff.)");
99 static inline const QString LOG_EVENT_NO_RUNOFF = QStringLiteral("The number of candidates could not be narrowed to two in order to perform the runoff.");
100 static inline const QString LOG_EVENT_DEFACTO_WINNER_SEAT_FILL = QStringLiteral(R"(Filling seat with defacto winner "%1")");
101 static inline const QString LOG_EVENT_PERFORM_PRIMARY_RUNOFF = QStringLiteral("Performing primary runoff...");
102
103 // Logging - Final Results
104 static inline const QString LOG_EVENT_FINAL_RESULTS = QStringLiteral(
105 "Final Results:\n"
106 "\n"
107 "Filled Seats:\n"
108 "%1"
109 "\n"
110 "Unresolved Candidates:\n"
111 "%2"
112 "\n"
113 "Unfilled Seats: %3\n"
114 );
115
116 // Logging - Finish
117 static inline const QString LOG_EVENT_CALC_FINISH = QStringLiteral("Calculation complete.");
118
119 // Logging - Lists
120 static inline const QString LIST_ITEM_CANDIDATE_TOTAL_SCORE = QStringLiteral("\t- \"%1\" <%2>");
121 static inline const QString LIST_ITEM_RANK = QStringLiteral("\t%1) { \"%2\" } <%3>");
122 static inline const QString LIST_ITEM_SEAT = QStringLiteral("\t%1) \"%2\"");
123 static inline const QString LIST_ITEM_UNRESOLVED = QStringLiteral("\t- \"%1\"");
124 static inline const QString LIST_ITEM_NONE = QStringLiteral("\t *NONE*");
125
126//-Instance Variables--------------------------------------------------------------------------------------------------
127private:
128 const Election* mElection;
129 std::unique_ptr<HeadToHeadResults> mHeadToHeadResults;
130 Options mOptions;
131
132//-Constructor---------------------------------------------------------------------------------------------------------
133public:
134 Calculator(const Election* election = nullptr);
135
136//-Destructor---------------------------------------------------------------------------------------------------------
137public:
138 /* Required for std::unique_ptr<HeadToHeadResults> member to work correctly
139 *
140 * See: https://stackoverflow.com/questions/33212686/how-to-use-unique-ptr-with-forward-declared-type
141 */
143
144//-Instance Functions-------------------------------------------------------------------------------------------------
145private:
146 // Main steps
147 QualifierResult performRunoffQualifier(const QList<Rank>& scoreRankings) const;
148 bool checkForDefactoWinner(const QString& firstSeed, const QSet<QString>& overflow) const;
149 QString performRunoff(std::pair<QString, QString> candidates) const;
150
151 // Utility
152 QList<Rank> rankByScore(const QSet<QString>& candidates, Rank::Order order) const;
153 QList<Rank> rankByVotesOfMaxScore(const QSet<QString>& candidates, Rank::Order order) const;
154 QList<Rank> rankByHeadToHeadLosses(const QSet<QString>& candidates, const HeadToHeadResults* hth, Rank::Order order) const;
155 QList<Rank> rankByHeadToHeadPreferences(const QSet<QString>& candidates, const HeadToHeadResults* hth, Rank::Order order) const;
156 QList<Rank> rankByHeadToHeadMargin(const QSet<QString>& candidates, const HeadToHeadResults* hth, Rank::Order order) const;
157
158 QSet<QString> rankBasedTiebreak(const QList<Rank>& rankings, const QString& note) const;
159 QSet<QString> breakTieMostFiveStar(const QSet<QString>& candidates) const;
160 QSet<QString> breakTieHighestScore(const QSet<QString>& candidates) const;
161 QString breakTieRandom(const QSet<QString>& candidates) const;
162
163 // Logging
164 QString createCandidateGeneralSetString(const QSet<QString>& candidates) const;
165 QString createCandidateToalScoreSetString(const QSet<QString>& candidates) const;
166 QString createCandidateRankListString(const QList<Rank>& ranks) const;
167 void logQualifierResult(const QualifierResult& result) const;
168 void logElectionResults(const ElectionResult& results) const;
169
170public:
171 const Election* election() const;
172 Options options() const;
173
174 void setElection(const Election* election);
175 void setOptions(Options options);
176
177 ElectionResult calculateResult();
178
179//-Signals & Slots-------------------------------------------------------------------------------------------------
180signals:
181 void calculationDetail(const QString& detail) const; // clazy:exclude=const-signal-or-slot
182};
183Q_DECLARE_OPERATORS_FOR_FLAGS(Calculator::Options)
184
185}
186
187#endif // CALCULATOR_H
The Calculator class provides the means to determine the result of an election.
Definition calculator.h:23
void calculationDetail(const QString &detail) const
QFlags< Option > Options
Definition calculator.h:34
Option
Definition calculator.h:28
The Election class represents a collection of candidates and ballots.
Definition election.h:19
The ElectionResult class holds the outcome of an election.
Definition electionresult.h:15
The QualifierResult class holds the outcome of a runoff qualifier.
Definition qualifierresult.h:19
The Star namespace is the main namespace through which all functionality of the STAR++ library is acc...
Definition calculator.cpp:21
Order
Definition rank.h:15