/*
 * Copyright 1993-2010 NVIDIA Corporation.  All rights reserved.
 *
 * Please refer to the NVIDIA end user license agreement (EULA) associated
 * with this source code for terms and conditions that govern your use of
 * this software. Any use, reproduction, disclosure, or distribution of
 * this software and related documentation outside the terms of the EULA
 * is strictly prohibited.
 *
 */
 
// *********************************************************************
// Generic Utilities for NVIDIA GPU Computing SDK 
// *********************************************************************

// includes
#define DPCT_USM_LEVEL_NONE
#include <string.h>
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <stdio.h>
#include "shrUtils.h"
#include <cmath>

using namespace std;

// size of PGM file header 
const unsigned int PGMHeaderSize = 0x40;
#define MIN_EPSILON_ERROR 1e-3f

// Deallocate memory allocated within shrUtils
// *********************************************************************
void shrFree(void* ptr) 
{
  if( NULL != ptr) free( ptr);
}

// Helper function to init data arrays 
// *********************************************************************
void shrFillArray(float* pfData, int iSize)
{
    int i; 
    const float fScale = 1.0f / (float)RAND_MAX;
    for (i = 0; i < iSize; ++i) 
    {
        pfData[i] = fScale * rand();
    }
}

// Helper function to print data arrays 
// *********************************************************************
void shrPrintArray(float* pfData, int iSize)
{
    int i;
    for (i = 0; i < iSize; ++i) 
    {
        shrLog("%d: %.3f\n", i, pfData[i]);
    }
}


// Optional LogFileName Override function
// *********************************************************************
char* cLogFilePathAndName = NULL;
void shrSetLogFileName (const char* cOverRideName)
{
    if( cLogFilePathAndName != NULL ) {
        free(cLogFilePathAndName);
    }
    cLogFilePathAndName = (char*) malloc(strlen(cOverRideName) + 1);
    #ifdef WIN32
        strcpy_s(cLogFilePathAndName, strlen(cOverRideName) + 1, cOverRideName);
    #else
        strcpy(cLogFilePathAndName, cOverRideName);
    #endif
    return;
}

