#include <amsculib2/amsculib2.hpp>

namespace amscuda
{

    __host__ __device__ cuvect3::cuvect3()
    {
        x = 0.0; y = 0.0; z = 0.0;
        return;
    }

    __host__ __device__ cuvect3::~cuvect3()
    {
        x = 0.0; y = 0.0; z = 0.0;
        return;
    }

    __host__ __device__ double& cuvect3::operator[](const int &I)
    {
        if(I==0) return x;
        if(I==1) return y;
        if(I==2) return z;
        return x;
    }

    __host__ __device__ const double& cuvect3::operator[](const int &I) const
    {
        if(I==0) return x;
        if(I==1) return y;
        if(I==2) return z;
        return x;
    }

    __host__ __device__ cuvect3 cuvect3::operator+(const cuvect3 &rhs)
    {
        cuvect3 ret;
        ret.x = x+rhs.x;
        ret.y = y+rhs.y;
        ret.z = z+rhs.z;

        return ret;
    }

    __host__ __device__ cuvect3 cuvect3::operator-(const cuvect3 &rhs)
    {
        cuvect3 ret;
        ret.x = x-rhs.x;
        ret.y = y-rhs.y;
        ret.z = z-rhs.z;

        return ret;
    }

    __host__ __device__ cuvect3 cuvect3::operator*(const double &rhs)
    {
        cuvect3 ret;
        ret.x = x*rhs;
        ret.y = y*rhs;
        ret.z = z*rhs;
        return ret;
    }

    __host__ __device__ cuvect3 cuvect3::operator/(const double &rhs)
    {
        cuvect3 ret;
        ret.x = x/rhs;
        ret.y = y/rhs;
        ret.z = z/rhs;
        return ret;
    }

    __host__ __device__ cuvect3& cuvect3::operator+=(const cuvect3 &rhs)
    {
        x = x + rhs.x;
        y = y + rhs.y;
        z = z + rhs.z;
        return *this;
    }

    __host__ __device__ cuvect3& cuvect3::operator-=(const cuvect3 &rhs)
    {
        x = x - rhs.x;
        y = y - rhs.y;
        z = z - rhs.z;
        return *this;
    }

    __host__ __device__ cuvect3& cuvect3::operator*=(const double &rhs)
    {
        x = x * rhs;
        y = y * rhs;
        z = z * rhs;
        return *this;
    }

    __host__ __device__ cuvect3& cuvect3::operator/=(const double &rhs)
    {
        x = x / rhs;
        y = y / rhs;
        z = z / rhs;
        return *this;
    }


    __host__ __device__ cuvect3::cuvect3(const double &_x, const double &_y, const double &_z)
    {
        x = _x; y = _y; z = _z;
        return;
    }


    __host__ __device__ double cuvect3_dot(const cuvect3 &a, const cuvect3 &b)
    {
        double ret = a.x*b.x+a.y*b.y+a.z*b.z;

        return ret;
    }

    __host__ __device__ cuvect3 cuvect3_cross(const cuvect3 &a, const cuvect3 &b)
    {
        cuvect3 ret;
        ret[0] = a[1]*b[2]-a[2]*b[1];
        ret[1] = a[2]*b[0]-a[0]*b[2];
        ret[2] = a[0]*b[1]-a[1]*b[0];

        return ret;
    }

    __host__ __device__ double cuvect3_norm(const cuvect3 &a)
    {
        double ret;
        ret = ::sqrtf(a.x*a.x+a.y*a.y+a.z*a.z);
        return ret;
    }

    __host__ __device__ cuvect3 cuvect3_normalize(const cuvect3 &a)
    {
        cuvect3 ret;
        double m;
        m = ::sqrtf(a.x*a.x+a.y*a.y+a.z*a.z);
        if(m>0.0)
        {
            ret.x = a.x/m; ret.y = a.y/m; ret.z = a.z/m;
        }
        else
        {
            ret.x = 0.0; ret.y = 0.0; ret.z = 0.0;
        }

        return ret;
    }

