use std::collections::BTreeSet;
use std::collections::btree_set;
use crate::quorum::GridQuorumSetError;
use crate::quorum::QuorumIntersection;
use crate::quorum::QuorumSet;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GridRule {
FullRow,
OnePerRow,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GridQuorumSet<ID> {
grid: Vec<Vec<ID>>,
rule: GridRule,
}
impl<ID> GridQuorumSet<ID>
where ID: Ord + Clone
{
pub fn new(grid: Vec<Vec<ID>>, rule: GridRule) -> Result<Self, GridQuorumSetError<ID>> {
check_grid(&grid)?;
let quorum_set = Self { grid, rule };
Ok(quorum_set)
}
pub fn grid(&self) -> &[Vec<ID>] {
&self.grid
}
pub fn rule(&self) -> GridRule {
self.rule
}
fn accepts(grid: &[Vec<ID>], rule: GridRule, candidates: &BTreeSet<&ID>) -> bool {
match rule {
GridRule::FullRow => {
for row in grid.iter() {
let holds_row = row.iter().all(|id| candidates.contains(id));
if holds_row {
return true;
}
}
false
}
GridRule::OnePerRow => {
for row in grid.iter() {
let meets_row = row.iter().any(|id| candidates.contains(id));
if !meets_row {
return false;
}
}
true
}
}
}
}
impl<ID> QuorumSet for GridQuorumSet<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 candidates: BTreeSet<&ID> = ids.collect();
Self::accepts(&self.grid, self.rule, &candidates)
}
fn ids(&self) -> Self::Iter {
let cells = self.grid.iter().flatten();
let ids: BTreeSet<ID> = cells.cloned().collect();
ids.into_iter()
}
}
impl<ID> QuorumIntersection<GridQuorumSet<ID>> for GridQuorumSet<ID>
where ID: Ord + Clone
{
fn intersects_with(&self, other: &GridQuorumSet<ID>) -> Option<bool> {
let (full_row, compared) = match (self.rule, other.rule) {
(GridRule::FullRow, _) => (self, other),
(_, GridRule::FullRow) => (other, self),
(GridRule::OnePerRow, GridRule::OnePerRow) => return None,
};
let meeting_rule = match compared.rule {
GridRule::FullRow => GridRule::OnePerRow,
GridRule::OnePerRow => GridRule::FullRow,
};
for row in full_row.grid.iter() {
let candidates = row.iter().collect();
let meets_every_quorum = Self::accepts(&compared.grid, meeting_rule, &candidates);
if !meets_every_quorum {
return Some(false);
}
}
Some(true)
}
}
fn check_grid<ID>(grid: &[Vec<ID>]) -> Result<(), GridQuorumSetError<ID>>
where ID: Ord + Clone {
let Some(first_row) = grid.first() else {
return Err(GridQuorumSetError::NoRow);
};
let column_count = first_row.len();
if column_count == 0 {
return Err(GridQuorumSetError::NoColumn);
}
for (row, cells) in grid.iter().enumerate() {
let len = cells.len();
if len != column_count {
let ragged = GridQuorumSetError::RaggedRow {
row,
len,
column_count,
};
return Err(ragged);
}
}
let mut seen = BTreeSet::new();
for id in grid.iter().flatten() {
let is_new = seen.insert(id);
if !is_new {
let duplicate = GridQuorumSetError::DuplicateId { id: id.clone() };
return Err(duplicate);
}
}
Ok(())
}