/*
								+----------------------------------+
								|                                  |
								|       ***  Vector math  ***      |
								|                                  |
								|   Copyright © -tHE SWINe- 2005   |
								|                                  |
								|             Vector.h             |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __VECTOR2_INCLUDED
#define __VECTOR2_INCLUDED

/**
 *	@file Vector.h
 *	@author -tHE SWINe-
 *	@date 2005
 *	@brief vector math primitives
 *
 *	@date 2006-05-16
 *
 *	passed code revision
 *	optimized some parts of code
 *	all integers storing just true of false were replaced with bool
 *
 *	@date 2006-07-02
 *
 *	passed code revision
 *	added swizzle operators to vector classes
 *	added redlect / refract / fresnel functions to Vector3<T> class
 *	renamed Vector3<T>::Len to Vector3<T>::f_Length, Vector3<T>::Len2 to Vector3<T>::f_Length2
 *	added some Matrix3f methods, renamed Matrix3f::Init() to Matrix3f::Identity()
 *	renamed Planef to Plane3f
 *	renamed Pi to f_pi
 *	general cleanup of code
 *
 *	@date 2006-07-17
 *
 *	renamed CPolygon::r_Vertex(int) to CPolygon::r_t_Vertex(int)
 *	renamed CPolygon::r_Normal() to CPolygon::r_t_Normal()
 *
 *	@date 2006-07-20
 *
 *	added CPolygon::b_MT_RayHit_CullFrontfaces and CPolygon::b_MT_RayHit_CullBackFaces
 *
 *	@date 2006-07-24
 *
 *	renamed TVertexStruct to TVertex3f
 *	added class CVectorMath with ray distances and bounding box / line (ray) intersection tests
 *
 *	@date 2007-01-23
 *
 *	fixed std::vector handling in template CPolygon<TVertex3f> (not-enough-memory checks)
 *	remade TVertex3f base class and CPolygon<TVertex3f> a bit (added + and * operators,
 *	renamed Lerp() to t_Lerp())
 *
 *	@date 2007-03-07
 *
 *	added complete swizzling functions set to vectors
 *
 *	@date 2007-06-04
 *
 *	added missing const keyword to input swizzle functions
 *
 *	@date 2007-08-03
 *
 *	added quaternion class with basic quaternion operations
 *
 *	@date 2007-09-17
 *
 *	fixed swizzle*_in macros
 *
 *	@date 2007-10-27
 *
 *	added convenience vector / constant operators and component-wise vector * vector
 *	and vector / vector operators to all vector templates
 *	added Vector4f Matrix4f::operator *(const Vector4f &) function for natural matrix-vertex
 *	multiplication as alternative to v_Transform_Pos() and v_Transform_Dir()
 *
 *	@date 2007-11-26
 *
 *	added Matrix4f::f_Subdet, Matrix4f::f_Determinant, Matrix4f::FullInvert and
 *	Matrix4f::t_FullInverse
 *	added convenience vector + constant and vector - constant operators to all vector templates
 *
 *	@date 2008-05-19
 *
 *	fixed typos in the word 'environment'
 *
 *	@date 2008-08-21
 *
 *	fixed Matrix4f::Scale(), added non-Vector3f variants of Matrix4f::Scale(),
 *	Matrix4f::Translate() and Matrix4f::Rotate()
 *
 *	@date 2008-08-23
 *
 *	documented Matrix4f class, added functions for generating transformation
 *	matrices (not applying transformation on the matrix), namely Matrix4f::Translation(),
 *	Matrix4f::Scaling(), Matrix4f::RotationX(), Matrix4f::RotationY(), Matrix4f::RotationZ(),
 *	and Matrix4f::Rotation(). added component-wise matrix by scalar multiplication (overloads
 *	of Matrix4f::operator *() and Matrix4f::operator *=()) and finaly added transposition
 *	function Matrix4f::Transposition()
 *
 *	@date 2009-05-04
 *
 *	fixed mixed windows / linux line endings
 *
 *	@date 2009-05-12
 *
 *	fixed out-of-bounds index in TObjectOrientedBox::b_Intersect_Ray()
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@date 2009-12-18
 *
 *	removed Plane3f::v_Intersect_Ray(), Plane3f::f_Intersect_Ray_t() in favor of
 *	Plane3f::Intersect_Ray(), Plane3f::Intersect_Ray_t(), the new functions do not throw
 *	exceptions, which is considered better solution here.
 *
 *	added Matrix4f::ProductOf(), Matrix4f::FastInverseTo(), Matrix4f::FullInverseTo()
 *	and Matrix4f::TransposeTo(), which are intended to reduce unnecessary matrix copying
 *	when using overloaded operators (overloaded operators remains unchanged)
 *
 *	renamed Matrix4f::Invert(), Matrix4f::t_Inverse() to Matrix4f::FastInvert(),
 *	Matrix4f::t_FastInverse() respectively
 *
 *	renamed Quat4f::q_*() to Quat4f::t_*() to satisfy naming convention (t_ for struct)
 *
 *	added connecting constructors for vectors, meaning it's possible to write
 *	<tt>Vector3f(Vector2f(1, 2), float(3))</tt> with the same effect, as <tt>Vector3f(1, 2, 3)</tt>.
 *	all combinations are possible.
 *
 *	@date 2011-06-09
 *
 *	Converted Plane3f to template Plane3<T>, added Plane3::Intersect() for two and three-plane
 *	intersections, added Plane3::b_Colinear(), Plane3::Build_Frustum(),
 *	Plane3::Build_SymmetricFrustum() and Plane3::t_From_Triangle().
 *
 *	Added Matrix4f::t_Transform_Plane() and Matrix4f::Get_FrustumPlanes().
 *
 *	Improved element indexing in Vector*<T> using array in an union.
 *
 *	Written some documentation comments.
 *
 *	@date 2011-01-22
 *
 *	Changed functions, returning *this to return a reference to the object instead of a copy.
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 *	@date 2013-03-16
 *
 *	Added __PLANE3_LEGACY_CONSTRUCTORS to disable the confusing constructor and added
 *	plane::from_* tags to specify the order and semantics of parameters at invokation.
 *
 *	Added __VECTOR_PASS_BY_REFERENCE and __VECTOR_RETURN_BY_REFERENCE, but those proved
 *	not optimizable, at least in Visual Studio 2008 (should be faster on g++ though).
 *
 *	Added CBaseVectorImpl, containing implementation of common vector function.
 *	Added documentation for those.
 *
 *	Separated matrix storage and basic implementation as individual templates.
 *	Added Matrix2 and Matrix3 templates. All of that is mostly untested, need to test it.
 *
 *	@date 2013-07-11
 *
 *	Unified order of matrix parameters to be (column, row) everywhere. Should not
 *	affect any code, as all the matrices are square at this point.
 *
 *	Added elementwise product and ratio and elementwise operations
 *	with right-hand-side scalar to matrices.
 *
 *	@date 2014-02-21
 *
 *	Improved the quaternion template, fixed some bugs in it, added documentation comments.
 *
 *	@date 2014-03-08
 *
 *	Fixed Matrix4f::Scaling(float), which scaled the homogenous coordinate
 *	as well, thus producing no scaling after dehomogenization.
 *
 *	Attempt to debug intellisense in Visual Studio 2008.
 *
 *	@date 2014-04-04
 *
 *	Fixed intellisense in Visual Studio 2008 (bugged by the UNUSED keyword; added
 *	VECTOR2_CONSTRUCTOR_TAG to handle it).
 *
 *	Unified matrix inverse implementations in CMatrixInverseImpl. Found a number of bugs:
 *		- Matrix2::f_Subdet() was transpose, therefore the inverse was also transposed (affects code
 *		  written after 2013-03-16)
 *		- Matrix4::f_Determinant() was returning value with the opposite sign (affects code written
 *		  after 2007-11-26)
 *		- Matrix4::FastInvert() was actually calculating only inverse, not inverse transpose as
 *		  referred in some source codes, but the documentation was correct (not mentioning the
 *		  transpose). No code was changed, the metod was only renamed to Matrix4::Fast_Invert_Inplace().
 *		- Matrix4::FullInvert() was calculating only inverse, not inverse transpose. This was fixed
 *		  and the method was renamed to Matrix4::Invert_Inplace(). Consequently,
 *		  Matrix4::FullInverseOf() was renamed to Matrix4::Inverse_Of(), Matrix4::FullInverseTo()
 *		  was renamed to Matrix4::Inverse_To() and Matrix4::t_FullInverse() was renamed to
 *		  Matrix4::t_Inverse() (affects code written after 2007-11-26).
 *		- Matrix4::FullInvertNoTranspose() was calculating inverse transpose, not only transpose.
 *		  The fixed method was renamed to Matrix4::InvertTranspose_Inplace(). Consequently,
 *		  Matrix4::FullInverseNoTransposeOf() was renamed to Matrix4::InverseTranspose_Of(),
 *		  Matrix4::FullInverseNoTransposeTo() was renamed to Matrix4::InverseTranspose_To() and
 *		  Matrix4::t_FullInverseNoTranspose() was renamed to Matrix4::t_InverseTranspose()
 *		  (affects code written after 2007-11-26).
 *
 *	Most of the code, using these fucntions was already "tweaked" to work with their flaws.
 *	Therefore, change FullInvert() to -Invert_Inplace(), or FullInvertNoTranspose() to
 *	-InvertTranspose_Inplace(). Yikes. All method names were changed to force software fixes,
 *	except t_Inverse() which was used in the past.
 *
 *	Added CMatrixInverseImpl that contains common matrix inversion code for matrices
 *	of all sizes.
 *
 *	Renamed CMatrixBase::ProductOf() to CMatrixBase::Product_Of(),
 *	CMatrixBase::TransposeOf() to CMatrixBase::Transpose_Of() and
 *	CMatrixBase::TransposeTo() to CMatrixBase::Transpose_To() in order
 *	to have the same naming conventions as the inverse functions.
 *
 *	@date 2014-06-12
 *
 *	Fixed quaternion-matrix conversions (both 3x3 and 4x4) to match transformations
 *	obtained by chaining matrices (they were both transposed).
 *
 *	Fixed an error in Quat::v_Transform() that rotated the vector in the opposite
 *	direction than the corresponding rotation matrix generated by Quat::t_ToMatrix()
 *	or by Quat::t_ToMatrix4().
 *
 *	Fixed an error in Quat::Align() that now performed the alignment in the opposite direction.
 *
 */

#include "NewFix.h"
#include "CallStack.h"
#include <math.h>
#include <vector>
#include "Unused.h"
#include "MinMax.h"

#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(for) && _MSC_VER <= 1200
#define for if(0) {} else for
#endif // _MSC_VER && !__MWERKS__ && !for && _MSC_VER <= 1200
// msvc 'for' scoping hack

#if defined(_MSC_VER) && !defined(__MWERKS__)
#define VECTOR2_CONSTRUCTOR_TAG t_tag
#define _UNUSED(x) x
#else // _MSC_VER) && !__MWERKS__
#define VECTOR2_CONSTRUCTOR_TAG UNUSED(t_tag)
#define _UNUSED(x) UNUSED(x)
#endif // _MSC_VER) && !__MWERKS__
// UNUSED breaks intellisense, has to be disabled for constructor tags

extern const double f_pi; /**< @brief Pi, equals 3.14159265358979323846264338327950288419716939937510 */
extern const float f_epsilon; /**< @brief small number, equals 0.001f */
extern const float f_edge_epsilon; /**< @brief somewhat larger epsilon, used to compare quantities in space, equals 0.05f */

/**
 *	@def __VECTOR_PASS_BY_REFERENCE
 *	@brief if defined, constant arguments of vector types are passed by reference (otherwise by value)
 *	@note Passing by value seems to be slower on Visual Studio 2008, both with
 *		and without LTCG, any suitable inline, intrinsics and function-level linking.
 */
#define __VECTOR_PASS_BY_REFERENCE

/**
 *	@def __VECTOR_RETURN_BY_REFERENCE
 *	@brief if defined, constant values of vector types (such as results of arithmetic operations)
 *		are returned as a reference (otherwise as a value)
 *	@note Passing by value seems to be slower on Visual Studio 2008, both with
 *		and without LTCG, any suitable inline, intrinsics and function-level linking.
 */
#define __VECTOR_RETURN_BY_REFERENCE

//#define __VECTOR_H_WANT_CPOLYGON
//#define __VECTOR_H_WANT_CVECTORMATH
// those are disabled by default

#if 0
#ifdef __VECTOR_PASS_BY_REFERENCE

/**
 *	@brief template to make constant argument type
 *	@tparam _Ty is the argument type
 */
template <class _Ty>
class CConstArgType {
public:
	typedef const _Ty &_TyResult; /**< @brief the result type */
};

#else // __VECTOR_PASS_BY_REFERENCE

/**
 *	@brief template to make constant argument type
 *	@tparam _Ty is the argument type
 */
template <class _Ty>
class CConstArgType {
public:
	typedef const _Ty _TyResult; /**< @brief the result type */
};

#endif // __VECTOR_PASS_BY_REFERENCE

#ifdef __VECTOR_RETURN_BY_REFERENCE

/**
 *	@brief template to make constant result type
 *	@tparam _Ty is the result type
 */
template <class _Ty>
class CConstResultType {
public:
	typedef const _Ty &_TyResult; /**< @brief the result type */
};

/**
 *	@brief template to make non-constant result type (used with "return *this;")
 *	@tparam _Ty is the result type
 */
template <class _Ty>
class CNonConstResultType {
public:
	typedef _Ty &_TyResult; /**< @brief the result type */
};

#else // __VECTOR_RETURN_BY_REFERENCE

/**
 *	@brief template to make constant result type
 *	@tparam _Ty is the result type
 */
template <class _Ty>
class CConstResultType {
public:
	typedef const _Ty _TyResult; /**< @brief the result type */
};

/**
 *	@brief template to make non-constant result type (used with "return *this;")
 *	@tparam _Ty is the result type
 */
template <class _Ty>
class CNonConstResultType {
public:
	typedef _Ty _TyResult; /**< @brief the result type */
};

#endif // __VECTOR_RETURN_BY_REFERENCE

/**
 *	@brief template to make write-able result type
 *	@tparam _Ty is the result type
 */
template <class _Ty>
class CWritableResultType {
public:
	typedef _Ty &_TyResult; /**< @brief the result type */
};
#endif // 0

/**
 *	@def ConstArgType
 *	@param[in] _Ty is the argument type
 *	@brief macro to make constant argument type
 */
/**
 *	@def ConstResultType
 *	@param[in] _Ty is the result type
 *	@brief macro to make constant result type
 */
/**
 *	@def NonConstResultType
 *	@param[in] _Ty is the result type
 *	@brief macro to make non-constant result type (used with "return *this;")
 */
/**
 *	@def WritableResultType
 *	@param[in] _Ty is the result type
 *	@brief macro to make write-able result type
 */
/**
 *	@def TConstArgType
 *	@param[in] _Ty is the argument type
 *	@brief macro to make constant argument type, prefixed with "typename"
 */
/**
 *	@def TConstResultType
 *	@param[in] _Ty is the result type
 *	@brief macro to make constant result type, prefixed with "typename"
 */
/**
 *	@def TNonConstResultType
 *	@param[in] _Ty is the result type
 *	@brief macro to make non-constant result type (used with "return *this;"), prefixed with "typename"
 */
/**
 *	@def TWritableResultType
 *	@param[in] _Ty is the result type
 *	@brief macro to make write-able result type, prefixed with "typename"
 */
#ifdef __VECTOR_PASS_BY_REFERENCE
#define ConstArgType(_Ty) const _Ty&
#else // __VECTOR_PASS_BY_REFERENCE
#define ConstArgType(_Ty) const _Ty
#endif // __VECTOR_PASS_BY_REFERENCE
#ifdef __VECTOR_RETURN_BY_REFERENCE
#define ConstResultType(_Ty) const _Ty&
#define NonConstResultType(_Ty) _Ty&
#else // __VECTOR_RETURN_BY_REFERENCE
#define ConstResultType(_Ty) const _Ty
#define NonConstResultType(_Ty) _Ty
#endif // __VECTOR_RETURN_BY_REFERENCE
#define WritableResultType(_Ty) _Ty&
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER < 1400
#define TConstArgType(_Ty) ConstArgType(_Ty)
#define TConstResultType(_Ty) ConstResultType(_Ty)
#define TNonConstResultType(_Ty) NonConstResultType(_Ty)
#define TWritableResultType(_Ty) WritableResultType(_Ty)
// a less nice solution for old MSVC versions
#else // _MSC_VER && !__MWERKS__ && _MSC_VER < 1400
/*#define ConstArgType(_Ty) CConstArgType<_Ty>::_TyResult
#define ConstResultType(_Ty) CConstResultType<_Ty>::_TyResult
#define NonConstResultType(_Ty) CNonConstResultType<_Ty>::_TyResult
#define WritableResultType(_Ty) CWritableResultType<_Ty>::_TyResult*/
#define TConstArgType(_Ty) typename ConstArgType(_Ty)
#define TConstResultType(_Ty) typename ConstResultType(_Ty)
#define TNonConstResultType(_Ty) typename NonConstResultType(_Ty)
#define TWritableResultType(_Ty) typename WritableResultType(_Ty)
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER < 1400

/*
 *								=== CBaseVectorImpl ===
 */

// todo - there is a better way to do the swizzles, can do expression template-like evaluation:
/*class CSwizzle3_Base {
public:
	struct Txyz { inline operator Vector3f() { return *((Vector3f*)this); } }; // can also overload =, +=, -=, *=, ... for write-access // assumes that CSwizzle3_Base begins at the same address as the Vector3f that inherits from it, assumes that CSwizzle3_Base::xyz begins at the same address as CSwizzle3_Base itself (this should be written in standard somewhere, probably easy to do a runtime chack for that)

public:
	Txyz xyz; // accessing it enables conversion to Vector3f. doesn't have dot operator though ...
};*/
// note that the swizzles might be the thing that breaks intellisense

/**
 *	@brief base euclidean vector implementation
 *
 *	@tparam _Ty is scalar data type
 *	@tparam n_dim is dimension of the vector (must be greater than 0)
 *	@tparam StorageBase is name of the storage class
 *	@tparam Derived is name of the final derived class (containing constructors and special functions)
 */
template <class _Ty, const int n_dim, class StorageBase, class Derived>
class CBaseVectorImpl : public StorageBase {
public:
	/**
	 *	@brief vector parameters, stored as enum
	 */
	enum {
		n_row_num = n_dim, /**< @brief number of vector rows (the dimension fo the vector) */
		n_column_num = 1 /**< @brief number of vector columns (always 1) */
	};

	typedef _Ty _TyScalar; /**< @brief scalar data type */

public:
	/**
	 *	@brief sets every element of the vector to zero
	 */
	void SetZero()
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] = 0;
	}

	/**
	 *	@brief sets every element of the vector to a constant value
	 *	@param[in] f_const is the value to set the elements to
	 */
	void SetConst(_TyScalar f_const)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] = f_const;
	}

	/**
	 *	@brief element accessing operator
	 *	@param[in] n_index is zero-based index of the element to be accessed
	 *	@return Returns value of the selected vector element.
	 */
	inline TConstResultType(_TyScalar) operator [](size_t n_index) const
	{
		_ASSERTE(n_index < n_dim);
		return StorageBase::p_elem[n_index];
	}

	/**
	 *	@brief element accessing operator
	 *	@param[in] n_index is zero-based index of the element to be accessed
	 *	@return Returns reference to the selected vector element.
	 */
	inline TWritableResultType(_TyScalar) operator [](size_t n_index)
	{
		_ASSERTE(n_index < n_dim);
		return StorageBase::p_elem[n_index];
	}

	/**
	 *	@brief gets squared norm of the vector
	 *	@return Returns squared norm of the vector.
	 */
	inline _TyScalar f_Length2() const
	{
		_ASSERTE(n_dim > 0);
		_TyScalar f = StorageBase::p_elem[0] * StorageBase::p_elem[0];
		for(int i = 1; i < n_dim; ++ i) // unroll
			f += StorageBase::p_elem[i] * StorageBase::p_elem[i];
		return f;
	}

	/**
	 *	@brief gets length of the vector
	 *	@return Returns length of the vector.
	 */
	inline _TyScalar f_Length() const
	{
		return _TyScalar(sqrt(double(f_Length2())));
	}

	/**
	 *	@brief calculates dot product of two vectors
	 *	@param[in] v_vec is the other vector
	 *	@return Returns dot product of the two vectors.
	 */
	inline _TyScalar f_Dot(TConstArgType(Derived) v_vec) const
	{
		_ASSERTE(n_dim > 0);
		_TyScalar f = StorageBase::p_elem[0] * v_vec[0];
		for(int i = 1; i < n_dim; ++ i) // unroll
			f += StorageBase::p_elem[i] * v_vec[i];
		return f;
	}

	/**
	 *	@brief normalizes the vector to unit length inplace
	 *	@note If the vector is null, it remains null.
	 */
	inline void Normalize()
	{
		_TyScalar t = f_Length();
		*this /= t;
	}

	/**
	 *	@brief normalizes the vector to a specified length inplace
	 *	@param[in] t_len is the desired length of the vector
	 *	@note If the vector is null, it remains null.
	 */
	inline void Normalize(_TyScalar t_len)
	{
		_TyScalar t = f_Length();
		if(t != 0)
			*this *= t_len / t;
	}

	/**
	 *	@brief normalizes the vector to unit length
	 *	@return Returns the normalized vector.
	 *	@note If the vector is null, it remains null.
	 */
	inline Derived v_Normalized() const
	{
		_TyScalar t = f_Length();
		if(t != 0)
			return *this / t;
		else {
			Derived zero;
			zero.SetZero();
			return zero;
		}
	}

	/**
	 *	@brief normalizes the vector to a specified length
	 *	@param[in] t_len is the desired length of the vector
	 *	@return Returns the normalized vector.
	 *	@note If the vector is null, it remains null.
	 */
	inline Derived v_Normalized(_TyScalar t_len) const
	{
		_TyScalar t = f_Length();
		if(t != 0)
			return *this * (t_len / t);
		else {
			Derived zero;
			zero.SetZero();
			return zero;
		}
	}

	/**
	 *	@brief unary minus operator
	 *	@return Returns the negative vector.
	 */
	inline Derived operator -() const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = -StorageBase::p_elem[i];
		return v_result;
	}

	/**
	 *	@brief adds scalar to every element of the vector
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the vector with the scalar added.
	 */
	inline Derived operator +(_TyScalar f_scalar) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] + f_scalar;
		return v_result;
	}

	/**
	 *	@brief subtracts scalar from every element of the vector
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the vector with the scalar subtracted.
	 */
	inline Derived operator -(_TyScalar f_scalar) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] - f_scalar;
		return v_result;
	}

	/**
	 *	@brief multiplies every element of the vector by a scalar
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the elementwise product of the vector and the scalar.
	 */
	inline Derived operator *(_TyScalar f_scalar) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] * f_scalar;
		return v_result;
	}

	/**
	 *	@brief divides every element of the vector by a scalar
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the elementwise division of the vector by the scalar.
	 */
	inline Derived operator /(_TyScalar f_scalar) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] / f_scalar;
		return v_result;
	}

	/**
	 *	@brief adds scalar to every element of the vector (inplace)
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator +=(_TyScalar f_scalar)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] += f_scalar;
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief subtracts scalar from every element of the vector (inplace)
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator -=(_TyScalar f_scalar)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] -= f_scalar;
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief multiplies every element of the vector by a scalar (inplace)
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator *=(_TyScalar f_scalar)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] *= f_scalar;
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief divides every element of the vector by a scalar (inplace)
	 *	@param[in] f_scalar is the value of the scalar
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator /=(_TyScalar f_scalar)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] /= f_scalar;
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief adds two vectors
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the sum of the vectors.
	 */
	inline Derived operator +(TConstArgType(Derived) v_vec) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] + v_vec[i];
		return v_result;
	}

	/**
	 *	@brief subtracts two vectors
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the difference of the vectors.
	 */
	inline Derived operator -(TConstArgType(Derived) v_vec) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] - v_vec[i];
		return v_result;
	}

	/**
	 *	@brief multiplies two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the elementwise product of the vectors.
	 */
	inline Derived operator *(TConstArgType(Derived) v_vec) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] * v_vec[i];
		return v_result;
	}

	/**
	 *	@brief divides two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the elementwise quotient of the vectors.
	 */
	inline Derived operator /(TConstArgType(Derived) v_vec) const
	{
		Derived v_result;
		for(int i = 0; i < n_dim; ++ i) // unroll
			v_result[i] = StorageBase::p_elem[i] / v_vec[i];
		return v_result;
	}

	/**
	 *	@brief adds two vectors inplace
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator +=(TConstArgType(Derived) v_vec)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] += v_vec[i];
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief subtracts two vectors inplace
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator -=(TConstArgType(Derived) v_vec)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] -= v_vec[i];
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief multiplies two vectors inplace in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator *=(TConstArgType(Derived) v_vec)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] *= v_vec[i];
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief divides two vectors inplace in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns the new value of this vector.
	 */
	inline TNonConstResultType(Derived) operator /=(TConstArgType(Derived) v_vec)
	{
		for(int i = 0; i < n_dim; ++ i) // unroll
			StorageBase::p_elem[i] /= v_vec[i];
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief compares two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns true if all the corresponding components
	 *		of the vectors are equal, otherwise returns false.
	 */
	inline bool operator ==(TConstArgType(Derived) v_vec) const
	{
		for(int i = 0; i < n_dim; ++ i) { // unroll
			if(StorageBase::p_elem[i] != v_vec[i])
				return false;
		}
		return true;
	}

	/**
	 *	@brief compares two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns true if all the corresponding components
	 *		of the vectors are not equal, otherwise returns false.
	 */
	inline bool operator !=(TConstArgType(Derived) v_vec) const
	{
		return !(*this == v_vec);
	}

	/**
	 *	@brief compares two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns true if all the elements of this vector are smaller than
	 *		the corresponding elements of the other vector, otherwise returns false.
	 */
	inline bool operator <(TConstArgType(Derived) v_vec) const
	{
		for(int i = 0; i < n_dim; ++ i) { // unroll
			if(StorageBase::p_elem[i] >= v_vec[i])
				return false;
		}
		return true;
	}

	/**
	 *	@brief compares two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns true if all the elements of this vector are greater than
	 *		the corresponding elements of the other vector, otherwise returns false.
	 */
	inline bool operator >(TConstArgType(Derived) v_vec) const
	{
		return v_vec < *this;
	}

	/**
	 *	@brief compares two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns true if all the elements of this vector are greater than or equal to
	 *		the corresponding elements of the other vector, otherwise returns false.
	 */
	inline bool operator >=(TConstArgType(Derived) v_vec) const
	{
		return !(*this < v_vec);
	}

	/**
	 *	@brief compares two vectors in elementwise fashion
	 *	@param[in] v_vec is the other vector
	 *	@return Returns true if all the elements of this vector are smaller than or equal to
	 *		the corresponding elements of the other vector, otherwise returns false.
	 */
	inline bool operator <=(TConstArgType(Derived) v_vec) const
	{
		return !(*this > v_vec);
	}
};

/*
 *								=== ~CBaseVectorImpl ===
 */

/*
 *								=== Vector2 ===
 */

#define define_swizzle2_in(a,b,name) inline void name(TConstArgType(Vector2<_TyScalar>) r_v_vec) \
	{ \
		_TyStorage::a = r_v_vec.x; \
		_TyStorage::b = r_v_vec.y; \
	}
#define define_swizzle3_in(a,b,c,name) inline void name(TConstArgType(Vector3<_TyScalar>) r_v_vec) \
	{ \
		_TyStorage::a = r_v_vec.x; \
		_TyStorage::b = r_v_vec.y; \
		_TyStorage::c = r_v_vec.z; \
	}
#define define_swizzle4_in(a,b,c,d,name) inline void name(TConstArgType(Vector4<_TyScalar>) r_v_vec) \
	{ \
		_TyStorage::a = r_v_vec.x; \
		_TyStorage::b = r_v_vec.y; \
		_TyStorage::c = r_v_vec.z; \
		_TyStorage::d = r_v_vec.w; \
	}
#define define_swizzle2_out(a,b,name) inline Vector2<_TyScalar> name() const \
	{ \
		return Vector2<_TyScalar>(_TyStorage::a, _TyStorage::b); \
	}
#define define_swizzle3_out(a,b,c,name) inline Vector3<_TyScalar> name() const \
	{ \
		return Vector3<_TyScalar>(_TyStorage::a, _TyStorage::b, _TyStorage::c); \
	}
#define define_swizzle4_out(a,b,c,d,name) inline Vector4<_TyScalar> name() const \
	{ \
		return Vector4<_TyScalar>(_TyStorage::a, _TyStorage::b, _TyStorage::c, _TyStorage::d); \
	}

/**
 *	@brief two-dimensional euclidean vector storage with named components
 */
template <class _Ty>
struct Vector2Storage {
	union {
		struct {
			_Ty x, y;
		};
		_Ty p_elem[2];
	};
};

/**
 *	@brief two-dimensional euclidean vector
 */
template <class _Ty>
struct Vector2 : public CBaseVectorImpl<_Ty, 2, Vector2Storage<_Ty>, Vector2<_Ty> > {
	typedef Vector2Storage<_Ty> _TyStorage;
	typedef _Ty _TyScalar;

	inline Vector2()
	{}

	inline Vector2(_Ty t_x, _Ty t_y)
	{
		_TyStorage::x = t_x;
		_TyStorage::y = t_y;
	}

	inline Vector2<_Ty> v_Orthogonal() const
	{
		return Vector2<_Ty>(-_TyStorage::y, _TyStorage::x);
	}

	define_swizzle2_out(x, x, v_xx)
	define_swizzle2_out(x, y, v_xy)
	define_swizzle2_out(y, x, v_yx)
	define_swizzle2_out(y, y, v_yy)
};

/**
 *	@brief two-dimensional euclidean floating-point vector
 */
typedef Vector2<float> Vector2f;

/**
 *	@brief two-dimensional euclidean souble-precision floating-point vector
 */
typedef Vector2<double> Vector2d;

/**
 *	@brief two-dimensional euclidean integer vector
 */
typedef Vector2<int> Vector2i;

/*
 *								=== ~Vector2 ===
 */

/*
 *								=== Vector3 ===
 */

/**
 *	@brief three-dimensional euclidean vector storage with named components
 */
template <class _Ty>
struct Vector3Storage {
	union {
		struct {
			_Ty x, y, z;
		};
		_Ty p_elem[3];
	};
};

/**
 *	@brief three-dimensional euclidean vector
 */
template <class _Ty>
struct Vector3 : public CBaseVectorImpl<_Ty, 3, Vector3Storage<_Ty>, Vector3<_Ty> > {
	typedef Vector3Storage<_Ty> _TyStorage;
	typedef _Ty _TyScalar;

	inline Vector3()
	{}

	inline Vector3(_Ty t_x, _Ty t_y, _Ty t_z)
	{
		_TyStorage::x = t_x;
		_TyStorage::y = t_y;
		_TyStorage::z = t_z;
	}

	inline Vector3(TConstArgType(Vector2<_Ty>) r_v_vec, _Ty t_z)
	{
		_TyStorage::x = r_v_vec.x;
		_TyStorage::y = r_v_vec.y;
		_TyStorage::z = t_z;
	}

	inline Vector3(_Ty t_x, TConstArgType(Vector2<_Ty>) r_v_vec)
	{
		_TyStorage::x = t_x;
		_TyStorage::y = r_v_vec.x;
		_TyStorage::z = r_v_vec.y;
	}

	inline Vector3<_Ty> v_Cross(TConstArgType(Vector3<_Ty>) r_v_vec) const // cross product
	{
		return Vector3<_Ty>(r_v_vec.y * _TyStorage::z - r_v_vec.z * _TyStorage::y,
			r_v_vec.z * _TyStorage::x - r_v_vec.x * _TyStorage::z, r_v_vec.x * _TyStorage::y - r_v_vec.y * _TyStorage::x);
	}

	inline TNonConstResultType(Vector3<_Ty>) Cross(TConstArgType(Vector3<_Ty>) r_v_vec) // cross product
	{
		_Ty tx, ty;

		tx = r_v_vec.y * _TyStorage::z - r_v_vec.z * _TyStorage::y;
		ty = r_v_vec.z * _TyStorage::x - r_v_vec.x * _TyStorage::z;
		_TyStorage::z = r_v_vec.x * _TyStorage::y - r_v_vec.y * _TyStorage::x;
		_TyStorage::x = tx;
		_TyStorage::y = ty;
		return *this;
	}

	float f_TE(TConstArgType(Vector3<_Ty>) r_v_normal, TConstArgType(Vector3<_Ty>) r_v_transmission,
		float f_n1_to_mi1, float f_n2_to_mi2) const
	{
		/*
		Fresnel law for transmission of light,
		polarized prependicular to surface of a dielectric

		n1, n2 are dielectric constants
		mi1, mi2 are magnetic permeabilities

		t_|_ = (2 * n1 / mi1 * cos(psi_i)) / (n1 / mi1 * cos(psi_i) + n2 / mi2 * cos(psi_t))
		*/

		float f_cos_psi_i = f_Dot(r_v_normal);
		float f_cos_psi_t = -r_v_normal.f_Dot(r_v_transmission);

		return 2 * f_n1_to_mi1 * f_cos_psi_i /
			(f_n1_to_mi1 * f_cos_psi_i + f_n2_to_mi2 * f_cos_psi_t);
	}

	float f_TM(TConstArgType(Vector3<_Ty>) r_v_normal,
		TConstArgType(Vector3<_Ty>) r_v_transmission,
		float f_n1_to_mi1, float f_n2_to_mi2) const
	{
		/*
		Fresnel law for transmission of light,
		polarized parallel to surface of a dielectric

		n1, n2 are dielectric constants
		mi1, mi2 are magnetic permeabilities

		t|| = (2 * n1 / mi1 * cos(psi_i)) / (n1 / mi1 * cos(psi_t) + n2 / mi2 * cos(psi_i))
		*/

		float f_cos_psi_i = f_Dot(r_v_normal);
		float f_cos_psi_t = -r_v_normal.f_Dot(r_v_transmission);

		return 2 * f_n1_to_mi1 * f_cos_psi_i /
			(f_n1_to_mi1 * f_cos_psi_t + f_n2_to_mi2 * f_cos_psi_i);
	}

	/*
	 *	float f_Fresnel_T(ConstArgType(Vector3<_Ty>) r_v_normal, ConstArgType(Vector3<_Ty>) r_v_transmission,
	 *		float f_n1_to_mi1, float f_n2_to_mi2)
	 *		- calculates transmission for ray that goes trough surface with normal r_v_normal
	 *		  and refracts in direction r_v_transmission (reflectance is 1 - transmission)
	 *		- f_n1_to_mi1 = n1 / mi1 = dielectric constant / magnetic permeability
	 *		- todo - find out if it actually works, look for some common constants
	 */
	float f_Fresnel_T(TConstArgType(Vector3<_Ty>) r_v_normal,
		TConstArgType(Vector3<_Ty>) r_v_transmission,
		float f_n1_to_mi1, float f_n2_to_mi2) const
	{
		// Fresnel law for transmission of unpolarized light t = .5 * (f_TM^2 + f_TE^2)

		float f_cos_psi_i = f_Dot(r_v_normal);
		float f_cos_psi_t = -r_v_normal.f_Dot(r_v_transmission);

		float f_te = 2 * f_n1_to_mi1 * f_cos_psi_i /
			(f_n1_to_mi1 * f_cos_psi_i + f_n2_to_mi2 * f_cos_psi_t);
		float f_tm = 2 * f_n1_to_mi1 * f_cos_psi_i /
			(f_n1_to_mi1 * f_cos_psi_t + f_n2_to_mi2 * f_cos_psi_i);

		return .5f * (f_te * f_te + f_tm * f_tm);
	}

	/*
	 *	inline Vector3<_Ty> v_Refraction(ConstArgType(Vector3<_Ty>) r_v_normal, float f_eta)
	 *		- calcualte refraction of vector coming trough surface with normal r_v_normal
	 *		- f_eta = index of refraction of first environment to index of refraction of second
	 *		- n(vacuum) = 1, n(air) = 1.00029, n(water) = 1.33,
	 *		  n(glass) = 1.52 - 1.62, n(diamond) = 2.417, n(plastic) = 1.46 - 1.55
	 */
	inline Vector3<_Ty> v_Refraction(TConstArgType(Vector3<_Ty>) r_v_normal, float f_eta) const
	{
		Vector3<_Ty> v_tangent = v_Orthogonal(r_v_normal);
		float f_sin2_psi2 = f_eta * f_eta * (1 -
			r_v_normal.f_Dot(*this) * r_v_normal.f_Dot(*this));
		return (r_v_normal * -(float)sqrt(double(1 - f_sin2_psi2))) +
			   (v_tangent * (float)sqrt(double(f_sin2_psi2)));
		// v_transmitted = cos(psi2) * (-r_v_normal) + sin(psi2) * v_tangent
	}

	inline TNonConstResultType(Vector3<_Ty>) Refract(TConstArgType(Vector3<_Ty>) r_v_normal, float f_eta)
	{
		return (*this = v_Refraction(r_v_normal, f_eta));
	}

	inline Vector3<_Ty> v_Reflection(TConstArgType(Vector3<_Ty>) r_v_normal) const
	{
		return (*this) - ((r_v_normal * f_Dot(r_v_normal)) * 2.0f);
	}

	inline TNonConstResultType(Vector3<_Ty>) Reflect(TConstArgType(Vector3<_Ty>) r_v_normal)
	{
		return ((*this) -= ((r_v_normal * f_Dot(r_v_normal)) * 2.0f));
	}

