Skip to main content

relmath/traits/
exact_support.rs

1//! Shared capability traits for exact-support materialization.
2//!
3//! These traits make the released add-on relation surfaces line up around one
4//! explicit question: what exact facts remain after forgetting provenance
5//! witnesses, annotation values, or valid-time intervals?
6
7use crate::{
8    BinaryRelation, NaryRelation, NaryRelationError, UnaryRelation,
9    annotated::{AnnotatedRelation, Semiring},
10    provenance::ProvenanceRelation,
11    temporal::ValidTimeRelation,
12};
13
14use super::FiniteRelation;
15
16/// Relation-level exact support for add-on relation surfaces.
17///
18/// `ExactSupport` makes explicit the deterministic relation-level boundary
19/// already used by the released provenance, annotated, and valid-time
20/// surfaces: forget the add-on payload and keep only the exact stored facts.
21///
22/// The forgotten payload differs by surface:
23///
24/// - provenance forgets the witness token set returned by
25///   [`crate::provenance::ProvenanceRelation::why`];
26/// - annotated relations forget the stored annotation returned by
27///   [`crate::annotated::AnnotatedRelation::annotation_of`];
28/// - valid-time relations forget the fact-level interval support returned by
29///   [`crate::temporal::ValidTimeRelation::valid_time_of`].
30///
31/// Do not confuse this relation-level exact support with
32/// [`crate::temporal::ValidTimeSupport`], which is the canonical interval
33/// support for one stored fact rather than the exact support of the whole
34/// relation.
35///
36/// # Examples
37///
38/// ```rust
39/// use relmath::{
40///     ExactSupport,
41///     annotated::{AnnotatedRelation, BooleanSemiring},
42///     provenance::ProvenanceRelation,
43///     temporal::{Interval, ValidTimeRelation},
44/// };
45///
46/// let evidence = ProvenanceRelation::from_facts([
47///     (("alice", "review"), "directory"),
48///     (("bob", "approve"), "policy"),
49/// ]);
50/// let permissions = AnnotatedRelation::from_facts([
51///     (("alice", "review"), BooleanSemiring::TRUE),
52///     (("bob", "approve"), BooleanSemiring::TRUE),
53/// ]);
54/// let schedule = ValidTimeRelation::from_facts([
55///     (
56///         ("alice", "review"),
57///         Interval::new(1, 3).expect("expected valid interval"),
58///     ),
59///     (
60///         ("bob", "approve"),
61///         Interval::new(2, 4).expect("expected valid interval"),
62///     ),
63/// ]);
64///
65/// assert_eq!(
66///     evidence.exact_support().to_vec(),
67///     vec![("alice", "review"), ("bob", "approve")]
68/// );
69/// assert_eq!(permissions.exact_support().to_vec(), evidence.exact_support().to_vec());
70/// assert_eq!(schedule.exact_support().to_vec(), evidence.exact_support().to_vec());
71/// ```
72pub trait ExactSupport<F>: FiniteRelation
73where
74    F: Ord + Clone,
75{
76    /// Returns the exact support of stored facts in deterministic fact order.
77    #[must_use]
78    fn exact_support(&self) -> UnaryRelation<F>;
79}
80
81impl<F: Ord + Clone, P: Ord> ExactSupport<F> for ProvenanceRelation<F, P> {
82    fn exact_support(&self) -> UnaryRelation<F> {
83        self.support()
84    }
85}
86
87impl<F: Ord + Clone, A: Semiring> ExactSupport<F> for AnnotatedRelation<F, A> {
88    fn exact_support(&self) -> UnaryRelation<F> {
89        self.support()
90    }
91}
92
93impl<F: Ord + Clone, T: Ord + Clone> ExactSupport<F> for ValidTimeRelation<F, T> {
94    fn exact_support(&self) -> UnaryRelation<F> {
95        self.support()
96    }
97}
98
99/// Materializes exact scalar support as a unary relation.
100///
101/// This trait mirrors the existing `to_unary_relation` conversion methods on
102/// released add-on surfaces and gives generic code one explicit unary
103/// materialization boundary.
104///
105/// # Examples
106///
107/// ```rust
108/// use relmath::{
109///     ToExactUnaryRelation,
110///     annotated::{AnnotatedRelation, BooleanSemiring},
111/// };
112///
113/// fn exact_values<R>(relation: &R) -> Vec<&'static str>
114/// where
115///     R: ToExactUnaryRelation<&'static str>,
116/// {
117///     relation.to_unary_relation().to_vec()
118/// }
119///
120/// let concepts = AnnotatedRelation::from_facts([
121///     ("Closure", BooleanSemiring::TRUE),
122///     ("Relations", BooleanSemiring::TRUE),
123///     ("Zero", BooleanSemiring::FALSE),
124/// ]);
125///
126/// assert_eq!(exact_values(&concepts), vec!["Closure", "Relations"]);
127/// ```
128pub trait ToExactUnaryRelation<T>: ExactSupport<T>
129where
130    T: Ord + Clone,
131{
132    /// Materializes exact scalar support as a unary relation.
133    #[must_use]
134    fn to_unary_relation(&self) -> UnaryRelation<T> {
135        self.exact_support()
136    }
137}
138
139impl<T, R> ToExactUnaryRelation<T> for R
140where
141    T: Ord + Clone,
142    R: ExactSupport<T>,
143{
144}
145
146/// Materializes exact pair support as a binary relation.
147///
148/// This trait mirrors the existing `to_binary_relation` conversion methods on
149/// released add-on surfaces and keeps deterministic pair order inherited from
150/// exact support materialization.
151///
152/// # Examples
153///
154/// ```rust
155/// use relmath::{
156///     BinaryRelation, ToExactBinaryRelation,
157///     annotated::{AnnotatedRelation, BooleanSemiring},
158/// };
159///
160/// fn exact_pairs<R>(relation: &R) -> BinaryRelation<&'static str, &'static str>
161/// where
162///     R: ToExactBinaryRelation<&'static str, &'static str>,
163/// {
164///     relation.to_binary_relation()
165/// }
166///
167/// let permissions = AnnotatedRelation::from_facts([
168///     (("alice", "read"), BooleanSemiring::TRUE),
169///     (("bob", "approve"), BooleanSemiring::TRUE),
170/// ]);
171///
172/// assert_eq!(
173///     exact_pairs(&permissions).to_vec(),
174///     vec![("alice", "read"), ("bob", "approve")]
175/// );
176/// ```
177pub trait ToExactBinaryRelation<A, B>: ExactSupport<(A, B)>
178where
179    A: Ord + Clone,
180    B: Ord + Clone,
181{
182    /// Materializes exact pair support as a binary relation.
183    #[must_use]
184    fn to_binary_relation(&self) -> BinaryRelation<A, B> {
185        self.exact_support().into_iter().collect()
186    }
187}
188
189impl<A, B, R> ToExactBinaryRelation<A, B> for R
190where
191    A: Ord + Clone,
192    B: Ord + Clone,
193    R: ExactSupport<(A, B)>,
194{
195}
196
197/// Materializes exact row support as an n-ary relation with an explicit schema.
198///
199/// This trait mirrors the existing `to_nary_relation` conversion methods on
200/// released add-on surfaces. It preserves the current exact n-ary contract:
201/// schema names remain explicit and `NaryRelation` validation rules still
202/// apply during materialization.
203///
204/// # Examples
205///
206/// ```rust
207/// use relmath::{NaryRelation, ToExactNaryRelation, provenance::ProvenanceRelation};
208///
209/// let rows = ProvenanceRelation::from_facts([
210///     (vec!["Alice", "Math", "passed"], "gradebook"),
211///     (vec!["Bob", "Physics", "passed"], "gradebook"),
212/// ]);
213///
214/// assert_eq!(
215///     rows.to_nary_relation(["student", "course", "status"])?,
216///     NaryRelation::from_rows(
217///         ["student", "course", "status"],
218///         [["Alice", "Math", "passed"], ["Bob", "Physics", "passed"]],
219///     )?
220/// );
221///
222/// # Ok::<(), relmath::NaryRelationError>(())
223/// ```
224pub trait ToExactNaryRelation<T>: ExactSupport<Vec<T>>
225where
226    T: Ord + Clone,
227{
228    /// Materializes exact row support as an n-ary relation with the given
229    /// schema.
230    fn to_nary_relation<I, S>(&self, schema: I) -> Result<NaryRelation<T>, NaryRelationError>
231    where
232        I: IntoIterator<Item = S>,
233        S: Into<String>,
234    {
235        NaryRelation::from_rows(schema, self.exact_support())
236    }
237}
238
239impl<T, R> ToExactNaryRelation<T> for R
240where
241    T: Ord + Clone,
242    R: ExactSupport<Vec<T>>,
243{
244}