/* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of NVIDIA CORPORATION nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// These are helper functions for the SDK samples (image,bitmap)
#ifndef COMMON_HELPER_IMAGE_H_
#define COMMON_HELPER_IMAGE_H_

#include <assert.h>
#include <exception.h>
#include <math.h>
#include <stdint.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#ifndef MIN
#define MIN(a, b) ((a < b) ? a : b)
#endif
#ifndef MAX
#define MAX(a, b) ((a > b) ? a : b)
#endif

#ifndef EXIT_WAIVED
#define EXIT_WAIVED 2
#endif

#include <helper_string.h>

// namespace unnamed (internal)
namespace helper_image_internal {
//! size of PGM file header
const unsigned int PGMHeaderSize = 0x40;

// types

//! Data converter from unsigned char / unsigned byte to type T
template <class T>
struct ConverterFromUByte;

//! Data converter from unsigned char / unsigned byte
template <>
struct ConverterFromUByte<unsigned char> {
  //! Conversion operator
  //! @return converted value
  //! @param  val  value to convert
  float operator()(const unsigned char &val) {
    return static_cast<unsigned char>(val);
  }
};

//! Data converter from unsigned char / unsigned byte to float
template <>
struct ConverterFromUByte<float> {
  //! Conversion operator
  //! @return converted value
  //! @param  val  value to convert
  float operator()(const unsigned char &val) {
    return static_cast<float>(val) / 255.0f;
  }
};

//! Data converter from unsigned char / unsigned byte to type T
template <class T>
struct ConverterToUByte;

//! Data converter from unsigned char / unsigned byte to unsigned int
template <>
struct ConverterToUByte<unsigned char> {
  //! Conversion operator (essentially a passthru
  //! @return converted value
  //! @param  val  value to convert
  unsigned char operator()(const unsigned char &val) { return val; }
};

//! Data converter from unsigned char / unsigned byte to unsigned int
template <>
struct ConverterToUByte<float> {
  //! Conversion operator
  //! @return converted value
  //! @param  val  value to convert
  unsigned char operator()(const float &val) {
    return static_cast<unsigned char>(val * 255.0f);
  }
};
}  // namespace helper_image_internal

#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
#ifndef FOPEN
#define FOPEN(fHandle, filename, mode) fopen_s(&fHandle, filename, mode)
#endif
#ifndef FOPEN_FAIL
#define FOPEN_FAIL(result) (result != 0)
#endif
#ifndef SSCANF
#define SSCANF sscanf_s
#endif
#else
#ifndef FOPEN
#define FOPEN(fHandle, filename, mode) (fHandle = fopen(filename, mode))
#endif
#ifndef FOPEN_FAIL
#define FOPEN_FAIL(result) (result == NULL)
#endif
#ifndef SSCANF
#define SSCANF sscanf
#endif
#endif

inline bool __loadPPM(const char *file, unsigned char **data, unsigned int *w,
                      unsigned int *h, unsigned int *channels) {
  FILE *fp = NULL;

  if (FOPEN_FAIL(FOPEN(fp, file, "rb"))) {
    std::cerr << "__LoadPPM() : Failed to open file: " << file << std::endl;
    return false;
  }

  // check header
  char header[helper_image_internal::PGMHeaderSize];

  if (fgets(header, helper_image_internal::PGMHeaderSize, fp) == NULL) {
    std::cerr << "__LoadPPM() : reading PGM header returned NULL" << std::endl;
    return false;
  }

  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 false;
  }

  // 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, helper_image_internal::PGMHeaderSize, fp) == NULL) {
      std::cerr << "__LoadPPM() : reading PGM header returned NULL"
                << std::endl;
      return false;
    }

    if (header[0] == '#') {
      continue;
    }

    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);
    }
  }

  // check if given handle for the data is initialized
  if (NULL != *data) {
    if (*w != width || *h != height) {
      std::cerr << "__LoadPPM() : Invalid image dimensions." << std::endl;
    }
  } 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) ==
      0) {
    std::cerr << "__LoadPPM() read data returned error." << std::endl;
  }

  fclose(fp);

  return true;
}

template <class T>
inline bool sdkLoadPGM(const char *file, T **data, unsigned int *w,
                       unsigned int *h) {
  unsigned char *idata = NULL;
  unsigned int channels;

  if (true != __loadPPM(file, &idata, w, h, &channels)) {
    return false;
  }

  unsigned int size = *w * *h * channels;

  // initialize mem if necessary
  // the correct size is checked / set in loadPGMc()
  if (NULL == *data) {
    *data = reinterpret_cast<T *>(malloc(sizeof(T) * size));
  }

  // copy and cast data
  std::transform(idata, idata + size, *data,
                 helper_image_internal::ConverterFromUByte<T>());

  free(idata);

  return true;
}

template <class T>
inline bool sdkLoadPPM4(const char *file, T **data, unsigned int *w,
                        unsigned int *h) {
  unsigned char *idata = 0;
  unsigned int channels;

  if (__loadPPM(file, &idata, w, h, &channels)) {
    // pad 4th component
    int size = *w * *h;
    // keep the original pointer
    unsigned char *idata_orig = idata;
    *data = reinterpret_cast<T *>(malloc(sizeof(T) * size * 4));
    unsigned char *ptr = *data;

    for (int i = 0; i < size; i++) {
      *ptr++ = *idata++;
      *ptr++ = *idata++;
      *ptr++ = *idata++;
      *ptr++ = 0;
    }

    free(idata_orig);
    return true;
  } else {
    free(idata);
    return false;
  }
}

inline bool __savePPM(const char *file, unsigned char *data, unsigned int w,
                      unsigned int h, unsigned int channels) {
  assert(NULL != data);
  assert(w > 0);
  assert(h > 0);

  std::fstream fh(file, std::fstream::out | std::fstream::binary);

  if (fh.bad()) {
    std::cerr << "__savePPM() : Opening file failed." << std::endl;
    return false;
  }

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

  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 false;
  }

  fh.close();

  return true;
}

template <class T>
inline bool sdkSavePGM(const char *file, T *data, unsigned int w,
                       unsigned int h) {
  unsigned int size = w * h;
  unsigned char *idata = (unsigned char *)malloc(sizeof(unsigned char) * size);

  std::transform(data, data + size, idata,
                 helper_image_internal::ConverterToUByte<T>());

  // write file
  bool result = __savePPM(file, idata, w, h, 1);

  // cleanup
  free(idata);

  return result;
}

inline bool sdkSavePPM4ub(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++;
  }

  bool result = __savePPM(file, ndata, w, h, 3);
  free(ndata);
  return result;
}

//////////////////////////////////////////////////////////////////////////////
//! Read file \filename and return the data
//! @return bool if reading the file succeeded, otherwise false
//! @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>
inline bool sdkReadFile(const char *filename, T **data, unsigned int *len,
                        bool verbose) {
  // check input arguments
  assert(NULL != filename);
  assert(NULL != len);

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

  // open file for reading
  FILE *fh = NULL;

  // check if filestream is valid
  if (FOPEN_FAIL(FOPEN(fh, filename, "r"))) {
    printf("Unable to open input file: %s\n", filename);
    return false;
  }

  // read all data elements
  T token;

  while (!feof(fh)) {
    fscanf(fh, "%f", &token);
    data_read.push_back(token);
  }

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

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

      return false;
    }
  } else {
    // allocate storage for the data read
    *data = reinterpret_cast<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 true;
}

//////////////////////////////////////////////////////////////////////////////
//! Read file \filename and return the data
//! @return bool if reading the file succeeded, otherwise false
//! @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>
inline bool sdkReadFileBlocks(const char *filename, T **data, unsigned int *len,
                              unsigned int block_num, unsigned int block_size,
                              bool verbose) {
  // check input arguments
  assert(NULL != filename);
  assert(NULL != len);

  // open file for reading
  FILE *fh = fopen(filename, "rb");

  if (fh == NULL && verbose) {
    std::cerr << "sdkReadFile() : Opening file failed." << std::endl;
    return false;
  }

  // check if the given handle is already initialized
  // allocate storage for the data read
  data[block_num] = reinterpret_cast<T *>(malloc(block_size));

  // read all data elements
  fseek(fh, block_num * block_size, SEEK_SET);
  *len = fread(data[block_num], sizeof(T), block_size / sizeof(T), fh);

  fclose(fh);

  return true;
}

//////////////////////////////////////////////////////////////////////////////
//! Write a data file \filename
//! @return true if writing the file succeeded, otherwise false
//! @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, class S>
inline bool sdkWriteFile(const char *filename, const T *data, unsigned int len,
                         const S epsilon, bool verbose, bool append = false) {
  assert(NULL != filename);
  assert(NULL != data);

  // open file for writing
  //    if (append) {
  std::fstream fh(filename, std::fstream::out | std::fstream::ate);

  if (verbose) {
    std::cerr << "sdkWriteFile() : Open file " << filename
              << " for write/append." << std::endl;
  }

  /*    } else {
          std::fstream fh(filename, std::fstream::out);
          if (verbose) {
              std::cerr << "sdkWriteFile() : Open file " << filename << " for
     write." << std::endl;
          }
      }
  */

  // check if filestream is valid
  if (!fh.good()) {
    if (verbose) {
      std::cerr << "sdkWriteFile() : Opening file failed." << std::endl;
    }

    return false;
  }

  // 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 << "sdkWriteFile() : Writing file failed." << std::endl;
    }

    return false;
  }

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

  return true;
}

