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}