/*
 * 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 TRANSFORM2_H_
#define TRANSFORM2_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/constants.h"
#include "base/except.h"
#include "base/rectangle.h"
#include "base/round.h"


//
/// 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 base2
{
using namespace base;

//
/// The main transformation class.
//
class GXX_VISIBLE transform
{
//	Data
protected:
	//
	/// 2X2 coefficient submatrix.
	//
	v2pd a11_a22;
	v2pd a21_a12;
	//
	// Offset for the transform.
	//
	v2pd voffset;
	
//	Typedefs
protected:

	inline double& a11()
	{
		return *(reinterpret_cast<double*>(&a11_a22));
	}
	inline double& a21()
	{
		return *(reinterpret_cast<double*>(&a21_a12));
	}
	inline double& a12()
	{
		return *(reinterpret_cast<double*>(&a21_a12)+1);
	}
	inline double& a22()
	{
		return *(reinterpret_cast<double*>(&a11_a22)+1);
	}
	inline double& offset_x()
	{
		return *(reinterpret_cast<double*>(&voffset));
	}
	inline double& offset_y()
	{
		return *(reinterpret_cast<double*>(&voffset)+1);
	}

	inline double a11() const
	{
		return *(reinterpret_cast<const double*>(&a11_a22));
	}
	inline double a21() const
	{
		return *(reinterpret_cast<const double*>(&a21_a12));
	}
	inline double a12() const
	{
		return *(reinterpret_cast<const double*>(&a21_a12)+1);
	}
	inline double a22() const
	{
		return *(reinterpret_cast<const double*>(&a11_a22)+1);
	}
	inline double offset_x() const
	{
		return *(reinterpret_cast<const double*>(&voffset));
	}
	inline double offset_y() const
	{
		return *(reinterpret_cast<const double*>(&voffset)+1);
	}

//	Construction
public:

	//
	/// Initialize the function tables.
	//
	static void initFnTables();

	//
	/// Default constuctor.
	//
	transform()
	{
		a11() = 1.0;
		a22() = 1.0;
		a21() = 0.0;
		a12() = 0.0;
		offset_x() = 0.0;
		offset_y() = 0.0;
	}
	//
	/// Default constuctor.
	//
	transform( v2pd a1122,
				v2pd a2112, 
				v2pd o )
	{
		a11_a22 = a1122;
		a21_a12 = a2112;
		voffset = o;
	}
	//
	/// Constructor for a scale offset transform.
	//
	transform(
		double offsetX,
		double offsetY,
		double scaleX,
		double scaleY
	)
	{
		a11() = scaleX;
		a22() = scaleY;
		a21() = 0.0;
		a12() = 0.0;
		offset_x() = offsetX;
		offset_y() = offsetY;
	}
	//
	/// Constructor for a general transform.
	//
	transform(
		double offsetX, 
		double offsetY, 
		double coeff11, 
		double coeff12, 
		double coeff21,
		double coeff22
	)
	{
		a11() = coeff11;
		a22() = coeff22;
		a21() = coeff21;
		a12() = coeff12;
		offset_x() = offsetX;
		offset_y() = offsetY;
	}
	//
	/// 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
	)
	{
		a11() = cos(degreesToRadians(angle));
		a22() = a11();
		a12() = sin(degreesToRadians(angle));
		a21() = -a12();
		offset_x() = inCy * a12() - inCx * a11() + inCx;
		offset_y() = - inCy * a11() - inCx * a12() + inCy;
	}
#if 0
	//
	/// Constructor for a standard transform.
	//
	transform(
		double offsetX, 
		double offsetY,
		stdOrientEnum inOrient = R0
	)
		:
		a11(1.0),
		a12(0.0),
		a21(0.0),
		a22(1.0),
		offset(offsetX,offsetY),
		next(NULL),
		orient(inOrient),
		type(StdTransformType)
	{}
	//
	/// Constructor for a standard transform with magnification.
	//
	transform(
		double offsetX, 
		double offsetY, 
		stdOrientEnum inOrient,
		double inMag)
		:
		a11(inMag),
		a12(0.0),
		a21(0.0),
		a22(inMag),
		offset(offsetX,offsetY),
		next(NULL),
		orient(inOrient),
		type(StdTransformWithMagType)
	{}
#endif
	//
	/// 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)
	{
		register double multiplier = ( inXFlip ) ? -1.0 : 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;
		offset_x() = offsetX;
		offset_y() = offsetY;
	}
	//
	/// 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 )
	{
		a11() = xScale;
		a22() = yScale;
		a21() = 0.;
		a12() = 0.;
		offset_x() = 0.;
		offset_y() = 0.;
	}

	//
	/// Copy constructor
	//
	transform( const transform & inT)
	{
		a11_a22 = inT.a11_a22;
		a21_a12 = inT.a21_a12;
		voffset = inT.voffset;
	}
	//
	/// Assignment operator
	//
	transform & operator = (const transform &inT)
	{
		a11_a22 = inT.a11_a22;
		a21_a12 = inT.a21_a12;
		voffset = inT.voffset;
		return *this;
	}

	//
	/// Destructor
	//
	~transform()
	{
	}

public:
	//
	///	Equal operator
	//
	bool operator == (const transform &inT)
	{
//			register v2pd cmp = _mm_cmpeq_pd( vx, inT.vx );
//			register v2pd tmp = _mm_cmpeq_pd( vy, inT.vy );
//			cmp = _mm_and_pd( cmp, tmp );
//			tmp = _mm_cmpeq_pd( voffset, inT.voffset );
//			cmp = _mm_and_pd( cmp, tmp );

		return false;
	}

	//
	/// Non equal operator
	//
	bool operator != (const transform &inT)
	{
//			register v2pd cmp = _mm_cmpneq_pd( vx, inT.vx );
//			register v2pd tmp = _mm_cmpneq_pd( vy, inT.vy );
//			cmp = _mm_or_pd( cmp, tmp );
//			tmp = _mm_cmpneq_pd( voffset, inT.voffset );
//			cmp = _mm_or_pd( cmp, tmp );

		return true;
	}

#if 0
	//
	/// Test whether this is an identity transform.
	//
	bool isIdentityTransform() const
		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;
	}

	//
	/// Returns the type of the transform.
	//
	transformType getType()	const
	{
		return (transformType) type;
	}

	stdOrient getOrient()	const
	{
		return orient;
	}

	//
	/// Return the next field.
	//
	const transform *getNext() const
	{
		return next;
	}
	//
	/// Return the next field.
	//
	transform *getNext()
	{
		return next;
	}
#endif

	//
	/// Returns the offset point of the transform.
	//
	pod::point<double> getOffset()	const
	{
		return pod::point<double>( offset_x(), offset_y() );
	}
	//
	/// Returns the offset point of the transform taking into account also resolution.
	//
	pod::point<double> getOffsetRes()	const
	{
		return pod::point<double>( offset_x(), offset_y() );
	}
	//
	/// Set the offset of the transform.
	//
	void setOffset( pod::point<double> off )
	{
		voffset = off.get_pd();
	}
	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
	{
		return getXVectorMag();
	}
	//
	/// 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();
	}

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

//	Complex functions.
public:


	////////////////////////////////////////////////////////////////

	//
	/// Modify a single point.
	//
	template <class C>
	pod::point<short> modifyDblToShort(pod::point<C> p) const
	{
		pod::rectangle<short> tmp;
		modify( r, tmp );
		return tmp;
	}

	template <class C>
	pod::point<int> modifyDblToInt(pod::point<C> p) const
	{
		pod::rectangle<int> tmp;
		modify( r, tmp );
		return tmp;
	}

	template <class C>
	pod::point<double> modifyDblToDbl(pod::point<C> p) const
	{
		pod::rectangle<double> tmp;
		modify( r, tmp );
		return tmp;
	}

	template <class C>
	pod::point<short> modifyIntToShort(pod::point<C> p) const
	{
		pod::rectangle<short> tmp;
		modify( r, tmp );
		return tmp;
	}

	template <class C>
	pod::point<int> modifyIntToInt(pod::point<C> p) const
	{
		pod::rectangle<int> tmp;
		modify( r, tmp );
		return tmp;
	}

	template <class C>
	pod::point<short> modifyLongToShort(pod::point<C> p) const
	{
		pod::rectangle<short> tmp;
		modify( r, tmp );
		return tmp;
	}

	template <class C>
	pod::point<int> modifyLongToInt(pod::point<C> p) const
	{
		pod::rectangle<int> tmp;
		modify( r, tmp );
		return tmp;
	}

	template <class C>
	pod::point<long> modifyLongToLong(pod::point<C> p) const
	{
		pod::rectangle<long> tmp;
		modify( r, tmp );
		return tmp;
	}

	template<class C>
	pod::rectangle<C> operator()(const pod::rectangle<C> & r) const
	{
		return modify( r );
	}

	template<class C>
	pod::point<C> operator()(const pod::point<C> & p) const
	{
		return modify( p );
	}

	transform operator *(const transform & t2 ) const
	{
		return mult( t2 );
	}

	///////////////////////////////////////////////////////////////////////////
	
	//
	/// Multiply with another transform.
	//
	void mult(const transform &T2, transform &outT) const
	{
		//
		//	new_a11 = t1_a11*t2_a11 + t1_a12*t2_a21
		//	new_a22 = t1_a22*t2_a22 + t1_a21*t2_a12
		//
		v2pd m1 = _mm_mul_pd( a11_a22, T2.a11_a22 );
		v2pd m2 = a21_a12;
		m2 = _mm_shuffle_pd( m2, m2, 1 );
		m2 = _mm_mul_pd( m2, T2.a21_a12 );
		outT.a11_a22 = _mm_add_pd( m1, m2 );
		//
		//	new_a21 = t1_a21*t2_a11 + t1_a22*t2_a21
		//	new_a12 = t1_a12*t2_a22 + t1_a11*t2_a12
		//
		v2pd m3 = _mm_mul_pd( a21_a12, T2.a11_a22 );
		v2pd m4 = a11_a22;
		m4 = _mm_shuffle_pd( m4, m4, 1 );
		m4 = _mm_mul_pd( m4, T2.a21_a12 );
		outT.a21_a12 = _mm_add_pd( m3, m4 );
		//
		//	new_xoffset = t1_xoffset*t2_a11 + t1_yoffset*t2_a21+t2_xoffset
		//	new_yoffset = t1_yoffset*t2_a22 + t1_xoffset*t2_a12+t2_yoffset
		//
		v2pd m5 = _mm_mul_pd( voffset, T2.a11_a22 );
		v2pd m6 = voffset;
		m6 = _mm_shuffle_pd( m6, m6, 1 );
		m6 = _mm_mul_pd( voffset, T2.a21_a12 );
		
		m5 = _mm_add_pd( m5, m6 );
		outT.voffset = _mm_add_pd( m5, T2.voffset );
	}
	
	//
	/// Multiply with another transform.
	//
	transform mult(const transform &T2) const
	{
		//
		//	new_a11 = t1_a11*t2_a11 + t1_a12*t2_a21
		//	new_a22 = t1_a22*t2_a22 + t1_a21*t2_a12
		//
		v2pd m1 = _mm_mul_pd( a11_a22, T2.a11_a22 );
		v2pd m2 = a21_a12;
		m2 = _mm_shuffle_pd( m2, m2, 1 );
		m2 = _mm_mul_pd( m2, T2.a21_a12 );
		v2pd t_a11_a22 = _mm_add_pd( m1, m2 );
		//
		//	new_a21 = t1_a21*t2_a11 + t1_a22*t2_a21
		//	new_a12 = t1_a12*t2_a22 + t1_a11*t2_a12
		//
		v2pd m3 = _mm_mul_pd( a21_a12, T2.a11_a22 );
		v2pd m4 = a11_a22;
		m4 = _mm_shuffle_pd( m4, m4, 1 );
		m4 = _mm_mul_pd( m4, T2.a21_a12 );
		v2pd t_a21_a12 = _mm_add_pd( m3, m4 );
		//
		//	new_xoffset = t1_xoffset*t2_a11 + t1_yoffset*t2_a21+t2_xoffset
		//	new_yoffset = t1_yoffset*t2_a22 + t1_xoffset*t2_a12+t2_yoffset
		//
		v2pd m5 = _mm_mul_pd( voffset, T2.a11_a22 );
		v2pd m6 = voffset;
		m6 = _mm_shuffle_pd( m6, m6, 1 );
		m6 = _mm_mul_pd( voffset, T2.a21_a12 );
		
		m5 = _mm_add_pd( m5, m6 );
		v2pd t_voffset = _mm_add_pd( m5, T2.voffset );
		//
		//	Return the result
		//
		return transform( t_a11_a22, t_a21_a12, t_voffset );
	}

	//
	/// Scale the transform by the given scalar.
	//
	transform & operator *= (const double d)
	{
		v2pd m = _mm_set1_pd( d );
		a11_a22 = _mm_mul_pd( a11_a22, m );
		a21_a12 = _mm_mul_pd( a21_a12, m );
		voffset = _mm_mul_pd( voffset, m );
		return *this;
	}

	//
	/// Return the inverse transform of the transform.
	//
	transform getInverse()	const
	{
		//
		//	Get determinant.
		//
		double det = a11()*a22()-a21()*a12();
		v2pd detx2 = _mm_set1_pd( det );
		//
		//	Get inv_a11 and inv_a22
		//
		//	inv_a11 = a22/det
		//	inv_a11 = a11/det
		//
		v2pd t_a11_a22 = a11_a22;
		t_a11_a22 = _mm_shuffle_pd( t_a11_a22, t_a11_a22, 1 );
		t_a11_a22 = _mm_div_pd( t_a11_a22, detx2 );
		//
		//	inv_a21 = -a21/det
		//	inv_a12 = -a12/det
		//
		v2pd t_a21_a12 = _mm_set1_pd( 0.0 );
		t_a21_a12 = _mm_sub_pd( t_a21_a12, a21_a12 );
		t_a21_a12 = _mm_div_pd( t_a21_a12, detx2 );
		//
		//	inv_xoffset = -xoffset*inv_a11 -yoffset*inv_a21
		//	inv_yoffset = -yoffset*inv_a22 -xoffset*inv_a12
		//
		v2pd t_voffset = _mm_set1_pd( 0.0 );
		t_voffset = _mm_sub_pd( t_voffset, voffset );
		t_voffset = _mm_mul_pd( t_voffset, t_a11_a22 );
		
		v2pd r_voffset = voffset;
		r_voffset = _mm_shuffle_pd( r_voffset, r_voffset, 1 );
		r_voffset = _mm_mul_pd( r_voffset, t_a21_a12 );
		t_voffset = _mm_sub_pd( t_voffset, r_voffset );
		//
		//	Return the result.
		//
		return transform( t_a11_a22, t_a21_a12, t_voffset );
	}

	
	//
	///	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>
	__forceinline void modify( const pod::point<iC>& rSrc, pod::point<oC>& rDest ) const
	{
//		rDest = modify( rSrc );
		//
		//	x = x*a11 + y*a21
		//	y = y*a22 + x*a12
		//
		v2pd d = rSrc.get_pd();
		v2pd d_rev = d;
		d_rev = _mm_shuffle_pd( d_rev, d, 1 );
		d = _mm_mul_pd( d, a11_a22 );
		d_rev = _mm_mul_pd( d_rev, a21_a12 );
		d = _mm_add_pd( d, voffset );
		d = _mm_add_pd( d, d_rev );
		rDest.set_pd( d );
	}

	//
	///	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 C>
	__forceinline pod::point<C> modify( const pod::point<C>& rSrc ) const
	{
		//
		//	x = x*a11 + y*a21
		//	y = y*a22 + x*a12
		//
		v2pd d = rSrc.get_pd();
		v2pd d_rev = d;
		d_rev = _mm_shuffle_pd( d_rev, d, 1 );
		d = _mm_mul_pd( d, a11_a22 );
		d_rev = _mm_mul_pd( d_rev, a21_a12 );
		d = _mm_add_pd( d, voffset );
		d = _mm_add_pd( d, d_rev );
		return pod::point<C>( d );
	}

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

	//
	///	This is pod::rectangle version of above function.
	//
	template <class C>
	__forceinline pod::rectangle<C> modify( const pod::rectangle<C>& rSrc ) const
	{
		pod::rectangle<C> dest;
		modifyBBox( rSrc, dest );
		return dest;
	}

	//
	///	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>
	__forceinline void modifyBBox( const pod::rectangle<iC>& rSrc, pod::rectangle<oC>& rDest ) const
	{
		pod::point<oC> b1 = modify( rSrc.getLowerLeftRef() );
		pod::point<oC> b2 = modify( rSrc.getUpperRightRef() );

		rDest.setRect( b1, b2 );
		rDest.makeValid();

		//
		//	If we have orthogonal transform?
		//	First check get12 since most of transform will
		//	have no rotation factor.
		//
//		v2pd a1112 = _mm_shuffle_pd( a11_a22, a21_a12, 1 )
//		v2pd a1112 = _mm_shuffle_pd( a11_a22, a21_a12, 1 )
		if ( a12() != 0. && a11() != 0. )
		{
			pod::point<oC> b3 = modify( rSrc.getUpperLeft() );
			rDest.merge( b3 );
			
			pod::point<oC> b4 = modify( rSrc.getLowerRight() );
			rDest.merge( b4 );
		}
	}

	// 
	/// Convert a transform type to a string.
	//
	friend std::ostream & operator << (std::ostream & os, const transform & t);

	//
	/// Print a transform, mainly for debug purpose.
	//
	void print(FILE *outStream, const char *linePrefix) const;

}; // class transform

} // namespace base

#endif // TRANSFORM_H_
