relmath/core/binary.rs
1use std::collections::BTreeSet;
2
3use crate::{UnaryRelation, traits::FiniteRelation};
4
5/// A finite binary relation.
6///
7/// The starter implementation stores pairs in a `BTreeSet` so iteration order
8/// is deterministic.
9///
10/// Composition direction is:
11///
12/// `r.compose(&s)` = `{ (a, c) | exists b. (a, b) in r and (b, c) in s }`
13///
14/// # Examples
15///
16/// ```rust
17/// use relmath::{BinaryRelation, UnaryRelation};
18///
19/// let user_role = BinaryRelation::from_pairs([
20/// ("ann", "admin"),
21/// ("bob", "reviewer"),
22/// ]);
23///
24/// let extra_role = BinaryRelation::from_pairs([
25/// ("bob", "reviewer"),
26/// ("cara", "guest"),
27/// ]);
28///
29/// let role_permission = BinaryRelation::from_pairs([
30/// ("admin", "read"),
31/// ("reviewer", "approve"),
32/// ("guest", "view"),
33/// ]);
34///
35/// assert_eq!(
36/// user_role.union(&extra_role).to_vec(),
37/// vec![("ann", "admin"), ("bob", "reviewer"), ("cara", "guest")]
38/// );
39/// assert_eq!(
40/// user_role.intersection(&extra_role).to_vec(),
41/// vec![("bob", "reviewer")]
42/// );
43/// assert_eq!(user_role.difference(&extra_role).to_vec(), vec![("ann", "admin")]);
44/// assert_eq!(
45/// user_role.converse().to_vec(),
46/// vec![("admin", "ann"), ("reviewer", "bob")]
47/// );
48///
49/// let effective_permission = user_role.union(&extra_role).compose(&role_permission);
50/// assert!(effective_permission.contains(&"ann", &"read"));
51/// assert_eq!(
52/// effective_permission.iter().copied().collect::<Vec<_>>(),
53/// vec![("ann", "read"), ("bob", "approve"), ("cara", "view")]
54/// );
55/// assert_eq!(
56/// effective_permission.domain().to_vec(),
57/// vec!["ann", "bob", "cara"]
58/// );
59/// assert_eq!(
60/// effective_permission.range().to_vec(),
61/// vec!["approve", "read", "view"]
62/// );
63/// assert_eq!(
64/// effective_permission
65/// .restrict_domain(&UnaryRelation::from_values(["ann", "cara"]))
66/// .to_vec(),
67/// vec![("ann", "read"), ("cara", "view")]
68/// );
69/// assert_eq!(
70/// effective_permission
71/// .restrict_range(&UnaryRelation::from_values(["read", "view"]))
72/// .to_vec(),
73/// vec![("ann", "read"), ("cara", "view")]
74/// );
75/// assert_eq!(
76/// effective_permission.image(&UnaryRelation::singleton("bob")).to_vec(),
77/// vec!["approve"]
78/// );
79/// assert_eq!(
80/// effective_permission
81/// .preimage(&UnaryRelation::from_values(["read", "view"]))
82/// .to_vec(),
83/// vec!["ann", "cara"]
84/// );
85/// ```
86#[derive(Clone, Debug, PartialEq, Eq)]
87pub struct BinaryRelation<A: Ord, B: Ord> {
88 pairs: BTreeSet<(A, B)>,
89}
90
91impl<A: Ord, B: Ord> BinaryRelation<A, B> {
92 /// Creates an empty binary relation.
93 #[must_use]
94 pub fn new() -> Self {
95 Self {
96 pairs: BTreeSet::new(),
97 }
98 }
99
100 /// Creates a binary relation from the provided pairs.
101 #[must_use]
102 pub fn from_pairs<I>(pairs: I) -> Self
103 where
104 I: IntoIterator<Item = (A, B)>,
105 {
106 pairs.into_iter().collect()
107 }
108
109 /// Inserts a pair into the relation.
110 ///
111 /// Returns `true` when the pair was not already present.
112 pub fn insert(&mut self, left: A, right: B) -> bool {
113 self.pairs.insert((left, right))
114 }
115
116 /// Returns `true` when the relation contains the given pair.
117 #[must_use]
118 pub fn contains(&self, left: &A, right: &B) -> bool {
119 self.pairs.iter().any(|(candidate_left, candidate_right)| {
120 candidate_left == left && candidate_right == right
121 })
122 }
123
124 /// Returns an iterator over the stored pairs in deterministic order.
125 pub fn iter(&self) -> impl Iterator<Item = &(A, B)> {
126 self.pairs.iter()
127 }
128
129 /// Returns the domain of the relation.
130 #[must_use]
131 pub fn domain(&self) -> UnaryRelation<A>
132 where
133 A: Clone,
134 {
135 self.pairs.iter().map(|(left, _)| left.clone()).collect()
136 }
137
138 /// Returns the range of the relation.
139 #[must_use]
140 pub fn range(&self) -> UnaryRelation<B>
141 where
142 B: Clone,
143 {
144 self.pairs.iter().map(|(_, right)| right.clone()).collect()
145 }
146
147 /// Returns the converse relation.
148 #[must_use]
149 pub fn converse(&self) -> BinaryRelation<B, A>
150 where
151 A: Clone,
152 B: Clone,
153 {
154 self.pairs
155 .iter()
156 .map(|(left, right)| (right.clone(), left.clone()))
157 .collect()
158 }
159
160 /// Returns the union of `self` and `other`.
161 #[must_use]
162 pub fn union(&self, other: &Self) -> Self
163 where
164 A: Clone,
165 B: Clone,
166 {
167 self.pairs.union(&other.pairs).cloned().collect()
168 }
169
170 /// Returns the intersection of `self` and `other`.
171 #[must_use]
172 pub fn intersection(&self, other: &Self) -> Self
173 where
174 A: Clone,
175 B: Clone,
176 {
177 self.pairs.intersection(&other.pairs).cloned().collect()
178 }
179
180 /// Returns the set difference `self \ other`.
181 #[must_use]
182 pub fn difference(&self, other: &Self) -> Self
183 where
184 A: Clone,
185 B: Clone,
186 {
187 self.pairs.difference(&other.pairs).cloned().collect()
188 }
189
190 /// Restricts the domain of the relation to `allowed`.
191 #[must_use]
192 pub fn restrict_domain(&self, allowed: &UnaryRelation<A>) -> Self
193 where
194 A: Clone,
195 B: Clone,
196 {
197 self.pairs
198 .iter()
199 .filter(|(left, _)| allowed.contains(left))
200 .cloned()
201 .collect()
202 }
203
204 /// Restricts the range of the relation to `allowed`.
205 #[must_use]
206 pub fn restrict_range(&self, allowed: &UnaryRelation<B>) -> Self
207 where
208 A: Clone,
209 B: Clone,
210 {
211 self.pairs
212 .iter()
213 .filter(|(_, right)| allowed.contains(right))
214 .cloned()
215 .collect()
216 }
217
218 /// Computes the image of `sources` through the relation.
219 ///
220 /// Returns `{ b | exists a in sources. (a, b) in self }`.
221 #[must_use]
222 pub fn image(&self, sources: &UnaryRelation<A>) -> UnaryRelation<B>
223 where
224 B: Clone,
225 {
226 self.pairs
227 .iter()
228 .filter(|(left, _)| sources.contains(left))
229 .map(|(_, right)| right.clone())
230 .collect()
231 }
232
233 /// Computes the preimage of `targets` through the relation.
234 ///
235 /// Returns `{ a | exists b in targets. (a, b) in self }`.
236 #[must_use]
237 pub fn preimage(&self, targets: &UnaryRelation<B>) -> UnaryRelation<A>
238 where
239 A: Clone,
240 {
241 self.pairs
242 .iter()
243 .filter(|(_, right)| targets.contains(right))
244 .map(|(left, _)| left.clone())
245 .collect()
246 }
247
248 /// Composes `self` with `rhs`.
249 ///
250 /// The result contains `(a, c)` whenever there exists `b` such that
251 /// `(a, b)` is in `self` and `(b, c)` is in `rhs`.
252 ///
253 /// # Examples
254 ///
255 /// ```rust
256 /// use relmath::BinaryRelation;
257 ///
258 /// let user_role = BinaryRelation::from_pairs([
259 /// ("ann", "admin"),
260 /// ("bob", "reviewer"),
261 /// ]);
262 /// let role_permission = BinaryRelation::from_pairs([
263 /// ("admin", "read"),
264 /// ("reviewer", "approve"),
265 /// ]);
266 ///
267 /// let effective_permission = user_role.compose(&role_permission);
268 ///
269 /// assert_eq!(
270 /// effective_permission.to_vec(),
271 /// vec![("ann", "read"), ("bob", "approve")]
272 /// );
273 /// ```
274 #[must_use]
275 pub fn compose<C>(&self, rhs: &BinaryRelation<B, C>) -> BinaryRelation<A, C>
276 where
277 A: Clone,
278 B: Clone,
279 C: Clone + Ord,
280 {
281 let mut result = BTreeSet::new();
282
283 for (left, middle_left) in &self.pairs {
284 for (middle_right, right) in &rhs.pairs {
285 if middle_left == middle_right {
286 result.insert((left.clone(), right.clone()));
287 }
288 }
289 }
290
291 BinaryRelation { pairs: result }
292 }
293
294 /// Converts the relation into a sorted vector of pairs.
295 #[must_use]
296 pub fn to_vec(&self) -> Vec<(A, B)>
297 where
298 A: Clone,
299 B: Clone,
300 {
301 self.pairs.iter().cloned().collect()
302 }
303}
304
305impl<A: Ord, B: Ord> Default for BinaryRelation<A, B> {
306 fn default() -> Self {
307 Self::new()
308 }
309}
310
311impl<T: Ord> BinaryRelation<T, T> {
312 /// Returns the carrier induced by the relation: domain union range.
313 #[must_use]
314 pub fn carrier(&self) -> UnaryRelation<T>
315 where
316 T: Clone,
317 {
318 self.domain().union(&self.range())
319 }
320
321 /// Returns the identity relation on `carrier`.
322 ///
323 /// # Examples
324 ///
325 /// ```rust
326 /// use relmath::{BinaryRelation, UnaryRelation};
327 ///
328 /// let carrier = UnaryRelation::from_values(["Draft", "Review"]);
329 ///
330 /// assert_eq!(
331 /// BinaryRelation::identity(&carrier).to_vec(),
332 /// vec![("Draft", "Draft"), ("Review", "Review")]
333 /// );
334 /// ```
335 #[must_use]
336 pub fn identity(carrier: &UnaryRelation<T>) -> Self
337 where
338 T: Clone,
339 {
340 carrier
341 .iter()
342 .map(|value| (value.clone(), value.clone()))
343 .collect()
344 }
345
346 /// Computes the transitive closure of the relation.
347 ///
348 /// # Examples
349 ///
350 /// ```rust
351 /// use relmath::BinaryRelation;
352 ///
353 /// let step = BinaryRelation::from_pairs([
354 /// ("Draft", "Review"),
355 /// ("Review", "Approved"),
356 /// ]);
357 ///
358 /// assert_eq!(
359 /// step.transitive_closure().to_vec(),
360 /// vec![
361 /// ("Draft", "Approved"),
362 /// ("Draft", "Review"),
363 /// ("Review", "Approved"),
364 /// ]
365 /// );
366 /// ```
367 #[must_use]
368 pub fn transitive_closure(&self) -> Self
369 where
370 T: Clone,
371 {
372 let mut closure = self.clone();
373 let mut changed = true;
374
375 while changed {
376 changed = false;
377 let snapshot = closure.to_vec();
378
379 for (left, middle_left) in &snapshot {
380 for (middle_right, right) in &snapshot {
381 if middle_left == middle_right
382 && closure.pairs.insert((left.clone(), right.clone()))
383 {
384 changed = true;
385 }
386 }
387 }
388 }
389
390 closure
391 }
392
393 /// Computes the reflexive-transitive closure on the given `carrier`.
394 ///
395 /// Values that appear in `carrier` but not in any pair still gain their
396 /// identity edge in the result.
397 ///
398 /// # Examples
399 ///
400 /// ```rust
401 /// use relmath::{BinaryRelation, UnaryRelation};
402 ///
403 /// let step = BinaryRelation::from_pairs([("Draft", "Review")]);
404 /// let states = UnaryRelation::from_values(["Draft", "Review", "Archived"]);
405 /// let reachable = step.reflexive_transitive_closure(&states);
406 ///
407 /// assert!(reachable.contains(&"Archived", &"Archived"));
408 /// assert!(reachable.contains(&"Draft", &"Review"));
409 /// ```
410 #[must_use]
411 pub fn reflexive_transitive_closure(&self, carrier: &UnaryRelation<T>) -> Self
412 where
413 T: Clone,
414 {
415 self.transitive_closure().union(&Self::identity(carrier))
416 }
417
418 /// Returns `true` when the relation is reflexive on `carrier`.
419 #[must_use]
420 pub fn is_reflexive(&self, carrier: &UnaryRelation<T>) -> bool {
421 carrier.iter().all(|value| self.contains(value, value))
422 }
423
424 /// Returns `true` when the relation is irreflexive on `carrier`.
425 #[must_use]
426 pub fn is_irreflexive(&self, carrier: &UnaryRelation<T>) -> bool {
427 carrier.iter().all(|value| !self.contains(value, value))
428 }
429
430 /// Returns `true` when the relation is symmetric.
431 #[must_use]
432 pub fn is_symmetric(&self) -> bool {
433 self.pairs
434 .iter()
435 .all(|(left, right)| self.contains(right, left))
436 }
437
438 /// Returns `true` when the relation is antisymmetric.
439 #[must_use]
440 pub fn is_antisymmetric(&self) -> bool {
441 self.pairs
442 .iter()
443 .all(|(left, right)| left == right || !self.contains(right, left))
444 }
445
446 /// Returns `true` when the relation is transitive.
447 #[must_use]
448 pub fn is_transitive(&self) -> bool
449 where
450 T: Clone,
451 {
452 let snapshot = self.to_vec();
453
454 snapshot.iter().all(|(left, middle_left)| {
455 snapshot.iter().all(|(middle_right, right)| {
456 middle_left != middle_right || self.contains(left, right)
457 })
458 })
459 }
460
461 /// Returns `true` when the relation is an equivalence relation on `carrier`.
462 ///
463 /// # Examples
464 ///
465 /// ```rust
466 /// use relmath::{BinaryRelation, UnaryRelation};
467 ///
468 /// let aliases = BinaryRelation::from_pairs([
469 /// ("A. Smith", "A. Smith"),
470 /// ("A. Smith", "Alice Smith"),
471 /// ("Alice Smith", "A. Smith"),
472 /// ("Alice Smith", "Alice Smith"),
473 /// ]);
474 /// let carrier = UnaryRelation::from_values(["A. Smith", "Alice Smith"]);
475 ///
476 /// assert!(aliases.is_equivalence(&carrier));
477 /// ```
478 #[must_use]
479 pub fn is_equivalence(&self, carrier: &UnaryRelation<T>) -> bool
480 where
481 T: Clone,
482 {
483 self.is_reflexive(carrier) && self.is_symmetric() && self.is_transitive()
484 }
485
486 /// Returns `true` when the relation is a partial order on `carrier`.
487 ///
488 /// # Examples
489 ///
490 /// ```rust
491 /// use relmath::{BinaryRelation, UnaryRelation};
492 ///
493 /// let divides = BinaryRelation::from_pairs([
494 /// (1_u8, 1_u8),
495 /// (1, 2),
496 /// (1, 4),
497 /// (2, 2),
498 /// (2, 4),
499 /// (4, 4),
500 /// ]);
501 /// let carrier = UnaryRelation::from_values([1_u8, 2_u8, 4_u8]);
502 ///
503 /// assert!(divides.is_partial_order(&carrier));
504 /// ```
505 #[must_use]
506 pub fn is_partial_order(&self, carrier: &UnaryRelation<T>) -> bool
507 where
508 T: Clone,
509 {
510 self.is_reflexive(carrier) && self.is_antisymmetric() && self.is_transitive()
511 }
512}
513
514impl<A: Ord, B: Ord> FiniteRelation for BinaryRelation<A, B> {
515 fn len(&self) -> usize {
516 self.pairs.len()
517 }
518}
519
520impl<A: Ord, B: Ord> FromIterator<(A, B)> for BinaryRelation<A, B> {
521 fn from_iter<I: IntoIterator<Item = (A, B)>>(iter: I) -> Self {
522 Self {
523 pairs: iter.into_iter().collect(),
524 }
525 }
526}
527
528impl<A: Ord, B: Ord> Extend<(A, B)> for BinaryRelation<A, B> {
529 fn extend<I: IntoIterator<Item = (A, B)>>(&mut self, iter: I) {
530 self.pairs.extend(iter);
531 }
532}
533
534impl<A: Ord, B: Ord> IntoIterator for BinaryRelation<A, B> {
535 type Item = (A, B);
536 type IntoIter = std::collections::btree_set::IntoIter<(A, B)>;
537
538 fn into_iter(self) -> Self::IntoIter {
539 self.pairs.into_iter()
540 }
541}
542
543impl<'a, A: Ord, B: Ord> IntoIterator for &'a BinaryRelation<A, B> {
544 type Item = &'a (A, B);
545 type IntoIter = std::collections::btree_set::Iter<'a, (A, B)>;
546
547 fn into_iter(self) -> Self::IntoIter {
548 self.pairs.iter()
549 }
550}