/*********************************************************************
 * This file is part of the cpplibs suite.
 *
 * Copyright (C) 2001 Topi Mäenpää
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.6 $
 *********************************************************************/

#ifndef _NUMBER_H
#define _NUMBER_H

#include "String.h"
#include "PrimitiveType.h"

namespace util
{
	/**
	 * Number is the base class for all numeric types. Each subclass of
	 * Number wraps a certain primitive numeric type. Numbers are
	 * <i>Computable</i>, i.e. arithmetic operations can be performed on
	 * the classes.
	 **/
	class Number : public PrimitiveType
	{
	public:
		template <class T> class IntegerTemplate;
		template <class T> class FloatTemplate;

		/**
		 * A shorthand for a 8-bit integer.
		 **/
		typedef IntegerTemplate<char> Char;
		/**
		 * A shorthand for a 16-bit integer.
		 **/
		typedef IntegerTemplate<short> Short;
		/**
		 * A shorthand for a 32-bit integer.
		 **/
		typedef IntegerTemplate<int> Integer;
		/**
		 * A shorthand for a 64-bit integer.
		 **/
		typedef IntegerTemplate<long> Long;
		/**
		 * A shorthand for a 32-bit floating point number.
		 **/
		typedef FloatTemplate<float> Float;
		/**
		 * A shorthand for a 64-bit floating point number.
		 **/
		typedef FloatTemplate<double> Double;

		/**
		 * Typecast to bool returns true when doubleValue() returns != 0.
		 **/
		operator bool() const throw (ComputationException&) { return doubleValue() != 0; }

		SmartPtr<Computable> complement() const throw (ComputationException&);

		operator char() const { return charValue(); }
		operator short() const { return shortValue(); }
		operator int() const { return intValue(); }
		operator long() const { return longValue(); }
		operator float() const { return floatValue(); }
		operator double() const { return doubleValue(); }
		
		virtual char charValue() const = 0;
		virtual short shortValue() const = 0;
		virtual int intValue() const = 0;
		virtual long longValue() const = 0;
		virtual float floatValue() const = 0;
		virtual double doubleValue() const = 0;

		SmartPtr<Computable> plus(const Computable& c, bool switched=false) const throw (ComputationException&);
		SmartPtr<Computable> minus(const Computable& c) const throw (ComputationException&);
		SmartPtr<Computable> multiply(const Computable& c, bool switched=false) const throw (ComputationException&);
		SmartPtr<Computable> divide(const Computable& c) const throw (ComputationException&);
		SmartPtr<Computable> modulus(const Computable& c) const throw (ComputationException&);

		int compare(const Computable& c) const throw (ComputationException&);
	};

	/**
	 * A template for non-floating point numbers.
	 **/
	template <class T> class Number::IntegerTemplate : public Number
	{
	public:
		/**
		 * Create a new number that holds the given value.
		 **/
		IntegerTemplate(T value = 0) : _value(value) {}
		/**
		 * Copy another number of the same type.
		 **/
		IntegerTemplate(const IntegerTemplate& other) : _value(other._value) {}
		/**
		 * Copy any number.
		 **/
		IntegerTemplate(const Number& other) : _value(other.doubleValue()+0.5) {}
		
		char charValue() const { return (char)_value; }
		short shortValue() const { return (short)_value; }
		int intValue() const { return (int)_value; }
		long longValue() const { return (long)_value; }
		float floatValue() const { return (float)_value; }
		double doubleValue() const { return (double)_value; }

		Object* clone() const throw (NotCloneableException&) { return new IntegerTemplate(_value); }

		std::string toString() const { return String::toString(_value); }

		SmartPtr<Computable> negate() const throw (ComputationException&)
		{
			return SmartPtr<Computable>(new IntegerTemplate(-_value));
		}
		SmartPtr<Computable> operator- () const
		{
			return SmartPtr<Computable>(new IntegerTemplate(-_value));
		}
	private:
		T _value;
	};

