1use std::collections::HashMap;
11use std::hash::Hash;
12
13use crate::borda::Ballot;
14
15pub fn rrf_merge<T>(ballots: &[Ballot<T>], k0: usize) -> Vec<T>
17where
18 T: Clone + Eq + Hash,
19{
20 if ballots.is_empty() {
21 return Vec::new();
22 }
23 let k0 = k0 as f64;
24 let mut scores: HashMap<T, f64> = HashMap::new();
25 let mut first_seen: Vec<T> = Vec::new();
26 for ballot in ballots {
27 for (pos, id) in ballot.iter().enumerate() {
28 if !scores.contains_key(id) {
29 first_seen.push(id.clone());
30 }
31 let rank = (pos + 1) as f64;
32 *scores.entry(id.clone()).or_insert(0.0) += 1.0 / (k0 + rank);
33 }
34 }
35 let mut ranked = first_seen;
36 ranked.sort_by(|a, b| scores[b].total_cmp(&scores[a]));
37 ranked
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43 use crate::borda::borda_merge;
44
45 #[test]
46 fn two_lists_of_three() {
47 let a = vec!["x", "y", "z"];
48 let b = vec!["y", "x", "z"];
49 let out = rrf_merge(&[a, b], 60);
50 assert_eq!(out, vec!["x", "y", "z"]);
52 }
53
54 #[test]
55 fn omitted_rank_lifts_z_above_borda_last() {
56 let a = vec!["x", "y", "z"];
57 let b = vec!["y", "x", "z"];
58 let c = vec!["z"];
59 let borda = borda_merge(&[a.clone(), b.clone(), c.clone()], 3);
60 assert_eq!(borda, vec!["x", "y", "z"]);
62 let out = rrf_merge(&[a, b, c], 60);
63 assert_eq!(out[0], "z");
64 }
65}