    __host__ __device__ cuvect3 cuvect3_proj(const cuvect3 &a, const cuvect3 &b)
    {
        cuvect3 ret;
        cuvect3 bn = cuvect3_normalize(b);
        double m = cuvect3_dot(a,bn);
        ret = bn*m;
        return ret;
    }




__host__ __device__ cumat3::cumat3()
{
    m00 = 0.0;
    m01 = 0.0;
    m02 = 0.0;
    m10 = 0.0;
    m11 = 0.0;
    m12 = 0.0;
    m20 = 0.0;
    m21 = 0.0;
    m22 = 0.0;

    return;
}

__host__ __device__ cumat3::~cumat3()
{
    m00 = 0.0;
    m01 = 0.0;
    m02 = 0.0;
    m10 = 0.0;
    m11 = 0.0;
    m12 = 0.0;
    m20 = 0.0;
    m21 = 0.0;
    m22 = 0.0;
    return;
}

__host__ __device__ double& cumat3::operator[](const int &I)
{
    if(I==0) return m00;
    if(I==1) return m10;
    if(I==2) return m20;
    if(I==3) return m01;
    if(I==4) return m11;
    if(I==5) return m21;
    if(I==6) return m02;
    if(I==7) return m12;
    if(I==8) return m22;
    
    return m00;
}

__host__ __device__ double& cumat3::operator()(const int &I, const int &J)
{
    if(I==0 && J==0) return m00;
    if(I==1 && J==0) return m10;
    if(I==2 && J==0) return m20;
    if(I==0 && J==1) return m01;
    if(I==1 && J==1) return m11;
    if(I==2 && J==1) return m21;
    if(I==0 && J==2) return m02;
    if(I==1 && J==2) return m12;
    if(I==2 && J==2) return m22;

    return m00;
}


__host__ __device__ double& cumat3::at(const int &I, const int &J)
{
    if(I==0 && J==0) return m00;
    if(I==1 && J==0) return m10;
    if(I==2 && J==0) return m20;
    if(I==0 && J==1) return m01;
    if(I==1 && J==1) return m11;
    if(I==2 && J==1) return m21;
    if(I==0 && J==2) return m02;
    if(I==1 && J==2) return m12;
    if(I==2 && J==2) return m22;

    return m00;
}

__host__ __device__ const double& cumat3::operator[](const int &I) const 
{
    if(I==0) return m00;
    if(I==1) return m10;
    if(I==2) return m20;
    if(I==3) return m01;
    if(I==4) return m11;
    if(I==5) return m21;
    if(I==6) return m02;
    if(I==7) return m12;
    if(I==8) return m22;
    
    return m00;
}

__host__ __device__ const double& cumat3::operator()(const int &I, const int &J) const 
{
    if(I==0 && J==0) return m00;
    if(I==1 && J==0) return m10;
    if(I==2 && J==0) return m20;
    if(I==0 && J==1) return m01;
    if(I==1 && J==1) return m11;
    if(I==2 && J==1) return m21;
    if(I==0 && J==2) return m02;
    if(I==1 && J==2) return m12;
    if(I==2 && J==2) return m22;

    return m00;
}


__host__ __device__ const double& cumat3::at(const int &I, const int &J) const 
{
    if(I==0 && J==0) return m00;
    if(I==1 && J==0) return m10;
    if(I==2 && J==0) return m20;
    if(I==0 && J==1) return m01;
    if(I==1 && J==1) return m11;
    if(I==2 && J==1) return m21;
    if(I==0 && J==2) return m02;
    if(I==1 && J==2) return m12;
    if(I==2 && J==2) return m22;

    return m00;
}


__host__ __device__ cumat3 cumat3::operator+(const cumat3 &rhs)
{
    cumat3 ret;
    ret.m00 = m00 + rhs.m00;
    ret.m10 = m10 + rhs.m10;
    ret.m20 = m20 + rhs.m20;
    ret.m01 = m01 + rhs.m01;
    ret.m11 = m11 + rhs.m11;
    ret.m21 = m21 + rhs.m21;
    ret.m02 = m02 + rhs.m02;
    ret.m12 = m12 + rhs.m12;
    ret.m22 = m22 + rhs.m22;
    

    return ret;
}

__host__ __device__ cumat3 cumat3::operator-(const cumat3 &rhs)
{
    cumat3 ret;
    ret.m00 = m00 - rhs.m00;
    ret.m10 = m10 - rhs.m10;
    ret.m20 = m20 - rhs.m20;
    ret.m01 = m01 - rhs.m01;
    ret.m11 = m11 - rhs.m11;
    ret.m21 = m21 - rhs.m21;
    ret.m02 = m02 - rhs.m02;
    ret.m12 = m12 - rhs.m12;
    ret.m22 = m22 - rhs.m22;
    return ret;
}

__host__ __device__ cumat3 cumat3::operator*(const double &rhs)
{
    cumat3 ret;
    ret.m00 = m00 * rhs;
    ret.m10 = m10 * rhs;
    ret.m20 = m20 * rhs;
    ret.m01 = m01 * rhs;
    ret.m11 = m11 * rhs;
    ret.m21 = m21 * rhs;
    ret.m02 = m02 * rhs;
    ret.m12 = m12 * rhs;
    ret.m22 = m22 * rhs;
    return ret;
}

__host__ __device__ cumat3 cumat3::operator/(const double &rhs)
{
    cumat3 ret;
    ret.m00 = m00 / rhs;
    ret.m10 = m10 / rhs;
    ret.m20 = m20 / rhs;
    ret.m01 = m01 / rhs;
    ret.m11 = m11 / rhs;
    ret.m21 = m21 / rhs;
    ret.m02 = m02 / rhs;
    ret.m12 = m12 / rhs;
    ret.m22 = m22 / rhs;
    return ret;
}
        
__host__ __device__ cuvect3 cumat3::operator*(const cuvect3 &rhs)
{
    cuvect3 ret;
    
    ret.x = m00*rhs.x + m01*rhs.y + m02*rhs.z;
    ret.y = m10*rhs.x + m11*rhs.y + m12*rhs.z;
    ret.z = m20*rhs.x + m21*rhs.y + m22*rhs.z;

    return ret;
}

__host__ __device__ cumat3 cumat3::operator*(const cumat3 &rhs)
{
    cumat3 ret;

    ret.m00 = m00*rhs.m00 + m01*rhs.m10 + m02*rhs.m20;
    ret.m01 = m00*rhs.m01 + m01*rhs.m11 + m02*rhs.m21;
    ret.m02 = m00*rhs.m02 + m01*rhs.m12 + m02*rhs.m22;
    ret.m10 = m10*rhs.m00 + m11*rhs.m10 + m12*rhs.m20;
    ret.m11 = m10*rhs.m01 + m11*rhs.m11 + m12*rhs.m21;
    ret.m12 = m10*rhs.m02 + m11*rhs.m12 + m12*rhs.m22;
    ret.m20 = m20*rhs.m00 + m21*rhs.m10 + m22*rhs.m20;
    ret.m21 = m20*rhs.m01 + m21*rhs.m11 + m22*rhs.m21;
    ret.m22 = m20*rhs.m02 + m21*rhs.m12 + m22*rhs.m22;

    return ret;
}

__host__ __device__ double cumat3::det()
{
    double ret = 0.0;

    ret += m00*m11*m22;
    ret += m01*m12*m20;
    ret += m02*m10*m21;
    ret -= m20*m11*m02;
    ret -= m21*m12*m00;
    ret -= m22*m10*m01;

    return ret;
}

__host__ __device__ cumat3 cumat3::transpose()
{
    cumat3 ret;

    ret.m00 = m00;
    ret.m01 = m10;
    ret.m02 = m20;
    ret.m10 = m01;
    ret.m11 = m11;
    ret.m12 = m21;
    ret.m20 = m02;
    ret.m21 = m12;
    ret.m22 = m22;

    return ret;
}

__host__ __device__ cumat3 cumat3::inverse()
{
    cumat3 q;
    double dt = det();
    if(dt!=0.0)
    {
        q(0,0) =  (at(1,1)*at(2,2)-at(1,2)*at(2,1))/dt;
        q(1,0) = -(at(1,0)*at(2,2)-at(1,2)*at(2,0))/dt;
        q(2,0) =  (at(1,0)*at(2,1)-at(1,1)*at(2,0))/dt;
        q(0,1) = -(at(0,1)*at(2,2)-at(0,2)*at(2,1))/dt;
        q(1,1) =  (at(0,0)*at(2,2)-at(0,2)*at(2,0))/dt;
        q(2,1) = -(at(0,0)*at(2,1)-at(0,1)*at(2,0))/dt;
        q(0,2) =  (at(0,1)*at(1,2)-at(0,2)*at(1,1))/dt;
        q(1,2) = -(at(0,0)*at(1,2)-at(0,2)*at(1,0))/dt;
        q(2,2) =  (at(0,0)*at(1,1)-at(0,1)*at(1,0))/dt;
        // q(0,0) =  (at(1,1)*at(2,2)-at(1,2)*at(2,1))/dt;
        // q(0,1) = -(at(1,0)*at(2,2)-at(1,2)*at(2,0))/dt;
        // q(0,2) =  (at(1,0)*at(2,1)-at(1,1)*at(2,0))/dt;
        // q(1,0) = -(at(0,1)*at(2,2)-at(0,2)*at(2,1))/dt;
        // q(1,1) =  (at(0,0)*at(2,2)-at(0,2)*at(2,0))/dt;
        // q(1,2) = -(at(0,0)*at(2,1)-at(0,1)*at(2,0))/dt;
        // q(2,0) =  (at(0,1)*at(1,2)-at(0,2)*at(1,1))/dt;
        // q(2,1) = -(at(0,0)*at(1,2)-at(0,2)*at(1,0))/dt;
        // q(2,2) =  (at(0,0)*at(1,1)-at(0,1)*at(1,0))/dt;
        // q = q.transpose();
    }
    else
    {
        q(0,0) = inf;
        q(0,1) = inf;
        q(0,2) = inf;
        q(1,0) = inf;
        q(1,1) = inf;
        q(1,2) = inf;
        q(2,0) = inf;
        q(2,1) = inf;
        q(2,2) = inf;
    }

    return q;
}

__host__ __device__ cumat3 operator-(const cumat3 &rhs)
{
    cumat3 ret;
    ret.m00 = -rhs.m00;
    ret.m10 = -rhs.m10;
    ret.m20 = -rhs.m20;
    ret.m01 = -rhs.m01;
    ret.m11 = -rhs.m11;
    ret.m21 = -rhs.m21;
    ret.m02 = -rhs.m02;
    ret.m12 = -rhs.m12;
    ret.m22 = -rhs.m22;

    return ret;
}

__host__ __device__ cumat3& cumat3::operator+=(const cumat3 &rhs)
{
    m00 += rhs.m00;
    m10 += rhs.m10;
    m20 += rhs.m20;
    m01 += rhs.m01;
    m11 += rhs.m11;
    m21 += rhs.m21;
    m02 += rhs.m02;
    m12 += rhs.m12;
    m22 += rhs.m22;

    return *this;
}

__host__ __device__ cumat3& cumat3::operator-=(const cumat3 &rhs)
{
    m00 -= rhs.m00;
    m10 -= rhs.m10;
    m20 -= rhs.m20;
    m01 -= rhs.m01;
    m11 -= rhs.m11;
    m21 -= rhs.m21;
    m02 -= rhs.m02;
    m12 -= rhs.m12;
    m22 -= rhs.m22;

    return *this;
}



__host__ __device__ cumat3& cumat3::operator/=(const double &rhs)
{
    m00 /= rhs;
    m10 /= rhs;
    m20 /= rhs;
    m01 /= rhs;
    m11 /= rhs;
    m21 /= rhs;
    m02 /= rhs;
    m12 /= rhs;
    m22 /= rhs;

    return *this;
}

__host__ __device__ cumat3& cumat3::operator*=(const double &rhs)
{
    m00 *= rhs;
    m10 *= rhs;
    m20 *= rhs;
    m01 *= rhs;
    m11 *= rhs;
    m21 *= rhs;
    m02 *= rhs;
    m12 *= rhs;
    m22 *= rhs;

    return *this;
}

__host__ __device__ cumat3& cumat3::operator*=(const cumat3 &rhs)
{
    cumat3 tmp;

    tmp.m00 = m00*rhs.m00 + m01*rhs.m10 + m02*rhs.m20;
    tmp.m01 = m00*rhs.m01 + m01*rhs.m11 + m02*rhs.m21;
    tmp.m02 = m00*rhs.m02 + m01*rhs.m12 + m02*rhs.m22;
    tmp.m10 = m10*rhs.m00 + m11*rhs.m10 + m12*rhs.m20;
    tmp.m11 = m10*rhs.m01 + m11*rhs.m11 + m12*rhs.m21;
    tmp.m12 = m10*rhs.m02 + m11*rhs.m12 + m12*rhs.m22;
    tmp.m20 = m20*rhs.m00 + m21*rhs.m10 + m22*rhs.m20;
    tmp.m21 = m20*rhs.m01 + m21*rhs.m11 + m22*rhs.m21;
    tmp.m22 = m20*rhs.m02 + m21*rhs.m12 + m22*rhs.m22;

    (*this) = tmp;

    return *this;
}

__host__ __device__ cumat3::cumat3(
    const double & _m00, const double & _m01, const double & _m02,
    const double & _m10, const double & _m11, const double & _m12,
    const double & _m20, const double & _m21, const double & _m22
)
{
    m00 = _m00;
    m01 = _m01;
    m02 = _m02;
    m10 = _m10;
    m11 = _m11;
    m12 = _m12;
    m20 = _m20;
    m21 = _m21;
    m22 = _m22;
}

__host__ __device__ double* cumat3::data()
{
     //pointer to double[9] representation of matrix
    return (double*) this;
}

__host__ __device__ const double* cumat3::data() const
{
     //pointer to double[9] representation of matrix
    return (const double*) this;
}

//transposes a 3x3 (9 element) matrix
__host__ __device__ void mat3f_transpose(double *mat3inout)
{
    int I,J;
    double matint[9];
    for(I=0;I<9;I++)
    {
        matint[I] = mat3inout[I];
    }

    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            mat3inout[I+J*3] = matint[J+I*3];
        }
    }

    return;
}

