use crate::node::{null_node, Node, NodeRef};
use crate::tally::TallyList;
use crate::utilstring::to_hex;
use crate::VoteReference;
use std::collections::VecDeque;
use std::sync::Arc;
fn build_level(mut queue: VecDeque<Node>, nodes_use_cache: bool) -> Result<Vec<Node>, String> {
assert!(queue.len() % 2 == 0);
let mut nodes: Vec<Node> = vec![];
loop {
let left = queue.pop_front();
if left.is_none() {
return Ok(nodes);
}
let right = queue.pop_front();
nodes.push(Node::new(
None,
Some(Arc::new(left.unwrap())),
Some(Arc::new(right.unwrap())),
nodes_use_cache,
)?);
}
}
pub fn generate_tree(
mut votes: Vec<(VoteReference, TallyList)>,
nodes_use_cache: bool,
) -> Result<NodeRef, String> {
if votes.is_empty() {
return Ok(None);
}
votes.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mut last: Option<&VoteReference> = None;
for (v, _) in &votes {
if last == Some(v) {
return Err(format!(
"Cannot have duplicate vote references (found duplciate of '{}')",
to_hex(v)?
));
}
last = Some(v);
}
let leafs = votes
.into_iter()
.map(|v| Node::new(Some(v), None, None, nodes_use_cache));
let nodes: Result<Vec<Node>, String> = leafs
.into_iter()
.map(|l| Node::new(None, Some(Arc::new(l?)), None, nodes_use_cache))
.collect();
let nodes = nodes?;
let mut queue = VecDeque::from(nodes);
if queue.len() == 1 {
queue.push_back(null_node());
}
loop {
if queue.len() == 1 {
return Ok(Some(Arc::new(queue.pop_front().unwrap())));
}
if queue.len() % 2 != 0 {
queue.push_back(null_node());
}
queue = VecDeque::from(build_level(queue, nodes_use_cache)?);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::hash_node_ref;
use crate::node::is_null_node_ref;
use crate::Validation;
#[cfg(feature = "with-benchmarks")]
use test::Bencher;
#[test]
fn test_generate_tree_one_vote() {
let voter = [0xaa; 32];
let vote = vec![1, 0];
let root = generate_tree(vec![(voter, vote.clone())], false).unwrap();
let root = root.unwrap();
assert!(is_null_node_ref(&root.right));
let (found_voter, found_vote) = root
.left
.as_ref()
.unwrap()
.left
.as_ref()
.unwrap()
.vote
.as_ref()
.unwrap();
assert_eq!(&voter, found_voter);
assert_eq!(&vote, found_vote);
}
#[test]
fn test_generate_tree_is_sorted() {
let root = generate_tree(
vec![
([0xcc; 32], vec![1, 0]),
([0xaa; 32], vec![1, 0]),
([0xbb; 32], vec![1, 0]),
],
false,
)
.unwrap()
.unwrap();
let (found_voter, _) = root
.left
.as_ref()
.unwrap()
.left
.as_ref()
.unwrap()
.left
.as_ref()
.unwrap()
.vote
.as_ref()
.unwrap();
assert_eq!(&[0xaa; 32], found_voter);
let (found_voter, _) = root
.right
.as_ref()
.unwrap()
.left
.as_ref()
.unwrap()
.left
.as_ref()
.unwrap()
.vote
.as_ref()
.unwrap();
assert_eq!(&[0xcc; 32], found_voter);
}
#[test]
fn test_generate_tree_rejects_dupes() {
let error = generate_tree(
vec![
([0xaa; 32], vec![1, 0]),
([0xbb; 32], vec![1, 0]),
([0xaa; 32], vec![0, 1]), ],
false,
);
let error_msg = "Cannot have duplicate vote references (found duplciate of 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')";
assert_eq!(error.err().unwrap(), error_msg);
}
#[test]
fn test_with_and_without_cache_match() {
let cached = generate_tree(
vec![
([0xaa; 32], vec![1, 0]),
([0xbb; 32], vec![1, 0]),
([0xcc; 32], vec![0, 1]),
],
true,
)
.unwrap();
let not_cached = generate_tree(
vec![
([0xaa; 32], vec![1, 0]),
([0xbb; 32], vec![1, 0]),
([0xcc; 32], vec![0, 1]),
],
false,
)
.unwrap();
assert_eq!(
hash_node_ref(&cached, &Validation::Strict).unwrap(),
hash_node_ref(¬_cached, &Validation::Strict).unwrap()
);
}
#[cfg(feature = "with-benchmarks")]
#[bench]
fn bench_generate_tree_10k_no_cache(b: &mut Bencher) {
let votes = crate::utiltest::create_votes(10000);
b.iter(|| generate_tree(votes.clone(), false))
}
#[cfg(feature = "with-benchmarks")]
#[bench]
fn bench_generate_tree_10k_cache(b: &mut Bencher) {
let votes = crate::utiltest::create_votes(10000);
b.iter(|| generate_tree(votes.clone(), true))
}
}