use std::collections::{HashMap, HashSet};
use std::marker::PhantomData;
use crate::category::Category;
use crate::category::entity::Entity;
use crate::category::relationship::Relationship;
use super::graph;
pub trait EquivalenceDef {
type Entity: Entity;
fn pairs() -> Vec<(Self::Entity, Self::Entity)>;
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Equivalent<E: Entity> {
pub left: E,
pub right: E,
}
impl<E: Entity> Relationship for Equivalent<E> {
type Object = E;
fn source(&self) -> E {
self.left.clone()
}
fn target(&self) -> E {
self.right.clone()
}
}
pub struct EquivalenceCategory<T: EquivalenceDef> {
_marker: PhantomData<T>,
}
fn symmetric_adj<E: Entity>(pairs: &[(E, E)]) -> HashMap<E, Vec<E>> {
let mut adj: HashMap<E, Vec<E>> = HashMap::new();
for (a, b) in pairs {
if a != b {
adj.entry(a.clone()).or_default().push(b.clone());
adj.entry(b.clone()).or_default().push(a.clone());
}
}
adj
}
impl<T: EquivalenceDef> Category for EquivalenceCategory<T> {
type Object = T::Entity;
type Morphism = Equivalent<T::Entity>;
fn identity(obj: &T::Entity) -> Equivalent<T::Entity> {
Equivalent {
left: obj.clone(),
right: obj.clone(),
}
}
fn compose(
f: &Equivalent<T::Entity>,
g: &Equivalent<T::Entity>,
) -> Option<Equivalent<T::Entity>> {
if f.right != g.left {
return None;
}
Some(Equivalent {
left: f.left.clone(),
right: g.right.clone(),
})
}
fn morphisms() -> Vec<Equivalent<T::Entity>> {
let entities = T::Entity::variants();
let adj = symmetric_adj(&T::pairs());
let mut morphisms = Vec::new();
for entity in &entities {
morphisms.push(Self::identity(entity));
for equiv in graph::reachable(entity, &adj) {
morphisms.push(Equivalent {
left: entity.clone(),
right: equiv,
});
}
}
morphisms
}
}
pub fn equivalent_to<T: EquivalenceDef>(entity: &T::Entity) -> Vec<T::Entity> {
let adj = symmetric_adj(&T::pairs());
graph::reachable(entity, &adj)
.into_iter()
.filter(|e| e != entity)
.collect()
}
pub fn equivalence_class<T: EquivalenceDef>(entity: &T::Entity) -> Vec<T::Entity> {
let mut class = vec![entity.clone()];
class.extend(equivalent_to::<T>(entity));
class
}
pub fn all_classes<T: EquivalenceDef>() -> Vec<Vec<T::Entity>> {
let entities = T::Entity::variants();
let adj = symmetric_adj(&T::pairs());
let mut seen = HashSet::new();
let mut classes = Vec::new();
for entity in &entities {
if seen.contains(entity) {
continue;
}
let mut class = vec![entity.clone()];
for equiv in graph::reachable(entity, &adj) {
class.push(equiv.clone());
seen.insert(equiv);
}
seen.insert(entity.clone());
classes.push(class);
}
classes
}
pub fn are_equivalent<T: EquivalenceDef>(a: &T::Entity, b: &T::Entity) -> bool {
if a == b {
return true;
}
equivalent_to::<T>(a).contains(b)
}
pub struct Symmetric<T: EquivalenceDef> {
_marker: PhantomData<T>,
}
impl<T: EquivalenceDef> Symmetric<T> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<T: EquivalenceDef> Default for Symmetric<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: EquivalenceDef> crate::logic::Axiom for Symmetric<T> {
fn description(&self) -> &str {
"equivalence is symmetric: if A ≡ B then B ≡ A"
}
fn holds(&self) -> bool {
for entity in T::Entity::variants() {
for equiv in equivalent_to::<T>(&entity) {
if !are_equivalent::<T>(&equiv, &entity) {
return false;
}
}
}
true
}
}
pub struct NoSelfEquivalence<T: EquivalenceDef> {
_marker: PhantomData<T>,
}
impl<T: EquivalenceDef> NoSelfEquivalence<T> {
pub fn new() -> Self {
Self {
_marker: PhantomData,
}
}
}
impl<T: EquivalenceDef> Default for NoSelfEquivalence<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: EquivalenceDef> crate::logic::Axiom for NoSelfEquivalence<T> {
fn description(&self) -> &str {
"no entity is declared equivalent to itself (reflexivity is implicit)"
}
fn holds(&self) -> bool {
T::pairs().iter().all(|(a, b)| a != b)
}
}