use std::collections::BTreeSet;
use crate::{FiniteCarrier, UnaryRelation, traits::FiniteRelation};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BinaryRelation<A: Ord, B: Ord> {
pairs: BTreeSet<(A, B)>,
}
impl<A: Ord, B: Ord> BinaryRelation<A, B> {
#[must_use]
pub fn new() -> Self {
Self {
pairs: BTreeSet::new(),
}
}
#[must_use]
pub fn from_pairs<I>(pairs: I) -> Self
where
I: IntoIterator<Item = (A, B)>,
{
pairs.into_iter().collect()
}
pub fn insert(&mut self, left: A, right: B) -> bool {
self.pairs.insert((left, right))
}
#[must_use]
pub fn contains(&self, left: &A, right: &B) -> bool {
self.pairs.iter().any(|(candidate_left, candidate_right)| {
candidate_left == left && candidate_right == right
})
}
pub fn iter(&self) -> impl Iterator<Item = &(A, B)> {
self.pairs.iter()
}
#[must_use]
pub fn domain(&self) -> UnaryRelation<A>
where
A: Clone,
{
self.pairs.iter().map(|(left, _)| left.clone()).collect()
}
#[must_use]
pub fn range(&self) -> UnaryRelation<B>
where
B: Clone,
{
self.pairs.iter().map(|(_, right)| right.clone()).collect()
}
#[must_use]
pub fn converse(&self) -> BinaryRelation<B, A>
where
A: Clone,
B: Clone,
{
self.pairs
.iter()
.map(|(left, right)| (right.clone(), left.clone()))
.collect()
}
#[must_use]
pub fn union(&self, other: &Self) -> Self
where
A: Clone,
B: Clone,
{
self.pairs.union(&other.pairs).cloned().collect()
}
#[must_use]
pub fn intersection(&self, other: &Self) -> Self
where
A: Clone,
B: Clone,
{
self.pairs.intersection(&other.pairs).cloned().collect()
}
#[must_use]
pub fn difference(&self, other: &Self) -> Self
where
A: Clone,
B: Clone,
{
self.pairs.difference(&other.pairs).cloned().collect()
}
#[must_use]
pub fn restrict_domain(&self, allowed: &UnaryRelation<A>) -> Self
where
A: Clone,
B: Clone,
{
self.pairs
.iter()
.filter(|(left, _)| allowed.contains(left))
.cloned()
.collect()
}
#[must_use]
pub fn restrict_range(&self, allowed: &UnaryRelation<B>) -> Self
where
A: Clone,
B: Clone,
{
self.pairs
.iter()
.filter(|(_, right)| allowed.contains(right))
.cloned()
.collect()
}
#[must_use]
pub fn image(&self, sources: &UnaryRelation<A>) -> UnaryRelation<B>
where
B: Clone,
{
self.pairs
.iter()
.filter(|(left, _)| sources.contains(left))
.map(|(_, right)| right.clone())
.collect()
}
#[must_use]
pub fn preimage(&self, targets: &UnaryRelation<B>) -> UnaryRelation<A>
where
A: Clone,
{
self.pairs
.iter()
.filter(|(_, right)| targets.contains(right))
.map(|(left, _)| left.clone())
.collect()
}
#[must_use]
pub fn compose<C>(&self, rhs: &BinaryRelation<B, C>) -> BinaryRelation<A, C>
where
A: Clone,
B: Clone,
C: Clone + Ord,
{
let mut result = BTreeSet::new();
for (left, middle_left) in &self.pairs {
for (middle_right, right) in &rhs.pairs {
if middle_left == middle_right {
result.insert((left.clone(), right.clone()));
}
}
}
BinaryRelation { pairs: result }
}
#[must_use]
pub fn to_vec(&self) -> Vec<(A, B)>
where
A: Clone,
B: Clone,
{
self.pairs.iter().cloned().collect()
}
}
impl<A: Ord, B: Ord> Default for BinaryRelation<A, B> {
fn default() -> Self {
Self::new()
}
}
impl<T: Ord> BinaryRelation<T, T> {
fn identity_from_iter<'a, I>(carrier: I) -> Self
where
T: Clone + 'a,
I: IntoIterator<Item = &'a T>,
{
carrier
.into_iter()
.map(|value| (value.clone(), value.clone()))
.collect()
}
fn is_reflexive_over<'a, I>(&self, carrier: I) -> bool
where
T: 'a,
I: IntoIterator<Item = &'a T>,
{
carrier.into_iter().all(|value| self.contains(value, value))
}
fn is_irreflexive_over<'a, I>(&self, carrier: I) -> bool
where
T: 'a,
I: IntoIterator<Item = &'a T>,
{
carrier
.into_iter()
.all(|value| !self.contains(value, value))
}
#[must_use]
pub fn carrier(&self) -> UnaryRelation<T>
where
T: Clone,
{
self.domain().union(&self.range())
}
#[must_use]
pub fn identity(carrier: &UnaryRelation<T>) -> Self
where
T: Clone,
{
Self::identity_from_iter(carrier.iter())
}
#[must_use]
pub fn identity_on(carrier: &FiniteCarrier<T>) -> Self
where
T: Clone,
{
Self::identity_from_iter(carrier.iter())
}
#[must_use]
pub fn transitive_closure(&self) -> Self
where
T: Clone,
{
let mut closure = self.clone();
let mut changed = true;
while changed {
changed = false;
let snapshot = closure.to_vec();
for (left, middle_left) in &snapshot {
for (middle_right, right) in &snapshot {
if middle_left == middle_right
&& closure.pairs.insert((left.clone(), right.clone()))
{
changed = true;
}
}
}
}
closure
}
#[must_use]
pub fn reflexive_transitive_closure(&self, carrier: &UnaryRelation<T>) -> Self
where
T: Clone,
{
self.transitive_closure().union(&Self::identity(carrier))
}
#[must_use]
pub fn reflexive_transitive_closure_on(&self, carrier: &FiniteCarrier<T>) -> Self
where
T: Clone,
{
self.transitive_closure().union(&Self::identity_on(carrier))
}
#[must_use]
pub fn is_reflexive(&self, carrier: &UnaryRelation<T>) -> bool {
self.is_reflexive_over(carrier.iter())
}
#[must_use]
pub fn is_reflexive_on(&self, carrier: &FiniteCarrier<T>) -> bool {
self.is_reflexive_over(carrier.iter())
}
#[must_use]
pub fn is_irreflexive(&self, carrier: &UnaryRelation<T>) -> bool {
self.is_irreflexive_over(carrier.iter())
}
#[must_use]
pub fn is_irreflexive_on(&self, carrier: &FiniteCarrier<T>) -> bool {
self.is_irreflexive_over(carrier.iter())
}
#[must_use]
pub fn is_symmetric(&self) -> bool {
self.pairs
.iter()
.all(|(left, right)| self.contains(right, left))
}
#[must_use]
pub fn is_antisymmetric(&self) -> bool {
self.pairs
.iter()
.all(|(left, right)| left == right || !self.contains(right, left))
}
#[must_use]
pub fn is_transitive(&self) -> bool
where
T: Clone,
{
let snapshot = self.to_vec();
snapshot.iter().all(|(left, middle_left)| {
snapshot.iter().all(|(middle_right, right)| {
middle_left != middle_right || self.contains(left, right)
})
})
}
#[must_use]
pub fn is_equivalence(&self, carrier: &UnaryRelation<T>) -> bool
where
T: Clone,
{
self.is_reflexive(carrier) && self.is_symmetric() && self.is_transitive()
}
#[must_use]
pub fn is_equivalence_on(&self, carrier: &FiniteCarrier<T>) -> bool
where
T: Clone,
{
self.is_reflexive_on(carrier) && self.is_symmetric() && self.is_transitive()
}
#[must_use]
pub fn is_partial_order(&self, carrier: &UnaryRelation<T>) -> bool
where
T: Clone,
{
self.is_reflexive(carrier) && self.is_antisymmetric() && self.is_transitive()
}
#[must_use]
pub fn is_partial_order_on(&self, carrier: &FiniteCarrier<T>) -> bool
where
T: Clone,
{
self.is_reflexive_on(carrier) && self.is_antisymmetric() && self.is_transitive()
}
}
impl<A: Ord, B: Ord> FiniteRelation for BinaryRelation<A, B> {
fn len(&self) -> usize {
self.pairs.len()
}
}
impl<A: Ord, B: Ord> FromIterator<(A, B)> for BinaryRelation<A, B> {
fn from_iter<I: IntoIterator<Item = (A, B)>>(iter: I) -> Self {
Self {
pairs: iter.into_iter().collect(),
}
}
}
impl<A: Ord, B: Ord> Extend<(A, B)> for BinaryRelation<A, B> {
fn extend<I: IntoIterator<Item = (A, B)>>(&mut self, iter: I) {
self.pairs.extend(iter);
}
}
impl<A: Ord, B: Ord> IntoIterator for BinaryRelation<A, B> {
type Item = (A, B);
type IntoIter = std::collections::btree_set::IntoIter<(A, B)>;
fn into_iter(self) -> Self::IntoIter {
self.pairs.into_iter()
}
}
impl<'a, A: Ord, B: Ord> IntoIterator for &'a BinaryRelation<A, B> {
type Item = &'a (A, B);
type IntoIter = std::collections::btree_set::Iter<'a, (A, B)>;
fn into_iter(self) -> Self::IntoIter {
self.pairs.iter()
}
}