/*
 * SYNOPSYS CONFIDENTIAL - This is an unpublished, proprietary work of Synopsys,
 * Inc., and is fully protected under copyright and trade secret laws. You may
 * not view, use, disclose, copy, or distribute this file or any information
 * contained herein except pursuant to a valid written license from Synopsys.
 */

#include <base/type-traits2.h>
#include <base/port.h>
#include <base/except.h>
#include <base/constants.h>
#include <base/round.h>
#include <base/point.h>
#include <base/rectangle.h>

#include "SSE2_transform.h"

namespace base
{

	const stdOrientEnum inverseStdOrient[stdOrientMAX] =
	{
			R0, 
			R270, 
			R180, 
			R90, 
			XFlipR0, 
			XFlipR90, 
			XFlipR180, 
			XFlipR270
			};
 /* In Aix64 platform char is by default unsigned this causes difference in values, ensuring signed char array */ 
	const signed char stdOrientCoeffMatrix[stdOrientMAX][2][2] =
	{
			//
			/// R0: 0 degree rotation with no flip.
			/// Coefficients = | 1  0|
			///                | 0  1|
			//
			{{1, 0}, 
			 {0, 1}},
			//
			/// R90: 90 degree rotation with no flip.
			/// Coefficients = | 0  1|
			///                |-1  0|
			//
			{{0, 1}, 
			 {-1, 0}},
			//
			/// R180: 180 degree rotation with no flip.
			/// Coefficients = |-1  0|
			///                | 0 -1|
			//
			{{-1, 0}, 
			 {0, -1}},
			//
			/// R270: 270 degree rotation with no flip.
			/// Coefficients = | 0 -1|
			///                | 1  0|
			//
			{{0, -1}, 
			 {1, 0}},
			//
			/// XFlipR0: X flip followed by 0 degree rotation.
			/// Coefficients = | 1  0|
			///                | 0 -1|
			//
			{{1, 0}, 
			 {0, -1}},
			//
			/// X flip followed by 90 degree rotation.
			/// Coefficients = | 0  1|
			///                | 1  0|
			//
			{{0, 1}, 
			 {1, 0}},
			//
			/// X flip followed by 180 degree rotation.
			/// Coefficients = |-1  0|
			///                | 0  1|
			//
			{{-1, 0}, 
			 {0, 1}},
			//
			/// X flip followed by 270 degree rotation.
			/// Coefficients = | 0 -1|
			///                |-1  0|
			//
			{{0, -1}, 
			 {-1, 0}}
			};
	//
	/// Matrix for multiplying standard orientations.
	//
	const stdOrientEnum multStdOrientMatrix[stdOrientMAX][stdOrientMAX] = {
				//           R0,       R90,      R180,      R270,   XFlipR0,  XFlipR90, XFlipR180, XFlipR270
	/* R0 */		{        R0,       R90,      R180,      R270,   XFlipR0,  XFlipR90, XFlipR180, XFlipR270},
	/* R90 */		{       R90,      R180,      R270,        R0, XFlipR270,   XFlipR0,  XFlipR90, XFlipR180},
	/* R180 */		{      R180,      R270,        R0,       R90, XFlipR180, XFlipR270,   XFlipR0,  XFlipR90},
	/* R270 */		{      R270,        R0,       R90,      R180,  XFlipR90, XFlipR180, XFlipR270,   XFlipR0},
	/* XFlipR0 */	{   XFlipR0,  XFlipR90, XFlipR180, XFlipR270,        R0,       R90,      R180,      R270},
	/* XFlipR90 */	{  XFlipR90, XFlipR180, XFlipR270,   XFlipR0,      R270,        R0,       R90,      R180},
	/* XFlipR180 */	{ XFlipR180, XFlipR270,   XFlipR0,  XFlipR90,      R180,      R270,        R0,       R90},
	/* XFlipR270 */	{ XFlipR270,   XFlipR0,  XFlipR90, XFlipR180,       R90,      R180,      R270,        R0}
			};

	typedef pod::point<short> (*pModifyFunDblToShort)(const transform &, pod::point<double>);
	typedef pod::point<short> (*pModifyFunLongToShort)(const transform &, pod::point<long>);
	typedef pod::point<short> (*pModifyFunIntToShort)(const transform &, pod::point<int>);
	typedef pod::point<int> (*pModifyFunDblToInt)(const transform &, pod::point<double>);
	typedef pod::point<double> (*pModifyFunDblToDbl)(const transform &, pod::point<double>);
	typedef pod::point<int> (*pModifyFunLongToInt)(const transform &, pod::point<long>);
	typedef pod::point<int> (*pModifyFunIntToInt)(const transform &, pod::point<int>);
	typedef pod::point<long> (*pModifyFunLongToLong)(const transform &, pod::point<long>);
	//
	/// Type definition for for transforming a rectangle of type double to
	/// another rectangle of type double. 
	//
	typedef pod::rectangle<double> (*pModifyRectFunDblToDbl)(const transform &, const pod::rectangle<double> &);
	//
	/// Type definition for for transforming a rectangle of type long to
	/// another rectangle of type long. 
	//
	typedef pod::rectangle<long> (*pModifyRectFunLongToLong)(const transform &, const pod::rectangle<long> &);
	//
	/// Type definition for for transforming a rectangle of type int to
	/// another rectangle of type int. 
	//
	typedef pod::rectangle<int> (*pModifyRectFunIntToInt)(const transform &, const pod::rectangle<int> &);

	static pModifyFunDblToShort  modifyDblToShortFunTbl[transformTypeMAX];
	static pModifyFunLongToShort modifyLongToShortFunTbl[transformTypeMAX];
	static pModifyFunIntToShort  modifyIntToShortFunTbl[transformTypeMAX];
	static pModifyFunDblToInt  modifyDblToIntFunTbl[transformTypeMAX];
	static pModifyFunDblToDbl  modifyDblToDblFunTbl[transformTypeMAX];
	static pModifyFunLongToInt modifyLongToIntFunTbl[transformTypeMAX];
	static pModifyFunIntToInt  modifyIntToIntFunTbl[transformTypeMAX];
	static pModifyFunLongToLong  modifyLongToLongFunTbl[transformTypeMAX];
	static pModifyRectFunLongToLong  modifyRectLongToLongFunTbl[transformTypeMAX];
	static pModifyRectFunDblToDbl  modifyRectDblToDblFunTbl[transformTypeMAX];
	static pModifyRectFunIntToInt  modifyRectIntToIntFunTbl[transformTypeMAX];
	//
	/// Modify a point using scale transform.
	//
	template <class iC,class oC>
		pod::point<oC> GXX_HIDDEN modifyScale(
			const transform & T, pod::point<iC> xy)
	{
		pod::point<double> tmp = xy % T.getDiagonal();
		return std::tr1::is_floating_point<oC>::value ?
				pod::point<oC>((oC)tmp.X(),(oC)tmp.Y())
				:
				pod::point<oC>((oC)base::round(tmp.X()),(oC)base::round(tmp.Y()));
	}
	//
	/// Modify a point using scale and offset transform.
	//
	template <class iC,class oC>
		pod::point<oC> GXX_HIDDEN modifyScaleOffset(
			const transform & T, pod::point<iC> xy)
	{
		pod::point<double> tmp = xy % T.getDiagonal() + T.getOffset();
		return std::tr1::is_floating_point<oC>::value ?
				pod::point<oC>((oC)tmp.X(),(oC)tmp.Y())
				:
				pod::point<oC>((oC)base::round(tmp.X()),(oC)base::round(tmp.Y()));
	}
	//
	/// Modify a point using a general transform.
	//
	template <class iC, class oC>
		pod::point<oC> GXX_HIDDEN modifyGeneral( const transform & T, pod::point<iC> xy )
	{
		pod::point<double> tmp(T.getCol1()*pod::point<double>(xy),T.getCol2()*pod::point<double>(xy));
		tmp += T.getOffset();
		return std::tr1::is_floating_point<oC>::value ?
				pod::point<oC>((oC)tmp.X(),(oC)tmp.Y())
				:
				pod::point<oC>((oC)base::round(tmp.X()),(oC)base::round(tmp.Y()));
	}