//////////////////////////////////////////////////////////////////////////////
//! Compare two arrays of arbitrary type
//! @return  true if \a reference and \a data are identical, otherwise false
//! @param reference  timer_interface 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>
inline bool compareData(const T *reference, const T *data,
                        const unsigned int len, const S epsilon,
                        const float threshold) {
  assert(epsilon >= 0);

  bool result = true;
  unsigned int error_count = 0;

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

    error_count += !comp;

#if 0

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

#endif
  }

  if (threshold == 0.0f) {
    return (result) ? true : false;
  } else {
    if (error_count) {
      printf("%4.2f(%%) of bytes mismatched (count=%d)\n",
             static_cast<float>(error_count) * 100 / static_cast<float>(len),
             error_count);
    }

    return (len * threshold > error_count) ? true : false;
  }
}

#ifndef __MIN_EPSILON_ERROR
#define __MIN_EPSILON_ERROR 1e-3f
#endif

//////////////////////////////////////////////////////////////////////////////
//! Compare two arrays of arbitrary type
//! @return  true if \a reference and \a data are identical, otherwise false
//! @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>
inline bool compareDataAsFloatThreshold(const T *reference, const T *data,
                                        const unsigned int len, const S epsilon,
                                        const float threshold) {
  assert(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(static_cast<float>(reference[i]) - static_cast<float>(data[i]));
    bool comp = (diff < max_error);
    result &= comp;

    if (!comp) {
      error_count++;
    }
  }

  if (threshold == 0.0f) {
    if (error_count) {
      printf("total # of errors = %d\n", error_count);
    }

    return (error_count == 0) ? true : false;
  } else {
    if (error_count) {
      printf("%4.2f(%%) of bytes mismatched (count=%d)\n",
             static_cast<float>(error_count) * 100 / static_cast<float>(len),
             error_count);
    }

    return ((len * threshold > error_count) ? true : false);
  }
}

