#ifndef __VMATH_H__
#define __VMATH_H__


#define _USE_MATH_DEFINES  1 // Include constants defined in math.h
#include <math.h>

#define translation translate
#define rotation    rotate
#define scaling     scale

namespace vmath
{

template <typename T> 
inline T radians(T angleInRadians)
{
	return angleInRadians * static_cast<T>(180.0/M_PI);
}

template <const bool cond>
class ensure
{
public:
    inline ensure() { switch (false) { case false: case cond: break; } }
};

template <typename T, const int len> class vecN;

template <typename T, const int len>
class vecN
{
public:
    typedef class vecN<T,len> my_type;

    // Default constructor does nothing, just like built-in types
    inline vecN()
    {
        // Uninitialized variable
    }

    // Copy constructor
    inline vecN(const vecN& that)
    {
        assign(that);
    }

    // Construction from scalar
    inline vecN(T s)
    {
        int n;
        for (n = 0; n < len; n++)
        {
            data[n] = s;
        }
    }

    // Assignment operator
    inline vecN& operator=(const vecN& that)
    {
        assign(that);
        return *this;
    }

    inline vecN operator+(const vecN& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < len; n++)
            result.data[n] = data[n] + that.data[n];
        return result;
    }

    inline vecN& operator+=(const vecN& that)
    {
        return (*this = *this + that);
    }

    inline vecN operator-() const
    {
        my_type result;
        int n;
        for (n = 0; n < len; n++)
            result.data[n] = -data[n];
        return result;
    }

    inline vecN operator-(const vecN& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < len; n++)
            result.data[n] = data[n] - that.data[n];
        return result;
    }

    inline vecN& operator-=(const vecN& that)
    {
        return (*this = *this - that);
    }

    inline vecN operator*(const vecN& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < len; n++)
            result.data[n] = data[n] * that.data[n];
        return result;
    }

    inline vecN& operator*=(const vecN& that)
    {
        return (*this = *this * that);
    }

    inline vecN operator*(const T& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < len; n++)
            result.data[n] = data[n] * that;
        return result;
    }

    inline vecN& operator*=(const T& that)
    {
        assign(*this * that);

        return *this;
    }

    inline vecN operator/(const vecN& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < len; n++)
            result.data[n] = data[n] * that.data[n];
        return result;
    }

    inline vecN& operator/=(const vecN& that)
    {
        assign(*this * that);

        return *this;
    }

    inline vecN operator/(const T& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < len; n++)
            result.data[n] = data[n] / that;
        return result;
    }

    inline vecN& operator/(const T& that)
    {
        assign(*this / that);
    }

    inline T& operator[](int n) { return data[n]; }
    inline const T& operator[](int n) const { return data[n]; }

    inline static int size(void) { return len; }

    inline operator const T* () const { return &data[0]; }

protected:
    T data[len];

    inline void assign(const vecN& that)
    {
        int n;
        for (n = 0; n < len; n++)
            data[n] = that.data[n];
    }
};

template <typename T>
class Tvec2 : public vecN<T,2>
{
public:
    typedef vecN<T,2> base;

    // Uninitialized variable
    inline Tvec2() {}
    // Copy constructor
    inline Tvec2(const base& v) : base(v) {}

    // vec2(x, y);
    inline Tvec2(T x, T y)
    {
        base::data[0] = x;
        base::data[1] = y;
    }
};

template <typename T>
class Tvec3 : public vecN<T,3>
{
public:
    typedef vecN<T,3> base;

    // Uninitialized variable
    inline Tvec3() {}

    // Copy constructor
    inline Tvec3(const base& v) : base(v) {}

    // vec3(x, y, z);
    inline Tvec3(T x, T y, T z)
    {
        base::data[0] = x;
        base::data[1] = y;
        base::data[2] = z;
    }

    // vec3(v, z);
    inline Tvec3(const Tvec2<T>& v, T z)
    {
        base::data[0] = v[0];
        base::data[1] = v[1];
        base::data[2] = z;
    }