	inline Vector3<_Ty> v_Orthogonal(TConstArgType(Vector3<_Ty>) r_v_normal) const
	{
		Vector3<_Ty> v_tangent;
		v_tangent = *this - r_v_normal * f_Dot(r_v_normal);
		v_tangent.Normalize();
		return v_tangent;
	}

	inline TNonConstResultType(Vector3<_Ty>) Orthogonalize(TConstArgType(Vector3<_Ty>) r_v_normal)
	{
		*this -= r_v_normal * f_Dot(r_v_normal);
		CBaseVectorImpl<_Ty, 3, Vector3Storage<_Ty>, Vector3<_Ty> >::Normalize();
		return *this;
	}

	define_swizzle2_out(x, y, v_xy)
	define_swizzle2_out(x, z, v_xz)
	define_swizzle2_out(y, x, v_yx)
	define_swizzle2_out(y, z, v_yz)
	define_swizzle2_out(z, x, v_zx)
	define_swizzle2_out(z, y, v_zy)
	define_swizzle2_out(x, x, v_xx)
	define_swizzle2_out(y, y, v_yy)
	define_swizzle2_out(z, z, v_zz)

	define_swizzle2_in(x, y, xy)
	define_swizzle2_in(x, z, xz)
	define_swizzle2_in(y, z, yz)

	define_swizzle3_out(x, x, x, v_xxx)
	define_swizzle3_out(x, x, y, v_xxy)
	define_swizzle3_out(x, x, z, v_xxz)
	define_swizzle3_out(x, y, x, v_xyx)
	define_swizzle3_out(x, y, y, v_xyy)
	define_swizzle3_out(x, y, z, v_xyz)
	define_swizzle3_out(x, z, x, v_xzx)
	define_swizzle3_out(x, z, y, v_xzy)
	define_swizzle3_out(x, z, z, v_xzz)
	define_swizzle3_out(y, x, x, v_yxx)
	define_swizzle3_out(y, x, y, v_yxy)
	define_swizzle3_out(y, x, z, v_yxz)
	define_swizzle3_out(y, y, x, v_yyx)
	define_swizzle3_out(y, y, y, v_yyy)
	define_swizzle3_out(y, y, z, v_yyz)
	define_swizzle3_out(y, z, x, v_yzx)
	define_swizzle3_out(y, z, y, v_yzy)
	define_swizzle3_out(y, z, z, v_yzz)
	define_swizzle3_out(z, x, x, v_zxx)
	define_swizzle3_out(z, x, y, v_zxy)
	define_swizzle3_out(z, x, z, v_zxz)
	define_swizzle3_out(z, y, x, v_zyx)
	define_swizzle3_out(z, y, y, v_zyy)
	define_swizzle3_out(z, y, z, v_zyz)
	define_swizzle3_out(z, z, x, v_zzx)
	define_swizzle3_out(z, z, y, v_zzy)
	define_swizzle3_out(z, z, z, v_zzz)
};

/**
 *	@brief three-dimensional euclidean floating-point vector
 */
typedef Vector3<float> Vector3f;

/**
 *	@brief three-dimensional euclidean souble-precision floating-point vector
 */
typedef Vector3<double> Vector3d;

/**
 *	@brief three-dimensional euclidean integer vector
 */
typedef Vector3<int> Vector3i;

/*
 *								=== ~Vector3 ===
 */

/*
 *								=== Vector4 ===
 */

/**
 *	@brief four-dimensional euclidean vector storage with named components
 */
template <class _Ty>
struct Vector4Storage {
	union {
		struct {
			_Ty x, y, z, w;
		};
		_Ty p_elem[4];
	};
};

/**
 *	@brief four-dimensional euclidean vector
 */
template <class _Ty>
struct Vector4 : public CBaseVectorImpl<_Ty, 4, Vector4Storage<_Ty>, Vector4<_Ty> > {
	typedef Vector4Storage<_Ty> _TyStorage;
	typedef _Ty _TyScalar;

	inline Vector4()
	{}

	inline Vector4(_Ty t_x, _Ty t_y, _Ty t_z, _Ty t_w)
	{
		_TyStorage::x = t_x;
		_TyStorage::y = t_y;
		_TyStorage::z = t_z;
		_TyStorage::w = t_w;
	}

	inline Vector4(TConstArgType(Vector2<_Ty>) r_t_vec, _Ty t_z, _Ty t_w)
	{
		_TyStorage::x = r_t_vec.x;
		_TyStorage::y = r_t_vec.y;
		_TyStorage::z = t_z;
		_TyStorage::w = t_w;
	}

	inline Vector4(_Ty t_x, TConstArgType(Vector2<_Ty>) r_t_vec, _Ty t_w)
	{
		_TyStorage::x = t_x;
		_TyStorage::y = r_t_vec.x;
		_TyStorage::z = r_t_vec.y;
		_TyStorage::w = t_w;
	}

	inline Vector4(_Ty t_x, _Ty t_y, TConstArgType(Vector2<_Ty>) r_t_vec)
	{
		_TyStorage::x = t_x;
		_TyStorage::y = t_y;
		_TyStorage::z = r_t_vec.x;
		_TyStorage::w = r_t_vec.y;
	}

	inline Vector4(TConstArgType(Vector2<_Ty>) r_t_vec, TConstArgType(Vector2<_Ty>) r_t_vec2)
	{
		_TyStorage::x = r_t_vec.x;
		_TyStorage::y = r_t_vec.y;
		_TyStorage::z = r_t_vec2.x;
		_TyStorage::w = r_t_vec2.y;
	}

	inline Vector4(TConstArgType(Vector3<_Ty>) r_t_vec, _Ty t_w)
	{
		_TyStorage::x = r_t_vec.x;
		_TyStorage::y = r_t_vec.y;
		_TyStorage::z = r_t_vec.z;
		_TyStorage::w = t_w;
	}

	inline Vector4(_Ty t_x, TConstArgType(Vector3<_Ty>) r_t_vec)
	{
		_TyStorage::x = t_x;
		_TyStorage::y = r_t_vec.x;
		_TyStorage::z = r_t_vec.y;
		_TyStorage::w = r_t_vec.z;
	}

	define_swizzle4_out(x, x, x, x, v_xxxx)
	define_swizzle4_out(x, x, x, y, v_xxxy)
	define_swizzle4_out(x, x, x, z, v_xxxz)
	define_swizzle4_out(x, x, x, w, v_xxxw)
	define_swizzle4_out(x, x, y, x, v_xxyx)
	define_swizzle4_out(x, x, y, y, v_xxyy)
	define_swizzle4_out(x, x, y, z, v_xxyz)
	define_swizzle4_out(x, x, y, w, v_xxyw)
	define_swizzle4_out(x, x, z, x, v_xxzx)
	define_swizzle4_out(x, x, z, y, v_xxzy)
	define_swizzle4_out(x, x, z, z, v_xxzz)
	define_swizzle4_out(x, x, z, w, v_xxzw)
	define_swizzle4_out(x, x, w, x, v_xxwx)
	define_swizzle4_out(x, x, w, y, v_xxwy)
	define_swizzle4_out(x, x, w, z, v_xxwz)
	define_swizzle4_out(x, x, w, w, v_xxww)
	define_swizzle4_out(x, y, x, x, v_xyxx)
	define_swizzle4_out(x, y, x, y, v_xyxy)
	define_swizzle4_out(x, y, x, z, v_xyxz)
	define_swizzle4_out(x, y, x, w, v_xyxw)
	define_swizzle4_out(x, y, y, x, v_xyyx)
	define_swizzle4_out(x, y, y, y, v_xyyy)
	define_swizzle4_out(x, y, y, z, v_xyyz)
	define_swizzle4_out(x, y, y, w, v_xyyw)
	define_swizzle4_out(x, y, z, x, v_xyzx)
	define_swizzle4_out(x, y, z, y, v_xyzy)
	define_swizzle4_out(x, y, z, z, v_xyzz)
	define_swizzle4_out(x, y, z, w, v_xyzw)
	define_swizzle4_out(x, y, w, x, v_xywx)
	define_swizzle4_out(x, y, w, y, v_xywy)
	define_swizzle4_out(x, y, w, z, v_xywz)
	define_swizzle4_out(x, y, w, w, v_xyww)
	define_swizzle4_out(x, z, x, x, v_xzxx)
	define_swizzle4_out(x, z, x, y, v_xzxy)
	define_swizzle4_out(x, z, x, z, v_xzxz)
	define_swizzle4_out(x, z, x, w, v_xzxw)
	define_swizzle4_out(x, z, y, x, v_xzyx)
	define_swizzle4_out(x, z, y, y, v_xzyy)
	define_swizzle4_out(x, z, y, z, v_xzyz)
	define_swizzle4_out(x, z, y, w, v_xzyw)
	define_swizzle4_out(x, z, z, x, v_xzzx)
	define_swizzle4_out(x, z, z, y, v_xzzy)
	define_swizzle4_out(x, z, z, z, v_xzzz)
	define_swizzle4_out(x, z, z, w, v_xzzw)
	define_swizzle4_out(x, z, w, x, v_xzwx)
	define_swizzle4_out(x, z, w, y, v_xzwy)
	define_swizzle4_out(x, z, w, z, v_xzwz)
	define_swizzle4_out(x, z, w, w, v_xzww)
	define_swizzle4_out(x, w, x, x, v_xwxx)
	define_swizzle4_out(x, w, x, y, v_xwxy)
	define_swizzle4_out(x, w, x, z, v_xwxz)
	define_swizzle4_out(x, w, x, w, v_xwxw)
	define_swizzle4_out(x, w, y, x, v_xwyx)
	define_swizzle4_out(x, w, y, y, v_xwyy)
	define_swizzle4_out(x, w, y, z, v_xwyz)
	define_swizzle4_out(x, w, y, w, v_xwyw)
	define_swizzle4_out(x, w, z, x, v_xwzx)
	define_swizzle4_out(x, w, z, y, v_xwzy)
	define_swizzle4_out(x, w, z, z, v_xwzz)
	define_swizzle4_out(x, w, z, w, v_xwzw)
	define_swizzle4_out(x, w, w, x, v_xwwx)
	define_swizzle4_out(x, w, w, y, v_xwwy)
	define_swizzle4_out(x, w, w, z, v_xwwz)
	define_swizzle4_out(x, w, w, w, v_xwww)
	define_swizzle4_out(y, x, x, x, v_yxxx)
	define_swizzle4_out(y, x, x, y, v_yxxy)
	define_swizzle4_out(y, x, x, z, v_yxxz)
	define_swizzle4_out(y, x, x, w, v_yxxw)
	define_swizzle4_out(y, x, y, x, v_yxyx)
	define_swizzle4_out(y, x, y, y, v_yxyy)
	define_swizzle4_out(y, x, y, z, v_yxyz)
	define_swizzle4_out(y, x, y, w, v_yxyw)
	define_swizzle4_out(y, x, z, x, v_yxzx)
	define_swizzle4_out(y, x, z, y, v_yxzy)
	define_swizzle4_out(y, x, z, z, v_yxzz)
	define_swizzle4_out(y, x, z, w, v_yxzw)
	define_swizzle4_out(y, x, w, x, v_yxwx)
	define_swizzle4_out(y, x, w, y, v_yxwy)
	define_swizzle4_out(y, x, w, z, v_yxwz)
	define_swizzle4_out(y, x, w, w, v_yxww)
	define_swizzle4_out(y, y, x, x, v_yyxx)
	define_swizzle4_out(y, y, x, y, v_yyxy)
	define_swizzle4_out(y, y, x, z, v_yyxz)
	define_swizzle4_out(y, y, x, w, v_yyxw)
	define_swizzle4_out(y, y, y, x, v_yyyx)
	define_swizzle4_out(y, y, y, y, v_yyyy)
	define_swizzle4_out(y, y, y, z, v_yyyz)
	define_swizzle4_out(y, y, y, w, v_yyyw)
	define_swizzle4_out(y, y, z, x, v_yyzx)
	define_swizzle4_out(y, y, z, y, v_yyzy)
	define_swizzle4_out(y, y, z, z, v_yyzz)
	define_swizzle4_out(y, y, z, w, v_yyzw)
	define_swizzle4_out(y, y, w, x, v_yywx)
	define_swizzle4_out(y, y, w, y, v_yywy)
	define_swizzle4_out(y, y, w, z, v_yywz)
	define_swizzle4_out(y, y, w, w, v_yyww)
	define_swizzle4_out(y, z, x, x, v_yzxx)
	define_swizzle4_out(y, z, x, y, v_yzxy)
	define_swizzle4_out(y, z, x, z, v_yzxz)
	define_swizzle4_out(y, z, x, w, v_yzxw)
	define_swizzle4_out(y, z, y, x, v_yzyx)
	define_swizzle4_out(y, z, y, y, v_yzyy)
	define_swizzle4_out(y, z, y, z, v_yzyz)
	define_swizzle4_out(y, z, y, w, v_yzyw)
	define_swizzle4_out(y, z, z, x, v_yzzx)
	define_swizzle4_out(y, z, z, y, v_yzzy)
	define_swizzle4_out(y, z, z, z, v_yzzz)
	define_swizzle4_out(y, z, z, w, v_yzzw)
	define_swizzle4_out(y, z, w, x, v_yzwx)
	define_swizzle4_out(y, z, w, y, v_yzwy)
	define_swizzle4_out(y, z, w, z, v_yzwz)
	define_swizzle4_out(y, z, w, w, v_yzww)
	define_swizzle4_out(y, w, x, x, v_ywxx)
	define_swizzle4_out(y, w, x, y, v_ywxy)
	define_swizzle4_out(y, w, x, z, v_ywxz)
	define_swizzle4_out(y, w, x, w, v_ywxw)
	define_swizzle4_out(y, w, y, x, v_ywyx)
	define_swizzle4_out(y, w, y, y, v_ywyy)
	define_swizzle4_out(y, w, y, z, v_ywyz)
	define_swizzle4_out(y, w, y, w, v_ywyw)
	define_swizzle4_out(y, w, z, x, v_ywzx)
	define_swizzle4_out(y, w, z, y, v_ywzy)
	define_swizzle4_out(y, w, z, z, v_ywzz)
	define_swizzle4_out(y, w, z, w, v_ywzw)
	define_swizzle4_out(y, w, w, x, v_ywwx)
	define_swizzle4_out(y, w, w, y, v_ywwy)
	define_swizzle4_out(y, w, w, z, v_ywwz)
	define_swizzle4_out(y, w, w, w, v_ywww)
	define_swizzle4_out(z, x, x, x, v_zxxx)
	define_swizzle4_out(z, x, x, y, v_zxxy)
	define_swizzle4_out(z, x, x, z, v_zxxz)
	define_swizzle4_out(z, x, x, w, v_zxxw)
	define_swizzle4_out(z, x, y, x, v_zxyx)
	define_swizzle4_out(z, x, y, y, v_zxyy)
	define_swizzle4_out(z, x, y, z, v_zxyz)
	define_swizzle4_out(z, x, y, w, v_zxyw)
	define_swizzle4_out(z, x, z, x, v_zxzx)
	define_swizzle4_out(z, x, z, y, v_zxzy)
	define_swizzle4_out(z, x, z, z, v_zxzz)
	define_swizzle4_out(z, x, z, w, v_zxzw)
	define_swizzle4_out(z, x, w, x, v_zxwx)
	define_swizzle4_out(z, x, w, y, v_zxwy)
	define_swizzle4_out(z, x, w, z, v_zxwz)
	define_swizzle4_out(z, x, w, w, v_zxww)
	define_swizzle4_out(z, y, x, x, v_zyxx)
	define_swizzle4_out(z, y, x, y, v_zyxy)
	define_swizzle4_out(z, y, x, z, v_zyxz)
	define_swizzle4_out(z, y, x, w, v_zyxw)
	define_swizzle4_out(z, y, y, x, v_zyyx)
	define_swizzle4_out(z, y, y, y, v_zyyy)
	define_swizzle4_out(z, y, y, z, v_zyyz)
	define_swizzle4_out(z, y, y, w, v_zyyw)
	define_swizzle4_out(z, y, z, x, v_zyzx)
	define_swizzle4_out(z, y, z, y, v_zyzy)
	define_swizzle4_out(z, y, z, z, v_zyzz)
	define_swizzle4_out(z, y, z, w, v_zyzw)
	define_swizzle4_out(z, y, w, x, v_zywx)
	define_swizzle4_out(z, y, w, y, v_zywy)
	define_swizzle4_out(z, y, w, z, v_zywz)
	define_swizzle4_out(z, y, w, w, v_zyww)
	define_swizzle4_out(z, z, x, x, v_zzxx)
	define_swizzle4_out(z, z, x, y, v_zzxy)
	define_swizzle4_out(z, z, x, z, v_zzxz)
	define_swizzle4_out(z, z, x, w, v_zzxw)
	define_swizzle4_out(z, z, y, x, v_zzyx)
	define_swizzle4_out(z, z, y, y, v_zzyy)
	define_swizzle4_out(z, z, y, z, v_zzyz)
	define_swizzle4_out(z, z, y, w, v_zzyw)
	define_swizzle4_out(z, z, z, x, v_zzzx)
	define_swizzle4_out(z, z, z, y, v_zzzy)
	define_swizzle4_out(z, z, z, z, v_zzzz)
	define_swizzle4_out(z, z, z, w, v_zzzw)
	define_swizzle4_out(z, z, w, x, v_zzwx)
	define_swizzle4_out(z, z, w, y, v_zzwy)
	define_swizzle4_out(z, z, w, z, v_zzwz)
	define_swizzle4_out(z, z, w, w, v_zzww)
	define_swizzle4_out(z, w, x, x, v_zwxx)
	define_swizzle4_out(z, w, x, y, v_zwxy)
	define_swizzle4_out(z, w, x, z, v_zwxz)
	define_swizzle4_out(z, w, x, w, v_zwxw)
	define_swizzle4_out(z, w, y, x, v_zwyx)
	define_swizzle4_out(z, w, y, y, v_zwyy)
	define_swizzle4_out(z, w, y, z, v_zwyz)
	define_swizzle4_out(z, w, y, w, v_zwyw)
	define_swizzle4_out(z, w, z, x, v_zwzx)
	define_swizzle4_out(z, w, z, y, v_zwzy)
	define_swizzle4_out(z, w, z, z, v_zwzz)
	define_swizzle4_out(z, w, z, w, v_zwzw)
	define_swizzle4_out(z, w, w, x, v_zwwx)
	define_swizzle4_out(z, w, w, y, v_zwwy)
	define_swizzle4_out(z, w, w, z, v_zwwz)
	define_swizzle4_out(z, w, w, w, v_zwww)
	define_swizzle4_out(w, x, x, x, v_wxxx)
	define_swizzle4_out(w, x, x, y, v_wxxy)
	define_swizzle4_out(w, x, x, z, v_wxxz)
	define_swizzle4_out(w, x, x, w, v_wxxw)
	define_swizzle4_out(w, x, y, x, v_wxyx)
	define_swizzle4_out(w, x, y, y, v_wxyy)
	define_swizzle4_out(w, x, y, z, v_wxyz)
	define_swizzle4_out(w, x, y, w, v_wxyw)
	define_swizzle4_out(w, x, z, x, v_wxzx)
	define_swizzle4_out(w, x, z, y, v_wxzy)
	define_swizzle4_out(w, x, z, z, v_wxzz)
	define_swizzle4_out(w, x, z, w, v_wxzw)
	define_swizzle4_out(w, x, w, x, v_wxwx)
	define_swizzle4_out(w, x, w, y, v_wxwy)
	define_swizzle4_out(w, x, w, z, v_wxwz)
	define_swizzle4_out(w, x, w, w, v_wxww)
	define_swizzle4_out(w, y, x, x, v_wyxx)
	define_swizzle4_out(w, y, x, y, v_wyxy)
	define_swizzle4_out(w, y, x, z, v_wyxz)
	define_swizzle4_out(w, y, x, w, v_wyxw)
	define_swizzle4_out(w, y, y, x, v_wyyx)
	define_swizzle4_out(w, y, y, y, v_wyyy)
	define_swizzle4_out(w, y, y, z, v_wyyz)
	define_swizzle4_out(w, y, y, w, v_wyyw)
	define_swizzle4_out(w, y, z, x, v_wyzx)
	define_swizzle4_out(w, y, z, y, v_wyzy)
	define_swizzle4_out(w, y, z, z, v_wyzz)
	define_swizzle4_out(w, y, z, w, v_wyzw)
	define_swizzle4_out(w, y, w, x, v_wywx)
	define_swizzle4_out(w, y, w, y, v_wywy)
	define_swizzle4_out(w, y, w, z, v_wywz)
	define_swizzle4_out(w, y, w, w, v_wyww)
	define_swizzle4_out(w, z, x, x, v_wzxx)
	define_swizzle4_out(w, z, x, y, v_wzxy)
	define_swizzle4_out(w, z, x, z, v_wzxz)
	define_swizzle4_out(w, z, x, w, v_wzxw)
	define_swizzle4_out(w, z, y, x, v_wzyx)
	define_swizzle4_out(w, z, y, y, v_wzyy)
	define_swizzle4_out(w, z, y, z, v_wzyz)
	define_swizzle4_out(w, z, y, w, v_wzyw)
	define_swizzle4_out(w, z, z, x, v_wzzx)
	define_swizzle4_out(w, z, z, y, v_wzzy)
	define_swizzle4_out(w, z, z, z, v_wzzz)
	define_swizzle4_out(w, z, z, w, v_wzzw)
	define_swizzle4_out(w, z, w, x, v_wzwx)
	define_swizzle4_out(w, z, w, y, v_wzwy)
	define_swizzle4_out(w, z, w, z, v_wzwz)
	define_swizzle4_out(w, z, w, w, v_wzww)
	define_swizzle4_out(w, w, x, x, v_wwxx)
	define_swizzle4_out(w, w, x, y, v_wwxy)
	define_swizzle4_out(w, w, x, z, v_wwxz)
	define_swizzle4_out(w, w, x, w, v_wwxw)
	define_swizzle4_out(w, w, y, x, v_wwyx)
	define_swizzle4_out(w, w, y, y, v_wwyy)
	define_swizzle4_out(w, w, y, z, v_wwyz)
	define_swizzle4_out(w, w, y, w, v_wwyw)
	define_swizzle4_out(w, w, z, x, v_wwzx)
	define_swizzle4_out(w, w, z, y, v_wwzy)
	define_swizzle4_out(w, w, z, z, v_wwzz)
	define_swizzle4_out(w, w, z, w, v_wwzw)
	define_swizzle4_out(w, w, w, x, v_wwwx)
	define_swizzle4_out(w, w, w, y, v_wwwy)
	define_swizzle4_out(w, w, w, z, v_wwwz)
	define_swizzle4_out(w, w, w, w, v_wwww)

	define_swizzle3_out(x, x, x, v_xxx)
	define_swizzle3_out(x, x, y, v_xxy)
	define_swizzle3_out(x, x, z, v_xxz)
	define_swizzle3_out(x, x, w, v_xxw)
	define_swizzle3_out(x, y, x, v_xyx)
	define_swizzle3_out(x, y, y, v_xyy)
	define_swizzle3_out(x, y, z, v_xyz)
	define_swizzle3_out(x, y, w, v_xyw)
	define_swizzle3_out(x, z, x, v_xzx)
	define_swizzle3_out(x, z, y, v_xzy)
	define_swizzle3_out(x, z, z, v_xzz)
	define_swizzle3_out(x, z, w, v_xzw)
	define_swizzle3_out(x, w, x, v_xwx)
	define_swizzle3_out(x, w, y, v_xwy)
	define_swizzle3_out(x, w, z, v_xwz)
	define_swizzle3_out(x, w, w, v_xww)
	define_swizzle3_out(y, x, x, v_yxx)
	define_swizzle3_out(y, x, y, v_yxy)
	define_swizzle3_out(y, x, z, v_yxz)
	define_swizzle3_out(y, x, w, v_yxw)
	define_swizzle3_out(y, y, x, v_yyx)
	define_swizzle3_out(y, y, y, v_yyy)
	define_swizzle3_out(y, y, z, v_yyz)
	define_swizzle3_out(y, y, w, v_yyw)
	define_swizzle3_out(y, z, x, v_yzx)
	define_swizzle3_out(y, z, y, v_yzy)
	define_swizzle3_out(y, z, z, v_yzz)
	define_swizzle3_out(y, z, w, v_yzw)
	define_swizzle3_out(y, w, x, v_ywx)
	define_swizzle3_out(y, w, y, v_ywy)
	define_swizzle3_out(y, w, z, v_ywz)
	define_swizzle3_out(y, w, w, v_yww)
	define_swizzle3_out(z, x, x, v_zxx)
	define_swizzle3_out(z, x, y, v_zxy)
	define_swizzle3_out(z, x, z, v_zxz)
	define_swizzle3_out(z, x, w, v_zxw)
	define_swizzle3_out(z, y, x, v_zyx)
	define_swizzle3_out(z, y, y, v_zyy)
	define_swizzle3_out(z, y, z, v_zyz)
	define_swizzle3_out(z, y, w, v_zyw)
	define_swizzle3_out(z, z, x, v_zzx)
	define_swizzle3_out(z, z, y, v_zzy)
	define_swizzle3_out(z, z, z, v_zzz)
	define_swizzle3_out(z, z, w, v_zzw)
	define_swizzle3_out(z, w, x, v_zwx)
	define_swizzle3_out(z, w, y, v_zwy)
	define_swizzle3_out(z, w, z, v_zwz)
	define_swizzle3_out(z, w, w, v_zww)
	define_swizzle3_out(w, x, x, v_wxx)
	define_swizzle3_out(w, x, y, v_wxy)
	define_swizzle3_out(w, x, z, v_wxz)
	define_swizzle3_out(w, x, w, v_wxw)
	define_swizzle3_out(w, y, x, v_wyx)
	define_swizzle3_out(w, y, y, v_wyy)
	define_swizzle3_out(w, y, z, v_wyz)
	define_swizzle3_out(w, y, w, v_wyw)
	define_swizzle3_out(w, z, x, v_wzx)
	define_swizzle3_out(w, z, y, v_wzy)
	define_swizzle3_out(w, z, z, v_wzz)
	define_swizzle3_out(w, z, w, v_wzw)
	define_swizzle3_out(w, w, x, v_wwx)
	define_swizzle3_out(w, w, y, v_wwy)
	define_swizzle3_out(w, w, z, v_wwz)
	define_swizzle3_out(w, w, w, v_www)

	define_swizzle2_out(x, x, v_xx)
	define_swizzle2_out(x, y, v_xy)
	define_swizzle2_out(x, z, v_xz)
	define_swizzle2_out(x, w, v_xw)
	define_swizzle2_out(y, x, v_yx)
	define_swizzle2_out(y, y, v_yy)
	define_swizzle2_out(y, z, v_yz)
	define_swizzle2_out(y, w, v_yw)
	define_swizzle2_out(z, x, v_zx)
	define_swizzle2_out(z, y, v_zy)
	define_swizzle2_out(z, z, v_zz)
	define_swizzle2_out(z, w, v_zw)
	define_swizzle2_out(w, x, v_wx)
	define_swizzle2_out(w, y, v_wy)
	define_swizzle2_out(w, z, v_wz)
	define_swizzle2_out(w, w, v_ww)

	define_swizzle2_in(x, y, xy)
	define_swizzle2_in(x, z, xz)
	define_swizzle2_in(x, w, xw)
	define_swizzle2_in(y, z, yz)
	define_swizzle2_in(y, w, yw)
	define_swizzle2_in(z, w, zw)

	define_swizzle3_in(x, y, z, xyz)
	define_swizzle3_in(x, y, w, xyw)
	define_swizzle3_in(x, z, w, xzw)
	define_swizzle3_in(y, z, w, yzw)
};

/**
 *	@brief four-dimensional euclidean integer vector
 */
typedef Vector4<int> Vector4i;

/**
 *	@brief four-dimensional euclidean floating-point vector
 */
typedef Vector4<float> Vector4f;

/**
 *	@brief four-dimensional euclidean souble-precision floating-point vector
 */
typedef Vector4<double> Vector4d;

/**
 *	@brief floating-point RGBA color
 */
typedef Vector4<float> Color4f;

/*
 *								=== Vector4 ===
 */

/*
 *								=== Plane3 ===
 */

/**
 *	@brief names of positions relative to a plane
 */
enum EPlanePos {
	plane_Front, /**< @brief the object is in front of the plane (the halfspace pointed to by the normal vector) */
	plane_Back, /**< @brief the object is in back of the plane */
	plane_Onplane, /**< @brief the object lies on the plane */
	plane_Split /**< @brief the object (eg. a polygon) is split by the plane */
};

/**
 *	@brief namespace with tag objects for Plane3
 */
namespace plane {

/**
 *	@brief tag for Plane3 constructor
 */
struct from_position_normal_tag_t {};

/**
 *	@brief tag for Plane3 constructor
 */
struct from_position_edge_vecs_tag_t {};

/**
 *	@brief tag for Plane3 constructor
 */
struct from_triangle_tag_t {};

extern const from_position_normal_tag_t from_position_normal; /**< @brief tag for Plane3 constructor */
extern const from_position_edge_vecs_tag_t from_position_edge_vecs; /**< @brief tag for Plane3 constructor */
extern const from_triangle_tag_t from_triangle; /**< @brief tag for Plane3 constructor */

} // ~plane

/**
 *	@brief a simple template of a hyperplane in 3-space
 *	@param[in] _Ty is scalar data type to be used by the plane
 */
template <class _Ty>
struct Plane3 {
	typedef _Ty _TyScalar;

	Vector3<_TyScalar> v_normal; /**< @brief normal vector */
	_TyScalar f_dist; /**< @brief perpendicular distance form the origin */

	/**
	 *	@brief default constructor; has no effect
	 */
	inline Plane3()
	{}

	/**
	 *	@brief constructor with direct plane parameters
	 *
	 *	@param[in] r_v_norm is the normal vector
	 *	@param[in] _f_dist is perpendicular distance form the origin
	 */
	inline Plane3(TConstArgType(Vector3<_TyScalar>) r_v_norm, _TyScalar _f_dist)
		:v_normal(r_v_norm), f_dist(_f_dist)
	{}

#ifdef __PLANE3_LEGACY_CONSTRUCTORS

	/**
	 *	@brief constructor with normal vector and a point lying on the plane
	 *
	 *	@param[in] r_v_pos is a point lying on the plane
	 *	@param[in] r_v_norm is the normal vector
	 */
	inline Plane3(TConstArgType(Vector3<_TyScalar>) r_v_pos,
		TConstArgType(Vector3<_TyScalar>) r_v_norm)
		:v_normal(r_v_norm), f_dist(-r_v_norm.f_Dot(r_v_pos))
	{}

	/**
	 *	@brief constructor with a pair of vectors lying on a plane and a point lying on the plane
	 *
	 *	@param[in] r_v_u is a vector parallel with the plane
	 *	@param[in] r_v_v is an other vector parallel with the plane
	 *	@param[in] r_v_pos is a point lying on the plane
	 *
	 *	@note Vectors r_v_u and r_v_v must not be colinear.
	 *	@note This constructor has the position as the last parameter!
	 */
	inline Plane3(TConstArgType(Vector3<_TyScalar>) r_v_u,
		TConstArgType(Vector3<_TyScalar>) r_v_v, TConstArgType(Vector3<_TyScalar>) r_v_pos)
	{
		v_normal = r_v_u.v_Cross(r_v_v);
		v_normal.Normalize(); // improves numerical stability
		f_dist = -v_normal.f_Dot(r_v_pos);
	}

#endif // __PLANE3_LEGACY_CONSTRUCTORS

	/**
	 *	@brief constructor with normal vector and a point lying on the plane
	 *
	 *	@param[in] r_v_pos is a point lying on the plane
	 *	@param[in] r_v_norm is the normal vector
	 *	@param[in] tag is tag that specifies the constructor type (unused)
	 */
	inline Plane3(TConstArgType(Vector3<_TyScalar>) r_v_pos, TConstArgType(Vector3<_TyScalar>) r_v_norm,
		plane::from_position_normal_tag_t VECTOR2_CONSTRUCTOR_TAG)
		:v_normal(r_v_norm), f_dist(-r_v_norm.f_Dot(r_v_pos))
	{}

	/**
	 *	@brief constructor with a pair of vectors lying on a plane and a point lying on the plane
	 *
	 *	@param[in] r_v_u is a vector parallel with the plane
	 *	@param[in] r_v_v is an other vector parallel with the plane
	 *	@param[in] r_v_pos is a point lying on the plane
	 *	@param[in] tag is tag that specifies the constructor type (unused)
	 *
	 *	@note Vectors r_v_u and r_v_v must not be colinear.
	 */
	inline Plane3(TConstArgType(Vector3<_TyScalar>) r_v_pos, TConstArgType(Vector3<_TyScalar>) r_v_u,
		TConstArgType(Vector3<_TyScalar>) r_v_v, plane::from_position_edge_vecs_tag_t VECTOR2_CONSTRUCTOR_TAG)
	{
		v_normal = r_v_u.v_Cross(r_v_v);
		v_normal.Normalize(); // improves numerical stability
		f_dist = -v_normal.f_Dot(r_v_pos);
	}

	/**
	 *	@brief constructor that makes a plane from a triangle
	 *
	 *	@param[in] r_t_a is the first triangle vertex
	 *	@param[in] r_t_b is the second triangle vertex
	 *	@param[in] r_t_c is the third triangle vertex
	 *
	 *	@return Returns plane lying on the given triangle.
	 *
	 *	@note In case the triangle is counter-clockwise, the normal points towards the observer.
	 */
	inline Plane3(TConstArgType(Vector3<_TyScalar>) r_t_a, TConstArgType(Vector3<_TyScalar>) r_t_b,
		TConstArgType(Vector3<_TyScalar>) r_t_c, plane::from_triangle_tag_t VECTOR2_CONSTRUCTOR_TAG)
	{
		v_normal = (r_t_b - r_t_a).v_Cross(r_t_c - r_t_a);
		v_normal.Normalize(); // improves numerical stability
		f_dist = -v_normal.f_Dot(r_t_a);
	}

	/**
	 *	@brief constructs a plane from a triangle
	 *
	 *	@param[in] r_t_a is the first triangle vertex
	 *	@param[in] r_t_b is the second triangle vertex
	 *	@param[in] r_t_c is the third triangle vertex
	 *
	 *	@return Returns plane lying on the given triangle.
	 *
	 *	@note In case the triangle is counter-clockwise, the normal points towards the observer.
	 */
	static inline Plane3<_TyScalar> t_From_Triangle(TConstArgType(Vector3<_TyScalar>) r_t_a,
		TConstArgType(Vector3<_TyScalar>) r_t_b, TConstArgType(Vector3<_TyScalar>) r_t_c)
	{
		return Plane3<_TyScalar>(r_t_b - r_t_a, r_t_c - r_t_a, r_t_a);
	}

	/**
	 *	@brief normalizes the plane (makes the normal vector unit length
	 *		without changing position of the plane in space)
	 */
	void Normalize()
	{
		_TyScalar f_inv_length = _TyScalar(1.0) / v_normal.f_Length();
		v_normal *= f_inv_length;
		f_dist *= f_inv_length;
	}

	/**
	 *	@brief creates the normalized version of the plane
	 *	@return Returns the normalized version of the plane (the normal
	 *		vector is unit length and the plane lies at the same position as the original plane).
	 */
	Plane3<_TyScalar> t_Normalized() const
	{
		_TyScalar f_inv_length = _TyScalar(1.0) / v_normal.f_Length();
		return Plane3<_TyScalar>(v_normal * f_inv_length, f_dist * f_inv_length);
	}

	/**
	 *	@brief calcualtes distance of a point from the plane
	 *	@param[in] r_v_vec is a point
	 *	@return Returns the perpendicular distance of the point from the plane.
	 */
	inline _TyScalar f_Vector_Dist(TConstArgType(Vector3<_TyScalar>) r_v_vec) const
	{
		return v_normal.f_Dot(r_v_vec) + f_dist;
	}

	/**
	 *	@brief calcualtes dot product of a given vector and the plane normal
	 *	@param[in] r_v_vec is a vector
	 *	@return Returns dot product of a given vector and the plane normal.
	 */
	inline _TyScalar f_Vector_Dot(TConstArgType(Vector3<_TyScalar>) r_v_vec) const
	{
		return v_normal.f_Dot(r_v_vec);
	}

	/**
	 *	@brief calculates position of a point relative to this plane
	 *	@param[in] r_v_vec is position of the point being tested
	 *	@return Returns the position of the point relative to this plane (one of plane_*).
	 */
	EPlanePos n_Vector_Pos(TConstArgType(Vector3<_TyScalar>) r_v_vec, _TyScalar f_epsilon_ex = f_epsilon) const
	{
		_TyScalar f = f_Vector_Dist(r_v_vec);
		return (f > f_epsilon_ex)? plane_Front : (f < -f_epsilon_ex)? plane_Back : plane_Onplane;
	}

