/** 
 * @file llunit.h
 * @brief Unit conversion classes
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2012, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLUNIT_H
#define LL_LLUNIT_H

#include "stdtypes.h"
#include "llpreprocessor.h"
#include "llerror.h"

template<typename STORAGE_TYPE, typename UNIT_TYPE>
struct LLUnit
{
	typedef LLUnit<STORAGE_TYPE, UNIT_TYPE> self_t;

	typedef STORAGE_TYPE storage_t;

	// value initialization
	explicit LLUnit(storage_t value = storage_t())
	:	mValue(value)
	{}

	// unit initialization and conversion
	template<typename OTHER_STORAGE, typename OTHER_UNIT>
	LLUnit(LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
	:	mValue(convert(other).mValue)
	{}
	
	bool operator == (const self_t& other)
	{
		return mValue = other.mValue;
	}

	// value assignment
	self_t& operator = (storage_t value)
	{
		mValue = value;
		return *this;
	}

	// unit assignment
	template<typename OTHER_STORAGE, typename OTHER_UNIT>
	self_t& operator = (LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
	{
		mValue = convert(other).mValue;
		return *this;
	}

	storage_t value() const
	{
		return mValue;
	}

	void value(storage_t value)
	{
		mValue = value;
	}

	template<typename NEW_UNIT_TYPE> 
	storage_t valueInUnits()
	{
		return LLUnit<storage_t, NEW_UNIT_TYPE>(*this).value();
	}

	template<typename NEW_UNIT_TYPE> 
	void valueInUnits(storage_t value)
	{
		*this = LLUnit<storage_t, NEW_UNIT_TYPE>(value);
	}

	void operator += (storage_t value)
	{
		mValue += value;
	}

	template<typename OTHER_STORAGE, typename OTHER_UNIT>
	void operator += (LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
	{
		mValue += convert(other).mValue;
	}

	void operator -= (storage_t value)
	{
		mValue -= value;
	}

	template<typename OTHER_STORAGE, typename OTHER_UNIT>
	void operator -= (LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
	{
		mValue -= convert(other).mValue;
	}

	void operator *= (storage_t multiplicand)
	{
		mValue *= multiplicand;
	}

	template<typename OTHER_UNIT, typename OTHER_STORAGE>
	void operator *= (LLUnit<OTHER_STORAGE, OTHER_UNIT> multiplicand)
	{
		// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
		LL_BAD_TEMPLATE_INSTANTIATION(OTHER_UNIT, "Multiplication of unit types not supported.");
	}

	void operator /= (storage_t divisor)
	{
		mValue /= divisor;
	}

	template<typename OTHER_UNIT, typename OTHER_STORAGE>
	void operator /= (LLUnit<OTHER_STORAGE, OTHER_UNIT> divisor)
	{
		// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
		LL_BAD_TEMPLATE_INSTANTIATION(OTHER_UNIT, "Illegal in-place division of unit types.");
	}

	template<typename SOURCE_STORAGE, typename SOURCE_UNITS>
	static self_t convert(LLUnit<SOURCE_STORAGE, SOURCE_UNITS> v) 
	{ 
		self_t result;
		ll_convert_units(v, result);
		return result;
	}

protected:
	storage_t mValue;
};

template<typename STORAGE_TYPE, typename UNIT_TYPE>
std::ostream& operator <<(std::ostream& s, const LLUnit<STORAGE_TYPE, UNIT_TYPE>& unit)
{
        s << unit.value() << UNIT_TYPE::getUnitLabel();
        return s;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE>
std::istream& operator >>(std::istream& s, LLUnit<STORAGE_TYPE, UNIT_TYPE>& unit)
{
        STORAGE_TYPE val;
        s >> val;
        unit = val;
        return s;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE>
struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNIT_TYPE>
{
	typedef LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> self_t;
	typedef typename LLUnit<STORAGE_TYPE, UNIT_TYPE>::storage_t storage_t;
	typedef LLUnit<STORAGE_TYPE, UNIT_TYPE> base_t;

	LLUnitImplicit(storage_t value = storage_t())
	:	base_t(value)
	{}

	template<typename OTHER_STORAGE, typename OTHER_UNIT>
	LLUnitImplicit(LLUnit<OTHER_STORAGE, OTHER_UNIT> other)
	:	base_t(convert(other))
	{}

	// unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD scalar (F32, S32, etc)
	// this allows for interoperability with legacy code
	operator storage_t() const
	{
		return base_t::value();
	}
};

template<typename STORAGE_TYPE, typename UNIT_TYPE>
std::ostream& operator <<(std::ostream& s, const LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>& unit)
{
        s << unit.value() << UNIT_TYPE::getUnitLabel();
        return s;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE>
std::istream& operator >>(std::istream& s, LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>& unit)
{
        STORAGE_TYPE val;
        s >> val;
        unit = val;
        return s;
}

template<typename S, typename T> 
struct LLIsSameType
{
	static const bool value = false;
};

template<typename T>
struct LLIsSameType<T, T>
{
	static const bool value = true;
};

template<typename S1, typename T1, typename S2, typename T2>
LL_FORCE_INLINE void ll_convert_units(LLUnit<S1, T1> in, LLUnit<S2, T2>& out, ...)
{
	LL_STATIC_ASSERT((LLIsSameType<T1, T2>::value 
		|| !LLIsSameType<T1, typename T1::base_unit_t>::value 
		|| !LLIsSameType<T2, typename T2::base_unit_t>::value), "invalid conversion");

	if (LLIsSameType<T1, typename T1::base_unit_t>::value)
	{
		if (LLIsSameType<T2, typename T2::base_unit_t>::value)

		{
			// T1 and T2 fully reduced and equal...just copy
			out = LLUnit<S2, T2>((S2)in.value());
		}
		else
		{
			// reduce T2
			LLUnit<S2, typename T2::base_unit_t> new_out;
			ll_convert_units(in, new_out);
			ll_convert_units(new_out, out);
		}
	}
	else
	{
		// reduce T1
		LLUnit<S1, typename T1::base_unit_t> new_in;
		ll_convert_units(in, new_in);
		ll_convert_units(new_in, out);
	}
}

//
// operator +
//
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator + (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator + (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
{
	LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result += LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1>(second);
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator + (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator + (SCALAR_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> second)
{
	LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result += second;
	return result;
}

//
// operator -
//
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> operator - (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator - (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator - (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
{
	LLUnit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator - (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> result(first);
	result -= LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1>(second);
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator - (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator - (SCALAR_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> second)
{
	LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> result(first);
	result -= second;
	return result;
}

//
// operator *
//
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnit<STORAGE_TYPE1, UNIT_TYPE1> operator * (LLUnit<STORAGE_TYPE1, UNIT_TYPE1>, LLUnit<STORAGE_TYPE2, UNIT_TYPE2>)
{
	// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "Multiplication of unit types results in new unit type - not supported.");
	return LLUnit<STORAGE_TYPE1, UNIT_TYPE1>();
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator * (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	return LLUnit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first.value() * second));
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator * (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
{
	return LLUnit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first * second.value()));
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> operator * (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1>, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2>)
{
	// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "Multiplication of unit types results in new unit type - not supported.");
	return LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1>();
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator * (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	return LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>(first.value() * second);
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator * (SCALAR_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> second)
{
	return LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>(first * second.value());
}


//
// operator /
//
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
SCALAR_TYPE operator / (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)
{
	return SCALAR_TYPE(first / second.value());
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnit<STORAGE_TYPE, UNIT_TYPE> operator / (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	return LLUnit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first.value() / second));
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
STORAGE_TYPE1 operator / (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	return STORAGE_TYPE1(first.value() / first.convert(second));
}

template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>
LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> operator / (LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)
{
	return LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE>((STORAGE_TYPE)(first.value() / second));
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
STORAGE_TYPE1 operator / (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	return STORAGE_TYPE1(first.value() / first.convert(second));
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
STORAGE_TYPE1 operator / (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	return STORAGE_TYPE1(first.value() / first.convert(second));
}

template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>
STORAGE_TYPE1 operator / (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)
{
	return STORAGE_TYPE1(first.value() / first.convert(second));
}

#define COMPARISON_OPERATORS(op)                                                                                     \
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>                                            \
bool operator op (SCALAR_TYPE first, LLUnit<STORAGE_TYPE, UNIT_TYPE> second)                                         \
{                                                                                                                    \
	return first op second.value();                                                                                  \
}                                                                                                                    \
	                                                                                                                 \
template<typename STORAGE_TYPE, typename UNIT_TYPE, typename SCALAR_TYPE>                                            \
bool operator op (LLUnit<STORAGE_TYPE, UNIT_TYPE> first, SCALAR_TYPE second)                                         \
{                                                                                                                    \
	return first.value() op second;                                                                                  \
}                                                                                                                    \
	                                                                                                                 \
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>                   \
bool operator op (LLUnitImplicit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnitImplicit<STORAGE_TYPE2, UNIT_TYPE2> second) \
{                                                                                                                    \
	return first.value() op first.convert(second);                                                                   \
}                                                                                                                    \
	                                                                                                                 \
template<typename STORAGE_TYPE1, typename UNIT_TYPE1, typename STORAGE_TYPE2, typename UNIT_TYPE2>                   \
	bool operator op (LLUnit<STORAGE_TYPE1, UNIT_TYPE1> first, LLUnit<STORAGE_TYPE2, UNIT_TYPE2> second)             \
{                                                                                                                    \
	return first.value() op first.convert(second);                                                                   \
}

COMPARISON_OPERATORS(<)
COMPARISON_OPERATORS(<=)
COMPARISON_OPERATORS(>)
COMPARISON_OPERATORS(>=)
COMPARISON_OPERATORS(==)
COMPARISON_OPERATORS(!=)


template<typename T> 
struct LLGetUnitLabel
{
	static const char* getUnitLabel() { return ""; }
};

template<typename T, typename STORAGE_T>
struct LLGetUnitLabel<LLUnit<STORAGE_T, T> >
{
	static const char* getUnitLabel() { return T::getUnitLabel(); }
};

#define LL_UNIT_PROMOTE_VALUE(output_type, value) ((true ? (output_type)(1) : (value/value)) * value)

template<typename INPUT_TYPE, typename OUTPUT_TYPE>
struct LLUnitLinearOps
{
	typedef LLUnitLinearOps<OUTPUT_TYPE, OUTPUT_TYPE> output_t;

	LLUnitLinearOps(INPUT_TYPE val) 
	:	mInput (val)
	{}

	operator OUTPUT_TYPE() const { return (OUTPUT_TYPE)mInput; }
	INPUT_TYPE mInput;

	template<typename T>
	output_t operator * (T other)
	{
		return mInput * other;
	}

	template<typename T>
	output_t operator / (T other)
	{
		return LL_UNIT_PROMOTE_VALUE(OUTPUT_TYPE, mInput) / other;
	}

	template<typename T>
	output_t operator + (T other)
	{
		return mInput + other;
	}

	template<typename T>
	output_t operator - (T other)
	{
		return mInput - other;
	}
};

template<typename INPUT_TYPE, typename OUTPUT_TYPE>
struct LLUnitInverseLinearOps
{
	typedef LLUnitInverseLinearOps<OUTPUT_TYPE, OUTPUT_TYPE> output_t;

	LLUnitInverseLinearOps(INPUT_TYPE val) 
	:	mInput(val)
	{}

	operator OUTPUT_TYPE() const { return (OUTPUT_TYPE)mInput; }
	INPUT_TYPE mInput;

	template<typename T>
	output_t operator * (T other)
	{
		return LL_UNIT_PROMOTE_VALUE(OUTPUT_TYPE, mInput) / other;
	}

	template<typename T>
	output_t operator / (T other)
	{
		return mInput * other;
	}

	template<typename T>
	output_t operator + (T other)
	{
		return mInput - other;
	}

	template<typename T>
	output_t operator - (T other)
	{
		return mInput + other;
	}
};

#define LL_DECLARE_BASE_UNIT(base_unit_name, unit_label)                                             \
struct base_unit_name                                                                                \
{                                                                                                    \
	typedef base_unit_name base_unit_t;                                                              \
	static const char* getUnitLabel() { return unit_label; }                                         \
	template<typename T>                                                                             \
	static LLUnit<T, base_unit_name> fromValue(T value) { return LLUnit<T, base_unit_name>(value); } \
	template<typename STORAGE_T, typename UNIT_T>                                                    \
	static LLUnit<STORAGE_T, base_unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value)              \
	{ return LLUnit<STORAGE_T, base_unit_name>(value); }                                             \
}


#define LL_DECLARE_DERIVED_UNIT(base_unit_name, conversion_operation, unit_name, unit_label)	        \
struct unit_name                                                                                        \
{                                                                                                       \
	typedef base_unit_name base_unit_t;                                                                 \
	static const char* getUnitLabel() { return unit_label; }									        \
	template<typename T>                                                                                \
	static LLUnit<T, unit_name> fromValue(T value) { return LLUnit<T, unit_name>(value); }		        \
	template<typename STORAGE_T, typename UNIT_T>                                                       \
	static LLUnit<STORAGE_T, unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value)				        \
	{ return LLUnit<STORAGE_T, unit_name>(value); }												        \
};                                                                                                      \
	                                                                                                    \
template<typename S1, typename S2>                                                                      \
void ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out)                        \
{                                                                                                       \
	out = LLUnit<S2, base_unit_name>((S2)(LLUnitLinearOps<S1, S2>(in.value()) conversion_operation));		\
}                                                                                                       \
                                                                                                        \
template<typename S1, typename S2>                                                                      \
void ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out)                        \
{                                                                                                       \
	out = LLUnit<S2, unit_name>((S2)(LLUnitInverseLinearOps<S1, S2>(in.value()) conversion_operation));     \
}                                                                                               

//
// Unit declarations
//

namespace LLUnits
{
LL_DECLARE_BASE_UNIT(Bytes, "B");
LL_DECLARE_DERIVED_UNIT(Bytes, * 1000,			Kilobytes, "KB");
LL_DECLARE_DERIVED_UNIT(Kilobytes, * 1000,		Megabytes, "MB");
LL_DECLARE_DERIVED_UNIT(Megabytes, * 1000,		Gigabytes, "GB");
LL_DECLARE_DERIVED_UNIT(Bytes, * 1024,			Kibibytes, "KiB");
LL_DECLARE_DERIVED_UNIT(Kibibytes, * 1024,		Mibibytes, "MiB");
LL_DECLARE_DERIVED_UNIT(Mibibytes, * 1024,		Gibibytes, "GiB");

typedef LLUnit<F32, Bytes>     F32Bytes;
typedef LLUnit<F32, Kilobytes> F32KiloBytes;
typedef LLUnit<F32, Megabytes> F32MegaBytes;
typedef LLUnit<F32, Gigabytes> F32GigaBytes;
typedef LLUnit<F32, Kibibytes> F32KibiBytes;
typedef LLUnit<F32, Mibibytes> F32MibiBytes;
typedef LLUnit<F32, Gibibytes> F32GibiBytes;

typedef LLUnit<F64, Bytes>     F64Bytes;
typedef LLUnit<F64, Kilobytes> F64KiloBytes;
typedef LLUnit<F64, Megabytes> F64MegaBytes;
typedef LLUnit<F64, Gigabytes> F64GigaBytes;
typedef LLUnit<F64, Kibibytes> F64KibiBytes;
typedef LLUnit<F64, Mibibytes> F64MibiBytes;
typedef LLUnit<F64, Gibibytes> F64GibiBytes;

typedef LLUnit<S32, Bytes>     S32Bytes;
typedef LLUnit<S32, Kilobytes> S32KiloBytes;
typedef LLUnit<S32, Megabytes> S32MegaBytes;
typedef LLUnit<S32, Gigabytes> S32GigaBytes;
typedef LLUnit<S32, Kibibytes> S32KibiBytes;
typedef LLUnit<S32, Mibibytes> S32MibiBytes;
typedef LLUnit<S32, Gibibytes> S32GibiBytes;

typedef LLUnit<U32, Bytes>     U32Bytes;
typedef LLUnit<U32, Kilobytes> U32KiloBytes;
typedef LLUnit<U32, Megabytes> U32MegaBytes;
typedef LLUnit<U32, Gigabytes> U32GigaBytes;
typedef LLUnit<U32, Kibibytes> U32KibiBytes;
typedef LLUnit<U32, Mibibytes> U32MibiBytes;
typedef LLUnit<U32, Gibibytes> U32GibiBytes;

typedef LLUnit<S64, Bytes>     S64Bytes;
typedef LLUnit<S64, Kilobytes> S64KiloBytes;
typedef LLUnit<S64, Megabytes> S64MegaBytes;
typedef LLUnit<S64, Gigabytes> S64GigaBytes;
typedef LLUnit<S64, Kibibytes> S64KibiBytes;
typedef LLUnit<S64, Mibibytes> S64MibiBytes;
typedef LLUnit<S64, Gibibytes> S64GibiBytes;

typedef LLUnit<U64, Bytes>     U64Bytes;
typedef LLUnit<U64, Kilobytes> U64KiloBytes;
typedef LLUnit<U64, Megabytes> U64MegaBytes;
typedef LLUnit<U64, Gigabytes> U64GigaBytes;
typedef LLUnit<U64, Kibibytes> U64KibiBytes;
typedef LLUnit<U64, Mibibytes> U64MibiBytes;
typedef LLUnit<U64, Gibibytes> U64GibiBytes;

LL_DECLARE_DERIVED_UNIT(Bytes, / 8,				Bits, "b");
LL_DECLARE_DERIVED_UNIT(Bits, * 1000,			Kilobits, "Kb");
LL_DECLARE_DERIVED_UNIT(Kilobits, * 1000,		Megabits, "Mb");
LL_DECLARE_DERIVED_UNIT(Megabits, * 1000,		Gigabits, "Gb");
LL_DECLARE_DERIVED_UNIT(Bits, * 1024,			Kibibits, "Kib");
LL_DECLARE_DERIVED_UNIT(Kibibits, * 1024,		Mibibits, "Mib");  
LL_DECLARE_DERIVED_UNIT(Mibibits, * 1024,		Gibibits, "Gib");

typedef LLUnit<F32, Bits>     F32Bits;
typedef LLUnit<F32, Kilobits> F32KiloBits;
typedef LLUnit<F32, Megabits> F32MegaBits;
typedef LLUnit<F32, Gigabits> F32GigaBits;
typedef LLUnit<F32, Kibibits> F32KibiBits;
typedef LLUnit<F32, Mibibits> F32MibiBits;
typedef LLUnit<F32, Gibibits> F32GibiBits;

typedef LLUnit<F64, Bits>     F64Bits;
typedef LLUnit<F64, Kilobits> F64KiloBits;
typedef LLUnit<F64, Megabits> F64MegaBits;
typedef LLUnit<F64, Gigabits> F64GigaBits;
typedef LLUnit<F64, Kibibits> F64KibiBits;
typedef LLUnit<F64, Mibibits> F64MibiBits;
typedef LLUnit<F64, Gibibits> F64GibiBits;

typedef LLUnit<S32, Bits>     S32Bits;
typedef LLUnit<S32, Kilobits> S32KiloBits;
typedef LLUnit<S32, Megabits> S32MegaBits;
typedef LLUnit<S32, Gigabits> S32GigaBits;
typedef LLUnit<S32, Kibibits> S32KibiBits;
typedef LLUnit<S32, Mibibits> S32MibiBits;
typedef LLUnit<S32, Gibibits> S32GibiBits;

typedef LLUnit<U32, Bits>     U32Bits;
typedef LLUnit<U32, Kilobits> U32KiloBits;
typedef LLUnit<U32, Megabits> U32MegaBits;
typedef LLUnit<U32, Gigabits> U32GigaBits;
typedef LLUnit<U32, Kibibits> U32KibiBits;
typedef LLUnit<U32, Mibibits> U32MibiBits;
typedef LLUnit<U32, Gibibits> U32GibiBits;

typedef LLUnit<S64, Bits>     S64Bits;
typedef LLUnit<S64, Kilobits> S64KiloBits;
typedef LLUnit<S64, Megabits> S64MegaBits;
typedef LLUnit<S64, Gigabits> S64GigaBits;
typedef LLUnit<S64, Kibibits> S64KibiBits;
typedef LLUnit<S64, Mibibits> S64MibiBits;
typedef LLUnit<S64, Gibibits> S64GibiBits;

typedef LLUnit<U64, Bits>     U64Bits;
typedef LLUnit<U64, Kilobits> U64KiloBits;
typedef LLUnit<U64, Megabits> U64MegaBits;
typedef LLUnit<U64, Gigabits> U64GigaBits;
typedef LLUnit<U64, Kibibits> U64KibiBits;
typedef LLUnit<U64, Mibibits> U64MibiBits;
typedef LLUnit<U64, Gibibits> U64GibiBits;

LL_DECLARE_BASE_UNIT(Seconds, "s");
LL_DECLARE_DERIVED_UNIT(Seconds, * 60,			Minutes, "min");
LL_DECLARE_DERIVED_UNIT(Minutes, * 60,			Hours, "h");
LL_DECLARE_DERIVED_UNIT(Hours, * 24,			Days, "d");
LL_DECLARE_DERIVED_UNIT(Seconds, / 1000,		Milliseconds, "ms");
LL_DECLARE_DERIVED_UNIT(Milliseconds, / 1000,	Microseconds, "\x09\x3cs");
LL_DECLARE_DERIVED_UNIT(Microseconds, / 1000,	Nanoseconds, "ns");

typedef LLUnit<F32, Seconds>      F32Seconds;
typedef LLUnit<F32, Minutes>      F32Minutes;
typedef LLUnit<F32, Hours>        F32Hours;
typedef LLUnit<F32, Days>         F32Days;
typedef LLUnit<F32, Milliseconds> F32Milliseconds;
typedef LLUnit<F32, Microseconds> F32Microseconds;
typedef LLUnit<F32, Nanoseconds>  F32Nanoseconds;

typedef LLUnit<F64, Seconds>      F64Seconds;
typedef LLUnit<F64, Minutes>      F64Minutes;
typedef LLUnit<F64, Hours>        F64Hours;
typedef LLUnit<F64, Days>         F64Days;
typedef LLUnit<F64, Milliseconds> F64Milliseconds;
typedef LLUnit<F64, Microseconds> F64Microseconds;
typedef LLUnit<F64, Nanoseconds>  F64Nanoseconds;

typedef LLUnit<S32, Seconds>      S32Seconds;
typedef LLUnit<S32, Minutes>      S32Minutes;
typedef LLUnit<S32, Hours>        S32Hours;
typedef LLUnit<S32, Days>         S32Days;
typedef LLUnit<S32, Milliseconds> S32Milliseconds;
typedef LLUnit<S32, Microseconds> S32Microseconds;
typedef LLUnit<S32, Nanoseconds>  S32Nanoseconds;

typedef LLUnit<U32, Seconds>      U32Seconds;
typedef LLUnit<U32, Minutes>      U32Minutes;
typedef LLUnit<U32, Hours>        U32Hours;
typedef LLUnit<U32, Days>         U32Days;
typedef LLUnit<U32, Milliseconds> U32Milliseconds;
typedef LLUnit<U32, Microseconds> U32Microseconds;
typedef LLUnit<U32, Nanoseconds>  U32Nanoseconds;

typedef LLUnit<S64, Seconds>      S64Seconds;
typedef LLUnit<S64, Minutes>      S64Minutes;
typedef LLUnit<S64, Hours>        S64Hours;
typedef LLUnit<S64, Days>         S64Days;
typedef LLUnit<S64, Milliseconds> S64Milliseconds;
typedef LLUnit<S64, Microseconds> S64Microseconds;
typedef LLUnit<S64, Nanoseconds>  S64Nanoseconds;

typedef LLUnit<U64, Seconds>      U64Seconds;
typedef LLUnit<U64, Minutes>      U64Minutes;
typedef LLUnit<U64, Hours>        U64Hours;
typedef LLUnit<U64, Days>         U64Days;
typedef LLUnit<U64, Milliseconds> U64Milliseconds;
typedef LLUnit<U64, Microseconds> U64Microseconds;
typedef LLUnit<U64, Nanoseconds>  U64Nanoseconds;

LL_DECLARE_BASE_UNIT(Meters, "m");
LL_DECLARE_DERIVED_UNIT(Meters, * 1000,			Kilometers, "km");
LL_DECLARE_DERIVED_UNIT(Meters, / 100,			Centimeters, "cm");
LL_DECLARE_DERIVED_UNIT(Meters, / 1000,			Millimeters, "mm");

typedef LLUnit<F32, Meters>      F32Meters;
typedef LLUnit<F32, Kilometers>  F32Kilometers;
typedef LLUnit<F32, Centimeters> F32Centimeters;
typedef LLUnit<F32, Millimeters> F32Millimeters;

typedef LLUnit<F64, Meters>      F64Meters;
typedef LLUnit<F64, Kilometers>  F64Kilometers;
typedef LLUnit<F64, Centimeters> F64Centimeters;
typedef LLUnit<F64, Millimeters> F64Millimeters;

typedef LLUnit<S32, Meters>      S32Meters;
typedef LLUnit<S32, Kilometers>  S32Kilometers;
typedef LLUnit<S32, Centimeters> S32Centimeters;
typedef LLUnit<S32, Millimeters> S32Millimeters;

typedef LLUnit<U32, Meters>      U32Meters;
typedef LLUnit<U32, Kilometers>  U32Kilometers;
typedef LLUnit<U32, Centimeters> U32Centimeters;
typedef LLUnit<U32, Millimeters> U32Millimeters;

typedef LLUnit<S64, Meters>      S64Meters;
typedef LLUnit<S64, Kilometers>  S64Kilometers;
typedef LLUnit<S64, Centimeters> S64Centimeters;
typedef LLUnit<S64, Millimeters> S64Millimeters;

typedef LLUnit<U64, Meters>      U64Meters;
typedef LLUnit<U64, Kilometers>  U64Kilometers;
typedef LLUnit<U64, Centimeters> U64Centimeters;
typedef LLUnit<U64, Millimeters> U64Millimeters;

// rare units
LL_DECLARE_BASE_UNIT(Hertz, "Hz");
LL_DECLARE_DERIVED_UNIT(Hertz, * 1000,			Kilohertz, "KHz");
LL_DECLARE_DERIVED_UNIT(Kilohertz, * 1000,		Megahertz, "MHz");
LL_DECLARE_DERIVED_UNIT(Megahertz, * 1000,		Gigahertz, "GHz");

LL_DECLARE_BASE_UNIT(Radians, "rad");
LL_DECLARE_DERIVED_UNIT(Radians, / 57.29578f,	Degrees, "deg");

LL_DECLARE_BASE_UNIT(Percent, "%");
LL_DECLARE_DERIVED_UNIT(Percent, * 100,			Ratio, "x");

LL_DECLARE_BASE_UNIT(Triangles, "tris");
LL_DECLARE_DERIVED_UNIT(Triangles, * 1000,		Kilotriangles, "ktris");

} // namespace LLUnits

#endif // LL_LLUNIT_H