/** @file  tst_psMatrixVectorArithmetic03.c
 *
 *  @brief Test driver for psMatrixVector arithmetic functions
 *
 *  This test driver contains negative tests for psBinaryOp and psUanryOp:
 *     Check for NULL arguments
 *     Inconsistent element types
 *     Inconsistent element count
 *     Inconsistent dimensionality
 *     Division by zero
 *     Attempt to use min with complex numbers
 *     Attempt to use max with complex numbers
 *     Invalid operation
 *
 *  @author  Ross Harman, MHPCC
 *
 *  @version $Revision: 1.5 $  $Name: not supported by cvs2svn $
 *  @date  $Date: 2004-10-12 19:18:20 $
 *
 *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
 *
 */

#include "pslib.h"
#include "psTest.h"


#define PRINT_VECTOR(VECTOR,TYPE)                                                                            \
for(int i=0; i<VECTOR->n; i++) {                                                                         \
    if(PS_IS_PSELEMTYPE_COMPLEX(VECTOR->type.type)) {                                                    \
        printf("%f+%fi ", creal(VECTOR->data.TYPE[i]), cimag(VECTOR->data.TYPE[i]));                     \
    } else if(PS_IS_PSELEMTYPE_INT(VECTOR->type.type)) {                                                 \
        printf("%d ", (int)VECTOR->data.TYPE[i]);                                                        \
    } else {                                                                                             \
        printf("%f ", (double)VECTOR->data.TYPE[i]);                                                     \
    }                                                                                                    \
}                                                                                                        \
printf("\n\n");


#define PRINT_MATRIX(IMAGE,TYPE)                                                                             \
for(int i=IMAGE->numRows-1; i>-1; i--) {                                                                 \
    for(int j=0; j<IMAGE->numCols; j++) {                                                                \
        if(PS_IS_PSELEMTYPE_COMPLEX(IMAGE->type.type)) {                                                 \
            printf("%f+%fi ", creal(IMAGE->data.TYPE[i][j]), cimag(IMAGE->data.TYPE[i][j]));             \
        } else if(PS_IS_PSELEMTYPE_INT(IMAGE->type.type)) {                                              \
            printf("%d ", (int)IMAGE->data.TYPE[i][j]);                                                  \
        } else {                                                                                         \
            printf("%f ", (double)IMAGE->data.TYPE[i][j]);                                               \
        }                                                                                                \
    }                                                                                                    \
    printf("\n");                                                                                        \
}                                                                                                        \
printf("\n");