	template <class iC,class oC>
		pod::point<oC> GXX_HIDDEN modifyStd ( const transform & T, pod::point<iC> xy )
	{
		//
		// Transform the point.
		//
		pod::point<double> d = xy * T.getOrient() + T.getOffset();
		//
		// Convert to target coordinate type.
		//
		return std::tr1::is_floating_point<oC>::value ?
			pod::point<oC>( (oC)(d.getX()), (oC)(d.getY()))
			:
			pod::point<oC>( (oC)base::round(d.getX()), (oC)base::round(d.getY()));
	}
	//
	/// Modify a point using standard transform with magnification
	//
	template <class iC, class oC>
		pod::point<oC> GXX_HIDDEN modifyStdMag( const transform &T, pod::point<iC> xy )
	{
		//
		// Transform the point.
		//
		pod::point<double> d = xy * T.getOrient() * T.get11() + T.getOffset();
		//
		// Convert to target coordinate type.
		//
		return std::tr1::is_floating_point<oC>::value ?
			pod::point<oC>( (oC)(d.getX()), (oC)(d.getY()))
			:
			pod::point<oC>( (oC)base::round(d.getX()), (oC)base::round(d.getY()));
	}
	//
	/// Modify a point using resolution transform.
	//
	template <class iC, class oC>
		pod::point<oC> GXX_HIDDEN modifyResolution( const transform &T, pod::point<iC> xy )
	{
		pod::point<double> tmp = xy % T.getDiagonal();
		if (T.getNext()) {
			if (std::tr1::is_double<oC>::value) {
				pod::point<double> tmp2 = pod::point<double>((double)tmp.X(),
						(double)tmp.Y());
				return T.getNext()->modifyDblToDbl(tmp2);
			} else if (std::tr1::is_long<oC>::value) {
				pod::point<long> tmp2 = pod::point<long>((long)base::round(tmp.X()),
						(long)base::round(tmp.Y()));
				return T.getNext()->modifyLongToLong(tmp2);
			} else if (std::tr1::is_int<oC>::value) {
				pod::point<long> tmp2 = pod::point<long>((long)base::round(tmp.X()),
						(long)base::round(tmp.Y()));
				return T.getNext()->modifyLongToInt(tmp2);
			} else if (std::tr1::is_short<oC>::value) {
				pod::point<long> tmp2 = pod::point<long>((long)base::round(tmp.X()),
						(long)base::round(tmp.Y()));
				return T.getNext()->modifyLongToShort(tmp2);
			}
		} else
			return std::tr1::is_floating_point<oC>::value ?
					pod::point<oC>((oC)tmp.X(),(oC)tmp.Y()) :
					pod::point<oC>((oC)base::round(tmp.X()),
						(oC)base::round(tmp.Y()));
		// Should not get here or something is missing.
		throw except::programError();
		return pod::point<oC>((oC)0, (oC)0);
	}
	//
	// Modify a rectangle using scale transform.
	//
	template <class iC,class oC>
		pod::rectangle<oC> GXX_HIDDEN modifyScaleRect(
			const transform & T,
			const pod::rectangle<iC> & r)
	{
		pod::rectangle<oC> rect(modifyScale<iC,oC>(T,r.getLowerLeft()),
								modifyScale<iC,oC>(T,r.getUpperRight()));
		rect.makeValid();
		return rect;
	}
	//
	// Modify a rectangle using scale and offset transform.
	//
	template <class iC,class oC>
		pod::rectangle<oC> GXX_HIDDEN modifyScaleOffsetRect(
			const transform & T,
			const pod::rectangle<iC> & r)
	{
		pod::rectangle<oC> rect(modifyScaleOffset<iC,oC>(T,r.getLowerLeft()),
								modifyScaleOffset<iC,oC>(T,r.getUpperRight()));
		rect.makeValid();
		return rect;
	}
	//
	// Modify a rectangle using a general transform.
	//
	template <class iC, class oC>
		pod::rectangle<oC> GXX_HIDDEN modifyGeneralRect(
				const transform & T,
				const pod::rectangle<iC> & r )
	{
		//
		// We have to transform all four corner points when using a
		// general transform in order to figure out what the new corner
		// points will be.
		//
		pod::point<oC> p0 = modifyGeneral<iC,oC>(T,r.getLowerLeft());
		pod::point<oC> p1 = modifyGeneral<iC,oC>(T,r.getLowerRight());
		pod::point<oC> p2 = modifyGeneral<iC,oC>(T,r.getUpperRight());
		pod::point<oC> p3 = modifyGeneral<iC,oC>(T,r.getUpperLeft());
		pod::rectangle<oC> rect;
		rect.merge(p0);
		rect.merge(p1);
		rect.merge(p2);
		rect.merge(p3);
		return rect;
	}

	template <class iC,class oC>
		pod::rectangle<oC> GXX_HIDDEN modifyStdRect (
				const transform & T,
				const pod::rectangle<iC> & r )
	{
		pod::rectangle<oC> rect(modifyStd<iC,oC>(T,r.getLowerLeft()),
								modifyStd<iC,oC>(T,r.getUpperRight()));
		rect.makeValid();
		return rect;
	}
	//
	// Modify a rectangle using standard transform with magnification.
	//
	template <class iC, class oC>
		pod::rectangle<oC> GXX_HIDDEN modifyStdMagRect(
				const transform &T,
				const pod::rectangle<iC> & r )
	{
		pod::rectangle<oC> rect(modifyStdMag<iC,oC>(T,r.getLowerLeft()),
								modifyStdMag<iC,oC>(T,r.getUpperRight()));
		rect.makeValid();
		return rect;
	}
	//
	// Modify a rectangle using resolution transform.
	//
	template <class iC,class oC>
		pod::rectangle<oC> GXX_HIDDEN modifyResolutionRect(
			const transform & T,
			const pod::rectangle<iC> & r)
	{
		pod::rectangle<oC> rect(modifyResolution<iC,oC>(T,r.getLowerLeft()),
				modifyResolution<iC,oC>(T,r.getUpperRight()));
		rect.makeValid();
		return rect;
	}

	//
	/// Type definition for determining inverse of various transform types.
	//
	typedef transform (*pGetInverseFun)(const transform &);
	//
	/// Table of inverse determining functions by transform type.
	//
	static pGetInverseFun getInverseFnTbl[transformTypeMAX];
	
	typedef void (*pMultFun)(	const transform & T1,
								const transform & T2,
								transform & outT );