inline void sdkDumpBin(void *data, unsigned int bytes, const char *filename) {
  printf("sdkDumpBin: <%s>\n", filename);
  FILE *fp;
  FOPEN(fp, filename, "wb");
  fwrite(data, bytes, 1, fp);
  fflush(fp);
  fclose(fp);
}

inline bool sdkCompareBin2BinUint(const char *src_file, const char *ref_file,
                                  unsigned int nelements, const float epsilon,
                                  const float threshold, char *exec_path) {
  unsigned int *src_buffer, *ref_buffer;
  FILE *src_fp = NULL, *ref_fp = NULL;

  uint64_t error_count = 0;
  size_t fsize = 0;

  if (FOPEN_FAIL(FOPEN(src_fp, src_file, "rb"))) {
    printf("compareBin2Bin <unsigned int> unable to open src_file: %s\n",
           src_file);
    error_count++;
  }

  char *ref_file_path = sdkFindFilePath(ref_file, exec_path);

  if (ref_file_path == NULL) {
    printf("compareBin2Bin <unsigned int>  unable to find <%s> in <%s>\n",
           ref_file, exec_path);
    printf(">>> Check info.xml and [project//data] folder <%s> <<<\n",
           ref_file);
    printf("Aborting comparison!\n");
    printf("  FAILED\n");
    error_count++;

    if (src_fp) {
      fclose(src_fp);
    }

    if (ref_fp) {
      fclose(ref_fp);
    }
  } else {
    if (FOPEN_FAIL(FOPEN(ref_fp, ref_file_path, "rb"))) {
      printf(
          "compareBin2Bin <unsigned int>"
          " unable to open ref_file: %s\n",
          ref_file_path);
      error_count++;
    }

    if (src_fp && ref_fp) {
      src_buffer = (unsigned int *)malloc(nelements * sizeof(unsigned int));
      ref_buffer = (unsigned int *)malloc(nelements * sizeof(unsigned int));

      fsize = fread(src_buffer, nelements, sizeof(unsigned int), src_fp);
      fsize = fread(ref_buffer, nelements, sizeof(unsigned int), ref_fp);

      printf(
          "> compareBin2Bin <unsigned int> nelements=%d,"
          " epsilon=%4.2f, threshold=%4.2f\n",
          nelements, epsilon, threshold);
      printf("   src_file <%s>, size=%d bytes\n", src_file,
             static_cast<int>(fsize));
      printf("   ref_file <%s>, size=%d bytes\n", ref_file_path,
             static_cast<int>(fsize));

      if (!compareData<unsigned int, float>(ref_buffer, src_buffer, nelements,
                                            epsilon, threshold)) {
        error_count++;
      }

      fclose(src_fp);
      fclose(ref_fp);

      free(src_buffer);
      free(ref_buffer);
    } else {
      if (src_fp) {
        fclose(src_fp);
      }

      if (ref_fp) {
        fclose(ref_fp);
      }
    }
  }

  if (error_count == 0) {
    printf("  OK\n");
  } else {
    printf("  FAILURE: %d errors...\n", (unsigned int)error_count);
  }

  return (error_count == 0);  // returns true if all pixels pass
}

