Skip to main content

lattices/
lib.rs

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