Update terminology and remove "stratify (floor)" support
Cincinnati -> Inclusive Hare (previous usage was erroneous/nonstandard) Every n-th ballot -> Cincinnati Remove "stratify (floor)" as it is not in contemporary use
This commit is contained in:
parent
0506283ae4
commit
93cb72c33a
@ -97,8 +97,8 @@ Other Gregory methods are supported, but not recommended:
|
||||
|
||||
Random sample methods are also supported, but also not recommended:
|
||||
|
||||
* *Cincinnati (inclusive sample)*: During surplus transfers, a subset of the elected candidate's ballot papers, equal in size to the surplus, is examined.
|
||||
* *Hare (exclusive sample)*: During surplus transfers, a subset of the ballot papers received in the last transfer, equal in size to the surplus, is examined.
|
||||
* *Inclusive Hare (sample)*: During surplus transfers, a subset of the elected candidate's ballot papers, equal in size to the surplus, is examined.
|
||||
|
||||
The use of a random sample method requires *Normalise ballots* to be enabled, and will usually be used with a *Quota criterion* set to *>=*.
|
||||
|
||||
@ -128,10 +128,9 @@ When *Surplus method* is set to *Meek method*, this option controls how candidat
|
||||
|
||||
When *Surplus method* is set to a random sample method, this option controls which subset of ballot papers is selected for transfer during surplus distributions:
|
||||
|
||||
* *Stratify (LR)* (default): The candidate's ballot papers are first stratified into subparcels according to next available preference, and an equal proportion of each subparcel is transferred, with the subset transferred comprising the ballot papers in each subparcel most recently received by the candidate. In the calculation of proportions, the largest remainders are rounded up so there is no loss by fraction. This is the method specified by the [*Electoral Act 1992* (Ireland)](http://www.irishstatutebook.ie/eli/1992/act/23/section/121/enacted/en/html#sec121).
|
||||
* *Stratify (floor)*: The same as *Stratify (LR)*, except in the calculation of proportions, all remainders are disregarded, and the difference is lost by fraction.
|
||||
* *Stratify* (default): The candidate's ballot papers are first stratified into subparcels according to next available preference, and an equal proportion of each subparcel is transferred, with the subset transferred comprising the ballot papers in each subparcel most recently received by the candidate. In the calculation of proportions, the largest remainders are rounded up so there is no loss by fraction. This is the method specified by the [*Electoral Act 1992* (Ireland)](http://www.irishstatutebook.ie/eli/1992/act/23/section/121/enacted/en/html#sec121).
|
||||
* *By order*: The subset transferred comprises the ballot papers most recently received by the candidate.
|
||||
* *Every n-th ballot*: The subset is selected using the deterministic method used in [Cambridge, Massachusetts](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf) (derived from Article IX of the former 1938 Cincinnati *Code of Ordinances*).
|
||||
* *Cincinnati*: The subset is selected using the deterministic method used in [Cambridge, Massachusetts](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf) (derived from Article IX of the former 1938 Cincinnati *Code of Ordinances*).
|
||||
|
||||
In any case, the subset selected depends on the order of ballot papers in the BLT file, and is independent of the *Random seed* option.
|
||||
|
||||
|
@ -115,8 +115,8 @@
|
||||
<option value="uig">Unweighted inclusive Gregory</option>
|
||||
<option value="eg">Exclusive Gregory (last bundle)</option>
|
||||
<option value="meek">Meek method</option>
|
||||
<option value="cincinnati">Cincinnati (inclusive sample)</option>
|
||||
<option value="hare">Hare (exclusive sample)</option>
|
||||
<option value="ihare">Inclusive Hare (sample)</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
@ -146,13 +146,13 @@
|
||||
</div>
|
||||
<div>
|
||||
<label style="margin-right:1em;">
|
||||
<span class="pill-grey" title="This option has effect only if “Method” is set to a random sample method">Sample</span>
|
||||
<span class="pill-grey" title="This option has effect only if “Method” is set to a Hare method">Hare</span>
|
||||
Sample method:
|
||||
<select id="selSample">
|
||||
<option value="stratify_lr" selected>Stratify (LR)</option>
|
||||
<option value="stratify_floor" selected>Stratify (floor)</option>
|
||||
<option value="stratify" selected>Stratify</option>
|
||||
<!--<option value="stratify_floor" selected>Stratify (floor)</option>-->
|
||||
<option value="by_order">By order</option>
|
||||
<option value="nth_ballot">Every n-th ballot</option>
|
||||
<option value="cincinnati">Cincinnati</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
|
@ -671,7 +671,7 @@ function changePreset() {
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('chkImmediateElect').checked = true;
|
||||
document.getElementById('selSample').value = 'nth_ballot';
|
||||
document.getElementById('selSample').value = 'cincinnati';
|
||||
document.getElementById('chkSamplePerBallot').checked = true;
|
||||
document.getElementById('txtMinThreshold').value = '49';
|
||||
document.getElementById('selNumbers').value = 'rational';
|
||||
@ -680,7 +680,7 @@ function changePreset() {
|
||||
document.getElementById('chkRoundQuota').checked = true;
|
||||
document.getElementById('txtRoundQuota').value = '0';
|
||||
document.getElementById('selSumTransfers').value = 'by_value';
|
||||
document.getElementById('selMethod').value = 'cincinnati';
|
||||
document.getElementById('selMethod').value = 'hare';
|
||||
document.getElementById('selPapers').value = 'transferable';
|
||||
document.getElementById('selExclusion').value = 'single_stage';
|
||||
document.getElementById('selTies').value = 'backwards,random';
|
||||
@ -692,7 +692,7 @@ function changePreset() {
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = true;
|
||||
document.getElementById('chkImmediateElect').checked = true;
|
||||
document.getElementById('selSample').value = 'stratify_lr';
|
||||
document.getElementById('selSample').value = 'stratify';
|
||||
document.getElementById('chkSamplePerBallot').checked = false;
|
||||
document.getElementById('txtMinThreshold').value = '0';
|
||||
document.getElementById('selNumbers').value = 'rational';
|
||||
|
@ -113,8 +113,8 @@ pub struct SubcmdOptions {
|
||||
#[clap(help_heading=Some("STV VARIANTS"), long, value_name="seed")]
|
||||
random_seed: Option<String>,
|
||||
|
||||
/// Method of surplus distributions
|
||||
#[clap(help_heading=Some("STV VARIANTS"), short='s', long, possible_values=&["wig", "uig", "eg", "meek", "cincinnati", "hare"], default_value="wig", value_name="method")]
|
||||
/// Method of surplus distributions [default: wig] [possible values: wig, uig, eg, meek, ihare, hare]
|
||||
#[clap(help_heading=Some("STV VARIANTS"), short='s', long, possible_values=&["wig", "uig", "eg", "meek", "ihare", "hare", "eh"], default_value="wig", value_name="method", hide_possible_values=true, hide_default_value=true)]
|
||||
surplus: String,
|
||||
|
||||
/// (Gregory STV) Order to distribute surpluses
|
||||
@ -133,11 +133,11 @@ pub struct SubcmdOptions {
|
||||
#[clap(help_heading=Some("STV VARIANTS"), long)]
|
||||
meek_nz_exclusion: bool,
|
||||
|
||||
/// (Cincinnati/Hare) Method of drawing a sample
|
||||
#[clap(help_heading=Some("STV VARIANTS"), long, possible_values=&["stratify_lr", "stratify_floor", "by_order", "nth_ballot"], default_value="stratify_lr", value_name="method")]
|
||||
/// (Hare) Method of drawing a sample [default: stratify] [possible values: stratify, by_order, cincinnati]
|
||||
#[clap(help_heading=Some("STV VARIANTS"), long, possible_values=&["stratify", "stratify_lr", "by_order", "cincinnati", "nth_ballot"], default_value="stratify", value_name="method", hide_possible_values=true, hide_default_value=true)]
|
||||
sample: String,
|
||||
|
||||
/// (Cincinnati/Hare) Sample-based methods: Check for candidate election after each individual ballot paper transfer
|
||||
/// (Hare) Sample-based methods: Check for candidate election after each individual ballot paper transfer
|
||||
#[clap(help_heading=Some("STV VARIANTS"), long)]
|
||||
sample_per_ballot: bool,
|
||||
|
||||
|
@ -158,7 +158,7 @@ where
|
||||
|
||||
match opts.surplus {
|
||||
SurplusMethod::WIG | SurplusMethod::UIG | SurplusMethod::EG => { distribute_surplus(state, &opts, elected_candidate); }
|
||||
SurplusMethod::Cincinnati | SurplusMethod::Hare => { sample::distribute_surplus(state, &opts, elected_candidate)?; }
|
||||
SurplusMethod::IHare | SurplusMethod::Hare => { sample::distribute_surplus(state, &opts, elected_candidate)?; }
|
||||
_ => unreachable!()
|
||||
}
|
||||
|
||||
|
@ -111,11 +111,11 @@ pub struct STVOptions {
|
||||
#[builder(default="false")]
|
||||
pub meek_nz_exclusion: bool,
|
||||
|
||||
/// (Cincinnati/Hare) Method of drawing a sample
|
||||
/// (Hare) Method of drawing a sample
|
||||
#[builder(default="SampleMethod::StratifyLR")]
|
||||
pub sample: SampleMethod,
|
||||
|
||||
/// (Cincinnati/Hare) Sample-based methods: Check for candidate election after each individual ballot paper transfer
|
||||
/// (Hare) Sample-based methods: Check for candidate election after each individual ballot paper transfer
|
||||
#[builder(default="false")]
|
||||
pub sample_per_ballot: bool,
|
||||
|
||||
@ -169,7 +169,7 @@ impl STVOptions {
|
||||
pub fn describe<N: Number>(&self) -> String {
|
||||
let mut flags = Vec::new();
|
||||
let n_str = N::describe_opt(); if !n_str.is_empty() { flags.push(N::describe_opt()) };
|
||||
if self.surplus != SurplusMethod::Cincinnati && self.surplus != SurplusMethod::Hare {
|
||||
if self.surplus != SurplusMethod::IHare && self.surplus != SurplusMethod::Hare {
|
||||
if let Some(dps) = self.round_surplus_fractions { flags.push(format!("--round-surplus-fractions {}", dps)); }
|
||||
if let Some(dps) = self.round_values { flags.push(format!("--round-values {}", dps)); }
|
||||
if let Some(dps) = self.round_votes { flags.push(format!("--round-votes {}", dps)); }
|
||||
@ -191,8 +191,8 @@ impl STVOptions {
|
||||
if self.exclusion != ExclusionMethod::SingleStage { flags.push(self.exclusion.describe()); }
|
||||
}
|
||||
if self.surplus == SurplusMethod::Meek && self.meek_nz_exclusion { flags.push("--meek-nz-exclusion".to_string()); }
|
||||
if (self.surplus == SurplusMethod::Cincinnati || self.surplus == SurplusMethod::Hare) && self.sample != SampleMethod::StratifyLR { flags.push(self.sample.describe()); }
|
||||
if (self.surplus == SurplusMethod::Cincinnati || self.surplus == SurplusMethod::Hare) && self.sample_per_ballot { flags.push("--sample-per-ballot".to_string()); }
|
||||
if (self.surplus == SurplusMethod::IHare || self.surplus == SurplusMethod::Hare) && self.sample != SampleMethod::StratifyLR { flags.push(self.sample.describe()); }
|
||||
if (self.surplus == SurplusMethod::IHare || self.surplus == SurplusMethod::Hare) && self.sample_per_ballot { flags.push("--sample-per-ballot".to_string()); }
|
||||
if !self.early_bulk_elect { flags.push("--no-early-bulk-elect".to_string()); }
|
||||
if self.bulk_exclude { flags.push("--bulk-exclude".to_string()); }
|
||||
if self.defer_surpluses { flags.push("--defer-surpluses".to_string()); }
|
||||
@ -216,11 +216,11 @@ impl STVOptions {
|
||||
if self.transferable_only { return Err(STVError::InvalidOptions("--surplus meek is incompatible with --transferable-only")); }
|
||||
if self.exclusion != ExclusionMethod::SingleStage { return Err(STVError::InvalidOptions("--surplus meek requires --exclusion single_stage")); }
|
||||
}
|
||||
if self.surplus == SurplusMethod::Cincinnati || self.surplus == SurplusMethod::Hare {
|
||||
if self.round_quota != Some(0) { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --round-quota 0")); }
|
||||
if !self.normalise_ballots { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --normalise-ballots")); }
|
||||
if self.surplus == SurplusMethod::IHare || self.surplus == SurplusMethod::Hare {
|
||||
if self.round_quota != Some(0) { return Err(STVError::InvalidOptions("--surplus ihare and --surplus hare require --round-quota 0")); }
|
||||
if !self.normalise_ballots { return Err(STVError::InvalidOptions("--surplus ihare and --surplus hare require --normalise-ballots")); }
|
||||
if self.sample == SampleMethod::StratifyLR && self.sample_per_ballot { return Err(STVError::InvalidOptions("--sample stratify_lr is incompatible with --sample-per-ballot")); }
|
||||
if self.sample == SampleMethod::StratifyFloor && self.sample_per_ballot { return Err(STVError::InvalidOptions("--sample stratify_floor is incompatible with --sample-per-ballot")); }
|
||||
//if self.sample == SampleMethod::StratifyFloor && self.sample_per_ballot { return Err(STVError::InvalidOptions("--sample stratify_floor is incompatible with --sample-per-ballot")); }
|
||||
if self.sample == SampleMethod::StratifyLR && self.round_surplus_fractions.is_some() { return Err(STVError::InvalidOptions("--sample stratify_lr is incompatible with --round-surplus-fractions")); }
|
||||
if self.sample_per_ballot && !self.immediate_elect { return Err(STVError::InvalidOptions("--sample-per-ballot is incompatible with --no-immediate-elect")); }
|
||||
}
|
||||
@ -386,9 +386,9 @@ pub enum SurplusMethod {
|
||||
EG,
|
||||
/// Meek method
|
||||
Meek,
|
||||
/// Cincinnati method (inclusive random subset)
|
||||
Cincinnati,
|
||||
/// Hare method (exclusive random subset)
|
||||
/// Inclusive Hare method (random subset)
|
||||
IHare,
|
||||
/// (Exclusive) Hare method (random subset)
|
||||
Hare,
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ impl SurplusMethod {
|
||||
SurplusMethod::UIG => "--surplus uig",
|
||||
SurplusMethod::EG => "--surplus eg",
|
||||
SurplusMethod::Meek => "--surplus meek",
|
||||
SurplusMethod::Cincinnati => "--surplus cincinnati",
|
||||
SurplusMethod::IHare => "--surplus ihare",
|
||||
SurplusMethod::Hare => "--surplus hare",
|
||||
}.to_string()
|
||||
}
|
||||
@ -422,8 +422,8 @@ impl<S: AsRef<str>> From<S> for SurplusMethod {
|
||||
"uig" => SurplusMethod::UIG,
|
||||
"eg" => SurplusMethod::EG,
|
||||
"meek" => SurplusMethod::Meek,
|
||||
"cincinnati" => SurplusMethod::Cincinnati,
|
||||
"hare" => SurplusMethod::Hare,
|
||||
"ihare" | "ih" | "cincinnati" => SurplusMethod::IHare, // Inclusive Hare method used to be erroneously referred to as "Cincinnati" method - accept for backwards compatibility
|
||||
"hare" | "eh" => SurplusMethod::Hare,
|
||||
_ => panic!("Invalid --surplus"),
|
||||
}
|
||||
}
|
||||
@ -510,22 +510,22 @@ impl<S: AsRef<str>> From<S> for ExclusionMethod {
|
||||
pub enum SampleMethod {
|
||||
/// Stratify the ballots into parcels according to next available preference and transfer the last ballots from each parcel; round fractions according to largest remainders
|
||||
StratifyLR,
|
||||
/// Stratify the ballots into parcels according to next available preference and transfer the last ballots from each parcel; disregard fractions
|
||||
StratifyFloor,
|
||||
// Stratify the ballots into parcels according to next available preference and transfer the last ballots from each parcel; disregard fractions
|
||||
//StratifyFloor,
|
||||
/// Transfer the last ballots
|
||||
ByOrder,
|
||||
/// Transfer every n-th ballot, Cincinnati style
|
||||
NthBallot,
|
||||
Cincinnati,
|
||||
}
|
||||
|
||||
impl SampleMethod {
|
||||
/// Convert to CLI argument representation
|
||||
fn describe(self) -> String {
|
||||
match self {
|
||||
SampleMethod::StratifyLR => "--sample stratify_lr",
|
||||
SampleMethod::StratifyFloor => "--sample stratify_floor",
|
||||
SampleMethod::StratifyLR => "--sample stratify",
|
||||
//SampleMethod::StratifyFloor => "--sample stratify_floor",
|
||||
SampleMethod::ByOrder => "--sample by_order",
|
||||
SampleMethod::NthBallot => "--sample nth_ballot",
|
||||
SampleMethod::Cincinnati => "--sample cincinnati",
|
||||
}.to_string()
|
||||
}
|
||||
}
|
||||
@ -533,10 +533,10 @@ impl SampleMethod {
|
||||
impl<S: AsRef<str>> From<S> for SampleMethod {
|
||||
fn from(s: S) -> Self {
|
||||
match s.as_ref() {
|
||||
"stratify_lr" => SampleMethod::StratifyLR,
|
||||
"stratify_floor" => SampleMethod::StratifyFloor,
|
||||
"stratify" | "stratify_lr" => SampleMethod::StratifyLR,
|
||||
//"stratify_floor" => SampleMethod::StratifyFloor,
|
||||
"by_order" => SampleMethod::ByOrder,
|
||||
"nth_ballot" => SampleMethod::NthBallot,
|
||||
"cincinnati" | "nth_ballot" => SampleMethod::Cincinnati,
|
||||
_ => panic!("Invalid --sample-method"),
|
||||
}
|
||||
}
|
||||
@ -780,7 +780,7 @@ where
|
||||
for<'r> &'r N: ops::Mul<&'r N, Output=N>,
|
||||
{
|
||||
match opts.surplus {
|
||||
SurplusMethod::WIG | SurplusMethod::UIG | SurplusMethod::EG | SurplusMethod::Cincinnati | SurplusMethod::Hare => {
|
||||
SurplusMethod::WIG | SurplusMethod::UIG | SurplusMethod::EG | SurplusMethod::IHare | SurplusMethod::Hare => {
|
||||
gregory::distribute_first_preferences(state, opts);
|
||||
}
|
||||
SurplusMethod::Meek => {
|
||||
@ -1227,7 +1227,7 @@ where
|
||||
for<'r> &'r N: ops::Neg<Output=N>,
|
||||
{
|
||||
match opts.surplus {
|
||||
SurplusMethod::WIG | SurplusMethod::UIG | SurplusMethod::EG | SurplusMethod::Cincinnati | SurplusMethod::Hare => {
|
||||
SurplusMethod::WIG | SurplusMethod::UIG | SurplusMethod::EG | SurplusMethod::IHare | SurplusMethod::Hare => {
|
||||
return gregory::distribute_surpluses(state, opts);
|
||||
}
|
||||
SurplusMethod::Meek => {
|
||||
@ -1558,7 +1558,7 @@ where
|
||||
SurplusMethod::Meek => {
|
||||
meek::exclude_candidates(state, opts, excluded_candidates);
|
||||
}
|
||||
SurplusMethod::Cincinnati | SurplusMethod::Hare => {
|
||||
SurplusMethod::IHare | SurplusMethod::Hare => {
|
||||
sample::exclude_candidates(state, opts, excluded_candidates)?;
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ where
|
||||
|
||||
let mut votes;
|
||||
match opts.surplus {
|
||||
SurplusMethod::Cincinnati => {
|
||||
SurplusMethod::IHare => {
|
||||
// Inclusive
|
||||
votes = count_card.concat_parcels();
|
||||
}
|
||||
@ -73,7 +73,7 @@ where
|
||||
}
|
||||
|
||||
match opts.sample {
|
||||
SampleMethod::StratifyLR | SampleMethod::StratifyFloor => {
|
||||
SampleMethod::StratifyLR /*| SampleMethod::StratifyFloor*/ => {
|
||||
// Stratified by next available preference (round fractions according to largest remainders)
|
||||
let result = super::next_preferences(state, votes);
|
||||
|
||||
@ -122,7 +122,7 @@ where
|
||||
let mut candidate_transfers;
|
||||
let remainder;
|
||||
match surplus_fraction {
|
||||
Some(ref f) => {
|
||||
Some(_) => {
|
||||
match opts.sample {
|
||||
SampleMethod::StratifyLR => {
|
||||
// Incompatible with --round-surplus-fractions
|
||||
@ -130,7 +130,7 @@ where
|
||||
candidate_transfers.floor_mut(0);
|
||||
remainder = (&entry.num_ballots * &surplus) % surplus_denom.as_ref().unwrap();
|
||||
}
|
||||
SampleMethod::StratifyFloor => {
|
||||
/*SampleMethod::StratifyFloor => {
|
||||
match opts.round_surplus_fractions {
|
||||
Some(_) => {
|
||||
candidate_transfers = &entry.num_ballots * f;
|
||||
@ -141,7 +141,7 @@ where
|
||||
}
|
||||
candidate_transfers.floor_mut(0);
|
||||
remainder = N::new();
|
||||
}
|
||||
}*/
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
@ -166,7 +166,7 @@ where
|
||||
exhausted_transfers.floor_mut(0);
|
||||
remainder = (&result.exhausted.num_ballots * &surplus) % surplus_denom.as_ref().unwrap();
|
||||
}
|
||||
SampleMethod::StratifyFloor => {
|
||||
/*SampleMethod::StratifyFloor => {
|
||||
match opts.round_surplus_fractions {
|
||||
Some(_) => {
|
||||
exhausted_transfers = &result.exhausted.num_ballots * surplus_fraction.as_ref().unwrap();
|
||||
@ -177,7 +177,7 @@ where
|
||||
}
|
||||
exhausted_transfers.floor_mut(0);
|
||||
remainder = N::new();
|
||||
}
|
||||
}*/
|
||||
_ => unreachable!()
|
||||
}
|
||||
|
||||
@ -314,7 +314,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
SampleMethod::NthBallot => {
|
||||
SampleMethod::Cincinnati => {
|
||||
// Every nth-ballot (Cincinnati-style)
|
||||
|
||||
// Calculate skip value
|
||||
|
@ -26,15 +26,15 @@ fn cambridge_cc03_rational() {
|
||||
.round_quota(Some(0))
|
||||
.normalise_ballots(true)
|
||||
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
|
||||
.surplus(stv::SurplusMethod::Cincinnati)
|
||||
.surplus(stv::SurplusMethod::Hare)
|
||||
.transferable_only(true)
|
||||
.sample(stv::SampleMethod::NthBallot)
|
||||
.sample(stv::SampleMethod::Cincinnati)
|
||||
.sample_per_ballot(true)
|
||||
.early_bulk_elect(false)
|
||||
.min_threshold("49".to_string())
|
||||
.build().unwrap();
|
||||
|
||||
assert_eq!(stv_opts.describe::<Rational>(), "--round-quota 0 --normalise-ballots --quota-criterion geq --surplus cincinnati --transferable-only --sample nth_ballot --sample-per-ballot --no-early-bulk-elect --min-threshold 49");
|
||||
assert_eq!(stv_opts.describe::<Rational>(), "--round-quota 0 --normalise-ballots --quota-criterion geq --surplus hare --transferable-only --sample cincinnati --sample-per-ballot --no-early-bulk-elect --min-threshold 49");
|
||||
|
||||
utils::read_validate_election::<Rational>("tests/data/CambCC2003.csv", "tests/data/CambCC2003.blt", stv_opts, None, &["exhausted"]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user