	static pMultFun multFnTbl[transformTypeMAX*transformTypeMAX];
	//
	/// Initialize the function tables.
	//
	void transform::initFnTables()
	{
		//
		// double to short
		//
		modifyDblToShortFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<double,short>;
		modifyDblToShortFunTbl[GeneralTransformType] = &modifyGeneral<double,short>;
		modifyDblToShortFunTbl[StdTransformType] = &modifyStd<double,short>;
		modifyDblToShortFunTbl[StdTransformWithMagType] = &modifyStdMag<double,short>;
		modifyDblToShortFunTbl[ScaleTransformType] = &modifyScale<double,short>;
		modifyDblToShortFunTbl[ResolutionTransformType] = &modifyResolution<double,short>;
		//
		// long to short
		//
		modifyLongToShortFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<long,short>;
		modifyLongToShortFunTbl[GeneralTransformType] = &modifyGeneral<long,short>;
		modifyLongToShortFunTbl[StdTransformType] = &modifyStd<long,short>;
		modifyLongToShortFunTbl[StdTransformWithMagType] = &modifyStdMag<long,short>;
		modifyLongToShortFunTbl[ScaleTransformType] = &modifyScale<long,short>;
		modifyLongToShortFunTbl[ResolutionTransformType] = &modifyResolution<long,short>;
		//
		// int to short
		//
		modifyIntToShortFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<int,short>;
		modifyIntToShortFunTbl[GeneralTransformType] = &modifyGeneral<int,short>;
		modifyIntToShortFunTbl[StdTransformType] = &modifyStd<int,short>;
		modifyIntToShortFunTbl[StdTransformWithMagType] = &modifyStdMag<int,short>;
		modifyIntToShortFunTbl[ScaleTransformType] = &modifyScale<int,short>;
		modifyIntToShortFunTbl[ResolutionTransformType] = &modifyResolution<int,short>;
		//
		// double to int
		//
		modifyDblToIntFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<double,int>;
		modifyDblToIntFunTbl[GeneralTransformType] = &modifyGeneral<double,int>;
		modifyDblToIntFunTbl[StdTransformType] = &modifyStd<double,int>;
		modifyDblToIntFunTbl[StdTransformWithMagType] = &modifyStdMag<double,int>;
		modifyDblToIntFunTbl[ScaleTransformType] = &modifyScale<double,int>;
		modifyDblToIntFunTbl[ResolutionTransformType] = &modifyResolution<double,int>;
		//
		// double to double
		//
		modifyDblToDblFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<double,double>;
		modifyDblToDblFunTbl[GeneralTransformType] = &modifyGeneral<double,double>;
		modifyDblToDblFunTbl[StdTransformType] = &modifyStd<double,double>;
		modifyDblToDblFunTbl[StdTransformWithMagType] = &modifyStdMag<double,double>;
		modifyDblToDblFunTbl[ScaleTransformType] = &modifyScale<double,double>;
		modifyDblToDblFunTbl[ResolutionTransformType] = &modifyResolution<double,double>;
		//
		// long to int
		//
		modifyLongToIntFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<long,int>;
		modifyLongToIntFunTbl[GeneralTransformType] = &modifyGeneral<long,int>;
		modifyLongToIntFunTbl[StdTransformType] = &modifyStd<long,int>;
		modifyLongToIntFunTbl[StdTransformWithMagType] = &modifyStdMag<long,int>;
		modifyLongToIntFunTbl[ScaleTransformType] = &modifyScale<long,int>;
		modifyLongToIntFunTbl[ResolutionTransformType] = &modifyResolution<long,int>;
		//
		// int to int
		//
		modifyIntToIntFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<int,int>;
		modifyIntToIntFunTbl[GeneralTransformType] = &modifyGeneral<int,int>;
		modifyIntToIntFunTbl[StdTransformType] = &modifyStd<int,int>;
		modifyIntToIntFunTbl[StdTransformWithMagType] = &modifyStdMag<int,int>;
		modifyIntToIntFunTbl[ScaleTransformType] = &modifyScale<int,int>;
		modifyIntToIntFunTbl[ResolutionTransformType] = &modifyResolution<int,int>;
		//
		// long to long
		//
		modifyLongToLongFunTbl[ScaleOffsetTransformType] = &modifyScaleOffset<long,long>;
		modifyLongToLongFunTbl[GeneralTransformType] = &modifyGeneral<long,long>;
		modifyLongToLongFunTbl[StdTransformType] = &modifyStd<long,long>;
		modifyLongToLongFunTbl[StdTransformWithMagType] = &modifyStdMag<long,long>;
		modifyLongToLongFunTbl[ScaleTransformType] = &modifyScale<long,long>;
		modifyLongToLongFunTbl[ResolutionTransformType] = &modifyResolution<long,long>;
		//
		/// Init get inverse func table.
		//
		getInverseFnTbl[ScaleOffsetTransformType] = &getInverseScaleOffset;
		getInverseFnTbl[GeneralTransformType] = &getInverseGeneral;
		getInverseFnTbl[StdTransformType] = &getInverseStd;
		getInverseFnTbl[StdTransformWithMagType] = &getInverseStdWithMag;
		getInverseFnTbl[ScaleTransformType] = &getInverseScale;
		getInverseFnTbl[ResolutionTransformType] = &getInverseResolution;
		//
		/// Init the multiply func table.
		//
		multFnTbl[0] = &multScaleOffsetXScaleOffset;
		multFnTbl[1] = &multScaleOffsetXGeneral;
		multFnTbl[2] = &multScaleOffsetXStd;
		multFnTbl[3] = &multScaleOffsetXStdWithMag;
		multFnTbl[4] = &multScaleOffsetXScaleOffset;
		multFnTbl[5] = &multScaleOffsetXResolution;
		multFnTbl[6] = &multGeneralXScaleOffset;
		multFnTbl[7] = &multGeneralXGeneral;
		multFnTbl[8] = &multGeneralXStd;
		multFnTbl[9] = &multGeneralXStdWithMag;
		multFnTbl[10] = &multGeneralXScaleOffset;
		multFnTbl[11] = &multGeneralXResolution;
		multFnTbl[12] = &multStdXScaleOffset;
		multFnTbl[13] = &multStdXGeneral;
		multFnTbl[14] = &multStdXStd;
		multFnTbl[15] = &multStdXStdWithMag;
		multFnTbl[16] = &multStdXScaleOffset;
		multFnTbl[17] = &multStdXResolution;
		multFnTbl[18] = &multStdWithMagXScaleOffset;
		multFnTbl[19] = &multStdWithMagXGeneral;
		multFnTbl[20] = &multStdWithMagXStd;
		multFnTbl[21] = &multStdWithMagXStdWithMag;
		multFnTbl[22] = &multStdWithMagXScaleOffset;
		multFnTbl[23] = &multStdWithMagXResolution;
		multFnTbl[24] = &multScaleOffsetXScaleOffset;
		multFnTbl[25] = &multScaleOffsetXGeneral;
		multFnTbl[26] = &multScaleOffsetXStd;
		multFnTbl[27] = &multScaleOffsetXStdWithMag;
		multFnTbl[28] = &multScaleXScale;
		multFnTbl[29] = &multScaleXResolution;
		multFnTbl[30] = &multResolutionXAny;
		multFnTbl[31] = &multResolutionXAny;
		multFnTbl[32] = &multResolutionXAny;
		multFnTbl[33] = &multResolutionXAny;
		multFnTbl[34] = &multResolutionXAny;
		multFnTbl[35] = &multResolutionXAny;
		//
		// Initialize the rectangle modification function tables.
		//
		modifyRectLongToLongFunTbl[ScaleOffsetTransformType] = modifyScaleOffsetRect<long,long>;
		modifyRectLongToLongFunTbl[GeneralTransformType] = modifyGeneralRect<long,long>;
		modifyRectLongToLongFunTbl[StdTransformType] = modifyStdRect<long,long>;
		modifyRectLongToLongFunTbl[StdTransformWithMagType] = modifyStdMagRect<long,long>;
		modifyRectLongToLongFunTbl[ScaleTransformType] = modifyScaleRect<long,long>;
		modifyRectLongToLongFunTbl[ResolutionTransformType] = modifyResolutionRect<long,long>;

		modifyRectDblToDblFunTbl[ScaleOffsetTransformType] = modifyScaleOffsetRect<double,double>;
		modifyRectDblToDblFunTbl[GeneralTransformType] = modifyGeneralRect<double,double>;
		modifyRectDblToDblFunTbl[StdTransformType] = modifyStdRect<double,double>;
		modifyRectDblToDblFunTbl[StdTransformWithMagType] = modifyStdMagRect<double,double>;
		modifyRectDblToDblFunTbl[ScaleTransformType] = modifyScaleRect<double,double>;
		modifyRectDblToDblFunTbl[ResolutionTransformType] = modifyResolutionRect<double,double>;

		modifyRectIntToIntFunTbl[ScaleOffsetTransformType] = modifyScaleOffsetRect<int,int>;
		modifyRectIntToIntFunTbl[GeneralTransformType] = modifyGeneralRect<int,int>;
		modifyRectIntToIntFunTbl[StdTransformType] = modifyStdRect<int,int>;
		modifyRectIntToIntFunTbl[StdTransformWithMagType] = modifyStdMagRect<int,int>;
		modifyRectIntToIntFunTbl[ScaleTransformType] = modifyScaleRect<int,int>;
		modifyRectIntToIntFunTbl[ResolutionTransformType] = modifyResolutionRect<int,int>;
	}