// Function to log standardized information to console, file or both
// *********************************************************************
static int shrLogV(int iLogMode, int iErrNum, const char* cFormatString, va_list vaArgList)
{
    static FILE* pFileStream0 = NULL;
    static FILE* pFileStream1 = NULL;
    size_t szNumWritten = 0;
    char cFileMode [3];

    // if the sample log file is closed and the call includes a "write-to-file", open file for writing
    if ((pFileStream0 == NULL) && (iLogMode & LOGFILE))
    {
        // if the default filename has not been overriden, set to default
        if (cLogFilePathAndName == NULL)
        {
            shrSetLogFileName(DEFAULTLOGFILE); 
        }

        #ifdef _WIN32   // Windows version
            // set the file mode
            if (iLogMode & APPENDMODE)  // append to prexisting file contents
            {
                sprintf_s (cFileMode, 3, "a+");  
            }
            else                        // replace prexisting file contents
            {
                sprintf_s (cFileMode, 3, "w"); 
            }

            // open the individual sample log file in the requested mode
            errno_t err = fopen_s(&pFileStream0, cLogFilePathAndName, cFileMode);
            
            // if error on attempt to open, be sure the file is null or close it, then return negative error code            
            if (err != 0)
            {
                if (pFileStream0)
                {
                    fclose (pFileStream0);
                }
				iLogMode = LOGCONSOLE; // if we can't open a file, we will still output to the console window
            }
        #else           // Linux & Mac version
            // set the file mode
            if (iLogMode & APPENDMODE)  // append to prexisting file contents
            {
                sprintf (cFileMode, "a+");  
            }
            else                        // replace prexisting file contents
            {
                sprintf (cFileMode, "w"); 
            }

            // open the file in the requested mode
            if ((pFileStream0 = fopen(cLogFilePathAndName, cFileMode)) == 0)
            {
                // if error on attempt to open, be sure the file is null or close it, then return negative error code
                if (pFileStream0)
                {
                    fclose (pFileStream0);
                }
				iLogMode = LOGCONSOLE; // if we can't open a file, we will still output to the console window
            }
        #endif
    }
    
    // if the master log file is closed and the call incudes a "write-to-file" and MASTER, open master logfile file for writing
    if ((pFileStream1 == NULL) && (iLogMode & LOGFILE) && (iLogMode & MASTER))
    {
        #ifdef _WIN32   // Windows version
            // open the master log file in append mode
            errno_t err = fopen_s(&pFileStream1, MASTERLOGFILE, "a+");

            // if error on attempt to open, be sure the file is null or close it, then return negative error code
            if (err != 0)
            {
                if (pFileStream1)
                {
                    fclose (pFileStream1);
					pFileStream1 = NULL;
                }
				iLogMode = LOGCONSOLE;  // Force to LOGCONSOLE only since the file stream is invalid
//				return -err;
            }
        #else           // Linux & Mac version

            // open the file in the requested mode
            if ((pFileStream1 = fopen(MASTERLOGFILE, "a+")) == 0)
            {
                // if error on attempt to open, be sure the file is null or close it, then return negative error code
                if (pFileStream1)
                {
                    fclose (pFileStream1);
					pFileStream1 = NULL;
                }
				iLogMode = LOGCONSOLE;  // Force to LOGCONSOLE only since the file stream is invalid
//              return -1;
            }
        #endif
        
        // If master log file length has become excessive, empty/reopen
		if (iLogMode != LOGCONSOLE)
		{
			fseek(pFileStream1, 0L, SEEK_END);            
			if (ftell(pFileStream1) > 50000L)
			{
				fclose (pFileStream1);
			#ifdef _WIN32   // Windows version
				fopen_s(&pFileStream1, MASTERLOGFILE, "w");
			#else
				pFileStream1 = fopen(MASTERLOGFILE, "w");
			#endif
			}
		}
    }

    // Handle special Error Message code
    if (iLogMode & ERRORMSG)  
    {   
        // print string to console if flagged
        if (iLogMode & LOGCONSOLE) 
        {
            szNumWritten = printf ("\n !!! Error # %i at ", iErrNum);                           // console 
        }
        // print string to file if flagged
        if (iLogMode & LOGFILE) 
        {
            szNumWritten = fprintf (pFileStream0, "\n !!! Error # %i at ", iErrNum);            // sample log file
        }
    }

    // Vars used for variable argument processing
    const char*     pStr; 
    const char*     cArg;
    int             iArg;
    double          dArg;
    unsigned int    uiArg;
    std::string sFormatSpec;
    const std::string sFormatChars = " -+#0123456789.dioufnpcsXxEeGgAa";
    const std::string sTypeChars = "dioufnpcsXxEeGgAa";
    char cType = 'c';

    // Start at the head of the string and scan to the null at the end
    for (pStr = cFormatString; *pStr; ++pStr)
    {
        // Check if the current character is not a formatting specifier ('%') 
        if (*pStr != '%')
        {
            // character is not '%', so print it verbatim to console and/or files as flagged
            if (iLogMode & LOGCONSOLE) 
            {
                szNumWritten = putc(*pStr, stdout);                                             // console 
            }
            if (iLogMode & LOGFILE)    
            {
                szNumWritten  = putc(*pStr, pFileStream0);                                      // sample log file
                if (iLogMode & MASTER)                          
                {
                    szNumWritten = putc(*pStr, pFileStream1);                                   // master log file
                }
            }
        } 
        else 
        {
            // character is '%', so skip over it and read the full format specifier for the argument
            ++pStr;
            sFormatSpec = '%';

            // special handling for string of %%%%
            bool bRepeater = (*pStr == '%');
            if (bRepeater)
            {
                cType = '%';
            }

            // chars after the '%' are part of format if on list of constants... scan until that isn't true or NULL is found
            while (pStr && ((sFormatChars.find(*pStr) != string::npos) || bRepeater))    
            {
                sFormatSpec += *pStr;

                // If the char is a type specifier, trap it and stop scanning
                // (a type specifier char is always the last in the format except for string of %%%)
                if (sTypeChars.find(*pStr) != string::npos)    
                {
                    cType = *pStr;
                    break;                                      
                }

                // Special handling for string of %%%
                // If a string of %%% was started and then it ends, break (There won't be a typical type specifier)
                if (bRepeater && (*pStr != '%'))
                {
                    break;
                }

                pStr++;
            }

            // Now handle the arg according to type 
            switch (cType)
            {
                case '%':   // special handling for string of %%%%
                {
                    if (iLogMode & LOGCONSOLE) 
                    {
                        szNumWritten = printf(sFormatSpec.c_str());                             // console 
                    }
                    if (iLogMode & LOGFILE)
                    {
                        szNumWritten = fprintf (pFileStream0, sFormatSpec.c_str());             // sample log file
                        if (iLogMode & MASTER)                          
                        {
                            szNumWritten  = fprintf(pFileStream1, sFormatSpec.c_str());         // master log file
                        }
                    }
                    continue;
                }
                case 'c':   // single byte char
                case 's':   // string of single byte chars
                {
                    // Set cArg as the next value in list and print to console and/or files if flagged
                    cArg = va_arg(vaArgList, char*);
                    if (iLogMode & LOGCONSOLE) 
                    {
                        szNumWritten = printf(sFormatSpec.c_str(), cArg);                       // console 
                    }
                    if (iLogMode & LOGFILE)
                    {
                        szNumWritten = fprintf (pFileStream0, sFormatSpec.c_str(), cArg);       // sample log file
                        if (iLogMode & MASTER)                          
                        {
                            szNumWritten  = fprintf(pFileStream1, sFormatSpec.c_str(), cArg);   // master log file
                        }
                    }
                    continue;
                }
                case 'd':   // signed decimal integer 
                case 'i':   // signed decimal integer 
                {
                    // set iArg as the next value in list and print to console and/or files if flagged
                    iArg = va_arg(vaArgList, int);
                    if (iLogMode & LOGCONSOLE) 
                    {
                        szNumWritten = printf(sFormatSpec.c_str(), iArg);                       // console 
                    }
                    if (iLogMode & LOGFILE)
                    {
                        szNumWritten = fprintf (pFileStream0, sFormatSpec.c_str(), iArg);       // sample log file  
                        if (iLogMode & MASTER)                          
                        {
                            szNumWritten  = fprintf(pFileStream1, sFormatSpec.c_str(), iArg);   // master log file
                        }
                    }
                    continue;
                }
                case 'u':   // unsigned decimal integer 
                case 'o':   // unsigned octal integer 
                case 'x':   // unsigned hexadecimal integer using "abcdef"
                case 'X':   // unsigned hexadecimal integer using "ABCDEF"
                {
                    // set uiArg as the next value in list and print to console and/or files if flagged
                    uiArg = va_arg(vaArgList, unsigned int);
                    if (iLogMode & LOGCONSOLE)                                                  
                    {
                        szNumWritten = printf(sFormatSpec.c_str(), uiArg);                      // console 
                    }
                    if (iLogMode & LOGFILE)
                    {
                        szNumWritten = fprintf (pFileStream0, sFormatSpec.c_str(), uiArg);      // sample log file
                        if (iLogMode & MASTER)                          
                        {
                            szNumWritten  = fprintf(pFileStream1, sFormatSpec.c_str(), uiArg);  // master log file
                        }
                    }
                    continue;
                }
                case 'f':   // float/double
                case 'e':   // scientific double/float
                case 'E':   // scientific double/float
                case 'g':   // scientific double/float
                case 'G':   // scientific double/float
                case 'a':   // signed hexadecimal double precision float
                case 'A':   // signed hexadecimal double precision float
                {
                    // set dArg as the next value in list and print to console and/or files if flagged
                    dArg = va_arg(vaArgList, double);
                    if (iLogMode & LOGCONSOLE) 
                    {
                        szNumWritten = printf(sFormatSpec.c_str(), dArg);                       // console 
                    }
                    if (iLogMode & LOGFILE)
                    {
                        szNumWritten = fprintf (pFileStream0, sFormatSpec.c_str(), dArg);       // sample log file
                        if (iLogMode & MASTER)                          
                        {
                            szNumWritten  = fprintf(pFileStream1, sFormatSpec.c_str(), dArg);   // master log file
                        }
                    }
                    continue;
                }
                default: 
                {
                    // print arg of unknown/unsupported type to console and/or file if flagged
                    if (iLogMode & LOGCONSOLE)                          // console 
                    {
                        szNumWritten = putc(*pStr, stdout);
                    }
                    if (iLogMode & LOGFILE)    
                    {
                        szNumWritten  = putc(*pStr, pFileStream0);      // sample log file
                        if (iLogMode & MASTER)                          
                        {
                            szNumWritten  = putc(*pStr, pFileStream1);  // master log file
                        }
                    }
                }
            }
        }
    }

    // end the sample log with a horizontal line if closing
    if (iLogMode & CLOSELOG) 
    {
        if (iLogMode & LOGCONSOLE) 
        {
            printf(HDASHLINE);
        }
        if (iLogMode & LOGFILE)
        {
            fprintf(pFileStream0, HDASHLINE);
        }
    }

    // flush console and/or file buffers if updated
    if (iLogMode & LOGCONSOLE) 
    {
        fflush(stdout);
    }
    if (iLogMode & LOGFILE)
    {
        fflush (pFileStream0);

        // if the master log file has been updated, flush it too
        if (iLogMode & MASTER)
        {
            fflush (pFileStream1);
        }
    }

    // If the log file is open and the caller requests "close file", then close and NULL file handle
    if ((pFileStream0) && (iLogMode & CLOSELOG))
    {
        fclose (pFileStream0);
        pFileStream0 = NULL;
    }
    if ((pFileStream1) && (iLogMode & CLOSELOG))
    {
        fclose (pFileStream1);
        pFileStream1 = NULL;
    }

    // return error code or OK 
    if (iLogMode & ERRORMSG)
    {
        return iErrNum;
    }
    else 
    {
        return 0;
    }
}

