1
use serde::{Deserialize, Serialize};
2
// use std::iter::Ste
3

            
4
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
5
pub enum Range<A>
6
where
7
    A: Ord,
8
{
9
    Single(A),
10
    Bounded(A, A),
11
}
12

            
13
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
14
pub enum Domain {
15
    BoolDomain,
16
    IntDomain(Vec<Range<i32>>),
17
}
18

            
19
impl Domain {
20
    /// Return a list of all possible i32 values in the domain if it is an IntDomain.
21
    pub fn values_i32(&self) -> Option<Vec<i32>> {
22
        match self {
23
            Domain::IntDomain(ranges) => Some(
24
                ranges
25
                    .iter()
26
                    .flat_map(|r| match r {
27
                        Range::Single(i) => vec![*i],
28
                        Range::Bounded(i, j) => (*i..=*j).collect(),
29
                    })
30
                    .collect(),
31
            ),
32
            _ => None,
33
        }
34
    }
35

            
36
    /// Return an unoptimised domain that is the result of applying a binary i32 operation to two domains.
37
    ///
38
    /// The given operator may return None if the operation is not defined for its arguments.
39
    /// Undefined values will not be included in the resulting domain.
40
    ///
41
    /// Returns None if the domains are not valid for i32 operations.
42
    pub fn apply_i32(&self, op: fn(i32, i32) -> Option<i32>, other: &Domain) -> Option<Domain> {
43
        if let (Some(vs1), Some(vs2)) = (self.values_i32(), other.values_i32()) {
44
            // TODO: (flm8) Optimise to use smarter, less brute-force methods
45
            let mut new_ranges = vec![];
46
            for (v1, v2) in itertools::iproduct!(vs1, vs2) {
47
                op(v1, v2).map(|v| new_ranges.push(Range::Single(v)));
48
            }
49
            return Some(Domain::IntDomain(new_ranges));
50
        }
51
        None
52
    }
53
}
54

            
55
#[cfg(test)]
56
mod tests {
57
    use super::*;
58

            
59
    #[test]
60
    fn test_negative_product() {
61
        let d1 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
62
        let d2 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
63
        let res = d1.apply_i32(|a, b| Some(a * b), &d2).unwrap();
64

            
65
        if let Domain::IntDomain(ranges) = res {
66
            assert!(!ranges.contains(&Range::Single(-4)));
67
            assert!(!ranges.contains(&Range::Single(-3)));
68
            assert!(ranges.contains(&Range::Single(-2)));
69
            assert!(ranges.contains(&Range::Single(-1)));
70
            assert!(ranges.contains(&Range::Single(0)));
71
            assert!(ranges.contains(&Range::Single(1)));
72
            assert!(ranges.contains(&Range::Single(2)));
73
            assert!(!ranges.contains(&Range::Single(3)));
74
            assert!(ranges.contains(&Range::Single(4)));
75
        } else {
76
            panic!();
77
        }
78
    }
79

            
80
    #[test]
81
    fn test_negative_div() {
82
        let d1 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
83
        let d2 = Domain::IntDomain(vec![Range::Bounded(-2, 1)]);
84
        let res = d1
85
            .apply_i32(|a, b| if b != 0 { Some(a / b) } else { None }, &d2)
86
            .unwrap();
87

            
88
        if let Domain::IntDomain(ranges) = res {
89
            assert!(!ranges.contains(&Range::Single(-4)));
90
            assert!(!ranges.contains(&Range::Single(-3)));
91
            assert!(ranges.contains(&Range::Single(-2)));
92
            assert!(ranges.contains(&Range::Single(-1)));
93
            assert!(ranges.contains(&Range::Single(0)));
94
            assert!(ranges.contains(&Range::Single(1)));
95
            assert!(ranges.contains(&Range::Single(2)));
96
            assert!(!ranges.contains(&Range::Single(3)));
97
            assert!(!ranges.contains(&Range::Single(4)));
98
        } else {
99
            panic!();
100
        }
101
    }
102
}