# HG changeset patch # User Paul (paul.d.anderson@comcast.net) # Date 1269578620 25200 # Node ID 526759f0ee1cc5cdd3efa57a8f87f07a31f58ec8 # Parent 8e5f172ec55cf1bde34c2d3d8758408cef2a2858 changed module decimal.bcd to decimal.decint diff -r 8e5f172ec55c -r 526759f0ee1c src/decimal/decint.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/decimal/decint.d Thu Mar 25 21:43:40 2010 -0700 @@ -0,0 +1,1763 @@ +/** + * A D programming language implementation of the + * General Decimal Arithmetic Specification, + * Version 1.70, (25 March 2009). + * + * by Paul D. Anderson + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +**/ + +module decimal.decint; + +import std.algorithm: max; +import std.conv: ConvError; +import std.math; +import std.stdio: write, writeln; +import std.string: strip; + +alias ubyte Digit; +alias DecInt decint; + +public immutable DecInt ZERO = { digits:[0] }; +public immutable DecInt ONE = { digits:[1] }; +public immutable DecInt NEG_ONE = { sign:true, digits:[1] }; + +/+static this() { + immutable(DecInt) MAX_LONG = cast(immutable) DecInt(long.max); +// MAX_LONG = cast(immutable) DecInt(long.max); + }+/ + +/** + * Enumeration of available rounding modes. + */ +public enum Rounding { + HALF_EVEN, + HALF_DOWN, + HALF_UP, + DOWN, + UP, + FLOOR, + CEILING, +} + +/** + * 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 DecInt { + +//-------------------------------- +// 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 ]; + +//-------------------------------- +// construction +//-------------------------------- + + //-------------------------------- + // copy construction + //-------------------------------- + + + /** + * Copy constructor. Returns a (non-const) copy of the argument. + */ + public this(const DecInt 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 DecInt bcd, uint numDigits) { + this(bcd); + setNumDigits(numDigits); + } + + //-------------------------------- + // construction from integer types. + //-------------------------------- + + /** + * Constructs a BCD integer from a (signed) byte, short, int or long value. + */ + public this(const long 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); + } + + unittest { + write("this(long).."); + DecInt p; + p = -1234; + assert(p == DecInt("-1234")); + p = 12345678L; + assert(p == DecInt("12345678")); + p = DecInt(-19166586400L); + assert(p == DecInt("-19166586400")); + p = DecInt(139676498390L); + assert(p == DecInt("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); + } + +//-------------------------------- +// member access functions +//-------------------------------- + + //-------------------------------- + // access to the sign of the number + //-------------------------------- + + const bool isSigned() { + return sign; + } + + 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. + * If truncating, first leading zeros are stripped away, then + * the remainder is clipped. + */ + void setNumDigits(uint n) { + if (n == 0) n++; + digits.length = n; + } + +/+ /** + * Adjusts the number of digits in this BCD integer, + * padding or truncating if necessary. + * If truncating, first leading zeros are stripped away, then + * the remainder is clipped. + */ + void setNumDigits(uint n) { + stripl(this); + if (n > digits.length) { + digits.length = n; + } + else { + this = truncate(this, n); + } + }+/ + +/+unittest { + write("setWidth..."); + DecInt a; + a = 1234567; + a.setNumDigits(5); + assert(a == DecInt(12345)); + a.setNumDigits(9); + assert(a.toString == "000012345"); + writeln("passed"); +}+/ + + /** + * 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]; + } + + /** + * 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]; + } + + /** + * 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 { + write("digits..."); + DecInt bcd; + bcd = 12345678; + assert(bcd.numDigits() == 8); + assert(bcd.firstDigit() == 1); + assert(bcd.lastDigit() == 8); + assert(bcd.getDigit(3) == 4); + bcd.setDigit(3, 7); + assert(bcd.getDigit(3) == 7); + bcd.setNumDigits(10); + assert(bcd.numDigits == 10); + assert(bcd.firstDigit() == 0); + assert(bcd.getDigit(5) == 7); + bcd.setNumDigits(5); + assert(bcd.getDigit(2) == 3); + 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; + } + + unittest { + write("toHash..."); + writeln("passed"); + } + + /** + * Returns a mutable copy of this BCD integer. + */ + const DecInt dup() { + DecInt 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 > 1 && firstDigit == 0; + } + + unittest { + write("hasLeadingZeros..."); + assert(!ZERO.hasLeadingZeros); + DecInt b = DecInt(0); + assert(!b.hasLeadingZeros); + b = "-00000001"; + assert(b.hasLeadingZeros); + b = "-00000000"; + assert(b.hasLeadingZeros); + writeln("passed"); + } + + /** + * Returns true if the value of this BCD integer is zero. + * Ignores leading zeros and sign. + */ + const bool isZero() { + DecInt b = canonical(this); + if (b.numDigits > 1) return false; + return b.lastDigit == 0; + } + + unittest { + write("isZero........."); + assert(ZERO.isZero); + DecInt b = DecInt(0); + assert(b.isZero); + b = "-00000001"; + assert(!b.isZero); + b = "-00000000"; + assert(b.isZero); + writeln("passed"); + } + +//-------------------------------- +// casting operators. +//-------------------------------- + + const long opCast() { + DecInt MAX_LONG = DecInt(long.max); + DecInt MIN_LONG = DecInt(long.min); + + if (this > MAX_LONG || this < MIN_LONG) + throw new Exception("Can't cast -- out of range"); + + long n = 0; + foreach_reverse(Digit digit; digits) { + n = 10*n + digit; + } + return this.sign ? -n : n; + } + + unittest { + write("cast(long)..."); + DecInt a; + a = "12345678901234567890123456"; +// long n = cast(long) a; + a = 12345678L; + long n = cast(long) a; + assert(n == 12345678L); + int m = cast(int) a; + assert(m == 12345678L); + a = -a; + m = cast(int) a; + assert(m == -12345678L); + + writeln("passed"); +} + + +//-------------------------------- +// Assignment operators. +//-------------------------------- + + /** + * Assignment operator for BCD integer to BCD integer operations. + */ + DecInt opAssign(const DecInt that) { + this.sign = that.sign; + this.digits = that.digits.dup; + return this; + } + + /** + * Converts the string argument into a BCD integer and assigns it + * to this BCD integer. + */ + void opAssign(const string str) { + this = DecInt(str); + } + + /** + * Converts the integer argument into a BCD integer and assigns it + * to this BCD integer. + */ + void opAssign(const long n) { + this = DecInt(n); + } + + + // index operator + Digit opIndex(uint index) { + return this.digits[index]; + } + + // index assign operator + void opIndexAssign(Digit value, uint index) { + this.digits[index] = value; + } + +//-------------------------------- +// unary operators +//-------------------------------- + + const DecInt opPos() { + return this.dup; + } + + const DecInt opNeg() { + return negate(this); + } + + const DecInt opCom() { + return twosComp(this); + } + + const DecInt opNot() { + return not(this); + } + + DecInt opPostInc() { + this = this + ONE; + return this; + } + + DecInt opPostDec() { + this = this - ONE; + return this; + } + + const DecInt opShl(int n) { + return shift(this, n); + } + + DecInt opShlAssign(int n) { + this = this << n; + return this; + } + + const DecInt opShr(int n) { + return shift(this, -n); + } + + DecInt opShrAssign(int n) { + this = this >> n; + return this; + } + +//-------------------------------- +// binary operators +//-------------------------------- + + const DecInt opAdd(T:DecInt)(const T addend) { + return add(this, addend); + } + + const DecInt opAdd(T)(const T addend) { + return add(this, DecInt(addend)); + } + + DecInt opAddAssign(T)(const T addend) { + this = this + addend; + return this; + } + + const DecInt opSub(T:DecInt)(const T addend) { + return subtract(this, addend); + } + + const DecInt opSub(T)(const T addend) { + return subtract(this, DecInt(addend)); + } + + DecInt opSubAssign(T)(const T addend) { + this = this - addend; + return this; + } + + const DecInt opMul(T:DecInt)(const T b) { + return multiply(this, b); + } + + const DecInt opMul(T)(const T b) { + return multiply(this, DecInt(b)); + } + + DecInt opMulAssign(T)(const T b) { + this = this * b; + return this; + } + + unittest { + write("multiply..."); + DecInt 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 DecInt opDiv(T:DecInt)(const T b) { + DecInt rem; + return divide(this, b, rem); + } + + unittest { + write("opDiv..."); + DecInt a, b; + a = 7; + b = 3; + assert(a/b == 2); + writeln("passed"); + } + + const DecInt opDiv(T)(const T b) { + DecInt rem; + return divide(this, DecInt(b), rem); + } + + DecInt opDivAssign(T)(const T b) { + this = this / b; + return this; + } + + const DecInt opMod(T:DecInt)(const T b) { + DecInt rem; + divide(this, b, rem); + return rem; + } + + unittest { + write("opMod..."); + DecInt a, b; + a = 7; + b = 3; + assert(a % b == 1); + writeln("passed"); + } + + const DecInt opMod(T)(const T b) { + DecInt rem; + divide(this, DecInt(b), rem); + return rem; + } + + DecInt opModAssign(T)(const T b) { + this = this % b; + return this; + } + + const DecInt opAnd(const DecInt a) { + return and(this, a); + } + + DecInt opAndAssign(const DecInt a) { + this = this & a; + return this; + } + + const DecInt opOr(const DecInt a) { + return or(this, a); + } + + DecInt opOrAssign(const DecInt a) { + this = this | a; + return this; + } + + const DecInt opXor(const DecInt a) { + return xor(this, a); + } + + DecInt opXorAssign(const DecInt a) { + this = this ^ a; + return this; + } + +//-------------------------------- +// comparison, equality operators +//-------------------------------- + + const int opCmp(T:DecInt)(const T that) { + return compare(this, that); + } + + unittest { + write("compare..."); + DecInt a,b; + a = 100; + b = 5; + assert(a > b); + a = -100; + b = 5; + assert(a < b); + a = -100; + b = -5; + assert(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(DecInt(that)); + } + + const bool opEquals(T:DecInt)(const T that) { + DecInt a = comparable(this); + DecInt b = comparable(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; + } + + const bool opEquals(T)(const T that) { + return opEquals(DecInt(that)); + } + + unittest { + write("equals..."); + assert(DecInt(0) == DecInt(0)); + assert(DecInt(4) == DecInt(4)); + assert(DecInt(40) == DecInt(40)); + assert(DecInt(-400) == DecInt(-400)); + assert(DecInt(12345678) == DecInt(+12345678)); + assert(DecInt(40) != DecInt(53)); + assert(DecInt(402) != DecInt(531)); + assert(DecInt(402) != DecInt(-402)); + assert(DecInt(65432) != DecInt(65431)); + assert(DecInt(2) != DecInt(1)); + assert(DecInt(1) != DecInt(2)); + writeln("passed"); + } + +} // end struct DecInt + + +public bool isCanonical(const DecInt a) { + // no leading zeros + return !a.hasLeadingZeros; +} + +// Strips leading zeros +public DecInt canonical(const DecInt a) { + DecInt d = a.dup; + if (isCanonical(a)) return d; + stripl(d); + return d; +} + +// Strips leading zeros and changes sign if == -0 +private DecInt comparable(const DecInt a) { + DecInt d = canonical(a); + if (d.numDigits == 1 && d.firstDigit == 0) { + d.sign = false; + } + return d; +} + + +private void clipFirst(ref DecInt a, uint n = 1) { + if (n == 0) return; + if (n >= a.digits.length) a = ZERO; + else a.digits = a.digits[0..$-n]; +} + +private void clipLast(ref DecInt a, uint n = 1) { + if (n == 0) return; + if (n >= a.digits.length) a = ZERO; + else a.digits = a.digits[n..$]; +} + +unittest { + write("clipping...."); + DecInt a; + a = 12345; + clipFirst(a); + assert(a == 2345); + clipLast(a); + assert(a == 234); + a = 1234567890; + clipFirst(a, 3); + assert(a == 4567890); + clipLast(a, 5); + assert(a == 45); + clipLast(a, 5); + assert(a == 0); + writeln("passed"); +} + + +/** + * Strips leading zeros from the specified decint. + */ +private void stripl(ref DecInt a) { + if (a.hasLeadingZeros) { + foreach_reverse(int i, Digit digit; a.digits) { + if (i == 0 || digit != 0) { + a.digits.length = i+1; + break; + } + } + } +} + +unittest { + write("stripl......"); + DecInt b = DecInt("+00023"); + assert(b.toString == "00023"); + stripl(b); + assert(b.toString == "23"); + b = "-00050432"; + stripl(b); + assert(b.toString == "-50432"); + b = "-000"; + stripl(b); + assert(b.toString == "-0"); + writeln("passed"); +} + +/** + * Strips trailing zeros from the specified decint. + */ +private void stripr(ref DecInt a) { + int len = a.digits.length; + foreach(int i, Digit digit; a.digits) { + if (i == len-1 || digit != 0) { + a.digits = a.digits[i..$]; + break; + } + } +} + +unittest { + write("stripl......"); + DecInt b; + b = 23; + stripr(b); + assert(b == 23); + b = "-5000"; + stripr(b); + assert(b.toString == "-5"); + b = "-000"; + stripr(b); + assert(b.toString == "-0"); + writeln("passed"); +} + +//public string format(const DecInt bcd, bool showPlus = false, bool showLeadingZeros = false) { +public string format(const DecInt bcd) { + int len = bcd.digits.length; + if (bcd.isSigned) len++; +// if (bcd.isSigned || bcd.hasLeadingZeros) len++; + char[] str = new char[len]; + + int index = 0; + if (bcd.sign) { + str[index] = '-'; + index++; + } +/+ if (bcd.hasLeadingZeros) { + str[index] = '+'; + index++; + }+/ + foreach_reverse(Digit digit; bcd.digits) { + str[index] = cast(char) digit + '0'; + index++; + } + return str.idup; +} + +public DecInt parse(string str) { + DecInt bcd; + // are leading zeros retained? + bool lz = false; + + // strip whitespace, convert to char array + char[] str1 = str.strip.dup; + + // check for leading '-' + if (str1[0] == '-') { + bcd.sign = true; + str1 = str1[1..$]; + lz = true; + } + + // check for leading '+' + if (str1[0] == '+') { + bcd.sign = false; + str1 = str1[1..$]; + lz = true; + } + + // verify chars are digits, strip out underscores + int index = 0; + char[] str2 = new char[str1.length]; + foreach_reverse(char ch; str1) { + if (isDigit(ch)) { + str2[index] = ch; + index++; + continue; + } + if (ch == '_') { + continue; + } + throw (new ConvError("Invalid character: " ~ ch)); + } + str2.length = index; + + bcd.digits.length = index; + foreach(int i, char ch; str2) { + bcd.digits[i] = cast(Digit)(ch - '0'); + } + + if (!lz) return canonical(bcd); + return bcd; +} + +private bool isDigit(const char ch) { + return (ch >= '0' && ch <= '9'); +} + +public DecInt negate(const DecInt a) { + DecInt b = a.dup; + b.sign = !a.sign; + return b; +} + +public DecInt abs(const DecInt a) { + return a.isSigned ? -a: +a; +} + +public int compare(const DecInt m, const DecInt n) { + DecInt a = comparable(m); + DecInt b = comparable(n); + if (!a.isSigned && b.isSigned) return 1; + if (a.isSigned && !b.isSigned) return -1; + if (!a.isSigned) { + 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; + } + } + else { + if (a.digits.length > b.digits.length) return -1; + if (a.digits.length < b.digits.length) return 11; + foreach_reverse(int i, Digit digit; a.digits) { + if (digit > b.digits[i]) return -1; + if (digit < b.digits[i]) return 1; + } + } + return 0; +} + +unittest { + write("strip..."); + DecInt bcd; + bcd = "+00123"; + assert(bcd.toString == "00123"); + bcd = canonical(bcd); +// writeln("bcd = ", bcd); + assert(bcd.toString == "123"); + bcd = "+000"; +// writeln("bcd = ", bcd); + assert(bcd.toString == "000"); + bcd = canonical(bcd); +// writeln("bcd = ", bcd); + writeln("passed"); +} + +private bool sameLength(const DecInt a, const DecInt b) { + return a.numDigits == b.numDigits; +} + +private int setSameLength(ref DecInt a, ref DecInt b) { + if (sameLength(a, b)) return a.numDigits; + uint alen = a.numDigits; + uint blen = b.numDigits; + if (alen > blen) { + b.setNumDigits(alen); + } + else { + a.setNumDigits(blen); + } + return a.numDigits(); +} + +// TODO: really? private? +private DecInt twosComp(const DecInt a) { + DecInt b = DecInt(0, a.numDigits); + foreach(int i, Digit digit; a.digits) { + b.digits[i] = digit == 0 ? 1 : 0 ; + } + return b; +} + +unittest { + write("com..."); + DecInt bcd; + bcd = 123; + assert(twosComp(bcd).toString == "000"); + bcd = 40509; + assert(twosComp(bcd).toString == "01010"); +// writeln("bcd = ", bcd); +// writeln("~bcd = ", twosComp(bcd)); + writeln("passed"); +} + +public int sgn(const DecInt bcd) { + if (bcd.isZero) return 0; + return bcd.isSigned ? -1 : 1; +} + +public DecInt not(const DecInt a) { + DecInt b = a.dup; + foreach(Digit digit; b.digits) { + digit = not(digit); + } + return b; +} + +private Digit not(const Digit a) { + return a != 0; +} + +public DecInt and(const DecInt x, const DecInt y) { + DecInt a = x.dup; + DecInt b = y.dup; + int len = setSameLength(a,b); + DecInt result = DecInt(0, len); + for (int i = 0; i < len; i++) { + result[i] = and(a[i], b[i]); + } + return result; +} + +private Digit and(const Digit a, const Digit b) { + return a != 0 && b != 0; +} + +unittest { + write("and..."); + DecInt a; + DecInt b; + a = 123000; + b = 123000; + assert(and(a,b).toString == "111000"); + b = 12300; + assert(and(a,b).toString == "011000"); + a = 1234567; + b = 7654321; + assert(and(a,b).toString == "1111111"); + a = 1010; + b = 101; +// writeln("a = ", a); +// writeln("b = ", b); +// writeln("and = ", and(a,b)); + assert(and(a,b).isZero); + writeln("passed"); +} + +// TODO: looks like there's some room for templates or mixins here. +public DecInt or(const DecInt x, const DecInt y) { + DecInt a = x.dup; + DecInt b = y.dup; + int len = setSameLength(a,b); + DecInt result = DecInt(0, len); + for (int i = 0; i < len; i++) { + result[i] = or(a[i], b[i]); + } + return result; +} + +private Digit or(const Digit a, const Digit b) { + return a != 0 || b != 0; +} + +public DecInt xor(const DecInt x, const DecInt y) { + DecInt a = x.dup; + DecInt b = y.dup; + int len = setSameLength(a,b); + DecInt result = DecInt(0, len); + for (int i = 0; i < len; i++) { + result[i] = xor(a[i], b[i]); + } + return result; +} + +private Digit xor(const Digit a, const Digit b) { + return (a == 0 && b != 0) || (a != 0 && b == 0); +} + +// TODO: watch the leading zeros +public DecInt add(const DecInt x, const DecInt y) { + DecInt a = x.dup; + DecInt b = y.dup; + DecInt sum; + if (a.sign == b.sign) { + sum = simpleAdd(a, b); + stripl(sum); + sum.sign = a.sign; + return sum; + } + 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); + } + stripl(sum); + sum.sign = sign; + } + return sum; +} + +unittest { + write("add...."); + DecInt 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"); +} + +DecInt subtract(const DecInt a, const DecInt b) { + return add(a, negate(b)); +} + +/** + * Adds two BCD integers without regard to sign +**/ +private DecInt simpleAdd(const DecInt a, const DecInt b) { + DecInt x = a.dup; + DecInt y = b.dup; + int len = setSameLength(x, y); + DecInt sum = DecInt(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 { + stripl(sum); + } + return sum; +} + +// TODO: need a clipFirst, clipLast method + +/** + * 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..."); + DecInt a, b; + a = 123; + b = 222; + DecInt sum = simpleAdd(a,b); + assert(sum == 345); + a = 2; + b = 102000; + sum = simpleAdd(a,b); + assert(sum == 102002); + writeln("passed"); +} + +private DecInt tensComp(const DecInt a) { +/+ foreach(Digit digit; a.digits) { + digit = 9 - digit; + } + a = simpleAdd(a, ONE); + return a;+/ + + DecInt x = DecInt(0, a.numDigits); + foreach (int i, digit; a.digits) { + x[i] = 9 - digit; + } + return simpleAdd(x, ONE); +} + +unittest { + write("tensComp..."); + DecInt a, b, c; + a = 123456; + b = tensComp(a); +// writeln("a = ", a); +// writeln("b = ", b); + c = 876544; + assert(b == c); + a = DecInt(0, 4); + b = tensComp(a); + c = 10000; + assert(b == c); + a = DecInt(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 DecInt multiply(const DecInt a, const DecInt b) { + DecInt n, m; + DecInt p = DecInt(0, a.numDigits + b.numDigits); + if (a.numDigits > b.numDigits) { + n = canonical(a); + m = canonical(b); + } + else { + n = canonical(b); + m = canonical(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..."); + DecInt a, b, p; + a = 2105, b = 301; + p = multiply(a, b); + assert(p == DecInt(633605)); + b = 800, a = 23958233; + p = multiply(a, b); + assert(p == DecInt("19166586400")); + b = 5830, a = 23958233; + p = multiply(a, b); + assert(p == DecInt("139676498390")); + writeln("passed"); +} + +private DecInt mul(const DecInt a, Digit m) { + DecInt p = DecInt(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..."); + DecInt a, p; + Digit m; + a = 2105, m = 3; + p = mul(a, m); + assert(p == DecInt(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 DecInt divide(const DecInt a, const DecInt b, out DecInt r) { + + if (b == ZERO) throw new Exception("Divide by zero"); + if (a == ZERO) return ZERO.dup; + + DecInt d = canonical(abs(b)); + r = canonical(abs(a)); + DecInt q; + DecInt p = d; + DecInt m; + + // shift the divisor + do { + m = p; + p <<= 1; + } while (p <= r && p > m); + + while (m >= d) { + q <<= 1; + // subtract repeatedly + while (m <= r) { + r -= m; + q++; + } + m >>= 1; + } + + // adjust the sign + r.sign = a.sign; + q.sign = a.sign != b.sign; + return q; +} + +unittest { + write("divide..."); + DecInt a, b, q, r; + a = 144; + b = 12; + q = divide(a, b, r); + assert(q == 12); + assert(r == 0); + a = 144; + b = 14; + q = divide(a, b, r); + assert(q == 10); + assert(r == 4); + a = 142351; + b = 12; + q = divide(a, b, r); +// writeln("q = ", q); +// writeln("r = ", r); + assert(q == 11862); + assert(r == 7); + a = 2; + b = 12; + q = divide(a, b, r); + assert(q == 0); + assert(r == 2); + a = 7; + b = 3; + q = divide(a, b, r); + assert(q == 2); + assert(r == 1); + a = 7; + b = -3; + q = divide(a, b, r); + assert(q == -2); + assert(r == 1); + a = -7; + b = 3; + q = divide(a, b, r); + assert(q == -2); + assert(r == -1); + a = -7; + b = -3; + q = divide(a, b, r); + assert(q == 2); + assert(r == -1); + writeln("passed"); +} + + +public DecInt truncate(const DecInt a, int n) { + return truncate(a, n, DecInt()); +} + +unittest { + write("truncate...."); + DecInt a; + a = 1234567; + assert(truncate(a,3) == 123); + a = 10256; + assert(truncate(a,5) == 10256); + a = 8500; + assert(truncate(a,1) == 8); + a = -8500; + assert(truncate(a,1) == -8); + a = -8500; + assert(truncate(a,-1) == 0); + writeln("passed"); +} + +/** + * Truncate to the specified number of digits + */ +public DecInt truncate(const DecInt a, int n, out DecInt r) { + if (n <= 0) { + r = canonical(a); + return ZERO.dup; + } + if (n >= a.numDigits) { + r = ZERO.dup; + return canonical(a); + } + DecInt b; + b.digits = a.digits[$-n..$].dup; + b.sign = a.sign; + r.digits = a.digits[0..$-n].dup; + return b; +} + +unittest { + write("truncRem..."); + DecInt a; + DecInt r; + a = 1234567; + assert(truncate(a, 3, r) == 123); + assert(r == 4567); + a = 10256; + assert(truncate(a,5,r) == 10256); + assert(r == 0); + a = 8500; + assert(truncate(a,1,r) == 8); + assert(r == 500); + a = -8500; + assert(truncate(a,1,r) == -8); + assert(r == 500); + a = -8500; +// writeln("truncate(a, 6, r) = ", truncate(a, 6, r)); +// writeln("r = ", r); + assert(truncate(a,6,r) == -8500); + assert(r == 0); + writeln("passed"); +} + +public DecInt pad(DecInt a, int n) { + if (n <= a.numDigits) return a.dup; + return DecInt(a,n); +} + +unittest { + write("pad........."); + DecInt a; + a = 1234567; + assert(pad(a,3) == 1234567); + a = 10256; +// writeln("pad(a,9) = ", pad(a,9)); + assert(pad(a,9).toString == "000010256"); + a = 8500; + assert(pad(a,6).toString == "008500"); + a = -8500; + assert(pad(a,6).toString == "-008500"); + writeln("passed"); +} + +public DecInt pow10(const DecInt a, uint n) { + DecInt b = canonical(a); + if (n == 0 || b == 0) return b; + Digit[] zeros = new Digit[n]; + b.digits = zeros ~ b.digits; + return b; +} + +public DecInt pow10(uint n) { + return pow10(ONE, n); +} + +unittest { + write("pow10..."); + DecInt a, b; + a = 21345; + a = pow10(a, 12); + assert(a == DecInt("21345000000000000")); + a = pow10(a, 0); + assert(a == "21345000000000000"); + a = pow10(9); + assert(a == DecInt("1000000000")); + writeln("passed"); +} + +public DecInt power(const DecInt a, uint n) { + DecInt b = canonical(a); + DecInt p = ONE.dup; + while (n > 0) { + if (n & 1) { + p *= b; + if (n == 1) return p; + } + b *= b; + n /= 2; + } + return p; +} + +unittest { + write("power......."); + DecInt a; + a = 2; + int n = 3; + assert(power(a,n) == 8); + writeln("passed"); +} + + +public DecInt shift(const DecInt a, int n) { + if (n == 0) return canonical(a); + if (n > 0) { // shift left + return pow10(a, n); + } + else { // shift right + if (-n >= a.numDigits) return ZERO.dup; + return truncate(a, a.numDigits + n); + } +} + +unittest { + write("shift......."); + DecInt a, b; + a = 1234; + b = shift(a, 1); + assert(b.toString == "12340"); + b = shift(a, 2); + assert(b.toString == "123400"); + b = shift(a, -1); +// writeln("b = ", b); + assert(b.toString == "123"); + b = shift(a, 0); + assert(b.toString == "1234"); + b = shift(a, -12); +/+ writeln("b = ", b);+/ + assert(b.toString == "0"); + b = shift(a, -4); + assert(b.toString == "0"); + writeln("passed"); +} + +public DecInt shiftFixed(const DecInt a, int n) { + if (n == 0) return a.dup; + DecInt b = DecInt(0, a.numDigits); + if (std.math.abs(n) >= a.numDigits) return b; + if (n > 0) { // shiftFixed left + b.digits[0..$-n] = a.digits[n..$]; + } + else { // shiftFixed right + n = -n; + b.digits[n..$] = a.digits[0..$-n]; + } + return b; +} + +unittest { + write("shiftFixed..."); + DecInt a, b; + a = 1234; + b = shiftFixed(a, 1); + assert(b.toString == "0123"); + b = shiftFixed(a, 2); + assert(b.toString == "0012"); + b = shiftFixed(a, -1); + assert(b.toString == "2340"); + b = shiftFixed(a, 0); + assert(b.toString == "1234"); + b = shiftFixed(a, 12); + assert(b.toString == "0000"); + b = shiftFixed(a, -4); + assert(b.toString == "0000"); + writeln("passed"); +} + +public DecInt rotate(const DecInt a, int n) { + if (n == 0) return a.dup; + DecInt b = DecInt(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..."); + DecInt 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"); +} + +/** + * Rounds the specified integer to the specified number of digits. + * The rounding mode governs the method of rounding. + */ +public DecInt round(const DecInt a, int n, Rounding mode) { + DecInt r; + DecInt b = truncate(a, n, r); + return round(b, r, mode); +// return b; +} + +/** + * Rounds the (truncated) integer based on its remainder and the rounding + * mode. + */ +public DecInt round(const DecInt a, const DecInt r, const Rounding mode) { + DecInt b = canonical(a); + ubyte rfd = r.firstDigit; + switch (mode) { + case Rounding.DOWN: + return b; + + case Rounding.HALF_UP: + if (rfd >= 5) b++; + return b; + + case Rounding.HALF_EVEN: + if (rfd < 5) return b; + if (rfd > 5) return ++b; + DecInt p = r.dup; + stripr(p); + if (p.numDigits > 1) return ++b; + // to reach this point the remainder must be exactly 5 + // if odd, increment + if (b.lastDigit & 1 != 0) b++; + return b; + + case Rounding.CEILING: + if (!b.isSigned && !r.isZero) b++; + return b; + + case Rounding.FLOOR: + if (b.isSigned && !r.isZero) b++; + return b; + + case Rounding.HALF_DOWN: + if (rfd < 5) return b; + if (rfd > 5) return ++b; + DecInt p = r.dup; + stripr(p); + if (p.numDigits > 1) return ++b; + // to reach this point the remainder must be exactly 5 + return b; + + case Rounding.UP: + if (!r.isZero) b++; + return b; + } + return b; +} + +unittest { + write("rounding...."); + DecInt a, b; + a = 12345; + b = round(a, 4, Rounding.DOWN); + assert(b == 1234); + b = round(a, 4, Rounding.HALF_UP); + assert(b == 1235); + b = round(a, 4, Rounding.CEILING); + assert(b == 1235); + b = round(a, 4, Rounding.HALF_EVEN); + assert(b == 1234); + b = round(a, 4, Rounding.FLOOR); + assert(b == 1234); + b = round(a, 4, Rounding.HALF_DOWN); + assert(b == 1234); + b = round(a, 4, Rounding.UP); + assert(b == 1235); + writeln("passed"); +} + + +//========================================== + +public void main() { + writeln(); + writeln("If you got this far all the unit tests were successful."); + writeln(); + writeln(" Congratulations!"); + DecInt bcd; + writeln("bcd = ", format(bcd)); + bcd = parse("12"); + writeln("bcd = ", format(bcd)); + bcd = parse("012"); + writeln("bcd = ", format(bcd)); + bcd = parse("-012"); + writeln("bcd = ", format(bcd)); + bcd = parse("+012"); + writeln("bcd = ", format(bcd)); + bcd = parse("-0"); + writeln("bcd = ", format(bcd)); + bcd = parse("012_345_678"); + writeln("bcd = ", format(bcd)); + +/+ + writeln("OK!");+/ +} + + +