	/**
	 *	@brief calculates position of a triangle relative to this plane
	 *
	 *	@param[in] r_v_a is the first triangle point
	 *	@param[in] r_v_b is the second triangle point
	 *	@param[in] r_v_c is the third triangle point
	 *	@param[in] f_epsilon_ex is position comparison epsilon
	 *
	 *	@return Returns the position of the triangle relative to this plane (one of plane_*).
	 */
	EPlanePos n_Tri_Pos(TConstArgType(Vector3<_TyScalar>) r_v_a, TConstArgType(Vector3<_TyScalar>) r_v_b,
		TConstArgType(Vector3<_TyScalar>) r_v_c, _TyScalar f_epsilon_ex = f_epsilon) const
	{
		bool b_front = false, b_back = false;

		switch(n_Vector_Pos(r_v_a, f_epsilon_ex)) {
		case plane_Front:
			b_front = true;
			break;
		case plane_Back:
			b_back = true;
			break;
		}

		switch(n_Vector_Pos(r_v_b, f_epsilon_ex)) {
		case plane_Front:
			b_front = true;
			break;
		case plane_Back:
			b_back = true;
			break;
		}

		switch(n_Vector_Pos(r_v_c, f_epsilon_ex)) {
		case plane_Front:
			if(!b_back)
				return plane_Front;
			return plane_Split;
		case plane_Back:
			if(!b_front)
				return plane_Back;
			return plane_Split;
		}

		if(!b_front) {
			if(!b_back)
				return plane_Onplane;
			return plane_Back;
		} else if(!b_back)
			return plane_Front;
		return plane_Split;
	}

	/**
	 *	@brief calculates intersection of a ray and the plane, along the ray
	 *
	 *	@param[out] r_v_intersection is intersection position
	 *	@param[in] r_v_org is ray origin
	 *	@param[in] r_v_dir is ray direction
	 *
	 *	@return Returns true if there is an intersection of the
	 *		specified ray with this plane, otherwise returns false.
	 */
	bool Intersect_Ray(Vector3<_TyScalar> &r_v_intersection, TConstArgType(Vector3<_TyScalar>) r_v_org,
		TConstArgType(Vector3<_TyScalar>) r_v_dir) const
	{
		_TyScalar f_t;
		if(fabs(f_t = f_Vector_Dot(r_v_dir)) < _TyScalar(1e-6))
			return false;
		r_v_intersection = r_v_org - r_v_dir * (f_Vector_Dist(r_v_org) / f_t);
		return true;
	}

	/**
	 *	@brief calculates offset of intersection of a ray and the plane, along the ray
	 *
	 *	@param[out] r_f_intersection_t is intersection position along the ray
	 *	@param[in] r_v_org is ray origin
	 *	@param[in] r_v_dir is ray direction
	 *
	 *	@return Returns true if there is an intersection of the
	 *		specified ray with this plane, otherwise returns false.
	 */
	bool Intersect_Ray_t(_TyScalar &r_f_intersection_t, TConstArgType(Vector3<_TyScalar>) r_v_org,
		TConstArgType(Vector3<_TyScalar>) r_v_dir) const
	{
		_TyScalar f_t;
		if(fabs(f_t = f_Vector_Dot(r_v_dir)) < _TyScalar(1e-6))
			return false;
		r_f_intersection_t = -f_Vector_Dist(r_v_org) / f_t;
		return true;
	}

	/**
	 *	@brief calculates intersection of two planes
	 *
	 *	@param[out] r_v_ray_org is a point lying in the intersection of the planes
	 *	@param[out] r_v_ray_dir is a direction of the line lying in the intersection of the planes
	 *	@param[in] r_t_other is the other plane
	 *
	 *	@return Returns true if this plane and r_t_other have an intersection
	 *		(returns false if the two planes are colinear).
	 *
	 *	@note Even though this function returns false, the two planes may be the same,
	 *		in which case there is an infinite number of intersections. But because
	 *		it is a condition, not trivial to detect robustly with floating-point
	 *		precision, it is not implemented here and is left for the caller to implement
	 *		it based on the task at hand. But usually operator ==() could be used for that case.
	 */
	bool Intersect(Vector3<_TyScalar> &r_v_ray_org, Vector3<_TyScalar> &r_v_ray_dir,
		TConstArgType(Plane3<_TyScalar>) r_t_other) const
	{
		if(fabs(v_normal.f_Dot(r_t_other.v_normal)) > 1 - _TyScalar(1e-6))
			return false;
		// planes are colinear

		const _TyScalar a = v_normal.x;
		const _TyScalar b = v_normal.y;
		const _TyScalar c = v_normal.z;
		const _TyScalar d = f_dist;
		// plane 1 equation

		const _TyScalar e = r_t_other.v_normal.x;
		const _TyScalar f = r_t_other.v_normal.y;
		const _TyScalar g = r_t_other.v_normal.z;
		const _TyScalar h = r_t_other.f_dist;
		// plane 2 equation

		Vector3<_TyScalar> v_ray_org, v_ray_dir;
		// intermediates

		v_ray_dir = v_normal.v_Cross(r_t_other.v_normal);
		v_ray_dir.Normalize(); // helps improve numerical stability
		// direction is perpendicular to the both planes

		if(fabs(v_ray_dir.z) > fabs(v_ray_dir.x)) {
			v_ray_org.z = 0;
			v_ray_org.y = (e * d - a * h) / (a * f - e * b);
			if(fabs(a) < 1e-6f && fabs(e) < 1e-6f)
				v_ray_org.x = 0; // don't care
			else if(fabs(a) < fabs(e))
				v_ray_org.x = -(f * v_ray_org.y + h) / e;
			else
				v_ray_org.x = -(b * v_ray_org.y + d) / a;
			// set z = 0, calculate ray origin
		} else if(fabs(v_ray_dir.x) > fabs(v_ray_dir.y)) {
			v_ray_org.z = (f * d - h * b) / (g * b - f * c);
			if(fabs(b) < 1e-6f && fabs(f) < 1e-6f)
				v_ray_org.y = 0; // don't care
			else if(fabs(b) < fabs(f))
				v_ray_org.y = -(g * v_ray_org.z + h) / f;
			else
				v_ray_org.y = -(c * v_ray_org.z + d) / b;
			v_ray_org.x = 0;
			// set x = 0, calculate ray origin
		} else {
			v_ray_org.x = (g * d - c * h) / (c * e - g * a);
			if(fabs(c) < 1e-6f && fabs(g) < 1e-6f)
				v_ray_org.z = 0; // don't care
			else if(fabs(c) < fabs(g))
				v_ray_org.z = -(e * v_ray_org.x + h) / g;
			else
				v_ray_org.z = -(a * v_ray_org.x + d) / c;
			v_ray_org.y = 0;
			// set y = 0, calculate ray origin
		}
		// calculate ray origin, set one of coordinates to zero, based on ray direction
		// and calculate the other two from the two plane equations

#ifdef _DEBUG
		Vector3f v_normal1 = v_normal, v_normal2 = r_t_other.v_normal;
		v_normal1.Normalize();
		v_normal2.Normalize();
		float f_dot = v_normal1.f_Dot(v_normal2);
		float f_c1 = (-d - -h * f_dot) / (1 - f_dot * f_dot);
		float f_c2 = (-h - -d * f_dot) / (1 - f_dot * f_dot);
		Vector3f v_alt_org = v_normal1 * f_c1 + v_normal2 * f_c2;
		// code from wiki

		_ASSERTE(fabs(f_Vector_Dist(v_alt_org)) <= v_alt_org.f_Length() * _TyScalar(1e-3));
		_ASSERTE(fabs(r_t_other.f_Vector_Dist(v_alt_org)) <= v_alt_org.f_Length() * _TyScalar(1e-3)); // works but is less precise
#endif // _DEBUG

		_ASSERTE(fabs(f_Vector_Dist(v_ray_org)) < _TyScalar(1e-3));
		_ASSERTE(fabs(r_t_other.f_Vector_Dist(v_ray_org)) < _TyScalar(1e-3));
		// make sure ray origin lies on the plane

		r_v_ray_org = v_ray_org;
		r_v_ray_dir = v_ray_dir;
		// copy the outputs

		return true;
	}

	/**
	 *	@brief calculates intersection of three planes
	 *
	 *	@param[out] r_v_dest is the intersection of the planes
	 *	@param[in] r_t_plane_b is the second plane
	 *	@param[in] r_t_plane_c is the third plane
	 *
	 *	@return Returns true if this plane, r_t_plane_b and r_t_plane_c have an intersection
	 *		(returns false if at least two of the planes are colinear).
	 *
	 *	@note Even though this function returns false, two of the planes may be the same,
	 *		in which case there is an infinite number of intersections, or a single one.
	 *		Refer to the other Intersect() function documentation for more details.
	 */
	bool Intersect(Vector3<_TyScalar> &r_v_dest, TConstArgType(Plane3<_TyScalar>) r_t_plane_b,
		TConstArgType(Plane3<_TyScalar>) r_t_plane_c) const
	{
		Vector3<_TyScalar> v_ray_org, v_ray_dir;
		if(!Intersect(v_ray_org, v_ray_dir, r_t_plane_b))
			return false;
		// calculate intersection of this plane ane r_plane_b (which is a line)

		return r_t_plane_c.Intersect_Ray(r_v_dest, v_ray_org, v_ray_dir);
		// calculate intersection of the third plane and the line calculated above
	}

	/**
	 *	@brief tests the two planes for colienarity
	 *	@param[in] r_t_plane is the other plane to compare with
	 *	@param[in] f_epsilon_ex is comparison epsilon
	 *	@return Returns true of the planes are colinear, otherwise returns false.
	 */
	inline bool b_Colinear(TConstArgType(Plane3<_TyScalar>) r_t_plane, _TyScalar f_epsilon_ex = f_epsilon) const
	{
		return fabs(f_Vector_Dot(r_t_plane.v_normal)) > _TyScalar(1.0) - f_epsilon_ex;
	}

	/**
	 *	@brief (exact) equality operator
	 *	@param[in] r_t_plane is the other plane to compare with
	 *	@return Returns true if the planes are colinear and lie on the same position.
	 */
	inline bool operator ==(TConstArgType(Plane3<_TyScalar>) r_t_plane) const
	{
		return r_t_plane.v_normal == v_normal && r_t_plane.f_dist == f_dist;
		/*if(fabs(f_Vector_Dot(r_t_plane.v_normal)) > _TyScalar(1.0) - f_epsilon &&
		   fabs(f_dist * r_t_plane.v_normal.f_Length() - r_t_plane.f_dist * v_normal.f_Length()) < f_epsilon)
			return true;
		return false;*/ // no epsilons here!
	}

	/**
	 *	@brief (exact) equality operator
	 *	@param[in] r_t_plane is the other plane to compare with
	 *	@return Returns true if the planes are colinear and lie on the same position.
	 */
	inline bool operator !=(TConstArgType(Plane3<_TyScalar>) r_t_plane) const
	{
		return !(*this == r_t_plane);
	}

	/**
	 *	@brief builds frustum planes from it's shape
	 *
	 *	These planes can be drawn using the following code:
	 *
	 *	@code
	 *	Matrix4f t_modelview; // modelview matrix
	 *	float f_fov = 60;
	 *	float f_near = .1f;
	 *	float f_far = 1000;
	 *	float f_aspect = 4.0f / 3; // such as 1024 * 768
	 *	// inputs
	 *
	 *	float f_h = _TyScalar(tan(f_fov * f_pi / 180 * .5)) * f_near;
	 *	float f_w = f_h * f_aspect;
	 *	// calc half width of frustum in x-y plane at z = f_near
	 *
	 *	Plane3<float> t_left, t_right, t_bottom, t_top, t_near, t_far;
	 *	Plane3<float>::Build_Frustum(t_left, t_right, t_bottom, t_top, t_near, t_far,
	 *		-f_w, f_w, -f_h, f_h, f_near, f_far, true);
	 *	// calculate frustum in camera space
	 *
	 *	Matrix4f t_mat = t_modelview.t_Transpose();
	 *	t_mat.Offset(-t_mat.v_Transform_Dir(t_modelview.v_Offset()));
	 *	t_mat[0][3] = 0;
	 *	t_mat[1][3] = 0;
	 *	t_mat[2][3] = 0;
	 *	t_mat[3][3] = 1; // ...
	 *	// prepare matrix for transforming camera space normals back to world space
	 *
	 *	t_near = t_mat.t_Transform_Plane(t_near);
	 *	t_far = t_mat.t_Transform_Plane(t_far);
	 *	t_left = t_mat.t_Transform_Plane(t_left);
	 *	t_right = t_mat.t_Transform_Plane(t_right);
	 *	t_bottom = t_mat.t_Transform_Plane(t_bottom);
	 *	t_top = t_mat.t_Transform_Plane(t_top);
	 *	// transform the frustum to world space
	 *
	 *	Vector3f na, nb, nc, nd;
	 *	Vector3f fa, fb, fc, fd;
	 *	t_near.Intersect(na, t_left, t_top);
	 *	t_near.Intersect(nb, t_right, t_top);
	 *	t_near.Intersect(nc, t_right, t_bottom);
	 *	t_near.Intersect(nd, t_left, t_bottom);
	 *	t_far.Intersect(fa, t_left, t_top);
	 *	t_far.Intersect(fb, t_right, t_top);
	 *	t_far.Intersect(fc, t_right, t_bottom);
	 *	t_far.Intersect(fd, t_left, t_bottom);
	 *	// calculate the corners of the frustum
	 *
	 *	float p_frustum_vertex_list[3 * 8];
	 *	{
	 *		float *p_dest = p_frustum_vertex_list;
	 *		*p_dest ++ = na.x; *p_dest ++ = na.y; *p_dest ++ = na.z;
	 *		*p_dest ++ = nb.x; *p_dest ++ = nb.y; *p_dest ++ = nb.z;
	 *		*p_dest ++ = nc.x; *p_dest ++ = nc.y; *p_dest ++ = nc.z;
	 *		*p_dest ++ = nd.x; *p_dest ++ = nd.y; *p_dest ++ = nd.z;
	 *		*p_dest ++ = fa.x; *p_dest ++ = fa.y; *p_dest ++ = fa.z;
	 *		*p_dest ++ = fb.x; *p_dest ++ = fb.y; *p_dest ++ = fb.z;
	 *		*p_dest ++ = fc.x; *p_dest ++ = fc.y; *p_dest ++ = fc.z;
	 *		*p_dest ++ = fd.x; *p_dest ++ = fd.y; *p_dest ++ = fd.z;
	 *	}
	 *	// fill the vertex array
	 *
	 *	const unsigned short p_frustum_index_list[] = {
	 *		0, 1, 2, 1, 3, 2, 0, 3,
	 *		4, 5, 5, 6, 6, 7, 7, 4,
	 *		0, 4, 1, 5, 2, 6, 3, 7
	 *	};
	 *	// index array
	 *
	 *	glVertexPointer(3, GL_FLOAT, 0, p_frustum_vertex_list);
	 *	glEnableClientState(GL_VERTEX_ARRAY);
	 *	glDrawElements(GL_LINES, 24, GL_UNSIGNED_SHORT, p_frustum_index_list);
	 *	glDisableClientState(GL_VERTEX_ARRAY);
	 *	// draw frustum (using the now-deprecated functionality)
	 *	@endcode
	 *
	 *	@param[out] r_t_left is the left clip plane
	 *	@param[out] r_t_right is the right clip plane
	 *	@param[out] r_t_bottom is the bottom clip plane
	 *	@param[out] r_t_top is the top clip plane
	 *	@param[out] r_t_near is the near clip plane
	 *	@param[out] r_t_far is the far clip plane
	 *	@param[in] f_left is position of left clip-plane
	 *	@param[in] f_right is position of right clip-plane
	 *	@param[in] f_bottom is position of bottom clip-plane
	 *	@param[in] f_top is position of top clip-plane
	 *	@param[in] f_near is depth of near clip plane
	 *	@param[in] f_far is depth of far clip plane
	 *	@param[in] b_normalize is plane normalization flag (if set, calculated planes are normalized)
	 */
	static void Build_Frustum(Plane3 &r_t_left, Plane3 &r_t_right, Plane3 &r_t_bottom,
		Plane3 &r_t_top, Plane3 &r_t_near, Plane3 &r_t_far, _TyScalar f_left, _TyScalar f_right,
		_TyScalar f_bottom, _TyScalar f_top, _TyScalar f_near, _TyScalar f_far, bool b_normalize)
	{
		r_t_near.v_normal.x = 0;
		r_t_near.v_normal.y = 0;
		r_t_near.v_normal.z = -1;
		r_t_near.f_dist = -f_near;

		r_t_far.v_normal.x = 0;
		r_t_far.v_normal.y = 0;
		r_t_far.v_normal.z = 1;
		r_t_far.f_dist = f_far;

		r_t_left.v_normal.x = -f_near;
		r_t_left.v_normal.y = 0;
		r_t_left.v_normal.z = f_left;
		r_t_left.f_dist = 0;

		r_t_right.v_normal.x = f_near;
		r_t_right.v_normal.y = 0;
		r_t_right.v_normal.z = -f_right;
		r_t_right.f_dist = 0;

		r_t_bottom.v_normal.x = 0;
		r_t_bottom.v_normal.y = -f_near;
		r_t_bottom.v_normal.z = f_bottom;
		r_t_bottom.f_dist = 0;

		r_t_top.v_normal.x = 0;
		r_t_top.v_normal.y = f_near;
		r_t_top.v_normal.z = -f_top;
		r_t_top.f_dist = 0;

		if(b_normalize) {
			// near and far come out normalized already
			r_t_left.Normalize();
			r_t_right.Normalize();
			r_t_bottom.Normalize();
			r_t_top.Normalize();
		}
	}

	/**
	 *	@brief builds symmetric frustum planes from it's shape
	 *
	 *	@param[out] r_t_left is the left clip plane
	 *	@param[out] r_t_right is the right clip plane
	 *	@param[out] r_t_bottom is the bottom clip plane
	 *	@param[out] r_t_top is the top clip plane
	 *	@param[out] r_t_near is the near clip plane
	 *	@param[out] r_t_far is the far clip plane
	 *	@param[in] f_fov is field-of-view in degrees
	 *	@param[in] f_aspect is aspect (viewport height / width)
	 *	@param[in] f_near is depth of near clip plane
	 *	@param[in] f_far is depth of far clip plane
	 *	@param[in] b_normalize is plane normalization flag (if set, calculated planes are normalized)
	 */
	static void Build_SymmetricFrustum(Plane3 &r_t_left, Plane3 &r_t_right, Plane3 &r_t_bottom,
		Plane3 &r_t_top, Plane3 &r_t_near, Plane3 &r_t_far, _TyScalar f_fov, _TyScalar f_aspect,
		_TyScalar f_near, _TyScalar f_far, bool b_normalize)
	{
		_TyScalar f_h = _TyScalar(tan(f_fov * f_pi / 180 * .5)) * f_near;
		_TyScalar f_w = f_h * f_aspect;
		// calc half width of frustum in x-y plane at z = f_near

		Build_Frustum(r_t_left, r_t_right, r_t_bottom, r_t_top, r_t_near, r_t_far,
			-f_w, f_w, -f_h, f_h, f_near, f_far, b_normalize);
	}
};

/**
 *	@brief a simple floating-point a hyperplane in 3-space
 */
typedef Plane3<float> Plane3f;

/**
 *	@brief a simple double-precision floating-point a hyperplane in 3-space
 */
typedef Plane3<double> Plane3d;

/*
 *								=== ~Plane3 ===
 */

/*
 *								=== Matrix4f ===
 */

/**
 *	@brief generic column-major matrix storage template
 *
 *	For programming purposes, OpenGL matrices are 16-value arrays with base
 *	vectors laid out contiguously in memory. The translation components occupy
 *	the 13th, 14th, and 15th elements
 *	(from http://www.opengl.org/archives/resources/faq/technical/transformations.htm)
 *
 *	The array
 *
 *		| 1 2 3 |
 *		| 4 5 6 |
 *
 *	if stored contiguously in linear memory with column-major order would look like the following:
 *
 *		1  4  2  5  3  6
 *
 *	(from http://en.wikipedia.org/wiki/Row-major_order#Column-major_order)
 *
 *	@tparam _Ty is scalar data type
 *	@tparam n_cols is number of matrix columns
 *	@tparam n_rows is number of matrix rows
 */
template <class _Ty, const int n_cols, const int n_rows>
class CMatrixStorage {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = n_cols, /**< @brief number of matrix columns */
		n_row_num = n_rows /**< @brief number of matrix rows */
	};

	typedef _Ty _TyScalar; /**< @brief scalar data type */

	typedef CMatrixStorage<_TyScalar, n_rows, n_cols> _TyTransposeType; /**< @brief transpose type of the storage */

protected:
	_TyScalar f[n_column_num][n_row_num]; /**< @brief matrix elements (column major order) */

public:
	/**
	 *	@brief gets matrix elements
	 *	@return Returns pointer to matrix elements (column major, compatible with OpenGL).
	 */
	inline _TyScalar *p_Data()
	{
		return &f[0][0];
	}

	/**
	 *	@brief gets matrix elements
	 *	@return Returns const pointer to matrix elements (column major, compatible with OpenGL).
	 */
	inline const _TyScalar *p_Data() const
	{
		return &f[0][0];
	}

	/**
	 *	@brief gets a column of this matrix
	 *	@param[in] n_index is zero-based index of the column
	 *	@return Returns pointer to the first element of the selected column of this matrix.
	 */
	inline _TyScalar *operator [](size_t n_column)
	{
		_ASSERTE(n_column < n_column_num);
		return f[n_column];
	}

	/**
	 *	@brief gets a column of this matrix
	 *	@param[in] n_index is zero-based index of the column
	 *	@return Returns const pointer to the first element of the selected column of this matrix.
	 */
	inline const _TyScalar *operator [](size_t n_column) const
	{
		_ASSERTE(n_column < n_column_num);
		return f[n_column];
	}

	/**
	 *	@brief accesses the elements of the matrix, Eigen style
	 *
	 *	@param[in] n_column is zero-based index of the column
	 *	@param[in] n_row is zero-based index of the row
	 *
	 *	@return Returns reference to the element at the selected column and row.
	 */
	inline TWritableResultType(_TyScalar) operator ()(size_t n_column, size_t n_row)
	{
		_ASSERTE(n_column < n_column_num && n_row < n_row_num);
		return f[n_column][n_row];
	}

	/**
	 *	@brief accesses the elements of the matrix, Eigen style
	 *
	 *	@param[in] n_column is zero-based index of the column
	 *	@param[in] n_row is zero-based index of the row
	 *
	 *	@return Returns value of the element at the selected column and row.
	 */
	inline TConstResultType(_TyScalar) operator ()(size_t n_column, size_t n_row) const
	{
		_ASSERTE(n_column < n_column_num && n_row < n_row_num);
		return f[n_column][n_row];
	}
};

/**
 *	@brief generic column-major matrix base template
 *
 *	@tparam _Ty is scalar data type
 *	@tparam n_cols is number of matrix columns
 *	@tparam n_rows is number of matrix rows
 *	@tparam Derived is name of the final derived class
 */
template <class _Ty, const int n_cols, const int n_rows, class Derived>
class CMatrixBase : public CMatrixStorage<_Ty, n_cols, n_rows> {
public:
	typedef CMatrixStorage<_Ty, n_cols, n_rows> _TyStorage;
	typedef _Ty _TyScalar;

	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = _TyStorage::n_column_num, /**< @brief number of matrix columns */
		n_row_num = _TyStorage::n_row_num /**< @brief number of matrix rows */
	};

	/**
	 *	@brief creates an unit matrix (identity transformation)
	 *	@note This is not done automatically by the constructor (there's none).
	 */
	void Identity()
	{
		for(int j = 0; j < n_column_num; ++ j) {
			for(int i = 0; i < n_row_num; ++ i)
				_TyStorage::f[j][i] = (_TyScalar)(i == j);
		}
	}

	/**
	 *	@brief sets every element of the matrix to zero
	 */
	void SetZero()
	{
		for(int j = 0; j < n_column_num; ++ j) {
			for(int i = 0; i < n_row_num; ++ i)
				_TyStorage::f[j][i] = (_TyScalar)0;
		}
	}

	/**
	 *	@brief sets every element of the matrix to a constant value
	 *	@param[in] f_const is the value to set the elements to
	 */
	void SetConst(_TyScalar f_const)
	{
		for(int j = 0; j < n_column_num; ++ j) {
			for(int i = 0; i < n_row_num; ++ i)
				_TyStorage::f[j][i] = f_const;
		}
	}

	/**
	 *	@brief creates a scaling matrix
	 *	@param[in] f_scale is scaling factor (same for x, y and z)
	 */
	void Scaling(_TyScalar f_scale)
	{
		for(int j = 0; j < n_column_num; ++ j) {
			for(int i = 0; i < n_row_num; ++ i)
				_TyStorage::f[j][i] = (i == j)? f_scale : _TyScalar(0);
		}
	}

	/**
	 *	@brief calculates trace of the matrix
	 *	@return Returns trace of the matrix.
	 *	@note This will work even for rectangulkar matrices, and will
	 *		return sum of elements on the main diagonal.
	 */
	_TyScalar f_Trace() const
	{
		_TyScalar f_trace = _TyStorage::f[0][0];
		for(int i = 1; i < min(n_column_num, n_row_num); ++ i)
			f_trace += _TyStorage::f[i][i]; // a sum
		return f_trace;
	}

	/**
	 *	@brief calculates squared norm of the matrix
	 *	@return Returns squared norm of the matrix.
	 */
	_TyScalar f_SquaredNorm() const
	{
		_TyScalar f_norm = 0;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				f_norm += _TyStorage::f[i][j] * _TyStorage::f[i][j];
		}
		return f_norm;
	}

	/**
	 *	@brief calculates norm of the matrix
	 *	@return Returns norm of the matrix.
	 */
	inline _TyScalar f_Norm() const
	{
		return _TyScalar(sqrt(double(f_SquaredNorm())));
	}

	/**
	 *	@brief unary minus operator
	 *	@return Returns an elementwise negative of this matrix.
	 */
	Derived operator -() const
	{
		Derived t_result;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_result[i][j] = -_TyStorage::f[i][j];
		}
		return t_result;
	}

	/**
	 *	@brief element-wise adds a scalar to this matrix elements
	 *	@param[in] f_offset is the added quantity
	 *	@return Returns an elementwise sum of this matrix and a scalar.
	 */
	Derived operator +(_TyScalar f_offset) const
	{
		Derived t_result;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_result[i][j] = _TyStorage::f[i][j] + f_offset;
		}
		return t_result;
	}

	/**
	 *	@brief element-wise subtracts a scalar from this matrix elements
	 *	@param[in] f_offset is the added quantity
	 *	@return Returns an elementwise sum of this matrix and a scalar.
	 */
	Derived operator -(_TyScalar f_offset) const
	{
		Derived t_result;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_result[i][j] = _TyStorage::f[i][j] - f_offset;
		}
		return t_result;
	}

	/**
	 *	@brief calculates element-wise product of this matrix elements and a scalar
	 *	@param[in] f_factor is the factor
	 *	@return Returns an elementwise product of this matrix and a scalar.
	 */
	Derived operator *(_TyScalar f_factor) const
	{
		Derived t_result;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_result[i][j] = _TyStorage::f[i][j] * f_factor;
		}
		return t_result;
	}

	/**
	 *	@brief calculates element-wise ratio of this matrix elements and a scalar
	 *	@param[in] f_factor is the factor
	 *	@return Returns an elementwise ratio of this matrix and a scalar.
	 */
	Derived operator /(_TyScalar f_factor) const
	{
		f_factor = 1 / f_factor; // multiplication faster than division
		Derived t_result;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_result[i][j] = _TyStorage::f[i][j] * f_factor;
		}
		return t_result;
	}

	/**
	 *	@brief element-wise adds a scalar to this matrix elements
	 *	@param[in] f_offset is the added quantity
	 *	@return Returns reference to this.
	 *	@note This is faster, than <tt>*this = *this + f_factor;</tt>.
	 */
	TNonConstResultType(Derived) operator +=(_TyScalar f_offset)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] += f_offset;
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief element-wise subtracrs a scalar from this matrix elements
	 *	@param[in] f_offset is the subtracted quantity
	 *	@return Returns reference to this.
	 *	@note This is faster, than <tt>*this = *this - f_factor;</tt>.
	 */
	TNonConstResultType(Derived) operator -=(_TyScalar f_offset)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] -= f_offset;
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief element-wise multiplies this matrix by a scalar
	 *	@param[in] f_factor is the multiplier
	 *	@return Returns reference to this.
	 *	@note This is faster, than <tt>*this = *this * f_factor;</tt>.
	 */
	TNonConstResultType(Derived) operator *=(_TyScalar f_factor)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] *= f_factor;
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief element-wise divides this matrix by a scalar
	 *	@param[in] f_factor is the divisor
	 *	@return Returns reference to this.
	 *	@note This is faster, than <tt>*this = *this / f_factor;</tt>.
	 */
	TNonConstResultType(Derived) operator /=(_TyScalar f_factor)
	{
		f_factor = 1 / f_factor; // multiplication faster than division
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] *= f_factor;
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief calculates sum of two matrices
	 *	@param[in] r_t_mat is the right-hand side summand
	 *	@return Returns sum of this matrix with the argument.
	 */
	Derived operator +(TConstArgType(_TyStorage) r_t_mat) const
	{
		Derived t_tmp;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_tmp/*._TyStorage::f*/[i][j] = _TyStorage::f[i][j] + r_t_mat/*._TyStorage::f*/[i][j];
		}
		return t_tmp;
	}

	/**
	 *	@brief calculates difference of two matrices
	 *	@param[in] r_t_mat is the right-hand side summand
	 *	@return Returns difference of this matrix with the argument.
	 */
	Derived operator -(TConstArgType(_TyStorage) r_t_mat) const
	{
		Derived t_tmp;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_tmp/*._TyStorage::f*/[i][j] = _TyStorage::f[i][j] - r_t_mat/*._TyStorage::f*/[i][j]; // refuses to compile for some reason
		}
		return t_tmp;
	}

	/**
	 *	@brief calculates elementwise product of two matrices
	 *	@param[in] r_t_mat is the right-hand side multiplicand
	 *	@return Returns elementwise product of this matrix with the argument.
	 */
	Derived t_ElementwiseProduct(TConstArgType(_TyStorage) r_t_mat) const
	{
		Derived t_tmp;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_tmp/*._TyStorage::f*/[i][j] = _TyStorage::f[i][j] * r_t_mat/*._TyStorage::f*/[i][j];
		}
		return t_tmp;
	}

	/**
	 *	@brief calculates elementwise ratio of two matrices
	 *	@param[in] r_t_mat is the right-hand side dividend
	 *	@return Returns elementwise ratio of this matrix with the argument.
	 */
	Derived t_ElementwiseRatio(TConstArgType(_TyStorage) r_t_mat) const
	{
		Derived t_tmp;
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				t_tmp/*._TyStorage::f*/[i][j] = _TyStorage::f[i][j] / r_t_mat/*._TyStorage::f*/[i][j];
		}
		return t_tmp;
	}

	/**
	 *	@brief calculates sum of two matrices inplace
	 *	@param[in] r_t_mat is the right-hand side summand
	 *	@return Returns reference to this.
	 */
	TNonConstResultType(Derived) operator +=(TConstArgType(_TyStorage) r_t_mat)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] += r_t_mat/*._TyStorage::f*/[i][j];
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief calculates difference of two matrices inplace
	 *	@param[in] r_t_mat is the right-hand side summand
	 *	@return Returns reference to this.
	 */
	TNonConstResultType(Derived) operator -=(TConstArgType(_TyStorage) r_t_mat)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] -= r_t_mat/*._TyStorage::f*/[i][j];
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief multiplies this matrix elements by another matrix, in elementwise fashion
	 *	@param[in] r_t_mat is the right-hand side multiplicand
	 *	@return Returns reference to this.
	 */
	TNonConstResultType(Derived) ElementwiseMultiply(TConstArgType(_TyStorage) r_t_mat)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] *= r_t_mat/*._TyStorage::f*/[i][j];
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief divides this matrix elements by another matrix, in elementwise fashion
	 *	@param[in] r_t_mat is the right-hand side dividend
	 *	@return Returns reference to this.
	 */
	TNonConstResultType(Derived) ElementwiseDivide(TConstArgType(_TyStorage) r_t_mat)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] /= r_t_mat/*._TyStorage::f*/[i][j];
		}
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief calculates multiplication of a matrix and scalar, stores the results in this
	 *
	 *	@param[in] r_t_mat1 is the matrix to be multiplied
	 *	@param[in] f_factor is the multiplier
	 */
	void Product_Of(TConstArgType(_TyStorage) r_t_mat1, _TyScalar f_factor)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] = r_t_mat1/*._TyStorage::f*/[i][j] * f_factor;
		}
	}

	/**
	 *	@brief calculates multiplication of two matrices, stores the results in this
	 *
	 *	@param[in] r_t_mat1 is the first matrix to be multiplied
	 *	@param[in] r_t_mat2 is the second matrix to be multiplied
	 */
	void Product_Of(TConstArgType(_TyStorage) r_t_mat1, TConstArgType(_TyStorage) r_t_mat2)
	{
		_ASSERTE((Derived*)this != &r_t_mat1);
		_ASSERTE((Derived*)this != &r_t_mat2); // have to use temp matrix, or operator *=()

		_ASSERTE(n_column_num == n_row_num); // only works for square matrices
		// both operands have the same dimensions

		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j) {
				_TyStorage::f[i][j] = r_t_mat1/*._TyStorage::f*/[0][j] * r_t_mat2/*._TyStorage::f*/[i][0];
				for(int k = 1; k < n_row_num; ++ k) // unroll
					_TyStorage::f[i][j] += r_t_mat1/*._TyStorage::f*/[k][j] * r_t_mat2/*._TyStorage::f*/[i][k];
			}
		}
	}

	/**
	 *	@brief calculates multiplication of two matrices, stores the results in this
	 *		(version for non-square operand matrices)
	 *
	 *	@param[in] r_t_mat1 is the first matrix to be multiplied
	 *	@param[in] r_t_mat2 is the second matrix to be multiplied
	 */
#if 0 // not supported by MSVC60
#ifdef __VECTOR_PASS_BY_REFERENCE
	template <const int n_common_dim>
	void Product_Of(const CMatrixStorage<_TyScalar, n_column_num, n_common_dim> &r_t_mat1,
		const CMatrixStorage<_TyScalar, n_common_dim, n_row_num> &r_t_mat2)
#else // __VECTOR_PASS_BY_REFERENCE
	template <const int n_common_dim>
	void Product_Of(CMatrixStorage<_TyScalar, n_column_num, n_common_dim> r_t_mat1,
		CMatrixStorage<_TyScalar, n_common_dim, n_row_num> r_t_mat2)
#endif // __VECTOR_PASS_BY_REFERENCE
	{
		_ASSERTE(this != &r_t_mat1);
		_ASSERTE(this != &r_t_mat2); // have to use temp matrix, or operator *=()

		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j) {
				_ASSERTE(n_common_dim > 0);
				_TyStorage::f[i][j] = r_t_mat1/*._TyStorage::f*/[0][j] * r_t_mat2/*._TyStorage::f*/[i][0];
				for(int k = 1; k < n_common_dim; ++ k) // unroll
					_TyStorage::f[i][j] += r_t_mat1/*._TyStorage::f*/[k][j] * r_t_mat2/*._TyStorage::f*/[i][k];
			}
		}
	}
#endif // 0

	/**
	 *	@brief multiplies this matrix by a matrix
	 *	@param[in] r_t_mat1 is the right-hand side of the product
	 *	@return Returns reference to this.
	 *	@note This requires temporary storage, more optimized versions may exits.
	 */
	TNonConstResultType(Derived) operator *=(TConstArgType(_TyStorage) r_t_mat1)
	{
		Derived tmp = static_cast<Derived&>(*this);
		Product_Of(tmp, r_t_mat1);
		return static_cast<Derived&>(*this);
	}

	/**
	 *	@brief calculates inplace transpose of this matrix
	 *	@note This requires temporary storage, more optimized versions may exits.
	 */
	inline void Transpose()
	{
		_ASSERTE(n_column_num == n_row_num); // only works for square matrices
		Derived tmp = static_cast<Derived&>(*this);
		tmp.Transpose_To(*this);
	}

#if 0 // this is duplicate and incorrect (except if n_column_num == n_row_num)
	/**
	 *	@brief transposes this matrix
	 *	@param[out] r_dest is the output for the transposed matrix
	 */
	void Transpose_To(_TyStorage &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use Transpose() instead

		_ASSERTE(n_column_num == n_row_num); // only works for square matrices
		// both operands have the same dimensions

		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest/*._TyStorage::f*/[i][j] = _TyStorage::f[j][i];
		}
	}
#endif //0

	/**
	 *	@brief transposes this matrix
	 *	@param[out] r_dest is the output for the transposed matrix
	 */
	void Transpose_To(typename _TyStorage::_TyTransposeType &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use Transpose() instead

		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest[i][j] = _TyStorage::f[j][i];
		}
	}

#if 0 // this is duplicate and incorrect (except if n_column_num == n_row_num)
	/**
	 *	@brief calculates transpose of a matrix
	 *	@param[in] r_src is the input matrix
	 */
	inline void Transpose_Of(TConstArgType(_TyStorage) r_src)
	{
		_ASSERTE(n_column_num == n_row_num); // only works for square matrices
		r_src.Transpose_To(static_cast<Derived&>(*this));
	}