	transform transform::getInverse()	const
	{
		return (*(getInverseFnTbl[type]))(*this);
	}

	void transform::mult(const transform &T2, transform & outT) const
	{
		(*(multFnTbl[(type*transformTypeMAX)+T2.type]))(*this,T2,outT);
	}

	pod::point<short> transform::modifyDblToShort(pod::point<double> p) const
	{
		return (*(modifyDblToShortFunTbl[type]))(*this, p);
	}

	pod::point<int> transform::modifyDblToInt(pod::point<double> p) const
	{
		return (*(modifyDblToIntFunTbl[type]))(*this, p);
	}

	pod::point<double> transform::modifyDblToDbl(pod::point<double> p) const
	{
		return (*(modifyDblToDblFunTbl[type]))(*this, p);
	}
	pod::point<double> transform::modify(pod::point<double> p) const
	{
		return (*(modifyDblToDblFunTbl[type]))(*this, p);
	}

	pod::point<int> transform::modifyIntToInt(pod::point<int> p) const
	{
		return (*(modifyIntToIntFunTbl[type]))(*this, p);
	}
	pod::point<int> transform::modify(pod::point<int> p) const
	{
		return (*(modifyIntToIntFunTbl[type]))(*this, p);
	}

	pod::point<short> transform::modifyIntToShort(pod::point<int> p) const
	{
		return (*(modifyIntToShortFunTbl[type]))(*this, p);
	}

	pod::point<short> transform::modifyLongToShort(pod::point<long> p) const
	{
		return (*(modifyLongToShortFunTbl[type]))(*this, p);
	}

	pod::point<int> transform::modifyLongToInt(pod::point<long> p) const
	{
		return (*(modifyLongToIntFunTbl[type]))(*this, p);
	}

	pod::point<long> transform::modifyLongToLong(pod::point<long> p) const
	{
		return (*(modifyLongToLongFunTbl[type]))(*this, p);
	}

	pod::point<long> transform::modify(pod::point<long> p) const
	{
		return (*(modifyLongToLongFunTbl[type]))(*this, p);
	}

	pod::rectangle<double> transform::operator()(const pod::rectangle<double> & r) const
	{
		return (*(modifyRectDblToDblFunTbl[type]))(*this, r);
	}

	pod::rectangle<long> transform::operator()(const pod::rectangle<long> & r) const
	{
		return (*(modifyRectLongToLongFunTbl[type]))(*this, r);
	}

	pod::rectangle<int> transform::operator()(const pod::rectangle<int> & r) const
	{
		return (*(modifyRectIntToIntFunTbl[type]))(*this, r);
	}

	pod::point<double> transform::operator()(const pod::point<double> & p) const
	{
		return (*(modifyDblToDblFunTbl[type]))(*this, p);
	}

	pod::point<long> transform::operator()(const pod::point<long> & p) const
	{
		return (*(modifyLongToLongFunTbl[type]))(*this, p);
	}

	pod::point<int> transform::operator()(const pod::point<int> & p) const
	{
		return (*(modifyIntToIntFunTbl[type]))(*this, p);
	}


	// Value of standard orientation
	const struct { bool xflip; double angle;} stdOrientXFlipAngValues[stdOrientMAX]=
	{
		{false,   0. }, // RO
		{false,  90. }, // R90
		{false, 180. }, // R180
		{false, 270. }, // R270			
		{true,    0. }, // XFlipR0
		{true,   90. }, //XFlipR90
		{true,  180. }, //XFlipR180
		{true,  270. } 	//XFlipR270
	};
	
	const void transform::getAngleFlipMag( double& angle, bool& isXFlip, double& mag) const
	{

		if( type!=GeneralTransformType)
		{
			//
			// standard orientation. Use table
			//
			int idx= (int) getOrient().getOri();
			isXFlip= stdOrientXFlipAngValues[idx].xflip;
			angle  = stdOrientXFlipAngValues[idx].angle;
			mag    = a11;
		}
		else
		{
			// From constructor
			//             double multiplier = 1.0;
			//				if (inXFlip == true)
			//					multiplier = -1.0;
			//				register double newAngle = degreesToRadians(normalizeAngle(inAngle));
			//				register double cosine = cos(newAngle);
			//				register double sine = sin(newAngle);
			//				a11 = inMag * cosine;
			//				a12 = inMag * sine;
			//				a21 = -inMag * multiplier * sine;
			//				a22 = inMag * multiplier * cosine;
			// 
			// How to get angle, inMage and inXFlip from a11, a12, a21, and a22
			//    Let sumProd = a11* a22 - a12*a21
			//    = (inMag*consine)(inMag * multiplier * cosine) - (inMag * sine)(-inMag * multiplier * sine)  
			//    = inMag*inMag*multiplier*consine*consine  + inMag*inMag*multiplier*sine*sine
			//    = inMag*inMag*multiplier(consine* consine + sine*sine)
			//    = inMag*inMag*multiplier
			//
			//    if( sumProd) < 0 ==> inXFlip=true
			//    inMag= SquareRoot( Abs(sumPro));
			//    Angle= ArcCosine(a11/inMag)

			double sumProd= a11*a22 - a12*a21;

			//get inXFlip.
			// sumProd= imMag*imMag*multiplier
			isXFlip= sumProd < 0;

			// get abs(imMag*imMag)
			if(sumProd<0)
				sumProd= -sumProd;

			// get mag
			mag= sqrt(sumProd);
			
			// get angle
			if(mag> 0)
			{

				double angleR= acos(a11/mag);
				angle=radiansToDegrees(angleR);

				// aCos funciton range is 0 to 180.
				// We need to decide if angle is between 180 to 360
				//
				// How to solve it
				//   Let (Xt, Yt) is the point by applying (1,0) with transformation without xFlip,
				//   if  Yt > 0, then angle is located between 180 and 360
				//   
				//   i.e. 
				//      | a11  a12 | x | 1 | =  [a11 a21p ] = (Xt, Yt)
				//      | a21p a22 |   | 0 |
				//	   a21=  multiplier * a21p = xFlip + angle
				//     if a21p > 0 angle is located between 180 and 360
				//
				double a21p= isXFlip? -a21: a21;
				if(   a21p> 0 )
					angle= 360. - angle;
				
			}
			else
				angle=0;
		}// end else
	}

