lattices/
lib.rs

1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4use std::cmp::Ordering::{self, *};
5
6use sealed::sealed;
7pub use {cc_traits, variadics};
8
9/// Module for definiting algebraic structures and properties.
10pub mod algebra;
11pub mod collections;
12mod conflict;
13mod dom_pair;
14pub mod ght;
15pub mod map_union;
16pub mod map_union_with_tombstones;
17mod ord;
18mod pair;
19mod point;
20pub mod semiring_application;
21pub mod set_union;
22pub mod set_union_with_tombstones;
23pub mod test;
24pub mod union_find;
25mod unit;
26mod vec_union;
27mod with_bot;
28mod with_top;
29
30pub use conflict::Conflict;
31pub use dom_pair::DomPair;
32pub use lattices_macro::*;
33pub use ord::{Max, Min};
34pub use pair::{Pair, PairBimorphism};
35pub use point::Point;
36pub use vec_union::VecUnion;
37pub use with_bot::WithBot;
38pub use with_top::WithTop;
39
40/// Alias trait for lattice types.
41#[sealed]
42pub trait Lattice: Sized + Merge<Self> + LatticeOrd + NaiveLatticeOrd + IsBot + IsTop {}
43#[sealed]
44impl<T> Lattice for T where T: Sized + Merge<Self> + LatticeOrd + NaiveLatticeOrd + IsBot + IsTop {}
45
46/// Alias trait for semirings.
47#[sealed]
48pub trait Semiring<T>: Addition<T> + Multiplication<T> + Zero<T> + One<T> {}
49
50/// Trait for Semiring Addition.
51pub trait Addition<Other> {
52    /// Add-assign `other` into self.
53    fn add(&mut self, other: Self);
54
55    /// Add `this` and `delta` together, returning the new value.
56    fn add_owned(mut self, other: Self) -> Self
57    where
58        Self: Sized,
59    {
60        self.add(other);
61        self
62    }
63}
64
65/// Trait for Semiring Multiplication.
66pub trait Multiplication<Other> {
67    /// Multiply-assign `other` into self.
68    fn mul(&mut self, other: Self);
69
70    /// Multiply `this` and `delta` together, returning the new value.
71    fn mul_owned(mut self, other: Self) -> Self
72    where
73        Self: Sized,
74    {
75        self.mul(other);
76        self
77    }
78}
79
80/// Trait to check if semiring contains a zero.
81pub trait Zero<T> {
82    /// Returns the zero element of the semiring. Identify for the Addition operation.
83    fn zero(&self) -> T;
84}
85
86/// Trait to define a one in a semiring.
87pub trait One<T> {
88    /// Returns the one element of the semiring. Identity for the multiplication operation.
89    fn one(&self) -> T;
90}
91/// Trait for lattice merge (AKA "join" or "least upper bound").
92pub trait Merge<Other> {
93    /// Merge `other` into the `self` lattice.
94    ///
95    /// This operation must be associative, commutative, and idempotent.
96    ///
97    /// Returns `true` if `self` changed, `false` otherwise.
98    /// Returning `true` implies that the new value for `self` is later in the lattice order than
99    /// the old value. Returning `false` means that `self` was unchanged and therefore `other` came
100    /// before `self` (or the two are equal) in the lattice order.
101    fn merge(&mut self, other: Other) -> bool;
102
103    /// Merge `this` and `delta` together, returning the new value.
104    fn merge_owned(mut this: Self, delta: Other) -> Self
105    where
106        Self: Sized,
107    {
108        Self::merge(&mut this, delta);
109        this
110    }
111}
112
113/// Trait for lattice partial order comparison
114/// PartialOrd is implemented for many things, this trait can be used to require the type be a lattice.
115pub trait LatticeOrd<Rhs = Self>: PartialOrd<Rhs> {}
116
117/// Naive lattice compare, based on the [`Merge::merge`] function.
118#[sealed]
119pub trait NaiveLatticeOrd<Rhs = Self>
120where
121    Self: Clone + Merge<Rhs> + Sized,
122    Rhs: Clone + Merge<Self>,
123{
124    /// Naive compare based on the [`Merge::merge`] method. This method can be very inefficient;
125    /// use [`PartialOrd::partial_cmp`] instead.
126    ///
127    /// This method should not be overridden.
128    fn naive_cmp(&self, other: &Rhs) -> Option<Ordering> {
129        let mut self_a = self.clone();
130        let other_a = other.clone();
131        let self_b = self.clone();
132        let mut other_b = other.clone();
133        match (self_a.merge(other_a), other_b.merge(self_b)) {
134            (true, true) => None,
135            (true, false) => Some(Less),
136            (false, true) => Some(Greater),
137            (false, false) => Some(Equal),
138        }
139    }
140}
141#[sealed]
142impl<This, Other> NaiveLatticeOrd<Other> for This
143where
144    Self: Clone + Merge<Other>,
145    Other: Clone + Merge<Self>,
146{
147}
148
149/// Same as `From` but for lattices.
150///
151/// This should only be implemented between different representations of the same lattice type.
152/// This should recursively convert nested lattice types, but not non-lattice ("scalar") types.
153pub trait LatticeFrom<Other> {
154    /// Convert from the `Other` lattice into `Self`.
155    fn lattice_from(other: Other) -> Self;
156}
157
158/// Trait to check if a lattice instance is bottom (⊥).
159pub trait IsBot {
160    /// Returns if `self` is lattice bottom (⊥).
161    ///
162    /// Must be consistent with equality, any element equal to bottom is also considered to be bottom.
163    fn is_bot(&self) -> bool;
164}
165
166/// Trait to check if a lattice instance is top (⊤) and therefore cannot change any futher.
167pub trait IsTop {
168    /// Returns if `self` is lattice top (⊤).
169    ///
170    /// Must be consistent with equality, any element equal to top is also considered to be top.
171    fn is_top(&self) -> bool;
172}
173
174/// Trait to atomize a lattice into individual elements. For example, a [`set_union::SetUnion`]
175/// will be broken up into individual singleton elements.
176///
177/// Formally, breaks up `Self` into an set of lattice points forming a (strong) [antichain](https://en.wikipedia.org/wiki/Antichain).
178/// "Strong" in the sense that any pair of lattice points in the antichain should have a greatest
179/// lower bound (GLB or "meet") of bottom.
180pub trait Atomize: Merge<Self::Atom> {
181    /// The type of atoms for this lattice.
182    type Atom: 'static + IsBot;
183
184    /// The iter type iterating the antichain atoms.
185    type AtomIter: 'static + Iterator<Item = Self::Atom>;
186
187    /// Atomize self: convert into an iter of atoms.
188    ///
189    /// The returned iterator should be empty if and only if `self.is_bot()` is true.
190    /// All atoms in the returned iterator should have `self.is_bot()` be false.
191    ///
192    /// Returned values must merge to reform a value equal to the original `self`.
193    fn atomize(self) -> Self::AtomIter;
194}
195
196/// Trait for recursively revealing the underlying types within lattice types.
197pub trait DeepReveal {
198    /// The underlying type when revealed.
199    type Revealed;
200
201    /// Reveals the underlying lattice types recursively.
202    fn deep_reveal(self) -> Self::Revealed;
203}
204
205/// Semilattice morphism. Lattice merge must distribute over this unary function.
206///
207/// Use [`crate::test::check_lattice_morphism`] to spot-test an implementation.
208///
209/// See the [lattice math doc's lattice morphism section](https://hydro.run/docs/dfir/lattices_crate/lattice_math/#lattice-morphism).
210pub trait LatticeMorphism<LatIn> {
211    /// The output lattice type.
212    type Output;
213    /// Executes the function.
214    fn call(&mut self, lat_in: LatIn) -> Self::Output;
215}
216
217/// Semilattice bimorphism. Lattice merge must distribute over this binary function, in both arguments.
218///
219/// Use [`crate::test::check_lattice_bimorphism`] to spot-test an implementation.
220///
221/// See the [lattice math doc's lattice bimorphism section](https://hydro.run/docs/dfir/lattices_crate/lattice_math/#lattice-bimorphism).
222pub trait LatticeBimorphism<LatA, LatB> {
223    /// The output lattice type.
224    type Output;
225    /// Executes the function.
226    fn call(&mut self, lat_a: LatA, lat_b: LatB) -> Self::Output;
227}
228
229/// Converts a closure to a morphism. Does not check for correctness.
230pub fn closure_to_morphism<LatIn, LatOut, F>(
231    func: F,
232) -> impl LatticeMorphism<LatIn, Output = LatOut>
233where
234    F: FnMut(LatIn) -> LatOut,
235{
236    struct FnMorphism<F>(F);
237    impl<F, LatIn, LatOut> LatticeMorphism<LatIn> for FnMorphism<F>
238    where
239        F: FnMut(LatIn) -> LatOut,
240    {
241        type Output = LatOut;
242
243        fn call(&mut self, lat_in: LatIn) -> Self::Output {
244            (self.0)(lat_in)
245        }
246    }
247    FnMorphism(func)
248}
249
250/// Converts a closure to a bimorphism. Does not check for correctness.
251pub fn closure_to_bimorphism<LatA, LatB, LatOut, F>(
252    func: F,
253) -> impl LatticeBimorphism<LatA, LatB, Output = LatOut>
254where
255    F: FnMut(LatA, LatB) -> LatOut,
256{
257    struct FnBimorphism<F>(F);
258    impl<F, LatA, LatB, LatOut> LatticeBimorphism<LatA, LatB> for FnBimorphism<F>
259    where
260        F: FnMut(LatA, LatB) -> LatOut,
261    {
262        type Output = LatOut;
263
264        fn call(&mut self, lat_a: LatA, lat_b: LatB) -> Self::Output {
265            (self.0)(lat_a, lat_b)
266        }
267    }
268    FnBimorphism(func)
269}