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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::cmp::Ordering::{self, *};

use crate::{DeepReveal, IsBot, IsTop, LatticeFrom, LatticeOrd, Merge};

/// A `Conflict` lattice, stores a single instance of `T` and goes to a "conflict" state (`None`)
/// if inequal `T` instances are merged together.
///
/// Like [`Point<T>`](crate::Point), but will go to "conflict" (top/`None`) instead of panicking.
///
/// Can be thought of as a lattice with a domain of size one, corresponding to the specific value
/// inside.
///
/// This can be used to wrap non-lattice (scalar) data into a lattice type.
#[repr(transparent)]
#[derive(Copy, Clone, Debug, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Conflict<T>(Option<T>);
impl<T> Conflict<T> {
    /// Create a new `Conflict` lattice instance from a value.
    pub fn new(val: Option<T>) -> Self {
        Self(val)
    }

    /// Create a new `Conflict` lattice instance from a value using `Into`.
    pub fn new_from(val: impl Into<Option<T>>) -> Self {
        Self::new(val.into())
    }

    /// Reveal the inner value as a shared reference.
    pub fn as_reveal_ref(&self) -> Option<&T> {
        self.0.as_ref()
    }

    /// Reveal the inner value as an exclusive reference.
    pub fn as_reveal_mut(&mut self) -> Option<&mut T> {
        self.0.as_mut()
    }

    /// Gets the inner by value, consuming self.
    pub fn into_reveal(self) -> Option<T> {
        self.0
    }
}

impl<T> DeepReveal for Conflict<T> {
    type Revealed = Option<T>;

    fn deep_reveal(self) -> Self::Revealed {
        self.0
    }
}

impl<T, O> Merge<Conflict<O>> for Conflict<T>
where
    T: PartialEq<O>,
{
    fn merge(&mut self, other: Conflict<O>) -> bool {
        if let Some(val_self) = &self.0 {
            if other.0.map_or(true, |val_other| val_self != &val_other) {
                self.0 = None;
                return true;
            }
        }
        false
    }
}

impl<T> LatticeFrom<Conflict<T>> for Conflict<T> {
    fn lattice_from(other: Conflict<T>) -> Self {
        other
    }
}

impl<T, O> PartialOrd<Conflict<O>> for Conflict<T>
where
    T: PartialEq<O>,
{
    fn partial_cmp(&self, other: &Conflict<O>) -> Option<Ordering> {
        match (&self.0, &other.0) {
            (None, None) => Some(Equal),
            (None, Some(_)) => Some(Greater),
            (Some(_), None) => Some(Less),
            (Some(val_self), Some(val_other)) => (val_self == val_other).then_some(Equal),
        }
    }
}
impl<T, O> LatticeOrd<Conflict<O>> for Conflict<T> where Self: PartialOrd<Conflict<O>> {}

impl<T, O> PartialEq<Conflict<O>> for Conflict<T>
where
    T: PartialEq<O>,
{
    fn eq(&self, other: &Conflict<O>) -> bool {
        match (&self.0, &other.0) {
            (None, None) => true,
            (Some(val_self), Some(val_other)) => val_self == val_other,
            _ => false,
        }
    }
}

impl<T> IsBot for Conflict<T> {
    fn is_bot(&self) -> bool {
        false
    }
}

impl<T> IsTop for Conflict<T> {
    fn is_top(&self) -> bool {
        self.0.is_none()
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::test::{
        check_all, check_lattice_is_bot, check_lattice_is_top, check_lattice_ord,
        check_lattice_properties, check_partial_ord_properties,
    };
    use crate::WithBot;

    #[test]
    fn consistency() {
        let items = &[
            Conflict::new_from("foo"),
            Conflict::new_from("bar"),
            Conflict::new(None),
        ];
        check_lattice_ord(items);
        check_partial_ord_properties(items);
        check_lattice_properties(items);
        check_lattice_is_bot(items);
        check_lattice_is_top(items);
    }

    #[test]
    fn consistency_withbot() {
        let items = &[
            WithBot::new_from(Conflict::new_from("foo")),
            WithBot::new_from(Conflict::new_from("bar")),
            WithBot::new_from(Conflict::new(None)),
            WithBot::new(None),
        ];
        check_all(items);
    }
}