//copies src to dest
__host__ __device__ void mat3f_copy(double *mat3f_dest, const double *mat3f_src)
{
    int I;
    if(mat3f_dest==NULL || mat3f_src==NULL)
        return;

    for(I=0;I<9;I++)
        mat3f_dest[I] = mat3f_src[I];

    return;
}


__host__ __device__ double mat3f_det(double *mat3in)
{
    double ret = 0.0;

    ret = ret + mat3in[0+0*3]*mat3in[1+1*3]*mat3in[2+2*3];
    ret = ret + mat3in[0+1*3]*mat3in[1+2*3]*mat3in[2+0*3];
    ret = ret + mat3in[0+2*3]*mat3in[1+0*3]*mat3in[2+1*3];
    ret = ret - mat3in[0+0*3]*mat3in[1+2*3]*mat3in[2+1*3];
    ret = ret - mat3in[0+1*3]*mat3in[1+0*3]*mat3in[2+2*3];
    ret = ret - mat3in[0+2*3]*mat3in[1+1*3]*mat3in[2+0*3];

    return ret;
}

//inverts a 3x3 (9 element) matrix
__host__ __device__ void mat3f_inverse(double *mat3inout)
{
    int I;
    double matint[9];
    double det = mat3f_det(mat3inout);

    for(I=0;I<9;I++)
    {
        matint[I] = mat3inout[I];
    }

    mat3inout[0+0*3] =  (matint[1+1*3]*matint[2+2*3]-matint[1+2*3]*matint[2+1*3])/det;
    mat3inout[0+1*3] = -(matint[1+0*3]*matint[2+2*3]-matint[1+2*3]*matint[2+0*3])/det;
    mat3inout[0+2*3] =  (matint[1+0*3]*matint[2+1*3]-matint[1+1*3]*matint[2+0*3])/det;
    mat3inout[1+0*3] = -(matint[0+1*3]*matint[2+2*3]-matint[0+2*3]*matint[2+1*3])/det;
    mat3inout[1+1*3] =  (matint[0+0*3]*matint[2+2*3]-matint[0+2*3]*matint[2+0*3])/det;
    mat3inout[1+2*3] = -(matint[0+0*3]*matint[2+1*3]-matint[0+1*3]*matint[2+0*3])/det;
    mat3inout[2+0*3] =  (matint[0+1*3]*matint[1+2*3]-matint[0+2*3]*matint[1+1*3])/det;
    mat3inout[2+1*3] = -(matint[0+0*3]*matint[1+2*3]-matint[0+2*3]*matint[1+0*3])/det;
    mat3inout[2+2*3] =  (matint[0+0*3]*matint[1+1*3]-matint[0+1*3]*matint[1+0*3])/det;

    mat3f_transpose(mat3inout);

    return;
}

