1 // Written in the D programming language
2 
3 /**
4  * Decimal logical functions.
5  *
6  * An implementation of the
7  * General Decimal Arithmetic Specification.
8  *
9  * Authors: Paul D. Anderson
10  *
11  * Copyright: Copyright 2009-2016 by Paul D. Anderson.
12  *
13  * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>
14  *
15  * Standards: Conforms to the
16  *	General Decimal Arithmetic Specification,
17  *	Version 1.70, (25 March 2009).
18  */
19 
20 /*
21 	TODO: For each function --
22 		1. Ensure algorithm is properly implemented.
23 		2. Ensure context flags are being set properly.
24 		3. Ensure function passes GDA tests.
25 		4. Determine additional tests needed and implement them.
26 		5. Ensure all special cases are enumerated.
27 		6. Automate all tests, if possible.
28 		7. Determine what effect the context has on the function and
29 			if it should be explained.
30 		8. Ensure all documentation is complete:
31 			a. Header - description, inputs, return value(s)
32 			b. Code - variables, control statements, branches, return points.
33 		9. Move most tests to the test module.
34 */
35 
36 module eris.decimal.logical;
37 
38 import std..string;
39 import std.math;
40 
41 import eris.decimal;
42 
43 unittest {
44 	writeln("      logical tests       ");
45 	writeln("==========================");
46 }
47 
48 // temporary import
49 import std.stdio;
50 
51 version(unittest)
52 {
53 	import std.stdio;
54 	import eris.decimal.test;
55 }
56 
57 //--------------------------------
58 // logical operations
59 //--------------------------------
60 
61 /**
62  *  Returns true if the argument is a valid logical decimal number.
63  *  The sign and exponent must both be zero, and all decimal digits
64  *  in the coefficient must be either '1' or '0'.
65  */
66 public bool isLogical(D)(in D arg, out string str) if (isDecimal!D)
67 {
68 	if (arg.sign != 0 || arg.expo != 0) return false;
69 	str = std..string.format("%d", arg.coff);
70 	size_t n = str.length;
71 	int p = D.precision;
72 	// if length > precision, truncate left chars
73 	if (n > p)
74 	{
75 		str = str[n-p..n];
76 	}
77 	// if length < precision, pad left with zeros
78 	if (n < p)
79 	{
80 		str = rightJustify(str, p, '0');
81 	}
82 	foreach (char ch; str) {
83 		if (ch != '0' && ch != '1') return false;
84 	}
85 	return true;
86 }
87 
88 /**
89  *  Returns true if the argument is a valid logical decimal number.
90  *  The sign and exponent must both be zero, and all decimal digits
91  *  in the coefficient must be either '1' or '0'.
92  */
93 public bool isLogical(D)(in D arg) if (isDecimal!D)
94 {
95 	string str;
96 	return isLogical!D(arg, str);
97 }
98 
99 unittest {	// logical string/number tests
100  	write("-- logical tests....");
101 	assertTrue(isLogical(TD("010101110101")));
102 	assertTrue(isLogical(TD("1011101")));
103 	writeln("passed");
104 }
105 
106 //--------------------------------
107 // unary logical operations
108 //--------------------------------
109 
110 /**
111  *  Inverts and returns a decimal logical number.
112  *  Implements the 'invert' function in the specification. (p. 44)
113  */
114 public D not(D)(D arg) if (isDecimal!D)
115 {
116 	string str;
117 	if (!isLogical(arg, str)) {
118 		return invalidOperation!D;
119 	}
120 	char[] result = new char[str.length];
121 	for (size_t i = 0; i < str.length; i++)
122 	{
123 		result[i] = str[i] == '0' ? '1' : '0';
124 	}
125 	return D(result.idup);
126 }
127 
128 /**
129  *  Inverts and returns a logical string.
130  *  Each '1' is changed to a '0', and vice versa.
131  */
132 private S not(S: string)(S str)
133 {
134 	char[] result = new char[str.length];
135 	for (size_t i = 0; i < str.length; i++)
136 	{
137 		result[i] = str[i] == '0' ? '1' : '0';
138 	}
139 	return result.idup;
140 }
141 
142 unittest
143 {	// inverse
144 	static struct S { TD num; TD expect; }
145 	S[] s =
146 	[
147 /*		{ "101001",	"10110" },
148 		{ "1",		"0" },
149 		{ "0",		"1" },*/
150 		{ "101001",	"1111111111010110" },
151 		{ "1",		"1111111111111110" },
152 		{ "0",		"1111111111111111" },
153 	];
154 	auto f = FunctionTest!(S,TD)("not");
155 	foreach (t; s) f.test(t, not!TD(t.num));
156     writefln(f.report);
157 }
158 
159 //--------------------------------
160 // binary logical operations
161 //--------------------------------
162 
163 /**
164  *  Performs a logical and of the arguments and returns the result.
165  *  Implements the 'and' function in the specification. (p. 41)
166  */
167 public D and(D)(in D x, in D y) if (isDecimal!D)
168 {
169 	string xstr;
170 	if (!isLogical(x, xstr)) {
171 		return invalidOperation!D;
172 	}
173 	string ystr;
174 	if (!isLogical(y, ystr)) {
175 		return invalidOperation!D;
176 	}
177 	auto len = xstr.length;
178 	char[] result = new char[len];
179 	for (size_t i = 0; i < len; i++)
180 	{
181 		if (xstr[i] == '1' && ystr[i] == '1') {
182 			result[i] = '1';
183 		} else {
184 			result[i] = '0';
185 		}
186 	}
187 	return D(result.idup);
188 }
189 
190 /**
191  *  Performs a logical or of the arguments and returns the result.
192  *  Implements the 'or' function in the specification. (p. 41)
193  */
194 public D or(D)(in D x, in D y) if (isDecimal!D)
195 {
196 	string xstr;
197 	if (!isLogical(x, xstr)) {
198 		return invalidOperation!D;
199 	}
200 	string ystr;
201 	if (!isLogical(y, ystr)) {
202 		return invalidOperation!D;
203 	}
204 	auto len = xstr.length;
205 	char[] result = new char[len];
206 	for (size_t i = 0; i < len; i++)
207 	{
208 		if (xstr[i] == '1' || ystr[i] == '1') {
209 			result[i] = '1';
210 		} else {
211 			result[i] = '0';
212 		}
213 	}
214 	return D(result.idup);
215 }
216 
217 /**
218  *  Performs a logical xor of the arguments and returns the result.
219  *  Implements the 'xor' function in the specification. (p. 41)
220  */
221 public D xor(D)(in D x, in D y) if (isDecimal!D)
222 {
223 	string xstr;
224 	if (!isLogical(x, xstr)) {
225 		return invalidOperation!D;
226 	}
227 	string ystr;
228 	if (!isLogical(y, ystr)) {
229 		return invalidOperation!D;
230 	}
231 	auto len = xstr.length;
232 	char[] result = new char[len];
233 	for (size_t i = 0; i < len; i++)
234 	{
235 		if (xstr[i] != ystr[i]) {
236 			result[i] = '1';
237 		} else {
238 			result[i] = '0';
239 		}
240 	}
241 	return D(result.idup);
242 }
243 
244 unittest { // binary logical ops
245 	TD op1, op2;
246 	op1 = 10010101;
247 	op2 = 11100100;
248 	assert(and(op1, op2) == TD(10000100));
249 	assert(or(op1, op2)  == TD(11110101));
250 	assert(xor(op1, op2) == TD( 1110001));
251 	op1 =   100101;
252 	op2 = 11100100;
253 //	assert((op1 & op2) == TD(  100100));
254 //	assert((op1 | op2) == TD(11100101));
255 //	assert((op1 ^ op2) == TD(11000001));
256 }
257 
258 //--------------------------------
259 // logical shift and rotate
260 //--------------------------------
261 
262 /// Performs a logical shift of the argument by the specified number of
263 /// decimal digits. Positive values of the second operand shift the argument
264 ///  to the left; negative values shift the argument to the right.
265 /// If the argument is not a valid logical operand, or if the absolute value of
266 /// the shift is greater than precision, an INVALID_OPERATION is signaled.
267 /// Implements the 'shift' function in the specification. (p. 49)
268 public D lsh(D)(in D arg, int n,
269 		Context context = D.context) if (isDecimal!D)
270 {
271 	string str;
272 	int p = context.precision;
273 
274 	if (n > p || n < -p || !isLogical(arg, str))
275 	{
276 		return invalidOperation!D;
277 	}
278 
279 	if (n == 0)
280 	{
281 	 	return arg;
282 	}
283 
284 	if (n > 0)
285 	{
286 		str = str[n..$];
287 		str = leftJustify(str, p, '0');
288 		return D(str);
289 	}
290 	else
291 	{
292 		str = str[0..$+n];
293 		str = rightJustify(str, p, '0');
294 		return D(str);
295 	}
296 }
297 
298 unittest
299 {	// shift  -- depends on context
300 	static struct S { TD x; int n; TD expect; }
301 	S[] s =
302 	[
303 		{ 111, 8, 11100000000 },
304 		{ 11111, -3, 11 },
305 		{ 1101, 0, 1101 },
306 		{ 1101, -16, 0 },
307 		{ 1101, -17, "NaN" },
308 	];
309 	auto f = FunctionTest!(S,TD)("lsh");
310 	foreach (t; s) f.test(t, lsh(t.x, t.n));
311     writefln(f.report);
312 }
313 
314 /// Rotates the argument by the specified number of decimal digits.
315 /// Positive values of the second operand rotate the argument to the left;
316 /// negative values rotate the argument to the right. If the argument
317 /// is not a valid logical operand, or if the absolute value of the rotation
318 /// is greater than precision, an INVALID_OPERATION is signaled.
319 /// Implements the 'rotate' function in the specification. (p. 47)
320 public D rot(D)(in D arg, int n,
321 		Context context = D.context) if (isDecimal!D)
322 {
323 	string str;
324 	int p = context.precision;
325 	if (n > p || n < -p || !isLogical(arg, str))
326 	{
327 		return invalidOperation!D;
328 	}
329 
330 	if (n == 0)
331 	{
332 	 	return arg;
333 	}
334 
335 	if (n > 0)
336 	{
337 		string str1 = str[n..$];
338 		string str2 = str[0..n];
339 		return D(str1 ~ str2);
340 	}
341 	else
342 	{
343 		string str1 = str[$+n..$];
344 		string str2 = str[0..$+n];
345 		return D(str1 ~ str2);
346 	}
347 }
348 
349 unittest
350 {	// shift  -- depends on context
351 	static struct S { TD x; int n; TD expect; }
352 	S[] s =
353 	[
354 		{ 1, 1, 10 },
355 		{ 1011001110001111, 4, 11100011111011 },
356 		{ 11111, -3, 1110000000000011 },
357 		{ 1101, 0, 1101 },
358 	];
359 	auto f = FunctionTest!(S,TD)("rot");
360 	foreach (t; s) f.test(t, rot(t.x, t.n));
361     writefln(f.report);
362 }
363 
364 
365 unittest
366 {
367 	writeln("==========================");
368 }
369 
370