1use crate::value::Ternary;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
24pub enum Algebra {
25 Kleene,
27 Lukasiewicz,
29 Bochvar,
31}
32
33impl Algebra {
34 pub const ALL: [Self; 3] = [Self::Kleene, Self::Lukasiewicz, Self::Bochvar];
36
37 pub const fn as_str(self) -> &'static str {
39 match self {
40 Self::Kleene => "kleene",
41 Self::Lukasiewicz => "lukasiewicz",
42 Self::Bochvar => "bochvar",
43 }
44 }
45
46 pub fn parse(s: &str) -> Option<Self> {
48 match s.trim().to_ascii_lowercase().as_str() {
49 "kleene" | "k3" => Some(Self::Kleene),
50 "lukasiewicz" | "luk" | "l3" => Some(Self::Lukasiewicz),
51 "bochvar" | "b3" => Some(Self::Bochvar),
52 _ => None,
53 }
54 }
55
56 pub const fn is_poisonous(self) -> bool {
58 matches!(self, Self::Bochvar)
59 }
60
61 pub const fn not(self, a: Ternary) -> Ternary {
63 match a {
64 Ternary::False => Ternary::True,
65 Ternary::Unknown => Ternary::Unknown,
66 Ternary::True => Ternary::False,
67 }
68 }
69
70 pub const fn and(self, a: Ternary, b: Ternary) -> Ternary {
74 match self {
75 Self::Bochvar => {
76 if a.is_unknown() || b.is_unknown() {
77 Ternary::Unknown
78 } else if a.is_true() && b.is_true() {
79 Ternary::True
80 } else {
81 Ternary::False
82 }
83 }
84 Self::Kleene | Self::Lukasiewicz => match (a, b) {
85 (Ternary::False, _) | (_, Ternary::False) => Ternary::False,
86 (Ternary::True, Ternary::True) => Ternary::True,
87 _ => Ternary::Unknown,
88 },
89 }
90 }
91
92 pub const fn or(self, a: Ternary, b: Ternary) -> Ternary {
96 match self {
97 Self::Bochvar => {
98 if a.is_unknown() || b.is_unknown() {
99 Ternary::Unknown
100 } else if a.is_true() || b.is_true() {
101 Ternary::True
102 } else {
103 Ternary::False
104 }
105 }
106 Self::Kleene | Self::Lukasiewicz => match (a, b) {
107 (Ternary::True, _) | (_, Ternary::True) => Ternary::True,
108 (Ternary::False, Ternary::False) => Ternary::False,
109 _ => Ternary::Unknown,
110 },
111 }
112 }
113
114 pub const fn strong_add(self, a: Ternary, b: Ternary) -> Ternary {
120 match self {
121 Self::Lukasiewicz => {
122 let s = a.as_f64() + b.as_f64();
124 if s >= 1.0 {
125 Ternary::True
126 } else if s <= 0.0 {
127 Ternary::False
128 } else {
129 Ternary::Unknown
130 }
131 }
132 Self::Kleene | Self::Bochvar => {
133 if a.is_unknown() || b.is_unknown() {
134 Ternary::Unknown
135 } else if a.is_true() && b.is_true() {
136 Ternary::True
137 } else {
138 Ternary::False
139 }
140 }
141 }
142 }
143
144 pub const fn strong_and(self, a: Ternary, b: Ternary) -> Ternary {
149 match self {
150 Self::Lukasiewicz => {
151 let s = a.as_f64() + b.as_f64() - 1.0;
152 if s <= 0.0 {
153 Ternary::False
154 } else if s >= 1.0 {
155 Ternary::True
156 } else {
157 Ternary::Unknown
158 }
159 }
160 Self::Kleene | Self::Bochvar => {
161 if a.is_unknown() || b.is_unknown() {
162 Ternary::Unknown
163 } else if a.is_true() && b.is_true() {
164 Ternary::True
165 } else {
166 Ternary::False
167 }
168 }
169 }
170 }
171
172 pub const fn implies(self, a: Ternary, b: Ternary) -> Ternary {
178 match self {
179 Self::Kleene => self.or(self.not(a), b),
180 Self::Bochvar => {
181 if a.is_unknown() || b.is_unknown() {
182 Ternary::Unknown
183 } else if a.is_true() {
184 b
185 } else {
186 Ternary::True
187 }
188 }
189 Self::Lukasiewicz => {
190 let s = 1.0 - a.as_f64() + b.as_f64();
192 if s >= 1.0 {
193 Ternary::True
194 } else if s <= 0.0 {
195 Ternary::False
196 } else {
197 Ternary::Unknown
198 }
199 }
200 }
201 }
202
203 pub const fn equiv(self, a: Ternary, b: Ternary) -> Ternary {
205 self.and(self.implies(a, b), self.implies(b, a))
206 }
207}
208
209impl std::fmt::Display for Algebra {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 f.write_str(self.as_str())
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn kleene_and_truth_table() {
221 use Ternary::*;
222 assert_eq!(Algebra::Kleene.and(False, Unknown), False);
223 assert_eq!(Algebra::Kleene.and(Unknown, Unknown), Unknown);
224 assert_eq!(Algebra::Kleene.and(Unknown, True), Unknown);
225 assert_eq!(Algebra::Kleene.and(True, True), True);
226 }
227
228 #[test]
229 fn bochvar_and_is_poisonous() {
230 use Ternary::*;
231 assert_eq!(Algebra::Bochvar.and(False, Unknown), Unknown);
232 assert_eq!(Algebra::Bochvar.and(True, Unknown), Unknown);
233 assert_eq!(Algebra::Bochvar.and(True, True), True);
234 assert_eq!(Algebra::Bochvar.and(False, False), False);
235 }
236
237 #[test]
238 fn bochvar_or_is_poisonous() {
239 use Ternary::*;
240 assert_eq!(Algebra::Bochvar.or(True, Unknown), Unknown);
241 assert_eq!(Algebra::Bochvar.or(False, Unknown), Unknown);
242 assert_eq!(Algebra::Bochvar.or(True, True), True);
243 }
244
245 #[test]
246 fn kleene_and_or_are_lattice_ops() {
247 for a in Ternary::ALL {
248 for b in Ternary::ALL {
249 assert_eq!(Algebra::Kleene.and(a, b), Algebra::Lukasiewicz.and(a, b));
250 assert_eq!(Algebra::Kleene.or(a, b), Algebra::Lukasiewicz.or(a, b));
251 }
252 }
253 }
254
255 #[test]
256 fn strong_add_differs_between_semantics() {
257 use Ternary::*;
258 assert_eq!(Algebra::Kleene.strong_add(Unknown, Unknown), Unknown);
259 assert_eq!(Algebra::Lukasiewicz.strong_add(Unknown, Unknown), True);
260 assert_eq!(Algebra::Bochvar.strong_add(Unknown, Unknown), Unknown);
261 assert_eq!(Algebra::Kleene.strong_add(True, False), False);
262 assert_eq!(Algebra::Kleene.strong_add(False, True), False);
263 assert_eq!(Algebra::Bochvar.strong_add(True, False), False);
264 assert_eq!(Algebra::Lukasiewicz.strong_add(False, True), True);
265 assert_eq!(Algebra::Lukasiewicz.strong_add(False, False), False);
266 assert_eq!(Algebra::Kleene.strong_add(True, True), True);
267 }
268
269 #[test]
270 fn strong_and_lukasiewicz_matches_formula() {
271 use Ternary::*;
272 assert_eq!(Algebra::Lukasiewicz.strong_and(Unknown, Unknown), False);
274 assert_eq!(Algebra::Lukasiewicz.strong_and(Unknown, True), Unknown);
275 assert_eq!(Algebra::Lukasiewicz.strong_and(True, True), True);
276 assert_eq!(Algebra::Lukasiewicz.strong_and(False, True), False);
277 assert_eq!(Algebra::Kleene.strong_and(Unknown, True), Unknown);
278 assert_eq!(Algebra::Kleene.strong_and(True, False), False);
279 assert_eq!(Algebra::Bochvar.strong_and(True, False), False);
280 assert_eq!(Algebra::Bochvar.strong_and(Unknown, True), Unknown);
281 }
282
283 #[test]
284 fn implication_differs_between_semantics() {
285 use Ternary::*;
286 assert_eq!(Algebra::Kleene.implies(Unknown, Unknown), Unknown);
288 assert_eq!(Algebra::Lukasiewicz.implies(Unknown, Unknown), True);
289 assert_eq!(Algebra::Bochvar.implies(Unknown, Unknown), Unknown);
290 assert_eq!(Algebra::Kleene.implies(True, False), False);
291 assert_eq!(Algebra::Kleene.implies(False, True), True);
292 assert_eq!(Algebra::Kleene.implies(False, Unknown), True);
293 assert_eq!(Algebra::Bochvar.implies(False, Unknown), Unknown);
294 assert_eq!(Algebra::Lukasiewicz.implies(False, Unknown), True);
295 }
296
297 #[test]
298 fn algebra_parsing() {
299 assert_eq!(Algebra::parse("K3"), Some(Algebra::Kleene));
300 assert_eq!(Algebra::parse("luk"), Some(Algebra::Lukasiewicz));
301 assert_eq!(Algebra::parse("b3"), Some(Algebra::Bochvar));
302 assert_eq!(Algebra::parse("nope"), None);
303 }
304}