    // vec3(x, v)
    inline Tvec3(T x, const Tvec2<T>& v)
    {
        base::data[0] = x;
        base::data[1] = v[0];
        base::data[2] = v[1];
    }
};

template <typename T>
class Tvec4 : public vecN<T,4>
{
public:
    typedef vecN<T,4> base;

    // Uninitialized variable
    inline Tvec4() {}

    // Copy constructor
    inline Tvec4(const base& v) : base(v) {}

    // vec4(x, y, z, w);
    inline Tvec4(T x, T y, T z, T w)
    {
        base::data[0] = x;
        base::data[1] = y;
        base::data[2] = z;
        base::data[3] = w;
    }

    // vec4(v, z, w);
    inline Tvec4(const Tvec2<T>& v, T z, T w)
    {
        base::data[0] = v[0];
        base::data[1] = v[1];
        base::data[2] = z;
        base::data[3] = w;
    }

    // vec4(x, v, w);
    inline Tvec4(T x, const Tvec2<T>& v, T w)
    {
        base::data[0] = x;
        base::data[1] = v[0];
        base::data[2] = v[1];
        base::data[3] = w;
    }

    // vec4(x, y, v);
    inline Tvec4(T x, T y, const Tvec2<T>& v)
    {
        base::data[0] = x;
        base::data[1] = y;
        base::data[2] = v[0];
        base::data[3] = v[1];
    }

    // vec4(v1, v2);
    inline Tvec4(const Tvec2<T>& u, const Tvec2<T>& v)
    {
        base::data[0] = u[0];
        base::data[1] = u[1];
        base::data[2] = v[0];
        base::data[3] = v[1];
    }

    // vec4(v, w);
    inline Tvec4(const Tvec3<T>& v, T w)
    {
        base::data[0] = v[0];
        base::data[1] = v[1];
        base::data[2] = v[2];
        base::data[3] = w;
    }

    // vec4(x, v);
    inline Tvec4(T x, const Tvec3<T>& v)
    {
        base::data[0] = x;
        base::data[1] = v[0];
        base::data[2] = v[1];
        base::data[3] = v[2];
    }
};

typedef Tvec2<float> vec2;
typedef Tvec2<int> ivec2;
typedef Tvec2<unsigned int> uvec2;
typedef Tvec2<double> dvec2;

typedef Tvec3<float> vec3;
typedef Tvec3<int> ivec3;
typedef Tvec3<unsigned int> uvec3;
typedef Tvec3<double> dvec3;

typedef Tvec4<float> vec4;
typedef Tvec4<int> ivec4;
typedef Tvec4<unsigned int> uvec4;
typedef Tvec4<double> dvec4;

template <typename T, int n>
static inline const vecN<T,n> operator * (T x, const vecN<T,n>& v)
{
    return v * x;
}

template <typename T>
static inline const Tvec2<T> operator / (T x, const Tvec2<T>& v)
{
    return Tvec2<T>(x / v[0], x / v[1]);
}

template <typename T>
static inline const Tvec3<T> operator / (T x, const Tvec3<T>& v)
{
    return Tvec3<T>(x / v[0], x / v[1], x / v[2]);
}

template <typename T>
static inline const Tvec4<T> operator / (T x, const Tvec4<T>& v)
{
    return Tvec4<T>(x / v[0], x / v[1], x / v[2], x / v[3]);
}

template <typename T, int len>
static inline T dot(const vecN<T,len>& a, const vecN<T,len>& b)
{
    int n;
    T total = T(0);
    for (n = 0; n < len; n++)
    {
        total += a[n] * b[n];
    }
    return total;
}

template <typename T>
static inline vecN<T,3> cross(const vecN<T,3>& a, const vecN<T,3>& b)
{
    return Tvec3<T>(a[1] * b[2] - b[1] * a[2],
                    a[2] * b[0] - b[2] * a[0],
                    a[0] * b[1] - b[0] * a[1]);
}