__host__ __device__ cuvect3 mat3f_mult(double *mat3in, const cuvect3 &cvin)
{
    int I,J;
    cuvect3 ret;
    for(I=0;I<3;I++)
    {
        ret[I] = 0.0;
        for(J=0;J<3;J++)
        {
            ret[I] = ret[I] + mat3in[I+3*J]*cvin[J];
        }
    }

    return ret;
}

__host__ __device__ void mat3f_mult(double *matina, double *matinb, double *matout)
{
    double wrk[9];
    int I,J,K;

    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            wrk[I+3*J] = 0.0;
        }
    }

    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            for(K=0;K<3;K++)
            {
                wrk[I+3*K] = wrk[I+3*K] + matina[I+3*J]*matinb[J+3*K];
            }
        }
    }

    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            matout[I+3*J] = wrk[I+3*J];
        }
    }

    return;
}

__host__ __device__ void mat3f_hodgedual(const cuvect3 &vecin, double *matout)
{
    matout[0 + 0*3] = 0.0;
    matout[1 + 0*3] = -vecin[2];
    matout[2 + 0*3] = vecin[1];

    matout[0 + 1*3] = vecin[2];
    matout[1 + 1*3] = 0.0;
    matout[2 + 1*3] = -vecin[0];

    matout[0 + 2*3] = -vecin[1];
    matout[1 + 2*3] = vecin[0];
    matout[2 + 2*3] = 0.0;
    return;
}

