Ternary logic: Difference between revisions

From Rosetta Code
Content added Content deleted
Line 688: Line 688:


#define forall(a) for(a = t_F; a <= t_T; a++)
#define forall(a) for(a = t_F; a <= t_T; a++)
void show_op(trit (*f)(trit, trit), char *name) {
void show_op(trit (*f)(trit, trit), const char *name) {
trit a, b;
trit a, b;
printf("\n[%s]\n F ? T\n -------", name);
printf("\n[%s]\n F ? T\n -------", name);

Revision as of 08:54, 26 December 2012

Task
Ternary logic
You are encouraged to solve this task according to the task description, using any language you may know.
This page uses content from Wikipedia. The original article was at Ternary logic. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)

In logic, a three-valued logic (also trivalent, ternary, or trinary logic, sometimes abbreviated 3VL) is any of several many-valued logic systems in which there are three truth values indicating true, false and some indeterminate third value. This is contrasted with the more commonly known bivalent logics (such as classical sentential or boolean logic) which provide only for true and false. Conceptual form and basic ideas were initially created by Łukasiewicz, Lewis and Sulski. These were then re-formulated by Grigore Moisil in an axiomatic algebraic form, and also extended to n-valued logics in 1945.

Example Ternary Logic Operators in Truth Tables:
not a
¬
True False
Maybe Maybe
False True
a and b
True Maybe False
True True Maybe False
Maybe Maybe Maybe False
False False False False
a or b
True Maybe False
True True True True
Maybe True Maybe Maybe
False True Maybe False
if a then b
True Maybe False
True True Maybe False
Maybe True Maybe Maybe
False True True True
a is equivalent to b
True Maybe False
True True Maybe False
Maybe Maybe Maybe Maybe
False False Maybe True

Task:

  • Define a new type that emulates ternary logic by storing data trits.
  • Given all the binary logic operators of the original programming language, reimplement these operators for the new Ternary logic type trit.
  • Generate a sampling of results using trit variables.
  • Kudos for actually thinking up a test case algorithm where ternary logic is intrinsically useful, optimises the test case algorithm and is preferable to binary logic.

Note: Setun (Сетунь) was a balanced ternary computer developed in 1958 at Moscow State University. The device was built under the lead of Sergei Sobolev and Nikolay Brusentsov. It was the only modern ternary computer, using three-valued ternary logic

Ada

We first specify a package "Logic" for three-valued logic. Observe that predefined Boolean functions, "and" "or" and "not" are overloaded: <lang Ada>package Logic is

  type Ternary is (True, Unknown, False); 
  -- logic functions
  function "and"(Left, Right: Ternary) return Ternary;
  function "or"(Left, Right: Ternary) return Ternary;
  function "not"(T: Ternary) return Ternary;
  function Equivalent(Left, Right: Ternary) return Ternary;
  function Implies(Condition, Conclusion: Ternary) return Ternary;
  -- conversion functions
  function To_Bool(X: Ternary) return Boolean;
  function To_Ternary(B: Boolean) return Ternary;
  function Image(Value: Ternary) return Character;

end Logic;</lang>

Next, the implementation of the package:

<lang Ada>package body Logic is

  -- type Ternary is (True, Unknown, False);
  function Image(Value: Ternary) return Character is
  begin
     case Value is
        when True    => return 'T';
        when False   => return 'F';
        when Unknown => return '?';
     end case;
  end Image;
  function "and"(Left, Right: Ternary) return Ternary is
  begin
     return Ternary'max(Left, Right);
  end "and";
  function "or"(Left, Right: Ternary) return Ternary is
  begin
     return Ternary'min(Left, Right);
  end "or";
  function "not"(T: Ternary) return Ternary is
  begin
     case T is
        when False   => return True;
        when Unknown => return Unknown;
        when True    => return False;
     end case;
  end "not";
  function To_Bool(X: Ternary) return Boolean is
  begin
     case X is
        when True  => return True;
        when False => return False;
        when Unknown => raise Constraint_Error;
     end case;
  end To_Bool;
  function To_Ternary(B: Boolean) return Ternary is
  begin
     if B then
        return True;
     else
        return False;
     end if;
  end To_Ternary;
  function Equivalent(Left, Right: Ternary) return Ternary is
  begin
     return To_Ternary(To_Bool(Left) = To_Bool(Right));
  exception
     when Constraint_Error => return Unknown;
  end Equivalent;
  function Implies(Condition, Conclusion: Ternary) return Ternary is
  begin
     return (not Condition) or Conclusion;
  end Implies;

end Logic;</lang>

Finally, a sample program: <lang Ada>with Ada.Text_IO, Logic;

procedure Test_Tri_Logic is

  use Logic;
  type F2 is access function(Left, Right: Ternary) return Ternary;
  type F1 is access function(Trit: Ternary) return Ternary;
  procedure Truth_Table(F: F1; Name: String) is
  begin
     Ada.Text_IO.Put_Line("X | " & Name & "(X)");
     for T in Ternary loop
        Ada.Text_IO.Put_Line(Image(T) & " |  " & Image(F(T)));
     end loop;
  end Truth_Table;
  procedure Truth_Table(F: F2; Name: String) is
  begin
     Ada.Text_IO.New_Line;
     Ada.Text_IO.Put_Line("X | Y | " & Name & "(X,Y)");
     for X in Ternary loop
        for Y in Ternary loop
           Ada.Text_IO.Put_Line(Image(X) & " | " & Image(Y) & " |  " & Image(F(X,Y)));
        end loop;
     end loop;
  end Truth_Table;

begin

  Truth_Table(F => "not"'Access, Name => "Not");
  Truth_Table(F => "and"'Access, Name => "And");
  Truth_Table(F => "or"'Access, Name => "Or");
  Truth_Table(F => Equivalent'Access, Name => "Eq");
  Truth_Table(F => Implies'Access, Name => "Implies");

end Test_Tri_Logic;</lang>

The output:

X | Not(X)
T |  F
? |  ?
F |  T

X | Y | And(X,Y)
T | T |  T
T | ? |  ?
T | F |  F
? | T |  ?
? | ? |  ?
? | F |  F
F | T |  F
F | ? |  F
F | F |  F

... (and so on)

ALGOL 68

Works with: ALGOL 68 version Revision 1 - one minor extension to language used - PRAGMA READ, like C's #include directive.
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny.

File: Ternary_logic.a68 <lang algol68># -*- coding: utf-8 -*- #

INT trit width = 1, trit base = 3; MODE TRIT = STRUCT(BITS trit); CO FORMAT trit fmt = $c("?","⌈","⌊",#|"~"#)$; CO

  1. These values treated are as per "Balanced ternary" #
  2. eg true=1, maybe=0, false=-1 #

TRIT true =INITTRIT 4r1, maybe=INITTRIT 4r0,

    false=INITTRIT 4r2;
  1. Warning: redefines standard builtins flip & flop #

LONGCHAR flap="?", flip="⌈", flop="⌊";

OP REPR = (TRIT t)LONGCHAR:

 []LONGCHAR(flap, flip, flop)[1+ABS trit OF t];
  1. Define some OPerators for coercing MODES #

OP INITTRIT = (BOOL in)TRIT:

 (in|true|false);

OP INITBOOL = (TRIT in)BOOL:

 (trit OF in=trit OF true|TRUE|:trit OF in=trit OF false|FALSE|
   raise value error(("vague TRIT to BOOL coercion: """, REPR in,""""));~
 );

OP B = (TRIT in)BOOL: INITBOOL in;

  1. These values treated are as per "Balanced ternary" #
  2. n.b true=1, maybe=0, false=-1 #
  3. Warning: BOOL ABS FALSE (0) is not the same as TRIT ABS false (-1) #

OP INITINT = (TRIT t)INT:

 CASE 1+ABS trit OF t
 IN #maybe# 0, #true # 1, #false#-1
 OUT raise value error(("invalid TRIT value",REPR t)); ~
 ESAC;

OP INITTRIT = (INT in)TRIT: (

 TRIT out;
 trit OF out:= trit OF
   CASE 2+in
   IN false, maybe, true
   OUT raise value error(("invalid TRIT value",in)); ~
   ESAC;
 out

);

OP INITTRIT = (BITS b)TRIT:

 (TRIT out; trit OF out:=b; out);
  1. Define the LOGICAL OPerators for the TRIT MODE #

MODE LOGICAL = TRIT; PR READ "Template_operators_logical_mixin.a68" PR

COMMENT

 Kleene logic truth tables:

END COMMENT

OP AND = (TRIT a,b)TRIT: (

 [,]TRIT(
   # ∧  ##  false, maybe, true  #
   #false# (false, false, false),
   #maybe# (false, maybe, maybe),
   #true # (false, maybe, true )
 )[@-1,@-1][INITINT a, INITINT b]

);

OP OR = (TRIT a,b)TRIT: (

 [,]TRIT(
   # ∨  ##  false, maybe, true #
   #false# (false, maybe, true),
   #maybe# (maybe, maybe, true),
   #true # (true,  true,  true)
 )[@-1,@-1][INITINT a, INITINT b]

);

PRIO IMPLIES = 1; # PRIO = 1.9 # OP IMPLIES = (TRIT a,b)TRIT: (

 [,]TRIT(
   # ⊃   ## false, maybe, true #
   #false# (true,  true,  true),
   #maybe# (maybe, maybe, true),
   #true # (false, maybe, true)
 )[@-1,@-1][INITINT a, INITINT b]

);

PRIO EQV = 1; # PRIO = 1.8 # OP EQV = (TRIT a,b)TRIT: (

 [,]TRIT(
   # ≡   ## false, maybe, true #
   #false# (true,  maybe, false),
   #maybe# (maybe, maybe, maybe),
   #true # (false, maybe, true )
 )[@-1,@-1][INITINT a, INITINT b]

);</lang>File: Template_operators_logical_mixin.a68 <lang algol68># -*- coding: utf-8 -*- #

OP & = (LOGICAL a,b)LOGICAL: a AND b; CO # not included as they are treated as SCALAR # OP EQ = (LOGICAL a,b)LOGICAL: a = b,

  NE = (LOGICAL a,b)LOGICAL: a /= b,
  ≠ = (TRIT a,b)TRIT: a /= b,
  ¬= = (TRIT a,b)TRIT: a /= b;

END CO

  1. IF html entities possible THEN

¢ "parked" operators for completeness ¢ OP ¬ = (LOGICAL a)LOGICAL: NOT a,

  ∧  = (LOGICAL a,b)LOGICAL: a AND b,
  /\ = (LOGICAL a,b)LOGICAL: a AND b,
  ∨  = (LOGICAL a,b)LOGICAL: a OR b,
  \/ = (LOGICAL a,b)LOGICAL: a OR b,
  ⊃ = (TRIT a,b)TRIT: a IMPLIES b,
  ≡ = (TRIT a,b)TRIT: a EQV b;

FI#

  1. IF algol68c THEN

OP ~ = (LOGICAL a)LOGICAL: NOT a,

  ~= = (LOGICAL a,b)LOGICAL: a /= b; SCALAR!

FI#</lang>File: test_Ternary_logic.a68 <lang algol68>#!/usr/local/bin/a68g --script #

  1. -*- coding: utf-8 -*- #

PR READ "prelude/general.a68" PR PR READ "Ternary_logic.a68" PR

[]TRIT trits = (false, maybe, true);

FORMAT

 col fmt = $" "g" "$,
 row fmt = $l3(f(col fmt)"|")f(col fmt)$,
 row sep fmt = $l3("---+")"---"l$;

PROC row sep = VOID:

 printf(row sep fmt);

PROC title = (STRING op name, LONGCHAR op char)VOID:(

 print(("Operator: ",op name));
 printf((row fmt,op char,REPR false, REPR maybe, REPR true))

);

PROC print trit op table = (LONGCHAR op char, STRING op name, PROC(TRIT,TRIT)TRIT op)VOID: (

 printf($l$);
 title(op name, op char);
 FOR i FROM LWB trits TO UPB trits DO
   row sep;
   TRIT ti = trits[i];
   printf((col fmt, REPR ti));
   FOR j FROM LWB trits TO UPB trits DO
     TRIT tj = trits[j];
     printf(($"|"$, col fmt, REPR op(ti,tj)))
   OD
 OD;
 printf($l$)

);

printf((

 $"Comparitive table of coercions:"l$,
 $"  TRIT BOOL         INT"l$

));

FOR it FROM LWB trits TO UPB trits DO

 TRIT t = trits[it];
 printf(( $"  "g"  "$, REPR t, 
   IF trit OF t = trit OF maybe THEN " " ELSE B t FI,
   INITINT t, $l$))

OD;

printf((

 $l"Specific test of the IMPLIES operator:"l$,
 $"  "g" implies "g" is "b("not ","")"a contradiction!"l$,
   B false,    B false,    B(false IMPLIES false),
   B false,    B true,     B(false IMPLIES true),
   B false,    REPR maybe, B(false IMPLIES maybe),
   B true,     B false,    B(true  IMPLIES false),
   B true,     B true,     B(true  IMPLIES true),
   REPR maybe, Btrue,      B(maybe IMPLIES true),
 $"  "g" implies "g" is "g" a contradiction!"l$,
   B true,     REPR maybe, REPR (true  IMPLIES maybe),
   REPR maybe, B false,    REPR (maybe IMPLIES false),
   REPR maybe, REPR maybe, REPR (maybe IMPLIES maybe),
 $l$

));

printf($"Kleene logic truth table samples:"l$);

print trit op table("≡","EQV", (TRIT a,b)TRIT: a EQV b); print trit op table("⊃","IMPLIES", (TRIT a,b)TRIT: a IMPLIES b); print trit op table("∧","AND", (TRIT a,b)TRIT: a AND b); print trit op table("∨","OR", (TRIT a,b)TRIT: a OR b)</lang> Output:

Comparitive table of coercions:
  TRIT BOOL         INT
  ⌊    F             -1  
  ?                  +0  
  ⌈    T             +1  

Specific test of the IMPLIES operator:
  F implies F is not a contradiction!
  F implies T is not a contradiction!
  F implies ? is not a contradiction!
  T implies F is a contradiction!
  T implies T is not a contradiction!
  ? implies T is not a contradiction!
  T implies ? is ? a contradiction!
  ? implies F is ? a contradiction!
  ? implies ? is ? a contradiction!

Kleene logic truth table samples:

Operator: EQV
 ≡ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌈ | ? | ⌊ 
---+---+---+---
 ? | ? | ? | ? 
---+---+---+---
 ⌈ | ⌊ | ? | ⌈ 

Operator: IMPLIES
 ⊃ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌈ | ⌈ | ⌈ 
---+---+---+---
 ? | ? | ? | ⌈ 
---+---+---+---
 ⌈ | ⌊ | ? | ⌈ 

Operator: AND
 ∧ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌊ | ⌊ | ⌊ 
---+---+---+---
 ? | ⌊ | ? | ? 
---+---+---+---
 ⌈ | ⌊ | ? | ⌈ 

Operator: OR
 ∨ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌊ | ? | ⌈ 
---+---+---+---
 ? | ? | ? | ⌈ 
---+---+---+---
 ⌈ | ⌈ | ⌈ | ⌈ 

BBC BASIC

<lang bbcbasic> INSTALL @lib$ + "CLASSLIB"

     REM Create a ternary class:
     DIM trit{tor, tand, teqv, tnot, tnor, s, v}
     DEF PRIVATE trit.s (t&) LOCAL t$():DIM t$(2):t$()="FALSE","MAYBE","TRUE":=t$(t&)
     DEF PRIVATE trit.v (t$) = INSTR("FALSE MAYBE TRUE", t$) DIV 6
     DEF trit.tnot (t$) = FN(trit.s)(2 - FN(trit.v)(t$))
     DEF trit.tor (a$,b$) LOCAL t:t=FN(trit.v)(a$)ORFN(trit.v)(b$):=FN(trit.s)(t+(t>2))
     DEF trit.tnor (a$,b$) = FN(trit.tnot)(FN(trit.tor)(a$,b$))
     DEF trit.tand (a$,b$) = FN(trit.tnor)(FN(trit.tnot)(a$),FN(trit.tnot)(b$))
     DEF trit.teqv (a$,b$) = FN(trit.tor)(FN(trit.tand)(a$,b$),FN(trit.tnor)(a$,b$))
     PROC_class(trit{})
     
     PROC_new(mytrit{}, trit{})
     
     REM Test it:
     PRINT "Testing NOT:"
     PRINT "NOT FALSE = " FN(mytrit.tnot)("FALSE")
     PRINT "NOT MAYBE = " FN(mytrit.tnot)("MAYBE")
     PRINT "NOT TRUE  = " FN(mytrit.tnot)("TRUE")
     
     PRINT '"Testing OR:"
     PRINT "FALSE OR FALSE = " FN(mytrit.tor)("FALSE","FALSE")
     PRINT "FALSE OR MAYBE = " FN(mytrit.tor)("FALSE","MAYBE")
     PRINT "FALSE OR TRUE  = " FN(mytrit.tor)("FALSE","TRUE")
     PRINT "MAYBE OR MAYBE = " FN(mytrit.tor)("MAYBE","MAYBE")
     PRINT "MAYBE OR TRUE  = " FN(mytrit.tor)("MAYBE","TRUE")
     PRINT "TRUE  OR TRUE  = " FN(mytrit.tor)("TRUE","TRUE")
     
     PRINT '"Testing AND:"
     PRINT "FALSE AND FALSE = " FN(mytrit.tand)("FALSE","FALSE")
     PRINT "FALSE AND MAYBE = " FN(mytrit.tand)("FALSE","MAYBE")
     PRINT "FALSE AND TRUE  = " FN(mytrit.tand)("FALSE","TRUE")
     PRINT "MAYBE AND MAYBE = " FN(mytrit.tand)("MAYBE","MAYBE")
     PRINT "MAYBE AND TRUE  = " FN(mytrit.tand)("MAYBE","TRUE")
     PRINT "TRUE  AND TRUE  = " FN(mytrit.tand)("TRUE","TRUE")
     
     PRINT '"Testing EQV (similar to EOR):"
     PRINT "FALSE EQV FALSE = " FN(mytrit.teqv)("FALSE","FALSE")
     PRINT "FALSE EQV MAYBE = " FN(mytrit.teqv)("FALSE","MAYBE")
     PRINT "FALSE EQV TRUE  = " FN(mytrit.teqv)("FALSE","TRUE")
     PRINT "MAYBE EQV MAYBE = " FN(mytrit.teqv)("MAYBE","MAYBE")
     PRINT "MAYBE EQV TRUE  = " FN(mytrit.teqv)("MAYBE","TRUE")
     PRINT "TRUE  EQV TRUE  = " FN(mytrit.teqv)("TRUE","TRUE")
     
     PROC_discard(mytrit{})</lang>

Output:

Testing NOT:
NOT FALSE = TRUE
NOT MAYBE = MAYBE
NOT TRUE  = FALSE

Testing OR:
FALSE OR FALSE = FALSE
FALSE OR MAYBE = MAYBE
FALSE OR TRUE  = TRUE
MAYBE OR MAYBE = MAYBE
MAYBE OR TRUE  = TRUE
TRUE  OR TRUE  = TRUE

Testing AND:
FALSE AND FALSE = FALSE
FALSE AND MAYBE = FALSE
FALSE AND TRUE  = FALSE
MAYBE AND MAYBE = MAYBE
MAYBE AND TRUE  = MAYBE
TRUE  AND TRUE  = TRUE

Testing EQV (similar to EOR):
FALSE EQV FALSE = TRUE
FALSE EQV MAYBE = MAYBE
FALSE EQV TRUE  = FALSE
MAYBE EQV MAYBE = MAYBE
MAYBE EQV TRUE  = MAYBE
TRUE  EQV TRUE  = TRUE

C

Implementing logic using lookup tables

<lang c>#include <stdio.h>

typedef enum {

 TRITTRUE,  /* In this enum, equivalent to integer value 0 */
 TRITMAYBE, /* In this enum, equivalent to integer value 1 */
 TRITFALSE  /* In this enum, equivalent to integer value 2 */

} trit;

/* We can trivially find the result of the operation by passing

  the trinary values as indeces into the lookup tables' arrays. */

trit tritNot[3] = {TRITFALSE , TRITMAYBE, TRITTRUE}; trit tritAnd[3][3] = { {TRITTRUE, TRITMAYBE, TRITFALSE},

                      {TRITMAYBE, TRITMAYBE, TRITFALSE},
                      {TRITFALSE, TRITFALSE, TRITFALSE} };

trit tritOr[3][3] = { {TRITTRUE, TRITTRUE, TRITTRUE},

                     {TRITTRUE, TRITMAYBE, TRITMAYBE},
                     {TRITTRUE, TRITMAYBE, TRITFALSE} };

trit tritThen[3][3] = { { TRITTRUE, TRITMAYBE, TRITFALSE},

                       { TRITTRUE, TRITMAYBE, TRITMAYBE},
                       { TRITTRUE, TRITTRUE, TRITTRUE } };

trit tritEquiv[3][3] = { { TRITTRUE, TRITMAYBE, TRITFALSE},

                        { TRITMAYBE, TRITMAYBE, TRITMAYBE},
                        { TRITFALSE, TRITMAYBE, TRITTRUE } };

/* Everything beyond here is just demonstration */

const char* tritString[3] = {"T", "?", "F"};

void demo_binary_op(trit operator[3][3], const char* name) {

 trit operand1 = TRITTRUE; /* Declare. Initialize for CYA */
 trit operand2 = TRITTRUE; /* Declare. Initialize for CYA */
 /* Blank line */
 printf("\n");
 /* Demo this operator */
 for( operand1 = TRITTRUE; operand1 <= TRITFALSE; ++operand1 )
 {
   for( operand2 = TRITTRUE; operand2 <= TRITFALSE; ++operand2 )
   {
     printf("%s %s %s: %s\n", tritString[operand1],
                              name,
                              tritString[operand2],
                              tritString[operator[operand1][operand2]]);
   }
 }

}

int main() {

 trit op1 = TRITTRUE; /* Declare. Initialize for CYA */
 trit op2 = TRITTRUE; /* Declare. Initialize for CYA */

 /* Demo 'not' */
 for( op1 = TRITTRUE; op1 <= TRITFALSE; ++op1 )
 {
   printf("Not %s: %s\n", tritString[op1], tritString[tritNot[op1]]);
 }
 demo_binary_op(tritAnd, "And");
 demo_binary_op(tritOr, "Or");
 demo_binary_op(tritThen, "Then");
 demo_binary_op(tritEquiv, "Equiv");


 return 0;

}</lang>

Output: <lang text>Not T: F Not ?: ? Not F: T

T And T: T T And ?: ? T And F: F ? And T: ? ? And ?: ? ? And F: F F And T: F F And ?: F F And F: F

T Or T: T T Or ?: T T Or F: T ? Or T: T ? Or ?: ? ? Or F: ? F Or T: T F Or ?: ? F Or F: F

T Then T: T T Then ?: ? T Then F: F ? Then T: T ? Then ?: ? ? Then F: ? F Then T: T F Then ?: T F Then F: T

T Equiv T: T T Equiv ?: ? T Equiv F: F ? Equiv T: ? ? Equiv ?: ? ? Equiv F: ? F Equiv T: F F Equiv ?: ? F Equiv F: T</lang>

Using functions

<lang c>#include <stdio.h>

typedef enum { t_F = -1, t_M, t_T } trit;

trit t_not (trit a) { return -a; } trit t_and (trit a, trit b) { return a < b ? a : b; } trit t_or (trit a, trit b) { return a > b ? a : b; } trit t_eq (trit a, trit b) { return a * b; } trit t_imply(trit a, trit b) { return -a > b ? -a : b; } char t_s(trit a) { return "F?T"[a + 1]; }

  1. define forall(a) for(a = t_F; a <= t_T; a++)

void show_op(trit (*f)(trit, trit), const char *name) { trit a, b; printf("\n[%s]\n F ? T\n -------", name); forall(a) { printf("\n%c |", t_s(a)); forall(b) printf(" %c", t_s(f(a, b))); } puts(""); }

int main(void) { trit a;

puts("[Not]"); forall(a) printf("%c | %c\n", t_s(a), t_s(t_not(a)));

show_op(t_and, "And"); show_op(t_or, "Or"); show_op(t_eq, "Equiv"); show_op(t_imply, "Imply");

return 0; }</lang>output<lang>[Not] F | T ? | ? T | F

[And]

   F ? T
 -------

F | F F F ? | F ? ? T | F ? T

[Or]

   F ? T
 -------

F | F ? T ? | ? ? T T | T T T

[Equiv]

   F ? T
 -------

F | T ? F ? | ? ? ? T | F ? T

[Imply]

   F ? T
 -------

F | T T T ? | ? ? T T | F ? T</lang>

Variable truthfulness

Represent each possible truth value as a floating point value x, where the var has x chance of being true and 1 - x chance of being false. When using if3 conditional on a potential truth varible, the result is randomly sampled to true or false according to the chance. (This description is definitely very confusing perhaps). <lang c>#include <stdio.h>

  1. include <stdlib.h>

typedef double half_truth, maybe;

inline maybe not3(maybe a) { return 1 - a; }

inline maybe and3(maybe a, maybe b) { return a * b; }

inline maybe or3(maybe a, maybe b) { return a + b - a * b; }

inline maybe eq3(maybe a, maybe b) { return 1 - a - b + 2 * a * b; }

inline maybe imply3(maybe a, maybe b) { return or3(not3(a), b); }

  1. define true3(x) ((x) * RAND_MAX > rand())
  2. define if3(x) if (true3(x))

int main() { maybe roses_are_red = 0.25; /* they can be white or black, too */ maybe violets_are_blue = 1; /* aren't they just */ int i;

puts("Verifying flowery truth for 40 times:\n");

puts("Rose is NOT red:"); /* chance: .75 */ for (i = 0; i < 40 || !puts("\n"); i++) printf( true3( not3(roses_are_red) ) ? "T" : "_");

/* pick a rose and a violet; */ puts("Rose is red AND violet is blue:"); /* chance of rose being red AND violet being blue is .25 */ for (i = 0; i < 40 || !puts("\n"); i++) printf( true3( and3(roses_are_red, violets_are_blue) ) ? "T" : "_");

/* chance of rose being red OR violet being blue is 1 */ puts("Rose is red OR violet is blue:"); for (i = 0; i < 40 || !puts("\n"); i++) printf( true3( or3(roses_are_red, violets_are_blue) ) ? "T" : "_");

/* pick two roses; chance of em being both red or both not red is .625 */ puts("This rose is as red as that rose:"); for (i = 0; i < 40 || !puts("\n"); i++) if3(eq3(roses_are_red, roses_are_red)) putchar('T'); else putchar('_');

return 0; }</lang>

C#

<lang csharp>using System;

/// <summary> /// Extension methods on nullable bool. /// </summary> /// <remarks> /// The operators !, & and | are predefined. /// </remarks> public static class NullableBoolExtension {

   public static bool? Implies(this bool? left, bool? right)
   {
       return !left | right;
   }
   public static bool? IsEquivalentTo(this bool? left, bool? right)
   {
       return left.HasValue && right.HasValue ? left == right : default(bool?);
   }
   public static string Format(this bool? value)
   {
       return value.HasValue ? value.Value.ToString() : "Maybe";
   }

}

public class Program {

   private static void Main()
   {
       var values = new[] { true, default(bool?), false };
       foreach (var left in values)
       {
           Console.WriteLine("¬{0} = {1}", left.Format(), (!left).Format());
           foreach (var right in values)
           {
               Console.WriteLine("{0} & {1} = {2}", left.Format(), right.Format(), (left & right).Format());
               Console.WriteLine("{0} | {1} = {2}", left.Format(), right.Format(), (left | right).Format());
               Console.WriteLine("{0} → {1} = {2}", left.Format(), right.Format(), left.Implies(right).Format());
               Console.WriteLine("{0} ≡ {1} = {2}", left.Format(), right.Format(), left.IsEquivalentTo(right).Format());
           }
       }
   }

}</lang> Output:

¬True = False
True & True = True
True | True = True
True → True = True
True ≡ True = True
True & Maybe = Maybe
True | Maybe = True
True → Maybe = Maybe
True ≡ Maybe = Maybe
True & False = False
True | False = True
True → False = False
True ≡ False = False
¬Maybe = Maybe
Maybe & True = Maybe
Maybe | True = True
Maybe → True = True
Maybe ≡ True = Maybe
Maybe & Maybe = Maybe
Maybe | Maybe = Maybe
Maybe → Maybe = Maybe
Maybe ≡ Maybe = Maybe
Maybe & False = False
Maybe | False = Maybe
Maybe → False = Maybe
Maybe ≡ False = Maybe
¬False = True
False & True = False
False | True = True
False → True = True
False ≡ True = False
False & Maybe = False
False | Maybe = Maybe
False → Maybe = True
False ≡ Maybe = Maybe
False & False = False
False | False = False
False → False = True
False ≡ False = True

Common Lisp

<lang lisp>(defun tri-not (x) (- 1 x)) (defun tri-and (&rest x) (apply #'* x)) (defun tri-or (&rest x) (tri-not (apply #'* (mapcar #'tri-not x)))) (defun tri-eq (x y) (+ (tri-and x y) (tri-and (- 1 x) (- 1 y)))) (defun tri-imply (x y) (tri-or (tri-not x) y))

(defun tri-test (x) (< (random 1e0) x)) (defun tri-string (x) (if (= x 1) "T" (if (= x 0) "F" "?")))

to say (tri-if (condition) (yes) (no))

(defmacro tri-if (tri ifcase &optional elsecase)

 `(if (tri-test ,tri) ,ifcase ,elsecase))

(defun print-table (func header)

 (let ((vals '(1 .5 0)))
   (format t "~%~a:~%" header)
   (format t "    ~{~a ~^~}~%---------~%" (mapcar #'tri-string vals))
   (loop for row in vals do

(format t "~a | " (tri-string row)) (loop for col in vals do (format t "~a " (tri-string (funcall func row col)))) (write-line ""))))

(write-line "NOT:") (loop for row in '(1 .5 0) do

     (format t "~a | ~a~%" (tri-string row) (tri-string (tri-not row))))

(print-table #'tri-and "AND") (print-table #'tri-or "OR") (print-table #'tri-imply "IMPLY") (print-table #'tri-eq "EQUAL")</lang>output<lang>NOT: T | F ? | ? F | T

AND:

   T ? F 

T | T ? F ? | ? ? F F | F F F

OR:

   T ? F 

T | T T T ? | T ? ? F | T ? F

IMPLY:

   T ? F 

T | T ? F ? | T ? ? F | T T T

EQUAL:

   T ? F 

T | T ? F ? | ? ? ? F | F ? T</lang>

D

Partial translation of a C entry: <lang d>import std.stdio;

struct Trit {

   private enum Val : byte { F = -1, M, T }
   private Val t;
   alias t this;
   static immutable Trit[3] vals = [{Val.F}, {Val.M}, {Val.T}];
   static immutable F = Trit(Val.F); // Not necessary but handy.
   static immutable M = Trit(Val.M);
   static immutable T = Trit(Val.T);
   string toString() const pure nothrow {
       return "F?T"[t + 1  .. t + 2];
   }
   Trit opUnary(string op)() const pure nothrow
   if (op == "~") {
       return Trit(-t);
   }
   Trit opBinary(string op)(in Trit b) const pure nothrow
   if (op == "&") {
       return t < b ? this : b;
   }
   Trit opBinary(string op)(in Trit b) const pure nothrow
   if (op == "|") {
       return t > b ? this : b;
   }
   Trit opBinary(string op)(in Trit b) const pure nothrow
   if (op == "^") {
       return ~(this == b);
   }
   Trit opEquals(in Trit b) const pure nothrow {
       return Trit(cast(Val)(t * b));
   }
   Trit imply(in Trit b) const pure nothrow {
       return -t > b ? ~this : b;
   }

}

void showOperation(string op)(in string opName) {

   writef("\n[%s]\n    F ? T\n  -------", opName);
   foreach (immutable a; Trit.vals) {
       writef("\n%s |", a);
       foreach (immutable b; Trit.vals)
           static if (op == "==>")
               writef(" %s", a.imply(b));
           else
               writef(" %s", mixin("a " ~ op ~ " b"));
   }
   writeln();

}

void main() {

   writeln("[Not]");
   foreach (const a; Trit.vals)
       writefln("%s | %s", a, ~a);
   showOperation!"&"("And");
   showOperation!"|"("Or");
   showOperation!"^"("Xor");
   showOperation!"=="("Equiv");
   showOperation!"==>"("Imply");

}</lang>

Output:
[Not]
F | T
? | ?
T | F

[And]
    F ? T
  -------
F | F F F
? | F ? ?
T | F ? T

[Or]
    F ? T
  -------
F | F ? T
? | ? ? T
T | T T T

[Xor]
    F ? T
  -------
F | F ? T
? | ? ? ?
T | T ? F

[Equiv]
    F ? T
  -------
F | T ? F
? | ? ? ?
T | F ? T

[Imply]
    F ? T
  -------
F | T T T
? | ? ? T
T | F ? T

Delphi

<lang delphi>unit TrinaryLogic;

interface

//Define our own type for ternary logic. //This is actually still a Boolean, but the compiler will use distinct RTTI information. type

   TriBool = type Boolean;

const

   TTrue:TriBool = True;
   TFalse:TriBool = False;
   TMaybe:TriBool = TriBool(2);

function TVL_not(Value: TriBool): TriBool; function TVL_and(A, B: TriBool): TriBool; function TVL_or(A, B: TriBool): TriBool; function TVL_xor(A, B: TriBool): TriBool; function TVL_eq(A, B: TriBool): TriBool;

implementation

Uses

   SysUtils;

function TVL_not(Value: TriBool): TriBool; begin

   if Value = True Then
       Result := TFalse
   else If Value = False Then
       Result := TTrue
   else
       Result := Value;

end;

function TVL_and(A, B: TriBool): TriBool; begin

   Result := TriBool(Iff(Integer(A * B) > 1, Integer(TMaybe), A * B));

end;

function TVL_or(A, B: TriBool): TriBool; begin

   Result := TVL_not(TVL_and(TVL_not(A), TVL_not(B)));

end;

function TVL_xor(A, B: TriBool): TriBool; begin

   Result := TVL_and(TVL_or(A, B), TVL_not(TVL_or(A, B)));

end;

function TVL_eq(A, B: TriBool): TriBool; begin

   Result := TVL_not(TVL_xor(A, B));

end;

end.</lang>

And that's the reason why you never on no account ever should compare against the values of True or False unless you intent ternary logic!

An alternative version would be using an enum type <lang delphi>type TriBool = (tbFalse, tbMaybe, tbTrue);</lang> and defining a set of constants implementing the above tables: <lang delphi>const

   tvl_not: array[TriBool] = (tbTrue, tbMaybe, tbFalse);
   tvl_and: array[TriBool, TriBool] = (
       (tbFalse, tbFalse, tbFalse),
       (tbFalse, tbMaybe, tbMaybe),
       (tbFalse, tbMaybe, tbTrue),
       );
   tvl_or: array[TriBool, TriBool] = (
       (tbFalse, tbMaybe, tbTrue),
       (tbMaybe, tbMaybe, tbTrue),
       (tbTrue, tbTrue, tbTrue),
       );
   tvl_xor: array[TriBool, TriBool] = (
       (tbFalse, tbMaybe, tbTrue),
       (tbMaybe, tbMaybe, tbMaybe),
       (tbTrue, tbMaybe, tbFalse),
       );
   tvl_eq: array[TriBool, TriBool] = (
       (tbTrue, tbMaybe, tbFalse),
       (tbMaybe, tbMaybe, tbMaybe),
       (tbFalse, tbMaybe, tbTrue),
       );

</lang>

That's no real fun, but lookup can then be done with <lang delphi>Result := tvl_and[A, B];</lang>

Factor

For boolean logic, Factor uses t and f with the words >boolean, not, and, or, xor. For ternary logic, we add m and define the words >trit, tnot, tand, tor, txor and t=. Our new class, trit, is the union class of t, m and f.

<lang factor>! rosettacode/ternary/ternary.factor ! http://rosettacode.org/wiki/Ternary_logic USING: combinators kernel ; IN: rosettacode.ternary

SINGLETON: m UNION: trit t m POSTPONE: f ;

GENERIC: >trit ( object -- trit ) M: trit >trit ;

tnot ( trit1 -- trit )
   >trit { { t [ f ] } { m [ m ] } { f [ t ] } } case ;
tand ( trit1 trit2 -- trit )
   >trit {
       { t [ >trit ] }
       { m [ >trit { { t [ m ] } { m [ m ] } { f [ f ] } } case ] }
       { f [ >trit drop f ] }
   } case ;
tor ( trit1 trit2 -- trit )
   >trit {
       { t [ >trit drop t ] }
       { m [ >trit { { t [ t ] } { m [ m ] } { f [ m ] } } case ] }
       { f [ >trit ] }
   } case ;
txor ( trit1 trit2 -- trit )
   >trit {
       { t [ tnot ] }
       { m [ >trit drop m ] }
       { f [ >trit ] }
   } case ;
t= ( trit1 trit2 -- trit )
   {
       { t [ >trit ] }
       { m [ >trit drop m ] }
       { f [ tnot ] }
   } case ;</lang>

Example use: <lang factor>( scratchpad ) CONSTANT: trits { t m f } ( scratchpad ) trits [ tnot ] map . { f m t } ( scratchpad ) trits [ trits swap [ tand ] curry map ] map . { { t m f } { m m f } { f f f } } ( scratchpad ) trits [ trits swap [ tor ] curry map ] map . { { t t t } { t m m } { t m f } } ( scratchpad ) trits [ trits swap [ txor ] curry map ] map . { { f m t } { m m m } { t m f } } ( scratchpad ) trits [ trits swap [ t= ] curry map ] map . { { t m f } { m m m } { f m t } }</lang>

Go

Go has four operators for the bool type: ==, &&, ||, and !. <lang go>package main

import "fmt"

type trit int8

const (

   trFalse = iota - 1
   trMaybe
   trTrue

)

func (t trit) String() string {

   switch t {
   case trFalse:
       return "False"
   case trMaybe:
       return "Maybe"
   case trTrue:
       return "True "
   }
   panic("Invalid trit")

}

func trNot(t trit) trit {

   return -t

}

func trAnd(s, t trit) trit {

   if s < t {
       return s
   }
   return t

}

func trOr(s, t trit) trit {

   if s > t {
       return s
   }
   return t

}

func trEq(s, t trit) trit {

   return s * t

}

func main() {

   trSet := []trit{trFalse, trMaybe, trTrue}
   fmt.Println("t     not t")
   for _, t := range trSet {
       fmt.Println(t, trNot(t))
   }
   fmt.Println("\ns     t     s and t")
   for _, s := range trSet {
       for _, t := range trSet {
           fmt.Println(s, t, trAnd(s, t))
       }
   }
   fmt.Println("\ns     t     s or t")
   for _, s := range trSet {
       for _, t := range trSet {
           fmt.Println(s, t, trOr(s, t))
       }
   }
   fmt.Println("\ns     t     s eq t")
   for _, s := range trSet {
       for _, t := range trSet {
           fmt.Println(s, t, trEq(s, t))
       }
   }

}</lang> Output:

t     not t
False True 
Maybe Maybe
True  False

s     t     s and t
False False False
False Maybe False
False True  False
Maybe False False
Maybe Maybe Maybe
Maybe True  Maybe
True  False False
True  Maybe Maybe
True  True  True 

s     t     s or t
False False False
False Maybe Maybe
False True  True 
Maybe False Maybe
Maybe Maybe Maybe
Maybe True  True 
True  False True 
True  Maybe True 
True  True  True 

s     t     s eq t
False False True 
False Maybe Maybe
False True  False
Maybe False Maybe
Maybe Maybe Maybe
Maybe True  Maybe
True  False False
True  Maybe Maybe
True  True  True 

Groovy

Solution: <lang groovy>enum Trit {

   TRUE, MAYBE, FALSE
 
   private Trit nand(Trit that) {
       switch ([this,that]) {
           case { FALSE in it }: return TRUE
           case { MAYBE in it }: return MAYBE
           default             : return FALSE
       }
   }
   private Trit nor(Trit that) { this.or(that).not() }
 
   Trit and(Trit that)   { this.nand(that).not() }
   Trit or(Trit that)    { this.not().nand(that.not()) }
   Trit not()            { this.nand(this) }
   Trit imply(Trit that) { this.nand(that.not()) }
   Trit equiv(Trit that) { this.and(that).or(this.nor(that)) }

}</lang>

Test: <lang groovy>printf 'AND\n ' Trit.values().each { b -> printf ('%6s', b) } println '\n ----- ----- -----' Trit.values().each { a ->

   printf ('%6s | ', a)
   Trit.values().each { b -> printf ('%6s', a.and(b)) }
   println()

}

printf '\nOR\n ' Trit.values().each { b -> printf ('%6s', b) } println '\n ----- ----- -----' Trit.values().each { a ->

   printf ('%6s | ', a)
   Trit.values().each { b -> printf ('%6s', a.or(b)) }
   println()

}

println '\nNOT' Trit.values().each {

   printf ('%6s | %6s\n', it, it.not())

}

printf '\nIMPLY\n ' Trit.values().each { b -> printf ('%6s', b) } println '\n ----- ----- -----' Trit.values().each { a ->

   printf ('%6s | ', a)
   Trit.values().each { b -> printf ('%6s', a.imply(b)) }
   println()

}

printf '\nEQUIV\n ' Trit.values().each { b -> printf ('%6s', b) } println '\n ----- ----- -----' Trit.values().each { a ->

   printf ('%6s | ', a)
   Trit.values().each { b -> printf ('%6s', a.equiv(b)) }
   println()

}</lang>

Output:

AND
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE MAYBE FALSE
 MAYBE |  MAYBE MAYBE FALSE
 FALSE |  FALSE FALSE FALSE

OR
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE  TRUE  TRUE
 MAYBE |   TRUE MAYBE MAYBE
 FALSE |   TRUE MAYBE FALSE

NOT
  TRUE |  FALSE
 MAYBE |  MAYBE
 FALSE |   TRUE

IMPLY
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE MAYBE FALSE
 MAYBE |   TRUE MAYBE MAYBE
 FALSE |   TRUE  TRUE  TRUE

EQUIV
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE MAYBE FALSE
 MAYBE |  MAYBE MAYBE MAYBE
 FALSE |  FALSE MAYBE  TRUE

Haskell

All operations given in terms of NAND, the functionally-complete operation.

<lang Haskell>import Prelude hiding (Bool(..), not, (&&), (||), (==))

main = mapM_ (putStrLn . unlines . map unwords)

   [ table "not"     $ unary not
   , table "and"     $ binary (&&)
   , table "or"      $ binary (||)
   , table "implies" $ binary (=->)
   , table "equals"  $ binary (==)
   ]

data Trit = False | Maybe | True deriving (Show)

False `nand` _ = True _ `nand` False = True True `nand` True = False _ `nand` _ = Maybe

not a = nand a a

a && b = not $ a `nand` b

a || b = not a `nand` not b

a =-> b = a `nand` not b

a == b = (a && b) || (not a && not b)

inputs1 = [True, Maybe, False] inputs2 = [(a,b) | a <- inputs1, b <- inputs1]

unary f = map (\a -> [a, f a]) inputs1 binary f = map (\(a,b) -> [a, b, f a b]) inputs2

table name xs = map (map pad) . (header :) $ map (map show) xs

   where header = map (:[]) (take ((length $ head xs) - 1) ['A'..]) ++ [name]

pad s = s ++ replicate (5 - length s) ' '</lang>

Output:

A     not
True  False
Maybe Maybe
False True

A     B     and
True  True  True
True  Maybe Maybe
True  False False
Maybe True  Maybe
Maybe Maybe Maybe
Maybe False False
False True  False
False Maybe False
False False False

A     B     or
True  True  True
True  Maybe True
True  False True
Maybe True  True
Maybe Maybe Maybe
Maybe False Maybe
False True  True
False Maybe Maybe
False False False

A     B     implies
True  True  True
True  Maybe Maybe
True  False False
Maybe True  True
Maybe Maybe Maybe
Maybe False Maybe
False True  True
False Maybe True
False False True

A     B     equals
True  True  True
True  Maybe Maybe
True  False False
Maybe True  Maybe
Maybe Maybe Maybe
Maybe False Maybe
False True  False
False Maybe Maybe
False False True

Icon and Unicon

The following example works in both Icon and Unicon. There are a couple of comments on the code that pertain to the task requirements:

  • Strictly speaking there are no binary values in Icon and Unicon. There are a number of flow control operations that result in expression success (and a result) or failure which affects flow. As a result there really isn't a set of binary operators to map into ternary. The example provides the minimum required by the task plus xor.
  • The code below does not define a data type as it doesn't really make sense in this case. Icon and Unicon can create records which would be overkill and clumsy in this case. Unicon can create objects which would also be overkill. The only remaining option is to reinterpret one of the existing types as ternary values. The code below implements balanced ternary values as integers in order to simplify several of the functions.
  • The use of integers doesn't really support strings of trits well. While there is a function showtrit to ease display a converse function to decode character trits in a string is not included.


<lang Icon>$define TRUE 1 $define FALSE -1 $define UNKNOWN 0

invocable all link printf

procedure main() # demonstrate ternary logic

ufunc := ["not3"] bfunc := ["and3", "or3", "xor3", "eq3", "ifthen3"]

every f := !ufunc do { # display unary functions

  printf("\nunary function=%s:\n",f)
  every t1 := (TRUE | FALSE | UNKNOWN) do
     printf(" %s : %s\n",showtrit(t1),showtrit(not3(t1)))
  }


every f := !bfunc do { # display binary functions

  printf("\nbinary function=%s:\n     ",f)
  every t1 := (&null | TRUE | FALSE | UNKNOWN) do { 
     printf(" %s : ",showtrit(\t1))
     every t2 := (TRUE | FALSE | UNKNOWN | &null) do {
        if /t1 then printf("  %s",showtrit(\t2)|"\n")
        else printf("  %s",showtrit(f(t1,\t2))|"\n")
        }
     }
  }

end

procedure showtrit(a) #: return printable trit of error if invalid return case a of {TRUE:"T";FALSE:"F";UNKNOWN:"?";default:runerr(205,a)} end

procedure istrit(a) #: return value of trit or error if invalid return (TRUE|FALSE|UNKNOWN|runerr(205,a)) = a end

procedure not3(a) #: not of trit or error if invalid return FALSE * istrit(a) end

procedure and3(a,b) #: and of two trits or error if invalid return min(istrit(a),istrit(b)) end

procedure or3(a,b) #: or of two trits or error if invalid return max(istrit(a),istrit(b)) end

procedure eq3(a,b) #: equals of two trits or error if invalid return istrit(a) * istrit(b) end

procedure ifthen3(a,b) #: if trit then trit or error if invalid return case istrit(a) of { TRUE: istrit(b) ; UNKNOWN: or3(a,b); FALSE: TRUE } end

procedure xor3(a,b) #: xor of two trits or error if invalid return not3(eq3(a,b)) end</lang>

printf.icn provides support for the printf family of functions

Output:

unary function=not3:
 T : F
 F : T
 ? : ?

binary function=and3:
       T  F  ?
 T :   T  F  ?
 F :   F  F  F
 ? :   ?  F  ?

binary function=or3:
       T  F  ?
 T :   T  T  T
 F :   T  F  ?
 ? :   T  ?  ?

binary function=xor3:
       T  F  ?
 T :   F  T  ?
 F :   T  F  ?
 ? :   ?  ?  ?

binary function=eq3:
       T  F  ?
 T :   T  F  ?
 F :   F  T  ?
 ? :   ?  ?  ?

binary function=ifthen3:
       T  F  ?
 T :   T  F  ?
 F :   T  T  T
 ? :   T  ?  ?

J

The designers of J felt that user defined types were harmful, so that part of the task will not be supported here.

Instead:

true: 1 false: 0 maybe: 0.5

<lang j>not=: -. and=: <. or =: >. if =: (>. -.)"0~ eq =: (<.&-. >. <.)"0</lang>

Example use:

<lang j> not 0 0.5 1 1 0.5 0

  0 0.5 1 and/ 0 0.5 1

0 0 0 0 0.5 0.5 0 0.5 1

  0 0.5 1 or/ 0 0.5 1
 0 0.5 1

0.5 0.5 1

 1   1 1
  0 0.5 1 if/ 0 0.5 1
 1   1 1

0.5 0.5 1

 0 0.5 1
  0 0.5 1 eq/ 0 0.5 1
 1 0.5   0

0.5 0.5 0.5

 0 0.5   1</lang>

Note that this implementation is a special case of "fuzzy logic" (using a limited set of values).

Note that while >. and <. could be used for boolean operations instead of J's +. and *., the identity elements for >. and <. are not boolean values, but are negative and positive infinity. See also: Boolean ring

Note that we might instead define values between 0 and 1 to represent independent probabilities:

<lang J>not=: -. and=: * or=: *&.-. if =: (or -.)"0~ eq =: (*&-. or *)"0</lang>

However, while this might be a more intellectually satisfying approach, this gives us some different results from the task requirement, for the combination of two "maybe" values:

<lang J> not 0 0.5 1 1 0.5 0

  0 0.5 1 and/ 0 0.5 1

0 0 0 0 0.25 0.5 0 0.5 1

  0 0.5 1 or/ 0 0.5 1
 0  0.5 1

0.5 0.75 1

 1    1 1
  0 0.5 1 if/ 0 0.5 1
 1    1 1

0.5 0.75 1

 0  0.5 1
  0 0.5 1 eq/ 0 0.5 1
 1    0.5   0

0.5 0.4375 0.5

 0    0.5   1</lang>

Another interesting possibility would involve using George Boole's original operations. This leaves us without any "not", (if we include the definition of logical negation which was later added to the definition of Boolean algebra, then the only numbers which can be used with Boolean algebra are 1 and 0). So, it's not clear how we would implement "if" or "eq". However, "and" and "or" would look like this:

<lang J>and=: *. or=: +.</lang>

And, the boolean result tables would look like this:

<lang J> 0 0.5 1 and/ 0 0.5 1 0 0 0 0 0.5 1 0 1 1

  0 0.5 1 or/ 0 0.5 1
 0 0.5   1

0.5 0.5 0.5

 1 0.5   1</lang>

Java

Works with: Java version 1.5+

<lang java5>public class Logic{ public static enum Trit{ TRUE, MAYBE, FALSE;

public Trit and(Trit other){ if(this == TRUE){ return other; }else if(this == MAYBE){ return (other == FALSE) ? FALSE : MAYBE; }else{ return FALSE; } }

public Trit or(Trit other){ if(this == TRUE){ return TRUE; }else if(this == MAYBE){ return (other == TRUE) ? TRUE : MAYBE; }else{ return other; } }

public Trit tIf(Trit other){ if(this == TRUE){ return other; }else if(this == MAYBE){ return (other == TRUE) ? TRUE : MAYBE; }else{ return TRUE; } }

public Trit not(){ if(this == TRUE){ return FALSE; }else if(this == MAYBE){ return MAYBE; }else{ return TRUE; } }

public Trit equals(Trit other){ if(this == TRUE){ return other; }else if(this == MAYBE){ return MAYBE; }else{ return other.not(); } } } public static void main(String[] args){ for(Trit a:Trit.values()){ System.out.println("not " + a + ": " + a.not()); } for(Trit a:Trit.values()){ for(Trit b:Trit.values()){ System.out.println(a+" and "+b+": "+a.and(b)+ "\t "+a+" or "+b+": "+a.or(b)+ "\t "+a+" implies "+b+": "+a.tIf(b)+ "\t "+a+" = "+b+": "+a.equals(b)); } } } }</lang> Output:

not TRUE: FALSE
not MAYBE: MAYBE
not FALSE: TRUE
TRUE and TRUE: TRUE	 TRUE or TRUE: TRUE	 TRUE implies TRUE: TRUE	 TRUE = TRUE: TRUE
TRUE and MAYBE: MAYBE	 TRUE or MAYBE: TRUE	 TRUE implies MAYBE: MAYBE	 TRUE = MAYBE: MAYBE
TRUE and FALSE: FALSE	 TRUE or FALSE: TRUE	 TRUE implies FALSE: FALSE	 TRUE = FALSE: FALSE
MAYBE and TRUE: MAYBE	 MAYBE or TRUE: TRUE	 MAYBE implies TRUE: TRUE	 MAYBE = TRUE: MAYBE
MAYBE and MAYBE: MAYBE	 MAYBE or MAYBE: MAYBE	 MAYBE implies MAYBE: MAYBE	 MAYBE = MAYBE: MAYBE
MAYBE and FALSE: FALSE	 MAYBE or FALSE: MAYBE	 MAYBE implies FALSE: MAYBE	 MAYBE = FALSE: MAYBE
FALSE and TRUE: FALSE	 FALSE or TRUE: TRUE	 FALSE implies TRUE: TRUE	 FALSE = TRUE: FALSE
FALSE and MAYBE: FALSE	 FALSE or MAYBE: MAYBE	 FALSE implies MAYBE: TRUE	 FALSE = MAYBE: MAYBE
FALSE and FALSE: FALSE	 FALSE or FALSE: FALSE	 FALSE implies FALSE: TRUE	 FALSE = FALSE: TRUE

Liberty BASIC

<lang lb> 'ternary logic '0 1 2 'F ? T 'False Don't know True 'LB has NOT AND OR XOR, so we implement them. 'LB has no EQ, but XOR could be expressed via EQ. In 'normal' boolean at least.

global tFalse, tDontKnow, tTrue tFalse = 0 tDontKnow = 1 tTrue = 2

print "Short and long names for ternary logic values" for i = tFalse to tTrue

   print shortName3$(i);" ";longName3$(i)

next print

print "Single parameter functions" print "x";" ";"=x";" ";"not(x)" for i = tFalse to tTrue

   print shortName3$(i);"  ";shortName3$(i);"    ";shortName3$(not3(i))

next print

print "Double parameter fuctions" print "x";" ";"y";" ";"x AND y";" ";"x OR y";" ";"x EQ y";" ";"x XOR y" for a = tFalse to tTrue

   for b = tFalse to tTrue
       print shortName3$(a);" ";shortName3$(b);"     "; _
           shortName3$(and3(a,b));"       "; shortName3$(or3(a,b));"       "; _
           shortName3$(eq3(a,b));"        "; shortName3$(xor3(a,b))
   next

next

function and3(a,b)

   and3 = min(a,b)

end function

function or3(a,b)

   or3 = max(a,b)

end function

function eq3(a,b)

   select case
   case a=tDontKnow or b=tDontKnow
       eq3 = tDontKnow
   case a=b
       eq3 = tTrue
   case else
       eq3 = tFalse
   end select

end function

function xor3(a,b)

   xor3 = not3(eq3(a,b))

end function

function not3(b)

   not3 = 2-b

end function

'------------------------------------------------ function shortName3$(i)

  shortName3$ = word$("F ? T", i+1)

end function

function longName3$(i)

   longName3$ = word$("False,Don't know,True", i+1, ",")

end function

</lang>

Output:

Short and long names for ternary logic values
F False
? Don't know
T True

Single parameter functions
x =x  not(x)
F  F    T
?  ?    ?
T  T    F

Double  parameter fuctions
x y  x AND y  x OR y  x EQ y  x XOR y
F F     F       F       T        F
F ?     F       ?       ?        ?
F T     F       T       F        T
? F     F       ?       ?        ?
? ?     ?       ?       ?        ?
? T     ?       T       ?        ?
T F     F       T       F        T
T ?     ?       T       ?        ?
T T     T       T       T        F

Mathematica

Type definition is not allowed in Mathematica. We can just use the build-in symbols "True" and "False", and add a new symbol "Maybe". <lang mathematica>Maybe /: ! Maybe = Maybe; Maybe /: (And | Or | Nand | Nor | Xor | Xnor | Implies | Equivalent)[Maybe, Maybe] = Maybe;</lang> Example: <lang mathematica>trits = {True, Maybe, False}; Print@Grid[

  ArrayFlatten[{{Template:Not, Template:Null}, {List /@ trits, 
     List /@ Not /@ trits}}]];

Do[Print@Grid[

  ArrayFlatten[{{Template:Operator, {{Null, Null, 
       Null}}}, {Template:Null, {trits}}, {List /@ trits, 
     Outer[operator, trits, trits]}}]], {operator, {And, Or, Nand, 
  Nor, Xor, Xnor, Implies, Equivalent}}]</lang>

Output:

Not	
True	False
Maybe	Maybe
False	True



And			
	True	Maybe	False
True	True	Maybe	False
Maybe	Maybe	Maybe	False
False	False	False	False



Or			
	True	Maybe	False
True	True	True	True
Maybe	True	Maybe	Maybe
False	True	Maybe	False



Nand			
	True	Maybe	False
True	False	Maybe	True
Maybe	Maybe	Maybe	True
False	True	True	True



Nor			
	True	Maybe	False
True	False	False	False
Maybe	False	Maybe	Maybe
False	False	Maybe	True



Xor			
	True	Maybe	False
True	False	Maybe	True
Maybe	Maybe	Maybe	Maybe
False	True	Maybe	False



Xnor			
	True	Maybe	False
True	True	Maybe	False
Maybe	Maybe	Maybe	Maybe
False	False	Maybe	True



Implies			
	True	Maybe	False
True	True	Maybe	False
Maybe	True	Maybe	Maybe
False	True	True	True



Equivalent			
	True	Maybe	False
True	True	Maybe	False
Maybe	Maybe	Maybe	Maybe
False	False	Maybe	True

OCaml

<lang ocaml>type trit = True | False | Maybe

let t_not = function

 | True -> False
 | False -> True
 | Maybe -> Maybe

let t_and a b = match (a,b) with

  | (True,True) -> True
  | (False,_)  | (_,False) -> False
  | _ -> Maybe

let t_or a b = t_not (t_and (t_not a) (t_not b))

let t_eq a b = match (a,b) with

  | (True,True) | (False,False) -> True
  | (False,True) | (True,False) -> False
  | _ -> Maybe

let t_imply a b = t_or (t_not a) b

let string_of_trit = function

 | True -> "True"
 | False -> "False"
 | Maybe -> "Maybe"

let () =

 let values = [| True; Maybe; False |] in
 let f = string_of_trit in
 Array.iter (fun v -> Printf.printf "Not %s: %s\n" (f v) (f (t_not v))) values;
 print_newline ();
 let print op str =
   Array.iter (fun a ->
     Array.iter (fun b ->
       Printf.printf "%s %s %s: %s\n" (f a) str (f b) (f (op a b))
     ) values
   ) values;
   print_newline ()
 in
 print t_and "And";
 print t_or "Or";
 print t_imply "Then";
 print t_eq "Equiv";
</lang>

Output:

Not True: False
Not Maybe: Maybe
Not False: True

True And True: True
True And Maybe: Maybe
True And False: False
Maybe And True: Maybe
Maybe And Maybe: Maybe
Maybe And False: False
False And True: False
False And Maybe: False
False And False: False

True Or True: True
True Or Maybe: True
True Or False: True
Maybe Or True: True
Maybe Or Maybe: Maybe
Maybe Or False: Maybe
False Or True: True
False Or Maybe: Maybe
False Or False: False

True Then True: True
True Then Maybe: Maybe
True Then False: False
Maybe Then True: True
Maybe Then Maybe: Maybe
Maybe Then False: Maybe
False Then True: True
False Then Maybe: True
False Then False: True

True Equiv True: True
True Equiv Maybe: Maybe
True Equiv False: False
Maybe Equiv True: Maybe
Maybe Equiv Maybe: Maybe
Maybe Equiv False: Maybe
False Equiv True: False
False Equiv Maybe: Maybe
False Equiv False: True

Using a general binary -> ternary transform

Instead of writing all of the truth-tables by hand, we can construct a general binary -> ternary transform and apply it to any logical function we want: <lang OCaml>type trit = True | False | Maybe

let to_bin = function True -> [true] | False -> [false] | Maybe -> [true;false]

let eval f x =

  List.fold_left (fun l c -> List.fold_left (fun m d -> ((d c) :: m)) l f) [] x

let rec from_bin =

  function [true] -> True | [false] -> False
  | h :: t -> (match (h, from_bin t) with
     (true,True) -> True | (false,False) -> False | _ -> Maybe)
  | _ -> Maybe

let to_ternary1 uop = fun x -> from_bin (eval [uop] (to_bin x)) let to_ternary2 bop = fun x y -> from_bin (eval (eval [bop] (to_bin x)) (to_bin y))

let t_not = to_ternary1 (not) let t_and = to_ternary2 (&&) let t_or = to_ternary2 (||) let t_equiv = to_ternary2 (=) let t_imply = to_ternary2 (fun p q -> (not p) || q)

let str = function True -> "True " | False -> "False" | Maybe -> "Maybe" let iterv f = List.iter f [True; False; Maybe]

let table1 s u =

  print_endline ("\n"^s^":");
  iterv (fun v -> print_endline ("  "^(str v)^" -> "^(str (u v))));;

let table2 s b =

  print_endline ("\n"^s^":");
  iterv (fun u ->
     iterv (fun v ->
        print_endline ("  "^(str u)^" "^(str v)^" -> "^(str (b u v)))));;

table1 "not" t_not;; table2 "and" t_and;; table2 "or" t_or;; table2 "equiv" t_equiv;; table2 "implies" t_imply;;</lang> Output:

not:
  True  -> False
  False -> True 
  Maybe -> Maybe

and:
  True  True  -> True 
  True  False -> False
  True  Maybe -> Maybe
  False True  -> False
  False False -> False
  False Maybe -> False
  Maybe True  -> Maybe
  Maybe False -> False
  Maybe Maybe -> Maybe

or:
  True  True  -> True 
  True  False -> True 
  True  Maybe -> True 
  False True  -> True 
  False False -> False
  False Maybe -> Maybe
  Maybe True  -> True 
  Maybe False -> Maybe
  Maybe Maybe -> Maybe

equiv:
  True  True  -> True 
  True  False -> False
  True  Maybe -> Maybe
  False True  -> False
  False False -> True 
  False Maybe -> Maybe
  Maybe True  -> Maybe
  Maybe False -> Maybe
  Maybe Maybe -> Maybe

implies:
  True  True  -> True 
  True  False -> False
  True  Maybe -> Maybe
  False True  -> True 
  False False -> True 
  False Maybe -> True 
  Maybe True  -> True 
  Maybe False -> Maybe
  Maybe Maybe -> Maybe

ooRexx

<lang ooRexx> tritValues = .array~of(.trit~true, .trit~false, .trit~maybe) tab = '09'x

say "not operation (\)" loop a over tritValues

   say "\"a":" (\a)

end

say say "and operation (&)" loop aa over tritValues

   loop bb over tritValues
       say (aa" & "bb":" (aa&bb))
   end

end

say say "or operation (|)" loop aa over tritValues

   loop bb over tritValues
       say (aa" | "bb":" (aa|bb))
   end

end

say say "implies operation (&&)" loop aa over tritValues

   loop bb over tritValues
       say (aa" && "bb":" (aa&&bb))
   end

end

say say "equals operation (=)" loop aa over tritValues

   loop bb over tritValues
       say (aa" = "bb":" (aa=bb))
   end

end

class trit

-- making this a private method so we can control the creation -- of these. We only allow 3 instances to exist

method new class private
 forward class(super)
method init class
 expose true false maybe
 -- delayed creation
 true = .nil
 false = .nil
 maybe = .nil

-- read only attribute access to the instances. -- these methods create the appropriate singleton on the first call

attribute true class get
 expose true
 if true == .nil then true = self~new("True")
 return true
attribute false class get
 expose false
 if false == .nil then false = self~new("False")
 return false
attribute maybe class get
 expose maybe
 if maybe == .nil then maybe = self~new("Maybe")
 return maybe

-- create an instance

method init
 expose value
 use arg value

-- string method to return the value of the instance

method string
 expose value
 return value

-- "and" method using the operator overload

method "&"
 use strict arg other
 if self == .trit~true then return other
 else if self == .trit~maybe then do
     if other == .trit~false then return .trit~false
     else return .trit~maybe
 end
 else return .trit~false

-- "or" method using the operator overload

method "|"
 use strict arg other
 if self == .trit~true then return .trit~true
 else if self == .trit~maybe then do
     if other == .trit~true then return .trit~true
     else return .trit~maybe
 end
 else return other

-- implies method...using the XOR operator for this

method "&&"
 use strict arg other
 if self == .trit~true then return other
 else if self == .trit~maybe then do
     if other == .trit~true then return .trit~true
     else return .trit~maybe
 end
 else return .trit~true

-- "not" method using the operator overload

method "\"
 if self == .trit~true then return .trit~false
 else if self == .trit~maybe then return .trit~maybe
 else return .trit~true

-- "equals" using the "=" override. This makes a distinction between -- the "==" operator, which is real equality and the "=" operator, which -- is trinary equality.

method "="
 use strict arg other
 if self == .trit~true then return other
 else if self == .trit~maybe then return .trit~maybe
 else return \other

</lang>

not operation (\)
\True: False
\False: True
\Maybe: Maybe

and operation (&)
True & True: True
True & False: False
True & Maybe: Maybe
False & True: False
False & False: False
False & Maybe: False
Maybe & True: Maybe
Maybe & False: False
Maybe & Maybe: Maybe

or operation (|)
True | True: True
True | False: True
True | Maybe: True
False | True: True
False | False: False
False | Maybe: Maybe
Maybe | True: True
Maybe | False: Maybe
Maybe | Maybe: Maybe

implies operation (&&)
True && True: True
True && False: False
True && Maybe: Maybe
False && True: True
False && False: True
False && Maybe: True
Maybe && True: True
Maybe && False: Maybe
Maybe && Maybe: Maybe

equals operation (=)
True = True: True
True = False: False
True = Maybe: Maybe
False = True: False
False = False: True
False = Maybe: Maybe
Maybe = True: Maybe
Maybe = False: Maybe
Maybe = Maybe: Maybe

Pascal

<lang pascal>Program TernaryLogic (output);

type

 trit = (terTrue, terMayBe, terFalse);

function terNot (a: trit): trit;

 begin
   case a of
     terTrue:  terNot := terFalse;
     terMayBe: terNot := terMayBe;
     terFalse: terNot := terTrue;
   end;
 end;

function terAnd (a, b: trit): trit;

 begin
   terAnd := terMayBe;
   if (a = terFalse) or (b = terFalse) then
     terAnd := terFalse
   else
     if (a = terTrue) and (b = terTrue) then
       terAnd := terTrue;
 end;

function terOr (a, b: trit): trit;

 begin
   terOr := terMayBe;
   if (a = terTrue) or (b = terTrue) then
     terOr := terTrue
   else
     if (a = terFalse) and (b = terFalse) then
       terOr := terFalse;
 end;

function terEquals (a, b: trit): trit;

 begin
   if a = b then
     terEquals := terTrue
   else
     if a <> b then
       terEquals := terFalse;
   if (a = terMayBe) or (b = terMayBe) then
     terEquals := terMayBe;
 end;

function terIfThen (a, b: trit): trit;

 begin
   terIfThen := terMayBe;
   if (a = terTrue) or (b = terFalse)  then
     terIfThen := terTrue
   else
     if (a = terFalse) and (b = terTrue) then
       terIfThen := terFalse;
 end;

function terToStr(a: trit): string;

 begin
   case a of
     terTrue:  terToStr := 'True ';
     terMayBe: terToStr := 'Maybe';
     terFalse: terToStr := 'False';
   end;
 end;

begin

 writeln('Ternary logic test:');
 writeln;
 writeln('NOT ', ' True ', ' Maybe', ' False');
 writeln('     ', terToStr(terNot(terTrue)), ' ', terToStr(terNot(terMayBe)), ' ', terToStr(terNot(terFalse)));
 writeln;
 writeln('AND   ', ' True ', ' Maybe', ' False');
 writeln('True   ', terToStr(terAnd(terTrue,terTrue)),  ' ', terToStr(terAnd(terMayBe,terTrue)),  ' ', terToStr(terAnd(terFalse,terTrue)));
 writeln('Maybe  ', terToStr(terAnd(terTrue,terMayBe)), ' ', terToStr(terAnd(terMayBe,terMayBe)), ' ', terToStr(terAnd(terFalse,terMayBe)));
 writeln('False  ', terToStr(terAnd(terTrue,terFalse)), ' ', terToStr(terAnd(terMayBe,terFalse)), ' ', terToStr(terAnd(terFalse,terFalse)));
 writeln;
 writeln('OR    ', ' True ', ' Maybe', ' False');
 writeln('True   ', terToStr(terOR(terTrue,terTrue)),  ' ', terToStr(terOR(terMayBe,terTrue)),  ' ', terToStr(terOR(terFalse,terTrue)));
 writeln('Maybe  ', terToStr(terOR(terTrue,terMayBe)), ' ', terToStr(terOR(terMayBe,terMayBe)), ' ', terToStr(terOR(terFalse,terMayBe)));
 writeln('False  ', terToStr(terOR(terTrue,terFalse)), ' ', terToStr(terOR(terMayBe,terFalse)), ' ', terToStr(terOR(terFalse,terFalse)));
 writeln;
 writeln('IFTHEN', ' True ', ' Maybe', ' False');
 writeln('True   ', terToStr(terIfThen(terTrue,terTrue)),  ' ', terToStr(terIfThen(terMayBe,terTrue)),  ' ', terToStr(terIfThen(terFalse,terTrue)));
 writeln('Maybe  ', terToStr(terIfThen(terTrue,terMayBe)), ' ', terToStr(terIfThen(terMayBe,terMayBe)), ' ', terToStr(terIfThen(terFalse,terMayBe)));
 writeln('False  ', terToStr(terIfThen(terTrue,terFalse)), ' ', terToStr(terIfThen(terMayBe,terFalse)), ' ', terToStr(terIfThen(terFalse,terFalse)));
 writeln;
 writeln('EQUAL ', ' True ', ' Maybe', ' False');
 writeln('True   ', terToStr(terEquals(terTrue,terTrue)),  ' ', terToStr(terEquals(terMayBe,terTrue)),  ' ', terToStr(terEquals(terFalse,terTrue)));
 writeln('Maybe  ', terToStr(terEquals(terTrue,terMayBe)), ' ', terToStr(terEquals(terMayBe,terMayBe)), ' ', terToStr(terEquals(terFalse,terMayBe)));
 writeln('False  ', terToStr(terEquals(terTrue,terFalse)), ' ', terToStr(terEquals(terMayBe,terFalse)), ' ', terToStr(terEquals(terFalse,terFalse)));
 writeln;

end.</lang> Output:

:> ./TernaryLogic
Ternary logic test:

NOT  True  Maybe False
     False Maybe True 

AND    True  Maybe False
True   True  Maybe False
Maybe  Maybe Maybe False
False  False False False

OR     True  Maybe False
True   True  True  True 
Maybe  True  Maybe Maybe
False  True  Maybe False

IFTHEN True  Maybe False
True   True  Maybe False
Maybe  True  Maybe Maybe
False  True  True  True 

EQUAL  True  Maybe False
True   True  Maybe False
Maybe  Maybe Maybe Maybe
False  False Maybe True 

Perl

File Trit.pm: <lang perl>package Trit;

  1. -1 = false ; 0 = maybe ; 1 = true

use Exporter 'import';

our @EXPORT_OK = qw(TRUE FALSE MAYBE is_true is_false is_maybe); our %EXPORT_TAGS = ( all => \@EXPORT_OK, const => [qw(TRUE FALSE MAYBE)], bool => [qw(is_true is_false is_maybe)], );

use List::Util qw(min max);

use overload '=' => sub { $_[0]->clone() }, '<=>'=> sub { $_[0]->cmp($_[1]) }, 'cmp'=> sub { $_[0]->cmp($_[1]) }, '==' => sub { ${$_[0]} == ${$_[1]} }, 'eq' => sub { $_[0]->equiv($_[1]) }, '>' => sub { ${$_[0]} > ${$_[1]} }, '<' => sub { ${$_[0]} < ${$_[1]} }, '>=' => sub { ${$_[0]} >= ${$_[1]} }, '<=' => sub { ${$_[0]} <= ${$_[1]} }, '|' => sub { $_[0]->or($_[1]) }, '&' => sub { $_[0]->and($_[1]) }, '!' => sub { $_[0]->not() }, '~' => sub { $_[0]->not() }, '""' => sub { $_[0]->tostr() }, '0+' => sub { $_[0]->tonum() },

sub new { my ($class, $v) = @_; my $ret = !defined($v) ? 0 : $v eq 'true' ? 1 : $v eq 'false'? -1 : $v eq 'maybe'? 0 : $v > 0 ? 1 : $v < 0 ? -1 : 0; return bless \$ret, $class; }

sub TRUE() { new Trit( 1) } sub FALSE() { new Trit(-1) } sub MAYBE() { new Trit( 0) }

sub clone { my $ret = ${$_[0]}; return bless \$ret, ref($_[0]); }

sub tostr { ${$_[0]} > 0 ? "true" : ${$_[0]} < 0 ? "false" : "maybe" } sub tonum { ${$_[0]} }

sub is_true { ${$_[0]} > 0 } sub is_false { ${$_[0]} < 0 } sub is_maybe { ${$_[0]} == 0 }

sub cmp { ${$_[0]} <=> ${$_[1]} } sub not { new Trit(-${$_[0]}) } sub and { new Trit(min(${$_[0]}, ${$_[1]}) ) } sub or { new Trit(max(${$_[0]}, ${$_[1]}) ) }

sub equiv { new Trit( ${$_[0]} * ${$_[1]} ) }</lang> File test.pl: <lang perl>use Trit ':all';

my @a = (TRUE(), MAYBE(), FALSE());

print "\na\tNOT a\n"; print "$_\t".(!$_)."\n" for @a; # Example of use of prefix operator NOT. Tilde ~ also can be used.


print "\nAND\t".join("\t",@a)."\n"; for my $a (@a) { print $a; for my $b (@a) { print "\t".($a & $b); # Example of use of infix & (and) } print "\n"; }

print "\nOR\t".join("\t",@a)."\n"; for my $a (@a) { print $a; for my $b (@a) { print "\t".($a | $b); # Example of use of infix | (or) } print "\n"; }

print "\nEQV\t".join("\t",@a)."\n"; for my $a (@a) { print $a; for my $b (@a) { print "\t".($a eq $b); # Example of use of infix eq (equivalence) } print "\n"; }

print "\n==\t".join("\t",@a)."\n"; for my $a (@a) { print $a; for my $b (@a) { print "\t".($a == $b); # Example of use of infix == (equality) } print "\n"; }</lang> Output:

a	NOT a
true	false
maybe	maybe
false	true

AND	true	maybe	false
true	true	maybe	false
maybe	maybe	maybe	false
false	false	false	false

OR	true	maybe	false
true	true	true	true
maybe	true	maybe	maybe
false	true	maybe	false

EQV	true	maybe	false
true	true	maybe	false
maybe	maybe	maybe	maybe
false	false	maybe	true

==	true	maybe	false
true	1		
maybe		1	
false			1

Perl 6

Works with: niecza

Implementation: <lang perl6>enum Trit <Foo Moo Too>;

sub prefix:<¬> (Trit $a) { Trit(1-($a-1)) }

sub infix:<∧> is equiv(&infix:<*>) (Trit $a, Trit $b) { $a min $b } sub infix:<∨> is equiv(&infix:<+>) (Trit $a, Trit $b) { $a max $b }

sub infix:<→> is equiv(&infix:<..>) (Trit $a, Trit $b) { ¬$a max $b } sub infix:<≡> is equiv(&infix:<eq>) (Trit $a, Trit $b) { Trit(1 + ($a-1) * ($b-1)) }</lang> The precedence of each operator is specified as equivalent to an existing operator. We've taken the liberty of using an arrow for implication, to avoid confusing it with , (U+2283 SUPERSET OF).

To test, we use this code: <lang perl6>say '¬'; say "Too {¬Too}"; say "Moo {¬Moo}"; say "Foo {¬Foo}";

sub tbl (&op,$name) {

   say ;
   say "$name   Too Moo Foo";
   say "   ╔═══════════";
   say "Too║{op Too,Too} {op Too,Moo} {op Too,Foo}";
   say "Moo║{op Moo,Too} {op Moo,Moo} {op Moo,Foo}";
   say "Foo║{op Foo,Too} {op Foo,Moo} {op Foo,Foo}";

}

tbl(&infix:<∧>, '∧'); tbl(&infix:<∨>, '∨'); tbl(&infix:<→>, '→'); tbl(&infix:<≡>, '≡');

say ; say 'Precedence tests should all print "Too":'; say ~(

   Foo ∧ Too ∨ Too ≡ Too,
   Foo ∧ (Too ∨ Too) ≡ Foo,
   Too ∨ Too ∧ Foo ≡ Too,
   (Too ∨ Too) ∧ Foo ≡ Foo,
   ¬Too ∧ Too ∨ Too ≡ Too,
   ¬Too ∧ (Too ∨ Too) ≡ ¬Too,
   Too ∨ Too ∧ ¬Too ≡ Too,
   (Too ∨ Too) ∧ ¬Too ≡ ¬Too,

   Foo ∧ Too ∨ Foo → Foo ≡ Too,
   Foo ∧ Too ∨ Too → Foo ≡ Foo,

);</lang> Output:

¬
Too Foo
Moo Moo
Foo Too

∧   Too Moo Foo
   ╔═══════════
Too║Too Moo Foo
Moo║Moo Moo Foo
Foo║Foo Foo Foo

∨   Too Moo Foo
   ╔═══════════
Too║Too Too Too
Moo║Too Moo Moo
Foo║Too Moo Foo

→   Too Moo Foo
   ╔═══════════
Too║Too Moo Foo
Moo║Too Moo Moo
Foo║Too Too Too

≡   Too Moo Foo
   ╔═══════════
Too║Too Moo Foo
Moo║Moo Moo Moo
Foo║Foo Moo Too

Precedence tests should all print "Too":
Too Too Too Too Too Too Too Too Too Too

PicoLisp

In addition for the standard T (for "true") and NIL (for "false") we define 0 (zero, for "maybe"). <lang PicoLisp>(de 3not (A)

  (or (=0 A) (not A)) )

(de 3and (A B)

  (cond
     ((=T A) B)
     ((=0 A) (and B 0)) ) )

(de 3or (A B)

  (cond
     ((=T A) T)
     ((=0 A) (or (=T B) 0))
     (T B) ) )

(de 3impl (A B)

  (cond
     ((=T A) B)
     ((=0 A) (or (=T B) 0))
     (T T) ) )

(de 3equiv (A B)

  (cond
     ((=T A) B)
     ((=0 A) 0)
     (T (3not B)) ) )</lang>

Test: <lang PicoLisp>(for X '(T 0 NIL)

  (println 'not X '-> (3not X)) )

(for Fun '((and . 3and) (or . 3or) (implies . 3impl) (equivalent . 3equiv))

  (for X '(T 0 NIL)
     (for Y '(T 0 NIL)
        (println X (car Fun) Y '-> ((cdr Fun) X Y)) ) ) )</lang>

Output:

not T -> NIL
not 0 -> 0
not NIL -> T
T and T -> T
T and 0 -> 0
T and NIL -> NIL
0 and T -> 0
0 and 0 -> 0
0 and NIL -> NIL
NIL and T -> NIL
NIL and 0 -> NIL
NIL and NIL -> NIL
T or T -> T
T or 0 -> T
T or NIL -> T
0 or T -> T
0 or 0 -> 0
0 or NIL -> 0
NIL or T -> T
NIL or 0 -> 0
NIL or NIL -> NIL
T implies T -> T
T implies 0 -> 0
T implies NIL -> NIL
0 implies T -> T
0 implies 0 -> 0
0 implies NIL -> 0
NIL implies T -> T
NIL implies 0 -> T
NIL implies NIL -> T
T equivalent T -> T
T equivalent 0 -> 0
T equivalent NIL -> NIL
0 equivalent T -> 0
0 equivalent 0 -> 0
0 equivalent NIL -> 0
NIL equivalent T -> NIL
NIL equivalent 0 -> 0
NIL equivalent NIL -> T

Python

In Python, the keywords 'and', 'not', and 'or' are coerced to always work as boolean operators. I have therefore overloaded the boolean bitwise operators &, |, ^ to provide the required functionality. <lang python>class Trit(int):

   def __new__(cls, value):
       if value == 'TRUE':
           value = 1
       elif value == 'FALSE':
           value = 0
       elif value == 'MAYBE':
           value = -1
       return super(Trit, cls).__new__(cls, value // (abs(value) or 1)) 
   def __repr__(self):
       if self > 0:
           return 'TRUE'
       elif self == 0:
           return 'FALSE'
       return 'MAYBE'
   def __str__(self):
       return repr(self)
   def __bool__(self):
       if self > 0:
           return True
       elif self == 0:
           return False
       else:
           raise ValueError("invalid literal for bool(): '%s'" % self)
   def __or__(self, other):
       if isinstance(other, Trit):
           return _ttable[(self, other)][1]
       else:
           try:
               return _ttable[(self, Trit(bool(other)))][1]
           except:
               return NotImplemented
   def __ror__(self, other):
       if isinstance(other, Trit):
           return _ttable[(self, other)][1]
       else:
           try:
               return _ttable[(self, Trit(bool(other)))][1]
           except:
               return NotImplemented
   def __and__(self, other):
       if isinstance(other, Trit):
           return _ttable[(self, other)][0]
       else:
           try:
               return _ttable[(self, Trit(bool(other)))][0]
           except:
               return NotImplemented
   def __rand__(self, other):
       if isinstance(other, Trit):
           return _ttable[(self, other)][0]
       else:
           try:
               return _ttable[(self, Trit(bool(other)))][0]
           except:
               return NotImplemented
   def __xor__(self, other):
       if isinstance(other, Trit):
           return _ttable[(self, other)][2]
       else:
           try:
               return _ttable[(self, Trit(bool(other)))][2]
           except:
               return NotImplemented
   def __rxor__(self, other):
       if isinstance(other, Trit):
           return _ttable[(self, other)][2]
       else:
           try:
               return _ttable[(self, Trit(bool(other)))][2]
           except:
               return NotImplemented
   def __invert__(self):
       return _ttable[self]
   
   def __getattr__(self, name):
       if name in ('_n', 'flip'):
           # So you can do x._n == x.flip; the inverse of x
           # In Python 'not' is strictly boolean so we can't write `not x`
           # Same applies to keywords 'and' and 'or'.
           return _ttable[self]
       else:
           raise AttributeError 


TRUE, FALSE, MAYBE = Trit(1), Trit(0), Trit(-1)

_ttable = {

   #    A: -> flip_A
        TRUE: FALSE,
       FALSE:  TRUE,
       MAYBE: MAYBE,
   #     (A, B): -> (A_and_B, A_or_B, A_xor_B)
       (MAYBE, MAYBE): (MAYBE, MAYBE, MAYBE),
       (MAYBE, FALSE): (FALSE, MAYBE, MAYBE),
       (MAYBE,  TRUE): (MAYBE,  TRUE, MAYBE),
       (FALSE, MAYBE): (FALSE, MAYBE, MAYBE),
       (FALSE, FALSE): (FALSE, FALSE, FALSE),
       (FALSE,  TRUE): (FALSE,  TRUE,  TRUE),
       ( TRUE, MAYBE): (MAYBE,  TRUE, MAYBE),
       ( TRUE, FALSE): (FALSE,  TRUE,  TRUE),
       ( TRUE,  TRUE): ( TRUE,  TRUE, FALSE),
   }


values = ('FALSE', 'TRUE ', 'MAYBE')

print("\nTrit logical inverse, '~'") for a in values:

   expr = '~%s' % a
   print('  %s = %s' % (expr, eval(expr)))

for op, ophelp in (('&', 'and'), ('|', 'or'), ('^', 'exclusive-or')):

   print("\nTrit logical %s, '%s'" % (ophelp, op))
   for a in values:
       for b in values:
           expr = '%s %s %s' % (a, op, b)
           print('  %s = %s' % (expr, eval(expr)))</lang>
Output
Trit logical inverse, '~'
  ~FALSE = TRUE
  ~TRUE  = FALSE
  ~MAYBE = MAYBE

Trit logical and, '&'
  FALSE & FALSE = FALSE
  FALSE & TRUE  = FALSE
  FALSE & MAYBE = FALSE
  TRUE  & FALSE = FALSE
  TRUE  & TRUE  = TRUE
  TRUE  & MAYBE = MAYBE
  MAYBE & FALSE = FALSE
  MAYBE & TRUE  = MAYBE
  MAYBE & MAYBE = MAYBE

Trit logical or, '|'
  FALSE | FALSE = FALSE
  FALSE | TRUE  = TRUE
  FALSE | MAYBE = MAYBE
  TRUE  | FALSE = TRUE
  TRUE  | TRUE  = TRUE
  TRUE  | MAYBE = TRUE
  MAYBE | FALSE = MAYBE
  MAYBE | TRUE  = TRUE
  MAYBE | MAYBE = MAYBE

Trit logical exclusive-or, '^'
  FALSE ^ FALSE = FALSE
  FALSE ^ TRUE  = TRUE
  FALSE ^ MAYBE = MAYBE
  TRUE  ^ FALSE = TRUE
  TRUE  ^ TRUE  = FALSE
  TRUE  ^ MAYBE = MAYBE
  MAYBE ^ FALSE = MAYBE
  MAYBE ^ TRUE  = MAYBE
  MAYBE ^ MAYBE = MAYBE
Extra doodling in the Python shell
>>> values = (TRUE, FALSE, MAYBE)
>>> for a in values:
	for b in values:
		assert (a & ~b) | (b & ~a) == a ^ b

		
>>> 

Ruby

Ruby, like Smalltalk, has two boolean classes: TrueClass for true and FalseClass for false. We add a third class, MaybeClass for MAYBE, and define ternary logic for all three classes.

We keep !a, a & b and so on for binary logic. We add !a.trit, a.trit & b and so on for ternary logic. The code for !a.trit uses def !, which works with Ruby 1.9, but fails as a syntax error with Ruby 1.8.

Works with: Ruby version 1.9

<lang ruby># trit.rb - ternary logic

  1. http://rosettacode.org/wiki/Ternary_logic

require 'singleton'

  1. MAYBE, the only instance of MaybeClass, enables a system of ternary
  2. logic using TrueClass#trit, MaybeClass#trit and FalseClass#trit.
  3. !a.trit # ternary not
  4. a.trit & b # ternary and
  5. a.trit | b # ternary or
  6. a.trit ^ b # ternary exclusive or
  7. a.trit == b # ternary equal
  8. Though +true+ and +false+ are internal Ruby values, +MAYBE+ is not.
  9. Programs may want to assign +maybe = MAYBE+ in scopes that use
  10. ternary logic. Then programs can use +true+, +maybe+ and +false+.

class MaybeClass

 include Singleton
 #  maybe.to_s  # => "maybe"
 def to_s; "maybe"; end

end

MAYBE = MaybeClass.instance

class TrueClass

 TritMagic = Object.new
 class << TritMagic
   def index; 0; end
   def !; false; end
   def & other; other; end
   def | other; true; end
   def ^ other; [false, MAYBE, true][other.trit.index]; end
   def == other; other; end
 end
 # Performs ternary logic. See MaybeClass.
 #  !true.trit        # => false
 #  true.trit & obj   # => obj
 #  true.trit | obj   # => true
 #  true.trit ^ obj   # => false, maybe or true
 #  true.trit == obj  # => obj
 def trit; TritMagic; end

end

class MaybeClass

 TritMagic = Object.new
 class << TritMagic
   def index; 1; end
   def !; MAYBE; end
   def & other; [MAYBE, MAYBE, false][other.trit.index]; end
   def | other; [true, MAYBE, MAYBE][other.trit.index]; end
   def ^ other; MAYBE; end
   def == other; MAYBE; end
 end
 # Performs ternary logic. See MaybeClass.
 #  !maybe.trit        # => maybe
 #  maybe.trit & obj   # => maybe or false
 #  maybe.trit | obj   # => true or maybe
 #  maybe.trit ^ obj   # => maybe
 #  maybe.trit == obj  # => maybe
 def trit; TritMagic; end

end

class FalseClass

 TritMagic = Object.new
 class << TritMagic
   def index; 2; end
   def !; true; end
   def & other; false; end
   def | other; other; end
   def ^ other; other; end
   def == other; [false, MAYBE, true][other.trit.index]; end
 end
 # Performs ternary logic. See MaybeClass.
 #  !false.trit        # => true
 #  false.trit & obj   # => false
 #  false.trit | obj   # => obj
 #  false.trit ^ obj   # => obj
 #  false.trit == obj  # => false, maybe or true
 def trit; TritMagic; end

end</lang>

This IRB session shows ternary not, and, or, equal.

<lang ruby>$ irb irb(main):001:0> require './trit' => true irb(main):002:0> maybe = MAYBE => maybe irb(main):003:0> !true.trit => false irb(main):004:0> !maybe.trit => maybe irb(main):005:0> maybe.trit & false => false irb(main):006:0> maybe.trit | true => true irb(main):007:0> false.trit == true => false irb(main):008:0> false.trit == maybe => maybe</lang>

This program shows all 9 outcomes from a.trit ^ b.

<lang ruby>require 'trit' maybe = MAYBE

[true, maybe, false].each do |a|

 [true, maybe, false].each do |b|
   printf "%5s ^ %5s => %5s\n", a, b, a.trit ^ b
 end

end</lang>

$ ruby -I. trit-xor.rb
 true ^  true => false
 true ^ maybe => maybe
 true ^ false =>  true
maybe ^  true => maybe
maybe ^ maybe => maybe
maybe ^ false => maybe
false ^  true =>  true
false ^ maybe => maybe
false ^ false => false

Seed7

The type boolean does not define separate xor, implies and equiv operators. But there are replacements for them:

Instead of Use
p xor q p <> q
p implies q p <= q
p equiv q p = q

Since ternary logic needs xor, implies and equiv with a trit result they are introduced as the operators xor, -> and ==. The trit operators and and or are defined as short circuit operators. A short circuit operator evaluates the second parameter only when necessary. This is analogous to the boolean operators and and or, which use also short circuit evaluation.

<lang seed7>$ include "seed7_05.s7i";

const type: trit is new enum

   False, Maybe, True
 end enum;
  1. Enum types define comparisons (=, <, >, <=, >=, <>) and
  2. the conversions ord and conv.

const func string: str (in trit: aTrit) is

 return [] ("False", "Maybe", "True")[succ(ord(aTrit))];

enable_output(trit); # Allow writing trit values

const array trit: tritNot is [] (True, Maybe, False); const array array trit: tritAnd is [] (

   [] (False, False, False),
   [] (False, Maybe, Maybe),
   [] (False, Maybe, True ));

const array array trit: tritOr is [] (

   [] (False, Maybe, True ),
   [] (Maybe, Maybe, True ),
   [] (True,  True,  True ));

const array array trit: tritXor is [] (

   [] (False, Maybe, True ),
   [] (Maybe, Maybe, Maybe),
   [] (True,  Maybe, False));

const array array trit: tritImplies is [] (

   [] (True,  True,  True ),
   [] (Maybe, Maybe, True ),
   [] (False, Maybe, True ));

const array array trit: tritEquiv is [] (

   [] (True,  Maybe, False),
   [] (Maybe, Maybe, Maybe),
   [] (False, Maybe, True ));

const func trit: not (in trit: aTrit) is

 return tritNot[succ(ord(aTrit))];

const func trit: (in trit: aTrit1) and (in trit: aTrit2) is

 return tritAnd[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: (in trit: aTrit1) and (ref func trit: aTrit2) is func

 result
   var trit: res is False;
 begin
   if aTrit1 = True then
     res := aTrit2;
   elsif aTrit1 = Maybe and aTrit2 <> False then
     res := Maybe;
   end if;
 end func;

const func trit: (in trit: aTrit1) or (in trit: aTrit2) is

 return tritOr[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: (in trit: aTrit1) or (ref func trit: aTrit2) is func

 result
   var trit: res is True;
 begin
   if aTrit1 = False then
     res := aTrit2;
   elsif aTrit1 = Maybe and aTrit2 <> True then
     res := Maybe;
   end if;
 end func;

$ syntax expr: .().xor.() is -> 15; const func trit: (in trit: aTrit1) xor (in trit: aTrit2) is

 return tritImplies[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: (in trit: aTrit1) -> (in trit: aTrit2) is

 return tritImplies[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: (in trit: aTrit1) == (in trit: aTrit2) is

 return tritEquiv[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: rand (in trit: low, in trit: high) is

 return trit conv (rand(ord(low), ord(high)));

const func integer: compare (in trit: aTrit1, in trit: aTrit2) is

 return compare(ord(aTrit1), ord(aTrit2));

const func integer: hashCode (in trit: aTrit) is

 return hashCode(ord(aTrit));
  1. Begin of test code

var trit: operand1 is False; var trit: operand2 is False;

const proc: writeTable (ref func trit: tritExpr, in string: name) is func

 begin
   writeln;
   writeln(" " <& name rpad 7 <& " | False  Maybe  True");
   writeln("---------+---------------------");
   for operand1 range False to True do
     write(" " <& operand1 rpad 7 <& " | ");
     for operand2 range False to True do
       write(tritExpr rpad 7);
     end for;
     writeln;
   end for;
 end func;

const proc: main is func

 begin
   writeln(" not" rpad 8 <& " | False  Maybe  True");
   writeln("---------+---------------------");
   write("         | ");
   for operand1 range False to True do
     write(not operand1 rpad 7);
   end for;
   writeln;
   writeTable(operand1 and operand2, "and");
   writeTable(operand1 or operand2,  "or");
   writeTable(operand1 xor operand2, "xor");
   writeTable(operand1 -> operand2,  "->");
   writeTable(operand1 == operand2,  "==");
 end func;</lang>

Output:

 not     | False  Maybe  True
---------+---------------------
         | True   Maybe  False  

 and     | False  Maybe  True
---------+---------------------
 False   | False  False  False  
 Maybe   | False  Maybe  Maybe  
 True    | False  Maybe  True   

 or      | False  Maybe  True
---------+---------------------
 False   | False  Maybe  True   
 Maybe   | Maybe  Maybe  True   
 True    | True   True   True   

 xor     | False  Maybe  True
---------+---------------------
 False   | True   True   True   
 Maybe   | Maybe  Maybe  True   
 True    | False  Maybe  True   

 ->      | False  Maybe  True
---------+---------------------
 False   | True   True   True   
 Maybe   | Maybe  Maybe  True   
 True    | False  Maybe  True   

 ==      | False  Maybe  True
---------+---------------------
 False   | True   Maybe  False  
 Maybe   | Maybe  Maybe  Maybe  
 True    | False  Maybe  True   

Tcl

The simplest way of doing this is by constructing the operations as truth tables. The code below uses an abbreviated form of truth table. <lang tcl>package require Tcl 8.5 namespace eval ternary {

   # Code generator
   proc maketable {name count values} {

set sep "" for {set i 0; set c 97} {$i<$count} {incr i;incr c} { set v [format "%c" $c] lappend args $v; append key $sep "$" $v set sep "," } foreach row [split $values \n] { if {[llength $row]>1} { lassign $row from to lappend table $from [list return $to] } } proc $name $args \ [list ckargs $args]\;[concat [list switch -glob --] $key [list $table]] namespace export $name

   }
   # Helper command to check argument syntax
   proc ckargs argList {

foreach var $argList { upvar 1 $var v switch -exact -- $v { true - maybe - false { continue } default { return -level 2 -code error "bad ternary value \"$v\"" } } }

   }
   # The "truth" tables; “*” means “anything”
   maketable not 1 {

true false maybe maybe false true

   }
   maketable and 2 {

true,true true false,* false *,false false * maybe

   }
   maketable or 2 {

true,* true *,true true false,false false * maybe

   }
   maketable implies 2 {

false,* true *,true true true,false false * maybe

   }
   maketable equiv 2 {

*,maybe maybe maybe,* maybe true,true true false,false true * false

   }

}</lang> Demonstrating: <lang tcl>namespace import ternary::* puts "x /\\ y == x \\/ y" puts " x | y || result" puts "-------+-------++--------" foreach x {true maybe false} {

   foreach y {true maybe false} {

set z [equiv [and $x $y] [or $x $y]] puts [format " %-5s | %-5s || %-5s" $x $y $z]

   }

}</lang> Output:

x /\ y == x \/ y
 x     | y     || result
-------+-------++--------
 true  | true  || true 
 true  | maybe || maybe
 true  | false || false
 maybe | true  || maybe
 maybe | maybe || maybe
 maybe | false || maybe
 false | true  || false
 false | maybe || maybe
 false | false || true