quantor/quantifiers/basic.rs
1//! Basic quantifiers such as `forall`, `exists`, `none`, `exactly_one`, and `all_equal`.
2//! These functions express simple logical evaluations over a single iterable.
3//!
4//! Useful in validation, invariant checks, and test assertions.
5
6use crate::{error::{QuantorKind}, QuantorError};
7
8/// Checks if all elements satisfy the predicate.
9///
10/// Equivalent to **_∀a ∈ iter: pred(a)_**.
11/// When `a` is `∅`, this returns `ok(())` because there is no counterexample.
12/// ## Arguments
13/// - `iter` - The collection to be checked.
14/// - `pred` - The predicate to test each element against.
15/// ## Returns
16/// - `Ok(())` if all elements satisfy the predicate.
17/// - `Err(QuantorError::PredicateFailed { kind, index })` if an element fails the predicate, with the index of the first failure.
18/// ## Example
19/// ```
20/// use quantor::{quantifiers::forall, error::QuantorResultExt};
21///
22/// let numbers = vec!(0, 2, 4, 6);
23/// assert!(forall(&numbers, |x| x % 2 == 0).is_ok());
24///
25/// let bad = vec!(0, 1, 2, 4, 6);
26/// let err = forall(&bad, |x| x % 2 == 0);
27///
28/// if let Some(index) = err.failing_index() {
29/// assert_eq!(1, index);
30/// }
31/// ```
32#[inline]
33#[must_use = "Quantifier results must be checked. Use `.is_ok()` or `?` to handle them."]
34pub fn forall<'a, I, T: 'a, F>(iter: I, pred: F) -> Result<(), QuantorError>
35where
36 I: IntoIterator<Item = &'a T>,
37 F: Fn(&T) -> bool,
38{
39 for (i, item) in iter.into_iter().enumerate() {
40 if !pred(item) {
41 return Err(QuantorError::PredicateFailed { kind: QuantorKind::Forall, index: i })
42 }
43 }
44
45 Ok(())
46}
47
48/// Checks if at least one element satisfies the predicate.
49///
50/// Equivalent to **_∃a ∈ iter: pred(a)_**.
51/// When `a` is `∅`, this returns `QuantorError` because there is no element that can satisfy `pred`.
52/// ## Arguments
53/// - `iter` - The collection to be checked.
54/// - `pred` - The predicate to test each element against.
55/// ## Returns
56/// - `Ok(())` if any element satisfies the predicate.
57/// - `Err(QuantorError::NoMatch { kind })` if no element satisfies the predicate.
58/// ## Example
59/// ```
60/// use quantor::quantifiers::exists;
61///
62/// let numbers = vec!(0, 1, 3, 5);
63/// assert!(exists(&numbers, |x| x % 2 == 0).is_ok());
64///
65/// let bad = vec!(1, 3, 5, 7);
66/// assert!(exists(&bad, |x| x % 2 == 0).is_err());
67/// ```
68#[inline]
69#[must_use = "Quantifier results must be checked. Use `.is_ok()` or `?` to handle them."]
70pub fn exists<'a, I, T: 'a, F>(iter: I, pred: F) -> Result<(), QuantorError>
71where
72 I: IntoIterator<Item = &'a T>,
73 F: Fn(&T) -> bool,
74{
75 for item in iter {
76 if pred(item) {
77 return Ok(());
78 }
79 }
80
81 Err(QuantorError::NoMatch {kind: QuantorKind::Exists})
82}
83
84/// Checks if no element satisfies the predicate.
85///
86/// Equivalent to **_∀a ∈ iter: ¬pred(a)_**.
87/// ## Arguments
88/// - `iter` - The collection to be checked.
89/// - `pred` - The predicate to test each element against.
90/// ## Returns
91/// - `Ok(())` if no elements satisfy the predicate.
92/// - `Err(QuantorError::UnexpectedMatch { kind, index })` if at least one element satisfies the predicate, with the `index`.
93///
94/// ## Example
95/// ```
96/// use quantor::{quantifiers::none, error::QuantorResultExt};
97///
98/// let numbers = vec!(1, 3, 5, 7);
99/// assert!(none(&numbers, |x| x % 2 == 0).is_ok());
100///
101/// let bad = vec!(1, 3, 5, 7, 0);
102/// let err = none(&bad, |x| x % 2 == 0);
103///
104/// if let Some(index) = err.failing_index() {
105/// assert_eq!(4, index);
106/// }
107/// ```
108#[inline]
109#[must_use = "Quantifier results must be checked. Use `.is_ok()` or `?` to handle them."]
110pub fn none<'a, I, T: 'a, F>(iter: I, pred: F) -> Result<(), QuantorError>
111where
112 I: IntoIterator<Item = &'a T>,
113 F: Fn(&T) -> bool,
114{
115 for (index, item) in iter.into_iter().enumerate() {
116 if pred(item) {
117 return Err(QuantorError::UnexpectedMatch { kind: QuantorKind::None, index });
118 }
119 }
120
121 Ok(())
122}
123
124/// Checks if exactly one element satisfies the predicate.
125///
126/// Equivalent to **_∃!a ∈ iter: pred(a)_**.
127/// ## Arguments
128/// - `iter` - The collection to be checked.
129/// - `pred` - The predicate to test each element against.
130/// ## Returns
131/// - `Ok(())` if exactly one element satisfies the predicate.
132/// - `Err(QuantorError::UnexpectedMatch { kind, index })` when there is more than one element which satisfies the predicate, with the `index` of the second passing element.
133/// ## Example
134/// ```
135/// use quantor::{quantifiers::exactly_one, error::QuantorResultExt};
136///
137/// let numbers = vec!(0, 1, 3, 5);
138/// assert!(exactly_one(&numbers, |x| x % 2 == 0).is_ok());
139///
140/// let bad = vec!(0, 1, 3, 5, 6);
141/// let err = exactly_one(&bad, |x| x % 2 == 0);
142///
143/// if let Some(index) = err.failing_index() {
144/// assert_eq!(4, index);
145/// }
146/// ```
147#[inline]
148#[must_use = "Quantifier results must be checked. Use `.is_ok()` or `?` to handle them."]
149pub fn exactly_one<'a, I, T: 'a, F>(iter: I, pred: F) -> Result<(), QuantorError>
150where
151 I: IntoIterator<Item = &'a T>,
152 F: Fn(&T) -> bool,
153{
154 let mut iter = iter.into_iter().enumerate().peekable();
155
156 // Check for empty input
157 if iter.peek().is_none() {
158 return Err(QuantorError::EmptyInput {
159 kind: QuantorKind::ExactlyOne,
160 });
161 }
162
163 let mut matched = 0;
164
165 for (index, item) in iter {
166 if pred(item) {
167 matched += 1;
168 if matched > 1 {
169 return Err(QuantorError::UnexpectedMatch { kind: QuantorKind::ExactlyOne, index });
170 }
171 }
172 }
173
174 if matched == 1 {
175 Ok(())
176 } else {
177 Err(QuantorError::PredicateFailed {
178 kind: QuantorKind::ExactlyOne,
179 index: 0,
180 })
181 }
182}
183
184/// Checks if all elements are equal to each other.
185///
186/// Equivalent to **_∀a,b ∈ iter: a = b_**.
187/// ## Arguments
188/// - `iter` - The collection to be checked.
189/// ## Returns
190/// - `Ok(())` if all elements are equal to each other.
191/// - `Err(QuantorError::NotAllEqual { kind, index })` if an element at `index` is not equal to the first element.
192/// ## Example
193/// ```
194/// use quantor::{quantifiers::all_equal, error::QuantorResultExt};
195///
196/// let empty: Vec<usize> = vec!();
197///
198/// let ones = vec!(1, 1, 1);
199/// let natural = vec!(1, 2, 3);
200///
201/// assert!(all_equal(&empty).is_ok());
202/// assert!(all_equal(&ones).is_ok());
203///
204/// let err = all_equal(&natural);
205///
206/// if let Some(index) = err.failing_index() {
207/// assert_eq!(1, index);
208/// }
209/// ```
210#[inline]
211#[must_use = "Quantifier results must be checked. Use `.is_ok()` or `?` to handle them."]
212pub fn all_equal<'a, I, T>(iter: I) -> Result<(), QuantorError>
213where
214 I: IntoIterator<Item = &'a T>,
215 T: 'a + Eq
216{
217 let mut iter = iter.into_iter();
218 if let Some(first) = iter.next() {
219 for (i, item) in iter.enumerate() {
220 if item != first {
221 return Err(QuantorError::NotAllEqual { kind: QuantorKind::AllEqual, index: i + 1 });
222 }
223 }
224 }
225
226 Ok(())
227}
228
229/// Checks if exactly `n` elements in the iterator satisfy the predicate.
230///
231/// Equivalent to **_|{x ∈ iter | pred(x)}| = n_**
232///
233/// ## Arguments
234/// - `iter` - The collection to be checked.
235/// - `n` - The number of assumed elements to satisfy `pred`.
236/// - `pred` - The predicate to test each element against.
237/// ## Returns
238/// - `Ok(())` if exactly `n` elements match.
239/// - `Err(QuantorError::ExactlyNFailed { kind, found, expected })` otherwise.
240/// ## Example
241/// ```
242/// use quantor::quantifiers::exactly_n;
243/// use quantor::error::QuantorResultExt;
244///
245/// let values = vec![1, 2, 4, 6];
246/// let result = exactly_n(&values, 3, |x| x % 2 == 0);
247///
248/// assert!(result.is_ok());
249///
250/// let err = exactly_n(&values, 2, |x| x % 2 == 0);
251///
252/// if let Some(expected) = err.match_count() {
253/// assert_eq!(expected, 3);
254/// }
255/// ```
256#[inline]
257#[must_use = "Quantifier results must be checked. Use `.is_ok()` or `?` to handle them."]
258pub fn exactly_n<'a, I, T: 'a, F>(
259 iter: I,
260 n: usize,
261 pred: F,
262) -> Result<(), QuantorError>
263where
264 I: IntoIterator<Item = &'a T>,
265 F: Fn(&T) -> bool,
266{
267 let found = iter.into_iter().filter(|x| pred(x)).count();
268
269 if found == n {
270 Ok(())
271 } else {
272 Err(QuantorError::ExactlyNFailed { kind: QuantorKind::ExactlyN, found, expected: n })
273 }
274}