use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::btree_set;
use crate::quorum::QuorumIntersection;
use crate::quorum::QuorumSet;
use crate::quorum::WeightedQuorumSetError;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WeightedQuorumSet<ID> {
weights: BTreeMap<ID, u64>,
threshold: u64,
}
impl<ID> WeightedQuorumSet<ID>
where ID: Ord + Clone
{
pub fn new(
threshold: u64,
weights: BTreeMap<ID, u64>,
) -> Result<Self, WeightedQuorumSetError<ID>> {
for (id, weight) in weights.iter() {
if *weight == 0 {
let id = id.clone();
return Err(WeightedQuorumSetError::ZeroWeight { id });
}
}
if threshold == 0 {
return Err(WeightedQuorumSetError::ZeroThreshold);
}
let rule = Self { weights, threshold };
Ok(rule)
}
pub fn weights(&self) -> &BTreeMap<ID, u64> {
&self.weights
}
pub fn threshold(&self) -> u64 {
self.threshold
}
}
impl<ID> QuorumSet for WeightedQuorumSet<ID>
where ID: Ord + Clone
{
type Id = ID;
type Iter = btree_set::IntoIter<ID>;
fn is_quorum<'a, I>(&self, ids: I) -> bool
where
ID: 'a,
I: Iterator<Item = &'a ID> + Clone,
{
let threshold = self.threshold();
let mut sum = 0u64;
for (id, weight) in self.weights.iter() {
if ids.clone().any(|candidate| candidate == id) {
sum = sum.saturating_add(*weight);
if sum >= threshold {
return true;
}
}
}
false
}
fn ids(&self) -> Self::Iter {
let keys = self.weights.keys();
let ids: BTreeSet<ID> = keys.cloned().collect();
ids.into_iter()
}
}
impl<ID> QuorumIntersection<WeightedQuorumSet<ID>> for WeightedQuorumSet<ID>
where ID: Ord + Clone
{
fn intersects_with(&self, other: &WeightedQuorumSet<ID>) -> Option<bool> {
if self.weights != other.weights {
return None;
}
let mut total_weight = 0u128;
for weight in self.weights.values() {
total_weight += u128::from(*weight);
}
let thresholds = u128::from(self.threshold()) + u128::from(other.threshold());
if thresholds > total_weight {
Some(true)
} else {
None
}
}
}