	/**
	 * A template for floating point numbers.
	 **/
	template <class T> class Number::FloatTemplate : public Number
	{
	public:
		FloatTemplate(T value = 0) : _value(value) {}
		FloatTemplate(const FloatTemplate& other) : _value(other._value) {}
		FloatTemplate(const Number& other) : _value((T)other.doubleValue()) {}

		char charValue() const { return (char)(_value+0.5); }
		short shortValue() const { return (short)(_value+0.5); }
		int intValue() const { return (int)(_value+0.5); }
		long longValue() const { return (long)(_value+0.5); }
		float floatValue() const { return (float)_value; }
		double doubleValue() const { return (double)_value; }

		Object* clone() const throw (NotCloneableException&) { return new FloatTemplate(_value); }

		std::string toString() const	{ return String::toString(_value); }

		SmartPtr<Computable> negate() const throw (ComputationException&)
		{
			return SmartPtr<Computable>(new FloatTemplate(-_value));
		}
		SmartPtr<Computable> operator- () const
		{
			return SmartPtr<Computable>(new FloatTemplate(-_value));
		}
	private:
		T _value;
	};

	inline SmartPtr<Computable> operator+ (const Computable& c1, int other) throw (ComputationException&)
	{ return c1 + SmartPtr<Computable>(new Number::Integer(other)); }
	inline SmartPtr<Computable> operator- (const Computable& c1, int other) throw (ComputationException&)
	{ return c1 - SmartPtr<Computable>(new Number::Integer(other)); }
	inline SmartPtr<Computable> operator+ (int other, const Computable& c1) throw (ComputationException&)
	{ return c1 + SmartPtr<Computable>(new Number::Integer(other)); }
	inline SmartPtr<Computable> operator- (int other, const Computable& c1) throw (ComputationException&)
	{ return c1 - SmartPtr<Computable>(new Number::Integer(other)); }
	inline SmartPtr<Computable> operator* (const Computable& c1, int other) throw (ComputationException&)
	{ return c1 * SmartPtr<Computable>(new Number::Integer(other)); }
	inline SmartPtr<Computable> operator/ (const Computable& c1, int other) throw (ComputationException&)
	{ return c1 / SmartPtr<Computable>(new Number::Integer(other)); }
	inline SmartPtr<Computable> operator* (int other, const Computable& c1) throw (ComputationException&)
	{ return c1 * SmartPtr<Computable>(new Number::Integer(other)); }
	inline SmartPtr<Computable> operator/ (int other, const Computable& c1) throw (ComputationException&)
	{ return c1 / SmartPtr<Computable>(new Number::Integer(other)); }

	inline SmartPtr<Computable> operator+ (const Computable& c1, double other) throw (ComputationException&)
	{ return c1 + SmartPtr<Computable>(new Number::Double(other)); }
	inline SmartPtr<Computable> operator- (const Computable& c1, double other) throw (ComputationException&)
	{ return c1 - SmartPtr<Computable>(new Number::Double(other)); }
	inline SmartPtr<Computable> operator+ (double other, const Computable& c1) throw (ComputationException&)
	{ return c1 + SmartPtr<Computable>(new Number::Double(other)); }
	inline SmartPtr<Computable> operator- (double other, const Computable& c1) throw (ComputationException&)
	{ return c1 - SmartPtr<Computable>(new Number::Double(other)); }
	inline SmartPtr<Computable> operator* (const Computable& c1, double other) throw (ComputationException&)
	{ return c1 * SmartPtr<Computable>(new Number::Double(other)); }
	inline SmartPtr<Computable> operator/ (const Computable& c1, double other) throw (ComputationException&)
	{ return c1 / SmartPtr<Computable>(new Number::Double(other)); }
	inline SmartPtr<Computable> operator* (double other, const Computable& c1) throw (ComputationException&)
	{ return c1 * SmartPtr<Computable>(new Number::Double(other)); }
	inline SmartPtr<Computable> operator/ (double other, const Computable& c1) throw (ComputationException&)
	{ return c1 / SmartPtr<Computable>(new Number::Double(other)); }
}

#endif
