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}