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}