view src/decimal/bcd.d @ 10:f925fe996255

added cast, clip, power
author Paul (paul.d.anderson@comcast.net)
date Thu, 25 Mar 2010 20:05:00 -0700
parents 48d564218e05
children 8e5f172ec55c
line wrap: on
line source

/** 
 * 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.bcd;

import std.algorithm: max;
import std.conv: ConvError;
import std.math;
import std.stdio: write, writeln;
import std.string: strip;

alias ubyte Digit;

public immutable Bcd ZERO = { digits:[0] };
public immutable Bcd ONE  = { digits:[1] };
public immutable Bcd NEG_ONE = { sign:true, digits:[1] };

/+static this() {
	immutable(Bcd) MAX_LONG = cast(immutable) Bcd(long.max);
//	MAX_LONG = cast(immutable) Bcd(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 Bcd {

//--------------------------------
// 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 Bcd 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);
		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)..");
		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);
	}
	
//--------------------------------
// 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...");
	Bcd a;
	a = 1234567;
	a.setNumDigits(5);
	assert(a == Bcd(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...");
		Bcd 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 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 > 1 && firstDigit == 0;
	}
	
	unittest {
	write("hasLeadingZeros...");
	assert(!ZERO.hasLeadingZeros);
	Bcd b = Bcd(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() {
		Bcd b = canonical(this);
		if (b.numDigits > 1) return false;
		return b.lastDigit == 0;
	}
	
	unittest {
	write("isZero.........");
	assert(ZERO.isZero);
	Bcd b = Bcd(0);
	assert(b.isZero);
	b = "-00000001";
	assert(!b.isZero);
	b = "-00000000";
	assert(b.isZero);
	writeln("passed");
	}
	
//--------------------------------
// casting operators.
//-------------------------------- 

	const long opCast() {
		Bcd MAX_LONG = Bcd(long.max);
		Bcd MIN_LONG = Bcd(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)...");
	Bcd 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.
	 */
	Bcd opAssign(const Bcd 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 = Bcd(str);
	}
	
	/**
	 * Converts the integer argument into a BCD integer and assigns it 
	 * to this BCD integer.
	 */
	void opAssign(const long n) {
		this = Bcd(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 Bcd opPos() {
		return this.dup;
	}
	
	const Bcd opNeg() {
		return negate(this);
	}
	
	const Bcd opCom() {
		return twosComp(this);
	}
	
	const Bcd opNot() {
		return not(this);
	}
	
	Bcd opPostInc() {
		this = this + ONE;
		return this;
	}
	
	Bcd opPostDec() {
		this = this - ONE;
		return this;
	}
	
	const Bcd opShl(int n) {
		return shift(this, n);
	}
	
	Bcd opShlAssign(int n) {
		this = this << n;
		return this;
	}
	
	const Bcd opShr(int n) {
		return shift(this, -n);
	}
	
	Bcd opShrAssign(int n) {
		this = this >> n;
		return this;
	}
	
//--------------------------------
// 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));
	}
	
	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 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));
	}
	
	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 opDiv(T:Bcd)(const T b) {
		Bcd rem;
		return divide(this, b, rem);
	}
	
	unittest {
		write("opDiv...");
		Bcd a, b;
		a = 7;
		b = 3;
		assert(a/b == 2);
		writeln("passed");
	}
	
	const Bcd opDiv(T)(const T b) {
		Bcd rem;
		return divide(this, Bcd(b), rem);
	}
	
	Bcd opDivAssign(T)(const T b) {
		this = this / b;
		return this;
	}

	const Bcd opMod(T:Bcd)(const T b) {
		Bcd rem;
		divide(this, b, rem);
		return rem;
	}
	
	unittest {
		write("opMod...");
		Bcd a, b;
		a = 7;
		b = 3;
		assert(a % b == 1);
		writeln("passed");
	}
	
	const Bcd opMod(T)(const T b) {
		Bcd rem;
		divide(this, Bcd(b), rem);
		return rem;
	}
	
	Bcd opModAssign(T)(const T b) {
		this = this % b;
		return this;
	}

	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 = -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(Bcd(that));
	}
	
	const bool opEquals(T:Bcd)(const T that) {
		Bcd a = canonical(this);
		Bcd b = canonical(that);
		// TODO: this is wrong -- signs can differ -0 and + 0
		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(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


public bool isCanonical(const Bcd a) {
	// no leading zeros
	if (a.hasLeadingZeros) return false;
	// not -0
/+	if (a.numDigits == 1 && a.firstDigit == 0) return !a.sign;+/
	return true;
}

// Strips leading zeros and changes sign if == -0
public Bcd canonical(const Bcd a) {
	Bcd d = a.dup;
	if (isCanonical(a)) return d;
	stripl(d);
/+	if (d.numDigits == 1 && d.firstDigit == 0) {
		d.sign = false;
	}+/
	return d;
}


private void clipFirst(ref Bcd 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 Bcd a, uint n = 1) {
	if (n == 0) return;
	if (n >= a.digits.length) a = ZERO;
	else a.digits = a.digits[n..$];
}

unittest {
	write("clipping....");
	Bcd 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 Bcd 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......");
	Bcd b = Bcd("+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 Bcd 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......");
	Bcd 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 Bcd bcd, bool showPlus = false, bool showLeadingZeros = false) {
public string format(const Bcd 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 Bcd parse(string str) {
	Bcd 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 Bcd negate(const Bcd a) {
	Bcd b = a.dup;
	b.sign = !a.sign;
	return b;
}

public Bcd abs(const Bcd a) {
	return a.isSigned ? -a: +a;
}

public int compare(const Bcd m, const Bcd n) {
	Bcd a = canonical(m);
	Bcd b = canonical(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...");
	Bcd 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 Bcd a, const Bcd b) {
	return a.numDigits == b.numDigits;
}

private int setSameLength(ref Bcd a, ref Bcd 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 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 ;
	}
	return b;
}

unittest {
	write("com...");
	Bcd 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 Bcd bcd) {
	if (bcd.isZero) return 0;
	return bcd.isSigned ? -1 : 1;
}

public Bcd not(const Bcd a) {
	Bcd b = a.dup;
	foreach(Digit digit; b.digits) {
		digit = not(digit);
	}
	return b;
}

private Digit not(const Digit a) {
	return a != 0;
}

public Bcd and(const Bcd x, const Bcd y) {
	Bcd a = x.dup;
	Bcd b = y.dup;
	int len = setSameLength(a,b);
	Bcd result = Bcd(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...");
	Bcd a;
	Bcd 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 Bcd or(const Bcd x, const Bcd y) {
	Bcd a = x.dup;
	Bcd b = y.dup;
	int len = setSameLength(a,b);
	Bcd result = Bcd(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 Bcd xor(const Bcd x, const Bcd y) {
	Bcd a = x.dup;
	Bcd b = y.dup;
	int len = setSameLength(a,b);
	Bcd result = Bcd(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 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);
		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....");
	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 {
		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...");
	Bcd a, b;
	a = 123;
	b = 222;
	Bcd sum = simpleAdd(a,b);
	assert(sum == 345);
	a = 2;
	b = 102000;
	sum = simpleAdd(a,b);
	assert(sum == 102002);
	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 = 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...");
	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 divide(const Bcd a, const Bcd b, out Bcd r) {

	if (b == ZERO) throw new Exception("Divide by zero");
	if (a == ZERO) return ZERO.dup;
	
	Bcd d = canonical(abs(b));
	r = canonical(abs(a));
	Bcd q;
	Bcd p = d;
	Bcd 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...");
	Bcd 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 Bcd truncate(const Bcd a, int n) {
	return truncate(a, n, Bcd());
}

unittest {
	write("truncate....");
	Bcd 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 Bcd truncate(const Bcd a, int n, out Bcd r) {
	if (n <= 0) {
		r = canonical(a);
		return ZERO.dup;
	}
	if (n >= a.numDigits) {
		r = ZERO.dup;
		return canonical(a);
	}
	Bcd b;
	b.digits = a.digits[$-n..$].dup;
	b.sign = a.sign;
	r.digits = a.digits[0..$-n].dup;
	return b;
}

unittest {
	write("truncRem...");
	Bcd a;
	Bcd 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 Bcd pad(Bcd a, int n) {
	if (n <= a.numDigits) return a.dup;
	return Bcd(a,n);
}

unittest {
	write("pad.........");
	Bcd 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 Bcd pow10(const Bcd a, uint n) {
	Bcd b = canonical(a);
	if (n == 0 || b == 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 power(const Bcd a, uint n) {
	Bcd b = canonical(a);
	Bcd 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.......");
	Bcd a;
	a = 2;
	int n = 3;
	assert(power(a,n) == 8);
	writeln("passed");
}


public Bcd shift(const Bcd 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.......");
	Bcd 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 Bcd shiftFixed(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) {	// 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...");
	Bcd 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 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");
}

/**
 * Rounds the specified integer to the specified number of digits.
 * The rounding mode governs the method of rounding.
 */
public Bcd round(const Bcd a, int n, Rounding mode) {
	Bcd r;
	Bcd 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 Bcd round(const Bcd a, const Bcd r, const Rounding mode) {
	Bcd 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;
            Bcd 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;
            Bcd 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....");
	Bcd 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!");
	Bcd 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!");+/
}