template <typename T, int len>
static inline T length(const vecN<T,len>& v)
{
    T result(0);

    for (int i = 0; i < v.size(); ++i)
    {
        result += v[i] * v[i];
    }

    return (T)sqrt(result);
}

template <typename T, int len>
static inline vecN<T,len> normalize(const vecN<T,len>& v)
{
    return v / length(v);
}

template <typename T, int len>
static inline T distance(const vecN<T,len>& a, const vecN<T,len>& b)
{
    return length(b - a);
}

template <typename T, const int w, const int h>
class matNM
{
public:
    typedef class matNM<T,w,h> my_type;
    typedef class vecN<T,h> vector_type;

    // Default constructor does nothing, just like built-in types
    inline matNM()
    {
        // Uninitialized variable
    }

    // Copy constructor
    inline matNM(const matNM& that)
    {
        assign(that);
    }

    // Construction from element type
    // explicit to prevent assignment from T
    explicit inline matNM(T f)
    {
        for (int n = 0; n < w; n++)
        {
            data[n] = f;
        }
    }

    // Construction from vector
    inline matNM(const vector_type& v)
    {
        for (int n = 0; n < w; n++)
        {
            data[n] = v;
        }
    }

    // Assignment operator
    inline matNM& operator=(const my_type& that)
    {
        assign(that);
        return *this;
    }

