changeset 0:42cf4db6be32

Creation
author Paul (paul.d.anderson@comcast.net)
date Sat, 13 Mar 2010 13:22:25 -0800
parents
children a984d3056cc4
files src/decimal/context.d src/decimal/decimal.d src/decimal/math.d
diffstat 3 files changed, 3949 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/decimal/context.d	Sat Mar 13 13:22:25 2010 -0800
@@ -0,0 +1,182 @@
+module decimal.context;
+
+import decimal.decimal;
+import std.string;
+
+//--------------------------
+//
+// DecimalContext struct
+//
+//--------------------------
+
+/**
+ * Enumeration of available rounding modes.
+ */
+public enum Rounding {
+	HALF_EVEN,
+	HALF_DOWN,
+	HALF_UP,
+	DOWN,
+	UP,
+	FLOOR,
+	CEILING,
+}
+
+
+// context flags
+public enum : ubyte {
+ 	 CLAMPED           = 0x01,
+ 	 DIVISION_BY_ZERO  = 0x02,
+ 	 INEXACT           = 0x04,
+ 	 INVALID_OPERATION = 0x08,
+ 	 OVERFLOW          = 0x10,
+ 	 ROUNDED           = 0x20,
+ 	 SUBNORMAL         = 0x40,
+ 	 UNDERFLOW         = 0x80
+}
+	
+/+public:
+ 	static immutable ubyte CLAMPED           = 0x01;
+ 	static immutable ubyte DIVISION_BY_ZERO  = 0x02;
+ 	static immutable ubyte INEXACT           = 0x04;
+ 	static immutable ubyte INVALID_OPERATION = 0x08;
+ 	static immutable ubyte OVERFLOW          = 0x10;
+ 	static immutable ubyte ROUNDED           = 0x20;
+ 	static immutable ubyte SUBNORMAL         = 0x40;
+ 	static immutable ubyte UNDERFLOW         = 0x80;+/
+
+/**
+ * Context for Decimal mathematic operations
+ */
+public struct DecimalContext {
+	public:
+		Rounding mode = Rounding.HALF_EVEN;
+		uint precision = 9;
+		ubyte traps;
+		ubyte flags;
+		int eMin = -98;
+		int eMax = 99;
+	
+	const int eTiny() {
+		return eMin - (precision - 1);
+	}
+	
+	/// Returns the number of decimal digits in this context.
+	const uint dig() {
+		return precision;
+	}
+	
+	/// returns the smallest available increment to one in this context
+	const Decimal epsilon() {
+		return Decimal(1,-precision);
+	}
+		
+	/// Returns the number of binary digits in this context.
+	const int mant_dig() {
+		return cast(int)(precision/LOG2);
+	}
+
+	const int min_exp() {
+		return cast(int)(eMin/LOG2);
+	}
+	
+	const int max_exp() {
+		return cast(int)(eMax/LOG2);
+	}
+	
+	const int min_10_exp() {
+		return eMin;
+	}
+	
+	const int max_10_exp() {
+		return eMax;
+	}
+	
+	/// Returns the maximum representable normal value in the current context.
+	// TODO: Replace this formula with a single execution when precision or eMax is changed.
+	const Decimal max() {
+		string cstr = "9." ~ repeat("9", precision-1) ~ "E" ~ format("%d", eMax);
+		return Decimal(cstr);
+	}
+	
+	// Returns the minimum representable normal value in the current context.
+	const Decimal min_normal() {
+		return Decimal(1, eMin);
+	}
+	
+	// Returns the minimum representable subnormal value in the current context.
+	const Decimal min() {
+		return Decimal(1, eTiny);
+	}
+	
+/+	public const int getPrecision() {
+		return precision;
+	}
+	
+	public void setPrecision(int precision) {
+		this.precision = precision;
+	}+/
+	
+	const void setFlag(const ubyte flag, const bool value = true) {
+		if (value) {
+		 context.flags |= flag; 
+		}
+		else {
+		  context.flags &= !flag;
+        }
+	}
+	
+	public bool getFlag(const ubyte flag) {
+		return (context.flags & flag) == flag;
+	}
+	
+	public void clearFlags() {
+		context.flags = 0;
+	}
+	
+
+};	// end struct DecimalContext
+
+public:
+	// default context
+	immutable DecimalContext DEFAULT_CONTEXT = DecimalContext();
+	
+private:
+	// context stack
+struct ContextStack {
+	private:
+		DecimalContext[] stack = [ DEFAULT_CONTEXT ];
+		uint capacity = 1;
+		uint count = 1;
+
+	public void push() {
+		if (count >= capacity) {
+			capacity *= 2;
+			stack.length = capacity;
+		}
+		count++;
+		stack[count-1] = context;
+	}
+	
+	public void pop() {
+		if (count == 0) {
+			push();
+		}
+		if (count == 1) {
+			context = stack[0];
+		}
+		else {
+			count--;
+			context = stack[count-1];
+		}
+	}
+	
+} // end struct ContextStack
+
+//--------------------------
+//
+// Emd of DecimalContext struct
+//
+//--------------------------
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/decimal/decimal.d	Sat Mar 13 13:22:25 2010 -0800
@@ -0,0 +1,3241 @@
+// TODO: ensure context flags are being set and cleared properly.
+
+// TODO: add exp()  add sqrt() add power():
+
+// TODO: add a set/getPayload to Decimal
+
+// TODO: unittest opPostDec && opPostInc.
+
+// TODO: this(str): add tests for just over/under int.max, int.min
+
+// TODO: organize by structs, lib functions modules functions, etc.
+
+// TODO: opEquals unit test should include numerically equal testing.
+
+// TODO: write some test cases for flag setting. test the add/sub/mul/div functions
+
+// TODO: to/from real or double (float) values needs definition and implementation.
+
+// TODO: need to determine the property name for dig and/or digits
+
+module decimal.decimal;
+
+import decimal.context;
+import std.bigint;
+import std.conv;
+import std.ctype: isdigit;
+import std.math: PI, LOG2;
+import std.stdio: write, writeln;
+import std.string;
+
+// special values
+private enum SpVal {CLEAR, ZERO, INF, QNAN, SNAN};
+
+// common decimal "numbers"
+public:	
+	static immutable Decimal ONE = {spval:SpVal.CLEAR, ceff:{[1]}};
+	static immutable Decimal TEN = {spval:SpVal.CLEAR, ceff:{[10]}};
+	static immutable Decimal NaN = {spval:SpVal.QNAN};
+	static immutable Decimal POS_INF = {spval:SpVal.INF};
+	static immutable Decimal NEG_INF = {sign:true, SpVal.INF};
+	static immutable Decimal ZERO = {spval:SpVal.ZERO};
+	static immutable Decimal NEG_ZERO = {sign:true, SpVal.ZERO};
+
+// active context
+public DecimalContext context = DEFAULT_CONTEXT;
+
+// singleton ContextStack	
+private ContextStack contextStack;
+	
+/// saves the current context
+public void pushContext() {
+	contextStack.push();
+}
+
+/// restores the previous context
+public void popContext() {
+	contextStack.pop();
+}
+
+/**
+ * A struct representing an arbitrary-precision floating-point number.
+ *
+ * The implementation follows the General Decimal Arithmetic
+ * Specification, Version 1.70 (25 Mar 2009),
+ * http://www.speleotrove.com/decimal. This specification conforms with 
+ * IEEE standard 754-2008.
+ */
+struct Decimal {
+
+private:
+	SpVal spval = SpVal.QNAN;	// special values: default value is quiet NaN
+	bool sign = false;		// true if the value is negative, false otherwise.
+	int expo = 0;			// the exponent of the Decimal value
+	BigInt ceff;		// the coefficient of the Decimal value
+	// NOTE: not a uint -- causes math problems down the line.
+	int digits;			    // the number of decimal digits in this number.
+							// (unless the number is a special value)
+
+//--------------------------------
+// construction
+//-------------------------------- 
+
+public:
+	/**
+	 * Constructs a Decimal number from a sign, a BigInt coefficient and
+	 * an integer exponent.
+	 * The precision of the number is deduced from the number of decimal
+	 * digits in the coefficient.
+	 */
+	this(const bool sign, const BigInt coefficient, const int exponent) {
+		this.clear();
+		if (coefficient < BIG_ZERO) {
+			this.sign = !sign;
+			this.ceff = -coefficient;
+		}
+		else {
+			this.sign = sign;
+			this.ceff = coefficient;
+			if (coefficient == BIG_ZERO) {
+				this.spval = SpVal.ZERO;
+			}
+		}
+		this.expo = exponent;
+		this.digits = numDigits(this.ceff, 1);
+	}
+	
+	/**
+	 * Constructs a Decimal number from a sign, an integer coefficient and 
+	 * an integer exponent.
+	 */
+	this(const bool sign, const int coefficient, const int exponent) {
+		this(sign, BigInt(coefficient), exponent);
+	}
+	
+	/**
+	 * Constructs a Decimal number from a sign, a special value string and 
+	 * an optional payload.
+	 */
+	this(const bool sign, string str, const uint payload = 0) {
+		this.clear();;
+		this.sign = sign;
+		if (icmp(str, "inf") == 0 || icmp(str, "infinity") == 0) {
+			spval = SpVal.INF;
+			return;
+		}
+		if (icmp(str, "snan") == 0) {
+			spval = SpVal.SNAN;
+		}
+		else {
+			spval = SpVal.QNAN;
+		}
+		ceff = payload;
+	}
+
+	/**
+	 * Constructs a Decimal from a BigInt coefficient and an int exponent.
+	 * The sign of the number is the sign of the coefficient.
+	 */
+	this(const BigInt coefficient, const int exponent) {
+		this(false, coefficient, exponent);
+	};
+	
+	/**
+	 * Constructs a Decimal from a BigInt.
+	 */
+	this(const BigInt coefficient) {
+		this(coefficient, 0);
+	};
+
+	/**
+	 * Constructs a Decimal from an integer coefficient and an integer exponent.
+	 */
+	this(const long coefficient, const int exponent) {
+		this(BigInt(coefficient), exponent);
+	}
+
+	/**
+	 * Constructs a Decimal from an integer value.
+	 */
+	this(const long coefficient) {
+		this(BigInt(coefficient), 0);
+	}
+
+	/**
+	 * Constructs a Decimal from an integer coefficient, exponent and precision.
+	 */
+	 // TODO: should this set flags? probably not.
+	this(const long coefficient, const int exponent, const int precision) {
+		this(coefficient, exponent);
+		pushContext();
+		context.precision = precision;
+		setDigits(this);
+		popContext();
+	}
+
+	/**
+	 *	Constructs a Decimal from a real value.
+	 */
+	this(const real r) {
+		string str = format("%.*G", cast(int)context.precision, r);
+		this = str;
+	}
+
+	/**
+	 *	Constructs a Decimal from a double value.
+	 * Set to the specified precision
+	 */
+	this(const real r, int precision) {
+		string str = format("%.*E", precision, r);
+		this = str;
+	}
+
+	// copy constructor
+	this(const Decimal that) {
+		this = that;
+	};
+
+	// construct from string representation
+	this(const string str) {
+		this = str;
+	};
+
+unittest {
+	write("construction.");
+	Decimal f = Decimal(BigInt(1234), 567);
+	assert(f.toString() == "1.234E+570");
+	f = Decimal(BigInt(1234));
+	assert(f.toString() == "1234");
+	f = Decimal(BigInt(123400));
+	assert(f.toString() == "123400");
+	f = Decimal(1234, 567);
+	assert(f.toString() == "1.234E+570");
+	f = Decimal(BigInt(1234));
+	assert(f.toString() == "1234");
+	f = Decimal(1234, 0, 9);
+	assert(f.toString() == "1234.00000");
+	Decimal dec = Decimal(1234, 1, 9);
+	assert(dec.toString() == "12340.0000");
+	dec = Decimal(12, 1, 9);
+	assert(dec.toString() == "120.000000");
+	dec = Decimal(int.max, -4, 9);
+	assert(dec.toString() == "214748.365");
+	dec = Decimal(int.max, -4);
+	assert(dec.toString() == "214748.3647");
+	dec = Decimal(1234567, -2, 5);
+	assert(dec.toString() == "12346");
+	writeln("passed");
+}
+
+unittest {
+	write("this(str)....");
+	Decimal f;
+	string str = "0";
+	f = str;
+	assert(f.toString() == str);
+	assert(f.toAbstract() == "[0,0,0]");
+	str = "0.00";
+	f = str;
+	assert(f.toString() == str);
+	assert(f.toAbstract() == "[0,0,-2]");
+	str = "0.0";
+	f = str;
+	assert(f.toString() == str);
+	assert(f.toAbstract() == "[0,0,-1]");
+	f = "0.";
+	assert(f.toString() == "0");
+	assert(f.toAbstract() == "[0,0,0]");
+	f = ".0";
+	assert(f.toString() == "0.0");
+	assert(f.toAbstract() == "[0,0,-1]");
+	str = "1.0";
+	f = str;
+	assert(f.toString() == str);
+	assert(f.toAbstract() == "[0,10,-1]");
+	str = "1.";
+	f = str;
+	assert(f.toString() == "1");
+	assert(f.toAbstract() == "[0,1,0]");
+	str = ".1";
+	f = str;
+	assert(f.toString() == "0.1");
+	assert(f.toAbstract() == "[0,1,-1]");
+	f = Decimal("123");
+	assert(f.toString() == "123");
+	f = Decimal("-123");
+	assert(f.toString() == "-123");
+	f = Decimal("1.23E3");
+	assert(f.toString() == "1.23E+3");
+	f = Decimal("1.23E");
+	assert(f.toString() == "NaN");
+	f = Decimal("1.23E-");
+	assert(f.toString() == "NaN");
+	f = Decimal("1.23E+");
+	assert(f.toString() == "NaN");
+	f = Decimal("1.23E+3");
+	assert(f.toString() == "1.23E+3");
+	f = Decimal("1.23E3B");
+	assert(f.toString() == "NaN");
+	f = Decimal("12.3E+007");
+	assert(f.toString() == "1.23E+8");
+	f = Decimal("12.3E+70000000000");
+	assert(f.toString() == "NaN");
+	f = Decimal("12.3E+7000000000");
+	assert(f.toString() == "NaN");
+	f = Decimal("12.3E+700000000");
+//	writeln(f.toString());
+	assert(f.toString() == "1.23E+700000001");
+	f = Decimal("12.3E-700000000");
+//	writeln(f.toString());
+	assert(f.toString() == "1.23E-699999999");
+	// NOTE: since there will still be adjustments -- maybe limit to 99999999?
+	f = Decimal("12.0");
+	assert(f.toString() == "12.0");
+	f = Decimal("12.3");
+	assert(f.toString() == "12.3");
+	f = Decimal("1.23E-3");
+	assert(f.toString() == "0.00123");
+	f = Decimal("0.00123");
+	assert(f.toString() == "0.00123");
+	f = Decimal("-1.23E-12");
+	assert(f.toString() == "-1.23E-12");
+	f = Decimal("-0");
+	assert(f.toString() == "-0");		
+	f = Decimal("inf");
+	assert(f.toString() == "Infinity");
+	f = Decimal("NaN");
+	assert(f.toString() == "NaN");
+	f = Decimal("-NaN");
+	assert(f.toString() == "-NaN");
+	f = Decimal("sNaN");
+	assert(f.toString() == "sNaN");
+	f = Decimal("Fred");
+	assert(f.toString() == "NaN");
+	writeln("passed");
+}
+
+	/**
+	 * dup property
+	 */
+	const Decimal dup() {
+		Decimal cp;
+		cp.sign = sign;
+		cp.spval = spval;
+		cp.ceff = ceff;
+		cp.expo = expo;
+		return cp;
+	}
+	
+//--------------------------------
+// assignment
+//--------------------------------
+
+	/// Assigns a Decimal (makes a copy)
+	void opAssign(const Decimal that) {
+		this.sign = that.sign;
+		this.spval = that.spval;
+		this.digits = that.digits;
+		this.expo = that.expo;
+		this.ceff = that.ceff;
+	}
+		
+	///	Assigns a floating point value.
+	void opAssign(const real r) {
+		this = Decimal(r);
+	}
+		
+	/// Assign an integer value
+	void opAssign(const long n) {
+		spval = (n == 0) ? SpVal.ZERO : SpVal.CLEAR;
+		sign = n < 0;
+		ceff = sign ? -n : n;
+		expo = 0;
+		digits = numDigits(ceff, 1);
+	}
+	
+	/// Assign a BigInt
+	void opAssign(const BigInt big) {
+		spval = (big == BIG_ZERO) ? SpVal.ZERO : SpVal.CLEAR;
+		sign = big < 0;
+		ceff = sign ? -big : big;
+		expo = 0;
+		digits = numDigits(ceff, 1);
+	}
+		
+	/// Assigns a string
+	void opAssign(const string numeric_string) {
+		clear();
+		sign = false;
+
+		// strip, copy, tolower
+		char[] str = strip(numeric_string).dup;
+		tolowerInPlace(str);
+		
+		// get sign, if any
+		if (startsWith(str,"-")) {
+			sign = true;
+			str = str[1..$];
+		}
+		else if (startsWith(str,"+")) {
+			str = str[1..$];
+		}
+		
+		// check for NaN
+		if (startsWith(str,"nan")) {
+			spval = SpVal.QNAN;
+			if (str == "nan") {
+				ceff = BIG_ZERO;
+				return;
+			}
+			// set payload
+			str = str[3..$];
+			// ensure string is all digits
+			foreach(char c; str) {
+				if (!isdigit(c)) {
+					return;
+				}
+			}
+			// convert string to payload
+			ceff = BigInt(str.idup);
+			return;
+		};
+
+		// check for sNaN
+		if (startsWith(str,"snan")) {
+			spval = SpVal.SNAN;
+			if (str == "snan") {
+				ceff = BIG_ZERO;
+				return;
+			}
+			// set payload
+			str = str[4..$];
+			// ensure string is all digits
+			foreach(char c; str) {
+				if (!isdigit(c)) {
+					return;
+				}
+			}
+			// convert string to payload
+			ceff = BigInt(str.idup);
+			return;
+		};
+		
+		// check for infinity
+		if (str == "inf" || str == "infinity") {
+			spval = SpVal.INF;
+			return;
+		};
+
+		clear();
+		// check for exponent
+		int pos = find(str, 'e');
+		if (pos > 0) {
+			// if it's just a trailing 'e', return NaN
+			if (pos == str.length - 1) {
+				spval = SpVal.QNAN;
+				return;
+			}
+			// split the string into coefficient and exponent
+			char[] xstr = str[pos+1..$];
+			str = str[0..pos];
+			// assume exponent is positive
+			bool xneg = false;
+			// check for minus sign
+			if (startsWith(xstr, "-")) {
+				xneg = true;
+				xstr = xstr[1..$];
+			}
+			// check for plus sign
+			else if (startsWith(xstr, "+")) {
+				xstr = xstr[1..$];
+			}
+			
+			// ensure it's not now empty
+			if (xstr.length < 1) {
+				spval = SpVal.QNAN;
+				return;
+			}
+			
+			// ensure exponent is all digits
+			foreach(char c; xstr) {
+				if (!isdigit(c)) {
+					spval = SpVal.QNAN;
+					return;
+				}
+			}
+			
+			// trim leading zeros
+			while (xstr[0] == '0' && xstr.length > 1) {
+				xstr = xstr[1..$];
+			}
+			
+			// make sure it will fit into an int
+			if (xstr.length > 10) {
+				spval = SpVal.QNAN;
+				return;
+			}
+			if (xstr.length == 10) {
+				// try to convert it to a long (should work) and
+				// then see if the long value is too big (or small)
+				long lex = to!long(xstr);
+				if ((xneg && (-lex < int.min)) || lex > int.max) {
+					spval = SpVal.QNAN;
+					return;
+				}
+				expo = cast(int) lex;
+			}
+			else {
+				// everything should be copacetic at this point
+				expo = to!int(xstr);
+			}
+			if (xneg) {
+				expo = -expo;
+			}
+		}
+		else {
+			expo = 0;
+		}
+
+		// remove trailing decimal point
+		if (endsWith(str, ".")) {
+			str = str[0..$-1];
+		}
+		// strip leading zeros
+		while (str[0] == '0' && str.length > 1) {
+			str = str[1..$];
+		}
+			
+		// remove internal decimal point
+		int point = find(str, '.');
+		if (point >= 0) {
+			// excise the point and adjust exponent
+			str = str[0..point] ~ str[point+1..$];
+			int diff = str.length - point;
+			expo -= diff;
+		}
+		
+		// ensure string is not empty
+		if (str.length < 1) {
+			spval = SpVal.QNAN;
+			return;
+		}
+		
+		// ensure string is all digits
+		foreach(char c; str) {
+			if (!isdigit(c)) {
+				spval = SpVal.QNAN;
+				return;
+			}
+		}
+		// convert string to BigInt
+		ceff = BigInt(str.idup);
+		digits = numDigits(ceff, str.length);
+		if (ceff == BIG_ZERO) spval = SpVal.ZERO;
+		
+	};	// end opAssign(string)
+
+//--------------------------------
+// string representations
+//--------------------------------
+	
+/**
+ * Converts a Decimal to an abstract string representation.
+ */
+private const string toAbstract() {
+	switch (spval) {
+		case SpVal.SNAN:
+			string payload = ceff == BIG_ZERO ? "" : "," ~ ceff.toString();
+			return format("[%d,%s%s]", sign ? 1 : 0, "sNaN", payload);
+		case SpVal.QNAN:
+			string payload = ceff == BIG_ZERO ? "" : "," ~ ceff.toString();
+			return format("[%d,%s%s]", sign ? 1 : 0, "qNaN", payload);
+		case SpVal.INF:
+			return format("[%d,%s]", sign ? 1 : 0, "inf");
+		default:
+			return format("[%d,%s,%d]", sign ? 1 : 0, ceff.toString(), expo);
+	}
+}
+
+/**
+ * Converts a Decimal to a string representation.
+ */
+const string toString() {
+	
+	// string representation of special values
+	if (spval > SpVal.ZERO) {
+		string str;
+		switch(spval) {
+			case SpVal.ZERO:
+				str = "0";
+				break;
+			case SpVal.INF:
+				str = "Infinity";
+				break;
+			case SpVal.SNAN:
+				str = "sNaN";
+				break;
+			default:
+				str = "NaN";
+		}
+		if (spval >= SpVal.QNAN && ceff != BIG_ZERO) {
+			str ~= ceff.toString();
+		}
+		return sign ? "-" ~ str : str;
+	}
+		
+	// string representation of finite numbers
+	string cstr = ceff.toString();
+	int clen = cstr.length;
+	int adjx = expo + clen - 1;
+	
+	// if exponent is small, don't use exponential notation
+	if (expo <= 0 && adjx >= -6) {
+		// if exponent is not zero, insert a decimal point
+		if (expo != 0) {
+			int point = std.math.abs(expo);
+			// if coefficient is too small, pad with zeroes
+			if (point > clen) {
+				cstr = zfill(cstr, point);
+				clen = cstr.length;
+			}
+			// if no chars precede the decimal point, prefix a zero
+			if (point == clen) {
+				cstr = "0." ~ cstr;
+			}
+			// otherwise insert a decimal point
+			else {
+				cstr = insert(cstr, cstr.length - point, ".");
+			}
+		}
+		return sign ? "-" ~ cstr : cstr;
+	}
+	// use exponential notation
+//	else {
+		if (clen > 1) {
+			cstr = insert(cstr, 1, ".");
+		}
+		string xstr = to!string(adjx);
+		if (adjx >= 0) {
+			xstr = "+" ~ xstr;
+		}
+		string str = cstr ~ "E" ~ xstr;
+		if (sign) {
+			return "-" ~ str;
+		}
+		else {
+			return str;
+		}
+//	}	// end else
+			
+};	// end toString()
+		
+	/**
+ 	* Converts a Decimal to an engineering string representation.
+ 	*/
+	const string toEngString() {
+		return toString();
+	};
+	
+	/**
+ 	* Converts a Decimal to a scientific string representation.
+ 	*/
+	const string toSciString() {
+		return toString();
+	};
+	
+unittest {
+	write("to-sci-str...");
+	Decimal dec = Decimal(false, 123, 0);
+	assert(dec.toString() == "123");
+	assert(dec.toAbstract() == "[0,123,0]");
+	dec = Decimal(true, 123, 0);
+	assert(dec.toString() == "-123");
+	assert(dec.toAbstract() == "[1,123,0]");
+	dec = Decimal(false, 123, 1);
+	assert(dec.toString() == "1.23E+3");
+	assert(dec.toAbstract() == "[0,123,1]");
+	dec = Decimal(false, 123, 3);
+	assert(dec.toString() == "1.23E+5");
+	assert(dec.toAbstract() == "[0,123,3]");
+	dec = Decimal(false, 123, -1);
+	assert(dec.toString() == "12.3");
+	assert(dec.toAbstract() == "[0,123,-1]");
+	dec = Decimal(false, 123, -5);
+	assert(dec.toString() == "0.00123");
+	assert(dec.toAbstract() == "[0,123,-5]");
+	dec = Decimal(false, 123, -10);
+	assert(dec.toString() == "1.23E-8");
+	assert(dec.toAbstract() == "[0,123,-10]");
+	dec = Decimal(true, 123, -12);
+	assert(dec.toString() == "-1.23E-10");
+	assert(dec.toAbstract() == "[1,123,-12]");
+	dec = Decimal(false, 0, 0);
+	assert(dec.toString() == "0");
+	assert(dec.toAbstract() == "[0,0,0]");
+	dec = Decimal(false, 0, -2);
+	assert(dec.toString() == "0.00");
+	assert(dec.toAbstract() == "[0,0,-2]");
+	dec = Decimal(false, 0, 2);
+	assert(dec.toString() == "0E+2");
+	assert(dec.toAbstract() == "[0,0,2]");
+	dec = Decimal(true, 0, 0);
+	assert(dec.toString() == "-0");
+	assert(dec.toAbstract() == "[1,0,0]");
+	dec = Decimal(false, 5, -6);
+	assert(dec.toString() == "0.000005");
+	assert(dec.toAbstract() == "[0,5,-6]");
+	dec = Decimal(false, 50,-7);
+	assert(dec.toString() == "0.0000050");
+	assert(dec.toAbstract() == "[0,50,-7]");
+	dec = Decimal(false, 5, -7);
+	assert(dec.toString() == "5E-7");
+	assert(dec.toAbstract() == "[0,5,-7]");
+	dec = Decimal(false, "inf");
+	assert(dec.toString() == "Infinity");
+	assert(dec.toAbstract() == "[0,inf]");
+	dec = Decimal(true, "inf");
+	assert(dec.toString() == "-Infinity");
+	assert(dec.toAbstract() == "[1,inf]");
+	dec = Decimal(false, "NaN");
+	assert(dec.toString() == "NaN");
+	assert(dec.toAbstract() == "[0,qNaN]");
+	dec = Decimal(false, "NaN", 123);
+	assert(dec.toString() == "NaN123");
+	assert(dec.toAbstract() == "[0,qNaN,123]");
+	dec = Decimal(true, "sNaN");
+	assert(dec.toString() == "-sNaN");
+	assert(dec.toAbstract() == "[1,sNaN]");
+	writeln("passed");
+}
+
+
+//--------------------------------
+// member properties
+//--------------------------------
+
+	/// returns the exponent of this Decimal
+	const int exponent() {
+		return this.expo;
+	}
+	/// returns the coefficient of this Decimal	
+	const BigInt coefficient() {
+		return this.ceff;
+	}
+	
+	/// returns the sign of this Decimal
+	const int sgn() {
+		if (isZero) return 0;
+		return sign ? -1 : 1;
+	}
+	
+	/// returns a Decimal with the same exponent as this Decimal
+	/// and a coefficient of 1.
+	const Decimal quantum() {
+		return Decimal(1, this.expo);
+	}
+
+//--------------------------------
+// floating point properties
+//--------------------------------
+
+	static int precision() {
+		return context.precision;
+	}
+
+	/// returns the default value for this type (NaN)
+	static Decimal init() {
+		return NaN;
+	}
+	
+	/// Returns NaN
+	static Decimal nan() {
+		return NaN;
+	}
+	
+	/// Returns positive infinity.
+	static Decimal infinity() {
+		return POS_INF;
+	}
+	
+//--------------------------------
+//  classification properties
+//--------------------------------
+
+	/**
+	 * Returns true if this number is canonical representation (always true).
+	 */
+	const bool isCanonical() {
+		return true;
+	}
+
+	/**
+	 * Returns true if this Decimal is +/- zero.
+	 */
+	const bool isZero() {
+		return spval == SpVal.ZERO;
+	}
+
+	/**
+	 * Returns true if this Decimal is a quiet or signaling NaN.
+	 */
+	const bool isNaN() {
+		return this.spval == SpVal.QNAN || this.spval == SpVal.SNAN;
+	}
+
+	/**
+	 * Returns true if this Decimal is a signaling NaN.
+	 */
+	const bool isSignaling() {
+		return this.spval == SpVal.SNAN;
+	}
+
+	/**
+	 * Returns true if this Decimal is a quiet NaN.
+	 */
+	const bool isQuiet() {
+		return this.spval == SpVal.QNAN;
+	}
+
+	/**
+	 * Returns true if this Decimal is +/- infinity.
+	 */
+	const bool isInfinite() {
+		return this.spval == SpVal.INF;
+	}
+
+	/**
+	 * Returns true if this Decimal is not +/- infinity and not a NaN.
+	 */
+	const bool isFinite() {
+		return spval != SpVal.INF 
+			&& spval != SpVal.QNAN 
+			&& spval != SpVal.SNAN;
+	}
+
+	/**
+	 * Returns true if this Decimal is a NaN or infinity.
+	 */
+	const bool isSpecial() {
+		return spval == SpVal.INF 
+			|| spval == SpVal.QNAN 
+			|| spval == SpVal.SNAN;
+	}
+
+	/**
+	 * Returns true if this Decimal is negative. (Includes -0)
+	 */
+	const bool isSigned() {
+		return this.sign;
+	}
+
+	/**
+	 * Returns true if this Decimal is subnormal.
+	 */
+	const bool isSubnormal() {
+		if (spval != SpVal.CLEAR) return false;
+		return adjustedExponent < context.eMin;
+	}
+	
+	/**
+	 * Returns true if this Decimal is subnormal.
+	 */
+	const bool isNormal() {
+		if (spval != SpVal.CLEAR) return false;
+		return adjustedExponent >= context.eMin;
+	}
+	
+//--------------------------------
+// comparison
+//--------------------------------
+
+	/**
+	 * Returns -1, 0 or 1, if this number is less than, equal to or 
+	 * greater than the argument, respectively.
+	 */
+	int opCmp(const Decimal that) {
+		return icompare(this, that);
+	}
+
+	/**
+ 	* Returns true if this Decimal is equal to the specified Decimal.
+ 	* A NaN is not equal to any number, not even to another NaN.
+ 	* Infinities are equal if they have the same sign.
+ 	* Zeros are equal regardless of sign.
+ 	* Finite numbers are equal if they are numerically equal to the current precision.
+ 	* A Decimal may not be equal to itself (this != this) if it is a NaN.
+ 	*/	
+	const bool opEquals(ref const Decimal that) {
+		// if either is NaN...
+		if (this.isNaN || that.isNaN) return false;
+
+		// if either is infinite...
+		if (this.isInfinite || that.isInfinite) {
+			return (this.spval == that.spval && this.sign == that.sign);
+		}
+		
+		// if either is zero...
+		if (this.isZero || that.isZero) {
+			return (this.isZero && that.isZero);
+		}
+		// if their signs differ
+		if (this.sign != that.sign) {
+			return false;
+		}
+		
+		// if they have the same representation, they are equal
+		if (this.expo == that.expo && this.ceff == that.ceff) {
+			return true;
+		}
+		
+		// otherwise they are equal if they represent the same value
+		// NOTE: this is only a check to current precision.
+		Decimal result = this - that;
+		return result.isZero;
+	}
+
+unittest {
+	write("equals.......");
+	Decimal op1;
+	Decimal op2;
+	op1 = "NaN";
+	op2 = "NaN";
+	assert(op1 != op2);
+	op1 = "inf";
+	op2 = "inf";
+	assert(op1 == op2);
+	op2 = "-inf";
+	assert(op1 != op2);
+	op1 = "-inf";
+	assert(op1 == op2);
+	op2 = "NaN";
+	assert(op1 != op2);
+	op1 = 0;
+	assert(op1 != op2);
+	op2 = 0;
+	assert(op1 == op2);
+	writeln("passed");
+}
+
+//--------------------------------
+// unary arithmetic operators
+//--------------------------------
+
+	/**
+	 * unary minus -- returns a copy with the opposite sign.
+	 * This operation may set flags -- equivalent to 
+	 * subtract('0', b);
+	 */
+	const Decimal opNeg() {
+		return minus(this);
+	}
+	
+	/**
+	 * unary plus -- returns a copy.
+	 * This operation may set flags -- equivalent to 
+	 * add('0', a);
+	 */
+	const Decimal opPos() {
+		return plus(this);
+	}
+	
+	/**
+	 * Returns this + 1.
+	 */
+	Decimal opPostInc() {
+		this += ONE;
+		return this;
+	}
+	
+	/**
+	 * Returns this - 1.
+	 */
+	Decimal opPostDec() {
+		this -= ONE;
+		return this;
+	}
+	
+//--------------------------------
+//  binary arithmetic operators
+//--------------------------------
+	 
+	 /// Adds a Decimal to this and returns the Decimal result
+	const Decimal opAdd(T:Decimal)(const T addend) {
+		return add(this, addend);
+	}
+	
+	// Adds a number to this and returns the result. 
+	const Decimal opAdd(T)(const T addend) {
+		return add(this, Decimal(addend), context);
+	}
+	
+	const Decimal opSub(T:Decimal)(const T subtrahend) {
+		return subtract(this, subtrahend);
+	}
+	
+	const Decimal opSub(T)(const T subtrahend) {
+		return subtract(this, Decimal(subtrahend), context);
+	}
+	
+	const Decimal opMul(T:Decimal)(const T factor) {
+		return multiply(this, factor);
+	}
+	
+	const Decimal opMul(T)(const T factor) {
+		return multiply(this, Decimal(factor));
+	}
+	
+	const Decimal opDiv(T:Decimal)(const T divisor) {
+		return divide(this, divisor);
+	}
+	
+	const Decimal opDiv(T)(const T divisor) {
+		return divide(this, Decimal(divisor));
+	}
+	
+	const Decimal opMod(T:Decimal)(const T divisor) {
+		return remainder(this, divisor);
+	}
+	
+	const Decimal opMod(T)(const T divisor) {
+		return remainder(this, Decimal(divisor));
+	}
+	
+//--------------------------------
+//  arithmetic assignment operators
+//--------------------------------
+
+	Decimal opAddAssign(T)(const T addend) {
+		this = this + addend;
+		return this;
+	}
+	
+	Decimal opSubAssign(T)(const T subtrahend) {
+		this = this - subtrahend;
+		return this;
+	}
+	
+	Decimal opMulAssign(T)(const T factor) {
+		this = this * factor;
+		return this;
+	}
+	
+	Decimal opDivAssign(T)(const T divisor) {
+		this = this / divisor;
+		return this;
+	}
+	
+	Decimal opModAssign(T)(const T divisor) {
+		this = this % divisor;
+		return this;
+	}
+	
+//-----------------------------
+// nextUp, nextDown, nextAfter
+//-----------------------------
+	
+	const Decimal nextUp() {
+		return nextPlus(this);
+	}
+
+	const Decimal nextDown() {
+		return nextMinus(this);
+	}
+
+	const Decimal nextAfter(const Decimal dcm) {
+		return nextToward(this, dcm);
+	}
+	
+private:
+	/**
+	 * clears the special value flags
+	 */
+	void clear() {
+		spval = SpVal.CLEAR;
+	}
+	
+	const int adjustedExponent() {
+		return expo + digits - 1;
+	}
+	
+	const bool overflow() {
+		return adjustedExponent > context.eMax;
+	}
+
+	unittest{
+		write("overflow.....");
+		Decimal dec = Decimal(123, 99);
+		assert(dec.overflow);
+		dec = Decimal(12, 99);
+		assert(dec.overflow);
+		dec = Decimal(1, 99);
+		assert(!dec.overflow);
+		dec = Decimal(9, 99);
+		assert(!dec.overflow);
+		writeln("passed");
+	}
+		
+}	// end struct Decimal
+
+
+//--------------------------------
+// context functions
+//--------------------------------
+
+// TODO: this is actually a property of the context.
+
+/**
+ * Returns radix of this representation (10).
+ */
+public int radix() {
+	return 10;
+}
+	
+unittest {
+	write("radix........");
+	assert(radix() == 10);
+	writeln("passed");
+}
+	
+//--------------------------------
+// classification functions
+//--------------------------------
+
+public string classify(const Decimal dcm) {
+	if (dcm.isSignaling()) {
+		return "sNaN";
+	}
+	if (dcm.isQuiet) {
+		return "NaN";
+	}
+	if (dcm.isInfinite) {
+		return dcm.sign ? "-Infinity" : "+Infinity";
+	}
+	if (dcm.isSubnormal) {
+		return dcm.sign ? "-Subnormal" : "+Subnormal";
+	}
+	if (dcm.isZero) {
+		return dcm.sign ? "-Zero" : "+Zero";
+	}
+	return dcm.sign ? "-Normal" : "+Normal";
+}
+
+/**
+ * Returns true if this number is canonical representation (always true).
+ */
+public bool isCanonical(const Decimal dcm) {
+	return dcm.isCanonical;
+}
+
+/**
+ * Returns true if this Decimal is a signaling NaN.
+ */
+public bool isSignaling(const Decimal dcm) {
+	return dcm.isSignaling;
+}
+
+/**
+ * Returns true if the specified Decimal is a quiet NaN.
+ */
+public bool isQuiet(const Decimal dcm) {
+	return dcm.isQuiet;
+}
+
+public bool isFinite(const Decimal dcm) {
+	return dcm.isFinite;
+}
+
+public bool isInfinite(const Decimal dcm) {
+	return dcm.isInfinite;
+}
+
+public bool isNaN(const Decimal dcm) {
+	return dcm.isNaN;
+}
+
+public bool isNormal(const Decimal dcm) {
+	return dcm.isNormal;
+}
+
+public bool isSubnormal(const Decimal dcm) {
+	return dcm.isSubnormal;
+}
+
+public bool isZero(const Decimal dcm) {
+	return dcm.isZero;
+}
+
+public bool isSigned(const Decimal dcm) {
+	return dcm.isSigned;
+}
+
+unittest {
+	write("classify.....");
+	Decimal dcm;
+	dcm = "Infinity";
+	assert(classify(dcm) == "+Infinity");
+	dcm = "1E-10";
+	assert(classify(dcm) == "+Normal");
+	dcm = "2.50";
+	assert(classify(dcm) == "+Normal");
+	dcm = "0.1E-99";
+	assert(classify(dcm) == "+Subnormal");
+	dcm = "0";
+	assert(classify(dcm) == "+Zero");
+	dcm = "-0";
+	assert(classify(dcm) == "-Zero");
+	dcm = "-0.1E-99";
+	assert(classify(dcm) == "-Subnormal");
+	dcm = "-1E-10";
+	assert(classify(dcm) == "-Normal");
+	dcm = "-2.50";
+	assert(classify(dcm) == "-Normal");
+	dcm = "-Infinity";
+	assert(classify(dcm) == "-Infinity");
+	dcm = "NaN";
+	assert(classify(dcm) == "NaN");
+	dcm = "-NaN";
+	assert(classify(dcm) == "NaN");
+	dcm = "sNaN";
+	assert(classify(dcm) == "sNaN");
+
+	dcm = Decimal("2.50");
+	assert(isCanonical(dcm));
+
+	dcm = Decimal("2.50");
+	assert(isFinite(dcm));
+	dcm = Decimal("-0.3");
+	assert(isFinite(dcm));
+	dcm = 0;
+	assert(isFinite(dcm));
+	dcm = Decimal("Inf");
+	assert(!isFinite(dcm));
+	dcm = Decimal("-Inf");
+	assert(!isFinite(dcm));
+	dcm = Decimal("NaN");
+	assert(!isFinite(dcm));
+
+	dcm = Decimal("2.50");
+	assert(!isInfinite(dcm));
+	dcm = Decimal("-Inf");
+	assert(isInfinite(dcm));
+	dcm = Decimal("NaN");
+	assert(!isInfinite(dcm));
+
+	dcm = Decimal("2.50");
+	assert(!isNaN(dcm));
+	dcm = Decimal("NaN");
+	assert(isNaN(dcm));
+	dcm = Decimal("-sNaN");
+	assert(isNaN(dcm));
+
+	dcm = Decimal("2.50");
+	assert(isNormal(dcm));
+	dcm = Decimal("0.1E-99");
+	assert(!isNormal(dcm));
+	dcm = Decimal("0.00");
+	assert(!isNormal(dcm));
+	dcm = Decimal("-Inf");
+	assert(!isNormal(dcm));
+	dcm = Decimal("NaN");
+	assert(!isNormal(dcm));
+	writeln("passed");
+
+	dcm = Decimal("2.50");
+	assert(!isQuiet(dcm));
+	dcm = Decimal("NaN");
+	assert(isQuiet(dcm));
+	dcm = Decimal("sNaN");
+	assert(!isQuiet(dcm));
+
+	dcm = Decimal("2.50");
+	assert(!isSignaling(dcm));
+	dcm = Decimal("NaN");
+	assert(!isSignaling(dcm));
+	dcm = Decimal("sNaN");
+	assert(isSignaling(dcm));
+
+	dcm = Decimal("2.50");
+	assert(!isSigned(dcm));
+	dcm = Decimal("-12");
+	assert(isSigned(dcm));
+	dcm = Decimal("-0");
+	assert(isSigned(dcm));
+
+	dcm = Decimal("2.50");
+	assert(!isSubnormal(dcm));
+	dcm = Decimal("0.1E-99");
+	assert(isSubnormal(dcm));
+	dcm = Decimal("0.00");
+	assert(!isSubnormal(dcm));
+	dcm = Decimal("-Inf");
+	assert(!isSubnormal(dcm));
+	dcm = Decimal("NaN");
+	assert(!isSubnormal(dcm));
+	
+	dcm = Decimal("0");
+	assert(isZero(dcm));
+	dcm = Decimal("2.50");
+	assert(!isZero(dcm));
+	dcm = Decimal("-0E+2");
+	assert(isZero(dcm));
+
+}
+
+//--------------------------------
+// copy functions
+//--------------------------------
+
+/**
+ * Returns a copy of the operand.
+ * The copy is unaffected by context; no flags are changed.
+ */
+Decimal copy(const Decimal dcm) {
+	Decimal cpy = dcm;
+	return cpy;
+}
+
+/**
+ * Returns a copy of the operand with a positive sign.
+ * The copy is unaffected by context; no flags are changed.
+ */
+Decimal copyAbs(const Decimal dcm) {
+	Decimal cpy = dcm;
+	cpy.sign = false;
+	return cpy;
+}
+
+/**
+ * Returns a copy of the operand with the sign inverted. 
+ * The copy is unaffected by context; no flags are changed.
+ */
+Decimal copyNegate(const Decimal dcm) {
+	Decimal cpy = dcm;
+	cpy.sign = !dcm.sign;
+	return cpy;
+}
+
+/**
+ * Returns a copy of the first operand with the sign of the second operand.
+ * The copy is unaffected by context; no flags are changed.
+ */
+Decimal copySign(const Decimal dcm1, const Decimal dcm2) {
+	Decimal cpy = dcm1;
+	cpy.sign = dcm2.sign;
+	return cpy;
+}
+
+// TODO: these should actually be compare-total assertions
+// This is probably true of other unit tests as well
+unittest {
+	write("copy.........");
+	Decimal dcm;
+	Decimal expd;
+	dcm  = "2.1";
+	expd = "2.1";
+	assert(copy(dcm) == expd);
+	dcm  = "-1.00";
+	expd = "-1.00";
+	assert(copy(dcm) == expd);
+	dcm  = "2.1";
+	expd = "2.1";
+
+	assert(copyAbs(dcm) == expd);
+	dcm  = "-1.00";
+	expd = "1.00";
+	assert(copyAbs(dcm) == expd);
+	dcm  = "101.5";
+	expd = "-101.5";
+
+	assert(copyNegate(dcm) == expd);
+	Decimal dcm1;
+	Decimal dcm2;
+	dcm1 = "1.50";
+	dcm2 = "7.33";
+	expd = "1.50";
+
+	assert(copySign(dcm1, dcm2) == expd);
+	dcm1 = "-1.50";
+	dcm2 = "7.33";
+	expd = "1.50";
+	assert(copySign(dcm1, dcm2) == expd);
+	dcm1 = "1.50";
+	dcm2 = "-7.33";
+	expd = "-1.50";
+	assert(copySign(dcm1, dcm2) == expd);
+	dcm1 = "-1.50";
+	dcm2 = "-7.33";
+	expd = "-1.50";
+	assert(copySign(dcm1, dcm2) == expd);
+	writeln("passed");
+}
+
+//--------------------------------
+// plus, minus and abs functions
+//--------------------------------
+
+/**
+ * unary minus -- returns a copy with the opposite sign.
+ * This operation may set flags -- equivalent to 
+ * subtract('0', b);
+ */
+Decimal minus(const Decimal dcm) {
+	Decimal result;
+	if(isInvalidOperation(dcm, result)) {
+		return result;
+	}
+	result = copyNegate(dcm);
+	round(result);
+	return result;
+}
+	
+/**
+ * unary plus -- returns a copy.
+ * This operation may set flags -- equivalent to 
+ * add('0', a);
+ */
+Decimal plus(const Decimal dcm) {
+	Decimal result;
+	if(isInvalidOperation(dcm, result)) {
+		return result;
+	}
+	result = dcm;
+	round(result);
+	return result;
+}
+	
+unittest {
+	write("minus/plus...");
+	// NOTE: result should equal 0+this or 0-this
+	Decimal zero = Decimal(0);
+	Decimal dcm;
+	Decimal expd;
+	dcm = "1.3";
+	expd = zero + dcm;
+	assert(+dcm == expd);
+	dcm = "-1.3";
+	expd = zero + dcm;
+	assert(+dcm == expd);
+	dcm = "1.3";
+	expd = zero - dcm;
+	assert(-dcm == expd);
+	dcm = "-1.3";
+	expd = zero - dcm;
+	assert(-dcm == expd);
+	// TODO: add tests that check flags.
+	writeln("passed");
+}
+
+/// Returns a new Decimal equal to the absolute value of this Decimal.
+public Decimal abs(const Decimal dcm) {
+	Decimal result;
+	if(isInvalidOperation(dcm, result)) {
+		return result;
+	}
+	result = copyAbs(dcm);
+	round(result);
+	return result;
+}
+	
+unittest {
+	// TODO: add rounding tests
+	write("abs..........");
+	Decimal dcm;
+	Decimal expd;
+	dcm = "sNaN";
+	assert(abs(dcm).isQuiet);
+	assert(context.flags && INVALID_OPERATION);
+	dcm = "NaN";
+	assert(abs(dcm).isQuiet);
+	assert(context.flags && INVALID_OPERATION);
+	dcm = "Inf";
+	expd = "Inf";
+	assert(abs(dcm) == expd);
+	dcm = "-Inf";
+	expd = "Inf";
+	assert(abs(dcm) == expd);
+	dcm = "0";
+	expd = "0";
+	assert(abs(dcm) == expd);
+	dcm = "-0";
+	expd = "0";
+	assert(abs(dcm) == expd);
+	dcm = "2.1";
+	expd = "2.1";
+	assert(abs(dcm) == expd);
+	dcm = -100;
+	expd = 100;
+	assert(abs(dcm) == expd);
+	dcm = 101.5;
+	expd = 101.5;
+	assert(abs(dcm) == expd);
+	dcm = -101.5;
+	assert(abs(dcm) == expd);
+	writeln("passed");
+}
+
+public Decimal nextPlus(const Decimal dcm) {
+	Decimal result;
+	if (isInvalidOperation(dcm, result)) {
+		return result;
+	}
+	if (dcm.isInfinite && dcm.sign) return -context.max();
+	int adjx = dcm.expo + dcm.digits - context.precision;
+	if (adjx < context.eTiny) {
+		return Decimal(true, 0, context.eTiny);
+	}
+	Decimal addend = Decimal(1, adjx);
+	result = dcm + addend;
+	if (result > context.max) {
+		result = POS_INF;
+	}
+	return result;
+}
+
+unittest {
+	write("next-plus....");
+	pushContext();
+	context.eMax = 999;
+	context.eMin = -999;
+	Decimal dcm;
+	Decimal expd;
+	dcm = 1;
+	expd = "1.00000001";
+	assert(nextPlus(dcm) == expd);
+	dcm = 10;
+	expd = "10.0000001";
+	assert(nextPlus(dcm) == expd);
+	dcm = 1E5;
+	expd = "100000.001";
+	assert(nextPlus(dcm) == expd);
+	dcm = 1E8;
+	expd = "100000001";
+	assert(nextPlus(dcm) == expd);
+	// num digits exceeds precision...
+	dcm = "1234567891";
+	expd = "1.23456790E9";
+	assert(nextPlus(dcm) == expd);
+	// result < tiny
+	dcm = "-1E-1007";
+	expd = "-0E-1007";
+	assert(nextPlus(dcm) == expd);
+	dcm = "-1.00000003";
+	expd = "-1.00000002";
+	assert(nextPlus(dcm) == expd);
+	dcm = "-Infinity";
+	expd = "-9.99999999E+999";
+	assert(nextPlus(dcm) == expd);
+	popContext();
+	writeln("passed");
+}
+
+public Decimal nextMinus(const Decimal dcm) {
+	Decimal result;
+	if (isInvalidOperation(dcm, result)) {
+		return result;
+	}
+	if (dcm.isInfinite && !dcm.sign) return context.max();
+	// This is necessary to catch the special case where ceff == 1
+	Decimal red = reduce(dcm);
+	int adjx = red.expo + red.digits - context.precision;
+	if (dcm.ceff == 1) adjx--;
+	if (adjx < context.eTiny) {
+		return Decimal(false, 0, context.eTiny);
+	}
+	Decimal addend = Decimal(1, adjx);
+	result = dcm - addend;
+	if (result < -context.max) {
+		result = NEG_INF;
+	}
+	return result;
+}
+
+unittest {
+	write("next-minus...");
+	pushContext();
+	context.eMax = 999;
+	context.eMin = -999;
+	Decimal dcm;
+	Decimal expd;
+	dcm = 1;
+	expd = "0.999999999";
+	assert(nextMinus(dcm) == expd);
+	dcm = "1E-1007";
+	expd = "0E-1007";
+	assert(nextMinus(dcm) == expd);
+	dcm = "-1.00000003";
+	expd = "-1.00000004";
+	assert(nextMinus(dcm) == expd);
+	dcm = "Infinity";
+	expd = "9.99999999E+999";
+	assert(nextMinus(dcm) == expd);
+	popContext();
+	writeln("passed");
+}
+
+public Decimal nextToward(const Decimal dcm1, const Decimal dcm2) {
+	Decimal result;
+	if (isInvalidOperation(dcm1, dcm2, result)) {
+		return result;
+	}
+	int comp = icompare(dcm1, dcm2);
+	if (comp < 0) return nextPlus(dcm1);
+	if (comp > 0) return nextMinus(dcm1);
+	result = copySign(dcm1, dcm2);
+	round(result);
+	return result;
+}
+
+unittest {
+	write("next-toward..");
+	DecimalContext save = context;
+	Decimal dcm1, dcm2;
+	Decimal expd;
+	dcm1 = 1;
+	dcm2 = 2;
+	expd = "1.00000001";
+	assert(nextToward(dcm1,dcm2) == expd);
+	dcm1 = "-1E-1007";
+	dcm2 = 1;
+	expd = "-0E-1007";
+	assert(nextToward(dcm1,dcm2) == expd);
+	dcm1 = "-1.00000003";
+	dcm2 = 0;
+	expd = "-1.00000002";
+	assert(nextToward(dcm1,dcm2) == expd);
+	dcm1 = 1;
+	dcm2 = 0;
+	expd = "0.999999999";
+	assert(nextToward(dcm1,dcm2) == expd);
+	dcm1 = "1E-1007";
+	dcm2 = -100;
+	expd = "0E-1007";
+	assert(nextToward(dcm1,dcm2) == expd);
+	dcm1 = "-1.00000003";
+	dcm2 = -10;
+	expd = "-1.00000004";
+	assert(nextToward(dcm1,dcm2) == expd);
+	dcm1 = "0.00";
+	dcm2 = "-0.0000";
+	expd = "-0.00";
+	assert(nextToward(dcm1,dcm2) == expd);
+	context = save;
+	writeln("passed");
+}
+
+//--------------------------------
+// comparison functions
+//--------------------------------
+
+/// returns true if the numbers have the same exponent.
+public bool sameQuantum(const Decimal x, const Decimal y) {
+	if (x.isNaN || y.isNaN) {
+		return x.isNaN && y.isNaN;
+	}
+	if (x.isInfinite || y.isInfinite) {
+		return x.isInfinite && y.isInfinite;
+	}
+	return x.expo == y.expo;
+}
+
+unittest {
+	write("same-quantum.");
+	Decimal op1;
+	Decimal op2;
+	op1 = "2.17";
+	op2 = "0.001";
+	assert(!sameQuantum(op1, op2));
+	op2 = "0.01";
+	assert(sameQuantum(op1, op2));
+	op2 = "0.1";
+	assert(!sameQuantum(op1, op2));
+	op2 = "1";
+	assert(!sameQuantum(op1, op2));
+	op1 = "Inf";
+	op2 = "Inf";
+	assert(sameQuantum(op1, op2));
+	op1 = "NaN";
+	op2 = "NaN";
+	assert(sameQuantum(op1, op2));
+	writeln("passed");
+}
+
+public Decimal compare(const Decimal dcm1, const Decimal dcm2, 
+		const bool signal = false) {
+	// sNaN is an invalid operand
+	 if (dcm1.isSignaling && dcm2.isSignaling) {
+		return invalidOp();
+	}
+	// qNaN is invalid if signal flag is set.
+	if (signal && (dcm1.isNaN || dcm2.isNaN)) {
+		return invalidOp();
+	}
+	// NaN returns > any number, including NaN
+	if (dcm1.isNaN) return ONE;
+	if (dcm2.isNaN) return -ONE;
+
+	if (dcm1.sign != dcm2.sign) {
+		Decimal op1 = dcm1.sign ? -ONE : ONE;
+		Decimal op2 = -op1;
+		op1 = dcm1.isZero ? ZERO : op1;
+		op2 = dcm2.isZero ? ZERO : op2;
+		return op1 != op2 ? op1 : ZERO;
+	}
+	int diff = (dcm1.expo + dcm1.digits) - (dcm2.expo + dcm2.digits);
+	if (!dcm1.sign) {
+	if (diff > 0) return ONE;
+	if (diff < 0) return -ONE;
+	}
+	else {
+	if (diff > 0) return -ONE;
+	if (diff < 0) return ONE;
+	}
+	Decimal result = dcm1 - dcm2;
+	if (result.isZero) return ZERO;
+	return result.sign ? -ONE : ONE;
+}
+
+public int icompare(const Decimal dcm1, const Decimal dcm2) {
+	// sNaN is invalid operand
+	// NaN returns > any number, including NaN
+	if (dcm1.isSignaling) {
+		invalidOp();
+		return 1;
+	}
+	if (dcm2.isSignaling) {
+		invalidOp();
+		return -1;
+	}
+	// NaN returns > any number, including NaN
+	if (dcm1.isNaN) return 1;
+	if (dcm2.isNaN) return -1;
+
+	if (dcm1.sign != dcm2.sign) {
+		int op1 = dcm1.sign ? -1 : 1;
+		int op2 = -op1;
+		op1 = dcm1.isZero ? 0 : op1;
+		op2 = dcm2.isZero ? 0 : op2;
+		return op1 != op2 ? op1 : 0;
+	}
+	int diff = (dcm1.expo + dcm1.digits) - (dcm2.expo + dcm2.digits);
+	if (!dcm1.sign) {
+	if (diff > 0) return 1;
+	if (diff < 0) return -1;
+	}
+	else {
+	if (diff > 0) return -1;
+	if (diff < 0) return 1;
+	}
+	Decimal result = dcm1 - dcm2;
+	if (result.isZero) return 0;
+	return result.sign ? -1 : 1;
+}
+
+unittest {
+	write("compare......");
+	Decimal op1;
+	Decimal op2;
+	int result;
+	op1 = "2.1";
+	op2 = "3";
+	result = icompare(op1, op2);
+	assert(result == -1);
+	op1 = "2.1";
+	op2 = "2.1";
+	result = icompare(op1, op2);
+	assert(result == 0);
+	op1 = "2.1";
+	op2 = "2.10";
+	result = icompare(op1, op2);
+	assert(result == 0);
+	op1 = "3";
+	op2 = "2.1";
+	result = icompare(op1, op2);
+	assert(result == 1);
+	op1 = "2.1";
+	op2 = "-3";
+	result = icompare(op1, op2);
+	assert(result == 1);
+	op1 = "-3";
+	op2 = "2.1";
+	result = icompare(op1, op2);
+	assert(result == -1);
+	op1 = -3;
+	op2 = -4;
+	result = icompare(op1, op2);
+	assert(result == 1);
+	op1 = -300;
+	op2 = -4;
+	result = icompare(op1, op2);
+	assert(result == -1);
+	op1 = 3;
+	op2 = context.max;
+	result = icompare(op1, op2);
+	assert(result == -1);
+	op1 = -3;
+	op2 = -context.max;
+	result = icompare(op1, op2);
+	assert(result == 1);
+	
+	writeln("passed");
+}
+
+/// Returns 0 if the numbers are equal and have the same representation
+public int compareTotal(const Decimal x, const Decimal y) {
+	if (x.sign != y.sign) {
+		return x.sign ? -1 : 1;
+	}
+	if (x.isQuiet || y.isQuiet) {
+		if (x.isQuiet && y.isQuiet) {
+			return 0;
+		}
+		return x.isQuiet ? 1 : -1;
+	}
+	if (x.isSignaling || y.isSignaling) {
+		return 0;
+	}
+	if (x.isInfinite || y.isInfinite) {
+		return 0;
+	}
+	int diff = (x.expo + x.digits) - (y.expo + y.digits);
+	if (diff > 0) return 1;
+	if (diff < 0) return -1;
+	Decimal result = x - y;
+	if (result.isZero) {
+		if (x.expo > y.expo) return 1;
+		if (x.expo < y.expo) return -1;
+		return 0;
+	}
+	return result.sign ? -1 : 1;
+}
+
+unittest {
+	write("comp-total...");
+	Decimal op1;
+	Decimal op2;
+	int result;
+	op1 = "12.73";
+	op2 = "127.9";
+	result = compareTotal(op1, op2);
+	assert(result == -1);
+	op1 = "-127";
+	op2 = "12";
+	result = compareTotal(op1, op2);
+	assert(result == -1);
+	op1 = "12.30";
+	op2 = "12.3";
+	result = compareTotal(op1, op2);
+	assert(result == -1);
+	op1 = "12.30";
+	op2 = "12.30";
+	result = compareTotal(op1, op2);
+	assert(result == 0);
+	op1 = "12.3";
+	op2 = "12.300";
+	result = compareTotal(op1, op2);
+	assert(result == 1);
+	op1 = "12.3";
+	op2 = "NaN";
+	result = compareTotal(op1, op2);
+	assert(result == -1);
+	writeln("passed");
+}
+
+int compareTotalMagnitude(const Decimal x, const Decimal y) {
+	return compareTotal(copyAbs(x), copyAbs(y));
+}
+
+// TODO: this is where the need for flags comes in.
+/**
+ * Returns the maximum of the two operands (or NaN).
+ * If either is an sNaN, or both are quiet NaNs, a NaN is returned.
+ * Otherwise, Any (finite or infinite) number is larger than a NaN.
+ * If they are not numerically equal, the larger is returned.
+ * If they are numerically equal:
+ * 1) If the signs differ, the one with the positive sign is returned. 
+ * 2) If they are positive, the one with the larger exponent is returned.
+ * 3) If they are negative, the one with the smaller exponent is returned.
+ * 4) Otherwise, they are indistinguishable; the first is returned.
+ */
+Decimal max(const Decimal op1, const Decimal op2) {
+	// if both are NaNs or either is an sNan, return NaN.
+	if (op1.isNaN && op2.isNaN || op1.isSignaling || op2.isSignaling) {
+		return NaN;
+	}
+	// if one op is a quiet NaN return the other
+	if (op1.isQuiet || op2.isQuiet) {
+		return (op1.isQuiet) ? op2 : op1;
+	}
+	// if the signs differ, return the unsigned operand
+	if (op1.sign != op2.sign) {
+		return op1.sign ? op2 : op1;
+	}
+	// if not numerically equal, return the larger
+	int comp = icompare(op1, op2);
+	if (comp != 0) {
+		return comp > 0 ? op1 : op2;
+	}
+	// if they have the same exponent they are identical, return either
+	if (op1.expo == op2.expo) {
+		return op1;
+	}
+	// if they are non-negative, return the one with larger exponent.
+	if (op1.sign == 0) {
+		return op1.expo > op2.expo ? op1 : op2;
+	}
+	// else they are negative; return the one with smaller exponent.
+	return op1.expo > op2.expo ? op2 : op1;
+}
+
+unittest {
+	write("max..........");
+	Decimal op1;
+	Decimal op2;
+	op1 = 3;
+	op2 = 2;
+	assert(max(op1, op2) == op1);
+	op1 = -10;
+	op2 = 3;
+	assert(max(op1, op2) == op2);
+	op1 = "1.0";
+	op2 = "1";
+	assert(max(op1, op2) == op2);
+	op1 = "7";
+	op2 = "NaN";
+	assert(max(op1, op2) == op1);
+	writeln("passed");
+}
+
+/**
+ * Returns the minimum of the two operands (or NaN).
+ * If either is an sNaN, or both are quiet NaNs, a NaN is returned.
+ * Otherwise, Any (finite or infinite) number is smaller than a NaN.
+ * If they are not numerically equal, the smaller is returned.
+ * If they are numerically equal:
+ * 1) If the signs differ, the one with the negative sign is returned. 
+ * 2) If they are negative, the one with the larger exponent is returned.
+ * 3) If they are positive, the one with the smaller exponent is returned.
+ * 4) Otherwise, they are indistinguishable; the first is returned.
+ */
+Decimal min(const Decimal op1, const Decimal op2) {
+	// if both are NaNs or either is an sNan, return NaN.
+	if (op1.isNaN && op2.isNaN || op1.isSignaling || op2.isSignaling) {
+/+		Decimal result;
+		result.flags = INVALID_OPERATION;+/
+		return NaN;
+	}
+	// if one op is a quiet NaN return the other
+	if (op1.isQuiet || op2.isQuiet) {
+		return (op1.isQuiet) ? op2 : op1;
+	}
+	// if the signs differ, return the unsigned operand
+	if (op1.sign != op2.sign) {
+		return op1.sign ? op1 : op2;
+	}
+	// if not numerically equal, return the smaller
+	int comp = icompare(op1, op2);
+	if (comp != 0) {
+		return comp < 0 ? op1 : op2;
+	}
+	// if they have the same exponent they are identical, return either
+	if (op1.expo == op2.expo) {
+		return op1;
+	}
+	// if they are non-negative, return the one with smaller exponent.
+	if (op1.sign == 0) {
+		return op1.expo < op2.expo ? op1 : op2;
+	}
+	// else they are negative; return the one with larger exponent.
+	return op1.expo < op2.expo ? op2 : op1;
+}
+
+unittest {
+	write("min..........");
+	Decimal op1;
+	Decimal op2;
+	op1 = 3;
+	op2 = 2;
+	assert(min(op1, op2) == op2);
+	op1 = -10;
+	op2 = 3;
+	assert(min(op1, op2) == op1);
+	op1 = "1.0";
+	op2 = "1";
+	assert(min(op1, op2) == op1);
+	op1 = "7";
+	op2 = "NaN";
+	assert(min(op1, op2) == op1);
+	writeln("passed");
+}
+
+//------------------------------------------
+//
+// Binary Arithmetic Operations
+//
+//------------------------------------------
+	
+/**
+ * Adds two Decimal numbers.
+ *
+ * This function corresponds to the "add and subtract" function 
+ * in the General Decimal Arithmetic Specification and is the basis
+ * for the opAdd and opSub functions for the Decimal struct.
+ */
+Decimal add(const Decimal augend, const Decimal addend) {
+		
+	Decimal sum;
+	// check for NaN operand(s)
+	if (isInvalidOperation(augend, addend, sum)) {
+		return sum;
+	}
+	// if both operands are infinite
+	if (augend.isInfinite && addend.isInfinite) {
+		// (+inf) + (-inf) => invalid operation
+		if (augend.sign != addend.sign) {
+			return invalidOp();
+		}
+		// both infinite with same sign
+		return augend;
+	}
+
+/+	if (isInvalidAddition(augend, addend, sum)) {
+		return sum;
+	}+/
+// TODO: is it okay to return the operand? is a copy implied?	
+	// only augend is infinite, 
+	if (augend.isInfinite) {
+		return augend;
+	}
+	// only addend is infinite
+	if (addend.isInfinite) {
+		return addend;
+	}
+	
+	// align the operands
+	alignOps(augend, addend);
+
+	// add(0, 0)
+	if (augend.isZero && addend.isZero) {
+		sum = augend;
+		sum.sign = augend.sign && addend.sign;
+		return sum;
+	}
+	
+	// at this point, the result will be finite and not zero
+	// (before rounding)
+	sum.clear();
+	
+	// if operands have the same sign...
+	if (augend.sign == addend.sign) {
+		sum.ceff = augend.ceff + addend.ceff;
+		sum.sign = augend.sign;
+	}
+	// ...else operands have different signs
+	else {
+		sum.ceff = augend.ceff - addend.ceff;
+		sum.sign = augend.sign;
+		if (sum.ceff < BIG_ZERO) {
+			sum.ceff = -sum.ceff;
+			sum.sign = !sum.sign;
+		}
+	}
+	// set the number of digits and the exponent
+	sum.digits = numDigits(sum.ceff, augend.digits);
+	sum.expo = augend.expo;
+	
+	// round the result
+	round(sum);
+	return sum;
+}	// end add(augend, addend)
+
+/**
+ * Subtracts two Decimal numbers.
+ *
+ * This function corresponds to the "add and subtract" function 
+ * in the General Decimal Arithmetic Specification and is the basis
+ * for the opAdd and opSub functions for the Decimal struct.
+ */
+Decimal subtract(const Decimal minuend, const Decimal subtrahend) {
+	return add(minuend, copyNegate(subtrahend));
+}	// end subtract(minuend, subtrahend)
+
+// TODO: these tests need to be cleaned up to rely less on strings
+// and to check the NaN, Inf combinations better.
+unittest {
+	write("add..........");
+	Decimal dcm1 = Decimal("12");
+	Decimal dcm2 = Decimal("7.00");
+	Decimal sum = add(dcm1, dcm2);
+	assert(sum.toString() == "19.00");
+	dcm1 = Decimal("1E+2");
+	dcm2 = Decimal("1E+4");
+	sum = add(dcm1, dcm2);
+	assert(sum.toString() == "1.01E+4");
+	dcm1 = Decimal("1.3");
+	dcm2 = Decimal("1.07");
+	sum = subtract(dcm1, dcm2);
+	assert(sum.toString() == "0.23");
+	dcm2 = Decimal("1.30");
+	sum = subtract(dcm1, dcm2);
+	assert(sum.toString() == "0.00");
+	dcm2 = Decimal("2.07");
+	sum = subtract(dcm1, dcm2);
+	assert(sum.toString() == "-0.77");
+	dcm1 = "Inf";
+	dcm2 = 1;
+	sum = add(dcm1, dcm2);
+	assert(sum.toString() == "Infinity");
+	dcm1 = "NaN";
+	dcm2 = 1;
+	sum = add(dcm1, dcm2);
+	assert(sum.isQuiet);
+	dcm2 = "Infinity";
+	sum = add(dcm1, dcm2);
+	assert(sum.isQuiet);
+	dcm1 = 1;
+	sum = subtract(dcm1, dcm2);
+	assert(sum.toString() == "-Infinity");
+	dcm1 = "-0";
+	dcm2 = 0;
+	sum = subtract(dcm1, dcm2);
+	assert(sum.toString() == "-0");
+	writeln("passed");
+}
+
+Decimal multiply(const Decimal multiplier, const Decimal multiplicand) {
+
+	Decimal product;
+	if (isInvalidMultiplication(multiplier, multiplicand, product)) {
+		return product;
+	}
+	if (multiplier.isInfinite || multiplicand.isInfinite) {
+		product = Decimal.infinity;
+		product.sign = multiplier.sign ^ multiplicand.sign;
+		return product;
+	}
+	product.clear();
+	product.ceff = multiplier.ceff * multiplicand.ceff;
+	product.expo = multiplier.expo + multiplicand.expo;
+	product.sign = multiplier.sign ^ multiplicand.sign;
+	product.digits = numDigits(product.ceff, multiplier.digits + multiplicand.digits);
+	round(product);
+	return product;
+}
+
+unittest {
+	write("multiply.....");
+	Decimal op1, op2, result;
+	op1 = Decimal("1.20");
+	op2 = 3;
+	result = op1 * op2;
+	assert(result.toString() == "3.60");
+	op1 = 7;
+	result = op1 * op2;
+	assert(result.toString() == "21");
+	op1 = Decimal("0.9");
+	op2 = Decimal("0.8");
+	result = op1 * op2;
+	assert(result.toString() == "0.72");
+	op1 = Decimal("0.9");
+	op2 = Decimal("-0.0");
+	result = op1 * op2;
+	assert(result.toString() == "-0.00");
+	op1 = Decimal(654321);
+	op2 = Decimal(654321);
+	result = op1 * op2;
+	assert(result.toString() == "4.28135971E+11");
+	op1 = -1;
+	op2 = "Infinity";
+	result = op1 * op2;
+	assert(result.toString() == "-Infinity");
+	op1 = -1;
+	op2 = 0;
+	result = op1 * op2;
+	assert(result.toString() == "-0");
+	writeln("passed");
+}
+
+Decimal fma(const Decimal multiplier, const Decimal multiplicand,
+		const Decimal addend) {
+	Decimal product;
+	if (isInvalidMultiplication(multiplier, multiplicand, product)) {
+		return product;
+	}
+	product.clear();
+	product.ceff = multiplier.ceff * multiplicand.ceff;
+	product.expo = multiplier.expo + multiplicand.expo;
+	product.sign = multiplier.sign ^ multiplicand.sign;
+	product.digits = numDigits(product.ceff, multiplier.digits + multiplicand.digits);
+	return add(product, addend);
+}
+
+unittest {
+	write("fma..........");
+	Decimal op1;
+	Decimal op2;
+	Decimal op3;
+	Decimal result;
+	op1 = 3;
+	op2 = 5;
+	op3 = 7;
+	result = (fma(op1, op2, op3));
+	assert(result == Decimal(22));
+	op1 = 3;
+	op2 = -5;
+	op3 = 7;
+	result = (fma(op1, op2, op3));
+	assert(result == Decimal(-8));
+	op1 = "888565290";
+	op2 = "1557.96930";
+	op3 = "-86087.7578";
+	result = (fma(op1, op2, op3));
+	assert(result == Decimal("1.38435736E+12"));
+	writeln("passed");
+}
+
+bool isZeroDividend(const Decimal dividend, const Decimal divisor,
+		Decimal quotient) {
+	if (dividend.isZero()) {
+		quotient.spval = SpVal.ZERO;
+		quotient.ceff = BIG_ZERO;
+		quotient.expo = 0;
+		quotient.digits = dividend.digits;
+		quotient.sign = dividend.sign;
+		return true;
+	}
+	return false;
+}
+
+Decimal divide(const Decimal dividend, const Decimal divisor) {
+
+	Decimal quotient;
+	if (isInvalidDivision(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	if (isZeroDividend(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	quotient.clear();
+
+	Decimal temp = dividend;
+	int adjust = 0;
+	while (temp.ceff < divisor.ceff) {
+		temp.ceff *= 10;
+		adjust++;
+	}
+	bool complete = false;
+	while (!complete) {
+		// repeated subtraction
+		while (divisor.ceff <= temp.ceff) {
+			temp.ceff -= divisor.ceff;
+			quotient.ceff++;
+		}
+		// check for done
+		if (temp.ceff == 0 && adjust >= 0 
+			|| numDigits(quotient.ceff, 1) == context.precision + 2) {
+			complete = true;
+		}
+		else {
+			// bump the quotient and temp by 10
+			quotient.ceff *= 10;
+			temp.ceff *= 10;
+			adjust++;
+		}
+		quotient.digits = numDigits(quotient.ceff, context.precision);
+	}
+	quotient.expo = temp.expo - divisor.expo - adjust;
+	quotient.sign = temp.sign ^ divisor.sign;
+	round(quotient);
+	return quotient;
+}
+
+Decimal edivide(const Decimal dividend, const Decimal divisor) {
+	Decimal quotient;
+	if (isInvalidDivision(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	if (isZeroDividend(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	quotient.clear();
+	Decimal denom = dividend;
+	Decimal numer = divisor;
+	// align operands
+	int diff = denom.expo - numer.expo;
+	if (diff < 0) {
+		numer.ceff *= pow10(-diff);
+	}
+	if (diff > 0) {
+		numer.ceff *= pow10(diff);
+	}
+	numer.digits = numDigits(numer.ceff, 1);
+	denom.digits = numDigits(denom.ceff, 1);
+	int shift = 2 + context.precision - denom.digits + numer.digits;
+	if (shift > 0) {
+		denom.ceff *= pow10(shift);
+		denom.expo -= shift;
+	}
+	quotient.ceff = denom.ceff / numer.ceff;
+	quotient.expo = denom.expo - numer.expo;
+	quotient.sign = denom.sign ^ numer.sign;
+	quotient.digits = numDigits(quotient.ceff, context.precision);
+	round(quotient);
+	return quotient;
+}
+
+unittest {
+	write("divide.......");
+	Decimal dcm1, dcm2;
+	Decimal expd;
+	dcm1 = 1;
+	dcm2 = 3;
+	Decimal quotient = edivide(dcm1, dcm2);
+	expd = "0.333333333";
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	dcm1 = 2;
+	dcm2 = 3;
+	quotient = edivide(dcm1, dcm2);
+	expd = "0.666666667";
+	assert(quotient == expd);
+	dcm1 = 5;
+	dcm2 = 2;
+	quotient = divide(dcm1, dcm2);
+	expd = "2.5";
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	dcm1 = 1;
+	dcm2 = 10;
+	expd = 0.1;
+	quotient = divide(dcm1, dcm2);
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	dcm1 = "8.00";
+	dcm2 = 2;
+	expd = "4.00";
+	quotient = divide(dcm1, dcm2);
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	dcm1 = "2.400";
+	dcm2 = "2.0";
+	expd = "1.20";
+	quotient = divide(dcm1, dcm2);
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	dcm1 = 1000;
+	dcm2 = 100;
+	expd = 10;
+	quotient = divide(dcm1, dcm2);
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	dcm2 = 1;
+	quotient = divide(dcm1, dcm2);
+	expd = 1000;
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	dcm1 = "2.40E+6";
+	dcm2 = 2;
+	expd = "1.20E+6";
+	quotient = divide(dcm1, dcm2);
+	assert(quotient == expd);
+	assert(quotient.toString() == expd.toString());
+	writeln("passed");
+}
+
+Decimal divideInteger(const Decimal dividend, const Decimal divisor) {
+	Decimal quotient;
+	if (isInvalidDivision(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	if (isZeroDividend(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	quotient.clear();
+	Decimal denom = dividend;
+	Decimal numer = divisor;
+	// align operands
+	int diff = denom.expo - numer.expo;
+	if (diff < 0) {
+		numer.ceff *= pow10(-diff);
+	}
+	if (diff > 0) {
+		denom.ceff *= pow10(diff);
+	}
+	quotient.ceff = denom.ceff / numer.ceff;
+	quotient.expo = 0;
+	quotient.sign = denom.sign ^ numer.sign;
+	quotient.digits = numDigits(quotient.ceff, context.precision);
+	if (quotient.ceff == 0) quotient.spval = SpVal.ZERO;
+	return quotient;
+}
+
+unittest {
+	write("div-int......");
+	Decimal dividend;
+	Decimal divisor;
+	Decimal quotient;
+	Decimal expd;
+	dividend = 2;
+	divisor = 3;
+	quotient = divideInteger(dividend, divisor);
+	expd = 0;
+	assert(quotient == expd);
+	dividend = 10;
+	quotient = divideInteger(dividend, divisor);
+	expd = 3;
+	assert(quotient == expd);
+	dividend = 1;
+	divisor = "0.3";
+	quotient = divideInteger(dividend, divisor);
+	assert(quotient == expd);
+	writeln("passed");
+}
+
+Decimal remainder(const Decimal dividend, const Decimal divisor) {
+	Decimal quotient;
+	if (isInvalidDivision(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	if (isZeroDividend(dividend, divisor, quotient)) {
+		return quotient;
+	}
+	quotient = dividend - divisor * divideInteger(dividend, divisor);
+	return quotient;
+}
+
+unittest {
+	write("remainder....");
+	Decimal dividend;
+	Decimal divisor;
+	Decimal quotient;
+	Decimal expected;
+	dividend = "2.1";
+	divisor = 3;
+	quotient = remainder(dividend, divisor);
+	expected = "2.1";
+	assert(quotient == expected);
+	dividend = 10;
+	quotient = remainder(dividend, divisor);
+	expected = 1;
+	assert(quotient == expected);
+	dividend = -10;
+	quotient = remainder(dividend, divisor);
+	expected = -1;
+	assert(quotient == expected);
+	dividend = 10.2;
+	divisor = 1;
+	quotient = remainder(dividend, divisor);
+	expected = "0.2";
+	assert(quotient == expected);
+	dividend = 10;
+	divisor = 0.3;
+	quotient = remainder(dividend, divisor);
+	expected = "0.1";
+	assert(quotient == expected);
+	dividend = 3.6;
+	divisor = 1.3;
+	quotient = remainder(dividend, divisor);
+	expected = "1.0";
+	assert(quotient == expected);
+	writeln("passed");
+}
+
+//--------------------------------
+// rounding
+//--------------------------------
+
+public Decimal rint(const Decimal dec){
+	if (dec.isSignaling) return invalidOp();
+	if (dec.isSpecial) return dec;
+	if (dec.expo >= 0) return dec;
+	pushContext();
+	context.precision = dec.digits;
+	Decimal result = quantize(dec, ONE);
+	popContext();
+	return result;
+}
+
+public Decimal nearbyint(const Decimal dec){
+	// this operation shouldn't affect the inexact or rounded flags
+	// so we'll save them in case they were already set.
+	bool inexact = context.getFlag(INEXACT);
+	bool rounded = context.getFlag(ROUNDED);
+	Decimal result = rint(dec);
+	context.setFlag(INEXACT, inexact);
+	context.setFlag(ROUNDED, rounded);
+	return result;
+}
+
+unittest {
+	write("rnd-int-ex...");
+	Decimal dec;
+	Decimal expd;
+	Decimal actual;
+	dec = 2.1;
+	expd = 2;
+	actual = rint(dec);
+	assert(actual == expd);
+	dec = 100;
+	expd = 100;
+	assert(rint(dec) == expd);
+	assert(rint(dec).toString() == expd.toString());
+	dec = "100.0";
+	assert(rint(dec) == expd);
+	assert(rint(dec).toString() == expd.toString());
+	dec = "101.5";
+	expd = 102;
+	assert(rint(dec) == expd);
+	assert(rint(dec).toString() == expd.toString());
+	dec = "-101.5";
+	expd = -102;
+	assert(rint(dec) == expd);
+	assert(rint(dec).toString() == expd.toString());
+	dec = "10E+5";
+	expd = "1.0E+6";
+	assert(rint(dec) == expd);
+	assert(rint(dec).toString() == expd.toString());
+	dec = "7.89E+77";
+	expd = "7.89E+77";
+	assert(rint(dec) == expd);
+	assert(rint(dec).toString() == expd.toString());
+	dec = "-Inf";
+	expd = "-Infinity";
+	assert(rint(dec) == expd);
+	assert(rint(dec).toString() == expd.toString());
+	writeln("passed");
+}
+
+/**
+ * Clips the coefficient of the number to the specified precision.
+ * Returns the remainder for adjustments based on rounding mode.
+ * Sets the ROUNDED and INEXACT flags.
+ */               
+private BigInt shorten(ref Decimal number) {
+	BigInt remainder = BIG_ZERO;
+	int diff = number.digits - context.precision;
+	if (diff <= 0) {
+		return remainder;
+	}
+	context.setFlag(ROUNDED);
+	if (context.precision == 0) {
+		remainder = number.ceff;
+		number.ceff = 0;
+		number.digits = 1;
+	}
+	else {
+		BigInt divisor = pow10(diff);
+		remainder = number.ceff % divisor;
+		number.ceff /= divisor;
+		number.digits = context.precision;
+		number.expo += diff;
+	}
+	if (remainder != BIG_ZERO) {
+		context.setFlag(INEXACT);
+	}
+	return remainder;
+}
+
+/**
+ * Increments the coefficient by 1. If this causes an overflow, divides by 10.
+ */
+private void increment(ref BigInt number) {
+	number++;
+	if (lastDigit(number) == 0) {
+		number /= 10;
+	}
+}
+
+// TODO: need to signal inexact and rounded.
+private void roundByMode(ref Decimal number) {
+	BigInt remainder = shorten(number);
+	
+	// if the rounded flag is not set by the shorten operation, return
+	if (!context.getFlag(ROUNDED)) {
+		return;
+	}
+	// if the remainder is zero, return
+	if (remainder == BIG_ZERO) {
+		return;
+	}
+	
+	switch (context.mode) {
+		case Rounding.DOWN:
+			return;
+		case Rounding.HALF_UP:
+			if (firstDigit(remainder) >= 5) {
+				increment(number.ceff);
+			}
+			return;
+		case Rounding.HALF_EVEN:
+			BigInt test = 5 * pow10(numDigits(remainder,1)-1);
+			int result = remainder.opCmp(test);
+			if (result > 0) {
+				increment(number.ceff);
+				return;
+			}
+			if (result < 0) {
+				return;
+			}
+			// if last digit is odd...
+			if (lastDigit(number.ceff) & 1 == 1) {
+				increment(number.ceff);
+			}
+			return;
+		case Rounding.CEILING:
+			if (!number.sign && remainder != BIG_ZERO) {
+				increment(number.ceff);
+			}
+			return;
+		case Rounding.FLOOR:
+			if (number.sign && remainder != BIG_ZERO) {
+				increment(number.ceff);
+			}
+			return;
+		case Rounding.HALF_DOWN:
+			if (firstDigit(remainder) > 5) {
+				increment(number.ceff);
+			}
+			return;
+		case Rounding.UP:
+			if (remainder != BIG_ZERO) {
+				increment(number.ceff);
+			}
+			return;
+	}	// end switch(mode)
+} // end roundByMode()
+
+public void round(ref Decimal number) {
+	if (!number.isFinite) return;
+
+	context.clearFlags();
+	// check for subnormal
+	bool subnormal = false;
+	if (number.isSubnormal()) {
+		context.setFlag(SUBNORMAL);
+		subnormal = true;
+	}
+
+	// check for overflow
+	if (number.overflow()) {
+		context.setFlag(OVERFLOW);
+		switch (context.mode) {
+			case Rounding.HALF_UP:
+			case Rounding.HALF_EVEN:
+			case Rounding.HALF_DOWN:
+			case Rounding.UP:
+				bool sign = number.sign;
+				number = POS_INF;
+				number.sign = sign;
+				break;
+			case Rounding.DOWN:
+				bool sign = number.sign;
+				number = context.max;
+				number.sign = sign;
+				break;
+			case Rounding.CEILING:
+				if (number.sign) {
+					number = context.max;
+					number.sign = true;
+				}
+				else {
+					number = POS_INF;
+				}
+				break;
+			case Rounding.FLOOR:
+				if (number.sign) {
+					number = NEG_INF;
+				} else {
+					number = context.max;
+				}
+				break;
+		}
+		context.setFlag(INEXACT);
+		context.setFlag(ROUNDED);
+		return;
+	}
+	roundByMode(number);
+	// check for underflow
+	if (number.isSubnormal /+&& number.isInexact+/) {
+		context.setFlag(SUBNORMAL);
+		int diff = context.eTiny - number.adjustedExponent();
+		if (diff > number.digits) {
+			number.ceff = 0;
+			number.expo = context.eTiny;
+		} else if (diff > 0) {
+			writeln("We got a tiny one!");
+		}
+	}
+	// check for zero
+	if (number.spval == SpVal.CLEAR && number.ceff == BIG_ZERO) {
+		number.spval = SpVal.ZERO;
+		// subnormal rounding to zero == clamped
+		// Spec. p. 51
+		if (subnormal) {
+			context.setFlag(CLAMPED);
+		}
+		return;
+	}
+} // end round()
+
+unittest {
+	write("round........");
+	Decimal before = Decimal(1234567890);
+	Decimal after = before;
+	pushContext();
+	context.precision = 3;
+	round(after);
+	assert(after.toString() == "1.23E+9");
+	after = before;
+	context.precision = 4;
+	round(after);
+	assert(after.toString() == "1.235E+9");
+	after = before;
+	context.precision = 5;
+	round(after);
+	assert(after.toString() == "1.2346E+9");
+	after = before;
+	context.precision = 6;
+	round(after);
+	assert(after.toString() == "1.23457E+9");
+	after = before;
+	context.precision = 7;
+	round(after);
+	assert(after.toString() == "1.234568E+9");
+	after = before;
+	context.precision = 8;
+	round(after);
+	assert(after.toString() == "1.2345679E+9");
+	before = "1235";
+	after = before;
+	context.precision = 3;
+	round(after);
+	assert(after.toAbstract() == "[0,124,1]");
+	before = "12359";
+	after = before;
+	context.precision = 3;
+	round(after);
+	assert(after.toAbstract() == "[0,124,2]");
+	before = "1245";
+	after = before;
+	context.precision = 3;
+	round(after);
+	assert(after.toAbstract() == "[0,124,1]");
+	before = "12459";
+	after = before;
+	context.precision = 3;
+	round(after);
+	assert(after.toAbstract() == "[0,125,2]");
+	popContext();
+	writeln("passed");
+}
+
+public void setDigits(ref Decimal number) {
+	int diff = number.digits - context.precision;
+	if (diff > 0) {
+		round(number);
+	}
+	else if (diff < 0) {
+		number.ceff *= pow10(-diff);
+		number.expo += diff;
+	}
+	number.digits = context.precision;
+}
+
+/**
+ * Returns the number which is equal in value and sign
+ * to the first operand and which has its exponent set
+ * to be equal to the exponent of the second operand.
+ */
+ // TODO: this has unusual flag rules
+Decimal quantize(const Decimal dcm1, const Decimal dcm2) {
+	Decimal result;
+	if (isInvalidOperation(dcm1, dcm2, result)) {
+		return result;
+	}
+	if (dcm1.isInfinite != dcm2.isInfinite() || 
+		dcm2.isInfinite != dcm1.isInfinite()) {
+		return invalidOp();
+	}
+	if (dcm1.isInfinite() && dcm2.isInfinite()) {
+		return dcm1.dup;
+	}
+	result = dcm1;
+	int diff = dcm1.expo - dcm2.expo;
+	if (diff == 0) {
+		return result;
+	}
+	if (diff > 0) {
+		result.ceff *= pow10(diff);
+		result.digits += diff;
+		result.expo = dcm2.expo; 
+		if (result.digits > context.precision) {
+			result = NaN;
+		}
+		return result;
+	}
+	else {
+		pushContext();
+		context.precision = 
+			-diff > dcm1.digits ? 0 : dcm1.digits + diff;
+		round(result);
+		result.expo = dcm2.expo; 
+		popContext();
+		return result;
+	}
+}
+
+unittest {
+	write("quantize.....");
+	Decimal op1;
+	Decimal op2;
+	Decimal result;
+	Decimal expd;
+	string str;
+	op1 = "2.17";
+	op2 = "0.001";
+	expd = "2.170";
+	result = quantize(op1, op2);
+	assert(result == expd);
+	op1 = "2.17";
+	op2 = "0.01";
+	expd = "2.17";
+	result = quantize(op1, op2);
+	assert(result == expd);
+	op1 = "2.17";
+	op2 = "0.1";
+	expd = "2.2";
+	result = quantize(op1, op2);
+	assert(result == expd);
+	op1 = "2.17";
+	op2 = "1e+0";
+	expd = "2";
+	result = quantize(op1, op2);
+	assert(result == expd);
+	op1 = "2.17";
+	op2 = "1e+1";
+	expd = "0E+1";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "-Inf";
+	op2 = "Infinity";
+	expd = "-Infinity";
+	result = quantize(op1, op2);
+	assert(result == expd);
+	op1 = "2";
+	op2 = "Infinity";
+	expd = "NaN";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "-0.1";
+	op2 = "1";
+	expd = "-0";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "-0";
+	op2 = "1e+5";
+	expd = "-0E+5";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "+35236450.6";
+	op2 = "1e-2";
+	expd = "NaN";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "-35236450.6";
+	op2 = "1e-2";
+	expd = "NaN";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "217";
+	op2 = "1e-1";
+	expd = "217.0";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "217";
+	op2 = "1e+0";
+	expd = "217";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "217";
+	op2 = "1e+1";
+	expd = "2.2E+2";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	op1 = "217";
+	op2 = "1e+2";
+	expd = "2E+2";
+	result = quantize(op1, op2);
+	assert(result.toString() == expd.toString());
+	assert(result == expd);
+	writeln("passed");
+}
+
+/**
+ * Reduces operand to simplest form. All trailing zeros are removed.
+ */
+ // TODO: has non-standard flag setting
+Decimal reduce(const Decimal dcm) {
+	Decimal result;
+	if (isInvalidOperation(dcm, result)) {
+		return result;
+	}
+	result = dcm;
+	if (!result.isFinite()) {
+		return result;
+	}
+	BigInt temp = result.ceff % 10;
+	while (result.ceff != 0 && temp == 0) {
+		result.expo++;
+		result.ceff = result.ceff / 10;
+		temp = result.ceff % 10;
+	}
+	if (result.ceff == 0) {
+		result.spval = SpVal.ZERO;
+		result.expo = 0;
+	}
+	result.digits = numDigits(result.ceff);
+	return result;
+}
+
+unittest {
+	write("reduce.......");
+	Decimal dec;
+	Decimal red;
+	string str;
+	dec = "2.1";
+	str = "2.1";
+	red = reduce(dec);
+	assert(red.toString() == str);
+	dec = "-2.0";
+	str = "-2";
+	red = reduce(dec);
+	assert(red.toString() == str);
+	dec = "1.200";
+	str = "1.2";
+	red = reduce(dec);
+	assert(red.toString() == str);
+	dec = "-120";
+	str = "-1.2E+2";
+	red = reduce(dec);
+	assert(red.toString() == str);
+	dec = "120.00";
+	str = "1.2E+2";
+	red = reduce(dec);
+	assert(red.toString() == str);
+	writeln("passed");
+}
+
+private:
+
+/** 
+ * Sets the invalid-operation flag and 
+ * returns a quiet NaN.
+ */
+Decimal invalidOp() {
+	context.flags |= INVALID_OPERATION;
+	return NaN;
+}
+
+/**
+ * aligns the two operands by raising the smaller exponent
+ * to the value of the larger exponent, and adjusting the
+ * coefficient so the value remains the same.
+ */
+void alignOps(ref Decimal op1, ref Decimal op2) {
+	int diff = op1.expo - op2.expo;
+	if (diff > 0) {
+		op1.ceff *= pow10(diff);
+		op1.expo = op2.expo;
+	}
+	else if (diff < 0) {
+		op2.ceff *= pow10(-diff);
+		op2.expo = op1.expo;
+	}
+}
+
+/* 
+ * "The result of any arithmetic operation which has an operand
+ * which is a NaN (a quiet NaN or a signaling NaN) is [s,qNaN]
+ * or [s,qNaN,d]. The sign and any diagnostic information is copied
+ * from the first operand which is a signaling NaN, or if neither is
+ * signaling then from the first operand which is a NaN."
+ * -- General Decimal Arithmetic Specification, p. 24
+ */
+bool isInvalidOperation(const Decimal dcm1, const Decimal dcm2,
+		ref Decimal result) {
+	// if either operand is an sNaN...
+	if (dcm1.isSignaling || dcm2.isSignaling) {
+		// set the result to the first sNaN operand
+		result = dcm1.isSignaling ? dcm1 : dcm2;
+		// retain sign and payload; convert to qNaN
+		result.spval = SpVal.QNAN;
+		// flag the invalid operation
+		context.flags |= INVALID_OPERATION;
+		return true;
+	}
+	// ...else if either operand is a qNaN...
+	if (dcm1.isQuiet || dcm2.isQuiet) {
+		// set the result to the first qNaN operand
+		result = dcm1.isQuiet ? dcm1 : dcm2;
+		// flag the invalid operation
+		context.flags |= INVALID_OPERATION;
+		return true;
+	}
+	// ...otherwise, no flags are set and result is unchanged
+	return false;
+}
+
+unittest {
+	write("invalid......");
+	Decimal dcm;
+	Decimal expd;
+	Decimal actual;
+
+	dcm = "sNaN123";
+	expd = "NaN123";
+	actual = abs(dcm);
+	assert(actual.isQuiet);
+	assert(context.flags && INVALID_OPERATION);
+	assert(actual.toAbstract == expd.toAbstract);
+	dcm = "NaN123";
+	actual = abs(dcm);
+	assert(actual.isQuiet);
+	assert(context.flags && INVALID_OPERATION);
+	assert(actual.toAbstract == expd.toAbstract);
+	
+	dcm = "sNaN123";
+	expd = "NaN123";
+	actual = -dcm;
+	assert(actual.isQuiet);
+	assert(context.flags && INVALID_OPERATION);
+	assert(actual.toAbstract == expd.toAbstract);
+	dcm = "NaN123";
+	actual = -dcm;
+	assert(actual.isQuiet);
+	assert(context.flags && INVALID_OPERATION);
+	assert(actual.toAbstract == expd.toAbstract);
+	writeln("passed");
+}
+
+/* 
+ * "The result of any arithmetic operation which has an operand
+ * which is a NaN (a quiet NaN or a signaling NaN) is [s,qNaN]
+ * or [s,qNaN,d]. The sign and any diagnostic information is copied
+ * from the first operand which is a signaling NaN, or if neither is
+ * signaling then from the first operand which is a NaN."
+ * -- General Decimal Arithmetic Specification, p. 24
+ */
+bool isInvalidOperation(const Decimal dcm, ref Decimal result) {
+	// if the operand is an sNaN...
+	if (dcm.isSignaling) {
+		// set the result to the sNaN operand
+		result = dcm;
+		// retain sign and payload; convert to qNaN
+		result.spval = SpVal.QNAN;
+		// flag the invalid operation
+		context.flags |= INVALID_OPERATION;
+		return true;
+	}
+	// ...else if the operand is a qNaN...
+	if (dcm.isQuiet) {
+		// set the result to the qNaN operand
+		result = dcm;
+		// flag the invalid operation
+		context.flags |= INVALID_OPERATION;
+		return true;
+	}
+	// ...otherwise, no flags are set and result is unchanged
+	return false;
+}
+
+// TODO: add unit tests here for operations that apply
+unittest {
+
+}
+
+/+/* 
+ * -- General Decimal Arithmetic Specification, p. 52, "Invalid operation"
+ */
+bool isInvalidAddition(Decimal op1, Decimal op2, ref Decimal result) {
+	if (isInvalidOperation(op1, op2, result)) {
+		return true;
+	}
+	// if both operands are infinite
+	if (op1.isInfinite && op2.isInfinite) {
+		// (+inf) + (-inf) => invalid operation
+		if (op1.sign != op2.sign) {
+			result = invalidOp();
+			return true;
+		}
+	}
+	return false;
+}+/
+
+/* 
+ * -- General Decimal Arithmetic Specification, p. 52, "Invalid operation"
+ */
+bool isInvalidMultiplication(Decimal op1, Decimal op2, ref Decimal result) {
+	if (isInvalidOperation(op1, op2, result)) {
+		return true;
+	}
+	if (op1.isZero && op2.isInfinite || op1.isInfinite && op2.isZero) {
+		result = NaN;
+		return true;
+	}
+	return false;
+}
+
+/* 
+ * -- General Decimal Arithmetic Specification, p. 52, "Invalid operation"
+ */
+bool isInvalidDivision(Decimal dividend, Decimal divisor, ref Decimal quotient) {
+	if (isInvalidOperation(dividend, divisor, quotient)) {
+		return true;
+	}
+	if (divisor.isZero()) {
+		if (dividend.isZero()) {
+			quotient = invalidOp();
+		}
+		else {
+			quotient.spval = SpVal.INF;
+			context.flags |= DIVISION_BY_ZERO;
+			quotient.ceff = BIG_ZERO;
+			quotient.sign = dividend.sign ^ divisor.sign;
+		}
+		return true;
+	}
+	return false;
+}
+
+//--------------------------------
+// unit tests
+//--------------------------------
+
+//--------------------------------
+// additions to BigInt
+//--------------------------------
+
+private:
+	static immutable BigInt BIG_ZERO = { [0] };
+	static immutable BigInt BIG_ONE  = { [1] };
+	static immutable BigInt BIG_FIVE = { [5] };
+	static immutable BigInt BIG_TEN = { [10] };
+	
+/**
+ * Returns the number of decimal digits in BigInt value.
+ * There are probably significant efficiency gains available.
+ **/
+uint numDigits(const BigInt big, int n = 1) {
+	if (big == 0) return 1;
+	if (n <= 0) n = 1;
+	int m = n;
+	while (big < pow10(m-1)) m--;
+	if (m != n) return m;
+	while (big >= pow10(m)) m++;
+	return m;
+}
+
+/**
+ * Returns a BigInt with the value of 10 raised to the specified power.
+ * There are probably significant efficiency gains available.
+ **/
+public BigInt pow10(uint power) {
+	static immutable int BILLION = 1000000000;
+	static immutable int array[10] = 
+		[ 1, 10, 100, 1000, 10000, 100000, 1000000,
+		10000000, 100000000, BILLION ];
+		
+	if (power < 10) {
+		return BigInt(array[power]);
+	}
+	BigInt big = BigInt(BILLION);
+	power -= 9;
+	int quo = power / 9;  
+	int rem = power % 9;
+	for (int i = 0; i < quo; i++) {
+		big *= BILLION;
+	}
+	return big * array[rem];
+}
+
+/**
+ * Returns the first decimal digit of the specified BigInt.
+ */
+uint firstDigit(BigInt big) {
+	uint digits = numDigits(big, 1);
+	if (digits == 0) {
+		return 0;
+	}
+	BigInt bfd = big / pow10(digits - 1);
+	uint ifd;
+	bfd.castTo(ifd);
+	return ifd;
+}
+
+/**
+ * Returns the last decimal digit of the specified BigInt.
+ */
+uint lastDigit(BigInt big) {
+	return big % 10;
+}
+
+unittest {
+	write("bigint.......");
+	string str = "1";
+	BigInt big = pow10(0);
+	assert(str == big.toString);
+	for (int i = 1; i < 35; i++) {
+		str ~= "0";
+		big = pow10(i);
+		assert(str == big.toString);
+		assert(numDigits(big) == str.length);
+	}
+	writeln("passed");
+}
+	
+public void main() {
+	writeln("Hello, world!");
+/+	writeln("eTiny = ", context.eTiny);
+	writeln("tiny min = ", Decimal(1, context.eTiny));
+	writeln("tiny min = ", Decimal(1, context.eTiny - 1));
+	writeln("max = ", context.max());
+	writeln("max1 = ", Decimal(999999999, 99));
+	writeln("dig = ", context.dig());
+	writeln("eps = ", context.epsilon());
+	writeln("smallest = ", context.min_normal()*context.epsilon());
+	writeln("1/epsilon = ", Decimal(1)/context.epsilon());
+	writeln("max * min = ", context.max * context.min_normal);
+	writeln("mant_dig = ", context.mant_dig);
+	writeln("min_exp = ", context.min_exp);
+	writeln("max_exp = ", context.max_exp);+/
+	
+	
+    // TODO: this next one goes crazy -- shows need for checks on construction
+	// TODO: turns out this is being converted to a double (or real) and
+	// then it's really weird.
+//	writeln("bigger = ", Decimal(999999999999));
+//	float f = float.max;
+	// TODO: convert these to assserts
+/+	writeln("f.max = ", f);
+	writeln("f.min = ", float.min);
+	writeln("f = ", 2 * float.min);
+	writeln("d.max = ", Decimal.max());
+	writeln("d.min = ", Decimal.min_normal());
+	writeln("2 * d.min = ", 2 * Decimal.min_normal());
+	writeln("0.1 * d.min = ", 0.1 * Decimal.min_normal());+/
+	// TODO: move this to unittesting
+/+	Decimal dec = Decimal(PI);
+	writeln("pi = ", dec );
+	dec = Decimal(PI, 19);
+	writeln("pi = ", dec );
+	dec = Decimal(1.3, 25);
+	writeln("pi = ", dec );
+	dec = Decimal(0.1, 25);
+	writeln("pi = ", dec );
+	dec = Decimal(2.0, 25);
+	writeln("pi = ", dec );
+	dec = Decimal(200.5, 25);
+	writeln("pi = ", dec );
+	writeln(double.dig);
+	writeln(real.dig);+/
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/decimal/math.d	Sat Mar 13 13:22:25 2010 -0800
@@ -0,0 +1,526 @@
+module decimal.math;
+
+import decimal.decimal;
+import decimal.context;
+
+public:
+    
+    //--------------------------------
+    //
+    // CONSTANTS
+    //
+    //--------------------------------
+
+    /**
+     * Returns the value of e to the default precision.
+     */    
+    Decimal e() {
+        Decimal result;
+        return result;
+    }
+     
+    /**
+     * Returns the value of e to the specified precision.
+     */    
+    Decimal e(uint precision) {
+        Decimal result;
+        return result;
+    }
+     
+    /**
+     * Returns the value of pi to the default precision.
+     */    
+    Decimal pi() {
+        Decimal result;
+        return result;
+    }
+     
+    /**
+     * Returns the value of pi to the specified precision.
+     */    
+    Decimal pi(uint precision) {;
+        Decimal result;
+        return result;
+    }
+     
+    /**
+     * Returns the square root of the argument to the default precision.
+     */    
+    Decimal sqrt(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+     
+    /**
+     * Returns the square root of the argument to the specified precision.
+     */    
+    Decimal sqrt(Decimal arg, uint precision) {
+        Decimal result;
+        return result;
+    }
+    
+    //--------------------------------
+    //
+    // EXPONENTIAL AND LOGARITHMIC FUNCTIONS
+    //
+    //--------------------------------
+    
+    /**
+     * Decimal version of std.math function.
+     * Required by General Decimal Arithmetic Specification
+     *
+     */
+    Decimal exp(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     * 2^x
+     */
+    Decimal exp2(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     * exp(x) - 1
+     */
+    Decimal expm1(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+   
+    /**
+     * Decimal version of std.math function.
+     * Required by General Decimal Arithmetic Specification
+     *
+     */
+    Decimal log(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     * log(1 + x)
+     */
+    Decimal log1p(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+   
+    /**
+     * Decimal version of std.math function.
+     * Required by General Decimal Arithmetic Specification
+     *
+     */
+    Decimal log10(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     * Required by General Decimal Arithmetic Specification
+     *
+     */
+    Decimal log2(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     * Required by General Decimal Arithmetic Specification
+     *
+     */
+    Decimal pow(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    //--------------------------------
+    //
+    // TRIGONOMETRIC FUNCTIONS
+    //
+    //--------------------------------
+    
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal sin(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal cos(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Replaces std.math function expi
+     *
+     */
+    Decimal[] sincos(Decimal arg) {
+        Decimal[] result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal tan(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal asin(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal acos(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal atan(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal atan2(Decimal y, Decimal x) {
+        Decimal result;
+        return result;
+    }
+
+    //--------------------------------
+    //
+    // HYPERBOLIC TRIGONOMETRIC FUNCTIONS
+    //
+    //--------------------------------
+    
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal sinh(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal cosh(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal tanh(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal asinh(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal acosh(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * Decimal version of std.math function.
+     *
+     */
+    Decimal atanh(Decimal arg) {
+        Decimal result;
+        return result;
+    }
+
+    //--------------------------------
+    //
+    // General Decimal Arithmetic Specification Functions
+    //
+    //--------------------------------
+    
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal compare(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal compareSignal(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal compareTotal(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+    
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal divideInteger(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal fma(Decimal op1, Decimal op2, Decimal op3) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal ln(Decimal op1) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal log10(Decimal op1) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal max(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal maxMagnitude(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal min(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal minMagnitude(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal nextMinus(Decimal op1) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal nextPlus(Decimal op1) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal nextToward(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal power(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal remainder(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal remainderNear(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     * 
+     * NOTE: performs both round-to-integral-exact and
+     * round-to-integral-value
+     *
+     * TODO: implement
+     */
+    Decimal rint(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+// logical operations
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal and(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal or(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal xor(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal invert(Decimal op1) {
+        Decimal result;
+        return result;
+    }
+
+
+    /**
+     * part of spec
+     *
+     * TODO: implement
+     */
+    Decimal compareTotal(Decimal op1, Decimal op2) {
+        Decimal result;
+        return result;
+    }
+
+
+