__host__ __device__ void mat3f_hodgedual(double *matin, cuvect3 &vecout)
{
    vecout[0] = 0.5*(matin[1 + 2*3] - matin[2 + 1*3]);
    vecout[1] = 0.5*(matin[2 + 0*3] - matin[0 + 2*3]);
    vecout[2] = 0.5*(matin[0 + 1*3] - matin[1 + 0*3]);
    
    return;
}

//returns direction cosine rotation matrix from axis and angle
__host__ __device__ void mat3f_rot_from_axisangle(cuvect3 axis, double angle, double *matout)
{
    int I;
    double H[9];
    double Hsq[9];
    double II[9];

    for(I=0;I<9;I++) II[I] = 0.0;
    II[0+0*3] = 1.0;
    II[1+1*3] = 1.0;
    II[2+2*3] = 1.0;

    axis = cuvect3_normalize(axis);
    
    mat3f_hodgedual(axis,H);
    mat3f_mult(H,H,Hsq);

    for(I=0;I<9;I++)
    {
        matout[I] = (II[I] + Hsq[I]) + H[I]*sinf(angle) - Hsq[I]*cosf(angle);
    }

    return;
}



__host__ void test_cudavect_logic1()
{
    //3 dim vector and matrix functional tests on host side

    printf("3 dim vector and matrix functional tests on host side\n");
    
    cuvect3 a,b,c;
    double ma[9],mb[9],mc[9];

    int I,J;

    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            ma[I+3*J] = ((double) rand())/((double) RAND_MAX);
            mb[I+3*J] = ma[I+3*J];
        }
    }

    mat3f_inverse(mb);
    mat3f_mult(ma,mb,mc);

    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            printf("ma[%d,%d] = %1.3f\n",I,J,ma[I+3*J]);
        }
    }
    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            printf("mb[%d,%d] = %1.3f\n",I,J,mb[I+3*J]);
        }
    }
    for(I=0;I<3;I++)
    {
        for(J=0;J<3;J++)
        {
            printf("mc[%d,%d] = %1.3f\n",I,J,mc[I+3*J]);
        }
    }

    a = cuvect3(1,1,1);
    b = mat3f_mult(ma,a);
    b = mat3f_mult(mb,b);
    
    for(I=0;I<3;I++)
    {
        printf("a[%d] = %1.3f, b[%d] = %1.3f\n",I,a[I],I,b[I]);
    }

    a = cuvect3(1,0,1);
    b = cuvect3(0,1,-1);
    c = a+b;

    for(I=0;I<3;I++)
    {
        printf("a[%d] = %1.3f, b[%d] = %1.3f, c[%d] = %1.3f\n",I,a[I],I,b[I],I,c[I]);
    }

    c = c/2.0;

    for(I=0;I<3;I++)
    {
        printf("a[%d] = %1.3f, b[%d] = %1.3f, c[%d] = %1.3f\n",I,a[I],I,b[I],I,c[I]);
    }

    c = cuvect3_cross(a,b);

    for(I=0;I<3;I++)
    {
        printf("a[%d] = %1.3f, b[%d] = %1.3f, c[%d] = %1.3f\n",I,a[I],I,b[I],I,c[I]);
    }

    printf("c dot a = %1.3f, c dot b = %1.3f\n",cuvect3_dot(c,a),cuvect3_dot(c,b));

    printf("norm(a)=%1.3f, norm(b)=%1.3f, norm(c)=%1.3f\n",cuvect3_norm(a),cuvect3_norm(b),cuvect3_norm(c));
    a = cuvect3_normalize(a);
    b = cuvect3_normalize(b);
    c = cuvect3_normalize(c);

    for(I=0;I<3;I++)
    {
        printf("a[%d] = %1.3f, b[%d] = %1.3f, c[%d] = %1.3f\n",I,a[I],I,b[I],I,c[I]);
    }
    printf("c dot a = %1.3f, c dot b = %1.3f\n",cuvect3_dot(c,a),cuvect3_dot(c,b));
    printf("norm(a)=%1.3f, norm(b)=%1.3f, norm(c)=%1.3f\n",cuvect3_norm(a),cuvect3_norm(b),cuvect3_norm(c));

    return;
}