#endif // 0

	/**
	 *	@brief calculates transpose of a matrix
	 *	@param[in] r_src is the input matrix
	 */
	inline void Transpose_Of(TConstArgType(typename _TyStorage::_TyTransposeType) r_src)
	{
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				_TyStorage::f[i][j] = r_src[j][i];
		}
	}

	/**
	 *	@brief calculates transpose of this matrix
	 *	@return Returns transpose of this matrix.
	 *	@note This only works for square matrices (don't know the derived type for others).
	 */
	inline Derived t_Transpose() const
	{
		_ASSERTE(n_column_num == n_row_num); // only works for square matrices
		Derived t_transpose;
		Transpose_To(t_transpose);
		return t_transpose;
	}

	/**
	 *	@brief calculates inplace LU decomposition with partial pivoting (assumes square matrix)
	 *
	 *	@param[out] p_row_perm is row permutation vector (must be allocated
	 *		to number of this matrix columns)
	 *	@param[in] n_row_perm_size is the number of elements the row permutation
	 *		vector is allocated to (must match number of this matrix columns)
	 *
	 *	@return Returns true on success, false on numerical issue.
	 */
	inline bool LU(int *p_row_perm, size_t _UNUSED(n_row_perm_size))
	{
		_ASSERTE(n_column_num == n_row_num);
		_ASSERTE(p_row_perm && n_row_perm_size == n_column_num);

		_TyScalar piv_weights[n_column_num];
		for(int j = 0; j < n_column_num; ++ j) {
			_TyScalar f_max = 0;
			for(int i = 0; i < n_column_num; ++ i) {
				_TyScalar f_temp = (_TyScalar)fabs(_TyStorage::f[i][j]);
				if(f_temp > f_max)
					f_max = f_temp;
			}
			if(f_max == 0) {
				//fprintf(stderr, "error: singular\n");
				return false;
			}
			piv_weights[j] = 1 / f_max;
		}
		// calculate (approximate) implicit scaling for pivoting

		for(int i = 0; i < n_column_num; ++ i)
			p_row_perm[i] = i;
		// begin with identity perm

		for(int k = 0; k < n_column_num; ++ k) {
			int n_pivot = k;
			_TyScalar f_max = piv_weights[k] * (_TyScalar)fabs(_TyStorage::f[k][k]);
			for(int i = k + 1; i < n_column_num; ++ i) {
				_TyScalar f_temp = piv_weights[i] * (_TyScalar)fabs(_TyStorage::f[k][i]);
				if(f_temp > f_max) {
					f_max = f_temp;
					n_pivot = i;
				}
			}
			// partial pivoting

			if(k != n_pivot) {
				_ASSERTE(n_pivot > k); // must be one of rows below the diagonal element

				for(int i = 0; i < n; ++ i)
					std::swap(_TyStorage::f[i][k], _TyStorage::f[i][n_pivot]);
				// swap a row of LU

				piv_weights[n_pivot] = piv_weights[k];
				// will not need piv_weights[k] anymore, no need to swap, just copy k below

				std::swap(p_row_perm[k], p_row_perm[n_pivot]);
				// modify the permutation, based on the pivoting
			}
			// swap rows n_pivot and k, if not the same row

#if 0
			if(!_TyStorage::f[k][k])
				_TyStorage::f[k][k] = 1e-37f;
			// can avoid INF and NaN in the solution by replacing zero pivot by a large number
#else // 0
			if(!_TyStorage::f[k][k])
				return false; // numerical issue, the resulting matrix would contain special values
#endif // 0

			for(int i = k + 1; i < n; ++ i) {
				_TyScalar f_temp = (_TyStorage::f[k][i] /= _TyStorage::f[k][k]); // solve using LU? it *is* on diagonal, maybe we can already regard it as a finished LU decomposition
				// divide by pivot

				for(int j = k + 1; j < n; ++ j)
					_TyStorage::f[j][i] -= f_temp * _TyStorage::f[j][k]; // causes scatter in sparse version
				// reduce the rest of the matrix
			}
		}
		// perform the LU decomposition

		return true;
	}

	/**
	 *	@brief calculates Cholesky decomposition U^TU (assumes square positive definite matrix)
	 *	@return Returns true on success, false on numerical issue (not positive definite).
	 */
	bool Cholesky()
	{
		_ASSERTE(n_column_num == n_row_num);

		for(int k = 0; k < n_column_num; ++ k) {
			if(_TyStorage::f[k][k] < 0) {
				//fprintf(stderr, "error: not pos def\n");
				return false;
			}
			_TyScalar l_kk = (_TyScalar)sqrt(double(_TyStorage::f[k][k])); // accesses the diagonal
			_TyStorage::f[k][k] = l_kk;
			for(int i = k + 1; i < n_column_num; ++ i)
				_TyStorage::f[i][k] /= l_kk; // writes a column below diagonal, doesn't introduce any fill-in
			for(int j = k + 1; j < n_column_num; ++ j) {
				for(int i = j; i < n_column_num; ++ i)
					_TyStorage::f[i][j] -= _TyStorage::f[i][k] * _TyStorage::f[j][k]; // writes to lower triangle, a lot of scatter :(
			}
		}
		for(int k = 0; k < n_column_num; ++ k) {
			for(int i = k + 1; i < n_column_num; ++ i)
				_TyStorage::f[k][i] = 0; // have to manually clear the upper half
		}
		// the [c][r] operator is actually transposed so this calculates upper Cholesky

		return true;
	}

	/**
	 *	@brief forward-substitution, assumes square upper-triangular matrix
	 *
	 *	@param[in] p_rhs is right-hand-side vector (allocated to n_rhs_size elements)
	 *	@param[in] n_rhs_size is size of the right-hand-side vector (must equal n_Column_Num())
	 */
	void UTSolve(_TyScalar *p_rhs, int _UNUSED(n_rhs_size)) const
	{
		_ASSERTE(n_row_num == n_rhs_size && n_column_num == n_row_num);
		for(int j = 0; j < n_column_num; ++ j) {
			for(int i = 0; i < j; ++ i)
				p_rhs[j] -= _TyStorage::f[j][i] * p_rhs[i];
			p_rhs[j] /= _TyStorage::f[j][j];
		}
	}

	/**
	 *	@brief back-substitution, assumes square upper-triangular matrix
	 *
	 *	@param[in] p_rhs is right-hand-side vector (allocated to n_rhs_size elements)
	 *	@param[in] n_rhs_size is size of the right-hand-side vector (must equal n_Column_Num())
	 */
	void USolve(_TyScalar *p_rhs, int _UNUSED(n_rhs_size)) const
	{
		_ASSERTE(n_row_num == n_rhs_size && n_column_num == n_row_num);
		for(int j = n_column_num; j > 0;) {
			-- j;
			p_rhs[j] /= _TyStorage::f[j][j];
			for(int i = 0; i < j; ++ i)
				p_rhs[i] -= _TyStorage::f[j][i] * p_rhs[j];
		}
	}

	/**
	 *	@brief forward-substitution, assumes square lower-triangular matrix
	 *
	 *	@param[in] p_rhs is right-hand-side vector (allocated to n_rhs_size elements)
	 *	@param[in] n_rhs_size is size of the right-hand-side vector (must equal n_Column_Num())
	 */
	void LSolve(_TyScalar *p_rhs, int _UNUSED(n_rhs_size)) const
	{
		_ASSERTE(n_row_num == n_rhs_size && n_column_num == n_row_num);
		for(int j = 0; j < n_column_num; ++ j) {
			p_rhs[j] /= _TyStorage::f[j][j];
			for(int i = j + 1; i < n_column_num; ++ i)
				p_rhs[i] -= _TyStorage::f[j][i] * p_rhs[j];
		}
	}

	/**
	 *	@brief back-substitution, assumes square lower-triangular matrix
	 *
	 *	@param[in] p_rhs is right-hand-side vector (allocated to n_rhs_size elements)
	 *	@param[in] n_rhs_size is size of the right-hand-side vector (must equal n_Column_Num())
	 *
	 *	@note In case the matrix has unit diagonal (such as L from LU decomposition,
	 *		LSolve_UnitDiag() is slightly faster).
	 */
	void LTSolve(_TyScalar *p_rhs, int _UNUSED(n_rhs_size)) const
	{
		_ASSERTE(n_row_num == n_rhs_size && n_column_num == n_row_num);
		for(int j = n_column_num; j > 0;) {
			-- j;
			// here

			for(int i = j + 1; i < n_column_num; ++ i)
				p_rhs[j] -= _TyStorage::f[j][i] * p_rhs[i];
			p_rhs[j] /= _TyStorage::f[j][j];
			// note that this is untested
		}
	}

	/**
	 *	@brief forward-substitution, assumes square lower-triangular matrix with unit diagonal
	 *
	 *	@param[in] p_rhs is right-hand-side vector (allocated to n_rhs_size elements)
	 *	@param[in] n_rhs_size is size of the right-hand-side vector (must equal n_Column_Num())
	 *
	 *	@note This function does not access the diagonal, does not verify its unit-ness.
	 */
	void LSolve_UnitDiag(_TyScalar *p_rhs, int _UNUSED(n_rhs_size)) const
	{
		_ASSERTE(n_row_num == n_rhs_size && n_column_num == n_row_num);
		for(int j = 0; j < n; ++ j) {
			//_ASSERTE(_TyStorage::f[j][j] == 1);
			// does not always have to be true, packed LU stores something else at the diagonal

			//p_rhs[j] /= _TyStorage::f[j][j];
			// the diagonal is unit, no need to be accessed

			for(int i = j + 1; i < n; ++ i) {
				_ASSERTE(b_IsLowerElem(j, i));
				p_rhs[i] -= _TyStorage::f[j][i] * p_rhs[j];
			}
		}
	}

	/**
	 *	@brief solves a system of linear equations U^TUx = b using Cholesky decomposition
	 *
	 *	@param[out] p_lhs is left-hand-side vector (x; allocated to n_lhs_size elements)
	 *	@param[in] n_lhs_size is size of the left-hand-side vector (must equal t_U.n_Column_Num())
	 *	@param[in] p_rhs is right-hand-side vector (b; allocated to n_rhs_size elements)
	 *	@param[in] n_rhs_size is size of the right-hand-side vector (must equal t_U.n_Column_Num())
	 *
	 *	@note This function can work inplace, p_lhs and p_rhs can point to the same array.
	 *	@note This function assumes that this is Cholesky decomposition (square, upper triangular).
	 */
	inline void CholSolve(_TyScalar *p_lhs, int _UNUSED(n_lhs_size),
		const _TyScalar *p_rhs, int n_rhs_size)
	{
		_ASSERTE(n_row_num == n_lhs_size && n_row_num == n_rhs_size && n_column_num == n_row_num);
		// vectors must be same dimensions and the matrix must be square

		_TyScalar *p_result = p_lhs;
		if(p_rhs != p_lhs) // can work inplace
			memcpy(p_result, p_rhs, n_rhs_size * sizeof(_TyScalar));
		// allocate result and copy data (works inplace)

		UTSolve(p_result, n_rhs_size);
		USolve(p_result, n_rhs_size);
		// solve
	}

	/**
	 *	@brief solves a system of linear equations LUx = b using previously calculated LU decomposition
	 *
	 *	@param[out] p_lhs is left-hand-side vector (x; allocated to n_lhs_size elements)
	 *	@param[in] n_lhs_size is size of the left-hand-side vector (must equal number of this matrix columns)
	 *	@param[in] p_row_perm is row permutation given by pivoting (allocated to n_row_perm_size)
	 *	@param[in] n_row_perm_size is size of row permutation (must equal number of this matrix columns)
	 *	@param[in] p_rhs is right-hand-side vector (b; allocated to n_rhs_size elements)
	 *	@param[in] n_rhs_size is size of the right-hand-side vector (must equal number of this matrix columns)
	 *
	 *	@note This function can not work inplace.
	 */
	void LU_Solve(_TyScalar *p_lhs, int _UNUSED(n_lhs_size),
		int *p_row_perm, size_t n_row_perm_size,
		const _TyScalar *p_rhs, int _UNUSED(n_rhs_size))
	{
		_ASSERTE(n_column_num == n_row_num);
		_ASSERTE(p_row_perm && n_row_perm_size == n_column_num);
		_ASSERTE(p_lhs && n_lhs_size == n_column_num);
		_ASSERTE(p_rhs && n_rhs_size == n_column_num);
		_ASSERTE(p_lhs != p_rhs); // can't work inplace

		for(size_t i = 0; i < n_column_num; ++ i)
			p_lhs[i] = p_rhs[p_row_perm[i]];
		// permute rows accordingly

		LSolve_UnitDiag(p_lhs, n_column_num);
		USolve(p_lhs, n_column_num);
		// L / U solve
	}

	/**
	 *	@brief calculates determinant of the matrix, using the LU decomposition
	 *
	 *	@param[in] p_row_perm is row permutation given by pivoting
	 *		(contains n_row_perm_size elements)
	 *	@param[in] n_row_perm_size is size of row permutation (must equal n_Column_Num())
	 *
	 *	@return Returns the value of the determinant of the original matrix A = LU.
	 *
	 *	@note This matrix must be the either the U matrix or packed
	 *		LU matrix of the LU decomposition.
	 */
	_TyScalar f_LU_Determinant(const int *p_row_perm, size_t _UNUSED(n_row_perm_size)) const
	{
		_ASSERTE(n_column_num == n_row_num);
		_ASSERTE(p_row_perm && n_row_perm_size == n_column_num);

		int n_sign = 1;
		int row_perm[n_column_num];
		_ASSERTE(sizeof(p_row_perm[0]) == sizeof(row_perm[0])); // make sure the type is the same
		memcpy(row_perm, p_row_perm, n_row_perm_size * sizeof(int));
		// determine sign of the determinant by tracking back the
		// number of changes in the permutation (the permutation is destroyed)

		double f_det = 1;
		// prone to under / overflow with big matrices, could use multiple precision accumulator

		for(int i = 0; i < n_column_num; ++ i) {
			if(row_perm[i] != i) {
				do {
					std::swap(row_perm[row_perm[i]], row_perm[i]);
					n_sign = -n_sign;
				} while(row_perm[i] != i); // could be permuted multiple times
			}

			f_det *= _TyStorage::f[i][i];
		}

		return _TyScalar(n_sign * f_det);
	}

	/**
	 *	@brief calculates the determinant
	 *	@return Returns determinant of this matrix
	 *	@note This uses LU decomposition.
	 */
	_TyScalar f_Determinant() const
	{
		_ASSERTE(n_column_num == n_row_num);
		Derived t_LU = *this;
		int p_row_perm[n_column_num];
		t_LU.LU(row_perm, n_column_num); // ignore errors here
		return f_LU_Determinant(p_row_perm, n_column_num);
	}

	// todo - need to implement inverse using gausselim
	// todo - QR
};

/**
 *	@brief implementation of matrix inverse methods (uses adjugate matrix method)
 *
 *	@tparam CScalar is scalar type
 *	@tparam n_cols is number of matrix columns
 *	@tparam n_rows is number of matrix rows
 *	@tparam CDerived is name of the final derived class (must have f_Minor() and f_Determinant() implemented)
 */
template <class CScalar, const int n_cols, const int n_rows, class CDerived>
class CMatrixInverseImpl {
public:
	typedef CDerived _TyDerived; /**< @brief derived matrix type */
	typedef CScalar _TyScalar; /**< @brief scalar data type */

	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = n_cols, /**< @brief number of matrix columns */
		n_row_num = n_rows /**< @brief number of matrix rows */
	};

