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