#include <amsculib2/amsculib2.hpp>

namespace amscuda
{

    __host__ __device__ double dabs(double x)
    {
        if(x<0.0)
        {
            x = -x;
        }
        return x;
    }

    __host__ __device__ float fabs(float x)
    {
        if(x<0.0f)
        {
            x = -x;
        }
        return x;
    }

    __host__ __device__ double mod(double x, double md)
    {
        x = fmod(x,md);
        if(x<0.0)
        {
            x = x + md;
        }
        return x;
    }

    __host__ __device__ float mod(float x, float md)
    {
        x = fmodf(x,md);
        if(x<0.0f)
        {
            x = x + md;
        }
        return x;
    }

    __host__ __device__ int mod(int x, int n)
    {
        x = x % n;
        if(x<0)
        {
            x = x + n;
        }
        return x;
    }

    __host__ __device__ long mod(long x, long n)
    {
        x = x % n;
        if(x<0)
        {
            x = x + n;
        }
        return x;
    }

    __host__ __device__ int truediv(int x, int y)
    {
        int z = 0;
        if(x>=0 && y>0)
        {
            z = x/y;
        }
        else if(x<0 && y>0)
        {
            z = -((-x)/y) - 1;
        }
        else if(x>=0 && y<0)
        {
            z = -(x/(-y)) - 1;
        }
        else if(x<0 && y<0)
        {
            z = ((-x)/(-y));
        }

        return z;
    }

    __host__ __device__ long truediv(long x, long y)
    {
        int z = 0;
        if(x>=0 && y>0)
        {
            z = x/y;
        }
        else if(x<0 && y>0)
        {
            z = -((-x)/y) - 1;
        }
        else if(x>=0 && y<0)
        {
            z = -(x/(-y)) - 1;
        }
        else if(x<0 && y<0)
        {
            z = ((-x)/(-y));
        }

        return z;
    }


    template<> __host__ __device__ double min(double a, double b)
    {
        if(isnan(a))
        {
            return b;
        }
        else if(isnan(b))
        {
            return a;
        }
        else if(a>b)
        {
            return b;
        }
        else
        {
            return a;
        }
    }

    template<> __host__ __device__ float min(float a, float b)
    {
        if(isnan(a))
        {
            return b;
        }
        else if(isnan(b))
        {
            return a;
        }
        else if(a>b)
        {
            return b;
        }
        else
        {
            return a;
        }
    }

    template<> __host__ __device__ double max(double a, double b)
    {
        if(isnan(a))
        {
            return b;
        }
        else if(isnan(b))
        {
            return a;
        }
        else if(a>b)
        {
            return a;
        }
        else
        {
            return b;
        }
    }

    template<> __host__ __device__ float max(float a, float b)
    {
        if(isnan(a))
        {
            return b;
        }
        else if(isnan(b))
        {
            return a;
        }
        else if(a>b)
        {
            return a;
        }
        else
        {
            return b;
        }
    }

    __device__ __host__ double arg(double x, double y)
    {
        double ret = 0.0;
        double z = ::sqrt(x*x+y*y);

        if(z>0.0)
        {
            if(y<=x && y>=-x)
            {
                ret = asin(y/z);
            }
            else if(y>=x && y>=-x)
            {
                ret = acos(x/z);
            }
            else if(y>=x && y<=-x)
            {
                ret = pi-asin(y/z);
            }
            else
            {
                ret = 2.0*pi-acos(x/z);
            }
        }

        if(ret<0.0)
        {
            ret = 2.0*pi+ret;
        }

        return ret;
    }

    __device__ __host__ void get_azel(double x, double y, double z, double *az, double *el)
    {
        //int ret = -2; //should never see this return
        double n, rp;
        n = ::sqrt(x*x+y*y+z*z);
        if(n>0.0)
        {
            rp = ::sqrt(x*x+y*y);
            if(rp>0.0)
            {
                //ret = 1; //nonzero vector - should work
                *az = arg(x,y);
                *el = ::atan(z/rp);
            }
            else
            {
                //ret = 0; //straight up or straight down
                if(z>0.0)
                {
                    *az = 0.0;
                    *el = pi/2.0;
                }
                else
                {
                    *az = 0.0;
                    *el = -pi/2.0;
                }
            }
        }
        else
        {
            *az = 0.0;
            *el = 0.0;
            //ret = -1; //zero vector - no real azimuth/elevation
        }

        return;
    }


    void test_amscumath1()
    {
        printf("pi = %1.16f\n",amscuda::pi);
    }

};