#define CREATE_AND_SET_VECTOR(NAME,TYPE,VALUE,SIZE)                                                          \
psVector *NAME = (psVector*)psVectorAlloc(SIZE, PS_TYPE_##TYPE);                                         \
for(int i=0; i<SIZE; i++) {                                                                              \
    NAME->data.TYPE[i] = VALUE;                                                                          \
}                                                                                                        \
NAME->n = SIZE;


#define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)                                                    \
psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                      \
for(int i=0; i<NAME->numRows; i++) {                                                                     \
    for(int j=0; j<NAME->numCols; j++) {                                                                 \
        NAME->data.TYPE[i][j] = VALUE;                                                                   \
    }                                                                                                    \
}


int main(int argc, char* argv[])
{
    CREATE_AND_SET_IMAGE(image1,F64,0,3,3);
    CREATE_AND_SET_IMAGE(image2,F64,0,3,3);
    CREATE_AND_SET_IMAGE(image3,F32,0,3,3);
    CREATE_AND_SET_IMAGE(image4,F64,0,2,2);
    CREATE_AND_SET_IMAGE(image5,C32,1+1i,3,3);
    CREATE_AND_SET_VECTOR(vector1,F64,0,2);
    CREATE_AND_SET_VECTOR(vector2,F64,0,3);


    // Check for NULL arguments
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic", "Check for NULL arguments",
                            "Null XXX argument", 0);
    psImage* image6 = (psImage*)psBinaryOp(NULL, image1, "+", image2);
    if (image6 == NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp failed to make an image given no output to recycle.");
        return 1;
    }

    image6 = (psImage*)psBinaryOp(image6, NULL, "+", image2);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given a NULL first operand.");
        return 2;
    }

    image6 = (psImage*)psBinaryOp(image6, image1, "+", NULL);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given a NULL second operand.");
        return 3;
    }
    image6 = (psImage*)psBinaryOp(image6, image1, NULL, image2);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given a NULL operator.");
        return 4;
    }

    image6 = (psImage*)psUnaryOp(NULL, image1, "sin");
    if (image6 == NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp failed to make an image given no output to recycle.");
        return 5;
    }

    image6 = (psImage*)psUnaryOp(image6, NULL, "sin");
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned a result given a NULL operand.");
        return 6;
    }

    image6 = (psImage*)psUnaryOp(image6, image1, NULL);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned a result given a NULL operator.");
        return 7;
    }

    printFooter(stdout, "psMatrixVectorArithmetic", "Check for NULL arguments", true);

    // Inconsistent element types
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic", "Inconsistent element types",
                            "Element types for arguments inconsistent", 0);
    image6 = (psImage*)psBinaryOp(image6, image3, "+", image2);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different types.");
        return 8;
    }

    image6 = psImageCopy(image6,image2,PS_TYPE_F64);
    image6 = (psImage*)psUnaryOp(image6, image3, "sin");
    if (image6 == NULL || image6->type.type != PS_TYPE_F32) {
        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp failed to convert the type of the output.");
        return 9;
    }

    printFooter(stdout, "psMatrixVectorArithmetic", "Inconsistent element types", true);


    // Inconsistent element count
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic", "Inconsistent element count",
                            "Inconsistent element count", 0);
    image6 = (psImage*)psBinaryOp(image6, image4, "+", image2);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different sizes.");
        return 10;
    }

    image6 = psImageCopy(image6,image2,PS_TYPE_F64);
    image6 = (psImage*)psUnaryOp(image6, image4, "sin");
    if (image6 == NULL ||
            image6->numCols != image6->numCols ||
            image6->numRows != image6->numRows) {
        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp failed to resize the output.");
        return 11;
    }

    image6 = (psImage*)psBinaryOp(image6, vector1, "+", image2);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different sizes.");
        return 12;
    }

    vector1->type.dimen = PS_DIMEN_TRANSV;
    image6 = (psImage*)psBinaryOp(image6, vector1, "+", image2);
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psBinaryOp returned a result given operands of different sizes.");
        return 13;
    }
    printFooter(stdout, "psMatrixVectorArithmetic", "Inconsistent element count", true);


    // Inconsistent dimensionality
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic", "Inconsistent dimensionality",
                            "Dimensionality for arguments inconsistent", 0);
    image6 = psImageCopy(image6,image2,PS_TYPE_F64);
    image6 = (psImage*)psUnaryOp(image6, vector2, "sin");
    if (image6 != NULL) {
        psLogMsg(__func__,PS_LOG_ERROR,"psUnaryOp returned result given wrong type out parameter.");
        return 14;
    }

    printFooter(stdout, "psMatrixVectorArithmetic", "Inconsistent dimensionality", true);

    // Attempt to use min with complex numbers
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic", "Use min with complex numbers",
                            "Minimum operation not supported for complex numbers", 0);
    image6 = (psImage*)psBinaryOp(image6, image5, "min", image5);
    printFooter(stdout, "psMatrixVectorArithmetic", "Use min with complex numbers", true);


    // Attempt to use max with complex numbers
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic", "Use max with complex numbers",
                            "Maximum operation not supported for complex numbers", 0);
    image6 = (psImage*)psBinaryOp(image6, image5, "max", image5);
    printFooter(stdout, "psMatrixVectorArithmetic", "Use max with complex numbers", true);


    // Invalid operation
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic", "Invalid operation",
                            "Invalid operation", 0);
    image6 = (psImage*)psBinaryOp(image6, image1, "yarg", image2);
    image6 = (psImage*)psUnaryOp(image6, image1, "yarg");
    printFooter(stdout, "psMatrixVectorArithmetic", "Invalid operation", true);

    // Invalid parameter in2
    printNegativeTestHeader(stdout,"psMatrixVectorArithmetic","Invalid input",
                            "Invalid operation", 0);
    CREATE_AND_SET_VECTOR(vector4,F64,0,3);
    CREATE_AND_SET_VECTOR(vector5,F64,0,3);
    CREATE_AND_SET_VECTOR(vector6,F64,0,3);
    if ( psBinaryOp(vector6,vector4,"+",NULL) != NULL ) {
        psError(__func__,"psBinaryOp should return null when out and in1 valid but in2 null.");
        return 10;
    }
    printFooter(stdout, "psMatrixVectorArithmetic", "Invalid input", true);

    vector4->type.dimen = PS_DIMEN_OTHER;
    if ( psBinaryOp(NULL,vector4,"+",vector5) != NULL) {
        psError(__func__,"psBinaryOp should return null when input dimen PS_DIMEN_OTHER.");
        return 11;
    }
    vector4->type.dimen = PS_DIMEN_VECTOR;

    psScalar* inScalar = psScalarAlloc(0,PS_TYPE_F64);
    CREATE_AND_SET_IMAGE(image10,F64,0,3,3);
    if ( psBinaryOp(image10,inScalar,"+",vector4) == NULL ) {
        psError(__func__,"psBinaryOp should not return null when input/out dimension don't match.");
        return 12;
    }

    psFree(vector4);
    psFree(vector5);

    return 0;
}