    inline matNM operator+(const my_type& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < w; n++)
            result.data[n] = data[n] + that.data[n];
        return result;
    }

    inline my_type& operator+=(const my_type& that)
    {
        return (*this = *this + that);
    }

    inline my_type operator-(const my_type& that) const
    {
        my_type result;
        int n;
        for (n = 0; n < w; n++)
            result.data[n] = data[n] - that.data[n];
        return result;
    }

    inline my_type& operator-=(const my_type& that)
    {
        return (*this = *this - that);
    }

    // Matrix multiply.
    // TODO: This only works for square matrices. Need more template skill to make a non-square version.
    inline my_type operator*(const my_type& that) const
    {
        ensure<w == h>();

        my_type result(0);

        for (int j = 0; j < w; j++)
        {
            for (int i = 0; i < h; i++)
            {
                T sum(0);

                for (int n = 0; n < w; n++)
                {
                    sum += data[n][i] * that[j][n];
                }

                result[j][i] = sum;
            }
        }

        return result;
    }

    inline my_type& operator*=(const my_type& that)
    {
        return (*this = *this * that);
    }

    inline vector_type& operator[](int n) { return data[n]; }
    inline const vector_type& operator[](int n) const { return data[n]; }
    inline operator T*() { return &data[0][0]; }
    inline operator const T*() const { return &data[0][0]; }

    inline matNM<T,h,w> transpose(void) const
    {
        matNM<T,h,w> result;
        int x, y;

        for (y = 0; y < w; y++)
        {
            for (x = 0; x < h; x++)
            {
                result[x][y] = data[y][x];
            }
        }

        return result;
    }

    inline matNM<T,h,w> inverse(void) const
    {
 		matNM<T,h,w> result;

 		// Compute determinant
 		T det = +data[3][0] * data[2][1] * data[1][2] * data[0][3] - data[2][0] * data[3][1] * data[1][2] * data[0][3]
				- data[3][0] * data[1][1] * data[2][2] * data[0][3] + data[1][0] * data[3][1] * data[2][2] * data[0][3]

		+ data[2][0] * data[1][1] * data[3][2] * data[0][3] - data[1][0] * data[2][1] * data[3][2] * data[0][3]
				- data[3][0] * data[2][1] * data[0][2] * data[1][3] + data[2][0] * data[3][1] * data[0][2] * data[1][3]

		+ data[3][0] * data[0][1] * data[2][2] * data[1][3] - data[0][0] * data[3][1] * data[2][2] * data[1][3]
				- data[2][0] * data[0][1] * data[3][2] * data[1][3] + data[0][0] * data[2][1] * data[3][2] * data[1][3]

		+ data[3][0] * data[1][1] * data[0][2] * data[2][3] - data[1][0] * data[3][1] * data[0][2] * data[2][3]
				- data[3][0] * data[0][1] * data[1][2] * data[2][3] + data[0][0] * data[3][1] * data[1][2] * data[2][3]

		+ data[1][0] * data[0][1] * data[3][2] * data[2][3] - data[0][0] * data[1][1] * data[3][2] * data[2][3]
				- data[2][0] * data[1][1] * data[0][2] * data[3][3] + data[1][0] * data[2][1] * data[0][2] * data[3][3]

		+ data[2][0] * data[0][1] * data[1][2] * data[3][3] - data[0][0] * data[2][1] * data[1][2] * data[3][3]
				- data[1][0] * data[0][1] * data[2][2] * data[3][3] + data[0][0] * data[1][1] * data[2][2] * data[3][3];

 		// Compute inverse elements
		result[0][0] = (+data[2][1] * data[3][2] * data[1][3] - data[3][1] * data[2][2] * data[1][3] + data[3][1] * data[1][2] * data[2][3]
				- data[1][1] * data[3][2] * data[2][3] - data[2][1] * data[1][2] * data[3][3] + data[1][1] * data[2][2] * data[3][3])/det;

		result[1][0] = (+data[3][0] * data[2][2] * data[1][3] - data[2][0] * data[3][2] * data[1][3] - data[3][0] * data[1][2] * data[2][3]
				+ data[1][0] * data[3][2] * data[2][3] + data[2][0] * data[1][2] * data[3][3] - data[1][0] * data[2][2] * data[3][3])/det;

		result[2][0] = (+data[2][0] * data[3][1] * data[1][3] - data[3][0] * data[2][1] * data[1][3] + data[3][0] * data[1][1] * data[2][3]
				- data[1][0] * data[3][1] * data[2][3] - data[2][0] * data[1][1] * data[3][3] + data[1][0] * data[2][1] * data[3][3])/det;

		result[3][0] = (+data[3][0] * data[2][1] * data[1][2] - data[2][0] * data[3][1] * data[1][2] - data[3][0] * data[1][1] * data[2][2]
				+ data[1][0] * data[3][1] * data[2][2] + data[2][0] * data[1][1] * data[3][2] - data[1][0] * data[2][1] * data[3][2])/det;

		result[0][1] = (+data[3][1] * data[2][2] * data[0][3] - data[2][1] * data[3][2] * data[0][3] - data[3][1] * data[0][2] * data[2][3]
				+ data[0][1] * data[3][2] * data[2][3] + data[2][1] * data[0][2] * data[3][3] - data[0][1] * data[2][2] * data[3][3])/det;

		result[1][1] = (+data[2][0] * data[3][2] * data[0][3] - data[3][0] * data[2][2] * data[0][3] + data[3][0] * data[0][2] * data[2][3]
				- data[0][0] * data[3][2] * data[2][3] - data[2][0] * data[0][2] * data[3][3] + data[0][0] * data[2][2] * data[3][3])/det;

		result[2][1] = (+data[3][0] * data[2][1] * data[0][3] - data[2][0] * data[3][1] * data[0][3] - data[3][0] * data[0][1] * data[2][3]
				+ data[0][0] * data[3][1] * data[2][3] + data[2][0] * data[0][1] * data[3][3] - data[0][0] * data[2][1] * data[3][3])/det;

		result[3][1] = (+data[2][0] * data[3][1] * data[0][2] - data[3][0] * data[2][1] * data[0][2] + data[3][0] * data[0][1] * data[2][2]
				- data[0][0] * data[3][1] * data[2][2] - data[2][0] * data[0][1] * data[3][2] + data[0][0] * data[2][1] * data[3][2])/det;

		result[0][2] = (+data[1][1] * data[3][2] * data[0][3] - data[3][1] * data[1][2] * data[0][3] + data[3][1] * data[0][2] * data[1][3]
				- data[0][1] * data[3][2] * data[1][3] - data[1][1] * data[0][2] * data[3][3] + data[0][1] * data[1][2] * data[3][3])/det;

		result[1][2] = (+data[3][0] * data[1][2] * data[0][3] - data[1][0] * data[3][2] * data[0][3] - data[3][0] * data[0][2] * data[1][3]
				+ data[0][0] * data[3][2] * data[1][3] + data[1][0] * data[0][2] * data[3][3] - data[0][0] * data[1][2] * data[3][3])/det;

		result[2][2] = (+data[1][0] * data[3][1] * data[0][3] - data[3][0] * data[1][1] * data[0][3] + data[3][0] * data[0][1] * data[1][3]
				- data[0][0] * data[3][1] * data[1][3] - data[1][0] * data[0][1] * data[3][3] + data[0][0] * data[1][1] * data[3][3])/det;

		result[3][2] = (+data[3][0] * data[1][1] * data[0][2] - data[1][0] * data[3][1] * data[0][2] - data[3][0] * data[0][1] * data[1][2]
				+ data[0][0] * data[3][1] * data[1][2] + data[1][0] * data[0][1] * data[3][2] - data[0][0] * data[1][1] * data[3][2])/det;

		result[0][3] = (+data[2][1] * data[1][2] * data[0][3] - data[1][1] * data[2][2] * data[0][3] - data[2][1] * data[0][2] * data[1][3]
				+ data[0][1] * data[2][2] * data[1][3] + data[1][1] * data[0][2] * data[2][3] - data[0][1] * data[1][2] * data[2][3])/det;

		result[1][3] = (+data[1][0] * data[2][2] * data[0][3] - data[2][0] * data[1][2] * data[0][3] + data[2][0] * data[0][2] * data[1][3]
				- data[0][0] * data[2][2] * data[1][3] - data[1][0] * data[0][2] * data[2][3] + data[0][0] * data[1][2] * data[2][3])/det;

		result[2][3] = (+data[2][0] * data[1][1] * data[0][3] - data[1][0] * data[2][1] * data[0][3] - data[2][0] * data[0][1] * data[1][3]
				+ data[0][0] * data[2][1] * data[1][3] + data[1][0] * data[0][1] * data[2][3] - data[0][0] * data[1][1] * data[2][3])/det;

		result[3][3] = (+data[1][0] * data[2][1] * data[0][2] - data[2][0] * data[1][1] * data[0][2] + data[2][0] * data[0][1] * data[1][2]
				- data[0][0] * data[2][1] * data[1][2] - data[1][0] * data[0][1] * data[2][2] + data[0][0] * data[1][1] * data[2][2])/det;

        return result;
    }

    static inline my_type identity()
    {
        ensure<w == h>();

        my_type result(0);

        for (int i = 0; i < w; i++)
        {
            result[i][i] = 1;
        }

        return result;
    }

    static inline int width(void) { return w; }
    static inline int height(void) { return h; }

