diff src/decimal/bcd.d @ 4:b37c218c1442

Initial development of BCD integers
author Paul (paul.d.anderson@comcast.net)
date Thu, 18 Mar 2010 18:10:25 -0700
parents
children c021ed211f89
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/decimal/bcd.d	Thu Mar 18 18:10:25 2010 -0700
@@ -0,0 +1,602 @@
+/** 
+ * 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.conv: ConvError;
+import std.math;
+import std.stdio: write, writeln;
+import std.string: strip;
+
+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 struct Bcd {
+
+	// members
+	private bool sign = false;
+	private Digit[] digits = [ 0 ];
+	
+	// constructors
+	public this(const Bcd bcd) {
+		this = bcd;
+	}
+	
+	public this(const Bcd bcd, uint numDigits) {
+		this = bcd;
+		setNumDigits(numDigits);
+	}
+	
+	public this(const string str) {
+		this = str;
+	}
+	
+	public this(const long n) {
+		this = n;
+	}
+		
+	public this(const long n, uint numDigits) {
+		this = n;
+		setNumDigits(numDigits);
+	}
+		
+	const string toString() {
+		return format(this);
+	}
+	
+	const hash_t toHash() {
+		hash_t hash = 0;
+		foreach(Digit digit; digits) {
+			hash += digit;
+		}
+		return hash;
+	}
+	
+	// access methods	
+	const uint numDigits() {
+		return digits.length;
+	}
+	
+	void setNumDigits(uint n) {
+		digits.length = n;
+	}
+	
+	const uint firstDigit() {
+		return digits[$-1];
+	}
+	
+	const uint lastDigit() {
+		return digits[0];
+	}
+	
+	void setDigit(uint n, Digit value) {
+		digits[$-n-1] = value;
+	}
+	
+	const int getDigit(int n) {
+		return digits[$-n-1];
+	}
+	
+	unittest {
+		write("digits...");
+		Bcd bcd = Bcd(12345678L);
+		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) == 6);
+//		writeln("bcd = ", bcd);
+		writeln("passed!");
+	}
+	
+	const bool isSigned() {
+		return sign;
+	}
+	
+	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;
+	}
+	
+	void opAssign(const Bcd that) {
+		this.sign = that.sign;
+		this.digits.length = that.digits.length;
+		this.digits[] = that.digits[];
+	}
+	
+	void opAssign(const string str) {
+		this = parse(str);
+	}
+	
+	void opAssign(const long n) {
+		uint m;	
+		if (n < 0) {
+			sign = true;
+			m = std.math.abs(n);
+		}
+		else {
+			sign = false;
+			m = n;
+		}
+		Digit[20] dig;
+		int i = 0;
+		do {
+			uint q = m/10;
+			uint r = m%10;
+			dig[i] = cast(Digit) r;
+			m = q;
+			i++;
+		} while (m > 0);
+		digits = dig[0..i].dup;
+	}
+	
+	
+	// index operator
+	Digit opIndex(uint index) {
+		return this.digits[index];
+	}
+	
+	// index assign operator
+	void opIndexAssign(Digit value, uint index) {
+		this.digits[index] = value;
+	}
+	
+/+	int opApply(int delegate (ref Digit) dg) {
+		int result = 0;
+		for (int i = 0; i < numDigits; i++) {
+			result = dg(digits[i]);
+			if (result) break;
+		}
+		return result;
+	}
+	
+	int opApply(int delegate (ref Digit) dg) {
+		int result = 0;
+		for (int i = 0; i < numDigits; i++) {
+			result = dg(digits[i]);
+			if (result) break;
+		}
+		return result;
+	}
+	
+	int opApplyReverse(int delegate (ref Digit) dg) {
+		int result = 0;
+		for (int i = 0; i < numDigits; i++) {
+			result = dg(digits[i]);
+			if (result) break;
+		}
+		return result;
+	}+/
+	
+	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);
+	}
+	
+	const Bcd opCom() {
+		return complement(this);
+	}
+	
+	const Bcd opNot() {
+		return not(this);
+	}
+	
+	const Bcd opAnd(const Bcd bcd) {
+		return and(this, bcd);
+	}
+	
+	const Bcd opOr(const Bcd bcd) {
+		return or(this, bcd);
+	}
+	
+	const Bcd opXor(const Bcd bcd) {
+		return xor(this, bcd);
+	}
+	
+	// TODO: what's the matter with the one-digit numbers??
+	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;
+		}
+		return true;
+	}
+	
+	const bool opEquals(T)(const T that) {
+		return opEquals(Bcd(that));
+	}
+	
+} // 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) {
+	int len = bcd.digits.length;
+	if (bcd.sign) len++;
+	char[] str = new char[len];
+	
+	int index = 0;
+	if (bcd.sign) {
+		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 stripLeadingZeros(bcd);	
+	return bcd;
+}
+
+public Bcd copy(const Bcd bcd) {
+	return bcd.dup;
+}
+
+public Bcd negate(const Bcd bcd) {
+	Bcd copy = bcd; //.dup;
+	copy.sign = !bcd.sign;
+	return copy;
+}
+
+public Bcd abs(const Bcd bcd) {
+	return bcd.isSigned ? -bcd: +bcd;
+}
+
+public Bcd stripLeadingZeros(const Bcd bcd) {
+	Bcd result = bcd.dup;
+	if (!bcd.hasLeadingZeros) return result;
+	int len = bcd.numDigits;
+	int i = 0;
+	while(i < len-1 && bcd.getDigit(i) == 0) {
+		i++;
+	} 
+	result.setNumDigits(len - i);
+	return result;
+}
+
+
+unittest {
+	write("strip...");
+	Bcd bcd;
+	bcd = "+00123";
+	assert(bcd.toString == "00123");
+	bcd = stripLeadingZeros(bcd);
+	writeln("bcd = ", bcd);
+	assert(bcd.toString == "123");
+	bcd = "+000";
+	writeln("bcd = ", bcd);
+	assert(bcd.toString == "000");
+	bcd = stripLeadingZeros(bcd);
+	writeln("bcd = ", bcd);
+	writeln("passed");
+}
+	
+public bool sameLength(const Bcd a, const Bcd b) {
+	return a.numDigits == b.numDigits;
+}
+
+public 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();
+}
+
+public Bcd complement(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(complement(bcd).toString == "000");
+	bcd = 40509;
+	assert(complement(bcd).toString == "01010");
+//	writeln("bcd = ", bcd);
+//	writeln("~bcd = ", complement(bcd));
+	writeln("passed");
+}
+	
+public int sgn(Bcd bcd) {
+	if (bcd.isZero) return 0;
+	return bcd.isSigned ? -1 : 1;
+}
+
+public Bcd not(const Bcd x) {
+	Bcd result = x.dup;
+	for (int i = 0; i < x.numDigits; i++) {
+		result[i] = not(result[i]);
+	}
+	return result;
+}
+
+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);
+}
+
+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;
+	}
+	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;
+	}
+	return sum;
+}
+
+unittest {
+	write("add...");
+	Bcd a, b;
+	a = 123;
+	b = 222;
+	Bcd sum = add(a,b);
+	assert(sum == 345);
+	a = 2;
+	b = 102000;
+	sum = add(a,b);
+	assert(sum == 102002);
+	writeln("passed!");
+}
+
+//==========================================
+
+public void main() {
+	writeln("Hello, world");
+	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));
+
+	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!");
+}
+
+
+