inline bool sdkCompareBin2BinFloat(const char *src_file, const char *ref_file,
                                   unsigned int nelements, const float epsilon,
                                   const float threshold, char *exec_path) {
  float *src_buffer = NULL, *ref_buffer = NULL;
  FILE *src_fp = NULL, *ref_fp = NULL;
  size_t fsize = 0;

  uint64_t error_count = 0;

  if (FOPEN_FAIL(FOPEN(src_fp, src_file, "rb"))) {
    printf("compareBin2Bin <float> unable to open src_file: %s\n", src_file);
    error_count = 1;
  }

  char *ref_file_path = sdkFindFilePath(ref_file, exec_path);

  if (ref_file_path == NULL) {
    printf("compareBin2Bin <float> unable to find <%s> in <%s>\n", ref_file,
           exec_path);
    printf(">>> Check info.xml and [project//data] folder <%s> <<<\n",
           exec_path);
    printf("Aborting comparison!\n");
    printf("  FAILED\n");
    error_count++;

    if (src_fp) {
      fclose(src_fp);
    }

    if (ref_fp) {
      fclose(ref_fp);
    }
  } else {
    if (FOPEN_FAIL(FOPEN(ref_fp, ref_file_path, "rb"))) {
      printf("compareBin2Bin <float> unable to open ref_file: %s\n",
             ref_file_path);
      error_count = 1;
    }

    if (src_fp && ref_fp) {
      src_buffer = reinterpret_cast<float *>(malloc(nelements * sizeof(float)));
      ref_buffer = reinterpret_cast<float *>(malloc(nelements * sizeof(float)));

      printf(
          "> compareBin2Bin <float> nelements=%d, epsilon=%4.2f,"
          " threshold=%4.2f\n",
          nelements, epsilon, threshold);
      fsize = fread(src_buffer, sizeof(float), nelements, src_fp);
      printf("   src_file <%s>, size=%d bytes\n", src_file,
             static_cast<int>(fsize * sizeof(float)));
      fsize = fread(ref_buffer, sizeof(float), nelements, ref_fp);
      printf("   ref_file <%s>, size=%d bytes\n", ref_file_path,
             static_cast<int>(fsize * sizeof(float)));

      if (!compareDataAsFloatThreshold<float, float>(
              ref_buffer, src_buffer, nelements, epsilon, threshold)) {
        error_count++;
      }

      fclose(src_fp);
      fclose(ref_fp);

      free(src_buffer);
      free(ref_buffer);
    } else {
      if (src_fp) {
        fclose(src_fp);
      }

      if (ref_fp) {
        fclose(ref_fp);
      }
    }
  }

  if (error_count == 0) {
    printf("  OK\n");
  } else {
    printf("  FAILURE: %d errors...\n", (unsigned int)error_count);
  }

  return (error_count == 0);  // returns true if all pixels pass
}

inline bool sdkCompareL2fe(const float *reference, const float *data,
                           const unsigned int len, const float epsilon) {
  assert(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 false;
  }

  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;
}

inline bool sdkLoadPPMub(const char *file, unsigned char **data,
                         unsigned int *w, unsigned int *h) {
  unsigned int channels;
  return __loadPPM(file, data, w, h, &channels);
}

inline bool sdkLoadPPM4ub(const char *file, unsigned char **data,
                          unsigned int *w, unsigned int *h) {
  unsigned char *idata = 0;
  unsigned int channels;

  if (__loadPPM(file, &idata, w, h, &channels)) {
    // pad 4th component
    int size = *w * *h;
    // keep the original pointer
    unsigned char *idata_orig = idata;
    *data = (unsigned char *)malloc(sizeof(unsigned char) * size * 4);
    unsigned char *ptr = *data;

    for (int i = 0; i < size; i++) {
      *ptr++ = *idata++;
      *ptr++ = *idata++;
      *ptr++ = *idata++;
      *ptr++ = 0;
    }

    free(idata_orig);
    return true;
  } else {
    free(idata);
    return false;
  }
}

