qubit_function/comparator/box_comparator.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! Defines the `BoxComparator` public type.
10
11#![allow(unused_imports)]
12
13use super::*;
14
15/// A boxed comparator with single ownership.
16///
17/// `BoxComparator` wraps a comparator function in a `Box`, providing single
18/// ownership semantics. It is not cloneable and consumes `self` in
19/// composition operations.
20///
21/// # Type Parameters
22///
23/// * `T` - The type of values being compared
24///
25/// # Examples
26///
27/// ```rust
28/// use qubit_function::comparator::{Comparator, BoxComparator};
29/// use std::cmp::Ordering;
30///
31/// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
32/// assert_eq!(cmp.compare(&5, &3), Ordering::Greater);
33/// ```
34///
35/// # Author
36///
37/// Haixing Hu
38pub struct BoxComparator<T> {
39 pub(super) function: Box<dyn Fn(&T, &T) -> Ordering>,
40}
41
42impl<T> BoxComparator<T> {
43 /// Creates a new `BoxComparator` from a closure.
44 ///
45 /// # Parameters
46 ///
47 /// * `f` - The closure to wrap
48 ///
49 /// # Returns
50 ///
51 /// A new `BoxComparator` instance.
52 ///
53 /// # Examples
54 ///
55 /// ```rust
56 /// use qubit_function::comparator::BoxComparator;
57 ///
58 /// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
59 /// ```
60 #[inline]
61 pub fn new<F>(f: F) -> Self
62 where
63 F: Fn(&T, &T) -> Ordering + 'static,
64 {
65 Self {
66 function: Box::new(f),
67 }
68 }
69
70 /// Returns a comparator that imposes the reverse ordering.
71 ///
72 /// # Returns
73 ///
74 /// A new `BoxComparator` that reverses the comparison order.
75 ///
76 /// # Examples
77 ///
78 /// ```rust
79 /// use qubit_function::comparator::{Comparator, BoxComparator};
80 /// use std::cmp::Ordering;
81 ///
82 /// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
83 /// let rev = cmp.reversed();
84 /// assert_eq!(rev.compare(&5, &3), Ordering::Less);
85 /// ```
86 #[inline]
87 pub fn reversed(self) -> Self
88 where
89 T: 'static,
90 {
91 BoxComparator::new(move |a, b| (self.function)(b, a))
92 }
93
94 /// Returns a comparator that uses this comparator first, then another
95 /// comparator if this one considers the values equal.
96 ///
97 /// # Parameters
98 ///
99 /// * `other` - The comparator to use for tie-breaking. **Note: This
100 /// parameter is passed by value and will transfer ownership.** If you
101 /// need to preserve the original comparator, clone it first (if it
102 /// implements `Clone`). Can be:
103 /// - A `BoxComparator<T>`
104 /// - An `RcComparator<T>`
105 /// - An `ArcComparator<T>`
106 /// - Any type implementing `Comparator<T>`
107 ///
108 /// # Returns
109 ///
110 /// A new `BoxComparator` that chains this comparator with another.
111 ///
112 /// # Examples
113 ///
114 /// ```rust
115 /// use qubit_function::comparator::{Comparator, BoxComparator};
116 /// use std::cmp::Ordering;
117 ///
118 /// #[derive(Debug)]
119 /// struct Person {
120 /// name: String,
121 /// age: i32,
122 /// }
123 ///
124 /// let by_name = BoxComparator::new(|a: &Person, b: &Person| {
125 /// a.name.cmp(&b.name)
126 /// });
127 /// let by_age = BoxComparator::new(|a: &Person, b: &Person| {
128 /// a.age.cmp(&b.age)
129 /// });
130 ///
131 /// // by_age is moved here
132 /// let cmp = by_name.then_comparing(by_age);
133 ///
134 /// let p1 = Person { name: "Alice".to_string(), age: 30 };
135 /// let p2 = Person { name: "Alice".to_string(), age: 25 };
136 /// assert_eq!(cmp.compare(&p1, &p2), Ordering::Greater);
137 /// // by_age.compare(&p1, &p2); // Would not compile - moved
138 /// ```
139 #[inline]
140 pub fn then_comparing(self, other: Self) -> Self
141 where
142 T: 'static,
143 {
144 BoxComparator::new(move |a, b| match (self.function)(a, b) {
145 Ordering::Equal => (other.function)(a, b),
146 ord => ord,
147 })
148 }
149
150 /// Returns a comparator that compares values by a key extracted by the
151 /// given function.
152 ///
153 /// # Parameters
154 ///
155 /// * `key_fn` - A function that extracts a comparable key from values
156 ///
157 /// # Returns
158 ///
159 /// A new `BoxComparator` that compares by the extracted key.
160 ///
161 /// # Examples
162 ///
163 /// ```rust
164 /// use qubit_function::comparator::{Comparator, BoxComparator};
165 /// use std::cmp::Ordering;
166 ///
167 /// #[derive(Debug)]
168 /// struct Person {
169 /// name: String,
170 /// age: i32,
171 /// }
172 ///
173 /// let by_age = BoxComparator::comparing(|p: &Person| &p.age);
174 /// let p1 = Person { name: "Alice".to_string(), age: 30 };
175 /// let p2 = Person { name: "Bob".to_string(), age: 25 };
176 /// assert_eq!(by_age.compare(&p1, &p2), Ordering::Greater);
177 /// ```
178 #[inline]
179 pub fn comparing<K, F>(key_fn: F) -> Self
180 where
181 K: Ord,
182 F: Fn(&T) -> &K + 'static,
183 {
184 BoxComparator::new(move |a: &T, b: &T| key_fn(a).cmp(key_fn(b)))
185 }
186
187 /// Converts this comparator into a closure.
188 ///
189 /// # Returns
190 ///
191 /// A closure that implements `Fn(&T, &T) -> Ordering`.
192 ///
193 /// # Examples
194 ///
195 /// ```rust
196 /// use qubit_function::comparator::{Comparator, BoxComparator};
197 /// use std::cmp::Ordering;
198 ///
199 /// let cmp = BoxComparator::new(|a: &i32, b: &i32| a.cmp(b));
200 /// let func = cmp.into_fn();
201 /// assert_eq!(func(&5, &3), Ordering::Greater);
202 /// ```
203 #[inline]
204 pub fn into_fn(self) -> impl Fn(&T, &T) -> Ordering {
205 move |a: &T, b: &T| (self.function)(a, b)
206 }
207}
208
209impl<T> Comparator<T> for BoxComparator<T> {
210 #[inline]
211 fn compare(&self, a: &T, b: &T) -> Ordering {
212 (self.function)(a, b)
213 }
214}