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