Update documentation

This commit is contained in:
RunasSudo 2021-06-16 17:20:29 +10:00
parent 4ebb6474fd
commit 8829fa5a7b
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
10 changed files with 126 additions and 5 deletions

View File

@ -15,6 +15,7 @@ OpenTally accepts data in the [BLT file format](https://yingtongli.me/git/OpenTa
* weighted inclusive Gregory STV (e.g. [Scottish STV](https://www.legislation.gov.uk/ssi/2011/399/schedule/1/made)) * weighted inclusive Gregory STV (e.g. [Scottish STV](https://www.legislation.gov.uk/ssi/2011/399/schedule/1/made))
* unweighted inclusive Gregory STV (e.g. [Australian Senate STV](https://www.legislation.gov.au/Details/C2020C00400/Html/Text#_Toc59107700)) * unweighted inclusive Gregory STV (e.g. [Australian Senate STV](https://www.legislation.gov.au/Details/C2020C00400/Html/Text#_Toc59107700))
* exclusive Gregory STV (e.g. [PRSA 1977](https://www.prsa.org.au/rule1977.htm) and [ERS97](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/)) * exclusive Gregory STV (e.g. [PRSA 1977](https://www.prsa.org.au/rule1977.htm) and [ERS97](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/))
* [Meek STV](http://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf) – with [tree-packed ballots](http://www.votingmatters.org.uk/ISSUE21/I21P1.pdf) for efficient computation
OpenTally is highly customisable, including options for: OpenTally is highly customisable, including options for:

View File

@ -6,6 +6,7 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV
* *Recommended WIGM*: A recommended set of simple STV rules designed for computer counting, using the weighted inclusive Gregory method and rational arithmetic. * *Recommended WIGM*: A recommended set of simple STV rules designed for computer counting, using the weighted inclusive Gregory method and rational arithmetic.
* *Scottish STV*: Rules from the [*Scottish Local Government Elections Order 2011*](https://www.legislation.gov.uk/ssi/2011/399/schedule/1/made), using the weighted inclusive Gregory method. Validated against the [2007 Scottish local government election result for Linn ward](https://web.archive.org/web/20121004213938/http://www.glasgow.gov.uk/en/YourCouncil/Elections_Voting/Election_Results/ElectionScotland2007/LGWardResults.htm?ward=1&wardname=1%20-%20Linn). * *Scottish STV*: Rules from the [*Scottish Local Government Elections Order 2011*](https://www.legislation.gov.uk/ssi/2011/399/schedule/1/made), using the weighted inclusive Gregory method. Validated against the [2007 Scottish local government election result for Linn ward](https://web.archive.org/web/20121004213938/http://www.glasgow.gov.uk/en/YourCouncil/Elections_Voting/Election_Results/ElectionScotland2007/LGWardResults.htm?ward=1&wardname=1%20-%20Linn).
* [*Meek STV*](http://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf): Advanced STV rules designed for computer counting, recognised by the Proportional Representation Society of Australia (Victoria–Tasmania) as the superior STV system. Validated against the [Hill–Wichmann–Woodall implementation](https://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf) for the ERS97 model election (see below).
* *Australian Senate STV*: Rules from the [*Commonwealth Electoral Act 1918*](https://www.legislation.gov.au/Details/C2020C00400/Html/Text#_Toc59107700), using the unweighted inclusive Gregory method. Validated against the [2019 Australian Senate election result for Tasmania](https://results.aec.gov.au/24310/Website/SenateDownloadsMenu-24310-Csv.htm). * *Australian Senate STV*: Rules from the [*Commonwealth Electoral Act 1918*](https://www.legislation.gov.au/Details/C2020C00400/Html/Text#_Toc59107700), using the unweighted inclusive Gregory method. Validated against the [2019 Australian Senate election result for Tasmania](https://results.aec.gov.au/24310/Website/SenateDownloadsMenu-24310-Csv.htm).
* [*PRSA 1977*](https://www.prsa.org.au/rule1977.htm): Simple rules designed for hand counting, using the exclusive Gregory method, with counting automatically performed in thousandths of a vote. Validated against [example 1](https://www.prsa.org.au/example1.pdf) of the PRSA's [*Proportional Representation Manual*](https://www.prsa.org.au/publicat.htm#p2). * [*PRSA 1977*](https://www.prsa.org.au/rule1977.htm): Simple rules designed for hand counting, using the exclusive Gregory method, with counting automatically performed in thousandths of a vote. Validated against [example 1](https://www.prsa.org.au/example1.pdf) of the PRSA's [*Proportional Representation Manual*](https://www.prsa.org.au/publicat.htm#p2).
* [*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. Validated against the ERS97 [model election](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). * [*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. Validated against the ERS97 [model election](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).
@ -40,6 +41,8 @@ This option allows you to specify whether the votes required for election can ch
* *Static quota*: The quota is calculated once after all first-preference votes are allocated, and remains constant throughout the count. * *Static quota*: The quota is calculated once after all first-preference votes are allocated, and remains constant throughout the count.
* *Static with ERS97 rules*: The quota is static, but candidates may be elected if their vote exceeds (or equals, according to the *Quota criterion*) the total active vote, divided by (*S* + 1) (or *S*, according to the *Quota* option). * *Static with ERS97 rules*: The quota is static, but candidates may be elected if their vote exceeds (or equals, according to the *Quota criterion*) the total active vote, divided by (*S* + 1) (or *S*, according to the *Quota* option).
When *Surplus method* is set to *Meek method*, this setting is ignored, and the progressively reducing quota of the Meek method is instead applied.
## STV variants ## STV variants
### Surplus order (--surplus-order) ### Surplus order (--surplus-order)
@ -56,7 +59,7 @@ Some STV counting rules provide, for example, that ‘no surplus shall be transf
This dropdown allows you to select how ballots are transferred during surplus transfers. The recommended methods are: This dropdown allows you to select how ballots are transferred during surplus transfers. The recommended methods are:
* *Weighted inclusive Gregory* (default): During surplus transfers, all applicable ballot papers of the transferring candidate are examined. Transfers are weighted according to the weights of the ballot papers. * *Weighted inclusive Gregory* (default): During surplus transfers, all applicable ballot papers of the transferring candidate are examined. Transfers are weighted according to the weights of the ballot papers.
* *Meek STV*: Transfers are computed as described at <http://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf>. * *Meek method*: Transfers are computed as described at <http://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf>.
Other methods are supported, but not recommended: Other methods are supported, but not recommended:
@ -76,6 +79,8 @@ Other surplus transfer methods, such as non-fractional transfers (e.g. random sa
* *Exclude by parcel (by order)*: When excluding a candidate, transfer their ballot papers one parcel at a time, in the order each was received. Each parcel forms a separate stage, i.e. if a transfer allows another candidate to meet the quota criterion, no further papers are transferred to that candidate. This option cannot be combined with bulk exclusion. * *Exclude by parcel (by order)*: When excluding a candidate, transfer their ballot papers one parcel at a time, in the order each was received. Each parcel forms a separate stage, i.e. if a transfer allows another candidate to meet the quota criterion, no further papers are transferred to that candidate. This option cannot be combined with bulk exclusion.
* *Exclude by value*: When excluding candidate(s), transfer their ballot papers in descending order of accumulated transfer value. Each transfer of all ballots of a certain transfer value forms a separate stage. * *Exclude by value*: When excluding candidate(s), transfer their ballot papers in descending order of accumulated transfer value. Each transfer of all ballots of a certain transfer value forms a separate stage.
When *Surplus method* is set to *Meek method*, this setting is ignored, and the Meek method is instead applied.
### Ties (-t/--ties) ### Ties (-t/--ties)
This dropdown allows you to select how ties (in surplus transfer or exclusion) are broken. The options are: This dropdown allows you to select how ties (in surplus transfer or exclusion) are broken. The options are:

View File

@ -23,10 +23,15 @@ use std::collections::HashMap;
/// An election to be counted /// An election to be counted
pub struct Election<N> { pub struct Election<N> {
/// Name of the election
pub name: String, pub name: String,
/// Number of candidates to be elected
pub seats: usize, pub seats: usize,
/// [Vec] of [Candidate]s in the election
pub candidates: Vec<Candidate>, pub candidates: Vec<Candidate>,
/// Indexes of withdrawn candidates
pub withdrawn_candidates: Vec<usize>, pub withdrawn_candidates: Vec<usize>,
/// [Vec] of [Ballot]s cast in the election
pub ballots: Vec<Ballot<N>>, pub ballots: Vec<Ballot<N>>,
} }
@ -125,32 +130,52 @@ impl<N: Number> Election<N> {
/// A candidate in an [Election] /// A candidate in an [Election]
#[derive(PartialEq, Eq, Hash)] #[derive(PartialEq, Eq, Hash)]
pub struct Candidate { pub struct Candidate {
/// Name of the candidate
pub name: String, pub name: String,
} }
/// The current state of counting an [Election] /// The current state of counting an [Election]
//#[derive(Clone)] //#[derive(Clone)]
pub struct CountState<'a, N: Number> { pub struct CountState<'a, N: Number> {
/// Pointer to the [Election] being counted
pub election: &'a Election<N>, pub election: &'a Election<N>,
/// [HashMap] of [CountCard]s for each [Candidate] in the election
pub candidates: HashMap<&'a Candidate, CountCard<'a, N>>, pub candidates: HashMap<&'a Candidate, CountCard<'a, N>>,
/// [CountCard] representing the exhausted pile
pub exhausted: CountCard<'a, N>, pub exhausted: CountCard<'a, N>,
/// [CountCard] representing loss by fraction
pub loss_fraction: CountCard<'a, N>, pub loss_fraction: CountCard<'a, N>,
/// [crate::stv::meek::BallotTree] for Meek STV
pub ballot_tree: Option<crate::stv::meek::BallotTree<'a, N>>, pub ballot_tree: Option<crate::stv::meek::BallotTree<'a, N>>,
/// Values used to break ties, based on forwards tie-breaking
pub forwards_tiebreak: Option<HashMap<&'a Candidate, usize>>, pub forwards_tiebreak: Option<HashMap<&'a Candidate, usize>>,
/// Values used to break ties, based on backwards tie-breaking
pub backwards_tiebreak: Option<HashMap<&'a Candidate, usize>>, pub backwards_tiebreak: Option<HashMap<&'a Candidate, usize>>,
/// [SHARandom] for random tie-breaking
pub random: Option<SHARandom<'a>>, pub random: Option<SHARandom<'a>>,
/// Quota for election
pub quota: Option<N>, pub quota: Option<N>,
/// Vote required for election
///
/// With a static quota, this is equal to the quota. With ERS97 rules, this may vary from the quota.
pub vote_required_election: Option<N>, pub vote_required_election: Option<N>,
/// Number of candidates who have been declared elected
pub num_elected: usize, pub num_elected: usize,
/// Number of candidates who have been declared excluded
pub num_excluded: usize, pub num_excluded: usize,
/// The type of stage being counted
///
/// For example, "Surplus of", "Exclusion of"
pub kind: Option<&'a str>, pub kind: Option<&'a str>,
/// The description of the stage being counted, excluding [CountState::kind]
pub title: String, pub title: String,
/// [Logger] for this stage of the count
pub logger: Logger<'a>, pub logger: Logger<'a>,
} }
@ -199,7 +224,11 @@ impl<'a, N: Number> CountState<'a, N> {
/// Represents either a reference to a [CountState] or a clone /// Represents either a reference to a [CountState] or a clone
#[allow(dead_code)] #[allow(dead_code)]
pub enum CountStateOrRef<'a, N: Number> { pub enum CountStateOrRef<'a, N: Number> {
State(CountState<'a, N>), // NYI: May be used e.g. for tie-breaking or rollback-based constraints /// Cloned [CountState]
///
/// Currently unused/unimplemented, but may be used in future for rollback-based constraints
State(CountState<'a, N>),
/// Reference to a [CountState]
Ref(&'a CountState<'a, N>), Ref(&'a CountState<'a, N>),
} }
@ -220,22 +249,33 @@ impl<'a, N: Number> CountStateOrRef<'a, N> {
/// Result of a stage of counting /// Result of a stage of counting
pub struct StageResult<'a, N: Number> { pub struct StageResult<'a, N: Number> {
/// See [CountState::kind]
pub kind: Option<&'a str>, pub kind: Option<&'a str>,
/// See [CountState::title]
pub title: &'a String, pub title: &'a String,
/// Detailed logs of this stage, rendered from [CountState::logger]
pub logs: Vec<String>, pub logs: Vec<String>,
/// Reference to the [CountState] or cloned [CountState] of this stage
pub state: CountStateOrRef<'a, N>, pub state: CountStateOrRef<'a, N>,
} }
/// Current state of a [Candidate] during an election count /// Current state of a [Candidate] during an election count
#[derive(Clone)] #[derive(Clone)]
pub struct CountCard<'a, N> { pub struct CountCard<'a, N> {
/// State of the candidate
pub state: CandidateState, pub state: CandidateState,
/// Order of election or exclusion
///
/// Positive integers represent order of election; negative integers represent order of exclusion
pub order_elected: isize, pub order_elected: isize,
//pub orig_votes: N, //pub orig_votes: N,
/// Net votes transferred to this candidate in this stage
pub transfers: N, pub transfers: N,
/// Votes of the candidate at the end of this stage
pub votes: N, pub votes: N,
/// Parcels of ballots assigned to this candidate
pub parcels: Vec<Parcel<'a, N>>, pub parcels: Vec<Parcel<'a, N>>,
/// Candidate's keep value (Meek STV) /// Candidate's keep value (Meek STV)
@ -275,7 +315,9 @@ pub type Parcel<'a, N> = Vec<Vote<'a, N>>;
/// Represents a [Ballot] with an associated value /// Represents a [Ballot] with an associated value
#[derive(Clone)] #[derive(Clone)]
pub struct Vote<'a, N> { pub struct Vote<'a, N> {
/// Ballot from which the vote is derived
pub ballot: &'a Ballot<N>, pub ballot: &'a Ballot<N>,
/// Current value of the ballot
pub value: N, pub value: N,
/// Index of the next preference to examine /// Index of the next preference to examine
pub up_to_pref: usize, pub up_to_pref: usize,
@ -283,7 +325,9 @@ pub struct Vote<'a, N> {
/// A record of a voter's preferences /// A record of a voter's preferences
pub struct Ballot<N> { pub struct Ballot<N> {
/// Original value/weight of the ballot
pub orig_value: N, pub orig_value: N,
/// Indexes of candidates preferenced on the ballot
pub preferences: Vec<usize>, pub preferences: Vec<usize>,
} }
@ -292,10 +336,16 @@ pub struct Ballot<N> {
#[derive(PartialEq)] #[derive(PartialEq)]
#[derive(Clone)] #[derive(Clone)]
pub enum CandidateState { pub enum CandidateState {
/// Hopeful (continuing candidate)
Hopeful, Hopeful,
/// Required by constraints to be guarded from exclusion
Guarded, Guarded,
/// Declared elected
Elected, Elected,
/// Required by constraints to be doomed to be excluded
Doomed, Doomed,
/// Withdrawn candidate
Withdrawn, Withdrawn,
/// Declared excluded
Excluded, Excluded,
} }

View File

@ -15,6 +15,10 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#![warn(missing_docs)]
//! Open source counting software for various preferential voting election systems
/// Data types for representing abstract elections /// Data types for representing abstract elections
pub mod election; pub mod election;
/// Smart logging framework /// Smart logging framework

View File

@ -18,6 +18,7 @@
/// Smart logger used in election counts /// Smart logger used in election counts
#[derive(Clone)] #[derive(Clone)]
pub struct Logger<'a> { pub struct Logger<'a> {
/// [Vec] of log entries for the current stage
pub entries: Vec<LogEntry<'a>>, pub entries: Vec<LogEntry<'a>>,
} }
@ -73,7 +74,9 @@ impl<'a> Logger<'a> {
/// Represents either a literal or smart log entry /// Represents either a literal or smart log entry
#[derive(Clone)] #[derive(Clone)]
pub enum LogEntry<'a> { pub enum LogEntry<'a> {
/// Smart log entry - see [SmartLogEntry]
Smart(SmartLogEntry<'a>), Smart(SmartLogEntry<'a>),
/// Literal log entry
Literal(String) Literal(String)
} }

View File

@ -42,6 +42,7 @@ fn get_factor() -> &'static IBig {
pub struct Fixed(IBig); pub struct Fixed(IBig);
impl Fixed { impl Fixed {
/// Set the number of decimal places to compute results to
pub fn set_dps(dps: usize) { pub fn set_dps(dps: usize) {
unsafe { unsafe {
DPS = Some(dps); DPS = Some(dps);
@ -108,6 +109,7 @@ impl From<f64> for Fixed {
} }
} }
// TODO: Fix rounding
impl fmt::Display for Fixed { impl fmt::Display for Fixed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let dps = match f.precision() { let dps = match f.precision() {

View File

@ -48,6 +48,7 @@ fn get_factor_cmp() -> &'static IBig {
pub struct GuardedFixed(IBig); pub struct GuardedFixed(IBig);
impl GuardedFixed { impl GuardedFixed {
/// Set the number of decimal places to compute results to
pub fn set_dps(dps: usize) { pub fn set_dps(dps: usize) {
unsafe { unsafe {
DPS = Some(dps); DPS = Some(dps);

View File

@ -22,6 +22,7 @@ pub mod gregory;
/// Meek method of surplus distributions, etc. /// Meek method of surplus distributions, etc.
pub mod meek; pub mod meek;
/// WebAssembly wrappers
//#[cfg(target_arch = "wasm32")] //#[cfg(target_arch = "wasm32")]
pub mod wasm; pub mod wasm;
@ -38,22 +39,39 @@ use std::ops;
/// Options for conducting an STV count /// Options for conducting an STV count
pub struct STVOptions { pub struct STVOptions {
/// Round transfer values to specified decimal places
pub round_tvs: Option<usize>, pub round_tvs: Option<usize>,
/// Round ballot weights to specified decimal places
pub round_weights: Option<usize>, pub round_weights: Option<usize>,
/// Round votes to specified decimal places
pub round_votes: Option<usize>, pub round_votes: Option<usize>,
/// Round quota to specified decimal places
pub round_quota: Option<usize>, pub round_quota: Option<usize>,
/// How to calculate votes to credit to candidates in surplus transfers
pub sum_surplus_transfers: SumSurplusTransfersMode, pub sum_surplus_transfers: SumSurplusTransfersMode,
/// Convert ballots with value >1 to multiple ballots of value 1
pub normalise_ballots: bool, pub normalise_ballots: bool,
/// Quota type
pub quota: QuotaType, pub quota: QuotaType,
/// Whether to elect candidates on meeting (geq) or strictly exceeding (gt) the quota
pub quota_criterion: QuotaCriterion, pub quota_criterion: QuotaCriterion,
/// Whether to apply a form of progressive quota
pub quota_mode: QuotaMode, pub quota_mode: QuotaMode,
/// Tie-breaking method
pub ties: Vec<TieStrategy>, pub ties: Vec<TieStrategy>,
/// Method of surplus distributions
pub surplus: SurplusMethod, pub surplus: SurplusMethod,
/// Order to distribute surpluses
pub surplus_order: SurplusOrder, pub surplus_order: SurplusOrder,
/// Examine only transferable papers during surplus distributions
pub transferable_only: bool, pub transferable_only: bool,
/// Method of exclusions
pub exclusion: ExclusionMethod, pub exclusion: ExclusionMethod,
/// Use bulk exclusion
pub bulk_exclude: bool, pub bulk_exclude: bool,
/// Defer surplus distributions if possible
pub defer_surpluses: bool, pub defer_surpluses: bool,
/// Print votes to specified decimal places in results report
pub pp_decimals: usize, pub pp_decimals: usize,
} }
@ -172,8 +190,11 @@ impl STVOptions {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum SumSurplusTransfersMode { pub enum SumSurplusTransfersMode {
/// Sum and round all surplus transfers for a candidate in a single step
SingleStep, SingleStep,
/// Sum and round a candidate's surplus transfers separately for ballot papers received at each particular value
ByValue, ByValue,
/// Sum and round a candidate's surplus transfers individually for each ballot paper
PerBallot, PerBallot,
} }
@ -193,9 +214,13 @@ impl SumSurplusTransfersMode {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum QuotaType { pub enum QuotaType {
/// Droop quota
Droop, Droop,
/// Hare quota
Hare, Hare,
/// Exact Droop quota (Newland–Britton/Hagenbach-Bischoff quota)
DroopExact, DroopExact,
/// Exact Hare quota
HareExact, HareExact,
} }
@ -216,7 +241,9 @@ impl QuotaType {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum QuotaCriterion { pub enum QuotaCriterion {
/// Elect candidates on equalling or exceeding the quota
GreaterOrEqual, GreaterOrEqual,
/// Elect candidates on strictly exceeding the quota
Greater, Greater,
} }
@ -235,7 +262,9 @@ impl QuotaCriterion {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum QuotaMode { pub enum QuotaMode {
/// Static quota
Static, Static,
/// Static quota with ERS97 rules
ERS97, ERS97,
} }
@ -254,9 +283,13 @@ impl QuotaMode {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum SurplusMethod { pub enum SurplusMethod {
/// Weighted inclusive Gregory method
WIG, WIG,
/// Unweighted inclusive Gregory method
UIG, UIG,
/// Exclusive Gregory method (last bundle)
EG, EG,
/// Meek method
Meek, Meek,
} }
@ -277,7 +310,9 @@ impl SurplusMethod {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum SurplusOrder { pub enum SurplusOrder {
/// Transfer the largest surplus first, even if it arose at a later stage of the count
BySize, BySize,
/// Transfer the surplus of the candidate elected first, even if it is smaller than another
ByOrder, ByOrder,
} }
@ -296,8 +331,11 @@ impl SurplusOrder {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum ExclusionMethod { pub enum ExclusionMethod {
/// Transfer all ballot papers of an excluded candidate in one stage
SingleStage, SingleStage,
/// Transfer the ballot papers of an excluded candidate in descending order of accumulated transfer value
ByValue, ByValue,
/// Transfer the ballot papers of an excluded candidate parcel by parcel in the order received
ParcelsByOrder, ParcelsByOrder,
} }
@ -316,7 +354,9 @@ impl ExclusionMethod {
#[wasm_bindgen] #[wasm_bindgen]
#[derive(Debug)] #[derive(Debug)]
pub enum STVError { pub enum STVError {
/// User input is required
RequireInput, RequireInput,
/// Tie could not be resolved
UnresolvedTie, UnresolvedTie,
} }

View File

@ -129,27 +129,38 @@ macro_rules! impl_type {
} }
// Wrapper structs // Wrapper structs
// Required as we cannot specify &'static in wasm-bindgen: issue #1187
/// Wrapper for [CountState] /// Wrapper for [CountState]
///
/// This is required as `&'static` cannot be specified in wasm-bindgen: see [issue 1187](https://github.com/rustwasm/wasm-bindgen/issues/1187).
///
#[wasm_bindgen] #[wasm_bindgen]
pub struct [<CountState$type>](CountState<'static, $type>); pub struct [<CountState$type>](CountState<'static, $type>);
#[wasm_bindgen] #[wasm_bindgen]
impl [<CountState$type>] { impl [<CountState$type>] {
/// Create a new [CountState] wrapper
pub fn new(election: &[<Election$type>]) -> Self { pub fn new(election: &[<Election$type>]) -> Self {
return [<CountState$type>](CountState::new(election.as_static())); return [<CountState$type>](CountState::new(election.as_static()));
} }
} }
/// Wrapper for [Election] /// Wrapper for [Election]
///
/// This is required as `&'static` cannot be specified in wasm-bindgen: see [issue 1187](https://github.com/rustwasm/wasm-bindgen/issues/1187).
///
#[wasm_bindgen] #[wasm_bindgen]
pub struct [<Election$type>](Election<$type>); pub struct [<Election$type>](Election<$type>);
#[wasm_bindgen] #[wasm_bindgen]
impl [<Election$type>] { impl [<Election$type>] {
/// Return [Election::seats]
pub fn seats(&self) -> usize { self.0.seats } pub fn seats(&self) -> usize { self.0.seats }
/// Return the underlying [Election] as a `&'static Election`
///
/// # Safety
/// This assumes that the underlying [Election] is valid for the `'static` lifetime, as it would be if the [Election] were created from Javascript.
///
fn as_static(&self) -> &'static Election<$type> { fn as_static(&self) -> &'static Election<$type> {
// Need to have this as we cannot specify &'static in wasm-bindgen: issue #1187
unsafe { unsafe {
let ptr = &self.0 as *const Election<$type>; let ptr = &self.0 as *const Election<$type>;
&*ptr &*ptr
@ -218,7 +229,7 @@ impl STVOptions {
/// Return the underlying [stv::STVOptions] as a `&'static stv::STVOptions` /// Return the underlying [stv::STVOptions] as a `&'static stv::STVOptions`
/// ///
/// # Safety /// # Safety
/// Assumes that the underlying [stv::STVOptions] is valid for the `'static` lifetime, as it would be if the [stv::STVOptions] were created from Javascript /// This assumes that the underlying [stv::STVOptions] is valid for the `'static` lifetime, as it would be if the [stv::STVOptions] were created from Javascript.
/// ///
fn as_static(&self) -> &'static stv::STVOptions { fn as_static(&self) -> &'static stv::STVOptions {
unsafe { unsafe {

View File

@ -29,9 +29,13 @@ use std::io::{stdin, stdout, Write};
/// Strategy for breaking ties /// Strategy for breaking ties
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum TieStrategy { pub enum TieStrategy {
/// Break ties according to the candidate who first had more/fewer votes
Forwards, Forwards,
/// Break ties according to the candidate who most recently had more/fewer votes
Backwards, Backwards,
/// Break ties randomly (see [crate::sharandom])
Random(String), Random(String),
/// Prompt the user to break ties
Prompt, Prompt,
} }