protected:
    // Column primary data (essentially, array of vectors)
    vecN<T,h> data[w];

    // Assignment function - called from assignment operator and copy constructor.
    inline void assign(const matNM& that)
    {
        int n;
        for (n = 0; n < w; n++)
            data[n] = that.data[n];
    }
};

/*
template <typename T, const int N>
class TmatN : public matNM<T,N,N>
{
public:
    typedef matNM<T,N,N> base;
    typedef TmatN<T,N> my_type;

    inline TmatN() {}
    inline TmatN(const my_type& that) : base(that) {}
    inline TmatN(float f) : base(f) {}
    inline TmatN(const vecN<T,4>& v) : base(v) {}

    inline my_type transpose(void)
    {
        my_type result;
        int x, y;

        for (y = 0; y < h; y++)
        {
            for (x = 0; x < h; x++)
            {
                result[x][y] = data[y][x];
            }
        }

        return result;
    }
};
*/

template <typename T>
class Tmat4 : public matNM<T,4,4>
{
public:
    typedef matNM<T,4,4> base;
    typedef Tmat4<T> my_type;

    inline Tmat4() {}
    inline Tmat4(const my_type& that) : base(that) {}
    inline Tmat4(const base& that) : base(that) {}
    inline Tmat4(const vecN<T,4>& v) : base(v) {}
    inline Tmat4(const vecN<T,4>& v0,
                 const vecN<T,4>& v1,
                 const vecN<T,4>& v2,
                 const vecN<T,4>& v3)
    {
        base::data[0] = v0;
        base::data[1] = v1;
        base::data[2] = v2;
        base::data[3] = v3;
    }
};