public:
	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted matrix
	 */
	void Invert_To(CDerived &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use Invert_Inplace() instead

		_TyScalar f_inv_det = 1 / static_cast<const CDerived&>(*this).f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest[j][i] = static_cast<const CDerived&>(*this).f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted and transposed matrix
	 */
	void InvertTranspose_To(CDerived &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use InvertTranspose_Inplace() instead

		_TyScalar f_inv_det = 1 / static_cast<const CDerived&>(*this).f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest[i][j] = static_cast<const CDerived&>(*this).f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@return Returns inverse of this.
	 */
	inline CDerived t_Inverse() const
	{
		CDerived t_inverse;
		Invert_To(t_inverse);
		return t_inverse;
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@return Returns inverse transpose of this.
	 */
	inline CDerived t_InverseTranspose() const
	{
		CDerived t_inverse;
		InvertTranspose_To(t_inverse);
		return t_inverse;
	}

	/**
	 *	@brief calculates inverse matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted
	 */
	inline void Inverse_Of(TConstArgType(CDerived) r_src)
	{
		r_src.Invert_To(static_cast<CDerived&>(*this));
	}

	/**
	 *	@brief calculates inverse transpose matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted and transposed
	 */
	inline void InverseTranspose_Of(TConstArgType(CDerived) r_src)
	{
		r_src.InvertTranspose_To(static_cast<CDerived&>(*this));
	}

	/**
	 *	@brief inverts this matrix (inplace; uses adjugate matrix method)
	 */
	inline void Invert_Inplace()
	{
		CDerived t_inverse = static_cast<const CDerived&>(*this);
		t_inverse.Invert_To(static_cast<CDerived&>(*this));
	}

	/**
	 *	@brief inverts and transposes this matrix (inplace; uses adjugate matrix method)
	 */
	inline void InvertTranspose_Inplace()
	{
		CDerived t_inverse = static_cast<const CDerived&>(*this);
		t_inverse.InvertTranspose_To(static_cast<CDerived&>(*this));
	}
};

/**
 *	@brief a simple 2 by 2 matrix class
 */
template <class _Ty>
struct Matrix2 : public CMatrixBase<_Ty, 2, 2, Matrix2<_Ty> >, public CMatrixInverseImpl<_Ty, 2, 2, Matrix2<_Ty> > {
public:
	typedef CMatrixBase<_Ty, 2, 2, Matrix2<_Ty> > _TyBase; /**< @brief base type of the matrix */
	typedef CMatrixStorage<_Ty, 2, 2 > _TyStorage; /**< @brief storage type of the matrix */

	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = _TyBase::n_column_num, /**< @brief number of matrix columns */
		n_row_num = _TyBase::n_row_num /**< @brief number of matrix rows */
	};

	typedef typename _TyBase::_TyScalar _TyScalar; /**< @brief scalar data type */

public:
	/**
	 *	@brief creates a scaling matrix
	 *	@param[in] f_scale is scaling factor (same for x and y)
	 */
	inline void Scaling(_TyScalar f_scale)
	{
		static_cast<_TyBase&>(*this).Scaling(f_scale); // otherwise not visible
	}

	/**
	 *	@brief creates a scaling matrix
	 *
	 *	@param[in] f_scale_x is scaling factor for x
	 *	@param[in] f_scale_y is scaling factor for y
	 */
	void Scaling(_TyScalar f_scale_x, _TyScalar f_scale_y)
	{
		_TyStorage::f[0][0] = f_scale_x;
		_TyStorage::f[0][1] = 0;
		_TyStorage::f[1][0] = 0;
		_TyStorage::f[1][1] = f_scale_y;
	}

	/**
	 *	@brief creates a scaling matrix
	 *	@param[in] r_v_scale is vector of scaling factors for x, y and z
	 */
	void Scaling(TConstArgType(Vector2<_TyScalar>) r_v_scale)
	{
		_TyStorage::f[0][0] = r_v_scale.x;
		_TyStorage::f[0][1] = 0;
		_TyStorage::f[1][0] = 0;
		_TyStorage::f[1][1] = r_v_scale.y;
	}

	/**
	 *	@brief creates a matrix for the in-plane rotation
	 *	@param[in] f_angle is angle in radians
	 */
	void Rotation(_TyScalar f_angle)
	{
		_TyScalar f_sin = _TyScalar(sin(f_angle));
		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyStorage::f[0][0] = f_cos;
		_TyStorage::f[0][1] = f_sin;
		_TyStorage::f[1][1] = f_cos;
		_TyStorage::f[1][0] = -f_sin;
	}

	/**
	 *	@brief applies scaling to this matrix
	 *	@param[in] f_scale is scaling factor (same for x and y)
	 */
	void Scale(_TyScalar f_scale)
	{
		Matrix2<_Ty> t_tmp;
		t_tmp.Scaling(f_scale);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies scaling to this matrix
	 *
	 *	@param[in] f_scale_x is scaling factor for x
	 *	@param[in] f_scale_y is scaling factor for y
	 */
	void Scale(_TyScalar f_scale_x, _TyScalar f_scale_y)
	{
		Matrix2<_Ty> t_tmp;
		t_tmp.Scaling(f_scale_x, f_scale_y);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies scaling to this matrix
	 *	@param[in] r_v_scale is vector of scaling factors for x, y and z
	 */
	void Scale(TConstArgType(Vector2<_TyScalar>) r_v_scale)
	{
		Matrix2<_Ty> t_tmp;
		t_tmp.Scaling(r_v_scale);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies in-plane rotation to this matrix
	 *	@param[in] f_angle is angle in radians
	 */
	void Rotate(_TyScalar f_angle)
	{
		Matrix2<_Ty> t_tmp;
		t_tmp.Rotation(f_angle);
		*this *= t_tmp;
	}

	/**
	 *	@brief calculates multiplication of a matrix and scalar
	 *	@param[in] f_factor is the multiplier
	 *	@return Returns element-wise multiplication of this matrix and f_factor.
	 */
	inline Matrix2 operator *(_TyScalar f_factor) const
	{
		Matrix2<_Ty> t_mult;
		t_mult.Product_Of(*this, f_factor);
		return t_mult;
	}

	/**
	 *	@brief calculates multiplication of two matrices
	 *	@param[in] r_t_mat is the second matrix to be multiplied
	 *	@return Returns the result of multiplication of this matrix and r_t_mat.
	 */
	inline Matrix2 operator *(TConstArgType(Matrix2) r_t_mat) const
	{
		Matrix2<_Ty> t_mult;
		t_mult.Product_Of(*this, r_t_mat);
		return t_mult;
	}

	/**
	 *	@brief vector-matrix multiplication
	 *	@param[in] r_v_vec is the vector to be multiplied (transformed) by this matrix
	 *	@return Returns the product of this and r_v_vec.
	 */
	Vector2<_TyScalar> operator *(TConstArgType(Vector2<_TyScalar>) r_v_vec) const
	{
		return Vector2<_TyScalar>(r_v_vec.x * _TyStorage::f[0][0] + r_v_vec.y * _TyStorage::f[1][0],
			r_v_vec.x * _TyStorage::f[0][1] + r_v_vec.y * _TyStorage::f[1][1]);
	}

	/**
	 *	@brief calculates a submatrix determinant
	 *
	 *	@param[in] n_col is zero-based index of the column to be left out
	 *	@param[in] n_row is zero-based index of the row to be left out
	 *
	 *	@return Returns a determinant of this matrix with the specified column and row left out.
	 *	@note The result is not multiplied by (-1)^(n_col + n_row).
	 */
	inline _TyScalar f_Subdet(int n_col, int n_row) const
	{
		int i0 = 1 - n_col;
		int j0 = 1 - n_row;
		return _TyStorage::f[i0][j0]; // !! this was swapped
	}

	/**
	 *	@brief calculates the first matrix minor
	 *
	 *	@param[in] n_col is zero-based index of the column to be left out
	 *	@param[in] n_row is zero-based index of the row to be left out
	 *
	 *	@return Returns a determinant of this matrix with the specified
	 *		column and row left out, multiplied by (-1)^(n_col + n_row).
	 */
	inline _TyScalar f_Minor(int n_col, int n_row) const
	{
		return (1 - 2 * ((n_col + n_row) & 1)) * f_Subdet(n_col, n_row);
	}

	/**
	 *	@brief calculates the determinant
	 *	@return Returns determinant of this matrix
	 */
	_TyScalar f_Determinant() const
	{
		return _TyStorage::f[0][0] * _TyStorage::f[1][1] - _TyStorage::f[0][1] * _TyStorage::f[1][0];
	}

#if 0 // implementation in CMatrixInverseImpl
	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted matrix
	 */
	void Invert_To(Matrix2 &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use Invert_Inplace() instead

		_TyScalar f_inv_det = 1 / f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest._TyStorage::f[j][i] = f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts this matrix (inplace; uses adjugate matrix method)
	 */
	inline void Invert_Inplace()
	{
		Matrix2 t_tmp = *this;
		t_tmp.Invert_To(*this);
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted and transposed matrix
	 */
	void InvertTranspose_To(Matrix2 &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use InvertTranspose_Inplace() instead

		_TyScalar f_inv_det = 1 / f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest._TyStorage::f[i][j] = f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts and transposes this matrix (inplace; uses adjugate matrix method)
	 */
	inline void InvertTranspose_Inplace()
	{
		Matrix2 t_tmp = *this;
		t_tmp.InvertTranspose_To(*this);
	}

	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@return Returns inverse of this.
	 */
	inline Matrix2 t_Inverse() const
	{
		Matrix2 t_tmp;
		Invert_To(t_tmp);
		return t_tmp;
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@return Returns inverse transpose of this.
	 */
	inline Matrix2 t_InverseTranspose() const
	{
		Matrix2 t_tmp;
		InvertTranspose_To(t_tmp);
		return t_tmp;
	}

	/**
	 *	@brief calculates inverse matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted
	 */
	inline void Inverse_Of(TConstArgType(Matrix2<_TyScalar>) r_src)
	{
		r_src.Invert_To(*this);
	}

	/**
	 *	@brief calculates inverse transpose matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted and transposed
	 */
	inline void InverseTranspose_Of(TConstArgType(Matrix2<_TyScalar>) r_src)
	{
		r_src.InvertTranspose_To(*this);
	}
#endif // 0

	/**
	 *	@brief finds eigenvalues of this matrix
	 *	@return Returns eigenvalues of this matrix in descending order.
	 */
	Vector2<_TyScalar> v_EigenValues() const
	{
		_TyScalar a = 1, b = f_Trace(), c = f_Determinant();
		_TyScalar f_disc = (_TyScalar)sqrt(double(b * b - 4 * a * c));
		_TyScalar f_root0 = (b - f_disc) * .5f;
		_TyScalar f_root1 = (b + f_disc) * .5f;
		return Vector2<_TyScalar>(f_root1, f_root0);
	}

	/**
	 *	@brief gets local x-axis vector
	 *	@return Returns local x-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector2<_TyScalar> v_Right() const
	{
		return Vector2<_TyScalar>(_TyStorage::f[0][0], _TyStorage::f[0][1]);
	}

	/**
	 *	@brief gets local y-axis vector
	 *	@return Returns local y-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector2<_TyScalar> v_Up() const
	{
		return Vector2<_TyScalar>(_TyStorage::f[1][0], _TyStorage::f[1][1]);
	}

	/**
	 *	@brief sets x-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Right(TConstArgType(Vector2<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[0][0] = r_v_vec.x;
		_TyStorage::f[0][1] = r_v_vec.y;
	}

	/**
	 *	@brief sets y-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Up(TConstArgType(Vector2<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[1][0] = r_v_vec.x;
		_TyStorage::f[1][1] = r_v_vec.y;
	}
};

/**
 *	@brief two by two floating-point matrix
 */
typedef Matrix2<float> Matrix2f;

/**
 *	@brief two by two double precision floating-point matrix
 */
typedef Matrix2<double> Matrix2d;

/**
 *	@brief a simple 3 by 3 matrix class
 */
template <class _Ty>
struct Matrix3 : public CMatrixBase<_Ty, 3, 3, Matrix3<_Ty> >, public CMatrixInverseImpl<_Ty, 3, 3, Matrix3<_Ty> > {
public:
	typedef CMatrixBase<_Ty, 3, 3, Matrix3<_Ty> > _TyBase; /**< @brief base type of the matrix */
	typedef CMatrixStorage<_Ty, 3, 3> _TyStorage;

	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = _TyBase::n_column_num, /**< @brief number of matrix columns */
		n_row_num = _TyBase::n_row_num /**< @brief number of matrix rows */
	};

	typedef typename _TyBase::_TyScalar _TyScalar; /**< @brief scalar data type */

public:
	/**
	 *	@brief creates a scaling matrix
	 *	@param[in] f_scale is scaling factor (same for x and y)
	 */
	inline void Scaling(_TyScalar f_scale)
	{
		static_cast<_TyBase&>(*this).Scaling(f_scale); // otherwise not visible
	}

	/**
	 *	@brief creates a scaling matrix
	 *
	 *	@param[in] f_scale_x is scaling factor for x
	 *	@param[in] f_scale_y is scaling factor for y
	 *	@param[in] f_scale_z is scaling factor for z
	 */
	void Scaling(_TyScalar f_scale_x, _TyScalar f_scale_y, _TyScalar f_scale_z)
	{
		_TyBase::Identity();
		_TyStorage::f[0][0] = f_scale_x;
		_TyStorage::f[1][1] = f_scale_y;
		_TyStorage::f[2][2] = f_scale_z;
	}

	/**
	 *	@brief creates a scaling matrix
	 *	@param[in] r_v_scale is vector of scaling factors for x, y and z
	 */
	void Scaling(TConstArgType(Vector3<_TyScalar>) r_v_scale)
	{
		_TyBase::Identity();
		_TyStorage::f[0][0] = r_v_scale.x;
		_TyStorage::f[1][1] = r_v_scale.y;
		_TyStorage::f[2][2] = r_v_scale.z;
	}

	/**
	 *	@brief creates a matrix for rotation arround x-axis
	 *	@param[in] f_angle is angle in radians
	 */
	void RotationX(_TyScalar f_angle)
	{
		_TyBase::Identity();
		_TyScalar f_sin = _TyScalar(sin(f_angle));
		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyStorage::f[2][1] = -f_sin;
		_TyStorage::f[2][2] = f_cos;
		_TyStorage::f[1][1] = f_cos;
		_TyStorage::f[1][2] = f_sin;
	}

	/**
	 *	@brief creates a matrix for rotation arround y-axis
	 *	@param[in] f_angle is angle in radians
	 */
	void RotationY(_TyScalar f_angle)
	{
		_TyBase::Identity();
		_TyScalar f_sin = _TyScalar(sin(f_angle));
		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyStorage::f[0][0] = f_cos;
		_TyStorage::f[0][2] = -f_sin;
		_TyStorage::f[2][2] = f_cos;
		_TyStorage::f[2][0] = f_sin;
	}

	/**
	 *	@brief creates a matrix for rotation arround z-axis
	 *	@param[in] f_angle is angle in radians
	 */
	void RotationZ(_TyScalar f_angle)
	{
		_TyBase::Identity();
		_TyScalar f_sin = _TyScalar(sin(f_angle));
		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyStorage::f[0][0] = f_cos;
		_TyStorage::f[0][1] = f_sin;
		_TyStorage::f[1][1] = f_cos;
		_TyStorage::f[1][0] = -f_sin;
	}

	/**
	 *	@brief creates a matrix for rotation arround an arbitrary axis
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] f_axis_x is the first element of the rotation axis vector
	 *	@param[in] f_axis_y is the second element of the rotation axis vector
	 *	@param[in] f_axis_z is the third element of the rotation axis vector
	 */
	void Rotation(_TyScalar f_angle, _TyScalar f_axis_x, _TyScalar f_axis_y, _TyScalar f_axis_z)
	{
		// formula for rotation matrix around arbitrary axis:
		// R = uuT + cos(f_angle) * (I - uuT) + sin(f_angle)S

		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyScalar f_o_m_cos = 1 - f_cos;
		_TyScalar f_axis_x_o_m_cos = f_axis_x * f_o_m_cos;
		_TyScalar f_axis_y_o_m_cos = f_axis_y * f_o_m_cos;
		_TyScalar f_axis_z_o_m_cos = f_axis_z * f_o_m_cos;
		_TyStorage::f[0][0] = f_axis_x * f_axis_x_o_m_cos + f_cos;
		_TyStorage::f[0][1] = f_axis_x * f_axis_y_o_m_cos;
		_TyStorage::f[0][2] = f_axis_x * f_axis_z_o_m_cos;
		_TyStorage::f[1][0] = f_axis_y * f_axis_x_o_m_cos;
		_TyStorage::f[1][1] = f_axis_y * f_axis_y_o_m_cos + f_cos;
		_TyStorage::f[1][2] = f_axis_y * f_axis_z_o_m_cos;
		_TyStorage::f[2][0] = f_axis_z * f_axis_x_o_m_cos;
		_TyStorage::f[2][1] = f_axis_z * f_axis_y_o_m_cos;
		_TyStorage::f[2][2] = f_axis_z * f_axis_z_o_m_cos + f_cos;
		// R = uu^T * (1 - cos(f_angle)) + cos(f_angle) * I + ...

		_TyScalar f_sin = _TyScalar(sin(f_angle));
		f_axis_x *= f_sin;
		f_axis_y *= f_sin;
		f_axis_z *= f_sin;
		_TyStorage::f[1][0] -= f_axis_z;
		_TyStorage::f[0][1] += f_axis_z;
		_TyStorage::f[2][0] += f_axis_y;
		_TyStorage::f[0][2] -= f_axis_y;
		_TyStorage::f[2][1] -= f_axis_x;
		_TyStorage::f[1][2] += f_axis_x;
		// ...  + sin(f_angle)S
	}

	/**
	 *	@brief creates a matrix for rotation arround an arbitrary axis
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] r_v_axis is the rotation axis vector
	 */
	void Rotation(_TyScalar f_angle, TConstArgType(Vector3<_TyScalar>) r_v_axis)
	{
		// formula for rotation matrix around arbitrary axis:
		// R = uuT + cos(f_angle) * (I - uuT) + sin(f_angle)S

		_TyScalar f_cos = _TyScalar(cos(f_angle));
		Vector3<_TyScalar> v_u_o_m_cos(r_v_axis * (1 - f_cos));
		for(int i = 0; i < 3; ++ i) {
			for(int j = 0; j < 3; ++ j)
				_TyStorage::f[i][j] = r_v_axis[i] * v_u_o_m_cos[j] + ((i == j)? f_cos : 0);
		}
		// R = uu^T * (1 - cos(f_angle)) + cos(f_angle) * I + ...

		Vector3<_TyScalar> v_s(r_v_axis * _TyScalar(sin(f_angle)));
		_TyStorage::f[1][0] -= v_s.z;
		_TyStorage::f[0][1] += v_s.z;
		_TyStorage::f[2][0] += v_s.y;
		_TyStorage::f[0][2] -= v_s.y;
		_TyStorage::f[2][1] -= v_s.x;
		_TyStorage::f[1][2] += v_s.x;
		// ...  + sin(f_angle)S
	}

	/**
	 *	@brief applies scaling on this matrix
	 *	@param[in] f_scale is scaling factor (same for x, y and z)
	 */
	void Scale(_TyScalar f_scale)
	{
		Matrix3 t_tmp;
		t_tmp.Scaling(f_scale);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies scaling on this matrix
	 *
	 *	@param[in] f_scale_x is scaling factor for x
	 *	@param[in] f_scale_y is scaling factor for y
	 *	@param[in] f_scale_z is scaling factor for z
	 */
	void Scale(_TyScalar f_scale_x, _TyScalar f_scale_y, _TyScalar f_scale_z)
	{
		Matrix3 t_tmp;
		t_tmp.Scaling(f_scale_x, f_scale_y, f_scale_z);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies scaling on this matrix
	 *	@param[in] r_v_scale is vector of scaling factors for x, y and z
	 */
	void Scale(TConstArgType(Vector3<_TyScalar>) r_v_scale)
	{
		Matrix3 t_tmp;
		t_tmp.Scaling(r_v_scale);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround x-axis on this matrix
	 *	@param[in] f_angle is angle in radians
	 */
	void RotateX(_TyScalar f_angle)
	{
		Matrix3 t_tmp;
		t_tmp.RotationX(f_angle);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround y-axis on this matrix
	 *	@param[in] f_angle is angle in radians
	 */
	void RotateY(_TyScalar f_angle)
	{
		Matrix3 t_tmp;
		t_tmp.RotationY(f_angle);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround z-axis on this matrix
	 *	@param[in] f_angle is angle in radians
	 */
	void RotateZ(_TyScalar f_angle)
	{
		Matrix3 t_tmp;
		t_tmp.RotationZ(f_angle);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround an arbitrary axis on this matrix
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] f_axis_x is the first element of the rotation axis vector
	 *	@param[in] f_axis_y is the second element of the rotation axis vector
	 *	@param[in] f_axis_z is the third element of the rotation axis vector
	 */
	void Rotate(_TyScalar f_angle, _TyScalar f_axis_x, _TyScalar f_axis_y, _TyScalar f_axis_z)
	{
		Matrix3 t_tmp;
		t_tmp.Rotation(f_angle, f_axis_x, f_axis_y, f_axis_z);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround an arbitrary axis on this matrix
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] r_v_axis is the rotation axis vector
	 */
	void Rotate(_TyScalar f_angle, TConstArgType(Vector3<_TyScalar>) r_v_axis)
	{
		Matrix3 t_tmp;
		t_tmp.Rotation(f_angle, r_v_axis);
		*this *= t_tmp;
	}

	/**
	 *	@brief calculates multiplication of a matrix and scalar
	 *	@param[in] f_factor is the multiplier
	 *	@return Returns element-wise multiplication of this matrix and f_factor.
	 */
	inline Matrix3 operator *(_TyScalar f_factor) const
	{
		Matrix3 t_mult;
		t_mult.Product_Of(*this, f_factor);
		return t_mult;
	}

	/**
	 *	@brief calculates multiplication of two matrices
	 *	@param[in] r_t_mat is the second matrix to be multiplied
	 *	@return Returns the result of multiplication of this matrix and r_t_mat.
	 */
	inline Matrix3 operator *(TConstArgType(Matrix3) r_t_mat) const
	{
		Matrix3 t_mult;
		t_mult.Product_Of(*this, r_t_mat);
		return t_mult;
	}

	/**
	 *	@brief vector-matrix multiplication
	 *	@param[in] r_v_vec is the vector to be multiplied (transformed) by this matrix
	 *	@return Returns the product of this and r_v_vec.
	 */
	Vector3<_TyScalar> operator *(TConstArgType(Vector3<_TyScalar>) r_v_vec) const
	{
		return Vector3<_TyScalar>(r_v_vec.x * _TyStorage::f[0][0] + r_v_vec.y * _TyStorage::f[1][0] + r_v_vec.z * _TyStorage::f[2][0],
			r_v_vec.x * _TyStorage::f[0][1] + r_v_vec.y * _TyStorage::f[1][1] + r_v_vec.z * _TyStorage::f[2][1],
			r_v_vec.x * _TyStorage::f[0][2] + r_v_vec.y * _TyStorage::f[1][2] + r_v_vec.z * _TyStorage::f[2][2]);
	}

	/**
	 *	@brief calculates a submatrix determinant
	 *
	 *	@param[in] n_col is zero-based index of the column to be left out
	 *	@param[in] n_row is zero-based index of the row to be left out
	 *
	 *	@return Returns a determinant of this matrix with the specified column and row left out.
	 *	@note The result is not multiplied by (-1)^(n_col + n_row).
	 */
	_TyScalar f_Subdet(int n_col, int n_row) const
	{
		int i0 = (n_row <= 0) + 0, i1 = (n_row <= 1) + 1;
		int j0 = (n_col <= 0) + 0, j1 = (n_col <= 1) + 1;

		return _TyStorage::f[j0][i0] * _TyStorage::f[j1][i1] - _TyStorage::f[j0][i1] * _TyStorage::f[j1][i0];
	}

	/**
	 *	@brief calculates the first matrix minor
	 *
	 *	@param[in] n_col is zero-based index of the column to be left out
	 *	@param[in] n_row is zero-based index of the row to be left out
	 *
	 *	@return Returns a determinant of this matrix with the specified
	 *		column and row left out, multiplied by (-1)^(n_col + n_row).
	 */
	inline _TyScalar f_Minor(int n_col, int n_row) const
	{
		return (1 - 2 * ((n_col + n_row) & 1)) * f_Subdet(n_col, n_row);
	}

	/**
	 *	@brief calculates the determinant
	 *	@return Returns determinant of this matrix
	 */
	_TyScalar f_Determinant() const
	{
		return (_TyStorage::f[0][0] * _TyStorage::f[1][1] * _TyStorage::f[2][2] +
			    _TyStorage::f[1][0] * _TyStorage::f[2][1] * _TyStorage::f[0][2] +
			    _TyStorage::f[0][1] * _TyStorage::f[1][2] * _TyStorage::f[2][0]) -
			   (_TyStorage::f[0][2] * _TyStorage::f[1][1] * _TyStorage::f[2][0] +
			    _TyStorage::f[0][1] * _TyStorage::f[1][0] * _TyStorage::f[2][2] +
			    _TyStorage::f[0][0] * _TyStorage::f[1][2] * _TyStorage::f[2][1]);
	}

#if 0 // implementation in CMatrixInverseImpl
	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted matrix
	 */
	void Invert_To(Matrix3 &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use Invert_Inplace() instead

		_TyScalar f_inv_det = 1 / f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest._TyStorage::f[j][i] = f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts this matrix (inplace; uses adjugate matrix method)
	 */
	inline void Invert_Inplace()
	{
		Matrix3 t_tmp = *this;
		t_tmp.Invert_To(*this);
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted and transposed matrix
	 */
	void InvertTranspose_To(Matrix3 &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use InvertTranspose_Inplace() instead

		_TyScalar f_inv_det = 1 / f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest._TyStorage::f[i][j] = f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts and transposes this matrix (inplace; uses adjugate matrix method)
	 */
	inline void InvertTranspose_Inplace()
	{
		Matrix3 t_tmp = *this;
		t_tmp.InvertTranspose_To(*this);
	}

	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@return Returns inverse of this.
	 */
	inline Matrix3 t_Inverse() const
	{
		Matrix3 t_tmp;
		Invert_To(t_tmp);
		return t_tmp;
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@return Returns inverse transpose of this.
	 */
	inline Matrix3 t_InverseTranspose() const
	{
		Matrix3 t_tmp;
		InvertTranspose_To(t_tmp);
		return t_tmp;
	}

	/**
	 *	@brief calculates inverse matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted
	 */
	inline void Inverse_Of(TConstArgType(Matrix3<_TyScalar>) r_src)
	{
		r_src.Invert_To(*this);
	}

	/**
	 *	@brief calculates inverse transpose matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted and transposed
	 */
	inline void InverseTranspose_Of(TConstArgType(Matrix3<_TyScalar>) r_src)
	{
		r_src.InvertTranspose_To(*this);
	}
#endif // 0

	Vector3<_TyScalar> v_EigenValues() const
	{
		if(_TyStorage::f[1][0] == _TyStorage::f[0][1] &&
		   _TyStorage::f[2][0] == _TyStorage::f[0][2] &&
		   _TyStorage::f[2][1] == _TyStorage::f[1][2])
			return v_EigenValues_Sym();
		// this will be mostly unused, since the hard comparisons

		_TyScalar a = 1, b = -f_Trace();
		Matrix3 A2 = (*this) * (*this); // todo - avoid calculating the whole product, only the diagonal is neded (and it is not equal to ((*this) * v_Diag()).f_Sum())
		_TyScalar c = (A2.f_Trace() + b) / 2, d = -f_Determinant();
		if(fabs(d) == 0) {
			float f_disc = b * b - 4 * a * c;
			if(f_disc < 0)
				return Vector3<_TyScalar>(0, 0, 0); // only two complex roots // todo - throw exception? return NaN?
			else {
				f_disc = (_TyScalar)sqrt(double(f_disc));
				_TyScalar f_real_eig1 = (-b - f_disc) / (2 * a);
				_TyScalar f_real_eig0 = (-b + f_disc) / (2 * a);
				return Vector3<_TyScalar>(f_real_eig0, f_real_eig1, f_real_eig1);
			}
			// the constant is 0, divide the whole equation by x to get a simple quadratic equation
		} else {
			// it is a full cubic equation

			_TyScalar q = (3 * _c - _b * _b) / 9;
			_TyScalar r = (-27 * _d + _b * (9 * _c - 2 * _b * _b)) / 54;
			_TyScalar disc = q * q * q + r * r;

			_TyScalar term1 = _b / 3;
			if(disc > 0) {
				_TyScalar s = r + sqrt(double(disc)), t = r - sqrt(double(disc));
				s = _TyScalar((s < 0)? -pow(double(-s), 1.0 / 3) : pow(double(s), 1.0 / 3));
				t = _TyScalar((t < 0)? -pow(double(-t), 1.0 / 3) : pow(double(t), 1.0 / 3));
				_TyScalar f_real_eig0 = -term1 + s + t;
				term1 += (s + t) / 2;
				_TyScalar f_real_eig1 = f_real_eig2 = -term1;
				term1 = _TyScalar(sqrt(3.0)) * (s - t) / 2;
				if(fabs(term1) > 0) { // can this even happen?
					//p_im_eig1 = -term1;
					//p_im_eig2 = term1;
					return Vector3<_TyScalar>(f_real_eig0, f_real_eig0, f_real_eig0); // the other two are complex conjugates
				} else {
					return (f_real_eig0 >= f_real_eig1)? Vector3<_TyScalar>(f_real_eig0, f_real_eig1, f_real_eig2) :
						Vector3<_TyScalar>(f_real_eig1, f_real_eig2, f_real_eig0);
				}
				// one real root and one double real or complex root
			}
			// one root real, two may be complex

			if(fabs(disc) == 0) {
				_TyScalar r13 = _TyScalar((r < 0)? -pow(double(-r), 1.0 / 3) : pow(double(r), 1.0 / 3));
				_TyScalar f_real_eig0 = -term1 + 2 * r13;
				_TyScalar f_real_eig1 = f_real_eig2 = -(r13 + term1);
				return (f_real_eig0 >= f_real_eig1)? Vector3<_TyScalar>(f_real_eig0, f_real_eig1, f_real_eig2) :
					Vector3<_TyScalar>(f_real_eig1, f_real_eig2, f_real_eig0);
			}
			// all roots real, at least two are equal

			q = -q;
			_TyScalar dum1 = _TyScalar(acos(r / sqrt(double(q * q * q))));
			_TyScalar r13 = 2 * _TyScalar(sqrt(double(q)));
			_TyScalar f_real_eig0 = -term1 + r13 * _TyScalar(cos(dum1 / 3));
			_TyScalar f_real_eig1 = -term1 + r13 * _TyScalar(cos((dum1 + 2 * f_pi) / 3));
			_TyScalar f_real_eig2 = -term1 + r13 * _TyScalar(cos((dum1 + 4 * f_pi) / 3));
			// todo - sort, return, then test
			// Only option left is that all roots are real and unequal (to get here, q < 0)
		}
	}

	/**
	 *	@brief finds eigenvalues of this matrix using only the upper triangular part (assumes a symmetric matrix)
	 *	@return Returns eigenvalues of this matrix in descending order.
	 *	@note This assumes the eigenvaluies are real, which is always true for symmetric matrices.
	 */
	Vector3<_TyScalar> v_EigenValues_Sym() const
	{
		/*_ASSERTE(_TyStorage::f[1][0] == _TyStorage::f[0][1] &&
			_TyStorage::f[2][0] == _TyStorage::f[0][2] &&
			_TyStorage::f[2][1] == _TyStorage::f[1][2]);*/
		// the matrix is assumed symetric
		// don't check, may have other data in the lower triangle, or it may not be calculated at all

		_TyScalar f_offdiag = _TyStorage::f[1][0] * _TyStorage::f[1][0] +
			_TyStorage::f[2][0] * _TyStorage::f[2][0] + _TyStorage::f[2][1] * _TyStorage::f[2][1];
		// calculates norm of the offdiagonal values

		Vector3<_TyScalar> v_diag(_TyStorage::f[0][0], _TyStorage::f[1][1], _TyStorage::f[2][2]);
		if(f_offdiag == 0) {
			std::sort(&v_diag[0], &v_diag[3]); // sort (may be an overkill for three values)
			std::swap(v_diag[0], v_diag[2]); // swap to be in ascending order
			return v_diag;
		} else {
			_TyScalar q = f_Trace();
			v_diag -= q; // will reuse the values in determinant calculation
			_TyScalar f_diag = v_diag.f_Length2() + 2 * f_offdiag;
			_TyScalar p = (_TyScalar)sqrt(double(f_diag / 6));
			/*Matrix3<_TyScalar> B = (*this); // t_odo - avoid formation of B in the first place, just unroll the determinant
			for(int i = 0; i < 3; ++ i)
				B[i][i] -= q;*/
			_TyScalar f_det = (           v_diag.x *            v_diag.y *            v_diag.z +
							   _TyStorage::f[1][0] * _TyStorage::f[2][1] * _TyStorage::f[0][2] +
							   _TyStorage::f[0][1] * _TyStorage::f[1][2] * _TyStorage::f[2][0]) -
							  (_TyStorage::f[0][2] *            v_diag.y * _TyStorage::f[2][0] +
							   _TyStorage::f[0][1] * _TyStorage::f[1][0] *            v_diag.z +
							              v_diag.x * _TyStorage::f[1][2] * _TyStorage::f[2][1]);
			//B /= p; // do this later, will save operations
			_TyScalar r = f_det/*B.f_Determinant()*/ / (p * p * p); // p^3, as dimension of the matrix is 3

			_TyScalar f_phi = (r > -1)? (r < 1)? _TyScalar(acos(r) / 3) : 0 : f_pi / 3;
			// "safe" acos, slight over / underflows from the [-1, 1] range may occur
			// due to limited-precision floating point arithmetic

			Vector3<_TyScalar> eig;
			eig.x = q + 2 * p * _TyScalar(cos(phi));
			eig.z = q + 2 * p * _TyScalar(cos(phi + 2 * _TyScalar(f_pi) / 3));
			eig.y = 3 * q - eig.x - eig.z; // since trace(A) = eig1 + eig2 + eig3
			_ASSERTE(eig.x >= eig.y && eig.y >= eig.z); // make sure the order is correct
			return eig;
		}
		// note this is slightly optimized code from http://en.wikipedia.org/wiki/Eigenvalue_algorithm#3.C3.973_matrices
		// todo - write test cases for LU, Cholesky, solutions and eigenvalues
		// todo - see how to recover eigenvectors
	}

	/**
	 *	@brief gets local x-axis vector
	 *	@return Returns local x-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector3<_TyScalar> v_Right() const
	{
		return Vector3<_TyScalar>(_TyStorage::f[0][0], _TyStorage::f[0][1], _TyStorage::f[0][2]);
	}

	/**
	 *	@brief gets local y-axis vector
	 *	@return Returns local y-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector3<_TyScalar> v_Up() const
	{
		return Vector3<_TyScalar>(_TyStorage::f[1][0], _TyStorage::f[1][1], _TyStorage::f[1][2]);
	}

	/**
	 *	@brief gets local z-axis vector
	 *	@return Returns local z-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector3<_TyScalar> v_Dir() const
	{
		return Vector3<_TyScalar>(_TyStorage::f[2][0], _TyStorage::f[2][1], _TyStorage::f[2][2]);
	}

	/**
	 *	@brief sets x-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Right(TConstArgType(Vector3<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[0][0] = r_v_vec.x;
		_TyStorage::f[0][1] = r_v_vec.y;
		_TyStorage::f[0][2] = r_v_vec.z;
	}

	/**
	 *	@brief sets y-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Up(TConstArgType(Vector3<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[1][0] = r_v_vec.x;
		_TyStorage::f[1][1] = r_v_vec.y;
		_TyStorage::f[1][2] = r_v_vec.z;
	}

	/**
	 *	@brief sets z-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Dir(TConstArgType(Vector3<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[2][0] = r_v_vec.x;
		_TyStorage::f[2][1] = r_v_vec.y;
		_TyStorage::f[2][2] = r_v_vec.z;
	}
};

/**
 *	@brief three by three floating-point matrix
 */
typedef Matrix3<float> Matrix3f;

/**
 *	@brief three by three double precision floating-point matrix
 */
typedef Matrix3<double> Matrix3d;

/**
 *	@brief a simple 4x4 column-major order matrix class (suitable for use with OpenGL)
 */
template <class _Ty>
struct Matrix4 : public CMatrixBase<_Ty, 4, 4, Matrix4<_Ty> >, public CMatrixInverseImpl<_Ty, 4, 4, Matrix4<_Ty> > {
public:
	typedef CMatrixBase<_Ty, 4, 4, Matrix4<_Ty> > _TyBase; /**< @brief base type of the matrix */
	typedef CMatrixStorage<_Ty, 4, 4> _TyStorage;

	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = _TyBase::n_column_num, /**< @brief number of matrix columns */
		n_row_num = _TyBase::n_row_num /**< @brief number of matrix rows */
	};

	typedef typename _TyBase::_TyScalar _TyScalar; /**< @brief scalar data type */

public:
	/**
	 *	@brief gets rotation part of the matrix
	 *	@return Returns the rotation part of the matrix.
	 */
	Matrix3<_TyScalar> t_RotationPart() const
	{
		Matrix3<_TyScalar> t_rot;
		for(int i = 0; i < 3; ++ i) {
			for(int j = 0; j < 3; ++ j)
				t_rot[i][j] = _TyStorage::f[i][j];
		}
		return t_rot;
	}

	/**
	 *	@brief sets rotation part of the matrix
	 *	@param[in] r_v_rot is a rotation matrix.
	 */
	void Set_RotationPart(TConstArgType(Matrix3<_TyScalar>) r_t_rot)
	{
		for(int i = 0; i < 3; ++ i) {
			for(int j = 0; j < 3; ++ j)
				_TyStorage::f[i][j] = r_t_rot[i][j];
		}
	}

	/**
	 *	@brief creates a translation matrix
	 *	@param[in] r_v_translate is translation vector
	 */
	void Translation(TConstArgType(Vector3<_TyScalar>) r_v_translate)
	{
		for(int j = 0; j < 4; ++ j) {
			for(int i = 0; i < 3; ++ i)
				_TyStorage::f[i][j] = (_TyScalar)(i == j);
		}
		_TyStorage::f[3][0] = r_v_translate.x;
		_TyStorage::f[3][1] = r_v_translate.y;
		_TyStorage::f[3][2] = r_v_translate.z;
		_TyStorage::f[3][3] = 1;
	}

	/**
	 *	@brief creates a translation matrix
	 *
	 *	@param[in] f_translate_x is the first element of the translation vector
	 *	@param[in] f_translate_y is the second element of the translation vector
	 *	@param[in] f_translate_z is the third element of the translation vector
	 */
	void Translation(_TyScalar f_translate_x, _TyScalar f_translate_y, _TyScalar f_translate_z)
	{
		for(int j = 0; j < 4; ++ j) {
			for(int i = 0; i < 3; ++ i)
				_TyStorage::f[i][j] = (_TyScalar)(i == j);
		}
		_TyStorage::f[3][0] = f_translate_x;
		_TyStorage::f[3][1] = f_translate_y;
		_TyStorage::f[3][2] = f_translate_z;
		_TyStorage::f[3][3] = 1;
	}

	/**
	 *	@brief creates a scaling matrix
	 *	@param[in] f_scale is scaling factor (same for x, y and z)
	 */
	inline void Scaling(_TyScalar f_scale)
	{
		for(int j = 0; j < 4; ++ j) {
			for(int i = 0; i < 3; ++ i)
				_TyStorage::f[i][j] = (_TyScalar)(i == j)? f_scale : _TyScalar(0);
		}
		_TyStorage::f[3][0] = 0;
		_TyStorage::f[3][1] = 0;
		_TyStorage::f[3][2] = 0;
		_TyStorage::f[3][3] = 1; // the homogenous coordinate must stay equal to 1
	}

	/**
	 *	@brief creates a scaling matrix
	 *
	 *	@param[in] f_scale_x is scaling factor for x
	 *	@param[in] f_scale_y is scaling factor for y
	 *	@param[in] f_scale_z is scaling factor for z
	 */
	void Scaling(_TyScalar f_scale_x, _TyScalar f_scale_y, _TyScalar f_scale_z)
	{
		_TyBase::Identity();
		_TyStorage::f[0][0] = f_scale_x;
		_TyStorage::f[1][1] = f_scale_y;
		_TyStorage::f[2][2] = f_scale_z;
	}

	/**
	 *	@brief creates a scaling matrix
	 *	@param[in] r_v_scale is vector of scaling factors for x, y and z
	 */
	void Scaling(TConstArgType(Vector3<_TyScalar>) r_v_scale)
	{
		_TyBase::Identity();
		_TyStorage::f[0][0] = r_v_scale.x;
		_TyStorage::f[1][1] = r_v_scale.y;
		_TyStorage::f[2][2] = r_v_scale.z;
	}

	/**
	 *	@brief creates a matrix for rotation arround x-axis
	 *	@param[in] f_angle is angle in radians
	 */
	void RotationX(_TyScalar f_angle)
	{
		_TyBase::Identity();
		_TyScalar f_sin = _TyScalar(sin(f_angle));
		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyStorage::f[2][1] = -f_sin;
		_TyStorage::f[2][2] = f_cos;
		_TyStorage::f[1][1] = f_cos;
		_TyStorage::f[1][2] = f_sin;
	}

	/**
	 *	@brief creates a matrix for rotation arround y-axis
	 *	@param[in] f_angle is angle in radians
	 */
	void RotationY(_TyScalar f_angle)
	{
		_TyBase::Identity();
		_TyScalar f_sin = _TyScalar(sin(f_angle));
		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyStorage::f[0][0] = f_cos;
		_TyStorage::f[0][2] = -f_sin;
		_TyStorage::f[2][2] = f_cos;
		_TyStorage::f[2][0] = f_sin;
	}

	/**
	 *	@brief creates a matrix for rotation arround z-axis
	 *	@param[in] f_angle is angle in radians
	 */
	void RotationZ(_TyScalar f_angle)
	{
		_TyBase::Identity();
		_TyScalar f_sin = _TyScalar(sin(f_angle));
		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyStorage::f[0][0] = f_cos;
		_TyStorage::f[0][1] = f_sin;
		_TyStorage::f[1][1] = f_cos;
		_TyStorage::f[1][0] = -f_sin;
	}

	/**
	 *	@brief creates a matrix for rotation arround an arbitrary axis
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] f_axis_x is the first element of the rotation axis vector
	 *	@param[in] f_axis_y is the second element of the rotation axis vector
	 *	@param[in] f_axis_z is the third element of the rotation axis vector
	 */
	void Rotation(_TyScalar f_angle, _TyScalar f_axis_x, _TyScalar f_axis_y, _TyScalar f_axis_z)
	{
		// formula for rotation matrix around arbitrary axis:
		// R = uuT + cos(f_angle) * (I - uuT) + sin(f_angle)S

		_TyScalar f_cos = _TyScalar(cos(f_angle));
		_TyScalar f_o_m_cos = 1 - f_cos;
		_TyScalar f_axis_x_o_m_cos = f_axis_x * f_o_m_cos;
		_TyScalar f_axis_y_o_m_cos = f_axis_y * f_o_m_cos;
		_TyScalar f_axis_z_o_m_cos = f_axis_z * f_o_m_cos;
		_TyStorage::f[0][0] = f_axis_x * f_axis_x_o_m_cos + f_cos;
		_TyStorage::f[0][1] = f_axis_x * f_axis_y_o_m_cos;
		_TyStorage::f[0][2] = f_axis_x * f_axis_z_o_m_cos;
		_TyStorage::f[0][3] = 0;
		_TyStorage::f[1][0] = f_axis_y * f_axis_x_o_m_cos;
		_TyStorage::f[1][1] = f_axis_y * f_axis_y_o_m_cos + f_cos;
		_TyStorage::f[1][2] = f_axis_y * f_axis_z_o_m_cos;
		_TyStorage::f[1][3] = 0;
		_TyStorage::f[2][0] = f_axis_z * f_axis_x_o_m_cos;
		_TyStorage::f[2][1] = f_axis_z * f_axis_y_o_m_cos;
		_TyStorage::f[2][2] = f_axis_z * f_axis_z_o_m_cos + f_cos;
		_TyStorage::f[2][3] = 0;
		_TyStorage::f[3][0] = 0;
		_TyStorage::f[3][1] = 0;
		_TyStorage::f[3][2] = 0;
		_TyStorage::f[3][3] = 1;
		// R = uu^T * (1 - cos(f_angle)) + cos(f_angle) * I + ...

		_TyScalar f_sin = _TyScalar(sin(f_angle));
		f_axis_x *= f_sin;
		f_axis_y *= f_sin;
		f_axis_z *= f_sin;
		_TyStorage::f[1][0] -= f_axis_z;
		_TyStorage::f[0][1] += f_axis_z;
		_TyStorage::f[2][0] += f_axis_y;
		_TyStorage::f[0][2] -= f_axis_y;
		_TyStorage::f[2][1] -= f_axis_x;
		_TyStorage::f[1][2] += f_axis_x;
		// ...  + sin(f_angle)S
	}

	/**
	 *	@brief creates a matrix for rotation arround an arbitrary axis
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] r_v_axis is the rotation axis vector
	 */
	void Rotation(_TyScalar f_angle, TConstArgType(Vector3<_TyScalar>) r_v_axis)
	{
		// formula for rotation matrix around arbitrary axis:
		// R = uuT + cos(f_angle) * (I - uuT) + sin(f_angle)S

		_TyScalar f_cos = _TyScalar(cos(f_angle));
		Vector3<_TyScalar> v_u_o_m_cos(r_v_axis * (1 - f_cos));
		for(int i = 0; i < 3; ++ i) {
			for(int j = 0; j < 3; ++ j)
				_TyStorage::f[i][j] = r_v_axis[i] * v_u_o_m_cos[j] + ((i == j)? f_cos : 0);
			_TyStorage::f[i][3] = 0;
			_TyStorage::f[3][i] = 0;
		}
		_TyStorage::f[3][3] = 1;
		// R = uu^T * (1 - cos(f_angle)) + cos(f_angle) * I + ...

		Vector3<_TyScalar> v_s(r_v_axis * _TyScalar(sin(f_angle)));
		_TyStorage::f[1][0] -= v_s.z;
		_TyStorage::f[0][1] += v_s.z;
		_TyStorage::f[2][0] += v_s.y;
		_TyStorage::f[0][2] -= v_s.y;
		_TyStorage::f[2][1] -= v_s.x;
		_TyStorage::f[1][2] += v_s.x;
		// ...  + sin(f_angle)S
	}

	/**
	 *	@brief applies translation on this matrix
	 *
	 *	@param[in] f_translate_x is the first element of the translation vector
	 *	@param[in] f_translate_y is the second element of the translation vector
	 *	@param[in] f_translate_z is the third element of the translation vector
	 */
	inline void Translate(_TyScalar f_translate_x, _TyScalar f_translate_y, _TyScalar f_translate_z)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Translation(f_translate_x, f_translate_y, f_translate_z);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies translation on this matrix
	 *	@param[in] r_v_translate is translation vector
	 */
	inline void Translate(TConstArgType(Vector3<_TyScalar>) r_v_translate)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Translation(r_v_translate);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies scaling on this matrix
	 *	@param[in] f_scale is scaling factor (same for x, y and z)
	 */
	inline void Scale(_TyScalar f_scale)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Scaling(f_scale);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies scaling on this matrix
	 *
	 *	@param[in] f_scale_x is scaling factor for x
	 *	@param[in] f_scale_y is scaling factor for y
	 *	@param[in] f_scale_z is scaling factor for z
	 */
	inline void Scale(_TyScalar f_scale_x, _TyScalar f_scale_y, _TyScalar f_scale_z)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Scaling(f_scale_x, f_scale_y, f_scale_z);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies scaling on this matrix
	 *	@param[in] r_v_scale is vector of scaling factors for x, y and z
	 */
	inline void Scale(TConstArgType(Vector3<_TyScalar>) r_v_scale)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Scaling(r_v_scale);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround x-axis on this matrix
	 *	@param[in] f_angle is angle in radians
	 */
	inline void RotateX(_TyScalar f_angle)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.RotationX(f_angle);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround y-axis on this matrix
	 *	@param[in] f_angle is angle in radians
	 */
	inline void RotateY(_TyScalar f_angle)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.RotationY(f_angle);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround z-axis on this matrix
	 *	@param[in] f_angle is angle in radians
	 */
	inline void RotateZ(_TyScalar f_angle)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.RotationZ(f_angle);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround an arbitrary axis on this matrix
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] f_axis_x is the first element of the rotation axis vector
	 *	@param[in] f_axis_y is the second element of the rotation axis vector
	 *	@param[in] f_axis_z is the third element of the rotation axis vector
	 */
	inline void Rotate(_TyScalar f_angle, _TyScalar f_axis_x, _TyScalar f_axis_y, _TyScalar f_axis_z)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Rotation(f_angle, f_axis_x, f_axis_y, f_axis_z);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies rotation arround an arbitrary axis on this matrix
	 *
	 *	@param[in] f_angle is angle in radians
	 *	@param[in] r_v_axis is the rotation axis vector
	 */
	inline void Rotate(_TyScalar f_angle, TConstArgType(Vector3<_TyScalar>) r_v_axis)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Rotation(f_angle, r_v_axis);
		*this *= t_tmp;
	}

	/**
	 *	@brief applies 3x3 rotation matrix on this matrix
	 *	@param[in] r_t_rot is a rotation matrix
	 */
	void Rotate(TConstArgType(Matrix3<_TyScalar>) r_t_rot)
	{
		Matrix4<_TyScalar> t_tmp;
		t_tmp.Set_RotationPart(r_t_rot);
		for(int i = 0; i < 3; ++ i) {
			t_tmp[3][i] = 0;
			t_tmp[i][3] = 0;
		}
		t_tmp[3][3] = 1;
		*this *= t_tmp;
	}

	/**
	 *	@brief calculates multiplication of a matrix and scalar
	 *	@param[in] f_factor is the multiplier
	 *	@return Returns element-wise multiplication of this matrix and f_factor.
	 */
	inline Matrix4<_TyScalar> operator *(_TyScalar f_factor) const
	{
		//Matrix4<_TyScalar> t_mult = *this;
		//t_mult *= f_factor;
		Matrix4<_TyScalar> t_mult;
		t_mult.Product_Of(*this, f_factor);
		return t_mult;
	}

	/**
	 *	@brief calculates multiplication of two matrices
	 *	@param[in] r_t_mat is the second matrix to be multiplied
	 *	@return Returns the result of multiplication of this matrix and r_t_mat.
	 */
	inline Matrix4<_TyScalar> operator *(TConstArgType(Matrix4<_TyScalar>) r_t_mat) const
	{
		//Matrix4<_TyScalar> t_mult = *this;
		//t_mult *= f_factor;
		Matrix4<_TyScalar> t_mult;
		t_mult.Product_Of(*this, r_t_mat);
		return t_mult;
	}

	/**
	 *	@brief element-wise multiplies this matrix by a scalar
	 *	@param[in] f_factor is the multiplier
	 *	@return Returns reference to this.
	 *	@note This is faster, than <tt>*this = *this * f_factor;</tt>.
	 */
	inline TNonConstResultType(Matrix4<_TyScalar>) operator *=(_TyScalar f_factor)
	{
		return static_cast<_TyBase&>(*this) *= f_factor; // otherwise not visible
	}

	/**
	 *	@brief multiplies this matrix by another matrix
	 *	@param[in] r_t_mat is the second matrix to be multiplied
	 *	@return Returns reference to this.
	 *	@note This is better optimized, than just <tt>*this = *this * r_t_mat;</tt>.
	 */
	TNonConstResultType(Matrix4<_TyScalar>) operator *=(TConstArgType(Matrix4<_TyScalar>) r_t_mat)
	{
		_TyScalar f_temp_1_0 = _TyStorage::f[0][0] * r_t_mat._TyStorage::f[1][0] + _TyStorage::f[1][0] * r_t_mat[1][1] +
			_TyStorage::f[2][0] * r_t_mat[1][2] + _TyStorage::f[3][0] * r_t_mat[1][3];
		_TyScalar f_temp_2_0 = _TyStorage::f[0][0] * r_t_mat[2][0] + _TyStorage::f[1][0] * r_t_mat[2][1] +
			_TyStorage::f[2][0] * r_t_mat[2][2] + _TyStorage::f[3][0] * r_t_mat[2][3];
		_TyScalar f_temp_3_0 = _TyStorage::f[0][0] * r_t_mat[3][0] + _TyStorage::f[1][0] * r_t_mat[3][1] +
			_TyStorage::f[2][0] * r_t_mat[3][2] + _TyStorage::f[3][0] * r_t_mat[3][3];
		_TyStorage::f[0][0] = _TyStorage::f[0][0] * r_t_mat[0][0] + _TyStorage::f[1][0] * r_t_mat[0][1] +
			_TyStorage::f[2][0] * r_t_mat[0][2] + _TyStorage::f[3][0] * r_t_mat[0][3];
		_TyStorage::f[1][0] = f_temp_1_0;
		_TyStorage::f[2][0] = f_temp_2_0;
		_TyStorage::f[3][0] = f_temp_3_0;
		_TyScalar f_temp_1_1 = _TyStorage::f[0][1] * r_t_mat[1][0] + _TyStorage::f[1][1] * r_t_mat[1][1] +
			_TyStorage::f[2][1] * r_t_mat[1][2] + _TyStorage::f[3][1] * r_t_mat[1][3];
		_TyScalar f_temp_2_1 = _TyStorage::f[0][1] * r_t_mat[2][0] + _TyStorage::f[1][1] * r_t_mat[2][1] +
			_TyStorage::f[2][1] * r_t_mat[2][2] + _TyStorage::f[3][1] * r_t_mat[2][3];
		_TyScalar f_temp_3_1 = _TyStorage::f[0][1] * r_t_mat[3][0] + _TyStorage::f[1][1] * r_t_mat[3][1] +
			_TyStorage::f[2][1] * r_t_mat[3][2] + _TyStorage::f[3][1] * r_t_mat[3][3];
		_TyStorage::f[0][1] = _TyStorage::f[0][1] * r_t_mat[0][0] + _TyStorage::f[1][1] * r_t_mat[0][1] +
			_TyStorage::f[2][1] * r_t_mat[0][2] + _TyStorage::f[3][1] * r_t_mat[0][3];
		_TyStorage::f[1][1] = f_temp_1_1;
		_TyStorage::f[2][1] = f_temp_2_1;
		_TyStorage::f[3][1] = f_temp_3_1;
		_TyScalar f_temp_1_2 = _TyStorage::f[0][2] * r_t_mat[1][0] + _TyStorage::f[1][2] * r_t_mat[1][1] +
			_TyStorage::f[2][2] * r_t_mat[1][2] + _TyStorage::f[3][2] * r_t_mat[1][3];
		_TyScalar f_temp_2_2 = _TyStorage::f[0][2] * r_t_mat[2][0] + _TyStorage::f[1][2] * r_t_mat[2][1] +
			_TyStorage::f[2][2] * r_t_mat[2][2] + _TyStorage::f[3][2] * r_t_mat[2][3];
		_TyScalar f_temp_3_2 = _TyStorage::f[0][2] * r_t_mat[3][0] + _TyStorage::f[1][2] * r_t_mat[3][1] +
			_TyStorage::f[2][2] * r_t_mat[3][2] + _TyStorage::f[3][2] * r_t_mat[3][3];
		_TyStorage::f[0][2] = _TyStorage::f[0][2] * r_t_mat[0][0] + _TyStorage::f[1][2] * r_t_mat[0][1] +
			_TyStorage::f[2][2] * r_t_mat[0][2] + _TyStorage::f[3][2] * r_t_mat[0][3];
		_TyStorage::f[1][2] = f_temp_1_2;
		_TyStorage::f[2][2] = f_temp_2_2;
		_TyStorage::f[3][2] = f_temp_3_2;
		_TyScalar f_temp_1_3 = _TyStorage::f[0][3] * r_t_mat[1][0] + _TyStorage::f[1][3] * r_t_mat[1][1] +
			_TyStorage::f[2][3] * r_t_mat[1][2] + _TyStorage::f[3][3] * r_t_mat[1][3];
		_TyScalar f_temp_2_3 = _TyStorage::f[0][3] * r_t_mat[2][0] + _TyStorage::f[1][3] * r_t_mat[2][1] +
			_TyStorage::f[2][3] * r_t_mat[2][2] + _TyStorage::f[3][3] * r_t_mat[2][3];
		_TyScalar f_temp_3_3 = _TyStorage::f[0][3] * r_t_mat[3][0] + _TyStorage::f[1][3] * r_t_mat[3][1] +
			_TyStorage::f[2][3] * r_t_mat[3][2] + _TyStorage::f[3][3] * r_t_mat[3][3];
		_TyStorage::f[0][3] = _TyStorage::f[0][3] * r_t_mat[0][0] + _TyStorage::f[1][3] * r_t_mat[0][1] +
			_TyStorage::f[2][3] * r_t_mat[0][2] + _TyStorage::f[3][3] * r_t_mat[0][3];
		_TyStorage::f[1][3] = f_temp_1_3;
		_TyStorage::f[2][3] = f_temp_2_3;
		_TyStorage::f[3][3] = f_temp_3_3;
		// somewhat better optimized, requires less copying

		return *this;
	}

	/**
	 *	@brief vector-matrix multiplication
	 *	@param[in] r_v_vec is the vector to be multiplied (transformed) by this matrix
	 *	@return Returns the product of this and r_v_vec.
	 */
	Vector4<_TyScalar> operator *(TConstArgType(Vector4<_TyScalar>) r_v_vec) const
	{
		return Vector4<_TyScalar>(r_v_vec.x * _TyStorage::f[0][0] + r_v_vec.y * _TyStorage::f[1][0] +
						r_v_vec.z * _TyStorage::f[2][0] + r_v_vec.w * _TyStorage::f[3][0],
						r_v_vec.x * _TyStorage::f[0][1] + r_v_vec.y * _TyStorage::f[1][1] +
						r_v_vec.z * _TyStorage::f[2][1] + r_v_vec.w * _TyStorage::f[3][1],
						r_v_vec.x * _TyStorage::f[0][2] + r_v_vec.y * _TyStorage::f[1][2] +
						r_v_vec.z * _TyStorage::f[2][2] + r_v_vec.w * _TyStorage::f[3][2],
						r_v_vec.x * _TyStorage::f[0][3] + r_v_vec.y * _TyStorage::f[1][3] +
						r_v_vec.z * _TyStorage::f[2][3] + r_v_vec.w * _TyStorage::f[3][3]);
	}

	/**
	 *	@brief calculates transformation of a point by matrix
	 *	@param[in] r_v_vec is position of the point to be transformed by this matrix
	 *	@return Returns the product of this and r_v_vec.
	 *	@note Calling this is equivalent to multiplying this matrix by Vector4<_TyScalar>(r_v_vec, 1).
	 */
	Vector3<_TyScalar> v_Transform_Pos(TConstArgType(Vector3<_TyScalar>) r_v_vec) const
	{
		return Vector3<_TyScalar>(r_v_vec.x * _TyStorage::f[0][0] + r_v_vec.y * _TyStorage::f[1][0] + r_v_vec.z * _TyStorage::f[2][0] + _TyStorage::f[3][0],
						r_v_vec.x * _TyStorage::f[0][1] + r_v_vec.y * _TyStorage::f[1][1] + r_v_vec.z * _TyStorage::f[2][1] + _TyStorage::f[3][1],
						r_v_vec.x * _TyStorage::f[0][2] + r_v_vec.y * _TyStorage::f[1][2] + r_v_vec.z * _TyStorage::f[2][2] + _TyStorage::f[3][2]);
	}

	/**
	 *	@brief calculates transformation of a vector by matrix
	 *	@param[in] r_v_vec is direction of the vector to be transformed by this matrix
	 *	@return Returns the product of this and r_v_vec.
	 *	@note Calling this is equivalent to multiplying this matrix by Vector4<_TyScalar>(r_v_vec, 0).
	 */
	Vector3<_TyScalar> v_Transform_Dir(TConstArgType(Vector3<_TyScalar>) r_v_vec) const
	{
		return Vector3<_TyScalar>(r_v_vec.x * _TyStorage::f[0][0] + r_v_vec.y * _TyStorage::f[1][0] + r_v_vec.z * _TyStorage::f[2][0],
				    r_v_vec.x * _TyStorage::f[0][1] + r_v_vec.y * _TyStorage::f[1][1] + r_v_vec.z * _TyStorage::f[2][1],
				    r_v_vec.x * _TyStorage::f[0][2] + r_v_vec.y * _TyStorage::f[1][2] + r_v_vec.z * _TyStorage::f[2][2]);
	}

	/**
	 *	@brief transforms a plane by this matrix
	 *
	 *	This performs plane transformation in space, eg. if the plane is given
	 *	by a three points, the result is the same as a plane given by these points
	 *	transformed by this matrix (using the v_Transform_Pos() function).
	 *
	 *	@param[in] r_t_plane is the plane to be transformed
	 *	@return Returns r_t_plane transformed by this matrix.
	 */
	inline Plane3f operator *(TConstArgType(Plane3f) r_t_plane) const
	{
		return t_Transform_Plane(r_t_plane);
	}

	/**
	 *	@brief transforms a plane by this matrix
	 *
	 *	This performs plane transformation in space, eg. if the plane is given
	 *	by a three points, the result is the same as a plane given by these points
	 *	transformed by this matrix (using the v_Transform_Pos() function).
	 *
	 *	@param[in] r_t_plane is the plane to be transformed
	 *	@return Returns r_t_plane transformed by this matrix.
	 */
	Plane3f t_Transform_Plane(TConstArgType(Plane3f) r_t_plane) const
	{
#if 0
		_TyScalar k = -r_t_plane.f_dist / r_t_plane.v_normal.f_Length2();
		Vector3<_TyScalar> v_pos(v_Transform_Pos(r_t_plane.v_normal * k));
		// calculate a point on the plane in the target space

		return Plane3f(v_pos, v_Transform_Dir(r_t_plane.v_normal));
		// new plane with the target normal and with the point on it
#else // 0
		_TyScalar k = -r_t_plane.f_dist / r_t_plane.v_normal.f_Length2(); // the same as above
		Vector3<_TyScalar> v_new_normal(v_Transform_Dir(r_t_plane.v_normal)); // 3 MUL + 3 ADD shorter than the corresponding line above
		_TyScalar f_new_dist = -v_new_normal.f_Dot(v_new_normal * k + v_Offset()); // 6 MUL + 3 ADD shorter than the corresponding line above
		// this version saves 9 MUL + 6 ADD, and is algebraically equivalent

		return Plane3f(v_new_normal, f_new_dist);

		// and how was it derived:
		//
		// let n = v_old_normal
		// k = -f_old_dist / n.f_Length2()
		// v_pos = vec3(dot(v_right, n * k), dot(v_up, n * k), dot(v_dir, n * k)) + v_off
		// v_new_normal = vec3(dot(v_right, n), dot(v_up, n), dot(v_dir, n))
		// f_new_dist = -dot(v_pos, v_new_normal)
		// f_new_dist = -dot(vec3(dot(v_right, n * k), dot(v_up, n * k), dot(v_dir, n * k)) + v_off, v_new_normal)
		// f_new_dist = -dot(vec3(dot(v_right, n * k), dot(v_up, n * k), dot(v_dir, n * k)), v_new_normal) - dot(v_off, v_new_normal)
		// f_new_dist = -dot(vec3(dot(v_right, n * k), dot(v_up, n * k), dot(v_dir, n * k)), vec3(dot(v_right, n), dot(v_up, n), dot(v_dir, n))) - dot(v_off, v_new_normal)
		// f_new_dist = -vec3(dot(v_right, n * k) * dot(v_right, n), dot(v_up, n * k) * dot(v_up, n), dot(v_dir, n * k) * dot(v_dir, n)) - dot(v_off, v_new_normal)
		// f_new_dist = -vec3(dot(v_right, n) * dot(v_right, n) * k, dot(v_up, n) * dot(v_up, n) * k, dot(v_dir, n) * dot(v_dir, n) * k) - dot(v_off, v_new_normal)
		// f_new_dist = -dot(v_new_normal, v_new_normal) * k - dot(v_off, v_new_normal)
		// f_new_dist = -dot(v_new_normal, v_new_normal * k + v_off)
#endif // 0
	}

	/**
	 *	@brief Gets frustum planes for a projection matrix.
	 *
	 *	In case this matrix is a projection matrix, this returns the planes in camera space.
	 *	In case this matrix is a modelview-projection matrix, this returns the planes in object space.
	 *	In case this matrix is a camera-projection matrix, this returns the planes in world space.
	 *	These planes can be drawn using the following code:
	 *
	 *	@code
	 *	Matrix4f t_mvp; // modelview-projection matrix (in)
	 *
	 *	Plane3f t_left, t_right, t_bottom, t_top, t_near, t_far;
	 *	t_mvp.Get_FrustumPlanes(t_left, t_right, t_bottom, t_top, t_near, t_far, true);
	 *	// extract the planes from MVP directly
	 *
	 *	Vector3f na, nb, nc, nd;
	 *	Vector3f fa, fb, fc, fd;
	 *	t_near.Intersect(na, t_left, t_top);
	 *	t_near.Intersect(nb, t_right, t_top);
	 *	t_near.Intersect(nc, t_right, t_bottom);
	 *	t_near.Intersect(nd, t_left, t_bottom);
	 *	t_far.Intersect(fa, t_left, t_top);
	 *	t_far.Intersect(fb, t_right, t_top);
	 *	t_far.Intersect(fc, t_right, t_bottom);
	 *	t_far.Intersect(fd, t_left, t_bottom);
	 *	// calculate the corners of the frustum
	 *
	 *	float p_frustum_vertex_list[3 * 8];
	 *	{
	 *		float *p_dest = p_frustum_vertex_list;
	 *		*p_dest ++ = na.x; *p_dest ++ = na.y; *p_dest ++ = na.z;
	 *		*p_dest ++ = nb.x; *p_dest ++ = nb.y; *p_dest ++ = nb.z;
	 *		*p_dest ++ = nc.x; *p_dest ++ = nc.y; *p_dest ++ = nc.z;
	 *		*p_dest ++ = nd.x; *p_dest ++ = nd.y; *p_dest ++ = nd.z;
	 *		*p_dest ++ = fa.x; *p_dest ++ = fa.y; *p_dest ++ = fa.z;
	 *		*p_dest ++ = fb.x; *p_dest ++ = fb.y; *p_dest ++ = fb.z;
	 *		*p_dest ++ = fc.x; *p_dest ++ = fc.y; *p_dest ++ = fc.z;
	 *		*p_dest ++ = fd.x; *p_dest ++ = fd.y; *p_dest ++ = fd.z;
	 *	}
	 *	// fill the vertex array
	 *
	 *	const unsigned short p_frustum_index_list[] = {
	 *		0, 1, 2, 1, 3, 2, 0, 3,
	 *		4, 5, 5, 6, 6, 7, 7, 4,
	 *		0, 4, 1, 5, 2, 6, 3, 7
	 *	};
	 *	// index array
	 *
	 *	glVertexPointer(3, GL_FLOAT, 0, p_frustum_vertex_list);
	 *	glEnableClientState(GL_VERTEX_ARRAY);
	 *	glDrawElements(GL_LINES, 24, GL_UNSIGNED_SHORT, p_frustum_index_list);
	 *	glDisableClientState(GL_VERTEX_ARRAY);
	 *	// draw frustum (using the now-deprecated functionality)
	 *	@endcode
	 *
	 *	@param[out] r_t_left is the left clip plane
	 *	@param[out] r_t_right is the right clip plane
	 *	@param[out] r_t_bottom is the bottom clip plane
	 *	@param[out] r_t_top is the top clip plane
	 *	@param[out] r_t_near is the near clip plane
	 *	@param[out] r_t_far is the far clip plane
	 *	@param[in] b_normalize is plane normalization flag (if set, calculated planes are normalized)
	 */
	void Get_FrustumPlanes(Plane3f &r_t_left, Plane3f &r_t_right, Plane3f &r_t_bottom,
		Plane3f &r_t_top, Plane3f &r_t_near, Plane3f &r_t_far, bool b_normalize) const
	{
		{
			r_t_left.v_normal.x =	_TyStorage::f[0][3] - _TyStorage::f[0][0];
			r_t_left.v_normal.y =	_TyStorage::f[1][3] - _TyStorage::f[1][0];
			r_t_left.v_normal.z =	_TyStorage::f[2][3] - _TyStorage::f[2][0];
			r_t_left.f_dist =		_TyStorage::f[3][3] - _TyStorage::f[3][0];

			r_t_right.v_normal.x =	_TyStorage::f[0][3] + _TyStorage::f[0][0];
			r_t_right.v_normal.y =	_TyStorage::f[1][3] + _TyStorage::f[1][0];
			r_t_right.v_normal.z =	_TyStorage::f[2][3] + _TyStorage::f[2][0];
			r_t_right.f_dist =		_TyStorage::f[3][3] + _TyStorage::f[3][0];

			r_t_bottom.v_normal.x =	_TyStorage::f[0][3] - _TyStorage::f[0][1];
			r_t_bottom.v_normal.y = _TyStorage::f[1][3] - _TyStorage::f[1][1];
			r_t_bottom.v_normal.z = _TyStorage::f[2][3] - _TyStorage::f[2][1];
			r_t_bottom.f_dist =		_TyStorage::f[3][3] - _TyStorage::f[3][1];

			r_t_top.v_normal.x =	_TyStorage::f[0][3] + _TyStorage::f[0][1];
			r_t_top.v_normal.y =	_TyStorage::f[1][3] + _TyStorage::f[1][1];
			r_t_top.v_normal.z =	_TyStorage::f[2][3] + _TyStorage::f[2][1];
			r_t_top.f_dist =		_TyStorage::f[3][3] + _TyStorage::f[3][1];

			r_t_near.v_normal.x =	_TyStorage::f[0][3] + _TyStorage::f[0][2];
			r_t_near.v_normal.y =	_TyStorage::f[1][3] + _TyStorage::f[1][2];
			r_t_near.v_normal.z =	_TyStorage::f[2][3] + _TyStorage::f[2][2];
			r_t_near.f_dist =		_TyStorage::f[3][3] + _TyStorage::f[3][2];

			r_t_far.v_normal.x =	_TyStorage::f[0][3] - _TyStorage::f[0][2];
			r_t_far.v_normal.y =	_TyStorage::f[1][3] - _TyStorage::f[1][2];
			r_t_far.v_normal.z =	_TyStorage::f[2][3] - _TyStorage::f[2][2];
			r_t_far.f_dist =		_TyStorage::f[3][3] - _TyStorage::f[3][2];
		}
		// extract the planes

		if(b_normalize) {
			r_t_left.Normalize();
			r_t_right.Normalize();
			r_t_bottom.Normalize();
			r_t_top.Normalize();
			r_t_near.Normalize();
			r_t_far.Normalize();
		}
		// normalize the planes, if requested
	}

	/**
	 *	@brief calculates a submatrix determinant
	 *
	 *	@param[in] n_col is zero-based index of the column to be left out
	 *	@param[in] n_row is zero-based index of the row to be left out
	 *
	 *	@return Returns a determinant of this matrix with the specified column and row left out.
	 *	@note The result is not multiplied by (-1)^(n_col + n_row).
	 */
	_TyScalar f_Subdet(int n_col, int n_row) const
	{
		int i0 = (n_row <= 0) + 0, i1 = (n_row <= 1) + 1, i2 = (n_row <= 2) + 2;
		int j0 = (n_col <= 0) + 0, j1 = (n_col <= 1) + 1, j2 = (n_col <= 2) + 2;

		return (_TyStorage::f[j0][i0] * _TyStorage::f[j1][i1] * _TyStorage::f[j2][i2] +
			   _TyStorage::f[j1][i0] * _TyStorage::f[j2][i1] * _TyStorage::f[j0][i2] +
			   _TyStorage::f[j0][i1] * _TyStorage::f[j1][i2] * _TyStorage::f[j2][i0]) -
			   (_TyStorage::f[j0][i2] * _TyStorage::f[j1][i1] * _TyStorage::f[j2][i0] +
			   _TyStorage::f[j0][i1] * _TyStorage::f[j1][i0] * _TyStorage::f[j2][i2] +
			   _TyStorage::f[j0][i0] * _TyStorage::f[j1][i2] * _TyStorage::f[j2][i1]);
	}

	/**
	 *	@brief calculates the first matrix minor
	 *
	 *	@param[in] n_col is zero-based index of the column to be left out
	 *	@param[in] n_row is zero-based index of the row to be left out
	 *
	 *	@return Returns a determinant of this matrix with the specified
	 *		column and row left out, multiplied by (-1)^(n_col + n_row).
	 */
	inline _TyScalar f_Minor(int n_col, int n_row) const
	{
		return (1 - 2 * ((n_col + n_row) & 1)) * f_Subdet(n_col, n_row);
	}

	/**
	 *	@brief calculates the determinant
	 *	@return Returns determinant of this matrix
	 *	@note It uses subdeterminants, it is optimized for matrices
	 *		having zeros in the last row (common transformation matrices).
	 */
	_TyScalar f_Determinant() const
	{
		_TyScalar f_result = 0;
		_TyScalar f_sign = -1; // this was wrong
		for(int i = 0; i < n_column_num; ++ i, f_sign *= -1) {
			if(_TyStorage::f[i][n_row_num - 1]) // last row is sometimes zero-prone
				f_result += f_sign * f_Subdet(i, n_row_num - 1) * _TyStorage::f[i][n_row_num - 1];
		}
		return f_result;
	}

	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted matrix
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	void Fast_Invert_To(Matrix4<_TyScalar> &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use Fast_Invert_Inplace() instead

		_ASSERTE(_TyStorage::f[0][3] == 0 && _TyStorage::f[1][3] == 0 && _TyStorage::f[2][3] == 0 && _TyStorage::f[3][3] == 1);
		// bottom row must be equal to 0 0 0 1

		_TyScalar f_det = _TyStorage::f[0][0] * _TyStorage::f[1][1] * _TyStorage::f[2][2] +
				_TyStorage::f[1][0] * _TyStorage::f[2][1] * _TyStorage::f[0][2] +
				_TyStorage::f[0][1] * _TyStorage::f[1][2] * _TyStorage::f[2][0] -
				_TyStorage::f[0][2] * _TyStorage::f[1][1] * _TyStorage::f[2][0] -
				_TyStorage::f[0][1] * _TyStorage::f[1][0] * _TyStorage::f[2][2] -
				_TyStorage::f[0][0] * _TyStorage::f[1][2] * _TyStorage::f[2][1];

		r_dest._TyStorage::f[0][0] = _TyStorage::f[1][1] * _TyStorage::f[2][2] - _TyStorage::f[1][2] * _TyStorage::f[2][1];
		r_dest._TyStorage::f[1][0] =-_TyStorage::f[1][0] * _TyStorage::f[2][2] + _TyStorage::f[1][2] * _TyStorage::f[2][0];
		r_dest._TyStorage::f[2][0] = _TyStorage::f[1][0] * _TyStorage::f[2][1] - _TyStorage::f[1][1] * _TyStorage::f[2][0];
		r_dest._TyStorage::f[3][0] =-_TyStorage::f[1][0] * _TyStorage::f[2][1] * _TyStorage::f[3][2] -
			_TyStorage::f[1][1] * _TyStorage::f[2][2] * _TyStorage::f[3][0] -
			_TyStorage::f[2][0] * _TyStorage::f[3][1] * _TyStorage::f[1][2] +
			_TyStorage::f[1][2] * _TyStorage::f[2][1] * _TyStorage::f[3][0] +
			_TyStorage::f[1][1] * _TyStorage::f[2][0] * _TyStorage::f[3][2] +
			_TyStorage::f[2][2] * _TyStorage::f[1][0] * _TyStorage::f[3][1];
		r_dest._TyStorage::f[0][1] =-_TyStorage::f[0][1] * _TyStorage::f[2][2] + _TyStorage::f[0][2] * _TyStorage::f[2][1];
		r_dest._TyStorage::f[1][1] = _TyStorage::f[0][0] * _TyStorage::f[2][2] - _TyStorage::f[0][2] * _TyStorage::f[2][0];
		r_dest._TyStorage::f[2][1] =-_TyStorage::f[0][0] * _TyStorage::f[2][1] + _TyStorage::f[0][1] * _TyStorage::f[2][0];
		r_dest._TyStorage::f[3][1] = _TyStorage::f[0][0] * _TyStorage::f[2][1] * _TyStorage::f[3][2] +
			_TyStorage::f[0][1] * _TyStorage::f[2][2] * _TyStorage::f[3][0] +
			_TyStorage::f[2][0] * _TyStorage::f[3][1] * _TyStorage::f[0][2] -
			_TyStorage::f[0][2] * _TyStorage::f[2][1] * _TyStorage::f[3][0] -
			_TyStorage::f[0][1] * _TyStorage::f[2][0] * _TyStorage::f[3][2] -
			_TyStorage::f[2][2] * _TyStorage::f[0][0] * _TyStorage::f[3][1];
		r_dest._TyStorage::f[0][2] = _TyStorage::f[0][1] * _TyStorage::f[1][2] - _TyStorage::f[0][2] * _TyStorage::f[1][1];
		r_dest._TyStorage::f[1][2] =-_TyStorage::f[0][0] * _TyStorage::f[1][2] + _TyStorage::f[0][2] * _TyStorage::f[1][0];
		r_dest._TyStorage::f[2][2] = _TyStorage::f[0][0] * _TyStorage::f[1][1] - _TyStorage::f[0][1] * _TyStorage::f[1][0];
		r_dest._TyStorage::f[3][2] =-_TyStorage::f[0][0] * _TyStorage::f[1][1] * _TyStorage::f[3][2] -
			_TyStorage::f[0][1] * _TyStorage::f[1][2] * _TyStorage::f[3][0] -
			_TyStorage::f[1][0] * _TyStorage::f[3][1] * _TyStorage::f[0][2] +
			_TyStorage::f[0][2] * _TyStorage::f[1][1] * _TyStorage::f[3][0] +
			_TyStorage::f[0][1] * _TyStorage::f[1][0] * _TyStorage::f[3][2] +
			_TyStorage::f[1][2] * _TyStorage::f[0][0] * _TyStorage::f[3][1];

		f_det = 1.0f / f_det;
		for(int j = 0; j < 4; ++ j) {
			for(int i = 0; i < 3; ++ i)
				r_dest._TyStorage::f[j][i] *= f_det;
		}

		r_dest._TyStorage::f[0][3] = 0;
		r_dest._TyStorage::f[1][3] = 0;
		r_dest._TyStorage::f[2][3] = 0;
		r_dest._TyStorage::f[3][3] = 1;
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is matrix to store the inverse transpose to
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	void Fast_InvertTranspose_To(Matrix4<_TyScalar> &r_dest) const
	{
		Fast_Invert_To(r_dest);
		r_dest.Transpose(); // todo - optimize away
	}

	/**
	 *	@brief inverts this matrix (inplace; uses adjugate matrix method)
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	inline void Fast_Invert_Inplace()
	{
		Matrix4<_TyScalar> t_inverse;
		Fast_Invert_To(t_inverse);
		*this = t_inverse;
	}

	/**
	 *	@brief inverts and transposes this matrix (inplace; uses adjugate matrix method)
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	void Fast_InvertTranspose_Inplace()
	{
		Matrix4<_TyScalar> t_tmp;
		Fast_Invert_To(t_tmp);
		t_tmp.TransposeTo(*this);
	}

	/**
	 *	@brief calculates inverse of a matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	inline void Fast_Inverse_Of(TConstArgType(Matrix4<_TyScalar>) r_src)
	{
		r_src.Fast_Invert_To(*this);
	}

	/**
	 *	@brief calculates inverse transpose of a matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted and transposed
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	inline void Fast_InverseTranpose_Of(TConstArgType(Matrix4<_TyScalar>) r_src)
	{
		r_src.Fast_InvertTranpose_To(*this);
	}

	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@return Returns inverse of this matrix.
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	inline Matrix4<_TyScalar> t_Fast_Inverse() const
	{
		Matrix4<_TyScalar> t_inverse;
		Fast_Invert_To(t_inverse);
		return t_inverse;
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@return Returns inverse transpose of this matrix.
	 *	@note This is optimized for matrices with bottom row equal to 0 0 0 1
	 *		(common transformation matrices), this will give faulty output for
	 *		other matrices; use FullInvert() instead.
	 */
	inline Matrix4<_TyScalar> t_Fast_InverseTranpose() const
	{
		Matrix4<_TyScalar> t_inverse;
		Fast_InvertTranspose_To(t_inverse);
		return t_inverse;
	}

#if 0 // implementation in CMatrixInverseImpl
	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted matrix
	 */
	void Invert_To(Matrix4<_TyScalar> &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use Invert_Inplace() instead

		_TyScalar f_inv_det = 1 / f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest.f[j][i] = f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@param[out] r_dest is place to store the inverted and transposed matrix
	 */
	void InvertTranspose_To(Matrix4<_TyScalar> &r_dest) const
	{
		_ASSERTE(&r_dest != this); // use InvertTranspose_Inplace() instead

		_TyScalar f_inv_det = 1 / f_Determinant();
		for(int i = 0; i < n_column_num; ++ i) {
			for(int j = 0; j < n_row_num; ++ j)
				r_dest.f[i][j] = f_Minor(i, j) * f_inv_det;
		}
	}

	/**
	 *	@brief inverts this matrix (uses adjugate matrix method)
	 *	@return Returns inverse of this.
	 */
	inline Matrix4<_TyScalar> t_Inverse() const
	{
		Matrix4<_TyScalar> t_inverse;
		Invert_To(t_inverse);
		return t_inverse;
	}

	/**
	 *	@brief inverts and transposes this matrix (uses adjugate matrix method)
	 *	@return Returns inverse transpose of this.
	 */
	inline Matrix4<_TyScalar> t_InverseTranspose() const
	{
		Matrix4<_TyScalar> t_inverse;
		InvertTranspose_To(t_inverse);
		return t_inverse;
	}

	/**
	 *	@brief calculates inverse matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted
	 */
	inline void Inverse_Of(TConstArgType(Matrix4<_TyScalar>) r_src)
	{
		r_src.Invert_To(*this);
	}

	/**
	 *	@brief calculates inverse transpose matrix (uses adjugate matrix method)
	 *	@param[in] r_src is matrix to be inverted and transposed
	 */
	inline void InverseTranspose_Of(TConstArgType(Matrix4<_TyScalar>) r_src)
	{
		r_src.InvertTranspose_To(*this);
	}

	/**
	 *	@brief inverts this matrix (inplace; uses adjugate matrix method)
	 */
	inline void Invert_Inplace()
	{
		Matrix4<_TyScalar> t_inverse = *this;
		t_inverse.Invert_To(*this);
	}

	/**
	 *	@brief inverts and transposes this matrix (inplace; uses adjugate matrix method)
	 */
	inline void InvertTranspose_Inplace()
	{
		Matrix4<_TyScalar> t_inverse = *this;
		t_inverse.InvertTranspose_To(*this);
	}
#endif // 0

	/**
	 *	@brief transposes this matrix inplace
	 *	@note This is better optimized, than just <tt>*this = t_Transpose();</tt>.
	 */
	void Transpose()
	{
		_TyScalar f_tmp0 = _TyStorage::f[0][1];
		_TyStorage::f[0][1] = _TyStorage::f[1][0];
		_TyStorage::f[1][0] = f_tmp0;
		_TyScalar f_tmp1 = _TyStorage::f[0][2];
		_TyStorage::f[0][2] = _TyStorage::f[2][0];
		_TyStorage::f[2][0] = f_tmp1;
		_TyScalar f_tmp2 = _TyStorage::f[0][3];
		_TyStorage::f[0][3] = _TyStorage::f[3][0];
		_TyStorage::f[3][0] = f_tmp2;
		_TyScalar f_tmp3 = _TyStorage::f[1][2];
		_TyStorage::f[1][2] = _TyStorage::f[2][1];
		_TyStorage::f[2][1] = f_tmp3;
		_TyScalar f_tmp4 = _TyStorage::f[1][3];
		_TyStorage::f[1][3] = _TyStorage::f[3][1];
		_TyStorage::f[3][1] = f_tmp4;
		_TyScalar f_tmp5 = _TyStorage::f[2][3];
		_TyStorage::f[2][3] = _TyStorage::f[3][2];
		_TyStorage::f[3][2] = f_tmp5;
	}

	/**
	 *	@brief gets local x-axis vector
	 *	@return Returns local x-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector3<_TyScalar> v_Right() const
	{
		return Vector3<_TyScalar>(_TyStorage::f[0][0], _TyStorage::f[0][1], _TyStorage::f[0][2]);
	}

	/**
	 *	@brief gets local y-axis vector
	 *	@return Returns local y-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector3<_TyScalar> v_Up() const
	{
		return Vector3<_TyScalar>(_TyStorage::f[1][0], _TyStorage::f[1][1], _TyStorage::f[1][2]);
	}

	/**
	 *	@brief gets local z-axis vector
	 *	@return Returns local z-axis vector of the coordinate frame defined by this matrix.
	 */
	inline Vector3<_TyScalar> v_Dir() const
	{
		return Vector3<_TyScalar>(_TyStorage::f[2][0], _TyStorage::f[2][1], _TyStorage::f[2][2]);
	}

	/**
	 *	@brief gets the offset vector
	 *	@return Returns the offset vector of the coordinate frame defined by this matrix.
	 */
	inline Vector3<_TyScalar> v_Offset() const
	{
		return Vector3<_TyScalar>(_TyStorage::f[3][0], _TyStorage::f[3][1], _TyStorage::f[3][2]);
	}

	/**
	 *	@brief sets x-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Right(TConstArgType(Vector3<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[0][0] = r_v_vec.x;
		_TyStorage::f[0][1] = r_v_vec.y;
		_TyStorage::f[0][2] = r_v_vec.z;
	}

	/**
	 *	@brief sets y-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Up(TConstArgType(Vector3<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[1][0] = r_v_vec.x;
		_TyStorage::f[1][1] = r_v_vec.y;
		_TyStorage::f[1][2] = r_v_vec.z;
	}

	/**
	 *	@brief sets z-axis vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Dir(TConstArgType(Vector3<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[2][0] = r_v_vec.x;
		_TyStorage::f[2][1] = r_v_vec.y;
		_TyStorage::f[2][2] = r_v_vec.z;
	}

	/**
	 *	@brief sets offset vector
	 *	@param[in] r_v_vec is the new value of the vector
	 */
	inline void Offset(TConstArgType(Vector3<_TyScalar>) r_v_vec)
	{
		_TyStorage::f[3][0] = r_v_vec.x;
		_TyStorage::f[3][1] = r_v_vec.y;
		_TyStorage::f[3][2] = r_v_vec.z;
	}
};

/**
 *	@brief four by four single precision floating-point matrix
 */
typedef Matrix4<float> Matrix4f;

/**
 *	@brief four by four double precision floating-point matrix
 */
typedef Matrix4<double> Matrix4d;

/*
 *								=== ~Matrix4f ===
 */

/*
 *								=== CMatrixTraits ===
 */

// todo - implement the same for vectors?

/**
 *	@brief matrix traits; use this to create a type of matrix of a given dimensions
 *
 *	@tparam _Ty is scalar data type
 *	@tparam n_cols is number of matrix columns
 *	@tparam n_rows is number of matrix rows
 *
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <class CScalar, const int n_cols, const int n_rows>
class CMatrixTraits {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = n_cols, /**< @brief number of matrix columns */
		n_row_num = n_rows	   /**< @brief number of matrix rows */
	};

	typedef CScalar _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_cols, n_rows> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_cols, n_rows, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef _TyBase _Ty; /**< @brief the matrix type (have no special type for this size) */
};

#if !defined(_MSC_VER) || defined(__MWERKS__) || _MSC_VER > 1200

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 2 x 2 matrices)
 *	@tparam _Ty is scalar data type
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <class CScalar>
class CMatrixTraits<CScalar, 2, 2> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 2, /**< @brief number of matrix columns */
		n_row_num = 2 /**< @brief number of matrix rows */
	};

	typedef CScalar _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix2<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 3 x 3 matrices)
 *	@tparam _Ty is scalar data type
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <class CScalar>
class CMatrixTraits<CScalar, 3, 3> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 3, /**< @brief number of matrix columns */
		n_row_num = 3 /**< @brief number of matrix rows */
	};

	typedef CScalar _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix3<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 4 x 4 matrices)
 *	@tparam _Ty is scalar data type
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <class CScalar>
class CMatrixTraits<CScalar, 4, 4> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 4, /**< @brief number of matrix columns */
		n_row_num = 4 /**< @brief number of matrix rows */
	};

	typedef CScalar _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix4<_TyScalar> _Ty; /**< @brief the matrix type */
};

#else // !defined(_MSC_VER) || defined(__MWERKS__) || _MSC_VER > 1200

// MSVC6 doesn't support partial specialization, need to write full specializations

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 2 x 2, float matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<float, 2, 2> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 2, /**< @brief number of matrix columns */
		n_row_num = 2 /**< @brief number of matrix rows */
	};

	typedef float _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix2<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 3 x 3, float matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<float, 3, 3> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 3, /**< @brief number of matrix columns */
		n_row_num = 3 /**< @brief number of matrix rows */
	};

	typedef float _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix3<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 4 x 4, float matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<float, 4, 4> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 4, /**< @brief number of matrix columns */
		n_row_num = 4 /**< @brief number of matrix rows */
	};

	typedef float _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix4<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 2 x 2, double matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<double, 2, 2> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 2, /**< @brief number of matrix columns */
		n_row_num = 2 /**< @brief number of matrix rows */
	};

	typedef double _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix2<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 3 x 3, double matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<double, 3, 3> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 3, /**< @brief number of matrix columns */
		n_row_num = 3 /**< @brief number of matrix rows */
	};

	typedef double _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix3<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 4 x 4, double matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<double, 4, 4> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 4, /**< @brief number of matrix columns */
		n_row_num = 4 /**< @brief number of matrix rows */
	};

	typedef double _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix4<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 2 x 2, int matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<int, 2, 2> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 2, /**< @brief number of matrix columns */
		n_row_num = 2 /**< @brief number of matrix rows */
	};

	typedef int _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix2<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 3 x 3, int matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<int, 3, 3> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 3, /**< @brief number of matrix columns */
		n_row_num = 3 /**< @brief number of matrix rows */
	};

	typedef int _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix3<_TyScalar> _Ty; /**< @brief the matrix type */
};

