mappy_core/
operators.rs

1//! Value merge operators for maplets
2//! 
3//! Implements the associative and commutative binary operators (⊕) that
4//! define how values are merged when keys collide in the maplet.
5
6use std::collections::HashSet;
7use std::hash::Hash;
8use crate::MapletResult;
9
10/// Trait for merge operators that define how values are combined
11pub trait MergeOperator<V>: Clone + Send + Sync {
12    /// Merge two values using the operator ⊕
13    fn merge(&self, left: V, right: V) -> MapletResult<V>;
14    
15    /// Get the identity element for this operator
16    fn identity(&self) -> V;
17    
18    /// Check if the operator is associative
19    fn is_associative(&self) -> bool {
20        true // Most operators are associative
21    }
22    
23    /// Check if the operator is commutative
24    fn is_commutative(&self) -> bool {
25        true // Most operators are commutative
26    }
27}
28
29/// Counter operator for counting use cases (addition)
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31pub struct CounterOperator;
32
33impl MergeOperator<u64> for CounterOperator {
34    fn merge(&self, left: u64, right: u64) -> MapletResult<u64> {
35        Ok(left.saturating_add(right))
36    }
37    
38    fn identity(&self) -> u64 {
39        0
40    }
41}
42
43impl MergeOperator<u32> for CounterOperator {
44    fn merge(&self, left: u32, right: u32) -> MapletResult<u32> {
45        Ok(left.saturating_add(right))
46    }
47    
48    fn identity(&self) -> u32 {
49        0
50    }
51}
52
53impl MergeOperator<i64> for CounterOperator {
54    fn merge(&self, left: i64, right: i64) -> MapletResult<i64> {
55        Ok(left.saturating_add(right))
56    }
57    
58    fn identity(&self) -> i64 {
59        0
60    }
61}
62
63/// Set operator for set-valued maps (union)
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
65pub struct SetOperator;
66
67impl<T: Clone + Hash + Eq> MergeOperator<HashSet<T>> for SetOperator {
68    fn merge(&self, mut left: HashSet<T>, right: HashSet<T>) -> MapletResult<HashSet<T>> {
69        left.extend(right);
70        Ok(left)
71    }
72    
73    fn identity(&self) -> HashSet<T> {
74        HashSet::new()
75    }
76}
77
78impl MergeOperator<i64> for SetOperator {
79    fn merge(&self, _left: i64, right: i64) -> MapletResult<i64> {
80        // For i64, we'll use "last value wins" semantics
81        Ok(right)
82    }
83    
84    fn identity(&self) -> i64 {
85        0
86    }
87}
88
89/// Max operator for tracking maximum values
90#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
91pub struct MaxOperator;
92
93impl MergeOperator<u64> for MaxOperator {
94    fn merge(&self, left: u64, right: u64) -> MapletResult<u64> {
95        Ok(left.max(right))
96    }
97    
98    fn identity(&self) -> u64 {
99        0
100    }
101}
102
103impl MergeOperator<f64> for MaxOperator {
104    fn merge(&self, left: f64, right: f64) -> MapletResult<f64> {
105        Ok(left.max(right))
106    }
107    
108    fn identity(&self) -> f64 {
109        f64::NEG_INFINITY
110    }
111}
112
113impl MergeOperator<i64> for MaxOperator {
114    fn merge(&self, left: i64, right: i64) -> MapletResult<i64> {
115        Ok(left.max(right))
116    }
117    
118    fn identity(&self) -> i64 {
119        i64::MIN
120    }
121}
122
123/// Min operator for tracking minimum values
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
125pub struct MinOperator;
126
127impl MergeOperator<u64> for MinOperator {
128    fn merge(&self, left: u64, right: u64) -> MapletResult<u64> {
129        Ok(left.min(right))
130    }
131    
132    fn identity(&self) -> u64 {
133        u64::MAX
134    }
135}
136
137impl MergeOperator<f64> for MinOperator {
138    fn merge(&self, left: f64, right: f64) -> MapletResult<f64> {
139        Ok(left.min(right))
140    }
141    
142    fn identity(&self) -> f64 {
143        f64::INFINITY
144    }
145}
146
147impl MergeOperator<i64> for MinOperator {
148    fn merge(&self, left: i64, right: i64) -> MapletResult<i64> {
149        Ok(left.min(right))
150    }
151    
152    fn identity(&self) -> i64 {
153        i64::MAX
154    }
155}
156
157/// Custom operator that allows user-defined merge logic
158pub struct CustomOperator<F> {
159    merge_fn: F,
160}
161
162impl<F> CustomOperator<F> {
163    /// Create a new custom operator
164    pub fn new(merge_fn: F) -> Self {
165        Self {
166            merge_fn,
167        }
168    }
169}
170
171impl<F> Clone for CustomOperator<F>
172where
173    F: Clone,
174{
175    fn clone(&self) -> Self {
176        Self {
177            merge_fn: self.merge_fn.clone(),
178        }
179    }
180}
181
182// CustomOperator doesn't implement Default since function pointers don't have a default
183
184impl<F> MergeOperator<i64> for CustomOperator<F>
185where
186    F: Fn(i64, i64) -> i64 + Clone + Send + Sync,
187{
188    fn merge(&self, left: i64, right: i64) -> MapletResult<i64> {
189        Ok((self.merge_fn)(left, right))
190    }
191    
192    fn identity(&self) -> i64 {
193        0
194    }
195}
196
197/// String concatenation operator
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
199pub struct StringConcatOperator;
200
201impl MergeOperator<String> for StringConcatOperator {
202    fn merge(&self, left: String, right: String) -> MapletResult<String> {
203        Ok(format!("{}{}", left, right))
204    }
205    
206    fn identity(&self) -> String {
207        String::new()
208    }
209}
210
211/// Vector concatenation operator
212#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
213pub struct VectorConcatOperator;
214
215impl<T: Clone> MergeOperator<Vec<T>> for VectorConcatOperator {
216    fn merge(&self, mut left: Vec<T>, right: Vec<T>) -> MapletResult<Vec<T>> {
217        left.extend(right);
218        Ok(left)
219    }
220    
221    fn identity(&self) -> Vec<T> {
222        Vec::new()
223    }
224}
225
226/// Vector operator for numeric values (element-wise addition)
227#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
228pub struct VectorOperator;
229
230impl MergeOperator<i64> for VectorOperator {
231    fn merge(&self, left: i64, right: i64) -> MapletResult<i64> {
232        Ok(left + right)
233    }
234    
235    fn identity(&self) -> i64 {
236        0
237    }
238}
239
240/// Boolean OR operator
241#[derive(Debug, Clone, Copy, PartialEq, Eq)]
242pub struct BoolOrOperator;
243
244impl MergeOperator<bool> for BoolOrOperator {
245    fn merge(&self, left: bool, right: bool) -> MapletResult<bool> {
246        Ok(left || right)
247    }
248    
249    fn identity(&self) -> bool {
250        false
251    }
252}
253
254/// Boolean AND operator
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub struct BoolAndOperator;
257
258impl MergeOperator<bool> for BoolAndOperator {
259    fn merge(&self, left: bool, right: bool) -> MapletResult<bool> {
260        Ok(left && right)
261    }
262    
263    fn identity(&self) -> bool {
264        true
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use super::*;
271    use std::collections::HashSet;
272
273    #[test]
274    fn test_counter_operator() {
275        let op: CounterOperator = CounterOperator;
276        
277        assert_eq!(op.merge(5u64, 3u64).unwrap(), 8);
278        assert_eq!(op.merge(0u64, 10u64).unwrap(), 10);
279        
280        // Test saturation
281        assert_eq!(op.merge(u64::MAX, 1).unwrap(), u64::MAX);
282    }
283
284    #[test]
285    fn test_set_operator() {
286        let op = SetOperator;
287        
288        let mut set1 = HashSet::new();
289        set1.insert("a".to_string());
290        set1.insert("b".to_string());
291        
292        let mut set2 = HashSet::new();
293        set2.insert("b".to_string());
294        set2.insert("c".to_string());
295        
296        let result = op.merge(set1, set2).unwrap();
297        assert_eq!(result.len(), 3);
298        assert!(result.contains("a"));
299        assert!(result.contains("b"));
300        assert!(result.contains("c"));
301    }
302
303    #[test]
304    fn test_max_operator() {
305        let op: MaxOperator = MaxOperator;
306        
307        assert_eq!(op.merge(5u64, 3u64).unwrap(), 5);
308        assert_eq!(op.merge(3u64, 5u64).unwrap(), 5);
309        assert_eq!(op.merge(5.0, 3.0).unwrap(), 5.0);
310    }
311
312    #[test]
313    fn test_min_operator() {
314        let op: MinOperator = MinOperator;
315        
316        assert_eq!(op.merge(5u64, 3u64).unwrap(), 3);
317        assert_eq!(op.merge(3u64, 5u64).unwrap(), 3);
318        assert_eq!(op.merge(5.0, 3.0).unwrap(), 3.0);
319    }
320
321    #[test]
322    fn test_string_concat_operator() {
323        let op = StringConcatOperator;
324        
325        assert_eq!(op.merge("hello".to_string(), "world".to_string()).unwrap(), "helloworld");
326        assert_eq!(op.identity(), "");
327    }
328
329    #[test]
330    fn test_vector_concat_operator() {
331        let op = VectorConcatOperator;
332        
333        let vec1 = vec![1, 2, 3];
334        let vec2 = vec![4, 5, 6];
335        let result = op.merge(vec1, vec2).unwrap();
336        assert_eq!(result, vec![1, 2, 3, 4, 5, 6]);
337    }
338
339    #[test]
340    fn test_bool_operators() {
341        let or_op = BoolOrOperator;
342        let and_op = BoolAndOperator;
343        
344        assert_eq!(or_op.merge(false, true).unwrap(), true);
345        assert_eq!(or_op.merge(false, false).unwrap(), false);
346        assert_eq!(or_op.identity(), false);
347        
348        assert_eq!(and_op.merge(true, false).unwrap(), false);
349        assert_eq!(and_op.merge(true, true).unwrap(), true);
350        assert_eq!(and_op.identity(), true);
351    }
352}