typedef Tmat4<float> mat4;
typedef Tmat4<int> imat4;
typedef Tmat4<unsigned int> umat4;
typedef Tmat4<double> dmat4;

template <typename T>
static inline Tmat4<T> translate(T x, T y, T z)
{
    return Tmat4<T>(Tvec4<T>(1.0f, 0.0f, 0.0f, 0.0f),
                    Tvec4<T>(0.0f, 1.0f, 0.0f, 0.0f),
                    Tvec4<T>(0.0f, 0.0f, 1.0f, 0.0f),
                    Tvec4<T>(x, y, z, 1.0f));
}

template <typename T>
static inline Tmat4<T> translate(const vecN<T,3>& v)
{
    return translate(v[0], v[1], v[2]);
}

template <typename T>
static inline Tmat4<T> scale(T x, T y, T z)
{
    return Tmat4<T>(Tvec4<T>(x, 0.0f, 0.0f, 0.0f),
                    Tvec4<T>(0.0f, y, 0.0f, 0.0f),
                    Tvec4<T>(0.0f, 0.0f, z, 0.0f),
                    Tvec4<T>(0.0f, 0.0f, 0.0f, 1.0f));
}

template <typename T>
static inline Tmat4<T> scale(const vecN<T,3>& v)
{
    return scale(v[0], v[1], v[2]);
}
/*
template <typename T>
static inline Tmat4<T> scale(T x)
{
    return Tmat4<T>(Tvec4<T>(x, 0.0f, 0.0f, 0.0f),
                    Tvec4<T>(0.0f, x, 0.0f, 0.0f),
                    Tvec4<T>(0.0f, 0.0f, x, 0.0f),
                    Tvec4<T>(0.0f, 0.0f, 0.0f, 1.0f));
}
*/
template <typename T>
static inline Tmat4<T> rotate(T angle, T x, T y, T z)
{
    Tmat4<T> result;

    const T x2 = x * x;
    const T y2 = y * y;
    const T z2 = z * z;
    float rads = float(angle) * 0.0174532925f;
    const float c = cosf(rads);
    const float s = sinf(rads);
    const float omc = 1.0f - c;

    result[0] = Tvec4<T>(T(x2 * omc + c), T(y * x * omc + z * s), T(x * z * omc - y * s), T(0));
    result[1] = Tvec4<T>(T(x * y * omc - z * s), T(y2 * omc + c), T(y * z * omc + x * s), T(0));
    result[2] = Tvec4<T>(T(x * z * omc + y * s), T(y * z * omc - x * s), T(z2 * omc + c), T(0));
    result[3] = Tvec4<T>(T(0), T(0), T(0), T(1));

    return result;
}

template <typename T>
static inline Tmat4<T> rotate(T angle, const vecN<T,3>& v)
{
    return rotate<T>(angle, v[0], v[1], v[2]);
}

static inline mat4 frustum(float left, float right, float bottom, float top, float n, float f)
{
    mat4 result(mat4::identity());

    if ((right == left) ||
        (top == bottom) ||
        (n == f) ||
        (n < 0.0) ||
        (f < 0.0))
       return result;

    result[0][0] = (2.0f * n) / (right - left);
    result[1][1] = (2.0f * n) / (top - bottom);

    result[2][0] = (right + left) / (right - left);
    result[2][1] = (top + bottom) / (top - bottom);
    result[2][2] = -(f + n) / (f - n);
    result[2][3]= -1.0f;

    result[3][2] = -(2.0f * f * n) / (f - n);
    result[3][3] =  0.0f;

    return result;
}

