zfc

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

commit 93f87d9457f5eee1b2dd318804db59ecba7a64b6
parent e81c0b442b32a4b537b541bef4fd7a997a514644
Author: Zack Newman <zack@philomathiclife.com>
Date:   Tue,  5 Sep 2023 15:55:05 -0600

added boundedcardinality to set. impl more traits and methods.

Diffstat:
MCargo.toml | 2+-
Msrc/lib.rs | 430++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 418 insertions(+), 14 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "zfc" readme = "README.md" repository = "https://git.philomathiclife.com/repos/zfc/" -version = "0.1.1" +version = "0.2.0" [lib] name = "zfc" diff --git a/src/lib.rs b/src/lib.rs @@ -34,10 +34,10 @@ use core::fmt::{self, Display, Formatter}; use core::ops::{Range, RangeInclusive, Sub}; use num_bigint::BigUint; /// Represents the quantity of elements in a [`Set`]. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[allow(clippy::exhaustive_enums)] pub enum Cardinality { - /// The contained [`BigUint`] represents the quantity of elements in a finite `Set`. + /// 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), @@ -51,6 +51,89 @@ impl Cardinality { Self::Finite(ref val) | Self::Transfinite(ref val) => val, } } + /// Creates a `Cardinality::Finite` containing `value`. + #[inline] + #[must_use] + pub const fn from_biguint(value: BigUint) -> Self { + Self::Finite(value) + } + /// `Cardinality::Transfinite` is always greater. + /// For `Cardinality::Finite` the contained `BigUint` + /// is compared to `value`. + #[inline] + #[must_use] + pub fn cmp_u8(&self, value: &u8) -> Ordering { + match *self { + Self::Finite(ref card) => card.cmp(&(*value).into()), + Self::Transfinite(_) => Ordering::Greater, + } + } + /// `Cardinality::Transfinite` is always greater. + /// For `Cardinality::Finite` the contained `BigUint` + /// is compared to `value`. + #[inline] + #[must_use] + pub fn cmp_u16(&self, value: &u16) -> Ordering { + match *self { + Self::Finite(ref card) => card.cmp(&(*value).into()), + Self::Transfinite(_) => Ordering::Greater, + } + } + /// `Cardinality::Transfinite` is always greater. + /// For `Cardinality::Finite` the contained `BigUint` + /// is compared to `value`. + #[inline] + #[must_use] + pub fn cmp_u32(&self, value: &u32) -> Ordering { + match *self { + Self::Finite(ref card) => card.cmp(&(*value).into()), + Self::Transfinite(_) => Ordering::Greater, + } + } + /// `Cardinality::Transfinite` is always greater. + /// For `Cardinality::Finite` the contained `BigUint` + /// is compared to `value`. + #[inline] + #[must_use] + pub fn cmp_u64(&self, value: &u64) -> Ordering { + match *self { + Self::Finite(ref card) => card.cmp(&(*value).into()), + Self::Transfinite(_) => Ordering::Greater, + } + } + /// `Cardinality::Transfinite` is always greater. + /// For `Cardinality::Finite` the contained `BigUint` + /// is compared to `value`. + #[inline] + #[must_use] + pub fn cmp_u128(&self, value: &u128) -> Ordering { + match *self { + Self::Finite(ref card) => card.cmp(&(*value).into()), + Self::Transfinite(_) => Ordering::Greater, + } + } + /// `Cardinality::Transfinite` is always greater. + /// For `Cardinality::Finite` the contained `BigUint` + /// is compared to `value`. + #[inline] + #[must_use] + pub fn cmp_usize(&self, value: &usize) -> Ordering { + match *self { + Self::Finite(ref card) => card.cmp(&(*value).into()), + Self::Transfinite(_) => Ordering::Greater, + } + } + /// `Cardinality::Transfinite` is always greater. + /// For `Cardinality::Finite` the contained `BigUint` + /// is compared to `value`. + #[inline] + #[must_use] + pub fn cmp_biguint(&self, value: &BigUint) -> Ordering { + match *self { + Self::Finite(ref card) => card.cmp(value), + Self::Transfinite(_) => Ordering::Greater, + } + } } impl Display for Cardinality { #[allow(clippy::min_ident_chars)] @@ -72,7 +155,7 @@ impl fmt::Debug for Cardinality { impl From<BigUint> for Cardinality { #[inline] fn from(value: BigUint) -> Self { - Self::Finite(value) + Self::from_biguint(value) } } impl From<Cardinality> for BigUint { @@ -83,6 +166,278 @@ impl From<Cardinality> for BigUint { } } } +impl PartialEq<u8> for Cardinality { + #[inline] + fn eq(&self, other: &u8) -> bool { + match *self { + Self::Finite(ref card) => *card == BigUint::from(*other), + Self::Transfinite(_) => false, + } + } +} +impl PartialEq<u16> for Cardinality { + #[inline] + fn eq(&self, other: &u16) -> bool { + match *self { + Self::Finite(ref card) => *card == BigUint::from(*other), + Self::Transfinite(_) => false, + } + } +} +impl PartialEq<u32> for Cardinality { + #[inline] + fn eq(&self, other: &u32) -> bool { + match *self { + Self::Finite(ref card) => *card == BigUint::from(*other), + Self::Transfinite(_) => false, + } + } +} +impl PartialEq<u64> for Cardinality { + #[inline] + fn eq(&self, other: &u64) -> bool { + match *self { + Self::Finite(ref card) => *card == BigUint::from(*other), + Self::Transfinite(_) => false, + } + } +} +impl PartialEq<u128> for Cardinality { + #[inline] + fn eq(&self, other: &u128) -> bool { + match *self { + Self::Finite(ref card) => *card == BigUint::from(*other), + Self::Transfinite(_) => false, + } + } +} +impl PartialEq<usize> for Cardinality { + #[inline] + fn eq(&self, other: &usize) -> bool { + match *self { + Self::Finite(ref card) => *card == BigUint::from(*other), + Self::Transfinite(_) => false, + } + } +} +impl PartialEq<BigUint> for Cardinality { + #[inline] + fn eq(&self, other: &BigUint) -> bool { + match *self { + Self::Finite(ref card) => card == other, + Self::Transfinite(_) => false, + } + } +} +impl PartialOrd<u8> for Cardinality { + #[inline] + fn partial_cmp(&self, other: &u8) -> Option<Ordering> { + Some(self.cmp_u8(other)) + } +} +impl PartialOrd<u16> for Cardinality { + #[inline] + fn partial_cmp(&self, other: &u16) -> Option<Ordering> { + Some(self.cmp_u16(other)) + } +} +impl PartialOrd<u32> for Cardinality { + #[inline] + fn partial_cmp(&self, other: &u32) -> Option<Ordering> { + Some(self.cmp_u32(other)) + } +} +impl PartialOrd<u64> for Cardinality { + #[inline] + fn partial_cmp(&self, other: &u64) -> Option<Ordering> { + Some(self.cmp_u64(other)) + } +} +impl PartialOrd<u128> for Cardinality { + #[inline] + fn partial_cmp(&self, other: &u128) -> Option<Ordering> { + Some(self.cmp_u128(other)) + } +} +impl PartialOrd<usize> for Cardinality { + #[inline] + fn partial_cmp(&self, other: &usize) -> Option<Ordering> { + Some(self.cmp_usize(other)) + } +} +impl PartialOrd<BigUint> for Cardinality { + #[inline] + fn partial_cmp(&self, other: &BigUint) -> Option<Ordering> { + Some(self.cmp_biguint(other)) + } +} +/// Contains a lower and upper bound +/// [`Cardinality`] used to bound the cardinality +/// of a [`Set`]. +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BoundedCardinality { + /// Lower bound. + lower: Cardinality, + /// Upper bound. + upper: Cardinality, +} +impl BoundedCardinality { + /// Creates an instance of `BoundedCardinality`. + /// Returns `None` iff `lower > upper`. + #[inline] + #[must_use] + pub fn new(lower: Cardinality, upper: Cardinality) -> Option<Self> { + if lower > upper { + None + } else { + Some(Self { lower, upper }) + } + } + /// Creates an instance of `BoundedCardinality` + /// with the lower and upper bounds as `Cardinality::Finite` + /// of their respective values. + /// Returns `None` iff `lower > upper`. + #[inline] + #[must_use] + pub fn from_biguint(lower: BigUint, upper: BigUint) -> Option<Self> { + if lower > upper { + None + } else { + Some(Self { + lower: Cardinality::Finite(lower), + upper: Cardinality::Finite(upper), + }) + } + } + /// Creates an instance of `BoundedCardinality` + /// without verifying `lower <= upper`. + /// + /// # Safety + /// + /// `lower <= upper`. + #[allow(unsafe_code)] + #[inline] + #[must_use] + pub const unsafe fn new_unsafe(lower: Cardinality, upper: Cardinality) -> Self { + Self { lower, upper } + } + /// Creates an instance of `BoundedCardinality` + /// with the lower and upper bounds as `Cardinality::Finite` + /// of their respective values without verifying `lower <= upper`. + /// + /// # Safety + /// + /// `lower <= upper`. + #[allow(unsafe_code)] + #[inline] + #[must_use] + pub const unsafe fn from_biguint_unsafe(lower: BigUint, upper: BigUint) -> Self { + Self { + lower: Cardinality::Finite(lower), + upper: Cardinality::Finite(upper), + } + } + /// Returns a reference to the lower bound. + #[inline] + #[must_use] + pub const fn lower(&self) -> &Cardinality { + &self.lower + } + /// Returns the lower bound. + #[inline] + #[must_use] + pub fn to_lower(self) -> Cardinality { + self.lower + } + /// Returns a reference to the upper bound. + #[inline] + #[must_use] + pub const fn upper(&self) -> &Cardinality { + &self.upper + } + /// Returns the upper bound. + #[inline] + #[must_use] + pub fn to_upper(self) -> Cardinality { + self.upper + } + /// Returns a tuple containing the references to the lower and upper bounds respectively. + #[inline] + #[must_use] + pub const fn lower_upper(&self) -> (&Cardinality, &Cardinality) { + (&self.lower, &self.upper) + } + /// Returns a reference to the contained `BigUint` of `self.lower()`. + #[inline] + #[must_use] + pub const fn lower_biguint(&self) -> &BigUint { + self.lower.as_biguint() + } + /// Returns the lower bound as a `BigUint`. + #[inline] + #[must_use] + pub fn to_lower_biguint(self) -> BigUint { + self.lower.into() + } + /// Returns a reference to the contained `BigUint` of `self.upper()`. + #[inline] + #[must_use] + pub const fn upper_biguint(&self) -> &BigUint { + self.upper.as_biguint() + } + /// Returns the upper bound as a `BigUint`. + #[inline] + #[must_use] + pub fn to_upper_biguint(self) -> BigUint { + self.upper.into() + } + /// Returns a tuple of references to the contained `BigUint`s of `self.lower()` and `self.upper()` respectively. + #[inline] + #[must_use] + pub const fn lower_upper_biguint(&self) -> (&BigUint, &BigUint) { + (self.lower_biguint(), self.upper_biguint()) + } +} +impl Display for BoundedCardinality { + #[allow(clippy::min_ident_chars)] + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "({}, {})", self.lower, self.upper) + } +} +impl fmt::Debug for BoundedCardinality { + #[allow(clippy::min_ident_chars)] + #[inline] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + <Self as Display>::fmt(self, f) + } +} +impl TryFrom<(Cardinality, Cardinality)> for BoundedCardinality { + type Error = (); + #[inline] + fn try_from(value: (Cardinality, Cardinality)) -> Result<Self, Self::Error> { + Self::new(value.0, value.1).ok_or(()) + } +} +impl TryFrom<(BigUint, BigUint)> for BoundedCardinality { + type Error = (); + #[inline] + fn try_from(value: (BigUint, BigUint)) -> Result<Self, Self::Error> { + Self::from_biguint(value.0, value.1).ok_or(()) + } +} +impl From<BoundedCardinality> for (Cardinality, Cardinality) { + #[inline] + fn from(value: BoundedCardinality) -> Self { + (value.lower, value.upper) + } +} +impl From<BoundedCardinality> for (BigUint, BigUint) { + #[inline] + fn from(value: BoundedCardinality) -> Self { + (value.lower.into(), value.upper.into()) + } +} /// 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<T>`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html) where `Elem` = `T`). @@ -90,8 +445,22 @@ 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 the cardinality of `self` if it is known exactly. + /// + /// The following properties must be true: + /// * `self.cardinality().is_none()` ⇔ `self.bounded_cardinality().lower() < self.bounded_cardinality().upper()`. + /// * `self.cardinality().is_some()` ⇔ `self.cardinality().unwrap() == self.bounded_cardinality().lower()`. + #[inline] + fn cardinality(&self) -> Option<Cardinality> { + let bound = self.bounded_cardinality(); + if bound.lower < bound.upper { + None + } else { + Some(bound.lower) + } + } + /// Returns the bounded cardinality of `self`. + fn bounded_cardinality(&self) -> BoundedCardinality; /// Returns `true` iff `Self` contains `elem`. fn contains<Q>(&self, elem: &Q) -> bool where @@ -205,8 +574,16 @@ where { type Elem = T; #[inline] - fn cardinality(&self) -> Cardinality { - Cardinality::Finite(self.len().into()) + fn cardinality(&self) -> Option<Cardinality> { + Some(Cardinality::Finite(self.len().into())) + } + #[inline] + fn bounded_cardinality(&self) -> BoundedCardinality { + let card = Cardinality::from_biguint(self.len().into()); + BoundedCardinality { + lower: card.clone(), + upper: card, + } } #[inline] fn contains<Q>(&self, elem: &Q) -> bool @@ -232,8 +609,16 @@ where { type Elem = T; #[inline] - fn cardinality(&self) -> Cardinality { - Cardinality::Finite((&self.end - &self.start).into()) + fn cardinality(&self) -> Option<Cardinality> { + Some(Cardinality::Finite((&self.end - &self.start).into())) + } + #[inline] + fn bounded_cardinality(&self) -> BoundedCardinality { + let card = Cardinality::Finite((&self.end - &self.start).into()); + BoundedCardinality { + lower: card.clone(), + upper: card, + } } #[inline] fn contains<Q>(&self, elem: &Q) -> bool @@ -263,8 +648,19 @@ where { type Elem = T; #[inline] - fn cardinality(&self) -> Cardinality { - Cardinality::Finite((self.end() - self.start()).into() + BigUint::new(alloc::vec![1])) + fn cardinality(&self) -> Option<Cardinality> { + Some(Cardinality::Finite( + (self.end() - self.start()).into() + BigUint::new(alloc::vec![1]), + )) + } + #[inline] + fn bounded_cardinality(&self) -> BoundedCardinality { + let card = + Cardinality::Finite((self.end() - self.start()).into() + BigUint::new(alloc::vec![1])); + BoundedCardinality { + lower: card.clone(), + upper: card, + } } #[inline] fn contains<Q>(&self, elem: &Q) -> bool @@ -300,8 +696,16 @@ where { type Elem = T; #[inline] - fn cardinality(&self) -> Cardinality { - Cardinality::Finite(self.len().into()) + fn cardinality(&self) -> Option<Cardinality> { + Some(Cardinality::Finite(self.len().into())) + } + #[inline] + fn bounded_cardinality(&self) -> BoundedCardinality { + let card = Cardinality::Finite(self.len().into()); + BoundedCardinality { + lower: card.clone(), + upper: card, + } } #[inline] fn contains<Q>(&self, elem: &Q) -> bool