	transform transform::getInverseScaleOffset(const transform & T)
	{
		return transform(	-T.offset.X()/T.a11,
							-T.offset.Y()/T.a22,
							1.0/T.a11,
							1.0/T.a22);
	}
	//
	/// Get the inverse of a general transform.
	//
	transform transform::getInverseGeneral(const transform & T )
	{
		//
		// compute the determinant
		//
		double det = T.a11*T.a22 - T.a12*T.a21;

		return transform(	(T.offset.Y()*T.a21 - T.offset.X()*T.a22)/det,
							(T.offset.X()*T.a12 - T.offset.Y()*T.a11)/det,
							T.a22/det,
							-T.a12/det,
							-T.a21/det,
							T.a11/det);
	}
	//
	/// Get the inverse of a standard transform.
	//
	transform transform::getInverseStd(const transform & T)
	{
		//
		// Transform the offset.
		//
		pod::point<double> off =  - T.offset * stdOrient(inverseStdOrient[T.orient.getOri()]);
		//
		// Return the inverted standard transform.
		//
		return transform (off.X(), off.Y(),
				inverseStdOrient[T.orient.getOri()]);
	}
	//
	/// Get the inverse of a standard transform with mag.
	//
	transform transform::getInverseStdWithMag(const transform & T)
	{
		register double oneOverMag = 1.0/T.a11;
		register stdOrientEnum e = inverseStdOrient[T.orient.getOri()];
		//
		// Transform the offset.
		//
		pod::point<double> off = - (T.offset * stdOrient(e)) * oneOverMag;

		return transform(	off.X(),
							off.Y(),
							e,
							oneOverMag );
	}
	//
	/// Get the inverse of a scale transform.
	//
	transform transform::getInverseScale(const transform & T)
	{
		return transform(	0.0,
							0.0,
							1.0/T.a11,
							1.0/T.a22);
	}
	//
	// Get the inverse of a resolution transform.
	//
	transform transform::getInverseResolution(const transform & T)
	{
		if (T.next) {
			transform T1(T.a11, T.a22, false);
			transform T2;
			T1.mult(*(T.next), T2);
			return T2.getInverse();
		} else {
			return transform(1.0/T.a11, 1.0/T.a22, false);
		}
	}
	//
	/// Multiply two scale transforms.
	//
	void transform::multScaleXScale(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = ScaleTransformType;
		outT.a11 = T1.a11*T2.a11;
		outT.a22 = T1.a22*T2.a22;
		outT.offset = pod::point<double>(0.0, 0.0);
	}
	//
	/// Multiply a scale transform with a resolution transform.
	//
	void transform::multScaleXResolution(const transform &T1,
			const transform &T2, transform &outT)
	{
		outT.type = ResolutionTransformType;
		outT.a11 = T2.a11;
		outT.a12 = T2.a12;
		outT.a21 = T2.a21;
		outT.a22 = T2.a22;

		if (T2.next) {
				if (!outT.next)
					outT.next = new transform;
				transform tmp(T1.a11, T1.a22, false);
				tmp.mult(*(T2.next), *(outT.next));
		} else {
			if (outT.next) {
				outT.next->type = ScaleTransformType;
				outT.next->a11 = T1.a11;
				outT.next->a12 = T1.a12;
				outT.next->a21 = T1.a21;
				outT.next->a22 = T1.a22;
				outT.next->offset.setX(0.0);
				outT.next->offset.setY(0.0);
			} else {
				outT.next = new transform(T1.a11, T1.a22, false);
			}
		}
	}
	//
	//
	/// Multiply two scale offset transforms.
	//
	void transform::multScaleOffsetXScaleOffset(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = ScaleOffsetTransformType;
		outT.a11 = T1.a11*T2.a11;
		outT.a22 = T1.a22*T2.a22;
		outT.offset = pod::point<double>(
				T2.a11*T1.offset.X() + T2.offset.X(), 
				T2.a22*T1.offset.Y() + T2.offset.Y());
	}
	//
	/// Multiply a scale offset transform with a general transform.
	//
	void transform::multScaleOffsetXGeneral(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 = T1.a11*T2.a11;
		outT.a12 = T1.a11*T2.a12;
		outT.a21 = T1.a22*T2.a21;
		outT.a22 = T1.a22*T2.a22;
		outT.offset = pod::point<double>(	T1.offset.X()*T2.a11 + T1.offset.Y()*T2.a21 + T2.offset.X(),
											T1.offset.X()*T2.a12 + T1.offset.Y()*T2.a22 + T2.offset.Y());
	}
	//
	/// Multiply a scale offset transform with a standard transform.
	//
	void transform::multScaleOffsetXStd(	const transform &T1, 
										const transform &T2,
										transform &outT)
	{
		//
		// If we have uniform magnification, then we do not have to go to
		// a general transform.
		//
		if ( T1.a11 == T1.a22 )
		{
			outT.type = StdTransformWithMagType;
			outT.orient = T2.orient;
			outT.a11 = T1.a11*T2.a11;
		}
		else
		{
			outT.type = GeneralTransformType;
			outT.a11 = T1.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][0];
			outT.a12 = T1.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][1];
			outT.a21 = T1.a22*stdOrientCoeffMatrix[T2.orient.getOri()][1][0];
			outT.a22 = T1.a22*stdOrientCoeffMatrix[T2.orient.getOri()][1][1];
		}
		outT.offset = pod::point<double>(
				T1.offset.X()*stdOrientCoeffMatrix[T2.orient.getOri()][0][0] + 
				T1.offset.Y()*stdOrientCoeffMatrix[T2.orient.getOri()][1][0],
				T1.offset.X()*stdOrientCoeffMatrix[T2.orient.getOri()][0][1] + 
				T1.offset.Y()*stdOrientCoeffMatrix[T2.orient.getOri()][1][1])
			+ T2.offset;
	}
	//
	/// Multiply a scale offset transform with a standard transform
	/// with magnification.
	//
	void transform::multScaleOffsetXStdWithMag(	const transform &T1, 
											const transform &T2,
											transform &outT)
	{
		//
		// If we have uniform magnification, then we do not have to go to
		// a general transform.
		//
		if ( T1.a11 == T1.a22 )
		{
			outT.type = StdTransformWithMagType;
			outT.orient = T2.orient;
			outT.a11 = T1.a11*T2.a11;
		}
		else
		{
			outT.type = GeneralTransformType;
			outT.a11 = T1.a11*T2.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][0];
			outT.a12 = T1.a11*T2.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][1];
			outT.a21 = T1.a22*T2.a11*stdOrientCoeffMatrix[T2.orient.getOri()][1][0];
			outT.a22 = T1.a22*T2.a11*stdOrientCoeffMatrix[T2.orient.getOri()][1][1];
		}
		outT.offset = pod::point<double>(
				T2.a11*(T1.offset.X()*stdOrientCoeffMatrix[T2.orient.getOri()][0][0] + 
						T1.offset.Y()*stdOrientCoeffMatrix[T2.orient.getOri()][1][0]),
				T2.a11*(T1.offset.X()*stdOrientCoeffMatrix[T2.orient.getOri()][0][1] + 
						T1.offset.Y()*stdOrientCoeffMatrix[T2.orient.getOri()][1][1]))
				+ T2.offset;
	}
	//
	/// Multiply a scale offset transform with a resolution transform.
	//
	void transform::multScaleOffsetXResolution(const transform &T1,
			const transform &T2, transform &outT)
	{
		outT.type = ResolutionTransformType;
		outT.a11 = T2.a11;
		outT.a12 = T2.a12;
		outT.a21 = T2.a21;
		outT.a22 = T2.a22;

		if (T2.next) {
				if (!outT.next)
					outT.next = new transform;
				transform tmp(
						T1.offset.getX()*T2.a11,
						T1.offset.getY()*T2.a22,
						T1.a11, T1.a22);
				tmp.mult(*(T2.next), *(outT.next));
		} else {
			if (outT.next) {
				outT.next->type = ScaleOffsetTransformType;
				outT.next->a11 = T1.a11;
				outT.next->a12 = 0.0;
				outT.next->a21 = 0.0;
				outT.next->a22 = T1.a22;
				outT.next->offset.setX(T1.offset.getX()*T2.a11);
				outT.next->offset.setY(T1.offset.getY()*T2.a22);
			} else {
				outT.next = new transform(
							T1.offset.getX()*T2.a11,
							T1.offset.getY()*T2.a22,
							T1.a11, T1.a22);
			}
		}
	}
	//
	/// Multiply a general transform with a scale offset transform.
	//
	void transform::multGeneralXScaleOffset(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 = T1.a11*T2.a11;
		outT.a12 = T1.a12*T2.a22;
		outT.a21 = T1.a21*T2.a11;
		outT.a22 = T1.a22*T2.a22;
		outT.offset = pod::point<double>(	T1.offset.X()*T2.a11,
								T1.offset.Y()*T2.a22)
			+ T2.offset;
	}
	//
	/// Multiply two general transforms.
	//
	void transform::multGeneralXGeneral(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 = T1.a11*T2.a11 + T1.a12*T2.a21;
		outT.a12 = T1.a11*T2.a12 + T1.a12*T2.a22;
		outT.a21 = T1.a21*T2.a11 + T1.a22*T2.a21;
		outT.a22 = T1.a21*T2.a12 + T1.a22*T2.a22;
		outT.offset = pod::point<double>(	T1.offset.X()*T2.a11 + T1.offset.Y()*T2.a21,
								T1.offset.X()*T2.a12 + T1.offset.Y()*T2.a22)
			+ T2.offset;
	}
	//
	/// Multiply a general transform with a standard transform.
	//
	void transform::multGeneralXStd(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 =	T1.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][0] + 
					T1.a12*stdOrientCoeffMatrix[T2.orient.getOri()][1][0];
		outT.a12 =	T1.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][1] + 
					T1.a12*stdOrientCoeffMatrix[T2.orient.getOri()][1][1];
		outT.a21 =	T1.a21*stdOrientCoeffMatrix[T2.orient.getOri()][0][0] + 
					T1.a22*stdOrientCoeffMatrix[T2.orient.getOri()][1][0];
		outT.a22 =	T1.a21*stdOrientCoeffMatrix[T2.orient.getOri()][0][1] + 
					T1.a22*stdOrientCoeffMatrix[T2.orient.getOri()][1][1];
		//
		// Transform the offset.
		//
		outT.offset = T1.offset * T2.orient + T2.offset;
	}
	//
	/// Multiply a general transform with a standard transform
	/// with magnification.
	//
	void transform::multGeneralXStdWithMag(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 =	T2.a11*(T1.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][0] + 
					T1.a12*stdOrientCoeffMatrix[T2.orient.getOri()][1][0]);
		outT.a12 =	T2.a11*(T1.a11*stdOrientCoeffMatrix[T2.orient.getOri()][0][1] + 
					T1.a12*stdOrientCoeffMatrix[T2.orient.getOri()][1][1]);
		outT.a21 =	T2.a11*(T1.a21*stdOrientCoeffMatrix[T2.orient.getOri()][0][0] + 
					T1.a22*stdOrientCoeffMatrix[T2.orient.getOri()][1][0]);
		outT.a22 =	T2.a11*(T1.a21*stdOrientCoeffMatrix[T2.orient.getOri()][0][1] + 
					T1.a22*stdOrientCoeffMatrix[T2.orient.getOri()][1][1]);
		//
		// Transform the offset.
		//
		outT.offset = (T1.offset * T2.orient) * T2.a11 + T2.offset;
	}
	//
	/// Multiply a general transform with a resolution transform.
	//
	void transform::multGeneralXResolution(const transform &T1,
			const transform &T2, transform &outT)
	{
		outT.type = ResolutionTransformType;
		outT.a11 = T2.a11;
		outT.a12 = T2.a12;
		outT.a21 = T2.a21;
		outT.a22 = T2.a22;

		if (T2.next) {
				if (!outT.next)
					outT.next = new transform;
				transform tmp(
						T1.offset.getX()*T2.a11,
						T1.offset.getY()*T2.a22,
						T1.a11, T1.a12, T1.a21, T1.a22);
				tmp.mult(*(T2.next), *(outT.next));
		} else {
			if (outT.next) {
				outT.next->type = GeneralTransformType;
				outT.next->a11 = T1.a11;
				outT.next->a22 = T1.a22;
				outT.next->a12 = T1.a12;
				outT.next->a21 = T1.a21;
				outT.next->offset.setX(T1.offset.getX()*T2.a11);
				outT.next->offset.setY(T1.offset.getY()*T2.a22);
			} else {
				outT.next = new transform(
							T1.offset.getX()*T2.a11,
							T1.offset.getY()*T2.a22,
							T1.a11, T1.a12, T1.a21, T1.a22);
			}
		}
	}
	//
	/// Multiply a standard transform with a scale and offset transform.
	//
	void transform::multStdXScaleOffset(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 = stdOrientCoeffMatrix[T1.orient.getOri()][0][0]*T2.a11;
		outT.a12 = stdOrientCoeffMatrix[T1.orient.getOri()][0][1]*T2.a22;
		outT.a21 = stdOrientCoeffMatrix[T1.orient.getOri()][1][0]*T2.a11;
		outT.a22 = stdOrientCoeffMatrix[T1.orient.getOri()][1][1]*T2.a22;
		//
		// Transform the offset.
		//
		outT.offset = pod::point<double>(	T1.offset.X()*T2.a11,
								T1.offset.Y()*T2.a22)
		   + T2.offset;
	}
	//
	/// Multiply a standard transform with a general transform.
	//
	void transform::multStdXGeneral(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 = stdOrientCoeffMatrix[T1.orient.getOri()][0][0]*T2.a11 + 
				stdOrientCoeffMatrix[T1.orient.getOri()][0][1]*T2.a21;
		outT.a12 = stdOrientCoeffMatrix[T1.orient.getOri()][0][0]*T2.a12 + 
				stdOrientCoeffMatrix[T1.orient.getOri()][0][1]*T2.a22;
		outT.a21 = stdOrientCoeffMatrix[T1.orient.getOri()][1][0]*T2.a11 + 
				stdOrientCoeffMatrix[T1.orient.getOri()][1][1]*T2.a21;
		outT.a22 = stdOrientCoeffMatrix[T1.orient.getOri()][1][0]*T2.a12 + 
				stdOrientCoeffMatrix[T1.orient.getOri()][1][1]*T2.a22;
		//
		// Transform the offset.
		//
		outT.offset = pod::point<double>(	T1.offset.X()*T2.a11 + T1.offset.Y()*T2.a21,
								T1.offset.X()*T2.a12 + T1.offset.Y()*T2.a22)
			+ T2.offset;
	}
	//
	/// Multiply two standard transforms.
	//
	void transform::multStdXStd(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = StdTransformType;
		outT.orient = multStdOrientMatrix[T1.orient.getOri()][T2.orient.getOri()];
		//
		// Transform the offset.
		//
		outT.offset = T1.offset * T2.orient + T2.offset;
	}
	//
	/// Multiply a standard transform with a standard transform 
	/// with magnification.
	//
	void transform::multStdXStdWithMag(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = StdTransformWithMagType;
		outT.orient = stdOrient(multStdOrientMatrix[T1.orient.getOri()][T2.orient.getOri()]);
		outT.a11 = T2.a11;
		//
		// Transform the offset.
		//
		outT.offset = T1.offset * T2.orient * T2.a11 + T2.offset;
	}
	//
	/// Multiply a standard transform with a resolution transform.
	//
	void transform::multStdXResolution(const transform &T1,
			const transform &T2, transform &outT)
	{
		outT.type = ResolutionTransformType;
		outT.a11 = T2.a11;
		outT.a12 = T2.a12;
		outT.a21 = T2.a21;
		outT.a22 = T2.a22;

		if (T2.next) {
				if (!outT.next)
					outT.next = new transform;
				transform tmp(
						T1.offset.getX()*T2.a11,
						T1.offset.getY()*T2.a22,
						T1.orient.getOri());
				tmp.mult(*(T2.next), *(outT.next));
		} else {
			if (outT.next) {
				outT.next->type = StdTransformType;
				outT.next->a11 = T1.a11;
				outT.next->a12 = T1.a12;
				outT.next->a21 = T1.a21;
				outT.next->a22 = T1.a22;
				outT.next->offset.setX(T1.offset.getX()*T2.a11);
				outT.next->offset.setY(T1.offset.getY()*T2.a22);
				outT.next->orient = T1.orient;
			} else {
				outT.next = new transform(
							T1.offset.getX()*T2.a11,
							T1.offset.getY()*T2.a22,
							T1.orient.getOri());
			}
		}
	}
	//
	/// Multiply a standard transform with magnification with a 
	/// scale and offset transform.
	//
	void transform::multStdWithMagXScaleOffset(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 = T1.a11*stdOrientCoeffMatrix[T1.orient.getOri()][0][0]*T2.a11;
		outT.a12 = T1.a11*stdOrientCoeffMatrix[T1.orient.getOri()][0][1]*T2.a22;
		outT.a21 = T1.a11*stdOrientCoeffMatrix[T1.orient.getOri()][1][0]*T2.a11;
		outT.a22 = T1.a11*stdOrientCoeffMatrix[T1.orient.getOri()][1][1]*T2.a22;
		//
		// Transform the offset.
		//
		outT.offset = pod::point<double>(	T1.offset.X()*T2.a11,
								T1.offset.Y()*T2.a22)
			+ T2.offset;
	}
	//
	/// Multiply a standard transform with magnification with
	/// a general transform.
	//
	void transform::multStdWithMagXGeneral(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = GeneralTransformType;
		outT.a11 = T1.a11*(	stdOrientCoeffMatrix[T1.orient.getOri()][0][0]*T2.a11 + 
							stdOrientCoeffMatrix[T1.orient.getOri()][0][1]*T2.a21);
		outT.a12 = T1.a11*(	stdOrientCoeffMatrix[T1.orient.getOri()][0][0]*T2.a12 + 
							stdOrientCoeffMatrix[T1.orient.getOri()][0][1]*T2.a22);
		outT.a21 = T1.a11*(	stdOrientCoeffMatrix[T1.orient.getOri()][1][0]*T2.a11 + 
							stdOrientCoeffMatrix[T1.orient.getOri()][1][1]*T2.a21);
		outT.a22 = T1.a11*(	stdOrientCoeffMatrix[T1.orient.getOri()][1][0]*T2.a12 + 
							stdOrientCoeffMatrix[T1.orient.getOri()][1][1]*T2.a22);
		//
		// Transfomr the offset.
		//
		outT.offset = pod::point<double>(	T1.offset.X()*T2.a11 + T1.offset.Y()*T2.a21,
								T1.offset.X()*T2.a12 + T1.offset.Y()*T2.a22)
			+ T2.offset;
	}
	//
	/// Multiply a standard transform with magnification with 
	/// a standard transform.
	//
	void transform::multStdWithMagXStd(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = StdTransformWithMagType;
		outT.orient = stdOrient(multStdOrientMatrix[T1.orient.getOri()][T2.orient.getOri()]);
		outT.a11 = T1.a11;
		//
		// Transform the offset.
		//
		outT.offset = T1.offset * T2.orient + T2.offset;
	}
	//
	/// Multiply two standard transforms with magnification.
	//
	void transform::multStdWithMagXStdWithMag(const transform &T1, 
			const transform &T2, transform &outT)
	{
		outT.type = StdTransformWithMagType;
		outT.orient = stdOrient(multStdOrientMatrix[T1.orient.getOri()][T2.orient.getOri()]);
		outT.a11 = T1.a11*T2.a11;
		//
		// Transform the offset.
		//
		outT.offset = T1.offset * T2.orient * T2.a11 + T2.offset;
	}
	//
	/// Multiply a standard transform with magnification with a 
	/// resolution transform.
	//
	void transform::multStdWithMagXResolution(const transform &T1,
			const transform &T2, transform &outT)
	{
		outT.type = ResolutionTransformType;
		outT.a11 = T2.a11;
		outT.a12 = T2.a12;
		outT.a21 = T2.a21;
		outT.a22 = T2.a22;

		if (T2.next) {
				if (!outT.next)
					outT.next = new transform;
				transform tmp(
						T1.offset.getX()*T2.a11,
						T1.offset.getY()*T2.a22,
						T1.orient.getOri(), T1.a11);
				tmp.mult(*(T2.next), *(outT.next));
		} else {
			if (outT.next) {
				outT.next->type = StdTransformWithMagType;
				outT.next->a11 = T1.a11;
				outT.next->a12 = T1.a12;
				outT.next->a21 = T1.a21;
				outT.next->a22 = T1.a22;
				outT.next->offset.setX(T1.offset.getX()*T2.a11);
				outT.next->offset.setY(T1.offset.getY()*T2.a22);
				outT.next->orient = T1.orient;
			} else {
				outT.next = new transform(
							T1.offset.getX()*T2.a11,
							T1.offset.getY()*T2.a22,
							T1.orient.getOri(), T1.a11);
			}
		}
	}
	//
	/// Multiply a resolution transform with any transform.
	//
	void transform::multResolutionXAny(const transform &T1,
			const transform &T2, transform &outT)
	{
		outT.type = ResolutionTransformType;
		outT.a11 = T1.a11;
		outT.a12 = T1.a12;
		outT.a21 = T1.a21;
		outT.a22 = T1.a22;

		if(T1.next) {
			if (!(outT.next))
				outT.next = new transform;
			T1.next->mult(T2, *(outT.next));
		} else {
			outT.next = new transform(T2);
		}
	}

	//
	///	This is a convinient function converting coordinate types without loss of precision.
	//
	/*!
		We try to not loss precision and use available functionality
		provided by transform maximally. This function does what
		all modifyXxxxToYyyyy does and is very helpful in generic programming.
	*/
	template <class iC, class oC>
	void transform::modify( const pod::point<iC>& rSrc, pod::point<oC>& rDest ) const
	{
		switch ( type )
		{
			case ScaleOffsetTransformType:
				rDest = modifyScaleOffset<iC,oC>( *this, rSrc );
				break;
			
			case GeneralTransformType:
				rDest = modifyGeneral<iC,oC>( *this, rSrc );
				break;
			
			case StdTransformType:
				rDest = modifyStd<iC,oC>( *this, rSrc );
				break;
			
			case StdTransformWithMagType:
				rDest = modifyStdMag<iC,oC>( *this, rSrc );
				break;
			
			case ScaleTransformType:
				rDest = modifyScale<iC,oC>( *this, rSrc );
				break;
			
			case ResolutionTransformType:
				rDest = modifyResolution<iC,oC>( *this, rSrc );
				break;

			default:
				throw except::outOfRange("base::modify: Unknown type.");
		} // switch
	}

	//
	///	This is pod::rectangle version of above function.
	//
	template <class iC, class oC>
	void transform::modify( const pod::rectangle<iC>& rSrc, pod::rectangle<oC>& rDest ) const
	{
		switch ( type )
		{
			case ScaleOffsetTransformType:
				rDest = modifyScaleOffsetRect<iC,oC>( *this, rSrc );
				break;
			
			case GeneralTransformType:
				rDest = modifyGeneralRect<iC,oC>( *this, rSrc );
				break;
			
			case StdTransformType:
				rDest = modifyStdRect<iC,oC>( *this, rSrc );
				break;
			
			case StdTransformWithMagType:
				rDest = modifyStdMagRect<iC,oC>( *this, rSrc );
				break;
			
			case ScaleTransformType:
				rDest = modifyScaleRect<iC,oC>( *this, rSrc );
				break;
			
			case ResolutionTransformType:
				rDest = modifyResolutionRect<iC,oC>( *this, rSrc );
				break;

			default:
				throw except::outOfRange("base::modify: Unknown type.");
		} // switch
	}

	//
	///	Transform given bbox.
	//
	/*!
		While we transform bbox, the usual approach of transforming
		lower left and upper right is not satisfactory. In case of
		rotations we need to transform and merge all four vertices
		of bbox to get one of bbox rectangles of a shape which bbox we
		want to transform. The bbox transformed this way will not be the
		smallest bbox of the shape, yet at least this will be a bbox.
		While if we use usual approach, then we will get (in case of rotation)
		a rectangle that is not nessecarily a bbox of original shape.

		The function also optimizes cases when transform has
		only orthogonal transforms.
	*/
	template <class iC, class oC>
	void transform::modifyBBox( const pod::rectangle<iC>& rSrc, pod::rectangle<oC>& rDest ) const
	{
		//
		//	If we have orthogonal transform?
		//	First check get12 since most of transform will
		//	have no rotation factor.
		//
		if ( get12() == 0 || get11() == 0 )
			modify( rSrc, rDest );
		//
		//	General case.
		//
		else
		{
			rDest.makeInvalid();
			pod::point<oC> tmp;
			
			modify( rSrc.getLowerLeft(), tmp );
			rDest.merge( tmp );

			modify( rSrc.getLowerRight(), tmp );
			rDest.merge( tmp );

			modify( rSrc.getUpperLeft(), tmp );
			rDest.merge( tmp );

			modify( rSrc.getUpperRight(), tmp );
			rDest.merge( tmp );
		}
	}


