/*
 * 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 "type-traits2.h"
#include "ssetypes.h"

#include "transform2.h"
#include "constants.h"

namespace base2
{

#if 0
	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}
			}
#endif

	//
	/// Initialize the function tables.
	//
	void transform::initFnTables()
	{
	}

	// 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 0
		if( type!=GeneralTransformType)
		{
			//
			// standard orientation. Use table
			//
			int idx= (int) getOrient().getOri();
			isXFlip= stdOrientXFlipAngValues[idx].xflip;
			angle  = stdOrientXFlipAngValues[idx].angle;
			mag    = a11;
		}
		else
#endif
		{
			// 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
	}

#if 0
	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);
		}
	}
#endif

#if 0	
	//
	/// 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);
		}
	}

#endif

//
/// Print a transform, mainly for debug purpose.
//
void transform::print(FILE *outStream, const char *linePrefix) const
{
	fprintf(outStream, "%sa11 = %f.\n", linePrefix, a11() );
	fprintf(outStream, "%sa12 = %f.\n", linePrefix, a12() );
	fprintf(outStream, "%sa21 = %f.\n", linePrefix, a21() );
	fprintf(outStream, "%sa22 = %f.\n", linePrefix, a22() );

	fprintf(outStream, "%stx = %f.\n", linePrefix, offset_x() );
	fprintf(outStream, "%sty = %f.\n", linePrefix, offset_y() );
}

std::ostream & operator << (std::ostream & os, const transform & t)
{
	os << "<transform ";
	os << ">" << std::endl;
	os << "<matrix>" << std::endl;
	os << t.getRow1();
	os << t.getRow2();
	os << "</matrix>" << std::endl;
	os << "<offset>" << std::endl;
	os << t.getOffset();
	os << "</offset>" << std::endl;
	os << "</transform>" << std::endl;
	return os;
}

}; // namespace base
