From 00bd492b30f0dcb49d354be74e6e9a312a84863f Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Tue, 27 Aug 2013 16:36:36 -0700
Subject: [PATCH] got linear unit conversions (like fahrenheit <-> celsius)
 working correctly further optimizations for codegen

---
 indra/llcommon/llunittype.h           | 74 +++++++++++++++------------
 indra/llcommon/tests/llunits_test.cpp | 40 ++++++++++++++-
 2 files changed, 79 insertions(+), 35 deletions(-)

diff --git a/indra/llcommon/llunittype.h b/indra/llcommon/llunittype.h
index a5e99070b6b..50037ccaded 100644
--- a/indra/llcommon/llunittype.h
+++ b/indra/llcommon/llunittype.h
@@ -147,35 +147,35 @@ struct LLUnit
 	}
 
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator == (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator == (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue == convert(other).value();
 	}
 
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator != (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator != (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue != convert(other).value();
 	}
 
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator < (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator < (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue < convert(other).value();
 	}
 
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator <= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator <= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue <= convert(other).value();
 	}
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator > (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator > (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue > convert(other).value();
 	}
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator >= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator >= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue >= convert(other).value();
 	}
@@ -193,6 +193,15 @@ struct LLUnit
 		return result;
 	}
 
+	template<typename FROM_UNITS>
+	LL_FORCE_INLINE static self_t convert(LLUnit<STORAGE_TYPE, FROM_UNITS> v) 
+	{
+		self_t result;
+		STORAGE_TYPE divisor = ll_convert_units(v, result);
+		result.mValue /= divisor;
+		return result;
+	}
+
 	template<typename FROM_STORAGE_TYPE, typename FROM_UNITS>
 	LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, FROM_UNITS> v) 
 	{ 
@@ -276,78 +285,78 @@ struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNITS>
 
 	using base_t::operator ==;
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator == (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator == (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue == convert(other).value();
 	}
 
 	template<typename STORAGE_T>
-	LL_FORCE_INLINE bool operator == (STORAGE_T other)
+	LL_FORCE_INLINE bool operator == (STORAGE_T other) const
 	{
 		return mValue == other;
 	}
 
 	using base_t::operator !=;
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator != (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator != (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue != convert(other).value();
 	}
 
 	template<typename STORAGE_T>
-	LL_FORCE_INLINE bool operator != (STORAGE_T other)
+	LL_FORCE_INLINE bool operator != (STORAGE_T other) const
 	{
 		return mValue != other;
 	}
 	
 	using base_t::operator <;
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator < (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator < (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue < convert(other).value();
 	}
 
 	template<typename STORAGE_T>
-	LL_FORCE_INLINE bool operator < (STORAGE_T other)
+	LL_FORCE_INLINE bool operator < (STORAGE_T other) const
 	{
 		return mValue < other;
 	}
 
 	using base_t::operator <=;
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator <= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator <= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue <= convert(other).value();
 	}
 
 	template<typename STORAGE_T>
-	LL_FORCE_INLINE bool operator <= (STORAGE_T other)
+	LL_FORCE_INLINE bool operator <= (STORAGE_T other) const
 	{
 		return mValue <= other;
 	}
 
 	using base_t::operator >;
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator > (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator > (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue > convert(other).value();
 	}
 
 	template<typename STORAGE_T>
-	LL_FORCE_INLINE bool operator > (STORAGE_T other)
+	LL_FORCE_INLINE bool operator > (STORAGE_T other) const
 	{
 		return mValue > other;
 	}
 
 	using base_t::operator >=;
 	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
-	LL_FORCE_INLINE bool operator >= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+	LL_FORCE_INLINE bool operator >= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
 	{
 		return mValue >= convert(other).value();
 	}
 
 	template<typename STORAGE_T>
-	LL_FORCE_INLINE bool operator >= (STORAGE_T other)
+	LL_FORCE_INLINE bool operator >= (STORAGE_T other) const
 	{
 		return mValue >= other;
 	}
@@ -670,16 +679,14 @@ struct LLUnitLinearOps
 	template<typename OTHER_T>
 	self_t operator + (OTHER_T other)
 	{
-		mValue /= mDivisor;
-		mValue += other;
+		mValue += other * mDivisor;
 		return *this;
 	}
 
 	template<typename OTHER_T>
 	self_t operator - (OTHER_T other)
 	{
-		mValue /= mDivisor;
-		mValue -= other;
+		mValue -= other * mDivisor;
 		return *this;
 	}
 
@@ -694,7 +701,8 @@ struct LLUnitInverseLinearOps
 
 	LLUnitInverseLinearOps(T val) 
 	:	mValue(val),
-		mDivisor(1)
+		mDivisor(1),
+		mMultiplicand(1)
 	{}
 
 	template<typename OTHER_T>
@@ -708,27 +716,27 @@ struct LLUnitInverseLinearOps
 	self_t operator / (OTHER_T other)
 	{
 		mValue *= other;
+		mMultiplicand *= other;
 		return *this;
 	}
 
 	template<typename OTHER_T>
 	self_t operator + (OTHER_T other)
 	{
-		mValue /= mDivisor;
-		mValue -= other;
+		mValue -= other * mMultiplicand;
 		return *this;
 	}
 
 	template<typename OTHER_T>
 	self_t operator - (OTHER_T other)
 	{
-		mValue /= mDivisor;
-		mValue += other;
+		mValue += other * mMultiplicand;
 		return *this;
 	}
 
 	T mValue;
 	T mDivisor;
+	T mMultiplicand;
 };
 
 #define LL_DECLARE_BASE_UNIT(base_unit_name, unit_label)                                             \
@@ -762,20 +770,20 @@ template<typename S1, typename S2>
 LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out)   \
 {                                                                                                \
 	typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t;                       \
-	LLUnitInverseLinearOps<result_storage_t> op =                                                \
+	LLUnitInverseLinearOps<result_storage_t> result =                                            \
 		LLUnitInverseLinearOps<result_storage_t>(in.value()) conversion_operation;               \
-	out = LLUnit<S2, base_unit_name>((S2)op.mValue);	                                         \
-	return op.mDivisor;                                                                          \
+	out = LLUnit<S2, base_unit_name>((S2)result.mValue);	                                     \
+	return result.mDivisor;                                                                      \
 }                                                                                                \
                                                                                                  \
 template<typename S1, typename S2>                                                               \
 LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out)   \
 {                                                                                                \
 	typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t;                       \
-	LLUnitLinearOps<result_storage_t> op =                                                       \
+	LLUnitLinearOps<result_storage_t> result =                                                   \
 		LLUnitLinearOps<result_storage_t>(in.value()) conversion_operation;				         \
-	out = LLUnit<S2, unit_name>((S2)op.mValue);                                                  \
-	return op.mDivisor;                                                                          \
+	out = LLUnit<S2, unit_name>((S2)result.mValue);                                              \
+	return result.mDivisor;                                                                      \
 }                                                                                               
 
 #define LL_DECLARE_UNIT_TYPEDEFS(ns, unit_name)                         \
diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp
index 5a18603e4e3..59876ce3b42 100644
--- a/indra/llcommon/tests/llunits_test.cpp
+++ b/indra/llcommon/tests/llunits_test.cpp
@@ -42,6 +42,18 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Quatloos);
 LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Latinum);
 LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Solari);
 