/**
 *	@brief matrix traits; use this to create a type of matrix of
 *		a given dimensions (specialization for 4 x 4, int matrices)
 *	@note This is required, as the matrix classes implement some additional
 *		constructors or functionality, which is different for each dimension.
 */
template <>
class CMatrixTraits<int, 4, 4> {
public:
	/**
	 *	@brief matrix parameters, stored as enum
	 */
	enum {
		n_column_num = 4, /**< @brief number of matrix columns */
		n_row_num = 4 /**< @brief number of matrix rows */
	};

	typedef int _TyScalar; /**< @brief scalar data type */
	typedef CMatrixStorage<_TyScalar, n_column_num, n_row_num> _TyStorage; /**< @brief matrix storage type */
	typedef CMatrixBase<_TyScalar, n_column_num, n_row_num, _TyStorage> _TyBase; /**< @brief matrix base type */
	typedef Matrix4<_TyScalar> _Ty; /**< @brief the matrix type */
};

#endif // !defined(_MSC_VER) || defined(__MWERKS__) || _MSC_VER > 1200

/*
 *								=== ~CMatrixTraits ===
 */

/*
 *								=== Quat ===
 */


/**
 *	@brief namespace with tag objects for Quat
 */
namespace quat {

/**
 *	@brief tag for Quat constructor
 */
struct from_vector_w_tag_t {};

/**
 *	@brief tag for Quat constructor
 */
struct from_axis_angle_tag_t {};

/**
 *	@brief tag for Quat constructor
 */
struct rotation_from_vector_to_vector_tag_t {};

extern const from_vector_w_tag_t from_vector_and_w; /**< @brief tag for Quat constructor */
extern const from_axis_angle_tag_t from_axis_angle; /**< @brief tag for Quat constructor */
extern const rotation_from_vector_to_vector_tag_t rotation_from_vector_to_vector; /**< @brief tag for Quat constructor */

} // ~quat

/**
 *	@brief quaternion class
 *	@tparam _Ty is scalar type
 */
template <class _Ty>
class Quat : public Vector4<_Ty> {
public:
	typedef _Ty _TyScalar; /**< @brief scalar type */
	typedef Vector4<_TyScalar> _TyVec; /** @brief the original vector type */

public:
	/**
	 *	@brief default constructor (has no effect)
	 */
	inline Quat()
	{}

	/**
	 *	@brief constructor with real scalar only
	 *	@param[in] f_w is value of the real (w) component
	 */
	inline Quat(_TyScalar f_w)
		:Vector4<_TyScalar>(0, 0, 0, f_w)
	{}

	/**
	 *	@brief constructor from scalar values
	 *
	 *	@param[in] f_x is value of the imaginary (x) component
	 *	@param[in] f_y is value of the imaginary (y) component
	 *	@param[in] f_z is value of the imaginary (z) component
	 *	@param[in] f_w is value of the real (w) component
	 */
	inline Quat(_TyScalar f_x, _TyScalar f_y, _TyScalar f_z, _TyScalar f_w)
		:Vector4<_TyScalar>(f_x, f_y, f_z, f_w)
	{}

	/**
	 *	@brief copy-constructor
	 *	@param[in] r_t_quat is quaternion to copy from
	 */
	inline Quat(TConstArgType(Quat<_TyScalar>) r_t_quat)
		:Vector4<_TyScalar>(r_t_quat.x, r_t_quat.y, r_t_quat.z, r_t_quat.w)
	{}

	/**
	 *	@brief conversion constructor form 4D vector
	 *	@param[in] r_t_vec is vector to convert from
	 */
	inline Quat(TConstArgType(Vector4<_TyScalar>) r_t_vec)
		:Vector4<_TyScalar>(r_t_vec)
	{}

	/**
	 *	@brief conversion constructor form 3D imaginary vector and real scalar
	 *
	 *	@param[in] r_t_vec is the imaginary vector
	 *	@param[in] f_w is the real scalar
	 *	@param[in] t_tag is disambiguation tag (unused)
	 */
	inline Quat(TConstArgType(Vector3<_TyScalar>) r_t_vec,
		_TyScalar f_w, quat::from_vector_w_tag_t VECTOR2_CONSTRUCTOR_TAG)
		:Vector4<_TyScalar>(r_t_vec, f_w)
	{}

	/**
	 *	@brief calculates rotation from one vector to other vector on a unit sphere
	 *
	 *	@param[in] v_original is the departure point at the unit sphere
	 *	@param[in] v_align_to is the destination point at the unit sphere
	 *	@param[in] t_tag is disambiguation tag (unused)
	 *
	 *	@note Both vectors must be normalized.
	 */
	inline Quat(TConstArgType(Vector3<_TyScalar>) v_original,
		TConstArgType(Vector3<_TyScalar>) v_align_to,
		quat::rotation_from_vector_to_vector_tag_t VECTOR2_CONSTRUCTOR_TAG)
	{
		Align(v_original, v_align_to);
	}

	/**
	 *	@brief conversion constructor form axis-angle form
	 *
	 *	@param[in] r_v_axis is rotation axis (must be unit length)
	 *	@param[in] f_angle is angle
	 *	@param[in] t_tag is disambiguation tag (unused)
	 */
	Quat(TConstArgType(Vector3<_TyScalar>) r_v_axis,
		_TyScalar f_angle, quat::from_axis_angle_tag_t VECTOR2_CONSTRUCTOR_TAG)
	{
		_ASSERTE(fabs(r_v_axis.f_Length() - 1) < f_epsilon); // assumes the axis is unit length

		f_angle *= (_TyScalar).5;
		_TyScalar f_sin = (_TyScalar)sin(f_angle);
		_TyVec::x = r_v_axis.x * f_sin;
		_TyVec::y = r_v_axis.y * f_sin;
		_TyVec::z = r_v_axis.z * f_sin;
		_TyVec::w = (_TyScalar)cos(f_angle);
	}

	/**
	 *	@brief conversion constructor form compact axis-angle form
	 *
	 *	@param[in] r_v_axis_angle is rotation axis, multiplied by angle in radians
	 *	@param[in] t_tag is disambiguation tag (unused)
	 */
	Quat(TConstArgType(Vector3<_TyScalar>) r_v_axis_angle, quat::from_axis_angle_tag_t VECTOR2_CONSTRUCTOR_TAG)
	{
		_TyScalar f_angle = r_v_axis_angle.f_Length();
		if(f_angle > 0) {
			float f_half_angle = f_angle * (_TyScalar).5;
			_TyScalar f_sin_and_renorm = (_TyScalar)sin(f_half_angle) / f_angle;
			_TyVec::x = r_v_axis_angle.x * f_sin_and_renorm;
			_TyVec::y = r_v_axis_angle.y * f_sin_and_renorm;
			_TyVec::z = r_v_axis_angle.z * f_sin_and_renorm;
			_TyVec::w = (_TyScalar)cos(f_half_angle);
		} else {
			_TyVec::x = 0;
			_TyVec::y = 0;
			_TyVec::z = 0;
			_TyVec::w = 1;
		}
	}