// Function to log standardized information to console, file or both
// *********************************************************************
int shrLogEx(int iLogMode = LOGCONSOLE, int iErrNum = 0, const char* cFormatString = "", ...)
{
    va_list vaArgList;

    // Prepare variable agument list 
    va_start(vaArgList, cFormatString);
    int ret = shrLogV(iLogMode, iErrNum, cFormatString, vaArgList);

    // end variable argument handler
    va_end(vaArgList);

    return ret;
}

// Function to log standardized information to console, file or both
// *********************************************************************
int shrLog(const char* cFormatString = "", ...)
{
    va_list vaArgList;

    // Prepare variable agument list 
    va_start(vaArgList, cFormatString);
    int ret = shrLogV(LOGBOTH, 0, cFormatString, vaArgList);

    // end variable argument handler
    va_end(vaArgList);

    return ret;
}

//////////////////////////////////////////////////////////////////////////////
//! Find the path for a file assuming that
//! files are found in the searchPath.
//!
//! @return the path if succeeded, otherwise 0
//! @param filename         name of the file
//! @param executable_path  optional absolute path of the executable
//////////////////////////////////////////////////////////////////////////////
char* shrFindFilePath(const char* filename, const char* executable_path) 
{
    // <executable_name> defines a variable that is replaced with the name of the executable

    // Typical relative search paths to locate needed companion files (e.g. sample input data, or JIT source files)
    // The origin for the relative search may be the .exe file, a .bat file launching an .exe, a browser .exe launching the .exe or .bat, etc
    const char* searchPath[] = 
    {
        "./",                                       // same dir 
        "./data/",                                  // "/data/" subdir 
        "./src/",                                   // "/src/" subdir
        "./src/<executable_name>/data/",            // "/src/<executable_name>/data/" subdir 
        "./inc/",                                   // "/inc/" subdir
        "../",                                      // up 1 in tree 
        "../data/",                                 // up 1 in tree, "/data/" subdir 
        "../src/",                                  // up 1 in tree, "/src/" subdir 
        "../inc/",                                  // up 1 in tree, "/inc/" subdir 
        "../OpenCL/src/<executable_name>/",         // up 1 in tree, "/OpenCL/src/<executable_name>/" subdir 
        "../OpenCL/src/<executable_name>/data/",    // up 1 in tree, "/OpenCL/src/<executable_name>/data/" subdir 
        "../OpenCL/src/<executable_name>/src/",     // up 1 in tree, "/OpenCL/src/<executable_name>/src/" subdir 
        "../OpenCL/src/<executable_name>/inc/",     // up 1 in tree, "/OpenCL/src/<executable_name>/inc/" subdir 
        "../C/src/<executable_name>/",              // up 1 in tree, "/C/src/<executable_name>/" subdir 
        "../C/src/<executable_name>/data/",         // up 1 in tree, "/C/src/<executable_name>/data/" subdir 
        "../C/src/<executable_name>/src/",          // up 1 in tree, "/C/src/<executable_name>/src/" subdir 
        "../C/src/<executable_name>/inc/",          // up 1 in tree, "/C/src/<executable_name>/inc/" subdir 
        "../DirectCompute/src/<executable_name>/",      // up 1 in tree, "/DirectCompute/src/<executable_name>/" subdir 
        "../DirectCompute/src/<executable_name>/data/", // up 1 in tree, "/DirectCompute/src/<executable_name>/data/" subdir 
        "../DirectCompute/src/<executable_name>/src/",  // up 1 in tree, "/DirectCompute/src/<executable_name>/src/" subdir 
        "../DirectCompute/src/<executable_name>/inc/",  // up 1 in tree, "/DirectCompute/src/<executable_name>/inc/" subdir 
        "../../",                                   // up 2 in tree 
        "../../data/",                              // up 2 in tree, "/data/" subdir 
        "../../src/",                               // up 2 in tree, "/src/" subdir 
        "../../inc/",                               // up 2 in tree, "/inc/" subdir 
        "../../../",                                // up 3 in tree 
        "../../../src/<executable_name>/",          // up 3 in tree, "/src/<executable_name>/" subdir 
        "../../../src/<executable_name>/data/",     // up 3 in tree, "/src/<executable_name>/data/" subdir 
        "../../../src/<executable_name>/src/",      // up 3 in tree, "/src/<executable_name>/src/" subdir 
        "../../../src/<executable_name>/inc/",      // up 3 in tree, "/src/<executable_name>/inc/" subdir 
        "../../../sandbox/<executable_name>/",      // up 3 in tree, "/sandbox/<executable_name>/" subdir
        "../../../sandbox/<executable_name>/data/", // up 3 in tree, "/sandbox/<executable_name>/data/" subdir
        "../../../sandbox/<executable_name>/src/",  // up 3 in tree, "/sandbox/<executable_name>/src/" subdir
        "../../../sandbox/<executable_name>/inc/"   // up 3 in tree, "/sandbox/<executable_name>/inc/" subdir
    };
    
    // Extract the executable name
    std::string executable_name;
    if (executable_path != 0) 
    {
        executable_name = std::string(executable_path);

    #ifdef _WIN32        
        // Windows path delimiter
        size_t delimiter_pos = executable_name.find_last_of('\\');        
        executable_name.erase(0, delimiter_pos + 1);

		if (executable_name.rfind(".exe") != string::npos)
        {
			// we strip .exe, only if the .exe is found
			executable_name.resize(executable_name.size() - 4);        
		}
    #else
        // Linux & OSX path delimiter
        size_t delimiter_pos = executable_name.find_last_of('/');        
        executable_name.erase(0,delimiter_pos+1);
    #endif
        
    }
    
    // Loop over all search paths and return the first hit
    for( unsigned int i = 0; i < sizeof(searchPath)/sizeof(char*); ++i )
    {
        std::string path(searchPath[i]);        
        size_t executable_name_pos = path.find("<executable_name>");

        // If there is executable_name variable in the searchPath 
        // replace it with the value
        if(executable_name_pos != std::string::npos)
        {
            if(executable_path != 0) 
            {
                path.replace(executable_name_pos, strlen("<executable_name>"), executable_name);

            } 
            else 
            {
                // Skip this path entry if no executable argument is given
                continue;
            }
        }
        
        // Test if the file exists
        path.append(filename);
        std::fstream fh(path.c_str(), std::fstream::in);
        if (fh.good())
        {
            // File found
            // returning an allocated array here for backwards compatibility reasons
            char* file_path = (char*) malloc(path.length() + 1);
        #ifdef _WIN32  
            strcpy_s(file_path, path.length() + 1, path.c_str());
        #else
            strcpy(file_path, path.c_str());
        #endif                
            return file_path;
        }
    }    

    // File not found
    return 0;
}

