1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum BoundType {
    Inclusive,
    Exclusive,
}

use std::cmp::Ordering;

use std::ops::Add;
use std::ops::Mul;
use std::ops::Neg;

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Bound<T> {
    pub bound_type: BoundType,
    pub value: T,
}

impl<T: Ord> Bound<T> {
    pub fn upper_bound_max(a: Self, b: Self) -> Self {
        if a.is_upper_bound_max(&b) {
            a
        } else {
            b
        }
    }

    pub fn is_upper_bound_max(&self, other: &Self) -> bool {
        match self.value.cmp(&other.value) {
            Ordering::Greater => true,
            Ordering::Less => false,
            Ordering::Equal => self.bound_type == BoundType::Inclusive,
        }
    }
    pub fn lower_bound_min(a: Self, b: Self) -> Self {
        if a.is_lower_bound_min(&b) {
            a
        } else {
            b
        }
    }

    pub fn is_lower_bound_min(&self, other: &Self) -> bool {
        match self.value.cmp(&other.value) {
            Ordering::Greater => false,
            Ordering::Less => true,
            Ordering::Equal => self.bound_type == BoundType::Inclusive,
        }
    }
}

impl<T> Bound<T> {
    pub fn inclusive(value: T) -> Bound<T> {
        Bound {
            bound_type: BoundType::Inclusive,
            value,
        }
    }
    pub fn exclusive(value: T) -> Bound<T> {
        Bound {
            bound_type: BoundType::Exclusive,
            value,
        }
    }

    pub fn to_exclusive(self) -> Bound<T> {
        Bound::exclusive(self.value)
    }

    pub fn combine<F: FnOnce(T, T) -> T>(self, other: Self, func: F) -> Self {
        let bound_type = if self.bound_type == BoundType::Exclusive
            || other.bound_type == BoundType::Exclusive
        {
            BoundType::Exclusive
        } else {
            BoundType::Inclusive
        };

        Bound {
            bound_type,
            value: func(self.value, other.value),
        }
    }
}

impl<T: Neg<Output = T>> Neg for Bound<T> {
    type Output = Self;

    fn neg(self) -> Self::Output {
        Bound {
            bound_type: self.bound_type,
            value: -self.value,
        }
    }
}

impl<T: Add<T, Output = T>> Add for Bound<T> {
    type Output = Self;

    fn add(self, other: Self) -> Self::Output {
        self.combine(other, Add::add)
    }
}

impl<T: Mul<T, Output = T>> Mul for Bound<T> {
    type Output = Self;

    fn mul(self, other: Self) -> Self::Output {
        self.combine(other, Mul::mul)
    }
}