+namespace LLUnits
+{
+	LL_DECLARE_BASE_UNIT(Celcius, "c");
+	LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32);
+	LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Celcius);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Fahrenheit);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kelvin);
+
+
 namespace tut
 {
 	using namespace LLUnits;
@@ -328,8 +340,8 @@ namespace tut
 		ensure("GHz -> Hz conversion", Hz.value() == 1000 * 1000 * 1000);
 
 		F32Radians rad(6.2831853071795f);
-		F32Degrees deg(rad);
-		ensure("radians -> degrees conversion", deg.value() > 359 && deg.value() < 361);
+		S32Degrees deg(rad);
+		ensure("radians -> degrees conversion", deg.value() == 360);
 
 		F32Percent percent(50);
 		F32Ratio ratio(percent);
@@ -339,4 +351,28 @@ namespace tut
 		U32Triangles tris(ktris);
 		ensure("kilotriangles -> triangles conversion", tris.value() == 1000);
 	}
+
+	bool value_near(F32 value, F32 target, F32 threshold)
+	{
+		return fabsf(value - target) < threshold;
+	}
+
+	// linear transforms
+	template<> template<>
+	void units_object_t::test<10>()
+	{
+		F32Celcius float_celcius(100);
+		F32Fahrenheit float_fahrenheit(float_celcius);
+		ensure("floating point celcius -> fahrenheit conversion using linear transform", value_near(float_fahrenheit.value(), 212, 0.1f) );
+
+		float_celcius = float_fahrenheit;
+		ensure("floating point fahrenheit -> celcius conversion using linear transform (round trip)", value_near(float_celcius.value(), 100.f, 0.1f) );
+
+		S32Celcius int_celcius(100);
+		S32Fahrenheit int_fahrenheit(int_celcius);
+		ensure("integer celcius -> fahrenheit conversion using linear transform", int_fahrenheit.value() == 212);
+
+		int_celcius = int_fahrenheit;
+		ensure("integer fahrenheit -> celcius conversion using linear transform (round trip)", int_celcius.value() == 100);
+	}
 }
-- 
GitLab