tribool/
lib.rs

1//! Three-valued / Three-state logic
2//!
3//! Three-valued logic is an extension to Boolean logic with three values indicated
4//! True, False and some Indeterminate third value.
5//!
6//! Because of the limitations of logical operator overloading in Rust, AND, OR and XOR operations
7//! are implemented with the bitwise `&`, `|` and `^` operators.
8//!
9//! For more information and the full truth tables of this implementation, see
10//! [the Wikipedia page](https://en.wikipedia.org/wiki/Three-valued_logic)
11
12#[cfg(feature = "serde")]
13mod serde;
14
15use std::fmt::{Display, Formatter, Result as FmtResult};
16use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not};
17use std::str::FromStr;
18
19/// Three-state Boolean logic
20#[derive(Debug, Clone, Copy, Hash)]
21pub enum Tribool {
22    /// Truth value
23    True,
24    /// False value
25    False,
26    /// Unknown/Indeterminate value
27    Indeterminate,
28}
29
30pub use Tribool::{False, Indeterminate, True};
31
32impl Default for Tribool {
33    #[inline]
34    fn default() -> Tribool {
35        False
36    }
37}
38
39impl FromStr for Tribool {
40    type Err = ();
41
42    #[inline]
43    fn from_str(s: &str) -> Result<Tribool, ()> {
44        Ok(match bool::from_str(s) {
45            Ok(value) => Tribool::boolean(value),
46            _ => Indeterminate,
47        })
48    }
49}
50
51impl Tribool {
52    /// Construct a new non-indeterminate Tribool from a regular boolean value
53    #[inline]
54    pub const fn boolean(value: bool) -> Tribool {
55        match value {
56            true => True,
57            false => False,
58        }
59    }
60
61    /// Returns `true` only if `self` is `True`
62    ///
63    /// # Example
64    /// ```
65    /// # use tribool::Tribool;
66    /// # fn main() {
67    /// assert!(Tribool::True.is_true())
68    /// # }
69    /// ```
70    #[inline]
71    pub const fn is_true(self) -> bool {
72        match self {
73            True => true,
74            _ => false,
75        }
76    }
77
78    /// Returns `true` only if `self` is `False`
79    ///
80    /// # Example
81    /// ```
82    /// # use tribool::Tribool;
83    /// # fn main() {
84    /// assert!(Tribool::False.is_false())
85    /// # }
86    /// ```
87    #[inline]
88    pub const fn is_false(self) -> bool {
89        match self {
90            False => true,
91            _ => false,
92        }
93    }
94
95    /// Returns `true` only if `self` is `Indeterminate`
96    ///
97    /// # Example
98    /// ```
99    /// # use tribool::Tribool;
100    /// # fn main() {
101    /// assert!(Tribool::Indeterminate.is_indeterminate())
102    /// # }
103    /// ```
104    #[inline]
105    pub const fn is_indeterminate(self) -> bool {
106        match self {
107            Indeterminate => true,
108            _ => false,
109        }
110    }
111
112    /// Checks for equality of two `Tribool`s,
113    /// returning `Indeterminate` if either are indeterminate.
114    ///
115    /// # Example
116    /// ```
117    /// # use tribool::Tribool;
118    /// # fn main() {
119    /// assert!(Tribool::True.equals(Tribool::True).is_true());
120    /// assert!(Tribool::True.equals(Tribool::False).is_false());
121    /// assert!(Tribool::Indeterminate.equals(Tribool::False).is_indeterminate())
122    /// # }
123    /// ```
124    #[inline]
125    pub const fn equals(self, rhs: Tribool) -> Tribool {
126        match (self, rhs) {
127            (False, False) | (True, True) => True,
128            (False, True) | (True, False) => False,
129            _ => Indeterminate,
130        }
131    }
132
133    /// Checks for inequality of two `Tribool`s,
134    /// returning `Indeterminate` if either are indeterminate.
135    ///
136    /// # Example
137    /// ```
138    /// # use tribool::Tribool;
139    /// # fn main() {
140    /// assert!(Tribool::True.not_equals(Tribool::True).is_false());
141    /// assert!(Tribool::True.not_equals(Tribool::False).is_true());
142    /// assert!(Tribool::Indeterminate.not_equals(Tribool::False).is_indeterminate())
143    /// # }
144    /// ```
145    #[inline]
146    pub const fn not_equals(self, rhs: Tribool) -> Tribool {
147        match (self, rhs) {
148            (False, False) | (True, True) => False,
149            (False, True) | (True, False) => True,
150            _ => Indeterminate,
151        }
152    }
153
154    /// Material implication using Kleene Logic.
155    ///
156    /// This is equivalent to `NOT(A) OR B`.
157    #[inline]
158    pub const fn kleene_implication(self, b: Tribool) -> Tribool {
159        self.negate().or(b) // !self | b
160    }
161
162    /// Material implication using Łukasiewicz Logic
163    ///
164    /// The Łukasiewicz Ł3 has the same tables for AND, OR, and NOT as the Kleene logic used elsewhere,
165    /// but differs in its definition of implication in that "unknown implies unknown" is true.
166    ///
167    /// For more information, see [the Wikipedia page and the section on Łukasiewicz Logic](https://en.wikipedia.org/wiki/Three-valued_logic#.C5.81ukasiewicz_logic)
168    #[inline]
169    pub fn lukasiewicz_implication(self, b: Tribool) -> Tribool {
170        match (self, b) {
171            (True, Indeterminate) | (Indeterminate, False) => Indeterminate,
172            (True, False) => False,
173            (False, False) => True,
174            (_, True) | (_, Indeterminate) => True,
175        }
176    }
177
178    /// Logical conjunction
179    #[inline]
180    pub const fn and(self, rhs: Tribool) -> Tribool {
181        match (self, rhs) {
182            (True, True) => True,
183            (False, _) | (_, False) => False,
184            _ => Indeterminate,
185        }
186    }
187
188    /// Logical disjunction
189    #[inline]
190    pub const fn or(self, rhs: Tribool) -> Tribool {
191        match (self, rhs) {
192            (False, False) => False,
193            (True, _) | (_, True) => True,
194            _ => Indeterminate,
195        }
196    }
197
198    /// Logical exclusive disjunction
199    #[inline]
200    pub const fn xor(self, rhs: Tribool) -> Tribool {
201        // (self | rhs) & !(self & rhs)
202        self.or(rhs).and(self.and(rhs).negate())
203    }
204
205    /// Logical negation
206    #[inline]
207    pub const fn negate(self) -> Tribool {
208        match self {
209            True => False,
210            False => True,
211            _ => Indeterminate,
212        }
213    }
214
215    /// Compares two Tribools similarly to a `bool`, where `true` is greater than `false`
216    ///
217    /// Indeterminate values cannot be compared to order and therefore return `None`
218    #[inline]
219    pub const fn compare(self, rhs: Tribool) -> Option<Ordering> {
220        match (self, rhs) {
221            (Indeterminate, _) | (_, Indeterminate) => None,
222            (True, False) => Some(Ordering::Greater),
223            (False, True) => Some(Ordering::Less),
224            (True, True) | (False, False) => Some(Ordering::Equal),
225        }
226    }
227}
228
229impl Display for Tribool {
230    #[inline]
231    fn fmt(&self, f: &mut Formatter) -> FmtResult {
232        f.write_str(match self {
233            True => "True",
234            False => "False",
235            Indeterminate => "Indeterminate",
236        })
237    }
238}
239
240impl<B: Into<Tribool> + Copy> PartialEq<B> for Tribool {
241    #[inline]
242    fn eq(&self, rhs: &B) -> bool {
243        self.equals((*rhs).into()).is_true()
244    }
245
246    #[inline]
247    fn ne(&self, rhs: &B) -> bool {
248        self.not_equals((*rhs).into()).is_true()
249    }
250}
251
252impl PartialEq<Tribool> for bool {
253    #[inline]
254    fn eq(&self, rhs: &Tribool) -> bool {
255        *rhs == *self
256    }
257
258    #[inline]
259    fn ne(&self, rhs: &Tribool) -> bool {
260        *rhs != *self
261    }
262}
263
264use std::cmp::Ordering;
265
266impl<B: Into<Tribool> + Copy> PartialOrd<B> for Tribool {
267    #[inline]
268    fn partial_cmp(&self, rhs: &B) -> Option<Ordering> {
269        self.compare((*rhs).into())
270    }
271
272    #[inline]
273    fn lt(&self, rhs: &B) -> bool {
274        match (*self, (*rhs).into()) {
275            (False, True) => true,
276            _ => false,
277        }
278    }
279
280    #[inline]
281    fn le(&self, rhs: &B) -> bool {
282        match (*self, (*rhs).into()) {
283            (True, True) | (False, False) | (False, True) => true,
284            _ => false,
285        }
286    }
287
288    #[inline]
289    fn gt(&self, rhs: &B) -> bool {
290        match (*self, (*rhs).into()) {
291            (True, False) => true,
292            _ => false,
293        }
294    }
295
296    #[inline]
297    fn ge(&self, rhs: &B) -> bool {
298        match (*self, (*rhs).into()) {
299            (True, True) | (False, False) | (True, False) => true,
300            _ => false,
301        }
302    }
303}
304
305impl Not for Tribool {
306    type Output = Tribool;
307
308    #[inline]
309    fn not(self) -> Tribool {
310        self.negate()
311    }
312}
313
314impl<B: Into<Tribool>> BitAnd<B> for Tribool {
315    type Output = Tribool;
316
317    #[inline]
318    fn bitand(self, rhs: B) -> Tribool {
319        self.and(rhs.into())
320    }
321}
322
323impl<B: Into<Tribool>> BitOr<B> for Tribool {
324    type Output = Tribool;
325
326    #[inline]
327    fn bitor(self, rhs: B) -> Tribool {
328        self.or(rhs.into())
329    }
330}
331
332impl<B: Into<Tribool>> BitXor<B> for Tribool {
333    type Output = Tribool;
334
335    #[inline]
336    fn bitxor(self, rhs: B) -> Tribool {
337        self.xor(rhs.into())
338    }
339}
340
341macro_rules! impl_binary_op {
342    ($op:ident => $f:ident, $assign_op:ident => $af:ident) => {
343        impl $op<Tribool> for bool {
344            type Output = Tribool;
345
346            #[inline]
347            fn $f(self, rhs: Tribool) -> Tribool {
348                rhs.$f(self)
349            }
350        }
351
352        impl<B: Into<Tribool>> $assign_op<B> for Tribool {
353            #[inline]
354            fn $af(&mut self, rhs: B) {
355                *self = self.$f(rhs.into());
356            }
357        }
358
359        impl $assign_op<Tribool> for bool {
360            #[inline]
361            fn $af(&mut self, rhs: Tribool) {
362                *self = rhs.$f(*self).is_true()
363            }
364        }
365    };
366}
367
368impl_binary_op!(BitAnd => bitand, BitAndAssign => bitand_assign);
369impl_binary_op!(BitOr => bitor, BitOrAssign => bitor_assign);
370impl_binary_op!(BitXor => bitxor, BitXorAssign => bitxor_assign);
371
372impl From<bool> for Tribool {
373    #[inline]
374    fn from(value: bool) -> Tribool {
375        Tribool::boolean(value)
376    }
377}
378
379/// Error result of `bool::try_from(TriBool::Indeterminate)`
380#[derive(Debug, Clone, Copy)]
381pub struct IndeterminateError;
382
383impl TryFrom<Tribool> for bool {
384    type Error = IndeterminateError;
385
386    #[inline]
387    fn try_from(value: Tribool) -> Result<Self, Self::Error> {
388        match value {
389            True => Ok(true),
390            False => Ok(false),
391            Indeterminate => Err(IndeterminateError),
392        }
393    }
394}
395
396impl From<Result<Tribool, IndeterminateError>> for Tribool {
397    #[inline]
398    fn from(value: Result<Tribool, IndeterminateError>) -> Self {
399        match value {
400            Ok(t) => t,
401            Err(_) => Indeterminate,
402        }
403    }
404}
405
406// implements the unary operator "op &T"
407// based on "op T" where T is expected to be `Copy`able
408macro_rules! forward_ref_unop {
409    (impl $imp:ident, $method:ident for $t:ty) => {
410        impl<'a> $imp for &'a $t {
411            type Output = <$t as $imp>::Output;
412
413            #[inline]
414            fn $method(self) -> <$t as $imp>::Output {
415                $imp::$method(*self)
416            }
417        }
418    };
419}
420
421// implements binary operators "&T op U", "T op &U", "&T op &U"
422// based on "T op U" where T and U are expected to be `Copy`able
423macro_rules! forward_ref_binop {
424    (impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
425        impl<'a> $imp<$u> for &'a $t {
426            type Output = <$t as $imp<$u>>::Output;
427
428            #[inline]
429            fn $method(self, other: $u) -> <$t as $imp<$u>>::Output {
430                $imp::$method(*self, other)
431            }
432        }
433
434        impl<'a> $imp<&'a $u> for $t {
435            type Output = <$t as $imp<$u>>::Output;
436
437            #[inline]
438            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
439                $imp::$method(self, *other)
440            }
441        }
442
443        impl<'a, 'b> $imp<&'a $u> for &'b $t {
444            type Output = <$t as $imp<$u>>::Output;
445
446            #[inline]
447            fn $method(self, other: &'a $u) -> <$t as $imp<$u>>::Output {
448                $imp::$method(*self, *other)
449            }
450        }
451    };
452}
453
454forward_ref_unop!(impl Not, not for Tribool);
455
456forward_ref_binop!(impl BitAnd, bitand for Tribool, Tribool);
457forward_ref_binop!(impl BitOr, bitor for Tribool, Tribool);
458forward_ref_binop!(impl BitXor, bitxor for Tribool, Tribool);
459
460forward_ref_binop!(impl BitAnd, bitand for Tribool, bool);
461forward_ref_binop!(impl BitOr, bitor for Tribool, bool);
462forward_ref_binop!(impl BitXor, bitxor for Tribool, bool);
463
464forward_ref_binop!(impl BitAnd, bitand for bool, Tribool);
465forward_ref_binop!(impl BitOr, bitor for bool, Tribool);
466forward_ref_binop!(impl BitXor, bitxor for bool, Tribool);
467
468#[cfg(test)]
469mod test {
470    use super::*;
471
472    #[test]
473    fn equality() {
474        assert_eq!(True, True);
475        assert_ne!(True, False);
476        assert_ne!(False, True);
477        assert_eq!(False, False);
478
479        assert!(!(Indeterminate == True));
480        assert!(!(Indeterminate == False));
481        assert!(!(Indeterminate == Indeterminate));
482        assert!(!(Indeterminate != True));
483        assert!(!(Indeterminate != False));
484        assert!(!(Indeterminate != Indeterminate));
485    }
486
487    #[test]
488    fn bool_equality() {
489        assert_eq!(True, true);
490        assert_eq!(False, false);
491        assert_ne!(True, false);
492        assert_ne!(False, true);
493        assert!(!(Indeterminate != true));
494        assert!(!(Indeterminate != false));
495    }
496
497    #[test]
498    fn ordering() {
499        assert!(True > False);
500        assert!(True >= False);
501        assert!(False < True);
502        assert!(False <= True);
503        assert!(False <= False);
504        assert!(False >= False);
505        assert!(True <= True);
506        assert!(True >= True);
507
508        assert!(!(True > True));
509        assert!(!(False > True));
510        assert!(!(False > False));
511
512        assert!(!(Indeterminate < True));
513        assert!(!(Indeterminate < False));
514        assert!(!(Indeterminate <= True));
515        assert!(!(Indeterminate <= False));
516        assert!(!(Indeterminate > True));
517        assert!(!(Indeterminate > False));
518        assert!(!(Indeterminate >= True));
519        assert!(!(Indeterminate >= False));
520    }
521
522    #[test]
523    fn bool_ordering() {
524        assert!(True > false);
525        assert!(True >= false);
526        assert!(False < true);
527        assert!(False <= true);
528        assert!(False <= false);
529        assert!(False >= false);
530        assert!(True <= true);
531        assert!(True >= true);
532
533        assert!(!(True > true));
534        assert!(!(False > true));
535        assert!(!(False > false));
536
537        assert!(!(Indeterminate < true));
538        assert!(!(Indeterminate < false));
539        assert!(!(Indeterminate <= true));
540        assert!(!(Indeterminate <= false));
541        assert!(!(Indeterminate > true));
542        assert!(!(Indeterminate > false));
543        assert!(!(Indeterminate >= true));
544        assert!(!(Indeterminate >= false));
545    }
546
547    #[test]
548    fn and() {
549        assert!((False & False).is_false());
550        assert!((False & Indeterminate).is_false());
551        assert!((False & True).is_false());
552
553        assert!((Indeterminate & False).is_false());
554        assert!((Indeterminate & Indeterminate).is_indeterminate());
555        assert!((Indeterminate & True).is_indeterminate());
556
557        assert!((True & False).is_false());
558        assert!((True & Indeterminate).is_indeterminate());
559        assert!((True & True).is_true());
560    }
561
562    #[test]
563    fn or() {
564        assert!((False | False).is_false());
565        assert!((False | Indeterminate).is_indeterminate());
566        assert!((False | True).is_true());
567
568        assert!((Indeterminate | False).is_indeterminate());
569        assert!((Indeterminate | Indeterminate).is_indeterminate());
570        assert!((Indeterminate | True).is_true());
571
572        assert!((True | False).is_true());
573        assert!((True | Indeterminate).is_true());
574        assert!((True | True).is_true());
575    }
576
577    #[test]
578    fn xor() {
579        assert!((False ^ False).is_false());
580        assert!((False ^ True).is_true());
581        assert!((False ^ Indeterminate).is_indeterminate());
582
583        assert!((Indeterminate ^ False).is_indeterminate());
584        assert!((Indeterminate ^ Indeterminate).is_indeterminate());
585        assert!((Indeterminate ^ True).is_indeterminate());
586
587        assert!((True ^ False).is_true());
588        assert!((True ^ Indeterminate).is_indeterminate());
589        assert!((True ^ True).is_false());
590    }
591
592    #[test]
593    fn kleene() {
594        assert!(True.kleene_implication(True).is_true());
595        assert!(Indeterminate.kleene_implication(True).is_true());
596        assert!(False.kleene_implication(True).is_true());
597
598        assert!(True.kleene_implication(Indeterminate).is_indeterminate());
599        assert!(Indeterminate
600            .kleene_implication(Indeterminate)
601            .is_indeterminate());
602        assert!(False.kleene_implication(Indeterminate).is_true());
603
604        assert!(True.kleene_implication(False).is_false());
605        assert!(Indeterminate.kleene_implication(False).is_indeterminate());
606        assert!(False.kleene_implication(False).is_true());
607    }
608
609    #[test]
610    fn lukasiewicz() {
611        assert!(True.lukasiewicz_implication(True).is_true());
612        assert!(Indeterminate.lukasiewicz_implication(True).is_true());
613        assert!(False.lukasiewicz_implication(True).is_true());
614
615        assert!(True
616            .lukasiewicz_implication(Indeterminate)
617            .is_indeterminate());
618        assert!(Indeterminate
619            .lukasiewicz_implication(Indeterminate)
620            .is_true());
621        assert!(False.lukasiewicz_implication(Indeterminate).is_true());
622
623        assert!(True.lukasiewicz_implication(False).is_false());
624        assert!(Indeterminate
625            .lukasiewicz_implication(False)
626            .is_indeterminate());
627        assert!(False.lukasiewicz_implication(False).is_true());
628    }
629
630    #[cfg(feature = "serde")]
631    #[test]
632    fn serde() {
633        let res_false = serde_json::to_string_pretty(&Tribool::False)
634            .expect("serde Serialize impl for False failed");
635        let res_true = serde_json::to_string_pretty(&Tribool::True)
636            .expect("serde Serialize impl for True failed");
637        let res_none = serde_json::to_string_pretty(&Tribool::Indeterminate)
638            .expect("serde Serialize impl for Indeterminate failed");
639
640        assert!(serde_json::from_str::<Tribool>(&res_false)
641            .expect("serde Deserialize impl for False failed")
642            .is_false());
643        assert!(serde_json::from_str::<Tribool>(&res_true)
644            .expect("serde Deserialize impl for True failed")
645            .is_true());
646        assert!(serde_json::from_str::<Tribool>(&res_none)
647            .expect("serde Deserialize impl for Indeterminate failed")
648            .is_indeterminate())
649    }
650}