	/**
	 *	@brief conversion constructor from a rotation matrix
	 *
	 *	@param[in] r_t_rot is rotation matrix
	 */
	inline Quat(TConstArgType(Matrix3<_TyScalar>) r_t_rot)
	{
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER <= 1200
		FromMatrix(3, &r_t_rot[0][0]);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
		FromMatrix<3>(&r_t_rot[0][0]);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
	}

	/**
	 *	@brief conversion constructor from a rotation matrix
	 *
	 *	@param[in] r_t_rot is transfomation matrix (only the rotation part is used)
	 */
	inline Quat(TConstArgType(Matrix4<_TyScalar>) r_t_rot)
	{
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER <= 1200
		FromMatrix(4, &r_t_rot[0][0]);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
		FromMatrix<4>(&r_t_rot[0][0]);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
	}

	/**
	 *	@brief copy-operator
	 *	@param[in] r_t_quat is quaternion to copy from
	 *	@return Returns reference to this.
	 */
	inline TNonConstResultType(Quat<_TyScalar>) operator =(TConstArgType(Quat<_TyScalar>) r_t_quat)
	{
		_TyVec::x = r_t_quat.x;
		_TyVec::y = r_t_quat.y;
		_TyVec::z = r_t_quat.z;
		_TyVec::w = r_t_quat.w;
		return *this;
	}

	/**
	 *	@brief copy-operator
	 *	@param[in] r_t_vec is vector to copy from
	 *	@return Returns reference to this.
	 */
	inline TNonConstResultType(Quat<_TyScalar>) operator =(TConstArgType(Vector4<_TyScalar>) r_t_vec)
	{
		_TyVec::x = r_t_vec.x;
		_TyVec::y = r_t_vec.y;
		_TyVec::z = r_t_vec.z;
		_TyVec::w = r_t_vec.w;
		return *this;
	}

	/**
	 *	@brief shortest rotation between two point on unit sphere
	 *
	 *	This calculates quaternion for the shortest rotation that aligns
	 *	two vectors lying on the surface of a unit sphere (such rotation
	 *	applied to the first vector yields the second vector).
	 *
	 *	@param[in] v_original is the vector to rotate from (must be unit length)
	 *	@param[in] v_align_to is the vector to rotate to (must be unit length)
	 */
	void Align(TConstArgType(Vector3<_TyScalar>) v_original,
		TConstArgType(Vector3<_TyScalar>) v_align_to)
	{
		_ASSERTE(fabs(v_original.f_Length() - 1) < f_epsilon);
		_ASSERTE(fabs(v_align_to.f_Length() - 1) < f_epsilon); // assumes input vectors are unit length

		_TyScalar f_cos_half_angle; // cos(angle / 2) where angle is rotation angle
		Vector3<_TyScalar> v_axis; // sin(angle / 2) * rotation_axis

		Vector3<_TyScalar> v_bisect = v_original + v_align_to;
		if(v_bisect.f_Length() > 1e-5f) {
			v_bisect.Normalize();
			f_cos_half_angle = v_bisect.f_Dot(v_original);
			v_axis = v_bisect.v_Cross(v_original); // cross-product has length of sin(angle)
		} else {
			_TyScalar f_dot = max(_TyScalar(-1), min(_TyScalar(1), v_original.f_Dot(v_align_to)));
			// the dot can give result >1 due to numerical issues (error in vector normalization),
			// which would yield NaN in acos(); happens surprisingly frequently

			_TyScalar f_half_angle = (_TyScalar).5 * (_TyScalar)acos(f_dot); // angle will be near pi / 2
			f_cos_half_angle = (_TyScalar)cos(f_half_angle); // cos will be near 0.0

			//v_axis = v_original.v_Cross(v_align_to);
			// cross product would yield zero vector (angle is pi, sin(pi) = 0)

			if(fabs(v_original.x) >= fabs(v_original.y)) {
				_TyScalar f_inv_length = ((_TyScalar)1.0) / v_original.v_xz().f_Length();
				v_axis = Vector3<_TyScalar>(-v_original.z * f_inv_length, 0, v_original.x * f_inv_length);
			} else {
				_TyScalar f_inv_length = ((_TyScalar)1.0) / v_original.v_zy().f_Length();
				v_axis = Vector3<_TyScalar>(0, v_original.z * f_inv_length, -v_original.y * f_inv_length);
			}
			// select greatest component and create perpendicular vector in 2D plane to be used as axis of rotation

			v_axis *= (_TyScalar)sin(f_half_angle); // sin will be near 1.0
		}

		_TyVec::w = f_cos_half_angle;
		_TyVec::x = v_axis.x;
		_TyVec::y = v_axis.y;
		_TyVec::z = v_axis.z;
	}

	/**
	 *	@brief conversion to rotation matrix
	 *	@return Returns a 3x3 rotation matrix, corresponding to this.
	 *	@note This must be normalized.
	 */
	inline Matrix3<_TyScalar> t_ToMatrix() const
	{
		Matrix3<_TyScalar> t_matrix;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER <= 1200
		ToMatrix(3, &t_matrix[0][0]);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
		ToMatrix<3>(&t_matrix[0][0]);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
		return t_matrix;
	}

	/**
	 *	@brief conversion to a 4x4 rotation matrix
	 *	@return Returns a 4x4 rotation matrix, corresponding to this.
	 *	@note This must be normalized.
	 */
	Matrix4<_TyScalar> t_ToMatrix4() const
	{
		Matrix4<_TyScalar> t_matrix;
		_TyScalar *p_matrix = &t_matrix[0][0];

#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER <= 1200
		ToMatrix(4, p_matrix);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
		ToMatrix<4>(p_matrix);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
		// fill the rotation part

		p_matrix[3 * 4 + 0] = 0;
		p_matrix[3 * 4 + 1] = 0;
		p_matrix[3 * 4 + 2] = 0;
		p_matrix[0 * 4 + 3] = 0;
		p_matrix[1 * 4 + 3] = 0;
		p_matrix[2 * 4 + 3] = 0;
		p_matrix[3 * 4 + 3] = 1;
		// initialize the rest of the matrix to identity

		return t_matrix;
	}

	/**
	 *	@brief conversion to compact axis-angle
	 *
	 *	@return Returns rotation axis, multiplied by angle in radians.
	 *
	 *	@note This must be normalized.
	 *	@note Quaternions are double cover of SO(3): every rotation can be expressed
	 *		in two ways. In case the rotation axes need to be compared or averaged,
	 *		use t_Positive() before calculating the rotation axis.
	 */
	inline Vector3<_TyScalar> v_ToAxisAngle() const
	{
		Vector3<_TyScalar> v_axis;
		_TyScalar f_angle;
		ToAxisAngle(v_axis, f_angle);
		v_axis *= f_angle;
		return v_axis;
	}

	/**
	 *	@brief conversion to axis-angle
	 *
	 *	@param[out] r_t_axis is rotation axis (normalized)
	 *	@param[out] r_f_angle is rotation angle, in radians
	 *
	 *	@note This must be normalized.
	 *	@note Quaternions are double cover of SO(3): every rotation can be expressed
	 *		in two ways. In case the rotation axes need to be compared or averaged,
	 *		use t_Positive() before calculating the rotation axis.
	 */
	void ToAxisAngle(Vector3<_TyScalar> &r_t_axis, _TyScalar &r_f_angle) const
	{
		_TyScalar f_length2 = _TyVec::x * _TyVec::x + _TyVec::y * _TyVec::y + _TyVec::z * _TyVec::z;
		if(f_length2 > _TyScalar(1e-5)) {
			r_f_angle = f_RotationAngle();
			_TyScalar f_inv_length = 1 / (_TyScalar)sqrt(double(f_length2));
			r_t_axis.x = _TyVec::x * f_inv_length;
			r_t_axis.y = _TyVec::y * f_inv_length;
			r_t_axis.z = _TyVec::z * f_inv_length;
		} else {
			r_f_angle = 0; // fmod(angle, 2 * pi) = 0
			r_t_axis.x = 1; // no rotation so axis doesn't matter
			r_t_axis.y = 0;
			r_t_axis.z = 0;
		}
	}

	/**
	 *	@brief gets a quaternion corresponding to the same rotation as this, where the real component is positive
	 *	@return Returns this or the negative value of this, so that the real (w) is positive.
	 *	@note This is used to compare quaternions (simple difference of quaternions will not work).
	 */
	inline Quat<_TyScalar> t_Positive() const
	{
		return (_TyVec::w >= 0)? *this : -*this;
	}

	/**
	 *	@brief unary minus operator
	 *	@return Returns the negative value of this (corresponds to the same rotation).
	 *	@note If the opposite rotation is required, use t_Conjugate().
	 */
	inline Quat<_TyScalar> operator -() const
	{
		return Quat<_TyScalar>(-_TyVec::x, -_TyVec::y, -_TyVec::z, -_TyVec::w);
	}

	/**
	 *	@brief addition operator
	 *	@param[in] r_t_quat is right-hand side operand
	 *	@return Returns the sum of this and r_t_quat.
	 */
	inline Quat<_TyScalar> operator +(TConstArgType(Quat<_TyScalar>) r_t_quat) const
	{
		return Quat<_TyScalar>(_TyVec::x + r_t_quat.x,
			_TyVec::y + r_t_quat.y, _TyVec::z + r_t_quat.z, _TyVec::w + r_t_quat.w);
	}

	/**
	 *	@brief subtraction operator
	 *	@param[in] r_t_quat is right-hand side operand
	 *	@return Returns the difference of this and r_t_quat.
	 */
	inline Quat<_TyScalar> operator -(TConstArgType(Quat<_TyScalar>) r_t_quat) const
	{
		return Quat<_TyScalar>(_TyVec::x - r_t_quat.x, _TyVec::y - r_t_quat.y,
			_TyVec::z - r_t_quat.z, _TyVec::w - r_t_quat.w);
	}

	/**
	 *	@brief scalar multiplication operator
	 *	@param[in] f_scale is the scalar
	 *	@return Returns the product of this and the scalar.
	 */
	inline Quat<_TyScalar> operator *(_TyScalar f_scale) const
	{
		return Quat<_TyScalar>(_TyVec::x * f_scale, _TyVec::y * f_scale,
			_TyVec::z * f_scale, _TyVec::w * f_scale);
	}

	/**
	 *	@brief quaternion multiplication operator
	 *	@param[in] r_t_quat is the right-hand side operand
	 *	@return Returns the product of this and r_t_quat.
	 *	@note Product of any two normalized quaternions is again normalized.
	 */
	Quat<_TyScalar> operator *(TConstArgType(Quat<_TyScalar>) r_t_quat) const
	{
		return Quat<_TyScalar>(
			_TyVec::w * r_t_quat.x + _TyVec::x * r_t_quat.w + _TyVec::y * r_t_quat.z - _TyVec::z * r_t_quat.y,
			_TyVec::w * r_t_quat.y - _TyVec::x * r_t_quat.z + _TyVec::y * r_t_quat.w + _TyVec::z * r_t_quat.x,
			_TyVec::w * r_t_quat.z + _TyVec::x * r_t_quat.y - _TyVec::y * r_t_quat.x + _TyVec::z * r_t_quat.w,
			_TyVec::w * r_t_quat.w - _TyVec::x * r_t_quat.x - _TyVec::y * r_t_quat.y - _TyVec::z * r_t_quat.z);
	}

	/**
	 *	@brief modify-addition operator
	 *	@param[in] r_t_quat is right-hand side operand
	 *	@return Returns reference to this.
	 */
	inline TNonConstResultType(Quat<_TyScalar>) operator +=(TConstArgType(Quat<_TyScalar>) r_t_quat)
	{
		_TyVec::x += r_t_quat.x;
		_TyVec::y += r_t_quat.y;
		_TyVec::z += r_t_quat.z;
		_TyVec::w += r_t_quat.w;
		return *this;
	}

	/**
	 *	@brief modify-subtraction operator
	 *	@param[in] r_t_quat is right-hand side operand
	 *	@return Returns reference to this.
	 */
	inline TNonConstResultType(Quat<_TyScalar>) operator -=(TConstArgType(Quat<_TyScalar>) r_t_quat)
	{
		_TyVec::x -= r_t_quat.x;
		_TyVec::y -= r_t_quat.y;
		_TyVec::z -= r_t_quat.z;
		_TyVec::w -= r_t_quat.w;
		return *this;
	}

	/**
	 *	@brief scalar multiplication operator
	 *	@param[in] f_scale is the scalar
	 *	@return Returns reference to this.
	 */
	inline TNonConstResultType(Quat<_TyScalar>) operator *=(_TyScalar f_scale)
	{
		_TyVec::x *= f_scale;
		_TyVec::y *= f_scale;
		_TyVec::z *= f_scale;
		_TyVec::w *= f_scale;
		return *this;
	}

	/**
	 *	@brief quaternion multiplication operator
	 *	@param[in] r_t_quat is the right-hand side operand
	 *	@return Returns reference to this.
	 *	@note Product of any two normalized quaternions is again normalized.
	 */
	TNonConstResultType(Quat<_TyScalar>) operator *=(TConstArgType(Quat<_TyScalar>) r_t_quat)
	{
		_TyScalar _x = _TyVec::w * r_t_quat.x + _TyVec::x * r_t_quat.w + _TyVec::y * r_t_quat.z - _TyVec::z * r_t_quat.y;
		_TyScalar _y = _TyVec::w * r_t_quat.y - _TyVec::x * r_t_quat.z + _TyVec::y * r_t_quat.w + _TyVec::z * r_t_quat.x;
		_TyScalar _z = _TyVec::w * r_t_quat.z + _TyVec::x * r_t_quat.y - _TyVec::y * r_t_quat.x + _TyVec::z * r_t_quat.w;
		_TyVec::w = _TyVec::w * r_t_quat.w - _TyVec::x * r_t_quat.x - _TyVec::y * r_t_quat.y - _TyVec::z * r_t_quat.z;
		_TyVec::x = _x;
		_TyVec::y = _y;
		_TyVec::z = _z;
		return *this;
	}

	/**
	 *	@brief gets conjugate quaternion
	 *	@return Returns conjugate of this.
	 */
	inline Quat<_TyScalar> t_Conjugate() const
	{
		return Quat<_TyScalar>(-_TyVec::x, -_TyVec::y, -_TyVec::z, _TyVec::w);
	}

	/**
	 *	@brief conjugates this quaternion inplace
	 *	@note This does not change the sign of the real component (w).
	 */
	inline void Conjugate() const
	{
		_TyVec::x = -_TyVec::x;
		_TyVec::y = -_TyVec::y;
		_TyVec::z = -_TyVec::z;
	}

	/*inline _TyScalar f_Length2() const // already in vec4
	{
		return x * x + y * y + z * z + w * w;
	}

	inline _TyScalar f_Length() const
	{
		return (_TyScalar)sqrt(double(x * x + y * y + z * z + w * w));
	}

	inline void Normalize()
	{
		_TyScalar f_inv_length = 1 / f_Length();
		*this *= f_inv_length;
	}*/

	/**
	 *	@brief calculates quaternion inverse
	 *	@return Returns inverse of this.
	 */
	inline Quat<_TyScalar> t_Inverse() const
	{
		return t_Conjugate() * (1 / _TyVec::f_Length2());
	}

	/**
	 *	@brief inverts this quaternion inplace
	 *	@note Inplace operations do not return value to avoid confusion for out of place operations.
	 */
	inline void Invert()
	{
		Conjugate();
		*this *= 1 / _TyVec::f_Length2();
	}

	/**
	 *	@brief calculates half of the rotation angle
	 *	@return Returns half of the rotation angle, in radians.
	 *
	 *	@note This must be normalized.
	 *	@note To recover both rotation axis and angle, use ToAxisAngle().
	 *	@note Quaternions are double cover of SO(3): every rotation can be expressed
	 *		in two ways. In case the rotation axes need to be compared or averaged,
	 *		use t_Positive() before calculating the rotation axis.
	 */
	inline _TyScalar f_RotationHalfAngle() const
	{
		_ASSERTE(fabs(_TyVec::w) <= 1 + _TyScalar(1e-5));
		return (_TyScalar)acos(min(_TyScalar(1), max(_TyScalar(-1), _TyVec::w)));
	}

	/**
	 *	@brief calculates rotation angle
	 *	@return Returns rotation angle, in radians.
	 *
	 *	@note This must be normalized.
	 *	@note To recover both rotation axis and angle, use ToAxisAngle().
	 *	@note Quaternions are double cover of SO(3): every rotation can be expressed
	 *		in two ways. In case the rotation axes need to be compared or averaged,
	 *		use t_Positive() before calculating the rotation axis.
	 */
	inline _TyScalar f_RotationAngle() const
	{
		return 2 * f_RotationHalfAngle();
	}

	/**
	 *	@brief calculates rotation axis
	 *
	 *	@return Returns normalized rotation axis.
	 *
	 *	@note This does not need to be normalized.
	 *	@note To recover both rotation axis and angle, use ToAxisAngle().
	 *	@note In case the axis does not need to be normalized, use v_Imaginary().
	 *	@note Quaternions are double cover of SO(3): every rotation can be expressed
	 *		in two ways. In case the rotation axes need to be compared or averaged,
	 *		use t_Positive() before calculating the rotation axis.
	 *	@note In case of zero rotation, the axis is ambiguous, and this
	 *		function returns (1, 0, 0).
	 */
	Vector3<_TyScalar> v_RotationAxis() const
	{
		_TyScalar f_length2 = _TyVec::x * _TyVec::x + _TyVec::y * _TyVec::y + _TyVec::z * _TyVec::z;
		if(f_length2 > _TyScalar(1e-5)) {
			_TyScalar f_inv_length = 1 / (_TyScalar)sqrt(double(f_length2)); // better sqrt than "1 / sin(acos(w))", doesn't require normalized quats
			return _TyVec::v_xyz() * f_inv_length;
		} else
			return Vector3<_TyScalar>(1, 0, 0); // no rotation, axis is ambiguous
	}

	/**
	 *	@brief gets the imaginary components of the quaternion in a vector
	 *	@return Returns vector of <tt>(x, y, z)</tt>.
	 *	@note The real component is simply accessible as <tt>w</tt>.
	 */
	inline Vector3<_TyScalar> v_Imaginary() const
	{
		return _TyVec::v_xyz();
	}

	/**
	 *	@brief rotate a point by a quaternion
	 *
	 *	This is slower than matrix-vector multiplication but faster than
	 *	conversion to matrix and a matrix-vector multiplication. If there
	 *	is only a single point to transform, this is faster. In total,
	 *	this is 15 add + 15 mul = 30 FLOPS. A 3x3 rotation matrix requires
	 *	6 add + 9 mul = 15 FLOPS only. The conversion to matrix is 15 add +
	 *	9 mul = 24 FLOPS.
	 *	For one point, it is 30 FLOPS / 24 + 15 = 39 FLOPS (quaternion is better).
	 *	For two points, it is 60 FLOPS / 24 + 30 = 54 FLOPS (matrix is better).
	 *
	 *	@param[in] v_vec is vector to be rotated
	 *	@return Returns the input vector, transformed by this rotation.
	 *	@note This must be normalized.
	 */
	Vector3<_TyScalar> v_Transform(TConstArgType(Vector3<_TyScalar>) v_vec) const
	{
		Vector3<_TyScalar> uv = v_vec.v_Cross(_TyVec::v_xyz()); // 6 mul, 3 add
		uv += uv; // 3 add
		return v_vec + uv * _TyVec::w + uv.v_Cross(_TyVec::v_xyz()); // 9 add, 9 mul
		// in total, 15 add + 15 mul = 30 FLOPS
	}

	/**
	 *	@brief calculates quaternion power
	 *	@param[in] f_power is power
	 *	@return Returns the specified power of this.
	 *	@note This must be normalized.
	 */
	Quat<_TyScalar> t_Power(_TyScalar f_power) const
	{
		return (t_Log() * f_power).t_Exp();
	}

	/**
	 *	@brief calculates power of this quaternion inplace
	 *	@param[in] f_power is power
	 *	@note This must be normalized.
	 */
	void Power(_TyScalar f_power) const
	{
		Log();
		*this *= f_power;
		Exp();
	}

	/**
	 *	@brief calculates quaternion exponent
	 *	@return Returns exponent of this.
	 *	@note This must be normalized.
	 */
	Quat<_TyScalar> t_Exp() const
	{
		// exp(v * fi) = cos(fi) + v * sin(fi)
		//_ASSERTE(w == 0);

		_TyScalar f_angle = (_TyScalar)sqrt(double(_TyVec::x * _TyVec::x + _TyVec::y * _TyVec::y + _TyVec::z * _TyVec::z));
		_TyScalar f_sin = (_TyScalar)sin(f_angle);
		_TyScalar f_cos = (_TyScalar)cos(f_angle);
		if(f_sin < -_TyScalar(1e-5) || f_sin > _TyScalar(1e-5)) {
			_TyScalar f_scale = f_sin / f_angle;
			return Quat<_TyScalar>(_TyVec::x * f_scale, _TyVec::y * f_scale, _TyVec::z * f_scale, f_cos);
		} else
			return Quat<_TyScalar>(_TyVec::x, _TyVec::y, _TyVec::z, f_cos); // lim(a / sin(a)) a -> 0 = 1
	}

	/**
	 *	@brief calculates exponent of this quaternion inplace
	 *
	 *	@note Inplace operations do not return value to avoid confusion for out of place operations.
	 *	@note This must be normalized.
	 */
	void Exp()
	{
		// exp(v * fi) = cos(fi) + v * sin(fi)
		//_ASSERTE(w == 0);

		_TyScalar f_angle = (_TyScalar)sqrt(double(_TyVec::x * _TyVec::x + _TyVec::y * _TyVec::y + _TyVec::z * _TyVec::z));
		_TyScalar f_sin = (_TyScalar)sin(f_angle);
		_TyVec::w = (_TyScalar)cos(f_angle);
		if(f_sin < -_TyScalar(1e-5) || f_sin > _TyScalar(1e-5)) {
			_TyScalar f_scale = f_sin / f_angle;
			_TyVec::x *= f_scale;
			_TyVec::y *= f_scale;
			_TyVec::z *= f_scale;
		} // no else because lim(a / sin(a)) a -> 0 = 1
	}

	/**
	 *	@brief calculates quaternion logarithm
	 *	@return Returns logarithm of this.
	 *	@note This must be normalized.
	 */
	Quat<_TyScalar> t_Log() const
	{
		// log(q) = log(cos(fi) + v * sin(fi)) = log(exp(v * fi)) = v * fi

		if(_TyVec::w > -1 && _TyVec::w < 1) { // in case w = +- 1, angle is 0 or pi, sin is 0, sin / angle is 1
			_TyScalar f_angle = f_RotationHalfAngle(); // contains "numerically robust" acos()
			_TyScalar f_sin = (_TyScalar)sin(f_angle);
			_TyScalar f_scale = (f_sin < -_TyScalar(1e-5) || f_sin > _TyScalar(1e-5))?
				f_angle / f_sin : 1; // lim(sin(a) / a) a -> 0 = 1
			return Quat<_TyScalar>(_TyVec::x * f_scale, _TyVec::y * f_scale, _TyVec::z * f_scale, 0);
		} else
			return Quat<_TyScalar>(_TyVec::x, _TyVec::y, _TyVec::z, 0);
	}

	/**
	 *	@brief calculates logarithm of this quaternion inplace
	 *
	 *	@note Inplace operations do not return value to avoid confusion for out of place operations.
	 *	@note This must be normalized.
	 */
	void Log()
	{
		// log(q) = log(cos(fi) + v * sin(fi)) = log(exp(v * fi)) = v * fi

		if(_TyVec::w > -1 && _TyVec::w < 1) { // in case w = +- 1, angle is 0 or pi, sin is 0, sin / angle is 1
			_TyScalar f_angle = f_RotationHalfAngle(); // contains "numerically robust" acos()
			_TyScalar f_sin = (_TyScalar)sin(f_angle);
			_TyScalar f_scale = (f_sin < -_TyScalar(1e-5) || f_sin > _TyScalar(1e-5))?
				f_angle / f_sin : 1; // lim(sin(a) / a) a -> 0 = 1
			_TyVec::x *= f_scale;
			_TyVec::y *= f_scale;
			_TyVec::z *= f_scale;
		}
		_TyVec::w = 0;
	}

	/*inline _TyScalar f_Dot(TConstArgType(Quat<_TyScalar>) r_t_quat) const // already in vec4
	{
		return x * r_t_quat.x + y * r_t_quat.y + z * r_t_quat.z + w * r_t_quat.w;
	}*/

	/**
	 *	@brief linear spherical interpolation between this and other quaternion
	 *
	 *	@param[in] f_t is rotation position (0 corresponds to no rotation, 1 to full rotation)
	 *	@param[in] t_quat_to is target rotation
	 *
	 *	@return Returns linear spherical interpolation between this and <tt>t_quat_to</tt>, at time <tt>f_t</tt>.
	 *
	 *	@note This can be used to extrapolate rotations (values
	 *		of <tt>f_t</tt> outside the [0, 1] interval are allowed).
	 */
	Quat<_TyScalar> t_Slerp(_TyScalar f_t,
		TConstArgType(Quat<_TyScalar>) r_t_quat_q) const
	{
		//_TyScalar f_cos = f_Dot(r_t_quat_q);
		_ASSERTE(fabs(f_Dot(r_t_quat_q)) <= 1 + _TyScalar(1e-5));
		_TyScalar f_cos = max(_TyScalar(-1), min(_TyScalar(1), f_Dot(r_t_quat_q)));
		// the dot can give result >1 due to numerical issues (error in vector normalization),
		// which would yield NaN in acos(); happens surprisingly frequently

		_TyScalar f_angle = (_TyScalar)acos(f_cos);
		if(f_angle < -_TyScalar(1e-5) || f_angle > _TyScalar(1e-5)) {
			_TyScalar f_sin = (_TyScalar)sin(f_angle);
			_TyScalar f_inv_sin = 1 / f_sin;
			_TyScalar f_weight_p = (_TyScalar)sin((1 - f_t) * f_angle) * f_inv_sin;
			_TyScalar f_weight_q = (_TyScalar)sin(f_t * f_angle) * f_inv_sin;
			return *this * f_weight_p + r_t_quat_q * f_weight_q;
		} // zero rotation otherwise
		return *this;
	}

	/**
	 *	@brief linear spherical interpolation between two quaternions
	 *
	 *	@param[in] f_t is rotation position (0 corresponds to no rotation, 1 to full rotation)
	 *	@param[in] t_quat_from is initial rotation
	 *	@param[in] t_quat_to is final rotation
	 *
	 *	@return Returns linear spherical interpolation between <tt>t_quat_from</tt>
	 *		and <tt>t_quat_to</tt>, at time <tt>f_t</tt>.
	 *
	 *	@note This can be used to extrapolate rotations (values
	 *		of <tt>f_t</tt> outside the [0, 1] interval are allowed).
	 */
	static Quat<_TyScalar> t_Slerp(_TyScalar f_t, TConstArgType(Quat<_TyScalar>) t_quat_from,
		TConstArgType(Quat<_TyScalar>) t_quat_to) // linear interpolation between t_quat_from and r_t_quat_to
	{
		return t_quat_from.t_Slerp(f_t, t_quat_to);
	}

	/**
	 *	@brief quadratic spherical interpolation between two quaternions
	 *
	 *	@param[in] f_t is rotation position (0 corresponds to no rotation, 1 to full rotation)
	 *	@param[in] r_t_quat_p is initial rotation
	 *	@param[in] r_t_quat_p0 is initial rotation
	 *	@param[in] r_t_quat_q0 is final rotation
	 *	@param[in] r_t_quat_q is final rotation
	 *
	 *	@return Returns quadratic spherical interpolation between <tt>r_t_quat_p</tt>, <tt>r_t_quat_p0</tt>
	 *		<tt>r_t_quat_q0</tt> and <tt>r_t_quat_q</tt>, at time <tt>f_t</tt>.
	 *
	 *	@note This can be used to extrapolate rotations (values
	 *		of <tt>f_t</tt> outside the [0, 1] interval are allowed).
	 */
	static Quat<_TyScalar> t_Squad(_TyScalar f_t, TConstArgType(Quat<_TyScalar>) r_t_quat_p,
		TConstArgType(Quat<_TyScalar>) r_t_quat_p0,
		TConstArgType(Quat<_TyScalar>) r_t_quat_q0,
		TConstArgType(Quat<_TyScalar>) r_t_quat_q) // cubic interpolation
	{
		Quat<_TyScalar> t_slerp_p = r_t_quat_p.t_Slerp(f_t, r_t_quat_q);
		Quat<_TyScalar> t_slerp_q = r_t_quat_p0.t_Slerp(f_t, r_t_quat_q0);
		return t_slerp_p.t_Slerp(2 * f_t * (1 - f_t), t_slerp_q);
	}

protected:
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER <= 1200
	void ToMatrix(const int n_stride, _TyScalar *p_matrix) const // MSVC 6.0 can't handle such a template function
#else // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
	template <const int n_stride>
	void ToMatrix(_TyScalar *p_matrix) const
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
	{
		_TyScalar x2 = _TyVec::x + _TyVec::x;
		_TyScalar y2 = _TyVec::y + _TyVec::y;
		_TyScalar z2 = _TyVec::z + _TyVec::z;
		{
			_TyScalar xx2 = _TyVec::x * x2;
			_TyScalar yy2 = _TyVec::y * y2;
			_TyScalar zz2 = _TyVec::z * z2;
			p_matrix[0 + n_stride * 0] = 1.0f - yy2 - zz2; // fixed at 2014-06-12 to match transformations obtained by chaining matrices
			p_matrix[1 + n_stride * 1] = 1.0f - xx2 - zz2;
			p_matrix[2 + n_stride * 2] = 1.0f - xx2 - yy2;
		}
		{
			_TyScalar yz2 = _TyVec::y * z2;
			_TyScalar wx2 = _TyVec::w * x2;
			p_matrix[1 + n_stride * 2] = yz2 - wx2;
			p_matrix[2 + n_stride * 1] = yz2 + wx2;
		}
		{
			_TyScalar xy2 = _TyVec::x * y2;
			_TyScalar wz2 = _TyVec::w * z2;
			p_matrix[0 + n_stride * 1] = xy2 - wz2;
			p_matrix[1 + n_stride * 0] = xy2 + wz2;
		}
		{
			_TyScalar xz2 = _TyVec::x * z2;
			_TyScalar wy2 = _TyVec::w * y2;
			p_matrix[2 + n_stride * 0] = xz2 - wy2;
			p_matrix[0 + n_stride * 2] = xz2 + wy2;
		}
	}

#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER <= 1200
	void FromMatrix(const int n_stride, const _TyScalar *p_matrix) // MSVC 6.0 can't handle such a template function
#else // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
	template <const int n_stride>
	void FromMatrix(const _TyScalar *p_matrix)
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER <= 1200
	{
		if(p_matrix[0 * n_stride + 0] + p_matrix[1 * n_stride + 1] + p_matrix[2 * n_stride + 2] > 0) { // fixed at 2014-06-12 to match transformations obtained by chaining matrices
			_TyScalar f_t = p_matrix[0 * n_stride + 0] + p_matrix[1 * n_stride + 1] + p_matrix[2 * n_stride + 2] + 1;
			_TyScalar f_s = (_TyScalar).5 / (_TyScalar)sqrt(double(f_t));
			_TyVec::w = f_s * f_t;
			_TyVec::z = (p_matrix[0 * n_stride + 1] - p_matrix[1 * n_stride + 0]) * f_s;
			_TyVec::y = (p_matrix[2 * n_stride + 0] - p_matrix[0 * n_stride + 2]) * f_s;
			_TyVec::x = (p_matrix[1 * n_stride + 2] - p_matrix[2 * n_stride + 1]) * f_s;
		} else if(p_matrix[0 * n_stride + 0] > p_matrix[1 * n_stride + 1] &&
		   p_matrix[0 * n_stride + 0] > p_matrix[2 * n_stride + 2]) {
			_TyScalar f_t = p_matrix[0 * n_stride + 0] - p_matrix[1 * n_stride + 1] - p_matrix[2 * n_stride + 2] + 1;
			_TyScalar f_s = (_TyScalar).5 / (_TyScalar)sqrt(double(f_t));
			_TyVec::x = f_s * f_t;
			_TyVec::y = (p_matrix[0 * n_stride + 1] + p_matrix[1 * n_stride + 0]) * f_s;
			_TyVec::z = (p_matrix[2 * n_stride + 0] + p_matrix[0 * n_stride + 2]) * f_s;
			_TyVec::w = (p_matrix[1 * n_stride + 2] - p_matrix[2 * n_stride + 1]) * f_s;
		} else if(p_matrix[1 * n_stride + 1] > p_matrix[2 * n_stride + 2]) {
			_TyScalar f_t = -p_matrix[0 * n_stride + 0] + p_matrix[1 * n_stride + 1] - p_matrix[2 * n_stride + 2] + 1;
			_TyScalar f_s = (_TyScalar).5 / (_TyScalar)sqrt(double(f_t));
			_TyVec::y = f_s * f_t;
			_TyVec::x = (p_matrix[0 * n_stride + 1] + p_matrix[1 * n_stride + 0]) * f_s;
			_TyVec::w = (p_matrix[2 * n_stride + 0] - p_matrix[0 * n_stride + 2]) * f_s;
			_TyVec::z = (p_matrix[1 * n_stride + 2] + p_matrix[2 * n_stride + 1]) * f_s;
		} else {
			_TyScalar f_t = -p_matrix[0 * n_stride + 0] - p_matrix[1 * n_stride + 1] + p_matrix[2 * n_stride + 2] + 1;
			_TyScalar f_s = (_TyScalar).5 / (_TyScalar)sqrt(double(f_t));
			_TyVec::z = f_s * f_t;
			_TyVec::w = (p_matrix[0 * n_stride + 1] - p_matrix[1 * n_stride + 0]) * f_s;
			_TyVec::x = (p_matrix[2 * n_stride + 0] + p_matrix[0 * n_stride + 2]) * f_s;
			_TyVec::y = (p_matrix[1 * n_stride + 2] + p_matrix[2 * n_stride + 1]) * f_s;
		}
	}
};

/**
 *	@brief single precision floating-point quaternion
 */
typedef Quat<float> Quatf;

/**
 *	@brief double precision floating-point quaternion
 */
typedef Quat<double> Quatd;

/*
 *								=== ~Quat ===
 */

#ifdef __VECTOR_H_WANT_CPOLYGON

/*
 *								=== TVertex3f ===
 */

struct TVertex3f : public Vector3f {
	TVertex3f();
	TVertex3f(ConstArgType(Vector3f) r_v_vec)
		:Vector3f(r_v_vec)
	{}

	inline ConstResultType(Vector3f) v_Pos() const
	{
		return *this;
	}

	inline WritableResultType(Vector3f) v_Pos()
	{
		return *this;
	}

	inline TVertex3f operator *(float f_scalar) const
	{
		return *this * f_scalar;
	}

	inline TVertex3f operator +(ConstArgType(TVertex3f) r_vertex) const
	{
		return *this + r_vertex;
	}

	TVertex3f t_Lerp(float f_t, ConstArgType(TVertex3f) r_t_right) const
	{
		return *this + (r_t_right - *this) * f_t;
	}
};

/*
 *								=== ~TVertex3f ===
 */

/*
 *								=== CPolygon ===
 */

template<class TVertStruct>
class CPolygon {
protected:
	std::vector<TVertStruct> m_vertex_list;
	Plane3f m_t_normal;

public:
	/*
	 *	void Delete()
	 *		- delete all polygon vertices
	 */
	void Delete()
	{
		m_vertex_list.clear();
	}

	/*
	 *	inline bool Add_Vertex(ConstArgType(TVertStruct) r_t_vertex)
	 *		- add single vertex past the last vertex in the array
	 */
	inline bool Add_Vertex(ConstArgType(TVertStruct) r_t_vertex)
	{
		return Add_Vertex(0, &r_t_vertex, 1);
	}

	/*
	 *	inline bool Add_Vertex(int n_insert_before, ConstArgType(TVertStruct) r_t_vertex)
	 *		- add single vertex before the n_insert_before-th vertex in the array
	 */
	inline bool Add_Vertex(int n_insert_before, ConstArgType(TVertStruct) r_t_vertex)
	{
		return Add_Vertex(n_insert_before, &r_t_vertex, 1);
	}

	/*
	 *	inline bool Add_Vertex(const TVertStruct *p_vertex, int n_count)
	 *		- add array of vertices past the last vertex in the array
	 */
	inline bool Add_Vertex(const TVertStruct *p_vertex, int n_count)
	{
		return Add_Vertex(0, p_vertex, n_count);
	}

	/*
	 *	bool Add_Vertex(int n_insert_before, const TVertStruct *p_vertex, int n_count)
	 *		- add array of vertices before the n_insert_before-th vertex in the array
	 */
	bool Add_Vertex(int n_insert_before, const TVertStruct *p_vertex, int n_count)
	{
		_ASSERTE(n_count > 0);
		_ASSERTE(n_insert_before >= 0 && n_insert_before < m_vertex_list.size());
		if(!stl_ut::Reserve_NMore(m_vertex_list, n_count))
			return false;
		m_vertex_list.insert(m_vertex_list.begin() + n_insert_before,
			p_vertex, p_vertex + n_count);
		return true;
	}

	/*
	 *	void Delete_Vertices(int n_index, int n_count)
	 *		- delete n_count vertices, beggining with vertex n_index
	 */
	void Delete_Vertices(int n_index, int n_count)
	{
		m_vertex_list.erase(m_vertex_list.begin() + n_index,
			m_vertex_list.begin() + (n_index + n_count));
	}

	/*
	 *	inline int n_Vertex_Num() const
	 *		- return number of vertices
	 */
	inline int n_Vertex_Num() const
	{
		return m_vertex_list.size();
	}

