removed an optimisation that makes things less accurate
This commit is contained in:
parent
96f1775004
commit
41c4833a2b
|
|
@ -2,18 +2,11 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ballot-counter"
|
name = "ballot-counter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"num",
|
|
||||||
"quick-csv",
|
"quick-csv",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -32,79 +25,6 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
|
|
||||||
dependencies = [
|
|
||||||
"num-bigint",
|
|
||||||
"num-complex",
|
|
||||||
"num-integer",
|
|
||||||
"num-iter",
|
|
||||||
"num-rational",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-bigint"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
|
||||||
dependencies = [
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-complex"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-integer"
|
|
||||||
version = "0.1.46"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
|
||||||
dependencies = [
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-iter"
|
|
||||||
version = "0.1.45"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-rational"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
|
||||||
dependencies = [
|
|
||||||
"num-bigint",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-traits"
|
|
||||||
version = "0.2.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-csv"
|
name = "quick-csv"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,4 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
num = "0.4.3"
|
|
||||||
quick-csv = "0.1.6"
|
quick-csv = "0.1.6"
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use crate::{ballot::Ballot, header::Header, score_item::{ScoreItem, ScoreItems}};
|
|
||||||
|
use crate::{ballot::Ballot, header::Header, util::ScoreItem};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Lose(Vec<ScoreItem>),
|
Lose(ScoreItem),
|
||||||
Win(Vec<ScoreItem>),
|
Win(Vec<ScoreItem>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,14 +43,17 @@ impl Counter {
|
||||||
quota,
|
quota,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn count_primaries(&self) -> ScoreItems {
|
fn count_primaries(&self) -> Vec<ScoreItem> {
|
||||||
let mut scores = vec![0.0; self.enabled.len()];
|
let mut scores = vec![0.0; self.enabled.len()];
|
||||||
for ballot in self.ballots.iter() {
|
for ballot in self.ballots.iter() {
|
||||||
if let Some(index) = ballot.get_primary_index(|id| self.enabled[id]) {
|
if let Some(index) = ballot.get_primary_index(|id| self.enabled[id]) {
|
||||||
scores[index] += ballot.get_weight();
|
scores[index] += ballot.get_weight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScoreItems::new(scores, &self.enabled)
|
scores.into_iter().enumerate().filter_map(|(index, value)| match self.enabled[index] {
|
||||||
|
true => Some(ScoreItem::new(index, value)),
|
||||||
|
false => None,
|
||||||
|
}).collect_vec()
|
||||||
}
|
}
|
||||||
pub fn get_quota(&self) -> f64 {
|
pub fn get_quota(&self) -> f64 {
|
||||||
self.quota
|
self.quota
|
||||||
|
|
@ -61,38 +65,24 @@ impl Iterator for Counter {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let mut winners = Vec::<ScoreItem>::with_capacity(self.enabled.len());
|
let mut winners = Vec::<ScoreItem>::with_capacity(self.enabled.len());
|
||||||
let mut losers = Vec::<ScoreItem>::with_capacity(self.enabled.len());
|
let primaries = self.count_primaries();
|
||||||
let mut primaries = self.count_primaries().collect_vec();
|
let mut lowest = *primaries.first()?;
|
||||||
primaries.sort_by(|a, b| a.cmp(b));
|
|
||||||
|
|
||||||
let &lowest = primaries.first()?;
|
|
||||||
let &highest = primaries.last()?;
|
|
||||||
let mut lose_total = 0.0;
|
|
||||||
|
|
||||||
for &score in primaries.iter() {
|
for &score in primaries.iter() {
|
||||||
|
lowest = lowest.min(score);
|
||||||
if score.value >= self.quota {
|
if score.value >= self.quota {
|
||||||
winners.push(score);
|
winners.push(score);
|
||||||
}
|
}
|
||||||
if lose_total + score.value < highest.value {
|
|
||||||
lose_total += score.value;
|
|
||||||
losers.push(score);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if primaries.len() == 1 && self.winners_left == 1 {
|
if primaries.len() == 1 && self.winners_left == 1 {
|
||||||
self.enabled[highest.index] = false;
|
self.enabled[lowest.index] = false;
|
||||||
return Some(Event::Win(vec![highest]));
|
return Some(Event::Win(vec![lowest]));
|
||||||
}
|
|
||||||
|
|
||||||
if losers.len() == 0 {
|
|
||||||
losers.push(lowest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if winners.len() == 0 {
|
if winners.len() == 0 {
|
||||||
for score in losers.iter() {
|
self.enabled[lowest.index] = false;
|
||||||
self.enabled[score.index] = false;
|
return Some(Event::Lose(lowest));
|
||||||
}
|
|
||||||
return Some(Event::Lose(losers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
winners.sort_by(|a,b| b.cmp(a));
|
winners.sort_by(|a,b| b.cmp(a));
|
||||||
|
|
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -7,7 +7,6 @@ pub mod candidate;
|
||||||
pub mod ballot;
|
pub mod ballot;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
pub mod party;
|
pub mod party;
|
||||||
pub mod score_item;
|
|
||||||
mod counter;
|
mod counter;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -36,18 +35,15 @@ fn main() {
|
||||||
|
|
||||||
for (index, event) in (1..).zip(counter) {
|
for (index, event) in (1..).zip(counter) {
|
||||||
match event {
|
match event {
|
||||||
counter::Event::Win(v) => {
|
counter::Event::Win(scores) => {
|
||||||
eprintln!(" {index}: Win:");
|
eprintln!(" {index}: Win:");
|
||||||
for winner in v.iter() {
|
for winner in scores.iter() {
|
||||||
eprintln!(" - {}, {}, {}", header.get_candidate_name(winner.index), winner.value, Percent(winner.value, total));
|
eprintln!(" - {}, {}, {}", header.get_candidate_name(winner.index), winner.value, Percent(winner.value, total));
|
||||||
}
|
}
|
||||||
winners.extend_from_slice(&v);
|
winners.extend(scores);
|
||||||
}
|
|
||||||
counter::Event::Lose(v) => {
|
|
||||||
eprintln!(" {index}: Lose:");
|
|
||||||
for loser in v.iter() {
|
|
||||||
eprintln!(" - {}, {}, {}", header.get_candidate_name(loser.index), loser.value, Percent(loser.value, total));
|
|
||||||
}
|
}
|
||||||
|
counter::Event::Lose(loser) => {
|
||||||
|
eprintln!(" {index}: Lose: {}, {}, {}", header.get_candidate_name(loser.index), loser.value, Percent(loser.value, total));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
|
|
||||||
#[derive(Clone,Copy,Debug)]
|
|
||||||
pub struct ScoreItem {
|
|
||||||
pub index: usize,
|
|
||||||
pub value: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ScoreItems<'a> {
|
|
||||||
enabled: &'a [bool],
|
|
||||||
data: Vec<f64>,
|
|
||||||
index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScoreItem {
|
|
||||||
pub fn new(index: usize, value: f64) -> Self {
|
|
||||||
Self { index, value }
|
|
||||||
}
|
|
||||||
pub fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.value.total_cmp(&other.value)
|
|
||||||
}
|
|
||||||
pub fn max(self, other: Self) -> Self {
|
|
||||||
if self.value > other.value {
|
|
||||||
self
|
|
||||||
} else {
|
|
||||||
other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn min(self, other: Self) -> Self {
|
|
||||||
if self.value > other.value {
|
|
||||||
other
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ScoreItems<'a> {
|
|
||||||
pub fn new(data: Vec<f64>, enabled: &'a [bool]) -> Self {
|
|
||||||
Self { data, enabled, index: 0 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Iterator for ScoreItems<'a> {
|
|
||||||
type Item = ScoreItem;
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
while self.index < self.data.len() {
|
|
||||||
if self.enabled[self.index] {
|
|
||||||
let v = ScoreItem::new(self.index, self.data[self.index]);
|
|
||||||
self.index += 1;
|
|
||||||
return Some(v);
|
|
||||||
}
|
|
||||||
self.index += 1;
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
||||||
let left = self.data.len() - self.index;
|
|
||||||
(0, Some(left))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
|
||||||
pub mod escape;
|
pub mod escape;
|
||||||
pub mod percent;
|
pub mod percent;
|
||||||
|
pub mod score_item;
|
||||||
pub mod csv;
|
pub mod csv;
|
||||||
|
|
||||||
|
pub use score_item::ScoreItem;
|
||||||
pub use escape::{EscapeWriter, EscapeWriterOpts};
|
pub use escape::{EscapeWriter, EscapeWriterOpts};
|
||||||
pub use percent::Percent;
|
pub use percent::Percent;
|
||||||
pub use csv::CsvWriter;
|
pub use csv::CsvWriter;
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ impl<'a,T> RowWriter<'a,T> where T: std::io::Write {
|
||||||
write!(self.csv.out, "\"")?;
|
write!(self.csv.out, "\"")?;
|
||||||
let mut writer = EscapeWriter::new(&mut self.csv.out, EscapeWriterOpts {
|
let mut writer = EscapeWriter::new(&mut self.csv.out, EscapeWriterOpts {
|
||||||
escape: b'\\',
|
escape: b'\\',
|
||||||
chars: b"\"",
|
chars: b"\"\t\r\n",
|
||||||
});
|
});
|
||||||
write!(writer, "{value}")?;
|
write!(writer, "{value}")?;
|
||||||
write!(self.csv.out, "\"")?;
|
write!(self.csv.out, "\"")?;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
|
||||||
|
#[derive(Clone,Copy,Debug)]
|
||||||
|
pub struct ScoreItem {
|
||||||
|
pub index: usize,
|
||||||
|
pub value: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScoreItem {
|
||||||
|
pub fn new(index: usize, value: f64) -> Self {
|
||||||
|
Self { index, value }
|
||||||
|
}
|
||||||
|
pub fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.value.total_cmp(&other.value)
|
||||||
|
}
|
||||||
|
pub fn max(self, other: Self) -> Self {
|
||||||
|
if self.value > other.value {
|
||||||
|
self
|
||||||
|
} else {
|
||||||
|
other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn min(self, other: Self) -> Self {
|
||||||
|
if self.value > other.value {
|
||||||
|
other
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue