# HG changeset patch # User Paul (paul.d.anderson@comcast.net) # Date 1269146302 25200 # Node ID c021ed211f892c81560f815a95de59aa26173544 # Parent b37c218c1442d49f049e7e4c5a64e7cbcc0ad974 bcd add, sub, multiply, logic and shift operations diff -r b37c218c1442 -r c021ed211f89 src/decimal/bcd.d --- a/src/decimal/bcd.d Thu Mar 18 18:10:25 2010 -0700 +++ b/src/decimal/bcd.d Sat Mar 20 21:38:22 2010 -0700 @@ -32,6 +32,7 @@ module decimal.bcd; +import std.algorithm: max; import std.conv: ConvError; import std.math; import std.stdio: write, writeln; @@ -39,74 +40,202 @@ alias ubyte Digit; -public static immutable Bcd ZERO = { digits:[0] }; -public static immutable Bcd ONE = { digits:[1] }; -public static immutable Bcd NEG_ONE = { sign:true, digits:[1] }; +public immutable Bcd ZERO = { digits:[0] }; +public immutable Bcd ONE = { digits:[1] }; +public immutable Bcd NEG_ONE = { sign:true, digits:[1] }; +/** + * Provides BCD-encoded integral values of arbitrary length. + * + * Advantages of BCD: + * To/from string is easy. + * Easy to pull values of individual digits. + * Easy to multiply and divide by powers of 10. + * + * Disadvantages of BCD: + * Significantly less compact representation. + * Operations are carried out bytewise, requiring more iterations. + * Extra steps required for addition. + * + */ public struct Bcd { - // members +//-------------------------------- +// members +//-------------------------------- + + /** + * The sign of the BCD integer. Sign is kept explicitly + * and not encoded in the number. + **/ private bool sign = false; + + /** + * An array of decimal digits in reverse (right-to-left) order + * representing an integral value. + * Trailing zeros have no effect on the value of the number; + * they correspond to leading zeros in a left-to-right representation. + **/ private Digit[] digits = [ 0 ]; - // constructors +//-------------------------------- +// construction +//-------------------------------- + + //-------------------------------- + // copy construction + //-------------------------------- + + + /** + * Copy constructor. Returns a (non-const) copy of the argument. + */ public this(const Bcd bcd) { - this = bcd; + sign = bcd.sign; + digits = bcd.digits.dup; } + /** + * Copies a BCD integer and adjusts the number of digits, + * padding or turncating, if necessary. + */ public this(const Bcd bcd, uint numDigits) { - this = bcd; + this(bcd); setNumDigits(numDigits); } - public this(const string str) { - this = str; - } - + //-------------------------------- + // construction from integer types. + //-------------------------------- + + /** + * Constructs a BCD integer from a (signed) byte, short, int or long value. + */ public this(const long n) { - this = n; + ulong m; + if (n < 0) { + sign = true; + m = std.math.abs(n); + } + else { + sign = false; + m = n; + } + Digit[20] dig; + int i = 0; + do { + ulong q = m/10; + ulong r = m%10; + dig[i] = cast(Digit) r; + m = q; + i++; + } while (m > 0); + digits = dig[0..i].dup; } + /** + * Constructs a BCD integer from a (signed) byte, short, + * int or long value, and then adjusts the number of digits, + * padding or truncating as needed. + */ public this(const long n, uint numDigits) { this = n; setNumDigits(numDigits); } - const string toString() { - return format(this); + unittest { + write("this(long).."); + Bcd p; + p = -1234; + assert(p == Bcd("-1234")); + p = 12345678L; + assert(p == Bcd("12345678")); + p = Bcd(-19166586400L); + assert(p == Bcd("-19166586400")); + p = Bcd(139676498390L); + assert(p == Bcd("139676498390")); + writeln("passed"); + } + + //-------------------------------- + // construction from a string. + //-------------------------------- + + /** + * Constructs a BCD integer from a string representation. + * If the string has leading zeros they are retained internally. + */ + public this(const string str) { + this = parse(str); } - const hash_t toHash() { - hash_t hash = 0; - foreach(Digit digit; digits) { - hash += digit; - } - return hash; +//-------------------------------- +// member access functions +//-------------------------------- + + //-------------------------------- + // access to the sign of the number + //-------------------------------- + + const bool isSigned() { + return sign; } - // access methods + void setSign(bool sign) { + this.sign = sign; + } + + //-------------------------------- + // access to the digits + //-------------------------------- + + /** + * Returns the number of digits in this BCD integer. + * Includes leading zero digits. + */ const uint numDigits() { return digits.length; } + /** + * Adjusts the number of digits in this BCD integer, + * padding or truncating if necessary. + */ void setNumDigits(uint n) { digits.length = n; } + /** + * Returns the first digit of this BCD integer. If the + * integer has leading zeros this function will return + * zero, but the value of the number is not necessarily zero. + */ const uint firstDigit() { return digits[$-1]; } + /** + * Returns the last digit of this BCD integer. + */ const uint lastDigit() { return digits[0]; } - void setDigit(uint n, Digit value) { - digits[$-n-1] = value; + /** + * Returns the specified digit of this BCD integer. The index is + * zero based, i.e., getDigit(0) returns the first digit. + */ + const int getDigit(int n) { + return digits[$-n-1]; } - const int getDigit(int n) { - return digits[$-n-1]; + /** + * Sets the specified digit of this BCD integer to the specified value. + * The index is zero based, i.e., getDigit(0) returns the first digit. + */ + void setDigit(uint n, Digit value) { + assert(value < 10); + digits[$-n-1] = value; } unittest { @@ -125,38 +254,103 @@ bcd.setNumDigits(5); assert(bcd.getDigit(2) == 6); // writeln("bcd = ", bcd); - writeln("passed!"); + writeln("passed"); + } + +//-------------------------------- +// Common object functions. +//-------------------------------- + + /** + * Returns a string representation of this BCD integer. + * Leading zeros are displayed. The sign is displayed if + * the number is negative. + */ + const string toString() { + return format(this); + } + + unittest { + write("toString..."); + writeln("passed"); + } + + /** + * Returns a hash code for this BCD integer. + */ + const hash_t toHash() { + hash_t hash = 0; + foreach(Digit digit; digits) { + hash += digit; + } + return hash; } - const bool isSigned() { - return sign; + unittest { + write("toHash..."); + writeln("passed"); + } + + /** + * Returns a mutable copy of this BCD integer. + */ + const Bcd dup() { + Bcd bcd; + bcd.sign = this.sign; + bcd.digits = digits.dup; + return bcd; } +//-------------------------------- +// Utility functions. +//-------------------------------- + + /** + * Returns true if the internal representation of + * this BCD integer has leading zeros. + */ + const bool hasLeadingZeros() { + return numDigits > 0 && firstDigit == 0; + } + + /** + * Returns true if the value of this BCD integer is zero. + * Ignores and leading zeros and the sign of the number. + */ const bool isZero() { - writeln("stripping"); Bcd temp = stripLeadingZeros(this); - writeln("stripped"); - writeln("temp = ", temp); if (temp.numDigits > 1) return false; return temp.lastDigit == 0; } - const bool hasLeadingZeros() { - return numDigits > 0 && firstDigit == 0; +//-------------------------------- +// Assignment operators. +//-------------------------------- + + /** + * Assignment operator for BCD integer to BCD integer operations. + */ + Bcd opAssign(const Bcd that) { + this.sign = that.sign; + this.digits = that.digits.dup; + return this; } - void opAssign(const Bcd that) { - this.sign = that.sign; - this.digits.length = that.digits.length; - this.digits[] = that.digits[]; + /** + * Converts the string argument into a BCD integer and assigns it + * to this BCD integer. + */ + void opAssign(const string str) { + this = Bcd(str); } - void opAssign(const string str) { - this = parse(str); - } - + /** + * Converts the integer argument into a BCD integer and assigns it + * to this BCD integer. + */ void opAssign(const long n) { - uint m; + this = Bcd(n); +/+ uint m; if (n < 0) { sign = true; m = std.math.abs(n); @@ -174,7 +368,7 @@ m = q; i++; } while (m > 0); - digits = dig[0..i].dup; + digits = dig[0..i].dup;+/ } @@ -214,81 +408,184 @@ } return result; }+/ + +//-------------------------------- +// unary operators +//-------------------------------- - const Bcd dup() { - Bcd bcd; - bcd.sign = this.sign; - bcd.digits.length = this.digits.length; - bcd.digits[] = this.digits[]; - return bcd; - } - - // TODO: always uncertain how this is done...will it make an unnecessary copy? const Bcd opPos() { return this.dup; } const Bcd opNeg() { - return negate(this); + Bcd copy = this.dup; + copy.sign = !this.sign; + return copy; } const Bcd opCom() { - return complement(this); + return twosComp(this); } const Bcd opNot() { return not(this); } - const Bcd opAnd(const Bcd bcd) { - return and(this, bcd); + Bcd opPostInc() { + return this + ONE; + } + + Bcd opPostDec() { + return this - ONE; + } + +//-------------------------------- +// binary operators +//-------------------------------- + + const Bcd opAdd(T:Bcd)(const T addend) { + return add(this, addend); + } + + const Bcd opAdd(T)(const T addend) { + return add(this, Bcd(addend)); } - const Bcd opOr(const Bcd bcd) { - return or(this, bcd); + Bcd opAddAssign(T)(const T addend) { + this = this + addend; + return this; + } + + const Bcd opSub(T:Bcd)(const T addend) { + return subtract(this, addend); } - const Bcd opXor(const Bcd bcd) { - return xor(this, bcd); + const Bcd opSub(T)(const T addend) { + return subtract(this, Bcd(addend)); + } + + Bcd opSubAssign(T)(const T addend) { + this = this - addend; + return this; + } + + const Bcd opMul(T:Bcd)(const T b) { + return multiply(this, b); + } + + const Bcd opMul(T)(const T b) { + return multiply(this, Bcd(b)); } - // TODO: what's the matter with the one-digit numbers?? + Bcd opMulAssign(T)(const T b) { + this = this * b; + return this; + } + +unittest { + write("multiply..."); + Bcd a, b, c; + a = 105, b = -22; + c = a * b; + assert(c == -2310); + c = 45; + c *= 1205; +// writeln("c = ", c); + assert(c == 54225); + writeln("passed"); +} + + const Bcd opAnd(const Bcd a) { + return and(this, a); + } + + Bcd opAndAssign(const Bcd a) { + this = this & a; + return this; + } + + const Bcd opOr(const Bcd a) { + return or(this, a); + } + + Bcd opOrAssign(const Bcd a) { + this = this | a; + return this; + } + + const Bcd opXor(const Bcd a) { + return xor(this, a); + } + + Bcd opXorAssign(const Bcd a) { + this = this ^ a; + return this; + } + +//-------------------------------- +// comparison, equality operators +//-------------------------------- + + const int opCmp(T:Bcd)(const T that) { + return compare(this, that); + } + + unittest { + write("compare..."); + Bcd a,b; + a = 100; + b = 5; + assert(a > b); + a = 5; + b = 100; + assert(a < b); + assert(b > a); + writeln("passed"); + } + + const int opCmp(T)(const T that) { + return opCmp(Bcd(that)); + } + const bool opEquals(T:Bcd)(const T that) { -// writeln("equating"); - if (this.sign != that.sign) return false; // what about +/- zero? -// writeln("same signs"); - Bcd thisOne = stripLeadingZeros(this); -// writeln("this = ", thisOne); - Bcd thatOne = stripLeadingZeros(that); -// writeln("that = ", thatOne); - if (thisOne.digits.length != thatOne.digits.length) return false; -// writeln("length = ", thisOne.digits.length); -// foreach(int i, Digit digit; thisOne) { // todo: have to make opApply here. -// } - foreach_reverse(int i, Digit digit; thisOne.digits) { -/+ writeln("i = ", i); - writeln("this[i] = ", thisOne.digits[i]); - writeln("that[i] = ", thatOne.digits[i]); - writeln("this = ", thisOne.digits); - writeln("that = ", thatOne.digits);+/ - if (digit != thatOne.digits[i]) return false; + Bcd a = stripLeadingZeros(this); + Bcd b = stripLeadingZeros(that); + if (this.sign != that.sign) return false; + if (a.digits.length != b.digits.length) return false; + foreach_reverse(int i, Digit digit; a.digits) { + if (digit != b.digits[i]) return false; } return true; +// return compare(this, that) == 0; } const bool opEquals(T)(const T that) { return opEquals(Bcd(that)); } + + unittest { + write("equals..."); + assert(Bcd(0) == Bcd(0)); + assert(Bcd(4) == Bcd(4)); + assert(Bcd(40) == Bcd(40)); + assert(Bcd(-400) == Bcd(-400)); + assert(Bcd(12345678) == Bcd(+12345678)); + assert(Bcd(40) != Bcd(53)); + assert(Bcd(402) != Bcd(531)); + assert(Bcd(402) != Bcd(-402)); + assert(Bcd(65432) != Bcd(65431)); + assert(Bcd(2) != Bcd(1)); + assert(Bcd(1) != Bcd(2)); + writeln("passed"); + } } // end struct Bcd -private bool isDigit(const char ch) { - return (ch >= '0' && ch <= '9'); -} - -public string format(const Bcd bcd, bool showPlus = false, bool showLeadingZeros = false) { +//public string format(const Bcd bcd, bool showPlus = false, bool showLeadingZeros = false) { +public string format(const Bcd bcd) { int len = bcd.digits.length; - if (bcd.sign) len++; + if (bcd.isSigned) len++; +// if (bcd.isSigned || bcd.hasLeadingZeros) len++; char[] str = new char[len]; int index = 0; @@ -296,7 +593,10 @@ str[index] = '-'; index++; } - +/+ if (bcd.hasLeadingZeros) { + str[index] = '+'; + index++; + }+/ foreach_reverse(Digit digit; bcd.digits) { str[index] = cast(char) digit + '0'; index++; @@ -347,50 +647,64 @@ bcd.digits[i] = cast(Digit)(ch - '0'); } - if (!lz) return stripLeadingZeros(bcd); + if (!lz) return stripLeadingZeros(bcd); return bcd; } -public Bcd copy(const Bcd bcd) { - return bcd.dup; +private bool isDigit(const char ch) { + return (ch >= '0' && ch <= '9'); } -public Bcd negate(const Bcd bcd) { - Bcd copy = bcd; //.dup; - copy.sign = !bcd.sign; - return copy; +public Bcd negate(const Bcd a) { + return -a; +} + +public Bcd abs(const Bcd a) { + return a.isSigned ? -a: +a; } -public Bcd abs(const Bcd bcd) { - return bcd.isSigned ? -bcd: +bcd; +public int compare(const Bcd m, const Bcd n) { + Bcd a = stripLeadingZeros(m); + Bcd b = stripLeadingZeros(n); + if (!a.isSigned && b.isSigned) return 1; + if (a.isSigned && !b.isSigned) return -1; + if (a.digits.length > b.digits.length) return 1; + if (a.digits.length < b.digits.length) return -1; + foreach_reverse(int i, Digit digit; a.digits) { + if (digit > b.digits[i]) return 1; + if (digit < b.digits[i]) return -1; + } + return 0; } - -public Bcd stripLeadingZeros(const Bcd bcd) { - Bcd result = bcd.dup; - if (!bcd.hasLeadingZeros) return result; - int len = bcd.numDigits; + +public Bcd stripLeadingZeros(const Bcd a) { + Bcd d = a.dup; + if (!a.hasLeadingZeros) return d; + int len = a.numDigits; int i = 0; - while(i < len-1 && bcd.getDigit(i) == 0) { + while(i < len-1 && a.getDigit(i) == 0) { i++; } - result.setNumDigits(len - i); - return result; + d.setNumDigits(len - i); + if (d.numDigits == 1 && d.firstDigit == 0) { + d.sign = false; + } + return d; } - unittest { write("strip..."); Bcd bcd; bcd = "+00123"; assert(bcd.toString == "00123"); bcd = stripLeadingZeros(bcd); - writeln("bcd = ", bcd); +// writeln("bcd = ", bcd); assert(bcd.toString == "123"); bcd = "+000"; - writeln("bcd = ", bcd); +// writeln("bcd = ", bcd); assert(bcd.toString == "000"); bcd = stripLeadingZeros(bcd); - writeln("bcd = ", bcd); +// writeln("bcd = ", bcd); writeln("passed"); } @@ -399,7 +713,7 @@ } public int setSameLength(ref Bcd a, ref Bcd b) { - if (sameLength(a,b)) return a.numDigits; + if (sameLength(a, b)) return a.numDigits; uint alen = a.numDigits; uint blen = b.numDigits; if (alen > blen) { @@ -411,7 +725,7 @@ return a.numDigits(); } -public Bcd complement(const Bcd a) { +public Bcd twosComp(const Bcd a) { Bcd b = Bcd(0, a.numDigits); foreach(int i, Digit digit; a.digits) { b.digits[i] = digit == 0 ? 1 : 0 ; @@ -423,21 +737,21 @@ write("com..."); Bcd bcd; bcd = 123; - assert(complement(bcd).toString == "000"); + assert(twosComp(bcd).toString == "000"); bcd = 40509; - assert(complement(bcd).toString == "01010"); + assert(twosComp(bcd).toString == "01010"); // writeln("bcd = ", bcd); -// writeln("~bcd = ", complement(bcd)); +// writeln("~bcd = ", twosComp(bcd)); writeln("passed"); } -public int sgn(Bcd bcd) { +public int sgn(const Bcd bcd) { if (bcd.isZero) return 0; return bcd.isSigned ? -1 : 1; } public Bcd not(const Bcd x) { - Bcd result = x.dup; + Bcd result = cast(Bcd) x; for (int i = 0; i < x.numDigits; i++) { result[i] = not(result[i]); } @@ -477,11 +791,11 @@ assert(and(a,b).toString == "1111111"); a = 1010; b = 101; - writeln("a = ", a); - writeln("b = ", b); - writeln("and = ", and(a,b)); +// writeln("a = ", a); +// writeln("b = ", b); +// writeln("and = ", and(a,b)); assert(and(a,b).isZero); - writeln("passed!"); + writeln("passed"); } // TODO: looks like there's some room for templates or mixins here. @@ -500,7 +814,6 @@ return a != 0 || b != 0; } - public Bcd xor(const Bcd x, const Bcd y) { Bcd a = x.dup; Bcd b = y.dup; @@ -516,55 +829,361 @@ return (a == 0 && b != 0) || (a != 0 && b == 0); } -public Bcd add(const Bcd a, const Bcd b) { - Bcd x = a.dup; - Bcd y = b.dup; - int len = setSameLength(x, y); - Bcd result = Bcd(0, len+1); - Digit carry = 0; - uint i; - for (i = 0; i < len; i++) { - result[i] = add(x[i], y[i], carry); - } - if (carry) { - result[i] = 1; +public Bcd add(const Bcd x, const Bcd y) { + Bcd a = x.dup; + Bcd b = y.dup; + Bcd sum; + if (a.sign == b.sign) { + sum = simpleAdd(a, b); + sum = stripLeadingZeros(sum); + sum.sign = a.sign; + return sum; } - else { - result = stripLeadingZeros(result); - } - return result; -} - -private Digit add(const Digit a, const Digit b, ref Digit carry) { - Digit sum = a + b + carry; - if (sum > 9) { - sum -= 10; - carry = 1; - } - else { - carry = 0; + else { // signs differ + bool sign; + switch (compare(abs(a), abs(b))) { + case 0: + return ZERO.dup; + case 1: + sign = a.isSigned; + break; + case -1: + sign = b.isSigned; + break; + } + setSameLength(a, b); + sum = simpleAdd(a, tensComp(b)); + if (sum.firstDigit == 1) { + sum.digits = sum.digits[0..$-1]; + } + else { + sum = tensComp(sum); + sum = stripLeadingZeros(sum); + } + sum.sign = sign; } return sum; } unittest { - write("add..."); + write("add...."); + Bcd a, b; + a = 1234; + b = 4321; + assert(a + b == 5555); + a = 2000; + b = 1; + assert(a + b == 2001); + a = -2000; + b = -1; + assert(a + b == -2001); + a = 2000; + b = -1; + assert(a + b == 1999); + a = -123; + b = 1; + assert(a + b == -122); + a = 123; + b = -1; + assert(a + b == 122); + a = -1; + b = 123; +// writeln(); +// writeln("a + b = ", a + b); + assert(a + b == 122); + a = -123; + b = 1; + assert(a + b == -122); + a = 1; + b = -123; + assert(a + b == -122); + writeln("passed"); +} + +Bcd subtract(const Bcd a, const Bcd b) { + return add(a, negate(b)); +} + +/** + * Adds two BCD integers without regard to sign +**/ +private Bcd simpleAdd(const Bcd a, const Bcd b) { + Bcd x = a.dup; + Bcd y = b.dup; + int len = setSameLength(x, y); + Bcd sum = Bcd(0, len+1); + Digit carry = 0; + uint i; + // TODO: this is the place to stop adding zeros and + // just copy the rest of the digits. + for (i = 0; i < len; i++) { + sum[i] = add(x[i], y[i], carry); + } + if (carry) { + sum[i] = 1; + } + else { + sum = stripLeadingZeros(sum); + } + return sum; +} + +/** + * Adds two digits and a carry digit. + * Returns the (single-digit) sum and sets or resets the carry digit. +**/ +private Digit add(const Digit a, const Digit b, ref Digit carry) { + Digit sum = a + b + carry; + carry = sum > 9; + if (carry) sum -= 10; + return sum; +} + +unittest { + write("simpleAdd..."); Bcd a, b; a = 123; b = 222; - Bcd sum = add(a,b); + Bcd sum = simpleAdd(a,b); assert(sum == 345); a = 2; b = 102000; - sum = add(a,b); + sum = simpleAdd(a,b); assert(sum == 102002); - writeln("passed!"); + writeln("passed"); +} + +private Bcd tensComp(const Bcd a) { +/+ foreach(Digit digit; a.digits) { + digit = 9 - digit; + } + a = simpleAdd(a, ONE); + return a;+/ + + Bcd x = Bcd(0, a.numDigits); + foreach (int i, digit; a.digits) { + x[i] = 9 - digit; + } + return simpleAdd(x, ONE); +} + +unittest { + write("tensComp..."); + Bcd a, b, c; + a = 123456; + b = tensComp(a); +// writeln("a = ", a); +// writeln("b = ", b); + c = 876544; + assert(b == c); + a = Bcd(0, 4); + b = tensComp(a); + c = 10000; + assert(b == c); + a = Bcd(1, 4); + b = tensComp(a); + c = 9999; + assert(b == c); + a = 9999; + b = tensComp(a); + c = 1; + assert(b == c); + a = 10000; + b = tensComp(a); + c = 90000; + assert(b == c); + a = 1; + b = tensComp(a); + c = 9; +// writeln("a = ", a); +// writeln("b = ", b); + assert(b == c); + writeln("passed"); +} + +// TODO: there are lots of ways to speed this up. +public Bcd multiply(const Bcd a, const Bcd b) { + Bcd n, m; + Bcd p = Bcd(0, a.numDigits + b.numDigits); + if (a.numDigits > b.numDigits) { + n = stripLeadingZeros(a); + m = stripLeadingZeros(b); + } + else { + n = stripLeadingZeros(b); + m = stripLeadingZeros(a); + } + foreach(uint i, Digit d; m.digits) { + if (d == 0) continue; +// writeln("mul(n, m[i]) = ", mul(n, m[i])); +// writeln("pow10(mul(n, m[i]), i) = ", pow10(mul(n, m[i]), i)); + p = simpleAdd(p, pow10(mul(n, m[i]), i)); + } + p.sign = a.sign ^ b.sign; + return p; +} + +unittest { + write("mul..."); + Bcd a, b, p; + a = 2105, b = 301; + p = multiply(a, b); + assert(p == Bcd(633605)); + b = 800, a = 23958233; + p = multiply(a, b); + assert(p == Bcd("19166586400")); + b = 5830, a = 23958233; + p = multiply(a, b); + assert(p == Bcd("139676498390")); + writeln("passed"); +} + +private Bcd mul(const Bcd a, Digit m) { + Bcd p = Bcd(0, a.numDigits+1); + Digit c = 0; + int i = 0; + foreach (Digit d; a.digits) { + p[i] = mul(m, d, c); + i++; + } + if (c) p[i] = c; + return p; +} + +unittest { + write("mul..."); + Bcd a, p; + Digit m; + a = 2105, m = 3; + p = mul(a, m); + assert(p == Bcd(6315)); + writeln("passed"); +} + +private Digit mul(Digit a, Digit b, ref Digit c) { + Digit p = a * b + c; + c = p / 10; + return p % 10; +} + +unittest { + write("mul..."); + Digit a, b, c, d; + a = 2, b = 3, c = 0; + d = mul(a,b,c); + assert(d == 6); + assert(c == 0); + a = 9, b = 8, c = 0; + d = mul(a,b,c); + assert(d == 2); + assert(c == 7); + writeln("passed"); +} + +public Bcd pow10(const Bcd a, uint n) { + Bcd b = a.dup; + if (n == 0) return b; + Digit[] zeros = new Digit[n]; + b.digits = zeros ~ b.digits; + return b; +} + +public Bcd pow10(uint n) { + return pow10(ONE, n); +} + +unittest { + write("pow10..."); + Bcd a, b; + a = 21345; + a = pow10(a, 12); + assert(a == Bcd("21345000000000000")); + a = pow10(a, 0); + assert(a == "21345000000000000"); + a = pow10(9); + assert(a == Bcd("1000000000")); + writeln("passed"); +} + +public Bcd shift(const Bcd a, int n) { + if (n == 0) return a.dup; + Bcd b = Bcd(0, a.numDigits); + if (std.math.abs(n) >= a.numDigits) return b; + if (n > 0) { // shift left + b.digits[0..$-n] = a.digits[n..$]; + } + else { // shift right + n = -n; + b.digits[n..$] = a.digits[0..$-n]; + } + return b; +} + +unittest { + write("shift..."); + Bcd a, b; + a = 1234; + b = shift(a, 1); + assert(b.toString == "0123"); + b = shift(a, 2); + assert(b.toString == "0012"); + b = shift(a, -1); + assert(b.toString == "2340"); + b = shift(a, 0); + assert(b.toString == "1234"); + b = shift(a, 12); + assert(b.toString == "0000"); + b = shift(a, -4); + assert(b.toString == "0000"); + writeln("passed"); +} + +public Bcd rotate(const Bcd a, int n) { + if (n == 0) return a.dup; + Bcd b = Bcd(0, a.numDigits); + if (std.math.abs(n) >= a.numDigits) { + bool sign = n < 0; + n = std.math.abs(n) % a.numDigits; + if (sign) n = -n; + } + if (n > 0) { // rotate left + b.digits[0..$-n] = a.digits[n..$]; + b.digits[$-n..$] = a.digits[0..n]; + } + else { // rotate right + n = -n; + b.digits[n..$] = a.digits[0..$-n]; + b.digits[0..n] = a.digits[$-n..$]; + } + return b; +} + +unittest { + write("rotate..."); + Bcd a, b; + a = 1234; + b = rotate(a, 1); + assert(b.toString == "4123"); + b = rotate(a, 2); + assert(b.toString == "3412"); + b = rotate(a, -1); + assert(b.toString == "2341"); + b = rotate(a, 0); + assert(b.toString == "1234"); + b = rotate(a, 12); + assert(b.toString == "1234"); + b = rotate(a, -4); + assert(b.toString == "1234"); + writeln("passed"); } //========================================== public void main() { - writeln("Hello, world"); + writeln(); + writeln("If you got this far all the unit tests were successful."); + writeln(); + writeln(" Congratulations!"); Bcd bcd; writeln("bcd = ", format(bcd)); bcd = parse("12"); @@ -580,22 +1199,8 @@ bcd = parse("012_345_678"); writeln("bcd = ", format(bcd)); - bcd = 1234; - writeln("bcd = ", format(bcd)); - bcd = 12345678L; - writeln("bcd = ", format(bcd)); - writeln("a == b : ", Bcd(0) == Bcd(0)); - writeln("a == b : ", Bcd(4) == Bcd(4)); - writeln("a == b : ", Bcd(40) == Bcd(40)); - writeln("a == b : ", Bcd(-400) == Bcd(-400)); - writeln("a == b : ", Bcd(12345678) == Bcd(+12345678)); - writeln("a != b : ", Bcd(40) == Bcd(53)); - writeln("a != b : ", Bcd(402) == Bcd(531)); - writeln("a != b : ", Bcd(402) == Bcd(-402)); - writeln("a != b : ", Bcd(65432) == Bcd(65431)); - writeln("a != b : ", Bcd(2) == Bcd(1)); - writeln("a != b : ", Bcd(1) == Bcd(2)); - writeln("OK!"); +/+ + writeln("OK!");+/ }