/*****************************************************************************
*	Instantiate template with some arguments.
*/
template void transform::modify<int,int>( const pod::point<int>&, pod::point<int>& ) const;
template void transform::modify<long,int>( const pod::point<long>&, pod::point<int>& ) const;
template void transform::modify<double,int>( const pod::point<double>&, pod::point<int>& ) const;

template void transform::modify<int,long>( const pod::point<int>&, pod::point<long>& ) const;
template void transform::modify<long,long>( const pod::point<long>&, pod::point<long>& ) const;
template void transform::modify<double,long>( const pod::point<double>&, pod::point<long>& ) const;

template void transform::modify<int,double>( const pod::point<int>&, pod::point<double>& ) const;
template void transform::modify<long,double>( const pod::point<long>&, pod::point<double>& ) const;
template void transform::modify<double,double>( const pod::point<double>&, pod::point<double>& ) const;

template void transform::modify<int,int>( const pod::rectangle<int>&, pod::rectangle<int>& ) const;
template void transform::modify<long,int>( const pod::rectangle<long>&, pod::rectangle<int>& ) const;
template void transform::modify<double,int>( const pod::rectangle<double>&, pod::rectangle<int>& ) const;

template void transform::modify<int,long>( const pod::rectangle<int>&, pod::rectangle<long>& ) const;
template void transform::modify<long,long>( const pod::rectangle<long>&, pod::rectangle<long>& ) const;
template void transform::modify<double,long>( const pod::rectangle<double>&, pod::rectangle<long>& ) const;

