/**  @file  tst_psCoord01.c
*
*    @brief  The code will test several functions with PSLib source file
*            psCoord.c
*
*    @author Eric Van Alst, MHPCC
*
* XXX: must do (r,d) -> (x, y) -> (r, d) test to ensure correctness.
* XXX: The (Xs, Ys) scales are not be used properly.
*
*
*    @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
*    @date  $Date: 2005-12-21 00:03:03 $
*    @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
*    @date  $Date: 2005-12-21 00:03:03 $
*
*    Copyright 2005 Maui High Performance Computing Center, Univ. of Hawaii
*/

#include "pslib_strict.h"
#include "psTest.h"

static psS32 testProjectionAlloc(void);
static psS32 testProjectTan(void);
static psS32 testDeprojectTan(void);
static psS32 testProjectSin(void);
static psS32 testDeprojectSin(void);
static psS32 testProjectAit(void);
static psS32 testDeprojectAit(void);
static psS32 testProjectPar(void);
static psS32 testDeprojectPar(void);
static psS32 testProjectFail(void);
static psS32 testDeprojectFail(void);
static psS32 testSetOffsetSphere(void);
static psS32 testSetOffsetLinear(void);
static psS32 testGetOffsetSphere(void);
static psS32 testGetOffsetLinear(void);
static psS32 testProjectTanDeProjectTan(void);

testDescription tests[] = {
                              {testProjectionAlloc, 833, "psProjectionAlloc",0,false},
                              {testProjectFail, 834, "psProject",0,false},
                              {testDeprojectFail, 835, "psProject",0,false},
                              {testSetOffsetSphere, 0000, "psSphereSetOffset",0,false},
                              {testSetOffsetLinear, 0000, "psSphereSetOffset",0,false},
                              {testGetOffsetSphere, 0000, "psSphereGetOffset",0,false},
                              {testGetOffsetLinear, 0000, "psSphereGetOffset",0,false},
                              {NULL},
                              /* XXX: Tests removed pending valuation -- test itself may be faulty. */
                              {testProjectTan, 834, "psProject(TAN)",0,false},
                              {testDeprojectTan, 835, "psDeproject(TAN)",0,false},
                              {testProjectSin, 834, "psProject(SIN)",0,false},
                              {testDeprojectSin, 835, "psDeproject(SIN)",0,false},
                              {testProjectAit, 834, "psProject(AIT)",0,false},
                              {testDeprojectAit, 835, "psDeproject(AIT)",0,false},
                              {testProjectPar, 834, "psProject(PAR)",0,false},
                              {testDeprojectPar, 835, "psDeproject(PAR)",0,false},
                              {testProjectTanDeProjectTan, 0000, "psProject(TAN) -> psDeproject(TAN)",0,false}
                          };


#define ERROR_TOL    0.0001
#define TESTPOINTS   4
#define DEG_INC   30.0

