1use crate::contravariant::{Contravariant, PredicateF};
5#[cfg(all(not(feature = "std"), feature = "alloc"))]
6use alloc::boxed::Box;
7
8pub trait Divide: Contravariant {
17 fn divide<A: 'static, B: 'static, C: 'static>(
18 f: impl Fn(C) -> (A, B) + 'static,
19 fa: Self::Of<A>,
20 fb: Self::Of<B>,
21 ) -> Self::Of<C>;
22}
23
24impl Divide for PredicateF {
25 fn divide<A: 'static, B: 'static, C: 'static>(
26 f: impl Fn(C) -> (A, B) + 'static,
27 fa: Box<dyn Fn(A) -> bool>,
28 fb: Box<dyn Fn(B) -> bool>,
29 ) -> Box<dyn Fn(C) -> bool> {
30 Box::new(move |c| {
31 let (a, b) = f(c);
32 fa(a) && fb(b)
33 })
34 }
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40
41 #[test]
42 fn predicate_divide() {
43 let is_positive: Box<dyn Fn(i32) -> bool> = Box::new(|x| x > 0);
44 let is_even: Box<dyn Fn(i32) -> bool> = Box::new(|x| x % 2 == 0);
45
46 let both: Box<dyn Fn((i32, i32)) -> bool> =
48 PredicateF::divide(|pair: (i32, i32)| pair, is_positive, is_even);
49
50 assert!(both((3, 4))); assert!(!both((-1, 4))); assert!(!both((3, 3))); }
54}
55
56#[cfg(test)]
57mod law_tests {
58 use super::*;
59 use proptest::prelude::*;
60
61 proptest! {
62 #[test]
64 fn predicate_associativity(x in any::<i8>(), y in any::<i8>(), z in any::<i8>()) {
65 let pa: Box<dyn Fn(i8) -> bool> = Box::new(|a| a > 0);
66 let pb: Box<dyn Fn(i8) -> bool> = Box::new(|b| b > 0);
67 let pc: Box<dyn Fn(i8) -> bool> = Box::new(|c| c > 0);
68
69 let pa2: Box<dyn Fn(i8) -> bool> = Box::new(|a| a > 0);
71 let pb2: Box<dyn Fn(i8) -> bool> = Box::new(|b| b > 0);
72 let pc2: Box<dyn Fn(i8) -> bool> = Box::new(|c| c > 0);
73
74 let ab = PredicateF::divide(|pair: (i8, i8)| pair, pa, pb);
75 let left = PredicateF::divide(
76 |triple: (i8, i8, i8)| ((triple.0, triple.1), triple.2),
77 ab,
78 pc,
79 );
80
81 let bc = PredicateF::divide(|pair: (i8, i8)| pair, pb2, pc2);
82 let right = PredicateF::divide(
83 |triple: (i8, i8, i8)| (triple.0, (triple.1, triple.2)),
84 pa2,
85 bc,
86 );
87
88 prop_assert_eq!(left((x, y, z)), right((x, y, z)));
89 }
90 }
91}