view src/decimal/decimal.d @ 1:a984d3056cc4

Incorporated Boost License
author Paul (paul.d.anderson@comcast.net)
date Sat, 13 Mar 2010 18:51:22 -0800
parents 42cf4db6be32
children bfda0b347c07
line wrap: on
line source

/** 
 * A D programming language implementation of the 
 * General Decimal Arithmetic Specification, 
 * Version 1.70, (25 March 2009).
 *
 * by Paul D. Anderson
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
**/

// 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);+/
}