#define MY_TINY 0.0001
#define PS_COMPARE_TINY_THEN_PRINT_ERROR(ACTUAL, EXPECT, TESTSTATUS) \
if (MY_TINY < fabs(EXPECT - ACTUAL)) { \
    psError(PS_ERR_UNKNOWN,true,"%s is %lg, should be %lg", #ACTUAL, ACTUAL, EXPECT, TESTSTATUS); \
    return TESTSTATUS; \
}


//  alpha, delta, alpha-center, delta-center, scale-x, scale-y
psF64 projectionTestPoint[TESTPOINTS][6] = {
            {  0.785398,  0.785398,  0.000000,  0.000000,  1.000000,  1.000000 },
            {  0.100000,  1.500000,  0.500000,  0.250000,  1.000000,  1.000000 },
            {  0.628319,  0.448799,  0.000000,  0.000000,  0.250000,  0.750000 },
            { -1.047196,  0.222222, -0.250000,  0.000000,  1.500000,  1.250000 }
        };

// Expected values for TAN
psF64 projectionTanExpected[TESTPOINTS][2] = {
            { -1.000000, -1.414214 },
            {  0.088884, -3.066567 },
            { -0.181636, -0.446444 },
            {  1.535818, -0.404231 }
        };

// Expected values for SIN
psF64 projectionSinExpected[TESTPOINTS][2] = {
            { -0.500000, -0.707101 },
            {  0.027546, -0.950366 },
            { -0.132394, -0.325413 },
            {  1.046712, -0.275497 }
        };

// Expected values for AIT
psF64 projectionAitExpected[TESTPOINTS][2] = {
            { -0.549175,  0.523375 },
            {  0.027895,  0.313807 },
            { -0.162822,  0.607646 },
            {  1.455312,  0.955388 }
        };

// Expected values for PAR
psF64 projectionParExpected[TESTPOINTS][2] = {
            { -0.541244,  0.545532 },
            {  0.027703,  0.329366 },
            { -0.157157,  0.633550 },
            {  1.432951,  0.971372 }
        };

// Testpoints, offset and expected values for psSphereSetOffset test
#define TESTPOINTS_OFFSET  4
psF64 setOffsetTestpoint[TESTPOINTS_OFFSET][2] = {
            { 0.50,  0.50 },
            {-0.50,  0.50 },
            { 0.00,  0.00 },
            { 1.50, -0.10 }
        };

psF64 setOffsetOffset[TESTPOINTS_OFFSET][2] = {
            { 0.190761, -0.272205 },
            {-0.553049, -0.460926 },
            { 0.100335,-14.172222 },
            {14.172222, -0.100335 }
        };

psF64 setOffsetResult[TESTPOINTS_OFFSET][2] = {
            { 0.25,  0.75 },
            { 0.20,  0.80 },
            {-0.10,  1.50 },
            { 0.00,  0.00 }
        };

psF64 getOffsetTestpoint1[TESTPOINTS_OFFSET][2] = {
            { 0.00,  0.00 },
            {-0.25,  0.50 },
            { 0.50, -0.25 },
            { 0.25, -0.10 }
        };

psF64 getOffsetTestpoint2[TESTPOINTS_OFFSET][2] = {
            { 0.75,  0.25 },
            { 0.40, -0.60 },
            { 1.50,  0.50 },
            { 0.10,  0.75 }
        };

psF64 getOffsetResult[TESTPOINTS_OFFSET][2] = {
            { -0.931596, -0.348976 },
            { -1.632830,  2.649629 },
            { -2.166795, -1.707211 },
            {  0.167752, -1.151351 }
        };


psS32 main( psS32 argc, char* argv[] )
{
    psLogSetFormat("HLNM");
    psLogSetLevel( PS_LOG_INFO );

    return ( !runTestSuite(stderr,"psCoord",tests,argc,argv) );
}

psS32 testProjectionAlloc(void)
{
    // Allocate new psProjection structure
    psProjection*  myProjection = psProjectionAlloc(1.1, 2.2, 3.3, 4.4, PS_PROJ_AIT);

    // Verify NULL is not returned
    if(myProjection == NULL) {
        psError(PS_ERR_UNKNOWN,true,"Unexpected return of NULL");
        return 1;
    }
    // Verify the members are set correctly
    if(myProjection->R != 1.1) {
        psError(PS_ERR_UNKNOWN,true,"R member set to %lg, but expected %lg",
                myProjection->R, 1.1);
        return 2;
    }
    if(myProjection->D != 2.2) {
        psError(PS_ERR_UNKNOWN,true,"D member set to %lg, but expected %lg",
                myProjection->D, 2.2);
        return 3;
    }
    if(myProjection->Xs != 3.3) {
        psError(PS_ERR_UNKNOWN,true,"Xs member set to %lg, but expected %lg",
                myProjection->Xs, 3.3);
        return 4;
    }
    if(myProjection->Ys != 4.4) {
        psError(PS_ERR_UNKNOWN,true,"Ys member set to %lg, but expected %lg",
                myProjection->Ys, 4.4);
        return 5;
    }
    if(myProjection->type != PS_PROJ_AIT) {
        psError(PS_ERR_UNKNOWN,true,"type member set to %d, but expected %d",
                myProjection->type, PS_PROJ_AIT);
        return 6;
    }

    // Free projection
    psFree(myProjection);

    return 0;
}

//HEY
psS32 testProjectTan(void)
{
    psPlane*       out = NULL;
    psSphere*      in = psSphereAlloc();
    psSphere*      inTest = NULL;
    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);

    // Perform projecton on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and project members
        in->r = projectionTestPoint[i][0];
        in->d = projectionTestPoint[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform projection
        out = psProject(in, myProjection);
        inTest = psDeproject(out, myProjection);

        // Verify output not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return i*10+3;
        }

        // Verify output is as expected
        if(fabs(out->x - projectionTanExpected[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
                    i,out->x, projectionTanExpected[i][0]);
            return i*10+1;
        }
        if(fabs(out->y - projectionTanExpected[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
                    i,out->y, projectionTanExpected[i][1]);
            return i*10+2;
        }

        // Verify output is as expected
        if(fabs(in->r - inTest->r) > ERROR_TOL) {
            printf("TEST ER (in->r, inTest->r) (%.2f %.2f)\n", in->r, inTest->r);
            return i*10+4;
        }
        if(fabs(in->d - inTest->d) > ERROR_TOL) {
            printf("TEST ERROR: (in->d, inTest->d) (%.2f %.2f)\n", in->d, inTest->d);
            return i*10+5;
        }

        psFree(inTest);
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testDeprojectTan(void)
{
    psSphere*       out = NULL;
    psPlane*        in = psPlaneAlloc();
    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);

    // Perform deprojection on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and projection members
        in->x = projectionTanExpected[i][0];
        in->y = projectionTanExpected[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform deprojection
        out = psDeproject(in, myProjection);

        // Verify output is not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return 20*i;
        }

        // Verify output is as expected
        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
                    i,out->r,projectionTestPoint[i][0]);
            return 20*i + 1;
        }
        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
                    i, out->d, projectionTestPoint[i][1]);
            return 20*i + 2;
        }
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testProjectSin(void)
{
    psPlane*       out = NULL;
    psSphere*      in = psSphereAlloc();
    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_SIN);

    // Perform projecton on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and project members
        in->r = projectionTestPoint[i][0];
        in->d = projectionTestPoint[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform projection
        out = psProject(in, myProjection);

        // Verify output not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return i*10;
        }

        // Verify output is as expected
        if(fabs(out->x - projectionSinExpected[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
                    i,out->x, projectionSinExpected[i][0]);
            return i*10+1;
        }
        if(fabs(out->y - projectionSinExpected[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
                    i,out->y, projectionSinExpected[i][1]);
            return i*10+2;
        }
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testDeprojectSin(void)
{
    psSphere*       out = NULL;
    psPlane*        in = psPlaneAlloc();
    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_SIN);

    // Perform deprojection on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and projection members
        in->x = projectionSinExpected[i][0];
        in->y = projectionSinExpected[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform deprojection
        out = psDeproject(in, myProjection);

        // Verify output is not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return 20*i;
        }

        // Verify output is as expected
        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
                    i,out->r,projectionTestPoint[i][0]);
            return 20*i + 1;
        }
        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
                    i, out->d, projectionTestPoint[i][1]);
            return 20*i + 2;
        }
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testProjectAit(void)
{
    psPlane*       out = NULL;
    psSphere*      in = psSphereAlloc();
    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_AIT);

    // Perform projecton on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and project members
        in->r = projectionTestPoint[i][0];
        in->d = projectionTestPoint[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform projection
        out = psProject(in, myProjection);

        // Verify output not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return i*10;
        }

        // Verify output is as expected
        if(fabs(out->x - projectionAitExpected[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
                    i,out->x, projectionAitExpected[i][0]);
            return i*10+1;
        }
        if(fabs(out->y - projectionAitExpected[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
                    i,out->y, projectionAitExpected[i][1]);
            return i*10+2;
        }
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testDeprojectAit(void)
{
    psSphere*       out = NULL;
    psPlane*        in = psPlaneAlloc();
    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_AIT);

    // Perform deprojection on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and projection members
        in->x = projectionAitExpected[i][0];
        in->y = projectionAitExpected[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform deprojection
        out = psDeproject(in, myProjection);

        // Verify output is not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return 20*i;
        }

        // Verify output is as expected
        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
                    i,out->r,projectionTestPoint[i][0]);
            return 20*i + 1;
        }
        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
                    i, out->d, projectionTestPoint[i][1]);
            return 20*i + 2;
        }
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testProjectPar(void)
{
    psPlane*       out = NULL;
    psSphere*      in = psSphereAlloc();
    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_PAR);

    // Perform projecton on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and project members
        in->r = projectionTestPoint[i][0];
        in->d = projectionTestPoint[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform projection
        out = psProject(in, myProjection);

        // Verify output not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return i*10;
        }

        // Verify output is as expected
        if(fabs(out->x - projectionParExpected[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psPlane->x = %lg  expected %lg",
                    i,out->x, projectionParExpected[i][0]);
            return i*10+1;
        }
        if(fabs(out->y - projectionParExpected[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint % d  psPlane->y = %lg  expected %lg",
                    i,out->y, projectionParExpected[i][1]);
            return i*10+2;
        }
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testDeprojectPar(void)
{
    psSphere*       out = NULL;
    psPlane*        in = psPlaneAlloc();
    psProjection*   myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_PAR);

    // Perform deprojection on various test points
    for(psS32 i = 0; i < TESTPOINTS; i++) {

        // Initialize input and projection members
        in->x = projectionParExpected[i][0];
        in->y = projectionParExpected[i][1];
        myProjection->R = projectionTestPoint[i][2];
        myProjection->D = projectionTestPoint[i][3];
        myProjection->Xs = projectionTestPoint[i][4];
        myProjection->Ys = projectionTestPoint[i][5];

        // Perform deprojection
        out = psDeproject(in, myProjection);

        // Verify output is not NULL
        if(out == NULL) {
            psError(PS_ERR_UNKNOWN,true,"Return null not expected");
            return 20*i;
        }

        // Verify output is as expected
        if(fabs(out->r - projectionTestPoint[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->r = %lg  expected %lg",
                    i,out->r,projectionTestPoint[i][0]);
            return 20*i + 1;
        }
        if(fabs(out->d - projectionTestPoint[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d  psSphere->d = %lg expected %lg",
                    i, out->d, projectionTestPoint[i][1]);
            return 20*i + 2;
        }
        psFree(out);
    }

    psFree(in);
    psFree(myProjection);

    return 0;
}

psS32 testProjectFail(void)
{
    psPlane*       out          = NULL;
    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
    psSphere*      in           = psSphereAlloc();

    // Invoke function with null coordinate argument
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null coord arg");
    out = psProject(NULL, myProjection);
    if(out != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
        return 1;
    }

    // Invoke function with null projection argument
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null projection arg");
    out = psProject(in, NULL);
    if(out != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
        return 2;
    }

    // Invoke function with invalid projection type
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid projection type");
    myProjection->type = PS_PROJ_NTYPE;
    out = psProject(in,myProjection);
    if(out != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
        return 3;
    }

    psFree(myProjection);
    psFree(in);

    return 0;
}

psS32 testDeprojectFail(void)
{
    psSphere*      out          = NULL;
    psProjection*  myProjection = psProjectionAlloc(0.0,0.0,1.0,1.0,PS_PROJ_TAN);
    psPlane*       in           = psPlaneAlloc();

    // Invoke function with null coordinate argument
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null coord arg");
    out = psDeproject(NULL, myProjection);
    if(out != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
        return 1;
    }

    // Invoke function with null projection argument
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null projection arg");
    out = psDeproject(in, NULL);
    if(out != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
        return 2;
    }

    // Invoke function with invalid projection type
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid projection type");
    myProjection->type = PS_PROJ_NTYPE;
    out = psDeproject(in,myProjection);
    if(out != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return null as expected");
        return 3;
    }

    psFree(myProjection);
    psFree(in);

    return 0;
}

psS32 testSetOffsetSphere( void )
{
    psSphere* position1 = psSphereAlloc();
    psSphere* position2 = NULL;
    psSphere* offset    = psSphereAlloc();
    psSphere* tmpOffset = psSphereAlloc();

    // Initialize first position
    position1->r = DEG_TO_RAD(90.0);
    position1->d = DEG_TO_RAD(45.0);
    position1->rErr = 0.0;
    position1->dErr = 0.0;

    //  Using various offset verify spherical offset
    //  Use all the valid unit types
    for (psF64 r = 0.0; r < 180.0; r += DEG_INC) {
        for (psF64 d = 0.0; d < 90.0; d += DEG_INC) {

            offset->r = DEG_TO_RAD(r);
            offset->d = DEG_TO_RAD(d);
            offset->rErr = 0.0;
            offset->dErr = 0.0;

            position2 = psSphereSetOffset(position1, offset, PS_SPHERICAL, PS_RADIAN);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 1);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 2);
            psFree(position2);

            tmpOffset->r = RAD_TO_DEG(offset->r);
            tmpOffset->d = RAD_TO_DEG(offset->d);
            tmpOffset->rErr = 0.0;
            tmpOffset->dErr = 0.0;
            position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_DEGREE);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 3);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 4);
            psFree(position2);

            tmpOffset->r = RAD_TO_MIN(offset->r);
            tmpOffset->d = RAD_TO_MIN(offset->d);
            tmpOffset->rErr = 0.0;
            tmpOffset->dErr = 0.0;
            position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_ARCMIN);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 5);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 6);
            psFree(position2);

            tmpOffset->r = RAD_TO_SEC(offset->r);
            tmpOffset->d = RAD_TO_SEC(offset->d);
            tmpOffset->rErr = 0.0;
            tmpOffset->dErr = 0.0;
            position2 = psSphereSetOffset(position1, tmpOffset, PS_SPHERICAL, PS_ARCSEC);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->r, (position1->r + offset->r), 7);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(position2->d, (position1->d + offset->d), 8);
            psFree(position2);
        }
    }

    // Attempt to call function with null input coordinate
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message null input coord");
    position2 = psSphereSetOffset(NULL, offset, PS_LINEAR, PS_ARCSEC);
    if (position2 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
        return 30;
    }

    // Attempt to call function with null offset
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message null offset");
    position2 = psSphereSetOffset(position1, NULL, PS_LINEAR, PS_ARCSEC);
    if (position2 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
        return 31;
    }

    // Attempt to call function with invalid mode
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate error message invalid mode");
    position2 = psSphereSetOffset(position1, offset, 0x54321, 0);
    if (position2 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
        return 32;
    }

    // Attempt to call function with invalid unit
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message invalid unit");
    position2 = psSphereSetOffset(position1, offset, PS_SPHERICAL, 0x54321);
    if (position2 != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL");
        return 33;
    }

    psFree(position1);
    psFree(offset);
    psFree(tmpOffset);

    return 0;
}

