use distances::Number;
use crate::Cluster;
pub trait PartitionCriterion<U: Number>: Send + Sync {
fn check(&self, c: &Cluster<U>) -> bool;
}
#[allow(clippy::module_name_repetitions)]
pub struct PartitionCriteria<U: Number> {
criteria: Vec<Box<dyn PartitionCriterion<U>>>,
check_all: bool,
}
impl<U: Number> Default for PartitionCriteria<U> {
fn default() -> Self {
Self::new(true).with_min_cardinality(1)
}
}
impl<U: Number> PartitionCriteria<U> {
#[must_use]
pub fn new(check_all: bool) -> Self {
Self {
criteria: Vec::new(),
check_all,
}
}
#[must_use]
pub fn with_max_depth(mut self, threshold: usize) -> Self {
self.criteria.push(Box::new(MaxDepth(threshold)));
self
}
#[must_use]
pub fn with_min_cardinality(mut self, threshold: usize) -> Self {
self.criteria.push(Box::new(MinCardinality(threshold)));
self
}
#[allow(dead_code)]
pub(crate) fn with_custom(mut self, c: Box<dyn PartitionCriterion<U>>) -> Self {
self.criteria.push(c);
self
}
}
impl<U: Number> PartitionCriterion<U> for PartitionCriteria<U> {
fn check(&self, cluster: &Cluster<U>) -> bool {
!cluster.is_singleton()
&& if self.check_all {
self.criteria.iter().all(|c| c.check(cluster))
} else {
self.criteria.iter().any(|c| c.check(cluster))
}
}
}
#[derive(Debug, Clone)]
pub struct MaxDepth(usize);
impl<U: Number> PartitionCriterion<U> for MaxDepth {
fn check(&self, c: &Cluster<U>) -> bool {
c.depth() < self.0
}
}
#[derive(Debug, Clone)]
pub struct MinCardinality(usize);
impl<U: Number> PartitionCriterion<U> for MinCardinality {
fn check(&self, c: &Cluster<U>) -> bool {
c.cardinality() > self.0
}
}