inline bool sdkComparePPM(const char *src_file, const char *ref_file,
                          const float epsilon, const float threshold,
                          bool verboseErrors) {
  unsigned char *src_data, *ref_data;
  uint64_t error_count = 0;
  unsigned int ref_width, ref_height;
  unsigned int src_width, src_height;

  if (src_file == NULL || ref_file == NULL) {
    if (verboseErrors) {
      std::cerr << "PPMvsPPM: src_file or ref_file is NULL."
                   "  Aborting comparison\n";
    }

    return false;
  }

  if (verboseErrors) {
    std::cerr << "> Compare (a)rendered:  <" << src_file << ">\n";
    std::cerr << ">         (b)reference: <" << ref_file << ">\n";
  }

  if (sdkLoadPPM4ub(ref_file, &ref_data, &ref_width, &ref_height) != true) {
    if (verboseErrors) {
      std::cerr << "PPMvsPPM: unable to load ref image file: " << ref_file
                << "\n";
    }

    return false;
  }

  if (sdkLoadPPM4ub(src_file, &src_data, &src_width, &src_height) != true) {
    std::cerr << "PPMvsPPM: unable to load src image file: " << src_file
              << "\n";
    return false;
  }

  if (src_height != ref_height || src_width != ref_width) {
    if (verboseErrors) {
      std::cerr << "PPMvsPPM: source and ref size mismatch (" << src_width
                << "," << src_height << ")vs(" << ref_width << "," << ref_height
                << ")\n";
    }
  }

  if (verboseErrors) {
    std::cerr << "PPMvsPPM: comparing images size (" << src_width << ","
              << src_height << ") epsilon(" << epsilon << "), threshold("
              << threshold * 100 << "%)\n";
  }

  if (compareData(ref_data, src_data, src_width * src_height * 4, epsilon,
                  threshold) == false) {
    error_count = 1;
  }

  if (error_count == 0) {
    if (verboseErrors) {
      std::cerr << "    OK\n\n";
    }
  } else {
    if (verboseErrors) {
      std::cerr << "    FAILURE!  " << error_count << " errors...\n\n";
    }
  }

  // returns true if all pixels pass
  return (error_count == 0) ? true : false;
}

inline bool sdkComparePGM(const char *src_file, const char *ref_file,
                          const float epsilon, const float threshold,
                          bool verboseErrors) {
  unsigned char *src_data = 0, *ref_data = 0;
  uint64_t error_count = 0;
  unsigned int ref_width, ref_height;
  unsigned int src_width, src_height;

  if (src_file == NULL || ref_file == NULL) {
    if (verboseErrors) {
      std::cerr << "PGMvsPGM: src_file or ref_file is NULL."
                   "  Aborting comparison\n";
    }

    return false;
  }

  if (verboseErrors) {
    std::cerr << "> Compare (a)rendered:  <" << src_file << ">\n";
    std::cerr << ">         (b)reference: <" << ref_file << ">\n";
  }

  if (sdkLoadPPMub(ref_file, &ref_data, &ref_width, &ref_height) != true) {
    if (verboseErrors) {
      std::cerr << "PGMvsPGM: unable to load ref image file: " << ref_file
                << "\n";
    }

    return false;
  }

  if (sdkLoadPPMub(src_file, &src_data, &src_width, &src_height) != true) {
    std::cerr << "PGMvsPGM: unable to load src image file: " << src_file
              << "\n";
    return false;
  }

  if (src_height != ref_height || src_width != ref_width) {
    if (verboseErrors) {
      std::cerr << "PGMvsPGM: source and ref size mismatch (" << src_width
                << "," << src_height << ")vs(" << ref_width << "," << ref_height
                << ")\n";
    }
  }

  if (verboseErrors)
    std::cerr << "PGMvsPGM: comparing images size (" << src_width << ","
              << src_height << ") epsilon(" << epsilon << "), threshold("
              << threshold * 100 << "%)\n";

  if (compareData(ref_data, src_data, src_width * src_height, epsilon,
                  threshold) == false) {
    error_count = 1;
  }

  if (error_count == 0) {
    if (verboseErrors) {
      std::cerr << "    OK\n\n";
    }
  } else {
    if (verboseErrors) {
      std::cerr << "    FAILURE!  " << error_count << " errors...\n\n";
    }
  }

  // returns true if all pixels pass
  return (error_count == 0) ? true : false;
}

#endif  // COMMON_HELPER_IMAGE_H_