template void transform::modify<int,double>( const pod::rectangle<int>&, pod::rectangle<double>& ) const;
template void transform::modify<long,double>( const pod::rectangle<long>&, pod::rectangle<double>& ) const;
template void transform::modify<double,double>( const pod::rectangle<double>&, pod::rectangle<double>& ) const;

template void transform::modifyBBox<int,int>( const pod::rectangle<int>&, pod::rectangle<int>& ) const;
template void transform::modifyBBox<long,int>( const pod::rectangle<long>&, pod::rectangle<int>& ) const;
template void transform::modifyBBox<double,int>( const pod::rectangle<double>&, pod::rectangle<int>& ) const;

template void transform::modifyBBox<int,long>( const pod::rectangle<int>&, pod::rectangle<long>& ) const;
template void transform::modifyBBox<long,long>( const pod::rectangle<long>&, pod::rectangle<long>& ) const;
template void transform::modifyBBox<double,long>( const pod::rectangle<double>&, pod::rectangle<long>& ) const;

template void transform::modifyBBox<int,double>( const pod::rectangle<int>&, pod::rectangle<double>& ) const;
template void transform::modifyBBox<long,double>( const pod::rectangle<long>&, pod::rectangle<double>& ) const;
template void transform::modifyBBox<double,double>( const pod::rectangle<double>&, pod::rectangle<double>& ) const;

