#ifndef __CUVEC2F_HPP__
#define __CUVEC2F_HPP__

namespace amscuda
{

    class cuvec2f
    {
        public:
        float x;
        float y;

        __host__ __device__ cuvec2f();
        __host__ __device__ ~cuvec2f();
        __host__ __device__ cuvec2f(const float &_x, const float &_y);
        
        
        __host__ __device__ float& operator[](const int &I);
        __host__ __device__ const float& operator[](const int &I) const;

        __host__ __device__ cuvec2f operator+(const cuvec2f &rhs);
        __host__ __device__ cuvec2f operator-(const cuvec2f &rhs);
        __host__ __device__ cuvec2f operator*(const float &rhs);
        __host__ __device__ cuvec2f operator/(const float &rhs);
        __host__ __device__ friend cuvec2f operator-(const cuvec2f &rhs);

        __host__ __device__ cuvec2f& operator+=(const cuvec2f &rhs);
        __host__ __device__ cuvec2f& operator-=(const cuvec2f &rhs);
        __host__ __device__ cuvec2f& operator/=(const float &rhs);
        __host__ __device__ cuvec2f& operator*=(const float &rhs);

    };

    class cumat2f
    {
        public:
        float m00,m10; //named references to force register use?
        float m01,m11; //switched to column-major-order to match GLSL/lapack

        __host__ __device__ cumat2f();
        __host__ __device__ ~cumat2f();

        __host__ __device__ cumat2f(
            const float & _m00, const float & _m01,
            const float & _m10, const float & _m11
        );

        __host__ __device__ explicit cumat2f(const float* data2x2);

        __host__ __device__ float& operator[](const int &I);
        __host__ __device__ float& operator()(const int &I, const int &J);
        __host__ __device__ float& at(const int &I, const int &J);

        __host__ __device__ const float& operator[](const int &I) const;
        __host__ __device__ const float& operator()(const int &I, const int &J) const;
        __host__ __device__ const float& at(const int &I, const int &J) const;

        __host__ __device__ cumat2f operator+(const cumat2f &rhs);
        __host__ __device__ cumat2f operator-(const cumat2f &rhs);
        __host__ __device__ cumat2f operator*(const float &rhs);
        __host__ __device__ cumat2f operator/(const float &rhs);
        __host__ __device__ cuvec2f operator*(const cuvec2f &rhs);
        __host__ __device__ cumat2f operator*(const cumat2f &rhs);
        __host__ __device__ friend cumat2f operator-(const cumat2f &rhs);
        
        __host__ __device__ float det();
        __host__ __device__ cumat2f transpose();
        __host__ __device__ cumat2f inverse();

        __host__ __device__ float* data(); //pointer to float[4] representation of matrix
        __host__ __device__ const float* data() const; //pointer to float[4] representation of matrix

        //In place operations (to save GPU register use)
        __host__ __device__ cumat2f& operator+=(const cumat2f &rhs);
        __host__ __device__ cumat2f& operator-=(const cumat2f &rhs);
        __host__ __device__ cumat2f& operator/=(const float &rhs);
        __host__ __device__ cumat2f& operator*=(const float &rhs);
        __host__ __device__ cumat2f& operator*=(const cumat2f &rhs);
    };

    __host__ __device__ float cuvec2f_dot(const cuvec2f &a, const cuvec2f &b);
    __host__ __device__ float cuvec2f_cross(const cuvec2f &a, const cuvec2f &b);
    __host__ __device__ float cuvec2f_norm(const cuvec2f &a);
    __host__ __device__ cuvec2f cuvec2f_normalize(const cuvec2f &a);
    __host__ __device__ cuvec2f cuvec2f_proj(const cuvec2f &a, const cuvec2f &b);
    __host__ __device__ cumat2f cumat2f_rot_from_angle(const float &angle);


    ///////////
    // Tests //
    ///////////
    void test_cuvec2f_1();





};

#endif

