/*
 * 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.
 */

#ifndef TRANSFORM_H_
#define TRANSFORM_H_

// _MSC_VER >= 1400 means we deal with VC8 or higher.
#if defined( _MSC_VER )
/*warning C4290: C++ exception specification ignored except to indicate a function is not __declspec(nothrow)*/
#pragma warning( disable: 4290 )
#endif

#include <math.h>
#include <string>
#include <stdio.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>

using namespace base;
//
/// The transform.h file contains the different transform types.
//
/*!
 *  Usage of transforms:
 *  Transforms are applied to row vectors from the right as shown below:
 *  OV = IV * T 
 *  Here, OV and IV are the output and input row vectors and T is the transform.
 *
 *  Multiplying transforms:
 *  Suppose we have a chain of transforms.
 *  OV = IV * T1 * T2
 *  OV = IV * T
 *  Thus, T can be computed as follows:
 *  Transform T;
 *  T1.mult(T2, T);
 * 
 *	A general 2-D transform can be viewed in matrix form as follows:
 *	|a11 a12 0|
 *	|a21 a22 0|
 *	| tx  ty 1|
 *	A general transform can be viewed as a sequence of 3 steps:
 *	1- Scaling with value (Sx, Sy) (i.e. x' = Sx*x, y' = Sy*y).
 *	2- rotating with angle A.
 *	3- translating with value (tx, ty).
 *	In other words, the 2-D transform is the multiplication of the 3 transforms
 *	as follows:  
 *	|Sx  0 0| | cos(A) sin(A) 0| | 1  0 0|
 *	|0  Sy 0|*|-sin(A) cos(A) 0|*| 0  1 0|
 *	|0   0 1| |      0      0 1| |tx ty 1|
 *
 *  Using resolution transforms:
 *  In order to mimic applying resolution transforms to each cell individually,
 *  a resolution change operation is applied before anything. Suppose we call
 *  the resolution change transform RES. Also suppose the transform from the 
 *  cell to the screen is TS. Thus:
 *  OV = IV * RES * TS.
 *  In order to change the resolution of a layout, we need to insert the 
 *  resolution transform RES in the chain. Hence:
 *  1- Create a resolution transform RES(xScale, yScale, true).
 *  2- Assuming T is the total transform, T can be computed as follows:
 *     RES.mult(TS, T).
 */
namespace test
{
	//
	/// Enum for types of transforms.
	//
	enum transformType
	{
		//
		/// ScaleOffsetTransform type.
		//
		ScaleOffsetTransformType = 0,
		//
		/// GeneralTransform type.
		//
		GeneralTransformType,
		//
		/// StdTransform type.
		//
		StdTransformType,
		//
		/// StdTransformWithMag type.
		//
		StdTransformWithMagType,
		//
		/// StdTransformWithMag type.
		//
		ScaleTransformType,
		//
		/// Resolution type.
		//
		ResolutionTransformType,
		//
		/// Always keep this entry as the last enum value.
		//
		transformTypeMAX
	}; // enum transformType
	//
	/// Inverses of the standard orientations.
	//
	extern const stdOrientEnum inverseStdOrient[stdOrientMAX];
	//
	/// Coefficient matrices for the standard orientations.
	//
	extern const signed char stdOrientCoeffMatrix[stdOrientMAX][2][2];
	//
	/// Matrix holding standard orientations resulting from multiplying
	/// different standard orientations.
	//
	extern const stdOrientEnum multStdOrientMatrix[stdOrientMAX][stdOrientMAX];

	//
	/// The main transformation class.
	//
	class GXX_VISIBLE transform
	{
	protected:
		//
		/// Type of transform.
		//
		unsigned int type : 4;
		//
		/// Orientation field.
		//
		base::stdOrient orient;
		//
		/// 2X2 coefficient submatrix.
		/// a11 would be used for Sx for scale and offset transforms.
		/// It would also be used for resolution change for resolution
		/// transforms.
		/// Also, a11 would be used for magnification in the case of 
		/// standard transforms with magnification.
		/// a22 would be used for Sy for scale and offset transforms.
		/// It would also be used for resolution change for resolution
		/// transforms.
		//
		double a11, a12, a21, a22;
		//
		// Offset for the transform.
		//
		pod::point<double> offset;
		//
		/// Pointer to next transform in chain. This is used by
		/// resolution transforms.
		//
		transform *next;

		typedef pod::point<short> (*pModifyFunDblToShort)(
					const transform &T,
					pod::point<double> xy );

