1 /** N-State (Fuzzy Logic).
2 
3     Generalization of `bool` to three or more states.
4 
5     See_Also: https://en.wikipedia.org/wiki/Three-state_logic
6     See_Also: https://en.wikipedia.org/wiki/Four-valued_logic
7     See_Also: https://en.wikipedia.org/wiki/Many-valued_logic
8     See_Also: https://en.wikipedia.org/wiki/Three-valued_logic
9     See_Also: https://forum.dlang.org/post/l4gnrc$2glg$1@digitalmars.com
10  */
11 module nxt.nstate;
12 
13 /** Fuzzy logic State.
14  */
15 struct Fuzzy
16 {
17     @safe pure nothrow @nogc:
18 
19     enum defaultCode = 0;
20 
21     enum no       = make(defaultCode); // probability: 0
22     enum yes      = make(1);    // probability: 1
23     enum likely   = make(2);    // probability: > 1/2
24     enum unlikely = make(3);    // probability: < 1/2
25     enum unknown  = make(4);    // probability: any
26 
27     this(bool b)
28     {
29         _v = b ? yes._v : no._v;
30     }
31 
32     void opAssign(bool b)
33     {
34         _v = b ? yes._v : no._v;
35     }
36 
37     Fuzzy opUnary(string s)() if (s == "~")
38     {
39         final switch (_v)
40         {
41         case no._v: return yes;
42         case yes._v: return no;
43         case likely._v: return unlikely;
44         case unlikely._v: return likely;
45         }
46     }
47 
48     Fuzzy opBinary(string s)(Fuzzy rhs) if (s == "|")
49     {
50         import std.algorithm.comparion : among;
51         if (_v.among!(yes._v, no._v) &&
52 			rhs._v.among!(yes._v, no._v))
53             return _v | rhs._v;
54         else if (_v == yes._v ||
55 				 rhs._v == yes._v)
56             return yes;
57         else if (_v == no._v)
58             return rhs._v;
59         else if (rhs._v == no._v)
60             return _v;
61         else if (_v == rhs._v) // both likely or unlikely or unknown
62             return _v;
63         else
64             return unknown;
65     }
66 
67     // Fuzzy opBinary(string s)(Fuzzy rhs) if (s == "&")
68     // {
69     //     return make(_v & rhs._v);
70     // }
71 
72     // Fuzzy opBinary(string s)(Fuzzy rhs) if (s == "^")
73     // {
74     //     auto v = _v + rhs._v;
75     //     return v >= 4 ? unknown : make(!!v);
76     // }
77 
78 private:
79     ubyte _v = defaultCode;
80     static Fuzzy make(ubyte b)
81     {
82         Fuzzy r = void;
83         r._v = b;
84         return r;
85     }
86 }
87 
88 @safe pure nothrow @nogc unittest
89 {
90     alias T = Fuzzy;
91     T a;
92     assert(a == T.no);
93 
94     a = true;
95     assert(a == T.yes);
96 
97     a = T.likely;
98     assert(a == T.likely);
99 
100     a = T.unlikely;
101     assert(a == T.unlikely);
102 
103     with (T)
104     {
105         assert(~no == yes);
106         assert(no == ~yes);
107         assert(~unlikely == likely);
108         assert(unlikely == ~likely);
109     }
110 }
111 
112 /** State being either `yes`, `no` or `unknown`.
113  */
114 struct Tristate
115 {
116     @safe pure nothrow @nogc:
117 
118     enum defaultCode = 0;
119 
120     enum no      = make(defaultCode);
121     enum yes     = make(2);
122     enum unknown = make(6);
123 
124     this(bool b)
125     {
126         _v = b ? yes._v : no._v;
127     }
128 
129     void opAssign(bool b)
130     {
131         _v = b ? yes._v : no._v;
132     }
133 
134     Tristate opUnary(string s)() if (s == "~") => make((193 >> _v & 3) << 1);
135     Tristate opBinary(string s)(Tristate rhs) if (s == "|") => make((12756 >> (_v + rhs._v) & 3) << 1);
136     Tristate opBinary(string s)(Tristate rhs) if (s == "&") => make((13072 >> (_v + rhs._v) & 3) << 1);
137     Tristate opBinary(string s)(Tristate rhs) if (s == "^") => make((13252 >> (_v + rhs._v) & 3) << 1);
138 
139 private:
140     ubyte _v = defaultCode;
141     static Tristate make(ubyte b)
142     {
143         Tristate r = void;
144         r._v = b;
145         return r;
146     }
147 }
148 
149 @safe pure nothrow @nogc unittest
150 {
151     alias T = Tristate;
152     T a;
153     assert(a == T.no);
154     static assert(!is(typeof({ if (a) {} })));
155     assert(!is(typeof({ auto b = T(3); })));
156 
157     a = true;
158     assert(a == T.yes);
159 
160     a = false;
161     assert(a == T.no);
162 
163     a = T.unknown;
164     T b;
165 
166     b = a;
167     assert(b == a);
168 
169     auto c = a | b;
170     assert(c == T.unknown);
171     assert((a & b) == T.unknown);
172 
173     a = true;
174     assert(~a == T.no);
175 
176     a = true;
177     b = false;
178     assert((a ^ b) == T.yes);
179 
180     with (T)
181     {
182         // not
183         assert(~no == yes);
184         assert(~yes == no);
185         assert(~unknown == unknown);
186 
187         // or
188         assert((no | no) == no);
189         assert((no | yes) == yes);
190         assert((yes | no) == yes);
191         assert((yes | yes) == yes);
192         assert((no | unknown) == unknown);
193         assert((yes | unknown) == yes);
194         assert((unknown | no) == unknown);
195         assert((unknown | yes) == yes);
196         assert((unknown | unknown) == unknown);
197 
198         // and
199         assert((no & no) == no);
200         assert((no & yes) == no);
201         assert((yes & no) == no);
202         assert((yes & yes) == yes);
203         assert((no & unknown) == no);
204         assert((unknown & no) == no);
205         assert((unknown & unknown) == unknown);
206         assert((yes & unknown) == unknown);
207         assert((unknown & yes) == unknown);
208 
209         // exclusive or
210         assert((yes ^ yes) == no);
211         assert((no ^ no) == no);
212         assert((no ^ yes) == yes);
213         assert((yes ^ no) == yes);
214         assert((no ^ unknown) == unknown);
215         assert((yes ^ unknown) == unknown);
216         assert((unknown ^ no) == unknown);
217         assert((unknown ^ yes) == unknown);
218         assert((unknown ^ unknown) == unknown);
219     }
220 }
221 
222 /** Tristate: Three-state logic.
223 */
224 struct TristateCond
225 {
226     @safe pure nothrow @nogc:
227 
228     enum defaultCode = 0;
229 
230     enum no      = make(defaultCode);
231     enum yes     = make(1);
232     enum unknown = make(4);
233 
234     this(bool b)
235     {
236         _v = b ? yes._v : no._v;
237     }
238 
239     void opAssign(bool b)
240     {
241         _v = b ? yes._v : no._v;
242     }
243 
244     TristateCond opUnary(string s)() if (s == "~")
245 		=> this == unknown ? this : make(!_v);
246 
247     TristateCond opBinary(string s)(TristateCond rhs) if (s == "|")
248     {
249         // | yields 0, 1, 4, 5
250         auto v = _v | rhs._v;
251         return v == 4 ? unknown : make(v & 1);
252     }
253 
254     TristateCond opBinary(string s)(TristateCond rhs) if (s == "&")
255 		=> make(_v & rhs._v); // & yields 0, 1, 4
256 
257     TristateCond opBinary(string s)(TristateCond rhs) if (s == "^")
258     {
259         // + yields 0, 1, 2, 4, 5, 8
260         auto v = _v + rhs._v;
261         return v >= 4 ? unknown : make(!!v);
262     }
263 
264 private:
265     ubyte _v = defaultCode;
266     static TristateCond make(ubyte b)
267     {
268         TristateCond r = void;
269         r._v = b;
270         return r;
271     }
272 }
273 
274 @safe pure nothrow @nogc unittest
275 {
276     TristateCond a;
277     assert(a == TristateCond.no);
278     static assert(!is(typeof({ if (a) {} })));
279     assert(!is(typeof({ auto b = TristateCond(3); })));
280     a = true;
281     assert(a == TristateCond.yes);
282     a = false;
283     assert(a == TristateCond.no);
284     a = TristateCond.unknown;
285     TristateCond b;
286     b = a;
287     assert(b == a);
288     auto c = a | b;
289     assert(c == TristateCond.unknown);
290     assert((a & b) == TristateCond.unknown);
291     a = true;
292     assert(~a == TristateCond.no);
293     a = true;
294     b = false;
295     assert((a ^ b) == TristateCond.yes);
296 }