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) && rhs._v.among!(yes._v, no._v))
52         {
53             return _v | rhs._v;
54         }
55         else if (_v == yes._v || rhs._v == yes._v)
56         {
57             return yes;
58         }
59         else if (_v == no._v)
60         {
61             return rhs._v;
62         }
63         else if (rhs._v == no._v)
64         {
65             return _v;
66         }
67         else if (_v == rhs._v) // both likely or unlikely or unknown
68         {
69             return _v;
70         }
71         else
72         {
73             return unknown;
74         }
75     }
76 
77     // Fuzzy opBinary(string s)(Fuzzy rhs) if (s == "&")
78     // {
79     //     return make(_v & rhs._v);
80     // }
81 
82     // Fuzzy opBinary(string s)(Fuzzy rhs) if (s == "^")
83     // {
84     //     auto v = _v + rhs._v;
85     //     return v >= 4 ? unknown : make(!!v);
86     // }
87 
88 private:
89     ubyte _v = defaultCode;
90     static Fuzzy make(ubyte b)
91     {
92         Fuzzy r = void;
93         r._v = b;
94         return r;
95     }
96 }
97 
98 @safe pure nothrow @nogc unittest
99 {
100     alias T = Fuzzy;
101     T a;
102     assert(a == T.no);
103 
104     a = true;
105     assert(a == T.yes);
106 
107     a = T.likely;
108     assert(a == T.likely);
109 
110     a = T.unlikely;
111     assert(a == T.unlikely);
112 
113     with (T)
114     {
115         assert(~no == yes);
116         assert(no == ~yes);
117         assert(~unlikely == likely);
118         assert(unlikely == ~likely);
119     }
120 }
121 
122 /** State being either `yes`, `no` or `unknown`.
123  */
124 struct Tristate
125 {
126     @safe pure nothrow @nogc:
127 
128     enum defaultCode = 0;
129 
130     enum no      = make(defaultCode);
131     enum yes     = make(2);
132     enum unknown = make(6);
133 
134     this(bool b)
135     {
136         _v = b ? yes._v : no._v;
137     }
138 
139     void opAssign(bool b)
140     {
141         _v = b ? yes._v : no._v;
142     }
143 
144     Tristate opUnary(string s)() if (s == "~")
145     {
146         return make((193 >> _v & 3) << 1);
147     }
148 
149     Tristate opBinary(string s)(Tristate rhs) if (s == "|")
150     {
151         return make((12756 >> (_v + rhs._v) & 3) << 1);
152     }
153 
154     Tristate opBinary(string s)(Tristate rhs) if (s == "&")
155     {
156         return make((13072 >> (_v + rhs._v) & 3) << 1);
157     }
158 
159     Tristate opBinary(string s)(Tristate rhs) if (s == "^")
160     {
161         return make((13252 >> (_v + rhs._v) & 3) << 1);
162     }
163 
164 private:
165     ubyte _v = defaultCode;
166     static Tristate make(ubyte b)
167     {
168         Tristate r = void;
169         r._v = b;
170         return r;
171     }
172 }
173 
174 @safe pure nothrow @nogc unittest
175 {
176     alias T = Tristate;
177     T a;
178     assert(a == T.no);
179     static assert(!is(typeof({ if (a) {} })));
180     assert(!is(typeof({ auto b = T(3); })));
181 
182     a = true;
183     assert(a == T.yes);
184 
185     a = false;
186     assert(a == T.no);
187 
188     a = T.unknown;
189     T b;
190 
191     b = a;
192     assert(b == a);
193 
194     auto c = a | b;
195     assert(c == T.unknown);
196     assert((a & b) == T.unknown);
197 
198     a = true;
199     assert(~a == T.no);
200 
201     a = true;
202     b = false;
203     assert((a ^ b) == T.yes);
204 
205     with (T)
206     {
207         // not
208         assert(~no == yes);
209         assert(~yes == no);
210         assert(~unknown == unknown);
211 
212         // or
213         assert((no | no) == no);
214         assert((no | yes) == yes);
215         assert((yes | no) == yes);
216         assert((yes | yes) == yes);
217         assert((no | unknown) == unknown);
218         assert((yes | unknown) == yes);
219         assert((unknown | no) == unknown);
220         assert((unknown | yes) == yes);
221         assert((unknown | unknown) == unknown);
222 
223         // and
224         assert((no & no) == no);
225         assert((no & yes) == no);
226         assert((yes & no) == no);
227         assert((yes & yes) == yes);
228         assert((no & unknown) == no);
229         assert((unknown & no) == no);
230         assert((unknown & unknown) == unknown);
231         assert((yes & unknown) == unknown);
232         assert((unknown & yes) == unknown);
233 
234         // exclusive or
235         assert((yes ^ yes) == no);
236         assert((no ^ no) == no);
237         assert((no ^ yes) == yes);
238         assert((yes ^ no) == yes);
239         assert((no ^ unknown) == unknown);
240         assert((yes ^ unknown) == unknown);
241         assert((unknown ^ no) == unknown);
242         assert((unknown ^ yes) == unknown);
243         assert((unknown ^ unknown) == unknown);
244     }
245 }
246 
247 /** Tristate: Three-state logic.
248 */
249 struct TristateCond
250 {
251     @safe pure nothrow @nogc:
252 
253     enum defaultCode = 0;
254 
255     enum no      = make(defaultCode);
256     enum yes     = make(1);
257     enum unknown = make(4);
258 
259     this(bool b)
260     {
261         _v = b ? yes._v : no._v;
262     }
263 
264     void opAssign(bool b)
265     {
266         _v = b ? yes._v : no._v;
267     }
268 
269     TristateCond opUnary(string s)() if (s == "~")
270     {
271         return this == unknown ? this : make(!_v);
272     }
273 
274     TristateCond opBinary(string s)(TristateCond rhs) if (s == "|")
275     {
276         // | yields 0, 1, 4, 5
277         auto v = _v | rhs._v;
278         return v == 4 ? unknown : make(v & 1);
279     }
280 
281     TristateCond opBinary(string s)(TristateCond rhs) if (s == "&")
282     {
283         // & yields 0, 1, 4
284         return make(_v & rhs._v);
285     }
286 
287     TristateCond opBinary(string s)(TristateCond rhs) if (s == "^")
288     {
289         // + yields 0, 1, 2, 4, 5, 8
290         auto v = _v + rhs._v;
291         return v >= 4 ? unknown : make(!!v);
292     }
293 
294 private:
295     ubyte _v = defaultCode;
296     static TristateCond make(ubyte b)
297     {
298         TristateCond r = void;
299         r._v = b;
300         return r;
301     }
302 }
303 
304 @safe pure nothrow @nogc unittest
305 {
306     TristateCond a;
307     assert(a == TristateCond.no);
308     static assert(!is(typeof({ if (a) {} })));
309     assert(!is(typeof({ auto b = TristateCond(3); })));
310     a = true;
311     assert(a == TristateCond.yes);
312     a = false;
313     assert(a == TristateCond.no);
314     a = TristateCond.unknown;
315     TristateCond b;
316     b = a;
317     assert(b == a);
318     auto c = a | b;
319     assert(c == TristateCond.unknown);
320     assert((a & b) == TristateCond.unknown);
321     a = true;
322     assert(~a == TristateCond.no);
323     a = true;
324     b = false;
325     assert((a ^ b) == TristateCond.yes);
326 }