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.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "ballot-counter"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"num",
|
||||
"quick-csv",
|
||||
]
|
||||
|
||||
|
|
@ -32,79 +25,6 @@ dependencies = [
|
|||
"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]]
|
||||
name = "quick-csv"
|
||||
version = "0.1.6"
|
||||
|
|
|
|||
|
|
@ -5,5 +5,4 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
itertools = "0.14.0"
|
||||
num = "0.4.3"
|
||||
quick-csv = "0.1.6"
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use std::rc::Rc;
|
||||
use itertools::Itertools;
|
||||
use crate::{ballot::Ballot, header::Header, score_item::{ScoreItem, ScoreItems}};
|
||||
|
||||
use crate::{ballot::Ballot, header::Header, util::ScoreItem};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
Lose(Vec<ScoreItem>),
|
||||
Lose(ScoreItem),
|
||||
Win(Vec<ScoreItem>),
|
||||
}
|
||||
|
||||
|
|
@ -42,14 +43,17 @@ impl Counter {
|
|||
quota,
|
||||
})
|
||||
}
|
||||
fn count_primaries(&self) -> ScoreItems {
|
||||
fn count_primaries(&self) -> Vec<ScoreItem> {
|
||||
let mut scores = vec![0.0; self.enabled.len()];
|
||||
for ballot in self.ballots.iter() {
|
||||
if let Some(index) = ballot.get_primary_index(|id| self.enabled[id]) {
|
||||
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 {
|
||||
self.quota
|
||||
|
|
@ -61,38 +65,24 @@ impl Iterator for Counter {
|
|||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut winners = Vec::<ScoreItem>::with_capacity(self.enabled.len());
|
||||
let mut losers = Vec::<ScoreItem>::with_capacity(self.enabled.len());
|
||||
let mut primaries = self.count_primaries().collect_vec();
|
||||
primaries.sort_by(|a, b| a.cmp(b));
|
||||
|
||||
let &lowest = primaries.first()?;
|
||||
let &highest = primaries.last()?;
|
||||
let mut lose_total = 0.0;
|
||||
let primaries = self.count_primaries();
|
||||
let mut lowest = *primaries.first()?;
|
||||
|
||||
for &score in primaries.iter() {
|
||||
lowest = lowest.min(score);
|
||||
if score.value >= self.quota {
|
||||
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 {
|
||||
self.enabled[highest.index] = false;
|
||||
return Some(Event::Win(vec![highest]));
|
||||
}
|
||||
|
||||
if losers.len() == 0 {
|
||||
losers.push(lowest);
|
||||
self.enabled[lowest.index] = false;
|
||||
return Some(Event::Win(vec![lowest]));
|
||||
}
|
||||
|
||||
if winners.len() == 0 {
|
||||
for score in losers.iter() {
|
||||
self.enabled[score.index] = false;
|
||||
}
|
||||
return Some(Event::Lose(losers));
|
||||
self.enabled[lowest.index] = false;
|
||||
return Some(Event::Lose(lowest));
|
||||
}
|
||||
|
||||
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 header;
|
||||
pub mod party;
|
||||
pub mod score_item;
|
||||
mod counter;
|
||||
|
||||
fn main() {
|
||||
|
|
@ -36,18 +35,15 @@ fn main() {
|
|||
|
||||
for (index, event) in (1..).zip(counter) {
|
||||
match event {
|
||||
counter::Event::Win(v) => {
|
||||
counter::Event::Win(scores) => {
|
||||
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));
|
||||
}
|
||||
winners.extend_from_slice(&v);
|
||||
}
|
||||
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));
|
||||
winners.extend(scores);
|
||||
}
|
||||
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 percent;
|
||||
pub mod score_item;
|
||||
pub mod csv;
|
||||
|
||||
pub use score_item::ScoreItem;
|
||||
pub use escape::{EscapeWriter, EscapeWriterOpts};
|
||||
pub use percent::Percent;
|
||||
pub use csv::CsvWriter;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl<'a,T> RowWriter<'a,T> where T: std::io::Write {
|
|||
write!(self.csv.out, "\"")?;
|
||||
let mut writer = EscapeWriter::new(&mut self.csv.out, EscapeWriterOpts {
|
||||
escape: b'\\',
|
||||
chars: b"\"",
|
||||
chars: b"\"\t\r\n",
|
||||
});
|
||||
write!(writer, "{value}")?;
|
||||
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