changeset 5:c021ed211f89

bcd add, sub, multiply, logic and shift operations
author Paul (paul.d.anderson@comcast.net)
date Sat, 20 Mar 2010 21:38:22 -0700
parents b37c218c1442
children 14badf9104b9
files src/decimal/bcd.d
diffstat 1 files changed, 771 insertions(+), 166 deletions(-) [+]
line wrap: on
line diff
--- 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!");+/
 }