alice-math 0.1.0

A topology and algebra library from first principles
Documentation
use alloc::collections::BTreeSet;
use alloc::vec::Vec;

pub trait TopologicalSpace {
    type Point: Clone + Eq;
    fn is_open(&self, subset: &BTreeSet<Self::Point>) -> bool;
    fn is_closed(&self, subset: &BTreeSet<Self::Point>) -> bool;
    fn closure(&self, subset: &BTreeSet<Self::Point>) -> BTreeSet<Self::Point>;
    fn interior(&self, subset: &BTreeSet<Self::Point>) -> BTreeSet<Self::Point>;
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FiniteTopologicalSpace<P: Clone + Eq + Ord> {
    points: BTreeSet<P>,
    open_sets: BTreeSet<BTreeSet<P>>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TopologyError {
    MissingEmpty,
    MissingTotal,
    NotClosedUnderUnion,
    NotClosedUnderIntersection,
    OpenSetContainsExternalPoint,
}

impl<P: Clone + Eq + Ord> FiniteTopologicalSpace<P> {
    pub fn new(
        points: BTreeSet<P>,
        open_sets: BTreeSet<BTreeSet<P>>,
    ) -> Result<Self, TopologyError> {
        for open in &open_sets {
            for p in open {
                if !points.contains(p) {
                    return Err(TopologyError::OpenSetContainsExternalPoint);
                }
            }
        }

        if !open_sets.contains(&BTreeSet::new()) {
            return Err(TopologyError::MissingEmpty);
        }

        if !open_sets.contains(&points) {
            return Err(TopologyError::MissingTotal);
        }

        let open_vec: Vec<&BTreeSet<P>> = open_sets.iter().collect();
        let n = open_vec.len();

        for i in 0..n {
            for j in 0..n {
                let union: BTreeSet<P> = open_vec[i]
                    .union(open_vec[j])
                    .cloned()
                    .collect();
                if !open_sets.contains(&union) {
                    return Err(TopologyError::NotClosedUnderUnion);
                }

                let inter: BTreeSet<P> = open_vec[i]
                    .intersection(open_vec[j])
                    .cloned()
                    .collect();
                if !open_sets.contains(&inter) {
                    return Err(TopologyError::NotClosedUnderIntersection);
                }
            }
        }

        Ok(Self { points, open_sets })
    }

    pub fn indiscrete(points: BTreeSet<P>) -> Self {
        let mut open_sets = BTreeSet::new();
        open_sets.insert(BTreeSet::new());
        open_sets.insert(points.clone());
        Self { points, open_sets }
    }

    pub fn discrete(points: BTreeSet<P>) -> Self {
        let mut open_sets = BTreeSet::new();
        Self::all_subsets(&points, &mut open_sets);
        Self { points, open_sets }
    }

    pub fn points(&self) -> &BTreeSet<P> {
        &self.points
    }

    pub fn open_sets(&self) -> &BTreeSet<BTreeSet<P>> {
        &self.open_sets
    }

    fn all_subsets(set: &BTreeSet<P>, out: &mut BTreeSet<BTreeSet<P>>) {
        let items: Vec<&P> = set.iter().collect();
        let n = items.len();
        for mask in 0u64..(1u64 << n) {
            let subset: BTreeSet<P> = (0..n)
                .filter(|&i| mask & (1 << i) != 0)
                .map(|i| items[i].clone())
                .collect();
            out.insert(subset);
        }
    }

    pub fn verify_axioms(&self) -> Result<(), TopologyError> {
        Self::new(self.points.clone(), self.open_sets.clone()).map(|_| ())
    }
}

impl<P: Clone + Eq + Ord> TopologicalSpace for FiniteTopologicalSpace<P> {
    type Point = P;

    fn is_open(&self, subset: &BTreeSet<P>) -> bool {
        self.open_sets.contains(subset)
    }

    fn is_closed(&self, subset: &BTreeSet<P>) -> bool {
        let complement: BTreeSet<P> = self
            .points
            .difference(subset)
            .cloned()
            .collect();
        self.is_open(&complement)
    }

    fn closure(&self, subset: &BTreeSet<P>) -> BTreeSet<P> {
        self.open_sets
            .iter()
            .filter(|closed| {
                let complement: BTreeSet<P> = self
                    .points
                    .difference(*closed)
                    .cloned()
                    .collect();
                self.is_open(closed) && subset.is_subset(&complement)
            })
            .fold(self.points.clone(), |acc, closed| {
                let complement: BTreeSet<P> = self
                    .points
                    .difference(closed)
                    .cloned()
                    .collect();
                acc.intersection(&complement).cloned().collect()
            })
    }

    fn interior(&self, subset: &BTreeSet<P>) -> BTreeSet<P> {
        self.open_sets
            .iter()
            .filter(|open| open.is_subset(subset))
            .fold(BTreeSet::new(), |acc, open| {
                acc.union(open).cloned().collect()
            })
    }
}