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