namespace transformTest
{


	/// called by testGetAngleFlipMag()
	bool testTran_(int idx, const transform& tr, bool xFlip0, double angle0, double mag0)
	{
		// Get angle/xflip/mag from tr
		bool xFlip;
		double angle, mag;
		tr.getAngleFlipMag( angle,xFlip, mag);

		// Compare 
		bool bFlp= xFlip == xFlip0;
		bool bAng=(angle - angle0) < absTolerance &&
         			  (angle0 -angle)  < absTolerance   ;
		bool bMag= 	(mag - mag0) < absTolerance &&
			          (mag0 -mag)  < absTolerance   ;

		// Print error message if there is different
		if(!bFlp || !bAng || !bMag)
		{
			printf(" %-2d tr(xFlop=%s)",idx, (xFlip0?"T":"F"));
			if(!bFlp)
				printf("!= %s", (xFlip?"T":"F"));
			
			printf(" (angle=%10.5f)", angle0) ;
			if(!bAng)
				printf("!= %10.5f", angle);
			
			printf(" (mag=%5.3f)",mag0);
			
			if(!bMag) 
				printf("!= %5.3f", mag);
			printf("\n");
			return false;
		}
		return true;
	}	

	//
	/// Function test getAngelFlipMag() 
	// 
	bool testGetAngleFlipMag()
	{
		struct { bool xFlip; double angle; double mag; } testData[]=
			{ // xFlip Angle  Max    index
				{false,   0,    1},    //0 standard Orientation
				{false,   90,   1.1},  //1
				{false,   180,  1.2},  //2
				{false,   270,  1.3},  //3
				{true,    0,    1.4},  //4
				{true,    90,   1.5},  //5
				{true,    180,  1.6},  //6
				{true,    270,  1.7},  //7
				{false,   23,   1.8},  //8 non-standard Orientation
				{true,    23,   1.9},  //9
				{false,   92,   2.1},  //10
				{true,    92,   2.2},  //11
				{false,   192,  2.3},  //12
				{true,    192,  2.4},  //13
				{false,   272,  2.5},  //14
				{true,    272,  2.6}   //15
			};
		const int dataCount=16;
		int failCtr=0;
		int i=0;
		for( ;i < dataCount; i++)
		{
			// Construct transform
			transform tr(0,0,
						 testData[i].xFlip,
						 testData[i].angle,
						 testData[i].mag);
			if(! testTran_(i, tr, testData[i].xFlip, testData[i].angle, testData[i].mag))
			   failCtr++;
		}

		/// set a11, a12, a21, a22 directly
		transform trR0(101,101, 1, 0, 0, 1);
		transform trR90(101,101, 0, 1, -1, 0);
		transform trR180(101,101, -1,0, 0, -1);
		transform trR270(101,101, 0, -1, 1, 0);		

		transform XtrR0(101,101, 1, 0,0, -1);
		transform XtrR90(101,101, 0, 1, 1, 0);
		transform XtrR180(101,101, -1,0, 0, 1);
		transform XtrR270(101,101, 0, -1, -1, 0);		

		if(!testTran_(i++, trR0, false, 0, 1)) failCtr++;
		if(!testTran_(i++, trR90, false, 90, 1)) failCtr++;
		if(!testTran_(i++, trR180, false, 180, 1)) failCtr++;
		if(!testTran_(i++, trR270, false, 270, 1)) failCtr++;
		if(!testTran_(i++, XtrR0,  true, 0, 1)) failCtr++;
		if(!testTran_(i++, XtrR90, true, 90, 1)) failCtr++;
		if(!testTran_(i++, XtrR180,true, 180, 1)) failCtr++;
		if(!testTran_(i++, XtrR270,true, 270, 1)) failCtr++;

		
		// Print summary
		printf("transformation::testGetAngleFlipMag() test:%s\n",
			   (failCtr >0 ? " failed":" passed"));

		return failCtr==0;
	}


};

}; // namespace base