//////////////////////////////////////////////////////////////////////////////
//! Read file \filename and return the data
//! @return shrTRUE if reading the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  uninitialized pointer, returned initialized and pointing to
//!        the data read
//! @param len  number of data elements in data, -1 on error
//////////////////////////////////////////////////////////////////////////////
template<class T>
shrBOOL
shrReadFile( const char* filename, T** data, unsigned int* len, bool verbose) 
{
    // check input arguments
    ARGCHECK(NULL != filename);
    ARGCHECK(NULL != len);

    // intermediate storage for the data read
    std::vector<T>  data_read;

    // open file for reading
    std::fstream fh( filename, std::fstream::in);
    // check if filestream is valid
    if(!fh.good()) 
    {
        if (verbose)
            std::cerr << "shrReadFile() : Opening file failed." << std::endl;
        return shrFALSE;
    }

    // read all data elements 
    T token;
    while( fh.good()) 
    {
        fh >> token;   
        data_read.push_back( token);
    }

    // the last element is read twice
    data_read.pop_back();

    // check if reading result is consistent
    if( ! fh.eof()) 
    {
        if (verbose)
            std::cerr << "WARNING : readData() : reading file might have failed." 
            << std::endl;
    }

    fh.close();

    // check if the given handle is already initialized
    if( NULL != *data) 
    {
        if( *len != data_read.size()) 
        {
            std::cerr << "shrReadFile() : Initialized memory given but "
                      << "size  mismatch with signal read "
                      << "(data read / data init = " << (unsigned int)data_read.size()
                      <<  " / " << *len << ")" << std::endl;

            return shrFALSE;
        }
    }
    else 
    {
        // allocate storage for the data read
		*data = (T*) malloc( sizeof(T) * data_read.size());
        // store signal size
        *len = static_cast<unsigned int>( data_read.size());
    }

    // copy data
    memcpy( *data, &data_read.front(), sizeof(T) * data_read.size());

    return shrTRUE;
}

//////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename 
//! @return shrTRUE if writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
//////////////////////////////////////////////////////////////////////////////
template<class T>
shrBOOL
shrWriteFile( const char* filename, const T* data, unsigned int len,
              const T epsilon, bool verbose) 
{
    ARGCHECK(NULL != filename);
    ARGCHECK(NULL != data);

    // open file for writing
    std::fstream fh( filename, std::fstream::out);
    // check if filestream is valid
    if(!fh.good()) 
    {
        if (verbose)
            std::cerr << "shrWriteFile() : Opening file failed." << std::endl;
        return shrFALSE;
    }

    // first write epsilon
    fh << "# " << epsilon << "\n";

    // write data
    for( unsigned int i = 0; (i < len) && (fh.good()); ++i) 
    {
        fh << data[i] << ' ';
    }

    // Check if writing succeeded
    if( ! fh.good()) 
    {
        if (verbose)
            std::cerr << "shrWriteFile() : Writing file failed." << std::endl;
        return shrFALSE;
    }

    // file ends with nl
    fh << std::endl;

    return shrTRUE;
}