__host__ __device__ cumat3 hodge_dual(const cuvect3 &vin)
{
    cumat3 ret;

    ret.m00 = 0.0;
    ret.m11 = 0.0;
    ret.m22 = 0.0;

    ret.m01 = vin.z;
    ret.m12 = vin.x;
    ret.m20 = vin.y;

    ret.m10 = -vin.z;
    ret.m21 = -vin.x;
    ret.m02 = -vin.y;

    return ret;
}

__host__ __device__ cuvect3 hodge_dual(const cumat3 &min)
{
    cuvect3 ret;

    ret.x = 0.5f*(min.m12 - min.m21);
    ret.y = 0.5f*(min.m20 - min.m02);
    ret.z = 0.5f*(min.m01 - min.m10);

    return ret;
}

__host__ __device__ const cumat3 cumat3_eye()
{
    cumat3 ret;
    ret.m00 = 1.0f;
    ret.m11 = 1.0f;
    ret.m22 = 1.0f;
    
    return ret;
}

__host__ __device__ const cumat3 cumat3_zeros()
{
    cumat3 ret;
    return ret;
}

__host__ __device__ cumat3 rotmat_from_axisangle(const cuvect3 &axis, const double &angle)
{
    cumat3 ret = cumat3_zeros();
    cumat3 H;
    cuvect3 _naxis;

    _naxis = cuvect3_normalize(axis);
    H = hodge_dual(_naxis);

    ret += H*sinf(angle);
    H = H*H;
    ret -= H*cosf(angle);
    ret += (H + cumat3_eye());

    return ret;
}

};