zfc

Library for sets according to ZFC.
git clone https://git.philomathiclife.com/repos/zfc
Log | Files | Refs | README

commit d0519057f07b1ae329711066f87a696be54a0a23
Author: Zack Newman <zack@philomathiclife.com>
Date:   Thu, 24 Aug 2023 18:10:09 -0600

init

Diffstat:
A.gitignore | 2++
ACargo.toml | 37+++++++++++++++++++++++++++++++++++++
AREADME.md | 11+++++++++++
Abuild.rs | 12++++++++++++
Asrc/lib.rs | 278+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 340 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +Cargo.lock +target/** diff --git a/Cargo.toml b/Cargo.toml @@ -0,0 +1,37 @@ +[package] +authors = ["Zack Newman <zack@philomathiclife.com>"] +categories = ["mathematics", "no-std"] +description = "Trait that represents a set according to Zermelo–Fraenkel set theory with the axiom of choice (ZFC)" +documentation = "https://docs.rs/zfc/latest/zfc/index.html" +edition = "2021" +keywords = ["math", "set", "zfc"] +license = "MIT OR Apache-2.0" +name = "zfc" +readme = "README.md" +repository = "https://git.philomathiclife.com/repos/zfc/" +version = "0.1.0" + +[lib] +name = "zfc" +path = "src/lib.rs" + +[build-dependencies] +rustc_version = "0.4.0" + +[features] +std = [] + +[dependencies] +num-bigint = { version = "0.4.4", default-features = false } + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[badges] +maintenance = { status = "actively-developed" } + +[profile.release] +lto = true +panic = 'abort' +strip = true diff --git a/README.md b/README.md @@ -0,0 +1,11 @@ +# `zfc` + +`zfc` is a library for sets according to [Zermelo–Fraenkel set theory with the axiom of choice (ZFC)](https://en.wikipedia.org/wiki/Zermelo%E2%80%93Fraenkel_set_theory). + +## Status + +This package will be actively maintained until it is deemed “feature complete”. + +The crates are only tested on the `x86_64-unknown-linux-gnu` and `x86_64-unknown-openbsd` targets, but +they should work on any [Tier 1 with Host Tools](https://doc.rust-lang.org/beta/rustc/platform-support.html) +target. diff --git a/build.rs b/build.rs @@ -0,0 +1,12 @@ +use rustc_version::{version_meta, Channel}; + +fn main() { + // Set cfg flags depending on release channel + let channel = match version_meta().unwrap().channel { + Channel::Stable => "CHANNEL_STABLE", + Channel::Beta => "CHANNEL_BETA", + Channel::Nightly => "CHANNEL_NIGHTLY", + Channel::Dev => "CHANNEL_DEV", + }; + println!("cargo:rustc-cfg={}", channel) +} diff --git a/src/lib.rs b/src/lib.rs @@ -0,0 +1,278 @@ +//! # `zfc` +//! +//! `zfc` is a library for sets according to [Zermelo–Fraenkel set theory with the axiom of choice (ZFC)](https://en.wikipedia.org/wiki/Zermelo%E2%80%93Fraenkel_set_theory). +#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))] +#![no_std] +#![deny( + unsafe_code, + unused, + warnings, + clippy::all, + clippy::cargo, + clippy::complexity, + clippy::correctness, + clippy::nursery, + clippy::pedantic, + clippy::perf, + clippy::restriction, + clippy::style, + clippy::suspicious +)] +#![allow( + clippy::arithmetic_side_effects, + clippy::blanket_clippy_restriction_lints, + clippy::implicit_return, + clippy::missing_trait_methods, + clippy::single_char_lifetime_names +)] +extern crate alloc; +use alloc::collections::BTreeSet; +use core::borrow::Borrow; +use core::cmp::Ordering; +use core::ops::{Range, RangeInclusive, Sub}; +use num_bigint::BigUint; +/// Represents the quantity of elements in a [`Set`]. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +#[allow(clippy::exhaustive_enums)] +pub enum Cardinality { + /// The contained [`BigUint`] represents the quantity of elements in a finite `Set`. + Finite(BigUint), + /// The contained `BigUint` represents the [aleph number](https://en.wikipedia.org/wiki/Aleph_number) of a transfinite `Set`. + Transfinite(BigUint), +} +/// Represents a set according to [Zermelo–Fraenkel set theory with the axiom of choice (ZFC)](https://en.wikipedia.org/wiki/Zermelo%E2%80%93Fraenkel_set_theory). +/// Note that elements in a `Set` must not be distinguishable by order or frequency, so care must be taken in its implementation if the implementing type +/// exposes an API that distinguishes between the order or frequency of `Elem` (e.g., [`Vec`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html) where `Elem` = `T`). +pub trait Set { + /// The elements that make up the set. + /// Per ZFC, this must not be the same type as `Self` nor a recursive type based on `Self`. + type Elem: Eq + ?Sized; + /// Returns the cardinality of `self`. + fn cardinality(&self) -> Cardinality; + /// Returns `true` iff `Self` contains `elem`. + fn contains<Q>(&self, elem: &Q) -> bool + where + Q: Borrow<Self::Elem> + Eq + ?Sized; + /// Must conform to the following properties: + /// * Irreflexivity. + /// * Antisymmetry. + /// * Transitivity. + /// * ∀`a`, `b`: `Self`, `a.is_proper_subset(b)`⇒ ∀`e`: `Elem` | `a.contains(e)`, `b.contains(e)` ∧ ∃`f`: `Elem`, `b.contains(f) && !a.contains(f)`. + /// * ∀`a`, `b`: `Self`, `a.is_proper_subset(b)` ⇒ `a.is_subset(b)`. + /// * ∀`a`, `b`: `Self`, `a.is_proper_subset(b)` ⇔ `b.is_proper_superset(a)`. + fn is_proper_subset(&self, val: &Self) -> bool; + /// Must conform to the following properties: + /// * Reflexivity. + /// * Antisymmetry. + /// * Transitivity. + /// * ∀`a`, `b`: `Self`, `a.is_subset(b)`⇒ ∀`e`: `Elem` | `a.contains(e)`, `b.contains(e)`. + /// * ∀`a`, `b`: `Self`, `a.is_subset(b)` ⇔ `b.is_superset(a)`. + fn is_subset(&self, val: &Self) -> bool; + /// Must conform to the following properties: + /// * Irreflexivity. + /// * Antisymmetry. + /// * Transitivity. + /// * ∀`a`, `b`: `Self`, `a.is_proper_superset(b)` ⇒ `a.is_superset(b)`. + #[inline] + fn is_proper_superset(&self, val: &Self) -> bool { + val.is_proper_subset(self) + } + /// Must conform to the following properties: + /// * Reflexivity. + /// * Antisymmetry. + /// * Transitivity. + #[inline] + fn is_superset(&self, val: &Self) -> bool { + val.is_subset(self) + } + /// Read [`Set::is_proper_subset`]. + #[inline] + fn is_proper_subset_iter<T>(&self, val: &T) -> bool + where + Self::Elem: Borrow<T::Elem> + PartialEq<T::Elem>, + for<'a> &'a Self: IntoIterator<Item = &'a Self::Elem>, + T: Set + ?Sized, + T::Elem: PartialEq<Self::Elem>, + { + self.cardinality() < val.cardinality() + && self + .into_iter() + .try_fold( + (), + |(), elem| { + if val.contains(elem) { + Ok(()) + } else { + Err(()) + } + }, + ) + .map_or(false, |()| true) + } + /// Read [`Set::is_subset`]. + #[inline] + fn is_subset_iter<T>(&self, val: &T) -> bool + where + Self::Elem: Borrow<T::Elem> + PartialEq<T::Elem>, + for<'a> &'a Self: IntoIterator<Item = &'a Self::Elem>, + T: Set + ?Sized, + T::Elem: PartialEq<Self::Elem>, + { + self.cardinality() <= val.cardinality() + && self + .into_iter() + .try_fold( + (), + |(), elem| { + if val.contains(elem) { + Ok(()) + } else { + Err(()) + } + }, + ) + .map_or(false, |()| true) + } + /// Read [`Set::is_proper_superset`]. + #[inline] + fn is_proper_superset_iter<T>(&self, val: &T) -> bool + where + Self::Elem: PartialEq<T::Elem>, + T: Set + ?Sized, + for<'a> &'a T: IntoIterator<Item = &'a T::Elem>, + T::Elem: Borrow<Self::Elem> + PartialEq<Self::Elem>, + { + val.is_proper_subset_iter(self) + } + /// Read [`Set::is_superset`]. + #[inline] + fn is_superset_iter<T>(&self, val: &T) -> bool + where + Self::Elem: PartialEq<T::Elem>, + T: Set + ?Sized, + for<'a> &'a T: IntoIterator<Item = &'a T::Elem>, + T::Elem: Borrow<Self::Elem> + PartialEq<Self::Elem>, + { + val.is_subset_iter(self) + } +} +impl<T> Set for BTreeSet<T> +where + T: Ord, +{ + type Elem = T; + #[inline] + fn cardinality(&self) -> Cardinality { + Cardinality::Finite(self.len().into()) + } + #[inline] + fn contains<Q>(&self, elem: &Q) -> bool + where + Q: Borrow<Self::Elem> + Eq + ?Sized, + { + self.contains(elem.borrow()) + } + #[inline] + fn is_proper_subset(&self, val: &Self) -> bool { + self.len() < val.len() && self.intersection(val).count() == val.len() + } + #[inline] + fn is_subset(&self, val: &Self) -> bool { + self.len() <= val.len() && self.intersection(val).count() == val.len() + } +} +impl<T> Set for Range<T> +where + T: Ord, + for<'a, 'b> &'a T: Sub<&'b T>, + for<'a, 'b> <&'a T as Sub<&'b T>>::Output: Into<BigUint>, +{ + type Elem = T; + #[inline] + fn cardinality(&self) -> Cardinality { + Cardinality::Finite((&self.end - &self.start).into()) + } + #[inline] + fn contains<Q>(&self, elem: &Q) -> bool + where + Q: Borrow<Self::Elem> + Eq + ?Sized, + { + self.contains(elem.borrow()) + } + #[inline] + fn is_proper_subset(&self, val: &Self) -> bool { + match self.start.cmp(&val.start) { + Ordering::Less => false, + Ordering::Equal => self.end < val.end, + Ordering::Greater => self.end <= val.end, + } + } + #[inline] + fn is_subset(&self, val: &Self) -> bool { + self.start >= val.start && self.end <= val.end + } +} +impl<T> Set for RangeInclusive<T> +where + T: Ord, + for<'a, 'b> &'a T: Sub<&'b T>, + for<'a, 'b> <&'a T as Sub<&'b T>>::Output: Into<BigUint>, +{ + type Elem = T; + #[inline] + fn cardinality(&self) -> Cardinality { + Cardinality::Finite((self.end() - self.start()).into() + BigUint::new(alloc::vec![1])) + } + #[inline] + fn contains<Q>(&self, elem: &Q) -> bool + where + Q: Borrow<Self::Elem> + Eq + ?Sized, + { + self.contains(elem.borrow()) + } + #[inline] + fn is_proper_subset(&self, val: &Self) -> bool { + match self.start().cmp(val.start()) { + Ordering::Less => false, + Ordering::Equal => self.end() < val.end(), + Ordering::Greater => self.end() <= val.end(), + } + } + #[inline] + fn is_subset(&self, val: &Self) -> bool { + self.start() >= val.start() && self.end() <= val.end() + } +} +#[cfg(feature = "std")] +extern crate std; +#[cfg(feature = "std")] +use core::hash::{BuildHasher, Hash}; +#[cfg(feature = "std")] +use std::collections::HashSet; +#[cfg(feature = "std")] +impl<T, S> Set for HashSet<T, S> +where + T: Eq + Hash, + S: BuildHasher, +{ + type Elem = T; + #[inline] + fn cardinality(&self) -> Cardinality { + Cardinality::Finite(self.len().into()) + } + #[inline] + fn contains<Q>(&self, elem: &Q) -> bool + where + Q: Borrow<Self::Elem> + Eq + ?Sized, + { + self.contains(elem.borrow()) + } + #[inline] + fn is_proper_subset(&self, val: &Self) -> bool { + self.len() < val.len() && self.intersection(val).count() == val.len() + } + #[inline] + fn is_subset(&self, val: &Self) -> bool { + self.len() <= val.len() && self.intersection(val).count() == val.len() + } +}