psS32 testSetOffsetLinear(void)
{
    psSphere*   coord = psSphereAlloc();
    psSphere*   offset = psSphereAlloc();
    psSphere*   out = NULL;

    for(psS32 i = 0; i < TESTPOINTS_OFFSET; i++) {

        coord->r = setOffsetTestpoint[i][0];
        coord->d = setOffsetTestpoint[i][1];

        offset->r = setOffsetOffset[i][0];
        offset->d = setOffsetOffset[i][1];

        out = psSphereSetOffset(coord, offset, PS_LINEAR, PS_RADIAN);

        if(fabs(out->r - setOffsetResult[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->r = %lg not equal to expected %lg",
                    i, out->r, setOffsetResult[i][0]);
            return i*10;
        }
        if(fabs(out->d - setOffsetResult[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->d = %lg not equal to expected %lg",
                    i,out->d, setOffsetResult[i][1]);
            return i*10+1;
        }

        psFree(out);
    }

    psFree(coord);
    psFree(offset);

    return 0;
}

psS32 testGetOffsetSphere( void )
{
    psSphere* position1 = psSphereAlloc();
    psSphere* position2 = psSphereAlloc();
    psSphere *offset = NULL;

    position1->r = DEG_TO_RAD(90.0);
    position1->d = DEG_TO_RAD(45.0);
    position1->rErr = 0.0;
    position1->dErr = 0.0;

    for (psF64 r = 0.0; r < 180.0;r += DEG_INC) {
        for (psF64 d = 0.0;d < 90.0; d += DEG_INC) {
            position2->r = DEG_TO_RAD(r);
            position2->d = DEG_TO_RAD(d);
            position2->rErr = 0.0;
            position2->dErr = 0.0;

            offset = psSphereGetOffset( position1,  position2,
                                        PS_SPHERICAL, PS_RADIAN);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 1);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 1);
            psFree(offset);

            offset = psSphereGetOffset( position1, position2,
                                        PS_SPHERICAL, PS_DEGREE);
            offset->r = DEG_TO_RAD(offset->r);
            offset->d = DEG_TO_RAD(offset->d);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
            psFree(offset);

            offset = psSphereGetOffset( position1,  position2,
                                        PS_SPHERICAL, PS_ARCMIN);
            offset->r = MIN_TO_RAD(offset->r);
            offset->d = MIN_TO_RAD(offset->d);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
            psFree(offset);

            offset = psSphereGetOffset( position1,  position2,
                                        PS_SPHERICAL, PS_ARCSEC);
            offset->r = SEC_TO_RAD(offset->r);
            offset->d = SEC_TO_RAD(offset->d);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->r, (position2->r - position1->r), 2);
            PS_COMPARE_TINY_THEN_PRINT_ERROR(offset->d, (position2->d - position1->d), 3);
            psFree(offset);
        }
    }

    // Attempt to invoke function with null position 1 parameter
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null position");
    offset = psSphereGetOffset(NULL, position2, PS_LINEAR, 0);
    if (offset != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 10;
    }

    // Attempt to invoke function with null position 2 parameter
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for null position");
    offset = psSphereGetOffset(position1, NULL, PS_LINEAR, 0);
    if (offset != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 11;
    }

    // Attempt to invoke function with invalid mode
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid mode");
    offset = psSphereGetOffset(position1, position2, 0x54321, 0);
    if (offset != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 12;
    }

    // Attempt to invoke function with invalid unit type
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate an error message for invalid unit type");
    offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, 0x54321);
    if (offset != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 13;
    }

    // Attempt to invoke function with coordinate 1 declination value 90.0 degree
    position1->d = DEG_TO_RAD(90.0);
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate warning message");
    offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, PS_RADIAN);
    if (offset != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 14;
    }

    // Attempt to invoke function with coordinate 2 declination value 90.0 degree
    position1->d = DEG_TO_RAD(45.0);
    position2->d = DEG_TO_RAD(90.0);
    psLogMsg(__func__,PS_LOG_INFO,"Following should generate warning message");
    offset = psSphereGetOffset(position1, position2, PS_SPHERICAL, PS_RADIAN);
    if (offset != NULL) {
        psError(PS_ERR_UNKNOWN,true,"Did not return NULL as expected");
        return 15;
    }

    psFree(position1);
    psFree(position2);

    return 0;
}