		typedef pod::point<short> (*pModifyFunIntToShort)(
					const transform &T,
					pod::point<int> xy );

		typedef pod::point<short> (*pModifyFunLongToShort)(
					const transform &T,
					pod::point<long> xy );

		typedef pod::point<int> (*pModifyFunDblToInt)(
					const transform &T,
					pod::point<double> xy );

		typedef pod::point<int> (*pModifyFunIntToInt)(
					const transform &T,
					pod::point<int> xy );

		typedef pod::point<int> (*pModifyFunLongToInt)(
					const transform &T,
					pod::point<long> xy );

		typedef pod::point<long> (*pModifyFunDblToLong)(
					const transform &T,
					pod::point<double> xy );

		typedef pod::point<long> (*pModifyFunIntToLong)(
					const transform &T,
					pod::point<int> xy );

		typedef pod::point<long> (*pModifyFunLongToLong)(
					const transform &T,
					pod::point<long> xy );

		typedef pod::point<double> (*pModifyFunDblToDbl)(
					const transform &T,
					pod::point<double> xy );

		typedef pod::point<double> (*pModifyFunIntToDbl)(
					const transform &T,
					pod::point<int> xy );

		typedef pod::point<double> (*pModifyFunLongToDbl)(
					const transform &T,
					pod::point<long> xy );

	private:

