Permit --surplus meek with --quota-mode static

This commit is contained in:
RunasSudo 2022-06-18 23:07:00 +10:00
parent 2f6614c0c1
commit 566cdeb185
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
1 changed files with 59 additions and 14 deletions

View File

@ -212,24 +212,68 @@ impl STVOptions {
/// Validate the combination of [STVOptions] and error if invalid
pub fn validate(&self) -> Result<(), STVError> {
if self.surplus == SurplusMethod::Meek {
if self.quota_mode != QuotaMode::DynamicByTotal { return Err(STVError::InvalidOptions("--surplus meek requires --quota-mode dynamic_by_total")); }
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.constraints_path.is_some() && self.constraint_mode == ConstraintMode::RepeatCount { return Err(STVError::InvalidOptions("--constraint-mode repeat_count requires a Gregory method for --surplus")); } // TODO: NYI?
if self.quota_mode == QuotaMode::ERS97 {
// Invalid because keep values cannot be calculated for a candidate elected with less than a surplus
return Err(STVError::InvalidOptions("--surplus meek is incompatible with --quota-mode ers97"));
}
if self.quota_mode == QuotaMode::ERS76 {
// Invalid because keep values cannot be calculated for a candidate elected with less than a surplus
return Err(STVError::InvalidOptions("--surplus meek is incompatible with --quota-mode ers76"));
}
if self.quota_mode == QuotaMode::DynamicByActive {
// Invalid because all votes are "active" in Meek STV
return Err(STVError::InvalidOptions("--surplus meek is incompatible with --quota-mode dynamic_by_active"));
}
if self.transferable_only {
// Invalid because this would imply a different keep value applies to nontransferable ballots (?)
// TODO: NYI?
return Err(STVError::InvalidOptions("--surplus meek is incompatible with --transferable-only"));
}
if self.exclusion != ExclusionMethod::SingleStage {
// Invalid because Meek STV is independent of order of exclusion, so segmented exclusion has no impact
return Err(STVError::InvalidOptions("--surplus meek requires --exclusion single_stage"));
}
if self.constraints_path.is_some() && self.constraint_mode == ConstraintMode::RepeatCount {
// TODO: NYI?
return Err(STVError::InvalidOptions("--constraint-mode repeat_count requires a Gregory method for --surplus"));
}
}
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.sample == SampleMethod::StratifyLR && self.sample_per_ballot { return Err(STVError::InvalidOptions("--sample stratify 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_per_ballot && !self.immediate_elect { return Err(STVError::InvalidOptions("--sample-per-ballot is incompatible with --no-immediate-elect")); }
if self.constraints_path.is_some() && self.constraint_mode == ConstraintMode::RepeatCount { return Err(STVError::InvalidOptions("--constraint-mode repeat_count requires a Gregory method for --surplus")); } // TODO: NYI?
if self.round_quota != Some(0) {
// Invalid because votes are counted only in whole numbers
return Err(STVError::InvalidOptions("--surplus ihare and --surplus hare require --round-quota 0"));
}
if self.sample == SampleMethod::StratifyLR && self.sample_per_ballot {
// Invalid because a stratification cannot be made until all relevant ballots are transferred
return Err(STVError::InvalidOptions("--sample stratify is incompatible with --sample-per-ballot"));
}
if self.sample_per_ballot && !self.immediate_elect {
// Invalid because otherwise --sample-per-ballot would be ineffectual
return Err(STVError::InvalidOptions("--sample-per-ballot is incompatible with --no-immediate-elect"));
}
if self.constraints_path.is_some() && self.constraint_mode == ConstraintMode::RepeatCount {
// TODO: NYI?
return Err(STVError::InvalidOptions("--constraint-mode repeat_count requires a Gregory method for --surplus"));
}
}
if self.subtract_nontransferable {
if self.surplus != SurplusMethod::WIG { return Err(STVError::InvalidOptions("--subtract-nontransferable requires --surplus wig")) }
if !self.transferable_only { return Err(STVError::InvalidOptions("--subtract-nontransferable requires --transferable-only")) }
if self.surplus != SurplusMethod::WIG {
// Invalid because other methods do not distinguish between ballots of different value during surplus transfer
return Err(STVError::InvalidOptions("--subtract-nontransferable requires --surplus wig"));
}
if !self.transferable_only {
// Invalid because nontransferables are only subtracted with --transferable-only
return Err(STVError::InvalidOptions("--subtract-nontransferable requires --transferable-only"));
}
}
if self.min_threshold != "0" && self.defer_surpluses {
// TODO: NYI
return Err(STVError::InvalidOptions("--min-threshold is incompatible with --defer-surpluses (not yet implemented)"));
}
if self.round_subtransfers == RoundSubtransfersMode::ByValueAndSource && self.bulk_exclude {
// TODO: NYI
return Err(STVError::InvalidOptions("--round-subtransfers by_value_and_source is incompatible with --bulk-exclude (not yet implemented)"));
}
if self.min_threshold != "0" && self.defer_surpluses { return Err(STVError::InvalidOptions("--min-threshold is incompatible with --defer-surpluses (not yet implemented)")); } // TODO: NYI
if self.round_subtransfers == RoundSubtransfersMode::ByValueAndSource && self.bulk_exclude { return Err(STVError::InvalidOptions("--round-subtransfers by_value_and_source is incompatible with --bulk-exclude (not yet implemented)")); } // TODO: NYI
return Ok(());
}
}
@ -1792,7 +1836,8 @@ pub fn should_show_vre(opts: &STVOptions) -> bool {
if opts.quota_mode == QuotaMode::ERS97 || opts.quota_mode == QuotaMode::ERS76 {
return true;
}
if opts.surplus == SurplusMethod::Meek {
if opts.surplus == SurplusMethod::Meek && opts.quota_mode == QuotaMode::DynamicByTotal {
// Meek method ensures that, if the quota is recalculated, every candidate will be elected with a quota
return false;
}
if opts.early_bulk_elect {