psS32 testGetOffsetLinear(void)
{
    psSphere*   coord1 = psSphereAlloc();
    psSphere*   coord2 = psSphereAlloc();
    psSphere*   out = NULL;

    for(psS32 i = 0; i < TESTPOINTS_OFFSET; i++) {

        coord1->r = getOffsetTestpoint1[i][0];
        coord1->d = getOffsetTestpoint1[i][1];

        coord2->r = getOffsetTestpoint2[i][0];
        coord2->d = getOffsetTestpoint2[i][1];

        out = psSphereGetOffset(coord1, coord2, PS_LINEAR, PS_RADIAN);

        if(fabs(out->r - getOffsetResult[i][0]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->r = %lg not equal to expected %lg",
                    i, out->r, getOffsetResult[i][0]);
            return i*10;
        }
        if(fabs(out->d - getOffsetResult[i][1]) > ERROR_TOL) {
            psError(PS_ERR_UNKNOWN,true,"Testpoint %d: Result out->d = %lg not equal to expected %lg",
                    i,out->d, getOffsetResult[i][1]);
            return i*10+1;
        }

        psFree(out);
    }

    psFree(coord1);
    psFree(coord2);

    return 0;
}

#define DEG_INC2 15.0
#define VERBOSE 0
psS32 testProjectTanDeProjectTan()
{
    psS32 rc = 0;
    //
    // I'm not convinced that the p_psProject() and p_psDeproject() functions work
    // correctly.  If we project a set of coordinates over a wide range of (R, D)
    // values, then deproject them, the original (R, D) values are only produced
    // when D is larger than 0.  This code demonstrates that.
    //
    psProjection *tmpProj = psProjectionAlloc(0.0,0.0,10.0,10.0,PS_PROJ_TAN);
    if (1) { //HEY
        psPlane planeCoord01;
        psSphere skyCoord01;
        psSphere skyCoord02;
        for (psF32 R = -90.0 ; R <= 90.0 ; R+= DEG_INC2) {
            for (psF32 D = -90.0 ; D <= 90.0 ; D+= DEG_INC2) {
                if ((fabs(R) != 90.0) && (fabs(D) != 90.0)) {
                    skyCoord01.r = DEG_TO_RAD(R);
                    skyCoord01.d = DEG_TO_RAD(D);
                    p_psProject(&planeCoord01, &skyCoord01, tmpProj);
                    p_psDeproject(&skyCoord02, &planeCoord01, tmpProj);
                    if ((fabs(skyCoord01.r - skyCoord02.r) < FLT_EPSILON) &&
                            (fabs(skyCoord01.d - skyCoord02.d) < FLT_EPSILON)) {
                        if (VERBOSE) {
                            printf("CORRECT: (%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)\n", R, D,
                                   skyCoord01.r, skyCoord01.d,
                                   planeCoord01.x, planeCoord01.y,
                                   skyCoord02.r, skyCoord02.d);
                        }
                        rc = 1;
                    } else {
                        printf("TEST ERROR: (%.2fr %.2fd) (%.2fr %.2fd) -> (%.2f %.2f) -> (%.2fr %.2fd)\n", R, D,
                               skyCoord01.r, skyCoord01.d,
                               planeCoord01.x, planeCoord01.y,
                               skyCoord02.r, skyCoord02.d);
                        rc = 1;
                    }
                }
            }
        }
    }
    return(rc);
}