		//
		/// Get the inverse of a scale offset transform.
		//
		static transform getInverseScaleOffset(const transform & T);
		//
		/// Get the inverse of a general transform.
		//
		static transform getInverseGeneral(const transform & T );
		//
		/// Get the inverse of a standard transform.
		//
		static transform getInverseStd(const transform & T);
		//
		/// Get the inverse of a standard transform with mag.
		//
		static transform getInverseStdWithMag(const transform & T);
		//
		/// Get the inverse of a scale transform.
		//
		static transform getInverseScale(const transform & T);
		//
		// Get the inverse of a resolution transform.
		//
		static transform getInverseResolution(const transform & T);
		//
		/// Multiply two scale transforms.
		//
		static void multScaleXScale(const transform &T1, 
									const transform &T2,
									transform &outT);
		//
		/// Multiply a scale transform with a resolution transform.
		//
		static void multScaleXResolution(	const transform &T1,
											const transform &T2,
											transform &outT);
		//
		//
		/// Multiply two scale offset transforms.
		//
		static void multScaleOffsetXScaleOffset(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a scale offset transform with a general transform.
		//
		static void multScaleOffsetXGeneral(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a scale offset transform with a standard transform.
		//
		static void multScaleOffsetXStd(	const transform &T1, 
											const transform &T2,
											transform &outT);
		//
		/// Multiply a scale offset transform with a standard transform
		/// with magnification.
		//
		static void multScaleOffsetXStdWithMag(	const transform &T1, 
												const transform &T2,
												transform &outT);
		//
		/// Multiply a scale offset transform with a resolution transform.
		//
		static void multScaleOffsetXResolution(const transform &T1,
				const transform &T2, transform &outT);
		//
		/// Multiply a general transform with a scale offset transform.
		//
		static void multGeneralXScaleOffset(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply two general transforms.
		//
		static void multGeneralXGeneral(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a general transform with a standard transform.
		//
		static void multGeneralXStd(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a general transform with a standard transform
		/// with magnification.
		//
		static void multGeneralXStdWithMag(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a general transform with a resolution transform.
		//
		static void multGeneralXResolution(const transform &T1,
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with a scale and offset transform.
		//
		static void multStdXScaleOffset(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with a general transform.
		//
		static void multStdXGeneral(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply two standard transforms.
		//
		static void multStdXStd(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with a standard transform 
		/// with magnification.
		//
		static void multStdXStdWithMag(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with a resolution transform.
		//
		static void multStdXResolution(const transform &T1,
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with magnification with a 
		/// scale and offset transform.
		//
		static void multStdWithMagXScaleOffset(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with magnification with
		/// a general transform.
		//
		static void multStdWithMagXGeneral(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with magnification with 
		/// a standard transform.
		//
		static void multStdWithMagXStd(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply two standard transforms with magnification.
		//
		static void multStdWithMagXStdWithMag(const transform &T1, 
				const transform &T2, transform &outT);
		//
		/// Multiply a standard transform with magnification with a 
		/// resolution transform.
		//
		static void multStdWithMagXResolution(const transform &T1,
				const transform &T2, transform &outT);
		//
		/// Multiply a resolution transform with any transform.
		//
		static void multResolutionXAny(const transform &T1,
				const transform &T2, transform &outT);

	public:

		//
		/// Default constuctor.
		//
		transform()
			:
			type(StdTransformType),
			orient(R0),
			a11(1.0),
			a12(0.0),
			a21(0.0),
			a22(1.0),
			offset(0.0,0.0),
			next(NULL)
		{}
		//
		/// Constructor for a scale offset transform.
		//
		transform(
			double offsetX,
			double offsetY,
			double scaleX,
			double scaleY
		)
			:
			type(ScaleOffsetTransformType),
			a11(scaleX),
			a12(0.0),
			a21(0.0),
			a22(scaleY),
			offset(offsetX,offsetY),
			next(NULL)
		{
			double xAbs(offsetX > 0.0 ? offsetX : -offsetX);
			double yAbs(offsetY > 0.0 ? offsetY : -offsetY);
			if (xAbs < absTolerance && yAbs < absTolerance)
				type = ScaleTransformType;
		}
		//
		/// Constructor for a general transform.
		//
		transform(
			double offsetX, 
			double offsetY, 
			double coeff11, 
			double coeff12, 
			double coeff21,
			double coeff22
		)
			:
			type(GeneralTransformType),
			a11(coeff11),
			a12(coeff12),
			a21(coeff21),
			a22(coeff22),
			offset(offsetX,offsetY),
			next(NULL)
		{};
		//
		/// Constructor for a rotate transform.
		///	@param inCx X coordinate of the center of rotation. 
		///	@param inCy Y coordinate of the center of rotation. 
		///	@param angle rotation angle in degrees.
		//
		transform(
			double inCx, 
			double inCy, 
			double angle
		)
			:
			type(GeneralTransformType),
			a11(cos(degreesToRadians(angle))),
			a12(sin(degreesToRadians(angle))),
			a21(-a12),
			a22(a11),
			offset(inCy * a12 - inCx * a11 + inCx,
					- inCy * a11 - inCx * a12 + inCy),
			next(NULL)
		{}
		//
		/// Constructor for a standard transform.
		//
		transform(
			double offsetX, 
			double offsetY,
			stdOrientEnum inOrient = R0
		)
			:
			type(StdTransformType),
			orient(inOrient),
			a11(1.0),
			a12(0.0),
			a21(0.0),
			a22(1.0),
			offset(offsetX,offsetY),
			next(NULL)
		{}
		//
		/// Constructor for a standard transform with magnification.
		//
		transform(
			double offsetX, 
			double offsetY, 
			stdOrientEnum inOrient,
			double inMag)
			:
			type(StdTransformWithMagType),
			orient(inOrient),
			a11(inMag),
			a12(0.0),
			a21(0.0),
			a22(inMag),
			offset(offsetX,offsetY),
			next(NULL)
		{}
		//
		/// Advanced constructor for typical references.
		///	@param offsetX X value of translation offset.
		///	@param offsetY Y value of translation offset.
		///	@param inXFlip boolean reflecting the presence of an X flip.
		///	@param inAngle rotation angle in degrees.
		///	@param inMag magnification.
		//
		transform(
			double offsetX, 
			double offsetY, 
			bool inXFlip,
			double inAngle,
			double inMag)
			:
			offset(offsetX,offsetY),
			next(NULL)
		{
			stdOrient newOrient(inXFlip, inAngle);
			if (newOrient.getOri() != UnknownStdOrient)
			{
				orient = newOrient;
				if (inMag > 1.0 + relTolerance ||
						inMag < 1.0 - relTolerance)
				{
					a11 = inMag;
					a12 = 0.0;
					a21 = 0.0;
					a22 = inMag;
					type = StdTransformWithMagType;
				}
				else
				{
					a11 = 1.0;
					a12 = 0.0;
					a21 = 0.0;
					a22 = 1.0;
					type = StdTransformType;
				}
			}
			else
			{
				type = GeneralTransformType;
				register 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;
			}
		}
		//
		/// Scale or resolution transform.
		///	@param xScale X value of the scale factor.
		///	@param yScale Y value of the scale factor.
		///	@param rouding A true value means it is a resolution transform.
		///	A false value will result in a standard scale transform.
		//
		transform(
			double xScale,
			double yScale,
			bool rounding)
		:
			type(ScaleTransformType),
			orient(R0),
			a11(xScale),
			a12(0.0),
			a21(0.0),
			a22(yScale),
			offset(0.0, 0.0),
			next(NULL)
		{
			if (rounding) {
				double rVal(base::round(xScale));
				double diff(xScale - rVal);
				diff = diff > 0.0 ? diff : -diff;
				if (diff > absTolerance)
					type = ResolutionTransformType;
				else {
					rVal = base::round(yScale);
					diff = yScale - rVal;
					diff = diff > 0.0 ? diff : -diff;
					if (diff > absTolerance)
						type = ResolutionTransformType;
				}
			}
		}
		//
		/// Copy constructor
		//
		transform(const transform & inT)
		{
			type = inT.type;
			orient = inT.orient;
			offset = inT.offset;
			a11 = inT.a11;
			a12 = inT.a12;
			a21 = inT.a21;
			a22 = inT.a22;
			if (inT.next) {
				next = new transform(*inT.next);
			} else
				next = NULL;
		}

		//
		///	Equal operator
		//
		bool operator == (const transform &inT)
		{
			if ((inT.type == type) &&
				(inT.orient.getOri() == orient.getOri()) &&
				(inT.a11 == a11) &&
				(inT.a12 == a12) &&
				(inT.a21 == a21) &&
				(inT.a22 == a22) &&
				(inT.offset == offset))
			{
				if (next)
					if (inT.next)
						return (*next == *(inT.next));
					else
						return false;
				else
					return true; 
			}

			return false;
		}

		//
		/// Non equal operator
		//
		bool operator != (const transform &inT)
		{
			return !(*this == inT);
		}

		//
		/// Assignment operator
		//
		transform & operator = (const transform &inT)
		{
			type = inT.type;
			orient = inT.orient;
			offset = inT.offset;
			a11 = inT.a11;
			a12 = inT.a12;
			a21 = inT.a21;
			a22 = inT.a22;
			if (inT.next) {
				if (next)
					*next = *(inT.next);
				else
				next = new transform(*(inT.next));
			}
			return *this;
		}

		//
		/// Destructor
		//
		~transform()
		{
			if (next)
				delete next;
		}

		//
		/// Scale the transform by the given scalar.
		//
		transform & operator *= (const double d)
		{
			if (type != ResolutionTransformType) {
				transform tmp(d, d, false);
				transform out;
				mult(tmp, out);
				*this = out;
			} else {
				if (next)
					*next *= d;
				else
					next = new transform(d, d, false);
			}
			return *this;
		}
		//
		/// Initialize the function tables.
		//
		static void initFnTables();
		//
		/// Test whether this is an identity transform.
		//
		bool isIdentityTransform()
			throw (except::outOfRange)
		{
			const double onePlusTolerance = 1.0 + relTolerance;
			const double oneMinusTolerance = 1.0 - relTolerance;
			//
			// See if the offsets are beyond the origin.
			//
			if (offset.X() > absTolerance || offset.X() < -absTolerance ||
				offset.Y() > absTolerance || offset.Y() < -absTolerance)
				return false;
			//
			// Check by transform type.
			//
			switch((transformType) type)
			{
			case StdTransformWithMagType:
				if (a11 > onePlusTolerance || a11 < oneMinusTolerance)
					return false;

			case StdTransformType:
				if (orient.getOri() != R0)
					return false;
				break;

			case GeneralTransformType:
				if (	a12 > absTolerance || 
						a12 < -absTolerance ||
						a21 > absTolerance || 
						a21 <  -absTolerance)
					return false;

			case ScaleOffsetTransformType:
			case ScaleTransformType:
				if (	a11 > onePlusTolerance ||
						a11 < oneMinusTolerance ||
						a22 > onePlusTolerance || 
						a22 < oneMinusTolerance)
					return false;
				break;

			case ResolutionTransformType:
				return false;
				break;

			default:
				throw except::outOfRange("base::transform:isIdentityTransform: Unknown type.");
			}
			return true;
		}
		//
		/// Multiply with another transform.
		//
		void mult(const transform &T2, transform &outT) const;
		transform operator *(const transform & T) const
		{
			transform out;
			mult(T,out);
			return out;
		}
		//
		/// Print a transform, mainly for debug purpose.
		//
		void print(FILE *outStream, const char *linePrefix) const
		{
			std::string str = (std::string)(*this);
			fprintf(outStream, "%stype = %s.\n", linePrefix,
					str.c_str());
			if (getType() != ResolutionTransformType &&
					getType() != ScaleTransformType) {
				fprintf(outStream, "%stx = %f.\n", linePrefix, offset.X());
				fprintf(outStream, "%sty = %f.\n", linePrefix, offset.Y());
			}
			switch(type) {
			case ScaleOffsetTransformType:
			case ScaleTransformType:
				fprintf(outStream, "%sscaleX = %f.\n", linePrefix, a11);
				fprintf(outStream, "%sscaleY = %f.\n", linePrefix, a22);
				break;

			case GeneralTransformType:
				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);
				break;

			case StdTransformType:
				str = (std::string)orient;
				fprintf(outStream, "%sorient = %s.\n", linePrefix, str.c_str());
				break;

			case StdTransformWithMagType:
				str = (std::string)orient;
				fprintf(outStream, "%sorient = %s.\n", linePrefix, str.c_str());
				fprintf(outStream, "%smag = %f.\n", linePrefix, a11);
				break;

			case ResolutionTransformType:
				fprintf(outStream, "%sscale = %f.\n", linePrefix, a11);
				if (next) {
					std::string bfr(linePrefix);
					bfr += "    ";
					next->print(outStream, bfr.c_str());
				}
				break;

			default:
				throw except::outOfRange("base::transform:print: Unknown type.");
			} // switch
		}
		//
		/// Return the inverse transform of the transform.
		//
		transform getInverse()	const;
		//
		/// Returns the type of the transform.
		//
		transformType getType()	const
		{
			return (transformType) type;
		}
		//
		/// Returns the offset point of the transform.
		//
		pod::point<double> getOffset()	const
		{
			return offset;
		}
		//
		/// Returns the offset point of the transform taking into account also resolution.
		//
		pod::point<double> getOffsetRes()	const
		{
			if ( getType() != ResolutionTransformType )
				return offset;
			else
			{
				pod::point<double> zero( 0., 0.);
				pod::point<double> off;
				modify( zero, off );
				return off;
			}
		}
		//
		/// Set the offset of the transform.
		//
		void setOffset( pod::point<double> off )
		{
			offset = off;
		}
		stdOrient getOrient()	const
		{
			return orient;
		}
		pod::point<double> getDiagonal()	const
		{
			return pod::point<double>(a11,a22);
		}
		pod::point<double> getRow1()	const
		{
			return pod::point<double>(a11,a12);
		}
		pod::point<double> getRow2()	const
		{
			return pod::point<double>(a21,a22);
		}
		pod::point<double> getCol1()	const
		{
			return pod::point<double>(a11,a21);
		}
		pod::point<double> getCol2()	const
		{
			return pod::point<double>(a12,a22);
		}
		//
		/// Return the magnitude of the vector (1,0).
		//
		double getXVectorMag()	const
		{
			if ( a21 == 0.0 )
				return ::fabs(a11);
			else if ( a11 == 0.0 )
				return ::fabs(a21);
			else
				return ::sqrt(a11*a11 + a21*a21);
		}
		//
		/// A special function to return the X scale factor. This
		/// is a nonnegative value that reflects the scaling effect
		/// in the X direction.
		//
		double getXScale() const
		{
			if (type != ResolutionTransformType)
				return getXVectorMag();
			else
				return getXVectorMag() * (next ? next->getXScale() : 1.0);
		}
		//
		/// Returns the a11 entry of the matrix.  If the matrix is a scaling
		/// one, then this returns the X-scale factor.
		/// The result is not correct for all types of transforms.
		//
		double get11()	const
		{
			return a11;
		}
		//
		/// Return the a12 entry of the matrix.
		/// The result is only valid for general transforms.
		/// The result is not correct for all types of transforms.
		//
		double get12()	const
		{
			return a12;
		}
		//
		/// Return the a21 entry of the matrix.
		/// The result is only valid for general transforms.
		/// The result is not correct for all types of transforms.
		//
		double get21()	const
		{
			return a21;
		}
		//
		/// Returns the a22 entry of the matrix.  If the matrix is a scaling
		/// one, then this returns the Y-scale factor.
		/// The result is not correct for all types of transforms.
		//
		double get22()	const
		{
			return a22;
		}
		//
		/// Return the next field.
		//
		const transform *getNext() const
		{
			return next;
		}

		//
		/// get angle, isXFlip and mag from transformation
		//
		const void getAngleFlipMag( double& angle, bool& isXFlip, double& mag) const;

	
		//
		/// Modify a single point.
		//
		pod::point<short> modifyDblToShort(pod::point<double>) const;
		pod::point<int> modifyDblToInt(pod::point<double>) const;
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<int> modifyDblToInt(pod::point<int>) const
		{	return pod::point<int>();	}
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<int> modifyDblToInt(pod::point<long>) const
		{	return pod::point<int>();	}
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<short> modifyDblToShort(pod::point<int>) const
		{	return pod::point<short>();	}
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<short> modifyDblToShort(pod::point<long>) const
		{	return pod::point<short>();	}
		//
		/// Transform doubles to doubles.
		//
		pod::point<double> modifyDblToDbl(pod::point<double>) const;
		pod::point<double> modify(pod::point<double>) const;
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<double> modifyDblToDbl(pod::point<int>) const
		{	return pod::point<double>();	}
		//
		/// Transform ints to ints. 
		//
		pod::point<int> modifyIntToInt(pod::point<int>) const;
		pod::point<int> modify(pod::point<int>) const;
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<int> modifyIntToInt(pod::point<double>) const
		{	return pod::point<int>();	}
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<int> modifyIntToInt(pod::point<long>) const
		{	return pod::point<int>();	}

		pod::point<short> modifyIntToShort(pod::point<int>) const;
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<short> modifyIntToShort(pod::point<double>) const
		{	return pod::point<short>();	}
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<short> modifyIntToShort(pod::point<long>) const
		{	return pod::point<short>();	}

		pod::point<short> modifyLongToShort(pod::point<long>) const;
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<short> modifyLongToShort(pod::point<int>) const
		{	return pod::point<short>();	}
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<short> modifyLongToShort(pod::point<double>) const
		{	return pod::point<short>();	}
		//
		/// Transform longs to ints.
		//
		pod::point<int> modifyLongToInt(pod::point<long>) const;
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<int> modifyLongToInt(pod::point<int>) const
		{	return pod::point<int>();	}
		//
		/// Do nothing method for syntactic completeness.
		//
		pod::point<int> modifyLongToInt(pod::point<double>) const
		{	return pod::point<int>();	}
		//
		/// Transform longs to longs.
		//
		pod::point<long> modifyLongToLong(pod::point<long>) const;
		pod::point<long> modify(pod::point<long>) const;
		//
		//
		//
		pod::rectangle<double> operator()(const pod::rectangle<double> &) const;
		pod::rectangle<long> operator()(const pod::rectangle<long> &) const;
		pod::rectangle<int> operator()(const pod::rectangle<int> &) const;
		pod::point<double> operator()(const pod::point<double> &) const;
		pod::point<long> operator()(const pod::point<long> &) const;
		pod::point<int> operator()(const pod::point<int> &) const;
		
		//
		///	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 modify( const pod::point<iC>& rSrc, pod::point<oC>& rDest ) const;

		//
		///	This is pod::rectangle version of above function.
		//
		template <class iC, class oC>
		void modify( const pod::rectangle<iC>& rSrc, pod::rectangle<oC>& rDest ) const;

		//
		///	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 modifyBBox( const pod::rectangle<iC>& rSrc, pod::rectangle<oC>& rDest ) const;

		// 
		/// Convert a transform type to a string.
		//
		operator std::string()	const
		{
			switch(type) {
			case ScaleOffsetTransformType:
				return std::string("ScaleOffsetTransform");
				break;
			case GeneralTransformType:
				return std::string("GeneralTransform");
				break;
			case StdTransformType:
				return std::string("StdTransform");
				break;
			case StdTransformWithMagType:
				return std::string("StdTransformWithMag");
				break;
			case ScaleTransformType:
				return std::string("ScaleTransform");
				break;
			case ResolutionTransformType:
				return std::string("ResolutionTransform");
				break;
			default:
				throw except::outOfRange("base::transformTypeToString: Unknown type.");
			} // switch
		}

		friend std::ostream & operator << (std::ostream & os, const transform & t)
		{
			os << "<transform ";
			os << "type=\"" << (std::string)t << "\"";
			if ( t.type == StdTransformType || t.type == StdTransformWithMagType )
				os << " orientation=\"" << (std::string)t.getOrient() << "\"";
			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;
		}

	}; // class transform

namespace transformTest
{

	/// API test getAngelFlipmag
	bool testGetAngleFlipMag();
};

} // namespace base

#endif // TRANSFORM_H_
