Skip to main content

qubit_function/predicates/
bi_predicate.rs

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