	/*
	 *	inline WritableResultType(TVertStruct) r_t_Vertex(int n_index) const
	 *		- vertex access function
	 *		- doesn't check array bounds
	 */
	inline WritableResultType(TVertStruct) r_t_Vertex(int n_index)
	{
		return m_vertex_list[n_index];
	}

	/*
	 *	inline TVertStruct &t_Vertex(int n_index) const
	 *		- vertex access function
	 *		- doesn't check array bounds
	 */
	inline ConstResultType(TVertStruct) t_Vertex(int n_index) const
	{
		return m_vertex_list[n_index];
	}

	/*
	 *	const Vector3f v_Center() const
	 *		- return position of center of polygon
	 *		- if polygon has no vertices, return the O vector
	 */
	const Vector3f v_Center() const
	{
		if(m_vertex_list.empty())
			return Vector3f(0, 0, 0);
		Vector3f v_center(0, 0, 0);
		for(int i = 0; i < m_vertex_list.size(); ++ i)
			v_center += m_vertex_list[i].v_Pos();
		v_center *= 1.0f / (float)m_vertex_list.size();

		return v_center;
	}

	/*
	 *	const Vector3f v_NearestPoint(ConstArgType(Vector3f) r_v_point) const
	 *		- return point, lying on polygon, nearest to <r_v_point>
	 *		- must have valid normal and >= 3 vertices
	 */
	const Vector3f v_NearestPoint(ConstArgType(Vector3f) r_v_point) const // t_odo - rewrite using std::for_each
	{
		Vector3f v_nearest;
		try {
			v_nearest = m_t_normal.v_Intersect_Ray(r_v_point, m_t_normal.v_normal);
		} catch(Plane3f::CRayIsParallel_Exception) {
			_ASSERTE(0); // normal should be never coplanar
		}
		// try point, projected perpendicularily

		if(m_vertex_list.size() < 3)
			return v_nearest;

		float f_distance;
		if(b_Contain_Point(v_nearest))
			f_distance = (v_nearest - r_v_point).f_Length2();
		else
			f_distance = -1;

		Vector3f v_prev = m_vertex_list[m_vertex_list.size() - 1].v_Pos();
		for(int i = 0; i < m_vertex_list.size(); v_prev = m_vertex_list[i ++].v_Pos()) {
			Vector3f v_cur = m_vertex_list[i].v_Pos();
			Vector3f v_edge = v_prev - v_cur, v_to_cur = r_v_point - v_cur;
			float f_len = v_edge.f_Length();
			v_edge *= 1.0 / f_len;
			float t = v_edge.f_Dot(v_to_cur); // projected to edge
			t = (t < 0)? 0 : ((t > f_len)? f_len : t); // clamp
			Vector3f v_near = v_cur + v_edge * t;

			if((v_near - r_v_point).f_Length2() < f_distance || f_distance < 0) {
				v_nearest = v_near;
				f_distance = (v_nearest - r_v_point).f_Length2();
			}
		}
		// try to find on edges ...

		return v_nearest;
	}

	/*
	 *	bool Calc_Normal()
	 *		- calculate normal, return false if there wasn't trinity
	 *		  of vertices to construct plane from
	 *		- it has to be called explicitly, it is never called by CPolygon itself
	 *		- normal state is saved onto disk, though
	 */
	bool Calc_Normal(float f_epsilon_ex = f_epsilon, float f_edge_epsilon_ex = f_edge_epsilon)
	{
		Vector3f v_u, v_v;
		Vector3f v_norm;

		if(m_vertex_list.size() < 3)
			return false;

		for(typename std::vector<TVertStruct>::const_iterator p_vertex_0 = m_vertex_list.begin(),
		   p_vertex_a = m_vertex_list.begin() + 1; p_vertex_a < m_vertex_list.end();
		   p_vertex_0 = p_vertex_a ++) {
			if((v_u = (*p_vertex_0).v_Pos() - (*p_vertex_a).v_Pos()).f_Length() >
			   f_edge_epsilon_ex) {
				for(typename std::vector<TVertStruct>::const_iterator p_vertex_b =
				   m_vertex_list.begin() + 1; p_vertex_b < m_vertex_list.end(); p_vertex_b ++) {
					if(p_vertex_a == p_vertex_b || p_vertex_b == p_vertex_0)
						continue;
					if((v_v = (*p_vertex_a).v_Pos() - (*p_vertex_b).v_Pos()).f_Length() >
					   f_edge_epsilon_ex) {
						v_norm = v_u.v_Cross(v_v);
						if(v_norm.f_Length() < f_epsilon_ex)
							continue;
						v_norm.Normalize();

						m_t_normal = Plane3f(m_vertex_list[0].v_Pos(), v_norm);
						return true;
					}
				}
			}
		}
		// seek such a vertices that does have distance between them and they aren't colinear

		return false;
	}

	/*
	 *	inline WritableResultType(Plane3f) r_t_Normal()
	 *		- return reference to a normal
	 *		- when you are going to split polygons, it's better to
	 *		  overwrite normals because once polygons are too small, you
	 *		  can't safely compute them again (that's why normals are saved into file)
	 */
	inline WritableResultType(Plane3f) r_t_Normal()
	{
		return m_t_normal;
	}

	/*
	 *	inline ConstResultType(Plane3f) t_Normal() const
	 *		- return normal
	 */
	inline ConstResultType(Plane3f) t_Normal() const
	{
		return m_t_normal;
	}

	/*
	 *	EPlanePos n_Plane_Pos(ConstArgType(Plane3f) t_plane, float f_epsilon_ex) const
	 *		- check position of polygon against plane, explicit epsilon
	 *		- possible return values are plane_Back, plane_Front, plane_Onplane or plane_Split
	 */
	EPlanePos n_Plane_Pos(ConstArgType(Plane3f) t_plane, float f_epsilon_ex = f_epsilon) const
	{
		bool b_front, b_back;

		for(int i = 0; i < m_vertex_list.size(); ++ i) {
			switch(t_plane.n_Vector_Pos(m_vertex_list[i].v_Pos(), f_epsilon_ex)) {
			case plane_Front:
				b_front = true;
				break;
			case plane_Back:
				b_back = true;
				break;
			}
		}
		if(!b_back) {
			if(!b_front)
				return plane_Onplane;
			return plane_Front;
		} else {
			if(!b_front)
				return plane_Back;
			return plane_Split;
		}
	}

	/*
	 *	bool Cut(ConstArgType(Plane3f) t_plane, EPlanePos n_desired_half, float f_epsilon_ex)
	 *		- seems to be working for non-convex polygons as well
	 *		- cuts polygon against plane so the rest lies completely
	 *		  in desired halfspace, defined by plane
	 *		- to perform actual cutting, n_desired_half must be one of plane_Back or plane_Front
	 *		  otherwise actual plane position is compared against n_desired_half
	 *		  and in case it differs, polygon is erased (otherwise polygon is kept as-is)
	 *		  i.e. for example in case plane_Split, only polygon that is split by plane is kept
	 *		- polygon, lying completely onplane is cut away! (in case cutting is performed at all)
	 *		- explicit epsilon
	 */
	bool Cut(ConstArgType(Plane3f) t_plane, EPlanePos n_desired_half, float f_epsilon_ex = f_epsilon)
	{
		if(m_vertex_list.size() < 3)
			return true;
		// it must be polygon

		EPlanePos n_side, n_prev_side;
		TVertStruct v_cur, v_prev;
		EPlanePos n_other_side;

		if(n_desired_half == plane_Front)
			n_other_side = plane_Back;
		else if(n_desired_half == plane_Back)
			n_other_side = plane_Front;
		else {
			if(n_Plane_Pos(t_plane, f_epsilon_ex) != n_desired_half)
				Delete();
			return true;
		}
		// other half

		bool b_onplane_edges = false;

		v_prev = m_vertex_list.back();
		n_prev_side = t_plane.n_Vector_Pos(v_prev.v_Pos(), f_epsilon_ex);
		for(int i = 0; i < m_vertex_list.size(); v_prev = v_cur, n_prev_side = n_side, ++ i) {
			v_cur = m_vertex_list[i];
			n_side = t_plane.n_Vector_Pos(v_cur.v_Pos(), f_epsilon_ex);
			//
			if(n_prev_side == n_other_side) { // prev vertex was on the side to be cut ...
				if(n_side == n_desired_half) { // current vertex is on our side ... calc intersection & insert current vertex
					m_vertex_list[i] = v_cur;
					if(!stl_ut::Reserve_1More(m_vertex_list))
						return false;
					m_vertex_list.insert(&m_vertex_list[i ++], v_cur.t_Lerp(t_plane.f_Intersect_Ray_t(v_cur.v_Pos(), v_prev.v_Pos() - v_cur.v_Pos()), v_prev));
				} else if(n_side != plane_Onplane) // current vertex is not on our side either -> it's about to be erased
					m_vertex_list.erase(m_vertex_list.begin() + i --);
				// otherwise current vertex is onplane and we're going to keep it as is
			} else { // prev vertex was on our side ...
				if(n_side == n_other_side) { // now, current vertex is on the other side
					if(n_prev_side != plane_Onplane) // prev vertex was on our side
						m_vertex_list[i] = v_cur.t_Lerp(t_plane.f_Intersect_Ray_t(v_cur.v_Pos(), v_prev.v_Pos() - v_cur.v_Pos()), v_prev);
					else // prev vertex was onplane -> erase cur. vertex
						m_vertex_list.erase(m_vertex_list.begin() + i --);
				}
				// otherwise cur vertex is on our side or on the edge so we'll keep it as is
			}
			if(n_side == plane_Onplane && n_prev_side == plane_Onplane)
				b_onplane_edges = true;
		}
		// cut polygon so only the part, lying on <n_desired_half> side of plane remains

		if(b_onplane_edges) {
			v_prev = m_vertex_list.back();
			n_prev_side = t_plane.n_Vector_Pos(v_prev.v_Pos(), f_epsilon_ex);
			for(int i = 0; i < m_vertex_list.size(); v_prev = v_cur, n_prev_side = n_side, ++ i) {
				v_cur = m_vertex_list[i];
				n_side = t_plane.n_Vector_Pos(v_cur.v_Pos(), f_epsilon_ex);

				if(n_side == plane_Onplane && n_prev_side == plane_Onplane) {
					Vector3f v_dir[2] = {v_cur.v_Pos() - v_prev.v_Pos(),
						m_vertex_list[(i + 1) % m_vertex_list.size()].v_Pos() - v_cur.v_Pos()};

					v_dir[0].Normalize();
					v_dir[1].Normalize();

					if(fabsf(v_dir[0].f_Dot(v_dir[1]) + 1.0f) < f_epsilon_ex) // ugly overlap
						m_vertex_list.erase(m_vertex_list.begin() + i --);
				}
			}
		}
		// check for onplane edges - there may occur edges, overlaping themselves
		// - work well for non-convex polygon situations
		// - work well for case of polygons whose all points are onplane (deletes them)

		if(m_vertex_list.size() < 3)
			m_vertex_list.clear();

		return true;
	}

	/*
	 *	bool Split(ConstArgType(Plane3f) t_plane,
	 *		CPolygon<TVertStruct> &r_new_poly, EPlanePos n_desired_half, float f_epsilon_ex)
	 *		- cut <this> polygon to n_desired_halfe halfspace, defined by plane
	 *		  and cut r_new_poly (where it loads a copy of <this>) to the other one
	 *		- if n_desired_half is one of plane_Onplane or plane_Split, <this> polygon
	 *		  will be copied to r_new_poly and erased in case it lies in different
	 *		  position against plane than specified. otherwise r_new_poly is erased
	 *		- return false if there's not enough memory
	 */
	bool Split(ConstArgType(Plane3f) t_plane, CPolygon<TVertStruct> &r_new_poly,
		EPlanePos n_desired_half, float f_epsilon_ex = f_epsilon) // t_odo - rewrite cut for two polygons
	{
		EPlanePos n_other_half;

		if(n_desired_half == plane_Front)
			n_other_half = plane_Back;
		else if(n_desired_half == plane_Back)
			n_other_half = plane_Front;
		else {
			if(n_Plane_Pos(t_plane, f_epsilon_ex) != n_desired_half) {
				if(!(r_new_poly = *this))
					return false;
				// copy, check if there was enough memory

				Delete();
			} else
				r_new_poly.Delete();
			// keep polygon, lying in the right position

			return true;
		}
		// other half

		if(!(r_new_poly = *this))
			return false;
		// copy, check if there was enough memory

		return r_new_poly.Cut(t_plane, n_other_half) && Cut(t_plane, n_desired_half);
		// make two copys of current polygon and split them
	}

	class CPointOutside {
	protected:
		Vector3f m_v_prev;
		Vector3f m_v_normal;
		Vector3f m_v_point;

	public:
		CPointOutside(ConstArgType(Vector3f) r_v_point,
			ConstArgType(Vector3f) r_v_prev, ConstArgType(Vector3f) r_v_normal)
			:m_v_point(r_v_point), m_v_prev(r_v_prev), m_v_normal(r_v_normal)
		{}

		inline bool operator ()(ConstArgType(TVertStruct) r_vertex)
		{
			Vector3f v_cur = r_vertex.v_Pos();
			Plane3f t_edge(m_v_normal, v_cur - m_v_prev, v_cur);
			m_v_prev = v_cur;
			return t_edge.n_Vector_Pos(m_v_point) == plane_Back;
		}
	};

	/*
	 *	bool b_Contain_Point(ConstArgType(Vector3f) r_v_point) const
	 *		- method with edge and normal collinear planes (work with convex polygons only)
	 *		- need to have calculated normal
	 */
	bool b_Contain_Point(ConstArgType(Vector3f) r_v_point) const
	{
		return std::find_if(m_vertex_list.begin(), m_vertex_list.end(), CPointOutside(r_v_point,
			m_vertex_list.back().v_Pos(), m_t_normal.v_normal)) == m_vertex_list.end();
		// clearer and possibly faster version with std::find_if

		/*for(int i = 0, n_prev = m_vertex_list.size() - 1; i < m_vertex_list.size(); n_prev = i ++) {
			Plane3f t_edge(m_t_normal.v_normal, m_vertex_list[i].v_Pos() -
				m_vertex_list[n_prev].v_Pos(), m_vertex_list[i].v_Pos());
			if(t_edge.n_Vector_Pos(r_v_point) == plane_Back)
				return false;
		}
		return true;*/
	}

	template <const bool b_cull_back = false>
	class CTriDecomposerRayHit {
	protected:
		Vector3f m_v_vert0;
		Vector3f m_v_vert1;

		const Vector3f &m_r_v_org, &m_r_v_dir;
		float &m_r_f_t, &m_r_f_u, &m_r_f_v;

	public:
		inline CTriDecomposerRayHit(Vector3f v_first, Vector3f v_prev,
			const Vector3f &r_v_org, const Vector3f &r_v_dir,
			float &r_f_t, float &r_f_u, float &r_f_v)
			:m_v_vert0(v_first), m_v_vert1(v_prev), m_r_v_org(r_v_org), m_r_v_dir(r_v_dir),
			m_r_f_t(r_f_t), m_r_f_u(r_f_u), m_r_f_v(r_f_v)
		{}

		inline bool operator ()(ConstArgType(TVertStruct) r_vertex)
		{
			Vector3f v_vert2 = r_vertex.v_Pos();
			Vector3f v_edge1 = m_v_vert1 - m_v_vert0,
					 v_edge2 = v_vert2 - m_v_vert0;
			Vector3f v_p = m_r_v_dir.v_Cross(v_edge2);
			float f_det = v_edge1.f_Dot(v_p);

			m_v_vert1 = v_vert2; // shift vertices

			if(b_cull_back) {
				if(f_det < f_epsilon)
					return false;
			} else {
				if(f_det > -f_epsilon && f_det < f_epsilon)
					return false;
			}
			f_det = 1.0f / f_det;
			Vector3f v_t = m_r_v_org - m_v_vert0;
			m_r_f_u = v_t.f_Dot(v_p) * f_det;
			if(m_r_f_u < 0.0f || m_r_f_u > 1.0f)
				return false;
			Vector3f v_q = v_t.v_Cross(v_edge1);
			m_r_f_v = m_r_v_dir.f_Dot(v_q) * f_det;
			if(m_r_f_v < 0.0f || m_r_f_u + m_r_f_v > 1.0f)
				return false;
			m_r_f_t = v_edge2.f_Dot(v_q) * f_det;
			return true;
		}
	};

	/*
	 *	inline bool b_MT_RayHit(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir,
	 *		float &r_f_t, float &r_f_u, float &r_f_v) const
	 *		- Moller-Trumbore ray-triangle hit test
	 *		- v_org and v_dir are ray source and direction,
	 *		- r_f_t is time of ray to hit
	 *		- r_f_u and r_f_v are barycentric coordinates of intersection
	 *		- return true if ray hits polygon, otherwise false
	 *		- hard to say if it's actualy faster or slower than the plane check,
	 *		  it definitely suffers from poylgon to triangle decomposition
	 */
	inline bool b_MT_RayHit(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir,
		float &r_f_t, float &r_f_u, float &r_f_v) const
	{
		if(m_vertex_list.size() < 3)
			return false;
		return std::find_if(m_vertex_list.begin() + 2, m_vertex_list.end(),
			CTriDecomposerRayHit<false>(m_vertex_list[0].v_Pos(), m_vertex_list[1].v_Pos(),
			r_v_org, r_v_dir, r_f_t, r_f_u, r_f_v)) != m_vertex_list.end();
		// clearer and possibly faster version with std::find_if

		/*for(int i = 1; i < m_vertex_list.size() - 1; ++ i) {
			const TVertStruct *p_vert0 = &m_vertex_list[0];
			const TVertStruct *p_vert1 = &m_vertex_list[i];
			const TVertStruct *p_vert2 = &m_vertex_list[(i + 1) % m_vertex_list.size()];

			Vector3f v_edge1 = p_vert1->v_Pos() - p_vert0->v_Pos(),
					 v_edge2 = p_vert2->v_Pos() - p_vert0->v_Pos();
			Vector3f v_p = r_v_dir.v_Cross(v_edge2);
			float f_det = v_edge1.f_Dot(v_p);

			if(f_det > -f_epsilon && f_det < f_epsilon)
				continue;
			f_det = 1.0f / f_det;

			Vector3f v_t = r_v_org - p_vert0->v_Pos();

			r_f_u = v_t.f_Dot(v_p) * f_det;
			if(r_f_u < 0.0f || r_f_u > 1.0f)
				continue;

			Vector3f v_q = v_t.v_Cross(v_edge1);

			r_f_v = r_v_dir.f_Dot(v_q) * f_det;
			if(r_f_v < 0.0f || r_f_u + r_f_v > 1.0f)
				continue;

			r_f_t = v_edge2.f_Dot(v_q) * f_det;

			return true;
		}
		return false;*/ // t_odo - perform speed test by raytracing some real mesh
	}

	/*
	 *	bool b_MT_RayHit_CullBackfaces(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir,
	 *		float &r_f_t, float &r_f_u, float &r_f_v) const
	 *		- the same as b_MT_RayHit, but in case the ray is coming from behind the polygon
	 *		  (dot product of direction of ray and polygon normal is positive),
	 *		  intersection isn't detected even if it does occur
	 */
	bool b_MT_RayHit_CullBackfaces(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir,
		float &r_f_t, float &r_f_u, float &r_f_v) const
	{
		if(m_vertex_list.size() < 3)
			return false;
		return std::find_if(m_vertex_list.begin() + 2, m_vertex_list.end(),
			CTriDecomposerRayHit<true>(m_vertex_list[0].v_Pos(), m_vertex_list[1].v_Pos(),
			r_v_org, r_v_dir, r_f_t, r_f_u, r_f_v)) != m_vertex_list.end();
		// clearer and possibly faster version with std::find_if
	}

	bool b_Collide(ConstArgType(CPolygon<TVertStruct>) r_polygon) const
	{
		if(r_polygon.m_vertex_list.size() < 3 || m_vertex_list.size() < 3) // t_odo
			return false;
		{
			const CPolygon<TVertStruct> &r_a = *this;
			const CPolygon<TVertStruct> &r_b = r_polygon;

			Vector3f v_edge_a = r_a.m_vertex_list[r_a.m_vertex_list.size() - 1].v_Pos(),
			   v_edge_b = r_a.m_vertex_list[0].v_Pos();
			for(int i = 0;;) {
				float t, u, v;
				if(r_b.b_MT_RayHit(v_edge_a, v_edge_b - v_edge_a, t, u, v) && t >= 0.0f && t <= 1.0f)
					return true;
				v_edge_a = v_edge_b;
				if(++ i < r_a.m_vertex_list.size())
					v_edge_b = r_a.m_vertex_list[i].v_Pos();
				else
					break;
			}
		}
		{
			const CPolygon<TVertStruct> &r_a = r_polygon;
			const CPolygon<TVertStruct> &r_b = *this;

			Vector3f v_edge_a = r_a.m_vertex_list[r_a.m_vertex_list.size() - 1].v_Pos(),
			   v_edge_b = r_a.m_vertex_list[0].v_Pos();
			for(int i = 0;;) {
				float t, u, v;
				if(r_b.b_MT_RayHit(v_edge_a, v_edge_b - v_edge_a, t, u, v) && t >= 0.0f && t <= 1.0f)
					return true;
				v_edge_a = v_edge_b;
				if(++ i < r_a.m_vertex_list.size())
					v_edge_b = r_a.m_vertex_list[i].v_Pos();
				else
					break;
			}
		}
		// see if this polygon is intersected by any of r_polygon's edges
		// (being touched by vertex alsou counts as collision)
		// it needs to check edge intersection this->r_polygon AND vice-versa
		return false;
	}

	bool Write(FILE *p_file) const
	{
		int n_temp = m_vertex_list.size();
		return fwrite(&m_t_normal, 1, sizeof(Plane3f), p_file) &&
			fwrite(&n_temp, 1, sizeof(int), p_file) &&
			fwrite(m_vertex_list.begin(), m_vertex_list.size(), sizeof(TVertStruct), p_file);
	}

	bool Read(FILE *p_file)
	{
		if(!fread(&m_t_normal, 1, sizeof(Plane3f), p_file))
			return false;
		int n_temp;
		if(!fread(&n_temp, sizeof(int), 1, p_file))
			return false;
		m_vertex_list.clear();
		if(!stl_ut::Reserve_N(m_vertex_list, n_temp))
			return false;
		for(int i = 0; i < n_temp; ++ i) {
			TVertStruct v_tmp;
			if(!fread(&v_tmp, sizeof(TVertStruct), 1, p_file))
				return false;
			m_vertex_list.push_back(v_tmp);
		}
		if(m_vertex_list.size() < n_temp)
			return false;
		return true;
	}

	/*
	 *	bool operator = (ConstArgType(CPolygon<TVertStruct>) r_poly)
	 *		- return value is true if everything went ok
	 */
	bool operator = (ConstArgType(CPolygon<TVertStruct>) r_poly)
	{
		m_t_normal = r_poly.m_t_normal;
		m_vertex_list.clear();
		if(!stl_ut::Reserve_N(m_vertex_list, r_poly.m_vertex_list.size()))
			return false;
		m_vertex_list.insert(m_vertex_list.end(),
			r_poly.m_vertex_list.begin(), r_poly.m_vertex_list.end());
		return true;
	}

	/*
	 *	void Reverb()
	 *		- reverb order of vertices. doesn't need any extra memory so it can't fail
	 */
	void Reverb()
	{
		TVertStruct v_temp;

		for(int i = 0; i < m_vertex_list.size() / 2; ++ i) {
			v_temp = m_vertex_list[i];
			m_vertex_list[i] = m_vertex_list[m_vertex_list.size() - i - 1];
			m_vertex_list[m_vertex_list.size() - i - 1] = v_temp;
		}
		// prohodí vrcholy
	}

	/*
	 *	bool b_Overlap(ConstArgType(CPolygon<TVertStruct>) r_poly) const
	 *		- return true if polygons overlap, otherwise false
	 */
	bool b_Overlap(ConstArgType(CPolygon<TVertStruct>) r_poly) const
	{
		for(int i = 0; i < r_poly.m_vertex_list.size(); ++ i) {
			if(b_Contain_Point(r_poly.m_vertex_list[i].v_Pos()))
				return true;
		}
		for(int i = 0; i < m_vertex_list.size(); ++ i) {
			if(r_poly.b_Contain_Point(m_vertex_list[i].v_Pos()))
				return true;
		}
		return false;
	}


	/*
	 *	bool b_IsTiny(float f_epsilon_ex, float f_edge_epsilon_ex)
	 *		- return true if polygon is considered to be tiny
	 *		  (either can't calculate normal or it's area is under treshold)
	 *		- explicit epsilon
	 */
	bool b_IsTiny(float f_epsilon_ex = f_epsilon, float f_edge_epsilon_ex = f_edge_epsilon)
	{
		Plane3f t_tmp_normal = m_t_normal;
		bool b_have_normal = Calc_Normal(f_epsilon_ex, f_edge_epsilon_ex);
		m_t_normal = t_tmp_normal;
		// if there are three vertices, far enough to calculate the plane, polygon isn't tiny

		return b_have_normal;
	}
};

/*
 *								=== ~CPolygon ===
 */

#endif // __VECTOR_H_WANT_CPOLYGON

#ifdef __VECTOR_H_WANT_CVECTORMATH

/*
 *								=== CVectorMath ===
 */

class CVectorMath {
public:
	/*
	 *	static Vector3f v_Project(ConstArgType(Vector3f) r_v_vec, ConstArgType(Vector3f) r_v_ray_org,
	 *		ConstArgType(Vector3f) r_v_ray_dir, float f_ray_dir_length2 = 1.0f)
	 *		- project vector r_v_vec onto ray coming trough point r_v_ray_org
	 *		  in direction r_v_ray_dir
	 *		- f_ray_dir_length2 is squared length of r_v_ray_dir (or 1 in case it's unit length)
	 */
	static Vector3f v_Project(ConstArgType(Vector3f) r_v_vec, ConstArgType(Vector3f) r_v_ray_org,
		ConstArgType(Vector3f) r_v_ray_dir, float f_ray_dir_length2 = 1.0f)
	{
		return r_v_ray_org + r_v_ray_dir *
			(r_v_ray_dir.f_Dot(r_v_vec - r_v_ray_org) / f_ray_dir_length2);
	}

	/*
	 *	static Vector3f v_Nearest_Point(ConstArgType(Vector3f) r_v_ray_org, ConstArgType(Vector3f) r_v_ray_dir,
	 *		ConstArgType(Vector3f) r_v_ray2_org, ConstArgType(Vector3f) r_v_ray2_dir)
	 *		- returns nearest point, lying on ray [r_v_ray_org, r_v_ray_dir] to ray
	 *		  [r_v_ray2_org, r_v_ray2_dir]
	 *		- ray directions don't have to be unit length
	 *		- doesn't work for colinear rays - every point on ray is nearest in such a case
	 */
	static Vector3f v_Nearest_Point(ConstArgType(Vector3f) r_v_ray_org, ConstArgType(Vector3f) r_v_ray_dir,
		ConstArgType(Vector3f) r_v_ray2_org, ConstArgType(Vector3f) r_v_ray2_dir)
	{
		Plane3f t_coplane(r_v_ray2_org, r_v_ray2_dir.v_Cross(r_v_ray2_dir.v_Cross(r_v_ray_dir)));
		// plane, this line lies on and it' also prependicular to both this and r_t_line

		Vector3f v_intersection;
		if(t_coplane.Intersect_Ray(v_intersection, r_v_ray_org, r_v_ray_dir)) {
			return v_intersection;
			// intersection of plane with this line is nearest point to r_t_line
			// (nearest points are connected by line, prependicular to both of them)
		} else {
			return (r_v_ray_org - v_Project(r_v_ray_org, r_v_ray2_org, r_v_ray2_dir, r_v_ray2_dir.f_Length2()));
			// rays are parallel
		}
	}

	/*
	 * static inline float f_Det3(float f_00, float f_10, float f_20,
	 *							  float f_01, float f_11, float f_21,
	 *							  float f_02, float f_12, float f_22)
	 *		- calculates determinant of 3rd-order matrix
	 */
	static inline float f_Det3(float f_00, float f_10, float f_20,
							   float f_01, float f_11, float f_21,
							   float f_02, float f_12, float f_22)
	{
		return (f_00 * f_11 * f_22) + (f_10 * f_21 * f_02) + (f_01 * f_12 * f_20) -
			   (f_20 * f_11 * f_02) - (f_10 * f_01 * f_22) - (f_00 * f_21 * f_12);
	}

	/*
	 * static inline float f_Det2(float f_00, float f_10,
	 *							  float f_01, float f_11)
	 *		- calculates determinant of 2nd-order matrix
	 */
	static inline float f_Det2(float f_00, float f_10,
							   float f_01, float f_11)
	{
		return (f_00 * f_11) - (f_01 * f_10);
	}

	/*
	 *	static inline float f_Sqr(float f_x)
	 *		- calculates 2nd power of x
	 */
	static inline float f_Sqr(float f_x)
	{
		return f_x * f_x;
	}

	/*
	 *	static float f_Min_Distance(ConstArgType(Vector3f) r_v_ray_org, ConstArgType(Vector3f) r_v_ray_dir,
	 *		ConstArgType(Vector3f) r_v_ray2_org, ConstArgType(Vector3f) r_v_ray2_dir)
	 *		- returns minimal distance of ray [r_v_ray_org, r_v_ray_dir] to ray
	 *		  [r_v_ray2_org, r_v_ray2_dir]
	 *		- ray directions don't have to be unit length
	 *		- works for colinear rays as well
	 */
	static float f_Min_Distance(ConstArgType(Vector3f) r_v_ray_org, ConstArgType(Vector3f) r_v_ray_dir,
		ConstArgType(Vector3f) r_v_ray2_org, ConstArgType(Vector3f) r_v_ray2_dir)
	{
		Vector3f v_org_dist = r_v_ray_org - r_v_ray2_org;

		return (float)fabs(f_Det3(v_org_dist.x, v_org_dist.y, v_org_dist.z,
								  r_v_ray_dir.x, r_v_ray_dir.y, r_v_ray_dir.z,
								  r_v_ray2_dir.x, r_v_ray2_dir.y, r_v_ray2_dir.z)) / (float)sqrt(
			f_Sqr(f_Det2(r_v_ray_dir.y, r_v_ray_dir.z,
						 r_v_ray2_dir.y, r_v_ray2_dir.z)) +
			f_Sqr(f_Det2(r_v_ray_dir.z, r_v_ray_dir.x,
						 r_v_ray2_dir.z, r_v_ray2_dir.x)) +
			f_Sqr(f_Det2(r_v_ray_dir.x, r_v_ray_dir.y,
						 r_v_ray2_dir.x, r_v_ray2_dir.y)));
		// 1 sqrt, 21 mul, 1 div, 7 add, 3 sub = 8 + 42 + 2 + 7 + 3 = 62 ticks
		// formula from Hans-Jochen Bartsch's math book (results are almost identical
		// to those, obtained by meassuring distance between nerarest points on rays)
	}

	/*
	 *	struct TAxisAlignedBox
	 *		- bounding box, aligned with coordinate axes
	 */
	struct TAxisAlignedBox {
		Vector3f v_center; // box center
		Vector3f v_size; // box size along coordinate axes

		TAxisAlignedBox()
		{ }

		TAxisAlignedBox(ConstArgType(Vector3f) r_v_center, ConstArgType(Vector3f) r_v_size)
			:v_center(r_v_center), v_size(r_v_size)
		{ }

		/*
		 *	bool b_Intersect_Ray(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir) const
		 *		- returns true if ray [r_v_org, r_v_dir] intersects the box. otherwise false
		 */
		bool b_Intersect_Ray(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir) const
		{
			Vector3f v_diff = v_center - r_v_org;
			Vector3f v_ray_normal = r_v_dir * (r_v_dir.f_Dot(v_diff) / r_v_dir.f_Length());
			float f_effective_radius = Vector3f(v_ray_normal.x * v_size.x,
				v_ray_normal.y * v_size.y, v_ray_normal.z * v_size.z).f_Length();
			// should be divided by length of v_ray_normal, but ...

			return f_effective_radius <= v_ray_normal.f_Length2(); // we compensate here and save sqrt!
		}

		/*
		 *	bool b_Intersect_Segment(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_end) const
		 *		- returns true if segment [r_v_org, r_v_end] intersects the box. otherwise false
		 */
		bool b_Intersect_Segment(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_end) const
		{
			Vector3f v_half_dir = (r_v_end - r_v_org) * .5f;
			Vector3f v_seg_center = r_v_org + v_half_dir;
			Vector3f v_diff = v_center - v_seg_center;
			Vector3f v_dir_dot_axis;
			for(int i = 0; i < 3; ++ i) {
				v_dir_dot_axis[i] = (float)fabs(v_half_dir[i]);
				if((float)fabs(v_diff[i]) + v_size[i] > v_dir_dot_axis[i])
					return false;
			}
			// see if segment, projected to box axis miss the box

			Vector3f v_tangent = v_half_dir.v_Cross(v_diff);
			for(int i = 0; i < 3; ++ i) {
				const int n_index0[] = {1, 0, 0};
				const int n_index1[] = {2, 2, 1};
				if((float)fabs(v_tangent[i]) > v_size[n_index0[i]] * v_dir_dot_axis[n_index1[i]] +
				   v_size[n_index1[i]] * v_dir_dot_axis[n_index0[i]])
					return false;
			}
			// see for each axial projection if segment miss the box orthogonaly

			return true;
		}
	};

	/*
	 *	struct TObjectOrientedBox
	 *		- extension of TAxisAlignedBox, box is now aligned to some arbitrary axes
	 *		  (but they still ought to form right-handed euclidean coordinate system)
	 */
	struct TObjectOrientedBox : public TAxisAlignedBox {
		Vector3f v_axis[3]; // normalized axes, box size is along those axes

		TObjectOrientedBox()
		{ }

		TObjectOrientedBox(ConstArgType(Vector3f) r_v_center, ConstArgType(Vector3f) r_v_size,
			const Vector3f _v_axis[3])
			:TAxisAlignedBox(r_v_center, r_v_size)
		{
			memcpy(v_axis, _v_axis, 3 * sizeof(Vector3f));
			for(int i = 0; i < 3; ++ i)
				v_axis[i].Normalize();
		}

		TObjectOrientedBox(ConstArgType(Vector3f) r_v_center, ConstArgType(Vector3f) r_v_size,
			ConstArgType(Matrix4f) r_t_matrix)
			:TAxisAlignedBox(r_v_center, r_v_size)
		{
			v_axis[0] = r_t_matrix.v_Right();
			v_axis[1] = r_t_matrix.v_Up();
			v_axis[2] = r_t_matrix.v_Dir();
			for(int i = 0; i < 3; ++ i)
				v_axis[i].Normalize();
		}

		TObjectOrientedBox(ConstArgType(TAxisAlignedBox) r_t_box)
			:TAxisAlignedBox(r_t_box.v_center, r_t_box.v_size)
		{
			v_axis[0] = Vector3f(1, 0, 0);
			v_axis[1] = Vector3f(0, 1, 0);
			v_axis[2] = Vector3f(0, 0, 1);
		}

		/*
		 *	bool TObjectOrientedBox::b_Intersect_Ray(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir) const
		 *		- returns true if ray [r_v_org, r_v_dir] intersects the box. otherwise false
		 */
		bool b_Intersect_Ray(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_dir) const
		{
			Vector3f v_diff = v_center - r_v_org;
			Vector3f v_ray_normal = r_v_dir * (r_v_dir.f_Dot(v_diff) / r_v_dir.f_Length());
			float f_effective_radius = Vector3f(v_axis[0].f_Dot(v_ray_normal) * v_size.x,
				v_axis[1].f_Dot(v_ray_normal) * v_size.y,
				v_axis[2].f_Dot(v_ray_normal) * v_size.z).f_Length();
			// should be divided by length of v_ray_normal, but ...

			return f_effective_radius <= v_ray_normal.f_Length2(); // we compensate here and save sqrt!
		}

		/*
		 *	bool b_Intersect_Segment(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_end) const
		 *		- returns true if segment [r_v_org, r_v_end] intersects the box. otherwise false
		 */
		bool b_Intersect_Segment(ConstArgType(Vector3f) r_v_org, ConstArgType(Vector3f) r_v_end) const
		{
			Vector3f v_half_dir = (r_v_end - r_v_org) * .5f;
			Vector3f v_seg_center = r_v_org + v_half_dir;
			Vector3f v_diff = v_center - v_seg_center;
			Vector3f v_dir_dot_axis;
			for(int i = 0; i < 3; ++ i) {
				v_dir_dot_axis[i] = (float)fabs(v_half_dir.f_Dot(v_axis[i]));
				if((float)fabs(v_diff.f_Dot(v_axis[i])) + v_size[i] > v_dir_dot_axis[i])
					return false;
			}
			// see if segment, projected to box axis miss the box

			Vector3f v_tangent = v_half_dir.v_Cross(v_diff);
			for(int i = 0; i < 3; ++ i) {
				const int n_index0[] = {1, 0, 0};
				const int n_index1[] = {2, 2, 1};
				if((float)fabs(v_tangent.f_Dot(v_axis[i])) >
				   v_size[n_index0[i]] * v_dir_dot_axis[n_index1[i]] +
				   v_size[n_index1[i]] * v_dir_dot_axis[n_index0[i]])
					return false;
			}
			// see for each axial projection if segment miss the box orthogonaly

			return true;
		}
	};
};

/*
 *								=== ~CVectorMath ===
 */

#endif // __VECTOR_H_WANT_CVECTORMATH

#endif // !__VECTOR2_INCLUDED
