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