stv_rs/types/
ballot.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Types to represent ballots in an election.
16
17use super::util::{address_of_slice, count_slice_allocations};
18use std::collections::BTreeMap;
19
20/// Ballot cast in the election.
21pub type Ballot = BallotImpl<BoxedFlatOrder>;
22
23/// Ballot cast in the election.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct BallotImpl<O: Order + Clone> {
26    /// Number of electors that have cast this ballot.
27    count: usize,
28    /// Whether this ballot contains candidates ranked equally.
29    has_tie: bool,
30    /// Ordering of candidates in this ballot.
31    order: O,
32}
33
34impl<O: Order + Clone> BallotImpl<O> {
35    /// Constructs a new ballot.
36    #[inline(always)]
37    pub fn new(
38        count: usize,
39        order: impl IntoIterator<Item = impl IntoIterator<Item = usize>>,
40    ) -> Self {
41        let order = O::new(order);
42        let has_tie = order.order().any(|ranking| ranking.len() != 1);
43        Self {
44            count,
45            has_tie,
46            order,
47        }
48    }
49
50    /// Returns an empty ballot with a count of zero.
51    #[cfg(test)]
52    pub(crate) fn empty() -> Self {
53        Self {
54            count: 0,
55            has_tie: false,
56            order: O::empty(),
57        }
58    }
59
60    /// Returns an empty ballot with the given count.
61    #[cfg(test)]
62    pub(crate) fn empties(count: usize) -> Self {
63        Self {
64            count,
65            has_tie: false,
66            order: O::empty(),
67        }
68    }
69
70    /// Returns the number of times this ballot was cast.
71    #[inline(always)]
72    pub fn count(&self) -> usize {
73        self.count
74    }
75
76    /// Returns the order of candidates in the ballot. The iterator yields
77    /// candidates from most preferred to least preferred. Each item
78    /// contains a set of candidates ranked equally.
79    #[inline(always)]
80    pub fn order(&self) -> impl Iterator<Item = &[impl Into<usize> + Copy + '_]> + '_ {
81        self.order.order()
82    }
83
84    /// Returns whether this ballot is empty.
85    #[inline(always)]
86    pub fn is_empty(&self) -> bool {
87        self.order.is_empty()
88    }
89
90    /// Returns the number of successive ranks in the ballot order.
91    #[inline(always)]
92    pub(crate) fn order_len(&self) -> usize {
93        self.order.len()
94    }
95
96    /// Returns the rank at the given index in the ballot order.
97    #[inline(always)]
98    pub(crate) fn order_at(&self, i: usize) -> &[impl Into<usize> + Copy + '_] {
99        self.order.at(i)
100    }
101
102    /// Returns whether this ballot contains candidates ranked equally.
103    #[inline(always)]
104    pub fn has_tie(&self) -> bool {
105        self.has_tie
106    }
107
108    /// Checks that a ballot is valid, i.e. that no candidate appears twice in
109    /// the ballot.
110    pub fn validate(&self) {
111        assert!(self.count() > 0);
112        let mut all: Vec<usize> = self.candidates().map(|&x| x.into()).collect();
113        all.sort_unstable();
114        let len = all.len();
115        all.dedup();
116        assert_eq!(len, all.len());
117    }
118
119    /// Returns the set of candidates present in this ballot.
120    #[inline(always)]
121    fn candidates(&self) -> impl Iterator<Item = &(impl Into<usize> + Copy + '_)> + '_ {
122        self.order.candidates()
123    }
124
125    #[inline(always)]
126    pub(super) fn count_allocations(&self, allocations: &mut BTreeMap<usize, usize>) {
127        self.order.count_allocations(allocations)
128    }
129
130    #[inline(always)]
131    pub(super) fn allocated_addresses(&self) -> impl Iterator<Item = usize> + '_ {
132        self.order.allocated_addresses()
133    }
134}
135
136/// Ordering of candidates in a ballot.
137pub trait Order {
138    /// Constructs a new ballot order.
139    fn new(order: impl IntoIterator<Item = impl IntoIterator<Item = usize>>) -> Self;
140
141    /// Constructs an empty ballot order.
142    #[cfg(test)]
143    fn empty() -> Self;
144
145    /// Returns an iterator over the ranks in this ballot order. Each item
146    /// contains the set of candidates at that rank.
147    fn order(&self) -> impl Iterator<Item = &[impl Into<usize> + Copy]>;
148
149    /// Returns the number of ranks.
150    fn len(&self) -> usize;
151
152    /// Returns whether this order is empty.
153    fn is_empty(&self) -> bool;
154
155    /// Returns the rank at a given index.
156    fn at(&self, i: usize) -> &[impl Into<usize> + Copy];
157
158    /// Returns an iterator over all the candidates present in this ballot
159    /// order.
160    fn candidates(&self) -> impl Iterator<Item = &(impl Into<usize> + Copy)>;
161
162    /// Counts the allocations used by this type, reporting them into the given
163    /// map.
164    fn count_allocations(&self, allocations: &mut BTreeMap<usize, usize>);
165
166    /// Returns the addresses of heap-allocated items contained in this object.
167    fn allocated_addresses(&self) -> impl Iterator<Item = usize>;
168}
169
170/// Ordering of candidates in a ballot.
171#[derive(Debug, Clone, PartialEq, Eq)]
172pub struct BoxedFlatOrder {
173    /// Flattened list of all the candidates in the ballot.
174    order: Box<[u8]>,
175    /// Indices where each rank starts in this order.
176    order_indices: Box<[u8]>,
177}
178
179impl Order for BoxedFlatOrder {
180    fn new(into_order: impl IntoIterator<Item = impl IntoIterator<Item = usize>>) -> Self {
181        let mut order = Vec::new();
182        let mut order_indices = vec![0];
183        for rank in into_order.into_iter() {
184            for x in rank.into_iter() {
185                order.push(x.try_into().unwrap());
186            }
187            order_indices.push(order.len().try_into().unwrap());
188        }
189
190        if order.is_empty() {
191            order_indices = Vec::new();
192        }
193
194        Self {
195            order: order.into_boxed_slice(),
196            order_indices: order_indices.into_boxed_slice(),
197        }
198    }
199
200    #[cfg(test)]
201    fn empty() -> Self {
202        Self {
203            order: Box::new([]),
204            order_indices: Box::new([]),
205        }
206    }
207
208    #[inline(always)]
209    fn order(&self) -> impl Iterator<Item = &[impl Into<usize> + Copy]> {
210        // TODO: Use array_windows once stable.
211        let count = if self.order_indices.is_empty() {
212            0
213        } else {
214            self.order_indices.len() - 1
215        };
216        (0..count).map(|i| {
217            let start = self.order_indices[i];
218            let end = self.order_indices[i + 1];
219            &self.order[start.into()..end.into()]
220        })
221    }
222
223    #[inline(always)]
224    fn len(&self) -> usize {
225        self.order_indices.len() - 1
226    }
227
228    #[inline(always)]
229    fn is_empty(&self) -> bool {
230        self.order_indices.is_empty()
231    }
232
233    #[inline(always)]
234    fn at(&self, i: usize) -> &[impl Into<usize> + Copy] {
235        let start = self.order_indices[i];
236        let end = self.order_indices[i + 1];
237        &self.order[start.into()..end.into()]
238    }
239
240    #[inline(always)]
241    fn candidates(&self) -> impl Iterator<Item = &(impl Into<usize> + Copy)> {
242        self.order.iter()
243    }
244
245    #[inline(always)]
246    fn count_allocations(&self, allocations: &mut BTreeMap<usize, usize>) {
247        count_slice_allocations(allocations, &self.order);
248        count_slice_allocations(allocations, &self.order_indices);
249    }
250
251    #[inline(always)]
252    fn allocated_addresses(&self) -> impl Iterator<Item = usize> {
253        [
254            address_of_slice(&self.order),
255            address_of_slice(&self.order_indices),
256        ]
257        .into_iter()
258        .flatten()
259    }
260}