////////////////////////////////////////////////////////////////////////////////
//! Read file \filename containg single precision floating point data 
//! @return shrTRUEif reading the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  uninitialized pointer, returned initialized and pointing to
//!        the data read
//! @param len  number of data elements in data, -1 on error
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrReadFilef( const char* filename, float** data, unsigned int* len, bool verbose) 
{
    return shrReadFile( filename, data, len, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Read file \filename containg double precision floating point data 
//! @return shrTRUEif reading the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  uninitialized pointer, returned initialized and pointing to
//!        the data read
//! @param len  number of data elements in data, -1 on error
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrReadFiled( const char* filename, double** data, unsigned int* len, bool verbose) 
{
    return shrReadFile( filename, data, len, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Read file \filename containg integer data 
//! @return shrTRUEif reading the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  uninitialized pointer, returned initialized and pointing to
//!        the data read
//! @param len  number of data elements in data, -1 on error
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrReadFilei( const char* filename, int** data, unsigned int* len, bool verbose) 
{
    return shrReadFile( filename, data, len, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Read file \filename containg unsigned integer data 
//! @return shrTRUEif reading the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  uninitialized pointer, returned initialized and pointing to
//!        the data read
//! @param len  number of data elements in data, -1 on error
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrReadFileui( const char* filename, unsigned int** data, unsigned int* len, bool verbose) 
{
    return shrReadFile( filename, data, len, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Read file \filename containg char / byte data 
//! @return shrTRUEif reading the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  uninitialized pointer, returned initialized and pointing to
//!        the data read
//! @param len  number of data elements in data, -1 on error
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrReadFileb( const char* filename, char** data, unsigned int* len, bool verbose) 
{
    return shrReadFile( filename, data, len, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Read file \filename containg unsigned char / byte data 
//! @return shrTRUEif reading the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  uninitialized pointer, returned initialized and pointing to
//!        the data read
//! @param len  number of data elements in data, -1 on error
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrReadFileub( const char* filename, unsigned char** data, unsigned int* len, bool verbose) 
{
    return shrReadFile( filename, data, len, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename for single precision floating point data
//! @return shrTRUEif writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrWriteFilef( const char* filename, const float* data, unsigned int len,
               const float epsilon, bool verbose) 
{
    return shrWriteFile( filename, data, len, epsilon, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename for double precision floating point data
//! @return shrTRUEif writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrWriteFiled( const char* filename, const double* data, unsigned int len,
               const double epsilon, bool verbose) 
{
    return shrWriteFile( filename, data, len, epsilon, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename for integer data
//! @return shrTRUEif writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrWriteFilei( const char* filename, const int* data, unsigned int len, bool verbose) 
{
    return shrWriteFile( filename, data, len, 0, verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename for unsigned integer data
//! @return shrTRUEif writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrWriteFileui( const char* filename,const unsigned int* data,unsigned int len, bool verbose)
{
    return shrWriteFile( filename, data, len, static_cast<unsigned int>(0), verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename for byte / char data
//! @return shrTRUEif writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrWriteFileb( const char* filename, const char* data, unsigned int len, bool verbose) 
{  
    return shrWriteFile( filename, data, len, static_cast<char>(0), verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename for byte / char data
//! @return shrTRUEif writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrWriteFileub( const char* filename, const unsigned char* data, 
                unsigned int len, bool verbose) 
{  
    return shrWriteFile( filename, data, len, static_cast<unsigned char>(0), verbose);
}

////////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename for unsigned byte / char data
//! @return shrTRUEif writing the file succeeded, otherwise shrFALSE
//! @param filename name of the source file
//! @param data  data to write
//! @param len  number of data elements in data, -1 on error
//! @param epsilon  epsilon for comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrWriteFileb( const char* filename,const unsigned char* data,unsigned int len, bool verbose)
{
    return shrWriteFile( filename, data, len, static_cast<unsigned char>(0), verbose);
}

//////////////////////////////////////////////////////////////////////////////
//! Load PGM or PPM file
//! @note if data == NULL then the necessary memory is allocated in the 
//!       function and w and h are initialized to the size of the image
//! @return shrTRUE if the file loading succeeded, otherwise shrFALSE
//! @param file        name of the file to load
//! @param data        handle to the memory for the image file data
//! @param w        width of the image
//! @param h        height of the image
//! @param channels number of channels in image
//////////////////////////////////////////////////////////////////////////////
shrBOOL loadPPM(const char* file, unsigned char** data, 
            unsigned int *w, unsigned int *h, unsigned int *channels) 
{
    FILE* fp = 0;

    #ifdef _WIN32
        // open the file for binary read
        errno_t err;
        if ((err = fopen_s(&fp, file, "rb")) != 0)
    #else
        // open the file for binary read
        if ((fp = fopen(file, "rb")) == 0)
    #endif
        {
            // if error on attempt to open, be sure the file is null or close it, then return negative error code
            if (fp)
            {
                fclose (fp);
            }
            std::cerr << "loadPPM() : Failed to open file: " << file << std::endl;
            return shrFALSE;
        }

    // check header
    char header[PGMHeaderSize];
    if ((fgets( header, PGMHeaderSize, fp) == NULL) && ferror(fp))
    {
        if (fp)
        {
            fclose (fp);
        }
        std::cerr << "loadPPM() : File is not a valid PPM or PGM image" << std::endl;
        *channels = 0;
        return shrFALSE;
    }

    if (strncmp(header, "P5", 2) == 0)
    {
        *channels = 1;
    }
    else if (strncmp(header, "P6", 2) == 0)
    {
        *channels = 3;
    }
    else
    {
        std::cerr << "loadPPM() : File is not a PPM or PGM image" << std::endl;
        *channels = 0;
        return shrFALSE;
    }

    // parse header, read maxval, width and height
    unsigned int width = 0;
    unsigned int height = 0;
    unsigned int maxval = 0;
    unsigned int i = 0;
    while(i < 3) 
    {
        if ((fgets(header, PGMHeaderSize, fp) == NULL) && ferror(fp))
        {
            if (fp)
            {
                fclose (fp);
            }
            std::cerr << "loadPPM() : File is not a valid PPM or PGM image" << std::endl;
            return shrFALSE;
        }
        if(header[0] == '#') continue;

        #ifdef _WIN32
            if(i == 0) 
            {
                i += sscanf_s(header, "%u %u %u", &width, &height, &maxval);
            }
            else if (i == 1) 
            {
                i += sscanf_s(header, "%u %u", &height, &maxval);
            }
            else if (i == 2) 
            {
                i += sscanf_s(header, "%u", &maxval);
            }
        #else
            if(i == 0) 
            {
                i += sscanf(header, "%u %u %u", &width, &height, &maxval);
            }
            else if (i == 1) 
            {
                i += sscanf(header, "%u %u", &height, &maxval);
            }
            else if (i == 2) 
            {
                i += sscanf(header, "%u", &maxval);
            }
        #endif
    }

    // check if given handle for the data is initialized
    if(NULL != *data) 
    {
        if (*w != width || *h != height) 
        {
            fclose(fp);
            std::cerr << "loadPPM() : Invalid image dimensions." << std::endl;
            return shrFALSE;
        }
    } 
    else 
    {
        *data = (unsigned char*)malloc( sizeof(unsigned char) * width * height * *channels);
        *w = width;
        *h = height;
    }

    // read and close file
    if (fread(*data, sizeof(unsigned char), width * height * *channels, fp) != width * height * *channels)
    {
        fclose(fp);
        std::cerr << "loadPPM() : Invalid image." << std::endl;
        return shrFALSE;
    }
    fclose(fp);

    return shrTRUE;
}

//////////////////////////////////////////////////////////////////////////////
//! Write / Save PPM or PGM file
//! @note Internal usage only
//! @param file  name of the image file
//! @param data  handle to the data read
//! @param w     width of the image
//! @param h     height of the image
//////////////////////////////////////////////////////////////////////////////  
shrBOOL savePPM( const char* file, unsigned char *data, 
             unsigned int w, unsigned int h, unsigned int channels) 
{
    ARGCHECK(NULL != data);
    ARGCHECK(w > 0);
    ARGCHECK(h > 0);

    std::fstream fh( file, std::fstream::out | std::fstream::binary );
    if( fh.bad()) 
    {
        std::cerr << "savePPM() : Opening file failed." << std::endl;
        return shrFALSE;
    }

    if (channels == 1)
    {
        fh << "P5\n";
    }
    else if (channels == 3) {
        fh << "P6\n";
    }
    else {
        std::cerr << "savePPM() : Invalid number of channels." << std::endl;
        return shrFALSE;
    }

    fh << w << "\n" << h << "\n" << 0xff << std::endl;

    for( unsigned int i = 0; (i < (w*h*channels)) && fh.good(); ++i) 
    {
        fh << data[i];
    }
    fh.flush();

    if( fh.bad()) 
    {
        std::cerr << "savePPM() : Writing data failed." << std::endl;
        return shrFALSE;
    } 
    fh.close();

    return shrTRUE;
}

////////////////////////////////////////////////////////////////////////////////
//! Load PPM image file (with unsigned char as data element type), padding 4th component
//! @return shrTrue if reading the file succeeded, otherwise shrFALSE
//! @param file  name of the image file
//! @param data  handle to the data read
//! @param w     width of the image
//! @param h     height of the image
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrLoadPPM4ub( const char* file, unsigned char** OutData, 
                unsigned int *w, unsigned int *h)
{
    // Load file data into a temporary buffer with automatic allocation
    unsigned char* cLocalData = 0;
    unsigned int channels;
    shrBOOL bLoadOK = loadPPM(file, &cLocalData, w, h, &channels);   // this allocates cLocalData, which must be freed later

    // If the data loaded OK from file to temporary buffer, then go ahead with padding and transfer 
    if (shrTRUE == bLoadOK)
    {
        // if the receiving buffer is null, allocate it... caller must free this 
        int size = *w * *h;
        if (*OutData == NULL)
        {
            *OutData = (unsigned char*)malloc(sizeof(unsigned char) * size * 4);
        }

        // temp pointers for incrementing
        unsigned char* cTemp = cLocalData;
        unsigned char* cOutPtr = *OutData;

        // transfer data, padding 4th element
        for(int i=0; i<size; i++) 
        {
            *cOutPtr++ = *cTemp++;
            *cOutPtr++ = *cTemp++;
            *cOutPtr++ = *cTemp++;
            *cOutPtr++ = 0;
        }

        // free temp lcoal buffer and return OK
        free(cLocalData);
        return shrTRUE;
    }
    else
    {
        // image wouldn't load
        free(cLocalData);
        return shrFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
//! Save PPM image file (with unsigned char as data element type, padded to 4 byte)
//! @return shrTrue if reading the file succeeded, otherwise shrFALSE
//! @param file  name of the image file
//! @param data  handle to the data read
//! @param w     width of the image
//! @param h     height of the image
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrSavePPM4ub( const char* file, unsigned char *data, 
               unsigned int w, unsigned int h) 
{
    // strip 4th component
    int size = w * h;
    unsigned char *ndata = (unsigned char*) malloc( sizeof(unsigned char) * size*3);
    unsigned char *ptr = ndata;
    for(int i=0; i<size; i++) {
        *ptr++ = *data++;
        *ptr++ = *data++;
        *ptr++ = *data++;
        data++;
    }
    
    shrBOOL succ = savePPM(file, ndata, w, h, 3);
    free(ndata);
    return succ;
}

////////////////////////////////////////////////////////////////////////////////
//! Save PGM image file (with unsigned char as data element type)
//! @param file  name of the image file
//! @param data  handle to the data read
//! @param w     width of the image
//! @param h     height of the image
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrSavePGMub( const char* file, unsigned char *data, 
              unsigned int w, unsigned int h) 
{
    return savePPM( file, data, w, h, 1);
}

////////////////////////////////////////////////////////////////////////////////
//! Load PGM image file (with unsigned char as data element type)
//! @return shrTRUE if reading the file succeeded, otherwise shrFALSE
//! @param file  name of the image file
//! @param data  handle to the data read
//! @param w     width of the image
//! @param h     height of the image
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrLoadPGMub( const char* file, unsigned char** data, 
              unsigned int *w,unsigned int *h)
{
    unsigned int channels;
    return loadPPM( file, data, w, h, &channels);
}

////////////////////////////////////////////////////////////////////////////// 
//! Compare two arrays of arbitrary type       
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
//! @param epsilon    epsilon to use for the comparison
//////////////////////////////////////////////////////////////////////////////
template<class T, class S>
shrBOOL  
compareData( const T* reference, const T* data, const unsigned int len, 
             const S epsilon, const float threshold) 
{
    ARGCHECK( epsilon >= 0);

    bool result = true;
    unsigned int error_count = 0;

    for( unsigned int i = 0; i < len; ++i) {

        T diff = reference[i] - data[i];
        bool comp = (diff <= epsilon) && (diff >= -epsilon);
        result &= comp;

        error_count += !comp;

#ifdef _DEBUG
        if( ! comp) 
        {
            std::cerr << "ERROR, i = " << i << ",\t " 
                << reference[i] << " / "
                << data[i] 
                << " (reference / data)\n";
        }
#endif
    }

    if (threshold == 0.0f) {
        return (result) ? shrTRUE : shrFALSE;
    } else {
        return (len*threshold > error_count) ? shrTRUE : shrFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////// 
//! Compare two arrays of arbitrary type       
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
//! @param epsilon    epsilon to use for the comparison
//////////////////////////////////////////////////////////////////////////////
template<class T, class S>
shrBOOL  
compareDataAsFloat( const T* reference, const T* data, const unsigned int len, 
                    const S epsilon) 
{
    ARGCHECK(epsilon >= 0);

    // If we set epsilon to be 0, let's set a minimum threshold
    float max_error = MAX( (float)epsilon, MIN_EPSILON_ERROR );
    int error_count = 0;
    bool result = true;

    for( unsigned int i = 0; i < len; ++i) {
        float diff = fabs((float)reference[i] - (float)data[i]);
        bool comp = (diff < max_error);
        result &= comp;

        if( ! comp) 
        {
            error_count++;
#ifdef _DEBUG
			if (error_count < 50) {
                shrLog("\n    ERROR(epsilon=%4.3f), i=%d, (ref)0x%02x / (data)0x%02x / (diff)%d\n", max_error, i, reference[i], data[i], (unsigned int)diff);
			}
#endif
        }
    }
    if (error_count) {
        shrLog("\n    Total # of errors = %d\n", error_count);
    }
    return (error_count == 0) ? shrTRUE : shrFALSE;
}

////////////////////////////////////////////////////////////////////////////// 
//! Compare two arrays of arbitrary type       
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
//! @param epsilon    epsilon to use for the comparison
//! @param epsilon    threshold % of (# of bytes) for pass/fail
//////////////////////////////////////////////////////////////////////////////
template<class T, class S>
shrBOOL  
compareDataAsFloatThreshold( const T* reference, const T* data, const unsigned int len, 
                    const S epsilon, const float threshold) 
{
    ARGCHECK(epsilon >= 0);

    // If we set epsilon to be 0, let's set a minimum threshold
    float max_error = MAX( (float)epsilon, MIN_EPSILON_ERROR);
    int error_count = 0;
    bool result = true;

    for( unsigned int i = 0; i < len; ++i) {
        float diff = fabs((float)reference[i] - (float)data[i]);
        bool comp = (diff < max_error);
        result &= comp;

        if( ! comp) 
        {
            error_count++;
#ifdef _DEBUG
			if (error_count < 50) {
                shrLog("\n    ERROR(epsilon=%4.3f), i=%d, (ref)0x%02x / (data)0x%02x / (diff)%d\n", max_error, i, reference[i], data[i], (unsigned int)diff);
			}
#endif
        }
    }

    if (threshold == 0.0f) {
        if (error_count) {
            shrLog("\n    Total # of errors = %d\n", error_count);
        }
        return (error_count == 0) ? shrTRUE : shrFALSE;
    } else {

        if (error_count) {
            shrLog("\n    %.2f(%%) of bytes mismatched (count=%d)\n", (float)error_count*100/(float)len, error_count);
        }

        return ((len*threshold > error_count) ? shrTRUE : shrFALSE);
    }
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two float arrays
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrComparef( const float* reference, const float* data,
            const unsigned int len ) 
{
    const float epsilon = 0.0;
    return compareData( reference, data, len, epsilon, 0.0f );
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two integer arrays
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrComparei( const int* reference, const int* data,
            const unsigned int len ) 
{
    const int epsilon = 0;
    return compareData( reference, data, len, epsilon, 0.0f);
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two unsigned integer arrays, with epsilon and threshold
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrCompareuit( const unsigned int* reference, const unsigned int* data,
            const unsigned int len, const float epsilon, const float threshold )
{
    return compareDataAsFloatThreshold( reference, data, len, epsilon, threshold );
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two integer arrays
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrCompareub( const unsigned char* reference, const unsigned char* data,
             const unsigned int len ) 
{
    const int epsilon = 0;
    return compareData( reference, data, len, epsilon, 0.0f);
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two integer arrays (inc Threshold for # of pixel we can have errors)
//! @return  shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrCompareubt( const unsigned char* reference, const unsigned char* data,
             const unsigned int len, const float epsilon, const float threshold ) 
{
    return compareDataAsFloatThreshold( reference, data, len, epsilon, threshold );
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two integer arrays
//! @return  shrTRUE if \a reference and \a data are identical, 
//!          otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrCompareube( const unsigned char* reference, const unsigned char* data,
             const unsigned int len, const float epsilon ) 
{
    return compareDataAsFloat( reference, data, len, epsilon );
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two float arrays with an epsilon tolerance for equality
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
//! @param epsilon    epsilon to use for the comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrComparefe( const float* reference, const float* data,
             const unsigned int len, const float epsilon ) 
{
    return compareData( reference, data, len, epsilon, 0.0f);
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two float arrays with an epsilon tolerance for equality and a 
//!     threshold for # pixel errors
//! @return  shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
//! @param epsilon    epsilon to use for the comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrComparefet( const float* reference, const float* data,
             const unsigned int len, const float epsilon, const float threshold ) 
{
    return compareDataAsFloatThreshold( reference, data, len, epsilon, threshold );
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two float arrays using L2-norm with an epsilon tolerance for equality
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param reference  handle to the reference data / gold image
//! @param data       handle to the computed data
//! @param len        number of elements in reference and data
//! @param epsilon    epsilon to use for the comparison
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrCompareL2fe( const float* reference, const float* data,
                const unsigned int len, const float epsilon ) 
{
    ARGCHECK(epsilon >= 0);

    float error = 0;
    float ref = 0;

    for( unsigned int i = 0; i < len; ++i) {

        float diff = reference[i] - data[i];
        error += diff * diff;
        ref += reference[i] * reference[i];
    }

    float normRef = sqrtf(ref);
    if (fabs(ref) < 1e-7) {
#ifdef _DEBUG
        std::cerr << "ERROR, reference l2-norm is 0\n";
#endif
        return shrFALSE;
    }
    float normError = sqrtf(error);
    error = normError / normRef;
    bool result = error < epsilon;
#ifdef _DEBUG
    if( ! result) 
    {
        std::cerr << "ERROR, l2-norm error " 
            << error << " is greater than epsilon " << epsilon << "\n";
    }
#endif

    return result ? shrTRUE : shrFALSE;
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two PPM image files with an epsilon tolerance for equality
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param src_file   filename for the image to be compared
//! @param data       filename for the reference data / gold image
//! @param epsilon    epsilon to use for the comparison
//! @param threshold  threshold of pixels that can still mismatch to pass (i.e. 0.15f = 15% must pass)
//! @param verboseErrors output details of image mismatch to std::cerr
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrComparePPM( const char *src_file, const char *ref_file, const float epsilon, const float threshold)
{
	unsigned char* src_data = NULL;
    unsigned char* ref_data = NULL;
	unsigned long error_count = 0;
	unsigned int ref_width, ref_height;
	unsigned int src_width, src_height;

    // Check sample and reference file pointers
	if (src_file == NULL || ref_file == NULL) {
		shrLog("\n> shrComparePGM: src_file or ref_file is NULL\n  Aborting comparison !!!\n\n");
		return shrFALSE;
	}
    shrLog("\n> shrComparePPM:\n    (a)rendered:  <%s>\n    (b)reference: <%s>\n", src_file, ref_file);

    // Load the ref image file
	if (shrLoadPPM4ub(ref_file, &ref_data, &ref_width, &ref_height) != shrTRUE) 
	{
		shrLog("\n    Unable to load ref image file: %s\n    Aborting comparison !!!\n\n", ref_file);
		return shrFALSE;
	}

    // Load the sample image file
	if (shrLoadPPM4ub(src_file, &src_data, &src_width, &src_height) != shrTRUE) 
	{
		shrLog("\n    Unable to load src image file: %s\n    Aborting comparison !!!\n\n", src_file);
		return shrFALSE;
	}

    // check to see if image dimensions match
	if(src_height != ref_height || src_width != ref_width)
	{
		shrLog("\n    Source and ref size mismatch (%u x %u) vs (%u x %u)\n    Aborting Comparison !!!\n\n ", 
		    src_width, src_height, ref_width, ref_height);
		return shrFALSE;
	}

    // compare the images
	if (shrCompareubt(ref_data, src_data, src_width*src_height*4, epsilon, threshold ) == shrFALSE) 
	{
		error_count=1;
	}

    shrLog("    Images %s\n\n", (error_count == 0) ? "Match" : "Don't Match !!!"); 
	return (error_count == 0) ? shrTRUE : shrFALSE;  // returns true if all pixels pass
}

////////////////////////////////////////////////////////////////////////////////
//! Compare two PGM image files with an epsilon tolerance for equality
//! @return shrTRUE if \a reference and \a data are identical, otherwise shrFALSE
//! @param src_file   filename for the image to be compared
//! @param data       filename for the reference data / gold image
//! @param epsilon    epsilon to use for the comparison
//! @param threshold  threshold of pixels that can still mismatch to pass (i.e. 0.15f = 15% must pass)
////////////////////////////////////////////////////////////////////////////////
shrBOOL shrComparePGM( const char *src_file, const char *ref_file, const float epsilon, const float threshold)
{
	unsigned char* src_data = NULL;
    unsigned char* ref_data = NULL;
	unsigned long error_count = 0;
	unsigned int ref_width, ref_height;
	unsigned int src_width, src_height;

    // Check sample and reference file pointers
	if (src_file == NULL || ref_file == NULL) {
		shrLog("\n> shrComparePGM: src_file or ref_file is NULL\n  Aborting comparison !!!\n\n");
		return shrFALSE;
	}
    shrLog("\n> shrComparePGM:\n    (a)rendered:  <%s>\n    (b)reference: <%s>\n", src_file, ref_file);

    // Load the ref image file
	if (shrLoadPPM4ub(ref_file, &ref_data, &ref_width, &ref_height) != shrTRUE) 
	{
		shrLog("\n    Unable to load ref image file: %s\n    Aborting comparison !!!\n\n", ref_file);
		return shrFALSE;
	}

    // Load the sample image file
	if (shrLoadPPM4ub(src_file, &src_data, &src_width, &src_height) != shrTRUE) 
	{
		shrLog("\n    Unable to load src image file: %s\n    Aborting comparison !!!\n\n", src_file);
		return shrFALSE;
	}

    // check to see if image dimensions match
	if(src_height != ref_height || src_width != ref_width)
	{
		shrLog("\n    Source and ref size mismatch (%u x %u) vs (%u x %u)\n    Aborting Comparison !!!\n\n ", 
		    src_width, src_height, ref_width, ref_height);
		return shrFALSE;
	}

    // compare the images
	if (shrCompareubt(ref_data, src_data, src_width*src_height*4, epsilon, threshold ) == shrFALSE) 
	{
		error_count=1;
	}

    shrLog("    Images %s\n\n", (error_count == 0) ? "Match" : "Don't Match !!!"); 
	return (error_count == 0) ? shrTRUE : shrFALSE;  // returns true if all pixels pass
}

// Load raw data from disk
unsigned char* shrLoadRawFile(const char* filename, size_t size)
{
    FILE *fp = NULL;
    #ifdef WIN32
        errno_t err;
        if ((err = fopen_s(&fp, filename, "rb")) != 0)
    #else
        if ((fp = fopen(filename, "rb")) == NULL) 
    #endif
        {
            shrLog(" Error opening file '%s' !!!\n", filename);
            return 0;
        }

    unsigned char* data = (unsigned char*)malloc(size);
    size_t read = fread(data, 1, size, fp);
    fclose(fp);

    shrLog(" Read '%s', %d bytes\n", filename, read);

    return data;
}

// Round Up Division function
size_t shrRoundUp(int group_size, int global_size) 
{
    int r = global_size % group_size;
    if(r == 0) 
    {
        return global_size;
    } else 
    {
        return global_size + group_size - r;
    }
}
