Skip to main content

qubit_function/predicates/
bi_predicate.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # BiPredicate Abstraction
10//!
11//! Provides a Rust implementation similar to Java's `BiPredicate`
12//! interface for testing whether two values satisfy a condition.
13//!
14//! ## Core Semantics
15//!
16//! A **BiPredicate** is fundamentally a pure judgment operation that
17//! tests whether two values satisfy a specific condition. It should
18//! be:
19//!
20//! - **Read-only**: Does not modify the tested values
21//! - **Side-effect free**: Does not change external state (from the
22//!   user's perspective)
23//! - **Repeatable**: Same inputs should produce the same result
24//! - **Deterministic**: Judgment logic should be predictable
25//!
26//! It is similar to the `Fn(&T, &U) -> bool` trait in the standard library.
27//!
28//! ## Design Philosophy
29//!
30//! This module follows the same principles as the `Predicate` module:
31//!
32//! 1. **Single Trait**: Only one `BiPredicate<T, U>` trait with
33//!    `&self`, keeping the API simple and semantically clear
34//! 2. **No BiPredicateMut**: All stateful scenarios use interior
35//!    mutability (`RefCell`, `Cell`, `Mutex`) instead of `&mut self`
36//! 3. **No BiPredicateOnce**: Violates bi-predicate semantics -
37//!    judgments should be repeatable
38//! 4. **Three Implementations**: `BoxBiPredicate`, `RcBiPredicate`,
39//!    and `ArcBiPredicate` cover all ownership scenarios
40//!
41//! ## Type Selection Guide
42//!
43//! | Scenario | Recommended Type | Reason |
44//! |----------|------------------|--------|
45//! | One-time use | `BoxBiPredicate` | Single ownership, no overhead |
46//! | Multi-threaded | `ArcBiPredicate` | Thread-safe, clonable |
47//! | Single-threaded reuse | `RcBiPredicate` | Better performance |
48//! | Stateful predicate | Any type + `RefCell`/`Cell`/`Mutex` | Interior mutability |
49//!
50//! ## Examples
51//!
52//! ### Basic Usage with Closures
53//!
54//! ```rust
55//! use qubit_function::BiPredicate;
56//!
57//! let is_sum_positive = |x: &i32, y: &i32| x + y > 0;
58//! assert!(is_sum_positive.test(&5, &3));
59//! assert!(!is_sum_positive.test(&-3, &-7));
60//! ```
61//!
62//! ### BoxBiPredicate - Single Ownership
63//!
64//! ```rust
65//! use qubit_function::{BiPredicate, BoxBiPredicate};
66//!
67//! let pred = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0)
68//!     .and(BoxBiPredicate::new(|x, y| x > y));
69//! assert!(pred.test(&10, &5));
70//! ```
71//!
72//! ### Closure Composition with Extension Methods
73//!
74//! Closures automatically gain `and`, `or`, `not` methods through the
75//! `FnBiPredicateOps` extension trait, returning `BoxBiPredicate`:
76//!
77//! ```rust
78//! use qubit_function::{BiPredicate,
79//!     FnBiPredicateOps};
80//!
81//! // Compose closures directly - result is BoxBiPredicate
82//! let is_sum_positive = |x: &i32, y: &i32| x + y > 0;
83//! let first_larger = |x: &i32, y: &i32| x > y;
84//!
85//! let combined = is_sum_positive.and(first_larger);
86//! assert!(combined.test(&10, &5));
87//! assert!(!combined.test(&3, &8));
88//!
89//! // Use `or` for disjunction
90//! let negative_sum = |x: &i32, y: &i32| x + y < 0;
91//! let both_large = |x: &i32, y: &i32| *x > 100 && *y > 100;
92//! let either = negative_sum.or(both_large);
93//! assert!(either.test(&-10, &5));
94//! assert!(either.test(&200, &150));
95//! ```
96//!
97//! ### RcBiPredicate - Single-threaded Reuse
98//!
99//! ```rust
100//! use qubit_function::{BiPredicate, RcBiPredicate};
101//!
102//! let pred = RcBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
103//! let combined1 = pred.and(RcBiPredicate::new(|x, y| x > y));
104//! let combined2 = pred.or(RcBiPredicate::new(|x, y| *x > 100));
105//!
106//! // Original predicate is still usable
107//! assert!(pred.test(&5, &3));
108//! ```
109//!
110//! ### ArcBiPredicate - Thread-safe Sharing
111//!
112//! ```rust
113//! use qubit_function::{BiPredicate, ArcBiPredicate};
114//! use std::thread;
115//!
116//! let pred = ArcBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
117//! let pred_clone = pred.clone();
118//!
119//! let handle = thread::spawn(move || {
120//!     pred_clone.test(&10, &5)
121//! });
122//!
123//! assert!(handle.join().unwrap());
124//! assert!(pred.test(&3, &7));  // Original still usable
125//! ```
126//!
127//! ### Stateful BiPredicates with Interior Mutability
128//!
129//! ```rust
130//! use qubit_function::{BiPredicate, BoxBiPredicate};
131//! use std::cell::Cell;
132//!
133//! let count = Cell::new(0);
134//! let pred = BoxBiPredicate::new(move |x: &i32, y: &i32| {
135//!     count.set(count.get() + 1);
136//!     x + y > 0
137//! });
138//!
139//! // No need for `mut` - interior mutability handles state
140//! assert!(pred.test(&5, &3));
141//! assert!(!pred.test(&-8, &-3));
142//! ```
143//!
144//! ## Author
145//!
146//! Haixing Hu
147use std::rc::Rc;
148use std::sync::Arc;
149
150use crate::macros::{
151    impl_arc_conversions,
152    impl_box_conversions,
153    impl_closure_trait,
154    impl_rc_conversions,
155};
156use crate::predicates::macros::{
157    constants::{
158        ALWAYS_FALSE_NAME,
159        ALWAYS_TRUE_NAME,
160    },
161    impl_box_predicate_methods,
162    impl_predicate_clone,
163    impl_predicate_common_methods,
164    impl_predicate_debug_display,
165    impl_shared_predicate_methods,
166};
167
168/// Type alias for bi-predicate function to simplify complex types.
169///
170/// This type alias represents a function that takes two references and returns a boolean.
171/// It is used to reduce type complexity in struct definitions.
172type BiPredicateFn<T, U> = dyn Fn(&T, &U) -> bool;
173
174/// Type alias for thread-safe bi-predicate function to simplify complex types.
175///
176/// This type alias represents a function that takes two references and returns a boolean,
177/// with Send + Sync bounds for thread-safe usage. It is used to reduce type complexity
178/// in Arc-based struct definitions.
179type SendSyncBiPredicateFn<T, U> = dyn Fn(&T, &U) -> bool + Send + Sync;
180
181mod box_bi_predicate;
182pub use box_bi_predicate::BoxBiPredicate;
183mod rc_bi_predicate;
184pub use rc_bi_predicate::RcBiPredicate;
185mod arc_bi_predicate;
186pub use arc_bi_predicate::ArcBiPredicate;
187mod fn_bi_predicate_ops;
188pub use fn_bi_predicate_ops::FnBiPredicateOps;
189
190/// A bi-predicate trait for testing whether two values satisfy a
191/// condition.
192///
193/// This trait represents a **pure judgment operation** - it tests
194/// whether two given values meet certain criteria without modifying
195/// either the values or the bi-predicate itself (from the user's
196/// perspective). This semantic clarity distinguishes bi-predicates
197/// from consumers or transformers.
198///
199/// ## Design Rationale
200///
201/// This is a **minimal trait** that only defines:
202/// - The core `test` method using `&self` (immutable borrow)
203/// - Type conversion methods (`into_box`, `into_rc`, `into_arc`)
204/// - Closure conversion method (`into_fn`)
205///
206/// Logical composition methods (`and`, `or`, `not`, `xor`, `nand`,
207/// `nor`) are intentionally **not** part of the trait. Instead, they
208/// are implemented on concrete types (`BoxBiPredicate`,
209/// `RcBiPredicate`, `ArcBiPredicate`), allowing each implementation
210/// to maintain its specific ownership characteristics:
211///
212/// - `BoxBiPredicate`: Methods consume `self` (single ownership)
213/// - `RcBiPredicate`: Methods borrow `&self` (shared ownership)
214/// - `ArcBiPredicate`: Methods borrow `&self` (thread-safe shared
215///   ownership)
216///
217/// ## Why `&self` Instead of `&mut self`?
218///
219/// Bi-predicates use `&self` because:
220///
221/// 1. **Semantic Clarity**: A bi-predicate is a judgment, not a
222///    mutation
223/// 2. **Flexibility**: Can be used in immutable contexts
224/// 3. **Simplicity**: No need for `mut` in user code
225/// 4. **Interior Mutability**: State (if needed) can be managed with
226///    `RefCell`, `Cell`, or `Mutex`
227///
228/// ## Automatic Implementation for Closures
229///
230/// Any closure matching `Fn(&T, &U) -> bool` automatically implements
231/// this trait, providing seamless integration with Rust's closure
232/// system.
233///
234/// ## Examples
235///
236/// ### Basic Usage
237///
238/// ```rust
239/// use qubit_function::BiPredicate;
240///
241/// let is_sum_positive = |x: &i32, y: &i32| x + y > 0;
242/// assert!(is_sum_positive.test(&5, &3));
243/// assert!(!is_sum_positive.test(&-5, &-3));
244/// ```
245///
246/// ### Type Conversion
247///
248/// ```rust
249/// use qubit_function::{BiPredicate,
250///     BoxBiPredicate};
251///
252/// let closure = |x: &i32, y: &i32| x + y > 0;
253/// let boxed: BoxBiPredicate<i32, i32> = closure.into_box();
254/// assert!(boxed.test(&5, &3));
255/// ```
256///
257/// ### Stateful BiPredicate with Interior Mutability
258///
259/// ```rust
260/// use qubit_function::{BiPredicate,
261///     BoxBiPredicate};
262/// use std::cell::Cell;
263///
264/// let count = Cell::new(0);
265/// let counting_pred = BoxBiPredicate::new(move |x: &i32, y: &i32| {
266///     count.set(count.get() + 1);
267///     x + y > 0
268/// });
269///
270/// // Note: No `mut` needed - interior mutability handles state
271/// assert!(counting_pred.test(&5, &3));
272/// assert!(!counting_pred.test(&-5, &-3));
273/// ```
274///
275/// ## Author
276///
277/// Haixing Hu
278pub trait BiPredicate<T, U> {
279    /// Tests whether the given values satisfy this bi-predicate.
280    ///
281    /// # Parameters
282    ///
283    /// * `first` - The first value to test.
284    /// * `second` - The second value to test.
285    ///
286    /// # Returns
287    ///
288    /// `true` if the values satisfy this bi-predicate, `false`
289    /// otherwise.
290    fn test(&self, first: &T, second: &U) -> bool;
291
292    /// Converts this bi-predicate into a `BoxBiPredicate`.
293    ///
294    /// # Returns
295    ///
296    /// A `BoxBiPredicate` wrapping this bi-predicate.
297    ///
298    /// # Default Implementation
299    ///
300    /// The default implementation wraps the bi-predicate in a
301    /// closure that calls `test`, providing automatic conversion
302    /// for custom types that only implement the core `test`
303    /// method.
304    fn into_box(self) -> BoxBiPredicate<T, U>
305    where
306        Self: Sized + 'static,
307    {
308        BoxBiPredicate::new(move |first, second| self.test(first, second))
309    }
310
311    /// Converts this bi-predicate into an `RcBiPredicate`.
312    ///
313    /// # Returns
314    ///
315    /// An `RcBiPredicate` wrapping this bi-predicate.
316    ///
317    /// # Default Implementation
318    ///
319    /// The default implementation wraps the bi-predicate in a
320    /// closure that calls `test`, providing automatic conversion
321    /// for custom types that only implement the core `test`
322    /// method.
323    fn into_rc(self) -> RcBiPredicate<T, U>
324    where
325        Self: Sized + 'static,
326    {
327        RcBiPredicate::new(move |first, second| self.test(first, second))
328    }
329
330    /// Converts this bi-predicate into an `ArcBiPredicate`.
331    ///
332    /// # Returns
333    ///
334    /// An `ArcBiPredicate` wrapping this bi-predicate.
335    ///
336    /// # Default Implementation
337    ///
338    /// The default implementation wraps the bi-predicate in a
339    /// closure that calls `test`, providing automatic conversion
340    /// for custom types that only implement the core `test`
341    /// method. Note that this requires `Send + Sync` bounds for
342    /// thread-safe sharing.
343    fn into_arc(self) -> ArcBiPredicate<T, U>
344    where
345        Self: Sized + Send + Sync + 'static,
346    {
347        ArcBiPredicate::new(move |first, second| self.test(first, second))
348    }
349
350    /// Converts this bi-predicate into a closure that can be used
351    /// directly with standard library methods.
352    ///
353    /// This method consumes the bi-predicate and returns a closure
354    /// with signature `Fn(&T, &U) -> bool`. Since `Fn` is a subtrait
355    /// of `FnMut`, the returned closure can be used in any context
356    /// that requires either `Fn(&T, &U) -> bool` or
357    /// `FnMut(&T, &U) -> bool`.
358    ///
359    /// # Returns
360    ///
361    /// A closure implementing `Fn(&T, &U) -> bool` (also usable as
362    /// `FnMut(&T, &U) -> bool`).
363    ///
364    /// # Default Implementation
365    ///
366    /// The default implementation returns a closure that calls the
367    /// `test` method, providing automatic conversion for custom
368    /// types.
369    ///
370    /// # Examples
371    ///
372    /// ## Using with Iterator Methods
373    ///
374    /// ```rust
375    /// use qubit_function::{BiPredicate,
376    ///     BoxBiPredicate};
377    ///
378    /// let pred = BoxBiPredicate::new(|x: &i32, y: &i32| x + y > 0);
379    ///
380    /// let pairs = vec![(1, 2), (-1, 3), (5, -6)];
381    /// let mut closure = pred.into_fn();
382    /// let positives: Vec<_> = pairs.iter()
383    ///     .filter(|(x, y)| closure(x, y))
384    ///     .collect();
385    /// assert_eq!(positives, vec![&(1, 2), &(-1, 3)]);
386    /// ```
387    fn into_fn(self) -> impl Fn(&T, &U) -> bool
388    where
389        Self: Sized + 'static,
390    {
391        move |first, second| self.test(first, second)
392    }
393
394    fn to_box(&self) -> BoxBiPredicate<T, U>
395    where
396        Self: Sized + Clone + 'static,
397    {
398        self.clone().into_box()
399    }
400
401    fn to_rc(&self) -> RcBiPredicate<T, U>
402    where
403        Self: Sized + Clone + 'static,
404    {
405        self.clone().into_rc()
406    }
407
408    fn to_arc(&self) -> ArcBiPredicate<T, U>
409    where
410        Self: Sized + Clone + Send + Sync + 'static,
411    {
412        self.clone().into_arc()
413    }
414
415    fn to_fn(&self) -> impl Fn(&T, &U) -> bool
416    where
417        Self: Sized + Clone + 'static,
418    {
419        self.clone().into_fn()
420    }
421}