static inline mat4 perspective(float fovy /* in degrees */, float aspect, float n, float f)
{
	float  top = n * (float)tan(radians(0.5f*fovy)); // bottom = -top
	float  right = top * aspect; // left = -right
	return frustum(-right, right, -top, top, n, f);
}

static inline mat4 ortho(float left, float right, float bottom, float top, float n, float f)
{
    return mat4( vec4(2.0f / (right - left), 0.0f, 0.0f, 0.0f),
                 vec4(0.0f, 2.0f / (top - bottom), 0.0f, 0.0f),
                 vec4(0.0f, 0.0f, 2.0f / (n - f), 0.0f),
                 vec4((left + right) / (left - right), (bottom + top) / (bottom - top), (n + f) / (f - n), 1.0f) );
}

template <typename T>
static inline Tmat4<T> lookat(vecN<T,3> eye, vecN<T,3> center, vecN<T,3> up)
{
    const Tvec3<T> f = normalize(center - eye);
    const Tvec3<T> upN = normalize(up);
    const Tvec3<T> s = normalize(cross(f, upN));
    const Tvec3<T> u = normalize(cross(s, f));
    const Tmat4<T> M = Tmat4<T>(Tvec4<T>(s[0], u[0], -f[0], T(0)),
                                Tvec4<T>(s[1], u[1], -f[1], T(0)),
                                Tvec4<T>(s[2], u[2], -f[2], T(0)),
                                Tvec4<T>(T(0), T(0), T(0), T(1)));

    return M * translate<T>(-eye);
}

#ifdef min
#undef min
#endif

template <typename T>
static inline T min(T a, T b)
{
    return a < b ? a : b;
}

#ifdef max
#undef max
#endif

template <typename T>
static inline T max(T a, T b)
{
    return a >= b ? a : b;
}

template <typename T, const int N>
static inline vecN<T,N> min(const vecN<T,N>& x, const vecN<T,N>& y)
{
    vecN<T,N> t;
    int n;

    for (n = 0; n < N; n++)
    {
        t[n] = min(x[n], y[n]);
    }

    return t;
}

template <typename T, const int N>
static inline vecN<T,N> max(const vecN<T,N>& x, const vecN<T,N>& y)
{
    vecN<T,N> t;
    int n;

    for (n = 0; n < N; n++)
    {
        t[n] = max<T>(x[n], y[n]);
    }

    return t;
}

template <typename T, const int N>
static inline vecN<T,N> clamp(const vecN<T,N>& x, const vecN<T,N>& minVal, const vecN<T,N>& maxVal)
{
    return min<T>(max<T>(x, minVal), maxVal);
}

template <typename T, const int N>
static inline vecN<T,N> smoothstep(const vecN<T,N>& edge0, const vecN<T,N>& edge1, const vecN<T,N>& x)
{
    vecN<T,N> t;
    t = clamp((x - edge0) / (edge1 - edge0), vecN<T,N>(T(0)), vecN<T,N>(T(1)));
    return t * t * (vecN<T,N>(T(3)) - vecN<T,N>(T(2)) * t);
}

template <typename T, const int N, const int M>
static inline matNM<T,N,M> matrixCompMult(const matNM<T,N,M>& x, const matNM<T,N,M>& y)
{
    matNM<T,N,M> result;
    int i, j;

    for (j = 0; j < M; ++j)
    {
        for (i = 0; i < N; ++i)
        {
            result[i][j] = x[i][j] * y[i][j];
        }
    }

    return result;
}

template <typename T, const int N, const int M>
static inline vecN<T,N> operator*(const vecN<T,M>& vec, const matNM<T,N,M>& mat)
{
    int n, m;
    vecN<T,N> result(T(0));

    for (m = 0; m < M; m++)
    {
        for (n = 0; n < N; n++)
        {
            result[n] += vec[m] * mat[n][m];
        }
    }

    return result;
}

};

#endif /* __VMATH_H__ */
