1mod ballot;
18mod util;
19
20pub use ballot::Ballot;
21use log::Level::{Debug, Trace};
22use log::{debug, log_enabled, trace};
23use std::borrow::Borrow;
24use std::collections::{BTreeMap, HashMap};
25use util::count_vec_allocations;
26
27#[derive(Debug, PartialEq, Eq)]
29pub struct Election {
30 pub title: String,
32 pub num_candidates: usize,
34 pub num_seats: usize,
36 pub num_ballots: usize,
38 pub candidates: Vec<Candidate>,
40 pub ballots: Vec<Ballot>,
42 pub tie_order: HashMap<usize, usize>,
45}
46
47impl Election {
48 pub fn builder() -> ElectionBuilder {
50 ElectionBuilder::default()
51 }
52
53 pub fn has_any_tied_ballot(&self) -> bool {
55 self.ballots.iter().any(|b| b.has_tie())
56 }
57
58 pub(crate) fn debug_allocations(&self) {
59 if !log_enabled!(Debug) {
60 return;
61 }
62
63 let mut allocations = BTreeMap::new();
64 count_vec_allocations(&mut allocations, &self.ballots);
65 for b in &self.ballots {
66 b.count_allocations(&mut allocations);
67 }
68 let mut total_count = 0;
69 let mut total_size = 0;
70 for (size, count) in allocations.iter() {
71 total_count += count;
72 total_size += size * count;
73 debug!(
74 "Allocations of {size} bytes: {count} => {} bytes",
75 size * count
76 );
77 }
78 debug!("Ballots use {total_size} bytes in {total_count} allocations");
79 let ballots_len = self.ballots.len() as f64;
80 debug!(
81 "Each ballot uses {} bytes in {} allocations",
82 total_size as f64 / ballots_len,
83 total_count as f64 / ballots_len
84 );
85
86 if !log_enabled!(Trace) {
87 return;
88 }
89
90 trace!("Allocated addresses:");
91 let mut diffs = BTreeMap::new();
92 let mut prev = None;
93 for b in &self.ballots {
94 for address in b.allocated_addresses() {
95 match prev {
96 None => trace!("- {address:#x?}"),
97 Some(prev) => {
98 let diff = if address >= prev {
99 trace!("- {address:#x?} (+{:#x?})", address - prev);
100 address - prev
101 } else {
102 trace!("- {address:#x?} (-{:#x?})", prev - address);
103 prev - address
104 };
105 *diffs.entry(diff.checked_ilog2().unwrap_or(0)).or_insert(0) += 1;
106 }
107 }
108 prev = Some(address);
109 }
110 }
111
112 trace!("Histogram of sequential jumps between allocated addresses:");
113 let max_count = diffs.values().max().unwrap();
114 for (&diff_log2, count) in diffs.iter() {
115 let start = if diff_log2 == 0 { 0 } else { 1u64 << diff_log2 };
116 let end = (1u64 << (diff_log2 + 1)) - 1;
117
118 let count_stars = count * 40 / max_count;
119 let count_spaces = 40 - count_stars;
120 let stars = &"****************************************"[..count_stars];
121 let spaces = &" "[..count_spaces];
122 trace!("{stars}{spaces} {start}..={end}: {count}");
123 }
124 }
125}
126
127#[derive(Default)]
129pub struct ElectionBuilder {
130 title: Option<String>,
131 num_seats: Option<usize>,
132 num_ballots: Option<usize>,
133 candidates: Vec<Candidate>,
134 ballots: Vec<Ballot>,
135 tie_order: Option<HashMap<usize, usize>>,
136}
137
138impl ElectionBuilder {
139 pub fn build(self) -> Election {
141 let num_ballots = self
142 .num_ballots
143 .unwrap_or_else(|| self.ballots.iter().map(|b| b.count()).sum());
144 let num_candidates = self.candidates.len();
145 Election {
146 title: self.title.unwrap(),
147 num_candidates,
148 num_seats: self.num_seats.unwrap(),
149 num_ballots,
150 candidates: self.candidates,
151 ballots: self.ballots,
152 tie_order: self
153 .tie_order
154 .unwrap_or_else(|| (0..num_candidates).map(|i| (i, i)).collect()),
155 }
156 }
157
158 pub fn title(mut self, title: &str) -> Self {
160 self.title = Some(title.to_owned());
161 self
162 }
163
164 pub fn num_seats(mut self, num_seats: usize) -> Self {
166 self.num_seats = Some(num_seats);
167 self
168 }
169
170 pub fn num_ballots(mut self, num_ballots: usize) -> Self {
172 self.num_ballots = Some(num_ballots);
173 self
174 }
175
176 pub fn check_num_ballots(mut self, num_ballots: usize) -> Self {
179 assert_eq!(num_ballots, self.ballots.iter().map(|b| b.count()).sum());
180 self.num_ballots = Some(num_ballots);
181 self
182 }
183
184 pub fn candidates(mut self, candidates: impl Into<Vec<Candidate>>) -> Self {
186 self.candidates = candidates.into();
187 self
188 }
189
190 pub fn ballots(mut self, ballots: impl Into<Vec<Ballot>>) -> Self {
192 self.ballots = ballots.into();
193 self
194 }
195
196 pub fn tie_order(mut self, order: impl Borrow<[usize]>) -> Self {
198 assert_eq!(order.borrow().len(), self.candidates.len());
199 let mut tie_order = HashMap::new();
200 for (i, &c) in order.borrow().iter().enumerate() {
201 assert!(c < self.candidates.len());
202 assert!(tie_order.insert(c, i).is_none());
203 }
204 self.tie_order = Some(tie_order);
205 self
206 }
207}
208
209#[derive(Debug, PartialEq, Eq)]
211pub struct Candidate {
212 pub nickname: String,
214 pub name: String,
216 pub is_withdrawn: bool,
218}
219
220impl Candidate {
221 pub fn new(nickname: impl Into<String>, is_withdrawn: bool) -> Self {
223 let nickname = nickname.into();
224 let mut name = nickname.clone().into_bytes();
225 if let Some(x) = name.first_mut() {
226 *x = x.to_ascii_uppercase();
227 }
228 let name = String::from_utf8(name).unwrap();
229 Candidate {
230 nickname,
231 name,
232 is_withdrawn,
233 }
234 }
235}
236
237#[derive(Debug, PartialEq, Eq)]
239pub struct ElectionResult {
240 pub elected: Vec<usize>,
242 pub not_elected: Vec<usize>,
244}