/*********************************************************************
 * E.S.O. - VLT project
 *
 * "@(#) $Id: kmclipm_test_functions.c,v 1.30 2013-10-08 14:55:01 erw Exp $"
 *
 * Tests for functions in src/kmclipm_functions.c/.h
 *
 * who       when        what
 * --------  ----------  ----------------------------------------------
 * aagudo     2008-06-16  created
 */

/**
    @defgroup kmclipm_test_functions Unit tests for the functions in kmclipm_functions

    Unit test for the functions in the module @ref kmclipm_functions

    @{
*/

/*------------------------------------------------------------------------------
 *              Includes
 *----------------------------------------------------------------------------*/
#define _ISOC99_SOURCE

#include <float.h>
#include <math.h>
#include <sys/wait.h>
#include <sys/stat.h>

#ifdef __USE_XOPEN2K
/*#include <stdlib.h>*/
#include "stdlib.h"
#define GGG
#else
#define __USE_XOPEN2K /* to get the definition for setenv in stdlib.h */
/*#include <stdlib.h>*/
#include "stdlib.h"
#undef __USE_XOPEN2K
#endif

#include "kmclipm_constants.h"
#include "kmclipm_priv_constants.h"
#include "kmclipm_functions.h"
#include "kmclipm_priv_error.h"
#include "kmclipm_priv_functions.h"

cpl_msg_severity my_level= CPL_MSG_WARNING;

/*------------------------------------------------------------------------------
 *              Implementation
 *----------------------------------------------------------------------------*/

/**
    @brief
        Fills a vector with increasing values.

    @param   vec         The vector to fill with values.
    @param   seed        The starting value.
    @param   offset      The offset for the values.

 */
void kmclipm_test_fill_vector(cpl_vector *vec,
                              float seed,
                              float offset)
{
    int         i       = 0,
                size    = 0;
    double      *data   = NULL;

    size = cpl_vector_get_size(vec);
    cpl_test_eq(1, size > 0);

    cpl_test_nonnull(data = cpl_vector_get_data(vec));

    /* horizontal stripes: bottom = 0, top = seed * (y-1) */
    for (i = 0; i < size; i++) {
        data[i] = seed + i * offset;
    }

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief
        Fills an image with increasing values.

    @param   img         The image to fill with values.
    @param   seed        The starting value.
    @param   offset      The offset for the values.

 */
void kmclipm_test_fill_image(cpl_image *img,
                             float seed,
                             float offset)
{
    float       *data   = NULL,
                f       = 0.0;

    int         d       = 0;
#if KMCLIPM_GET_INSTALLED_CPL_VERSION >= 6
    cpl_size    x       = 0,
                y       = 0,
                i       = 0,
                j       = 0;
#else
    int         x       = 0,
                y       = 0,
                i       = 0,
                j       = 0;
#endif

    x = cpl_image_get_size_x(img);
    y = cpl_image_get_size_y(img);

    cpl_test_nonnull(data = cpl_image_get_data_float(img));

    /* horizontal stripes: bottom = 0, top = seed * (y-1) */
    for (i = 0; i < y; i++) {
        for (j = 0; j < x; j++) {
            data[d] = seed + f;
            d++;
            f += offset;
        }
    }

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief
        Fills a cube with increasing values.

    @param   cube        The cube to fill with values.
    @param   seed        The starting value.
    @param   offset      The offset for the values.

 */
void kmclipm_test_fill_cube(cpl_imagelist *cube,
                            float seed,
                            float offset)
{
    int         i       = 0,
                size    = 0;

    size = cpl_imagelist_get_size(cube);
    cpl_test_eq(1, size > 0);

    for (i = 0; i < size; i++) {
        kmclipm_test_fill_image(cpl_imagelist_get(cube, i), seed, offset);
        seed += offset;
    }

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief
        Allocates images in an empty imagelist.

    @param   cube        The cube to allocate.
    @param   x           Size in x-dimension.
    @param   y           Size in y-dimension.
    @param   z           Size in z-dimension.

 */
void kmclipm_test_alloc_cube(cpl_imagelist *cube,
                             int x,
                             int y,
                             int z)
{
    int         i       = 0;
    cpl_image   *img    = NULL;

    cpl_test_eq(0, cpl_imagelist_get_size(cube));

    for (i = 0; i < z; i++) {
        cpl_test_nonnull(img = cpl_image_new(x, y, CPL_TYPE_FLOAT));
        cpl_imagelist_set(cube, img, i);
        cpl_test_error(CPL_ERROR_NONE);
    }
}

/**
    @brief  Routine to test kmclipm_load_image_with_extensions()
 */
void test_kmclipm_load_image_with_extensions()
{
    float         *img_data = NULL;

    cpl_image     *img1      = NULL,
                  *img2      = NULL,
                  *img3      = NULL,
                  *img_ref  = NULL,
                  *ret_ptr  = NULL;

    int           i         = 0,
                  j         = 0,
                  k         = 0;

    char *path0 = cpl_sprintf("%s%s", ".", "/test_data/test_ext0.fits");
    char *path1 = cpl_sprintf("%s%s", ".", "/test_data/test_ext1.fits");
    char *path2 = cpl_sprintf("%s%s", ".", "/test_data/test_ext2.fits");
    char *path3 = cpl_sprintf("%s%s", ".", "/test_data/test_ext3.fits");

    /* --- empty image with no extensions */
    cpl_image_save(NULL, path0, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);

    /* --- image with only one extension */
    cpl_image_save(NULL, path1, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
    img1 = cpl_image_new(10, 5, CPL_TYPE_FLOAT);
    img_data = cpl_image_get_data_float(img1);
    for (i = 0; i< 10 * 5; i++) { img_data[i] = 1.0; }
    cpl_image_save(img1, path1, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_EXTEND);

    /* --- image with only two extensions */
    cpl_image_save(NULL, path2, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
    img2 = cpl_image_new(10, 5, CPL_TYPE_FLOAT);
    img_data = cpl_image_get_data_float(img2);
    for (i = 0; i< 10 * 5; i++) { img_data[i] = 2.0; }
    cpl_image_save(img1, path2, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_EXTEND);
    cpl_image_save(img2, path2, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_EXTEND);

    /* --- image with three extensions */
    cpl_image_save(NULL, path3, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
    img3 = cpl_image_new(10, 5, CPL_TYPE_FLOAT);
    img_data = cpl_image_get_data_float(img3);
    for (i = 0; i< 10 * 5; i++) { img_data[i] = 3.0; }
    cpl_image_save(img1, path3, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_EXTEND);
    cpl_image_save(img2, path3, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_EXTEND);
    cpl_image_save(img3, path3, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_EXTEND);

    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;
    cpl_image_delete(img3); img3 = NULL;

    /* ----- test with wrong values ----- */
    cpl_msg_set_level(CPL_MSG_OFF);
    /* --- empty path */
    cpl_test_null(ret_ptr = kmclipm_load_image_with_extensions(NULL));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- empty image with no extensions */
    cpl_test_null(ret_ptr = kmclipm_load_image_with_extensions(path0));
    if (((cpl_version_get_major() == 5) && (cpl_version_get_minor() >= 3)) ||
        (cpl_version_get_major() > 5))
    {
        cpl_test_error(CPL_ERROR_BAD_FILE_FORMAT);
    } else {
        cpl_test_error(CPL_ERROR_FILE_IO);
    }

    /* --- image with only one extension */
    cpl_test_null(ret_ptr = kmclipm_load_image_with_extensions(path1));
    if (((cpl_version_get_major() == 5) && (cpl_version_get_minor() >= 3)) ||
        (cpl_version_get_major() > 5))
    {
        cpl_test_error(CPL_ERROR_BAD_FILE_FORMAT);
    } else {
        cpl_test_error(CPL_ERROR_FILE_IO);
    }

    /* --- image with only two extensions */
    cpl_test_null(ret_ptr = kmclipm_load_image_with_extensions(path2));
    if (((cpl_version_get_major() == 5) && (cpl_version_get_minor() >= 3)) ||
        (cpl_version_get_major() > 5))
    {
        cpl_test_error(CPL_ERROR_BAD_FILE_FORMAT);
    } else {
        cpl_test_error(CPL_ERROR_FILE_IO);
    }

    cpl_msg_set_level(my_level);

    /* ----- test with correct values ----- */
    /* --- image with three extensions */
    cpl_test_nonnull(ret_ptr = kmclipm_load_image_with_extensions(path3));

    /* create reference image and compare it with output */
    img_ref = cpl_image_new(30, 5, CPL_TYPE_FLOAT);
    img_data = cpl_image_get_data_float(img_ref);
    for (k = 1; k <= 3; k++) {
        for (i = 1; i <= 10; i++) {
            for (j = 1; j <= 5; j++) {
                img_data[((k - 1) * 10 + i - 1) + (j - 1) * 30]
                          = (float)k;
            }
        }
    }

    /* Assert that images are equal */
    cpl_image_subtract(ret_ptr, img_ref);
    cpl_test_abs(cpl_image_get_max(ret_ptr), 0.0, 0.01);

    cpl_image_delete(img_ref);
    cpl_image_delete(ret_ptr);

    cpl_free(path0);
    cpl_free(path1);
    cpl_free(path2);
    cpl_free(path3);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_gaussfit_2d()
{
    cpl_image       *img    = NULL;

    double          fit[11],
                    err = 0.0001;

    /* ----- test with wrong values ----- */
    /* --- empty image, empty fit-parameters */
    kmclipm_gaussfit_2d(NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- empty image, empty fit-parameters */
    img = cpl_image_new(20,20, CPL_TYPE_FLOAT);
    kmclipm_gaussfit_2d(img, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* ----- test with correct values ----- */
    cpl_image_fill_gaussian(img, 6.5, 8.0, 1.0, 3.0, 3.0);

    cpl_test_error(kmclipm_gaussfit_2d(img, fit));

    cpl_test_abs(fit[0], 0.0176839, err);
    cpl_test_abs(fit[1], 6.5 - err, err);
    cpl_test_abs(fit[2], 8.0, err);
    cpl_test_abs(fit[3], 7.06446, err);
    cpl_test_abs(fit[4], -2.93566e-12, err);
    cpl_test_abs(fit[5], 2.76066e-11, err);
    cpl_test_abs(fit[6], 4.68791e-09, err);
    cpl_test_abs(fit[7], 4.64295e-09, err);
    cpl_test_abs(fit[8], 9.3853e-09, err);
    cpl_test_abs(fit[9], 7.09255e-12, err);

    cpl_image_delete(img); img = NULL;

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_reconstruct()
{
    cpl_image       *img    = NULL;
    float          *img_data = NULL;
    cpl_imagelist   *cube = NULL;
    cpl_imagelist   *refCube = NULL;
    int ix, iy, il;
    int p;
    int mx, dx;

    /* ----- test with correct values ----- */

    gridDefinition gd;
    gd.lamdaDistanceScale = 1.0;
    gd.x.start = -1300;
    gd.x.delta = 200;
    gd.x.dim = (1300 - gd.x.start) / gd.x.delta + 1;
    gd.y.start = -1300;
    gd.y.delta = 200;
    gd.y.dim = (1300 - gd.y.start) / gd.y.delta + 1;
    gd.l.start = 1.927;
    gd.l.delta = 0.0002649921875;
    gd.l.dim = (2.469704 - gd.l.start) / gd.l.delta + 1;
    gd.method = NEAREST_NEIGHBOR;
    gd.neighborHood.distance = 1.001;
    gd.neighborHood.scale = PIXEL;
    gd.neighborHood.type = N_CUBE;
    gd.rot_na_angle = 0.0;
    gd.rot_off_angle = 0.0;

    int imgSize = gd.x.dim * gd.y.dim * gd.l.dim;
    float *xcalArray = (float *) cpl_calloc(imgSize, sizeof(float));
    float *ycalArray = (float *) cpl_calloc(imgSize, sizeof(float));
    float *lcalArray = (float *) cpl_calloc(imgSize, sizeof(float));
    float *vcalArray = (float *) cpl_calloc(imgSize, sizeof(float));

    ix = setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);
    cpl_array *timestamp = NULL;
    cpl_vector *calAngles = NULL;

    refCube = cpl_imagelist_new();

    for (il=0; il<gd.l.dim; il++) {
       img = cpl_image_new(gd.x.dim, gd.y.dim, CPL_TYPE_FLOAT);
       img_data = cpl_image_get_data_float(img);
       for (iy=0; iy<gd.y.dim; iy++) {
        for (ix=0; ix<gd.x.dim; ix++) {
          p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
          xcalArray[p] =  gd.x.start + ix * gd.x.delta;
          ycalArray[p] =  gd.y.start + iy * gd.y.delta;
          lcalArray[p] =  gd.l.start + il * gd.l.delta;
          vcalArray[p] =  sqrt(xcalArray[p]*xcalArray[p] +
                               ycalArray[p]*ycalArray[p] +
                               1.e6 * lcalArray[p]*lcalArray[p]);
          img_data[ix + iy*gd.x.dim] = vcalArray[p];
       }
      }
      cpl_imagelist_set (refCube, img, il);
    }
    char *my_path = cpl_sprintf("%s/test_data/cube_reference.fits", ".");
    cpl_imagelist_save(refCube, my_path, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);
    cpl_free(my_path);
    for (il=0; il<gd.l.dim; il++) {
      for (iy=0; iy<gd.y.dim; iy++) {
        for (ix=0; ix<gd.x.dim; ix++) {
          int p = ix + iy * gd.x.dim + il * gd.x.dim * gd.y.dim;
          if ((10*il/gd.l.dim == 10*ix/gd.x.dim) ||
              (10*il/gd.l.dim == 10*iy/gd.y.dim))
          {
            xcalArray[p] =  0;
            ycalArray[p] =  0;
            lcalArray[p] =  0;
          }
        }
      }
    }

    cpl_image *xcal = cpl_image_wrap_float(gd.x.dim * gd.y.dim, gd.l.dim, xcalArray);
    cpl_image *ycal = cpl_image_wrap_float(gd.x.dim * gd.y.dim, gd.l.dim, ycalArray);
    cpl_image *lcal = cpl_image_wrap_float(gd.x.dim * gd.y.dim, gd.l.dim, lcalArray);
    cpl_image *vcal = cpl_image_wrap_float(gd.x.dim * gd.y.dim, gd.l.dim, vcalArray);
/*    printf("--> Memory dump before kmclipm_reconstruct\n");
    cpl_memory_dump();*/
    int method [3] = {NEAREST_NEIGHBOR, LINEAR_WEIGHTED_NEAREST_NEIGHBOR,
                      SQUARE_WEIGHTED_NEAREST_NEIGHBOR};
    for (mx=0; mx<3; mx++) {
        gd.method = method[mx];
        float distances [6] = {0.9, 0.999, 1.0, 1.001, 1.9, 2.1};
        for (dx=0; dx<6; dx++) {
            for (p=0; p<gd.x.dim*gd.y.dim*gd.l.dim; p++) {
                if (xcalArray[p] >= 0.0) {   /* mark ycal data (which is swapped with xcal */
                    xcalArray[p] += 0.1;     /* for IFU 1) as assigned for IFU 1           */
                    ycalArray[p] += 0.1;
                } else {
                    xcalArray[p] -= 0.1;
                    ycalArray[p] -= 0.1;
                }
            }
            gd.neighborHood.distance = distances[dx];
            cpl_imagelist *dummy_noise_cube = NULL;
            timestamp = kmclipm_reconstruct_nnlut_get_timestamp("", 1, gd);
            calAngles = cpl_vector_new(3);
            cube = kmclipm_reconstruct(1, vcal, NULL, xcal, ycal, lcal, gd, "",
                    FALSE, timestamp, calAngles, &dummy_noise_cube);
            cpl_array_delete(timestamp);
            cpl_vector_delete(calAngles);
            char * fileName = cpl_sprintf("%s/test_data/cube_nearestNeigbor_%1d_%5.4f.fits",
                                          ".", gd.method, gd.neighborHood.distance);
/*            printf("%s\n",fileName); */
            if (cube != NULL) {
                cpl_imagelist_save(cube, fileName, CPL_BPP_IEEE_FLOAT, NULL,
                                   CPL_IO_CREATE);
            }
            cpl_free(fileName); fileName = NULL;
            cpl_imagelist *diffCube = cpl_imagelist_duplicate (refCube);
            cpl_imagelist_subtract( diffCube, cube);
            fileName = cpl_sprintf("%s/test_data/cube_nearestNeigbor_%1d_%5.4f_diff.fits",
                                   ".", gd.method, gd.neighborHood.distance);
             if (diffCube != NULL) {
                 cpl_imagelist_save(diffCube, fileName, CPL_BPP_IEEE_FLOAT,
                                    NULL, CPL_IO_CREATE);
             }
             cpl_free(fileName); fileName = NULL;
             cpl_imagelist_delete(cube); cube = NULL;
             cpl_imagelist_delete(diffCube); diffCube = NULL;
        }

    }
    cpl_imagelist_delete(refCube); refCube = NULL;
/*    printf("--> Memory dump after freeing returned cube\n");\
    cpl_memory_dump();
*/

    /*printf("--=--> %f\n",4.8);*/
    cpl_image_delete(xcal); xcal = NULL;
    cpl_image_delete(ycal); ycal = NULL;
    cpl_image_delete(lcal); lcal = NULL;
    cpl_image_delete(vcal); vcal = NULL;
    /*printf("--=--> %f\n",4.2);*/

    cpl_test_error(CPL_ERROR_NONE);
}

int single_shift_test(const char *filename,
                      cpl_image *image,
                      double xshift,
                      double yshift,
                      const char *method,
                      enum extrapolationType type)
{
    FILE            *idl    = NULL;
    cpl_imagelist   *cube   = NULL,
                    *cube1  = NULL,
                    *cube2  = NULL;
    float           *pixel  = NULL;
    int             result  = -1;
#if KMCLIPM_GET_INSTALLED_CPL_VERSION >= 6
    cpl_size    xdim    = 0,
                ydim    = 0,
                i       = 0,
                j       = 0;
#define FT_CPLSIZE "lld"
#else
    int         xdim    = 0,
                ydim    = 0,
                i       = 0,
                j       = 0;
#define FT_CPLSIZE "d"
#endif
    char *dataFilename = cpl_sprintf("%s/test_data/kmclipm_test_shift_%s.dat", ".", filename);
    char *refFilename = cpl_sprintf("%s/ref/kmclipm_test_shift_%s.ref", getenv("srcdir"), filename);
    idl = fopen(dataFilename, "w");

    xdim = cpl_image_get_size_x(image);
    ydim = cpl_image_get_size_y(image);

    fprintf(idl, "%"FT_CPLSIZE" %"FT_CPLSIZE"\n", xdim, ydim);
    pixel = cpl_image_get_data_float(image);
    for (j=0; j<ydim ; j++) {
        for (i=0; i<xdim ; i++) {
            fprintf(idl, " %g", pixel[i + j*xdim]);
        }
    }
    fprintf(idl, "\n");

    cube = cpl_imagelist_new();
    cpl_imagelist_set(cube,cpl_image_duplicate(image),0);
    cpl_test_nonnull(cube1 = kmclipm_shift(cube, xshift, yshift, method, type));
    cpl_imagelist_delete(cube); cube = NULL;

    pixel = cpl_image_get_data_float(cpl_imagelist_get(cube1, 0));
    xdim = cpl_image_get_size_x(cpl_imagelist_get(cube1, 0));
    ydim = cpl_image_get_size_y(cpl_imagelist_get(cube1, 0));
    fprintf(idl, "%"FT_CPLSIZE" %"FT_CPLSIZE"\n", xdim, ydim);
    for (j=0; j<ydim ; j++) {
        for (i=0; i<xdim ; i++) {
            fprintf(idl, " %g", pixel[i + j*xdim]);
        }
    }
    fprintf(idl, "\n");

    cpl_test_nonnull(cube2 = kmclipm_shift(cube1, -xshift, -yshift, method, type));
    cpl_imagelist_delete(cube1);

    pixel = cpl_image_get_data_float(cpl_imagelist_get(cube2, 0));
    xdim = cpl_image_get_size_x(cpl_imagelist_get(cube2, 0));
    ydim = cpl_image_get_size_y(cpl_imagelist_get(cube2, 0));
    fprintf(idl, "%"FT_CPLSIZE" %"FT_CPLSIZE"\n", xdim, ydim);
    for (j=0; j<ydim ; j++) {
        for (i=0; i<xdim ; i++) {
            fprintf(idl, " %g", pixel[i + j*xdim]);
        }
    }
    fprintf(idl, "\n");
    cpl_imagelist_delete(cube2);

    fclose(idl);
    char* cmpCommand = cpl_sprintf("cmp %s %s", dataFilename, refFilename);
    result = system(cmpCommand);
    cpl_free(dataFilename); dataFilename = NULL;
    cpl_free(refFilename); refFilename = NULL;
    cpl_free(cmpCommand); cmpCommand = NULL;

    return result;
}

void test_kmclipm_shift() {

    cpl_image *image;
    double xshift;
    double yshift;

    const int xdim=14;
    const int ydim=14;

    image = cpl_image_new(xdim, ydim, CPL_TYPE_FLOAT);
    /*
    cpl_image_set(image, 3, 4, 1.0);
    cpl_image_set(image, 5, 3, 2.0);
    */
    cpl_image_fill_gaussian(image,xdim/2.,ydim/2.,200.,xdim/8.,ydim/8.);
    xshift = -0.8;
    yshift = -0.8;

    cpl_test_eq(0, single_shift_test("BCS_NATURAL-0.8", image, xshift, yshift,
                                     "BCS", BCS_NATURAL));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_ESTIMATED-0.8", image, xshift, yshift,
                                     "BCS", BCS_ESTIMATED));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_NANS-0.8", image, xshift, yshift,
                                     "BCS", NONE_NANS));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_CLIPPING-0.8", image, xshift, yshift,
                                     "BCS", NONE_CLIPPING));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("NN_NANS-0.8", image, xshift, yshift,
                                     "NN", NONE_NANS));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("NN_CLIPPING-0.8", image, xshift, yshift,
                                     "NN", NONE_CLIPPING));
    cpl_test_error(CPL_ERROR_NONE);

    xshift = +0.8;
    yshift = +0.8;

    cpl_test_eq(0, single_shift_test("BCS_NATURAL+0.8", image, xshift, yshift,
                                     "BCS", BCS_NATURAL));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_ESTIMATED+0.8", image, xshift, yshift,
                                     "BCS", BCS_ESTIMATED));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_NANS+0.8", image, xshift, yshift,
                                     "BCS", NONE_NANS));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_CLIPPING+0.8", image, xshift, yshift,
                                     "BCS", NONE_CLIPPING));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("NN_NANS+0.8", image, xshift, yshift,
                                     "NN", NONE_NANS));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("NN_CLIPPING+0.8", image, xshift, yshift,
                                     "NN", NONE_CLIPPING));

    xshift = +1.0;
    yshift = +1.0;
    cpl_test_eq(0, single_shift_test("BCS_NANS+1.0", image, xshift, yshift,
                                     "BCS", NONE_NANS));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_CLIPPING+1.0", image, xshift, yshift,
                                     "BCS", NONE_CLIPPING));
    cpl_test_error(CPL_ERROR_NONE);

    xshift = -1.0;
    yshift = -1.0;
    cpl_test_eq(0, single_shift_test("BCS_NANS-1.0", image, xshift, yshift,
                                     "BCS", NONE_NANS));
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_eq(0, single_shift_test("BCS_CLIPPING-1.0", image, xshift, yshift,
                                     "BCS", NONE_CLIPPING));
    cpl_test_error(CPL_ERROR_NONE);

    cpl_image_delete(image);

    cpl_test_error(CPL_ERROR_NONE);
}

int single_rotate_test(const char *filename,
                       cpl_image *image,
                       double alpha,
                       const char *method,
                       enum extrapolationType type)
{
    FILE            *idl    = NULL;
    cpl_imagelist   *cube   = NULL,
                    *cube1  = NULL,
                    *cube2  = NULL;
    float           *pixel  = NULL;
    int             result  = -1;
#if KMCLIPM_GET_INSTALLED_CPL_VERSION >= 6
    cpl_size    xdim    = 0,
                ydim    = 0,
                i       = 0,
                j       = 0;
#define FT_CPLSIZE "lld"
#else
    int         xdim    = 0,
                ydim    = 0,
                i       = 0,
                j       = 0;
#define FT_CPLSIZE "d"
#endif

    char *dataFilename = cpl_sprintf("%s/test_data/kmclipm_test_rotate_%s.dat", ".", filename);
    char *refFilename = cpl_sprintf("%s/ref/kmclipm_test_rotate_%s.ref", getenv("srcdir"), filename);
    idl = fopen(dataFilename, "w");

    xdim = cpl_image_get_size_x(image);
    ydim = cpl_image_get_size_y(image);
    fprintf(idl, "%"FT_CPLSIZE" %"FT_CPLSIZE"\n", xdim, ydim);

    pixel = cpl_image_get_data_float(image);
    for (j=0; j<ydim ; j++) {
        for (i=0; i<xdim ; i++) {
            fprintf(idl, " %g", pixel[i + j*xdim]);
        }
    }
    fprintf(idl, "\n");

    cube = cpl_imagelist_new();
    cpl_imagelist_set(cube,cpl_image_duplicate(image),0);
    cpl_test_nonnull(cube1 = kmclipm_rotate(cube, alpha, method, type));
    cpl_imagelist_delete(cube);

    pixel = cpl_image_get_data_float(cpl_imagelist_get(cube1, 0));
    xdim = cpl_image_get_size_x(cpl_imagelist_get(cube1, 0));
    ydim = cpl_image_get_size_y(cpl_imagelist_get(cube1, 0));
    fprintf(idl, "%"FT_CPLSIZE" %"FT_CPLSIZE"\n", xdim, ydim);
    for (j=0; j<ydim ; j++) {
        for (i=0; i<xdim ; i++) {
            fprintf(idl, " %g", pixel[i + j*xdim]);
        }
    }
    fprintf(idl, "\n");

    cpl_test_nonnull(cube2 = kmclipm_rotate(cube1, -alpha, method, type));
    cpl_imagelist_delete(cube1);

    pixel = cpl_image_get_data_float(cpl_imagelist_get(cube2, 0));
    xdim = cpl_image_get_size_x(cpl_imagelist_get(cube2, 0));
    ydim = cpl_image_get_size_y(cpl_imagelist_get(cube2, 0));
    fprintf(idl, "%"FT_CPLSIZE" %"FT_CPLSIZE"\n", xdim, ydim);
    for (j=0; j<ydim ; j++) {
        for (i=0; i<xdim ; i++) {
            fprintf(idl, " %g", pixel[i + j*xdim]);
        }
    }
    fprintf(idl, "\n");
    cpl_imagelist_delete(cube2);

    fclose(idl);
    char* cmpCommand = cpl_sprintf("cmp %s %s", dataFilename, refFilename);
    result = system(cmpCommand);
    cpl_free(dataFilename); dataFilename = NULL;
    cpl_free(refFilename); refFilename = NULL;
    cpl_free(cmpCommand); cmpCommand = NULL;

    return result;
}

void test_kmclipm_rotate() {
    cpl_image *image;
    const int xdim=14;
    const int ydim=14;
    char *testName;
    char *errorMessage;
    double alphas [] = {4.,40.,90.,180.,270.,356.,-1.};
    double alpha;
    FILE *animFile = NULL;
    cpl_imagelist *cubein;
    cpl_imagelist *cubeout;
    float *pixel;
    int rl, al;
    char *my_path;

    image = cpl_image_new(xdim, ydim, CPL_TYPE_FLOAT);
    cpl_image_fill_gaussian(image,xdim/3.,ydim/3.,200.,xdim/8.,ydim/8.);

    int idx=0;
    while (alphas[idx] > 0.) {
        alpha = alphas[idx] / 180.0 * CPL_MATH_PI;
        testName = cpl_sprintf("BCS_NATURAL_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: BCS_NATURAL_%03.0g does "
                                   "not match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "BCS",
                                          BCS_NATURAL));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;


        testName = cpl_sprintf("BCS_ESTIMATED_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: BCS_ESTIMATED_%03.0g does "
                                   "not match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "BCS",
                                          BCS_ESTIMATED));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;

        testName = cpl_sprintf("BCS_NANS_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: BCS_NANS_%03.0g does not "
                                   "match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "BCS",
                                          NONE_NANS));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;

        alpha = alphas[idx] / 180.0 * CPL_MATH_PI;
        testName = cpl_sprintf("RESIZE_BCS_NATURAL_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: RESIZE_BCS_NATURAL_%03.0g "
                                   "does not match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "BCS",
                                          RESIZE_BCS_NATURAL));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;

        testName = cpl_sprintf("RESIZE_BCS_ESTIMATED_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: RESIZE_BCS_ESTIMATED_%03.0g "
                                   "does not match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "BCS",
                                          RESIZE_BCS_ESTIMATED));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;

        testName = cpl_sprintf("RESIZE_NANS_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: RESIZE_NANS_%03.0g does not "
                                   "match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "BCS",
                                          RESIZE_NANS));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;

        testName = cpl_sprintf("NN_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: NN_%03.0g does not "
                                   "match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "NN",
                                          NONE_NANS));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;

        testName = cpl_sprintf("NN_RESIZE_%03g", alphas[idx]);
        errorMessage = cpl_sprintf("kmclipm_rotate: RESIZE_NN_%03.0g does not "
                                   "match reference", alphas[idx]);
        cpl_test_eq(0, single_rotate_test(testName, image, alpha, "NN",
                                          RESIZE_NANS));
        cpl_test_error(CPL_ERROR_NONE);
        cpl_free(testName); testName = NULL;
        cpl_free(errorMessage); errorMessage = NULL;

        idx++;
    }

    cubein = cpl_imagelist_new();
    cpl_imagelist_set(cubein,cpl_image_duplicate(image),0);
    enum extrapolationType eType = NONE_NANS;
    const char *method;
    for (rl=0; rl<8; rl++) {
        switch (rl) {
        case 0:
            method = "BCS";
            eType = NONE_NANS;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_NONE_NANS.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
        case 1:
            method = "BCS";
            eType = BCS_NATURAL;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_BCS_NATURAL.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
        case 2:
            method = "BCS";
            eType = BCS_ESTIMATED;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_BCS_ESTIMATED.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
        case 3:
            method = "BCS";
            eType = RESIZE_NANS;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_RESIZE_NANS.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
        case 4:
            method = "BCS";
            eType = RESIZE_BCS_NATURAL;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_RESIZE_BCS_NATURAL.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
        case 5:
            method = "BCS";
            eType = RESIZE_BCS_ESTIMATED;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_RESIZE_BCS_ESTIMATED.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
        case 6:
            method = "NN";
            eType = NONE_NANS;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_NONE_NANS_NN.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
        case 7:
            method = "NN";
            eType = RESIZE_NANS;
            my_path = cpl_sprintf("%s/test_data/kmclipm_test_rotate_animation_RESIZE_NANS_NN.dat", ".");
            animFile = fopen(my_path, "w");
            cpl_free(my_path);
            break;
         }

#if KMCLIPM_GET_INSTALLED_CPL_VERSION >= 6
    cpl_size    i   = 0,
                j   = 0,
                xc  = 0,
                yc  = 0,
                xo  = 0,
                yo  = 0;
#else
    int         i   = 0,
                j   = 0,
                xc  = 0,
                yc  = 0,
                xo  = 0,
                yo  = 0;
#endif
        fprintf(animFile, "%d %d %d\n",100,20,20);
        for (al=0.0; al<360.; al=al+3.6) {
            alpha = al / 180.0 * CPL_MATH_PI;
            cpl_test_nonnull(cubeout = kmclipm_rotate(cubein, alpha, method, eType));
            pixel = cpl_image_get_data_float(cpl_imagelist_get(cubeout, 0));
            xc = cpl_image_get_size_x(cpl_imagelist_get(cubeout, 0));
            yc = cpl_image_get_size_y(cpl_imagelist_get(cubeout, 0));
            xo = (20 - xc) / 2;
            yo = (20 - yc) / 2;
            for (j=0; j<20 ; j++) {
                for (i=0; i<20 ; i++) {
                    if (i <= xo || i >= 20-xo || j <= yo || j >= 20-yo) {
                        fprintf(animFile, " %g", 0.2);
                    } else {
                        fprintf(animFile, " %g", pixel[i-xo + (j-yo)*xc]);
                    }
                }
            }
            fprintf(animFile, "\n");
            cpl_imagelist_delete(cubeout);
        }
        fclose(animFile);
    }
    cpl_imagelist_delete(cubein);
    cpl_image_delete(image);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
 * @brief   test for kmclipm_reject_deviant()
 */
void test_kmclipm_reject_deviant()
{
    cpl_msg_set_level(CPL_MSG_OFF);
    kmclipm_reject_deviant(NULL, -2, -2, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    double stddev = 0, mean = 0;
    cpl_vector *d = cpl_vector_new(5);
    cpl_vector_set(d, 0, 0.1);
    cpl_vector_set(d, 1, 100);
    cpl_vector_set(d, 2, 0.05);
    cpl_vector_set(d, 3, 0.74);
    cpl_vector_set(d, 4, 0.2);
    kmclipm_vector *dd = kmclipm_vector_create(d);

    kmclipm_reject_deviant(dd, -2, -2, NULL, NULL);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    kmclipm_reject_deviant(dd, 0, -2, NULL, NULL);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_msg_set_level(my_level);

    kmclipm_reject_deviant(dd, 3, 3, NULL, NULL);
    cpl_test_eq(1, cpl_vector_get(dd->mask, 0));
    cpl_test_eq(0, cpl_vector_get(dd->mask, 1));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 2));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 3));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 4));
    kmclipm_vector_delete(dd);
    cpl_test_error(CPL_ERROR_NONE);

    d = cpl_vector_new(5);
    cpl_vector_set(d, 0, 0.1);
    cpl_vector_set(d, 1, 100);
    cpl_vector_set(d, 2, 0.05);
    cpl_vector_set(d, 3, 0.74);
    cpl_vector_set(d, 4, 0.2);
    dd = kmclipm_vector_create(d);
    kmclipm_reject_deviant(dd, 3, 3, &stddev, NULL);
    cpl_test_abs(0.317844, stddev, 0.01);
    cpl_test_eq(1, cpl_vector_get(dd->mask, 0));
    cpl_test_eq(0, cpl_vector_get(dd->mask, 1));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 2));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 3));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 4));
    kmclipm_vector_delete(dd);
    cpl_test_error(CPL_ERROR_NONE);

    d = cpl_vector_new(5);
    cpl_vector_set(d, 0, 0.1);
    cpl_vector_set(d, 1, 100);
    cpl_vector_set(d, 2, 0.05);
    cpl_vector_set(d, 3, 0.74);
    cpl_vector_set(d, 4, 0.2);
    dd = kmclipm_vector_create(d);
    kmclipm_reject_deviant(dd, 3, 3, &stddev, &mean);
    cpl_test_abs(0.317844, stddev, 0.01);
    cpl_test_abs(0.2725, mean, 0.01);
    cpl_test_eq(1, cpl_vector_get(dd->mask, 0));
    cpl_test_eq(0, cpl_vector_get(dd->mask, 1));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 2));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 3));
    cpl_test_eq(1, cpl_vector_get(dd->mask, 4));
    kmclipm_vector_delete(dd);
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_make_image()
{
    float           tol         = 0.01;

    cpl_imagelist   *data       = NULL,
                    *noise      = NULL;

    cpl_image       *data_out   = NULL,
                    *noise_out  = NULL,
                    *tmp        = NULL;

    cpl_vector      *slices     = NULL;

    /* --- invalid tests --- */
    cpl_test_eq(CPL_ERROR_NULL_INPUT, kmclipm_make_image(NULL, NULL,
                                                         NULL, NULL,
                                                         NULL,
                                                         "gaga",
                                                         -1, -1,
                                                         -1,
                                                         -1, -1));
    cpl_error_reset();

    data = cpl_imagelist_new();

    cpl_test_eq(CPL_ERROR_NULL_INPUT, kmclipm_make_image(data, NULL,
                                                         NULL, NULL,
                                                         NULL,
                                                         "gaga",
                                                         -1, -1,
                                                         -1,
                                                         -1, -1));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, NULL,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "gaga",
                                                          -1, -1,
                                                          -1,
                                                          -1, -1));
    cpl_error_reset();

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.0, 0.1);
    cpl_imagelist_set(data, tmp, 0);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 1);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 3.0, 0.1);
    cpl_imagelist_set(data, tmp, 2);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 3);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 4);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 5);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 6);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 7);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 8);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 199.0, 0.1);
    cpl_imagelist_set(data, tmp, 9);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 10);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 1.0, 0.1);
    cpl_imagelist_set(data, tmp, 11);

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, NULL,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "gaga",
                                                          -1, -1,
                                                          -1,
                                                          -1, -1));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, NULL,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "ksigma",
                                                          2, -1,
                                                          -1,
                                                          -1, -1));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, NULL,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "ksigma",
                                                          2, 2,
                                                          -1,
                                                          -1, -1));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, NULL,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "min_max",
                                                          -1, -1,
                                                          -1,
                                                          -1, -1));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, NULL,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "min_max",
                                                          -1, -1,
                                                          -1,
                                                          1, -1));
    cpl_error_reset();

    noise = cpl_imagelist_new();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, noise,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "median",
                                                          -1, -1,
                                                          -1,
                                                          -1, -1));
    cpl_error_reset();

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.0, 0.01);
    cpl_imagelist_set(noise, tmp, 0);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.1, 0.01);
    cpl_imagelist_set(noise, tmp, 1);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.3, 0.01);
    cpl_imagelist_set(noise, tmp, 2);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.4, 0.01);
    cpl_imagelist_set(noise, tmp, 3);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.0, 0.01);
    cpl_imagelist_set(noise, tmp, 4);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.1, 0.01);
    cpl_imagelist_set(noise, tmp, 5);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.3, 0.01);
    cpl_imagelist_set(noise, tmp, 6);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.4, 0.01);
    cpl_imagelist_set(noise, tmp, 7);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.0, 0.01);
    cpl_imagelist_set(noise, tmp, 8);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.1, 0.01);
    cpl_imagelist_set(noise, tmp, 9);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.3, 0.01);
    cpl_imagelist_set(noise, tmp, 10);

    tmp = cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    kmclipm_test_fill_image(tmp, 0.4, 0.01);
    cpl_imagelist_set(noise, tmp, 11);

    slices = cpl_vector_new(3);
    cpl_vector_fill(slices, 1.0);
    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT, kmclipm_make_image(data, noise,
                                                          &data_out, NULL,
                                                          slices,
                                                          "median",
                                                          -1, -1,
                                                          -1,
                                                          -1, -1));
    cpl_error_reset();
    cpl_vector_delete(slices);

    /* --- valid tests --- */

    slices = cpl_vector_new(12);
    cpl_vector_set(slices, 0, 1.0);
    cpl_vector_set(slices, 1, 1.0);
    cpl_vector_set(slices, 2, 1.0);
    cpl_vector_set(slices, 3, 1.0);
    cpl_vector_set(slices, 4, 1.0);
    cpl_vector_set(slices, 5, 1.0);
    cpl_vector_set(slices, 6, 1.0);
    cpl_vector_set(slices, 7, 1.0);
    cpl_vector_set(slices, 8, 1.0);
    cpl_vector_set(slices, 9, 0.0);
    cpl_vector_set(slices, 10, 1.0);
    cpl_vector_set(slices, 11, 1.0);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, NULL,
                                                    &data_out, NULL,
                                                    NULL,
                                                    "median",
                                                    -1, -1,
                                                    -1,
                                                    -1, -1));
    cpl_test_abs(cpl_image_get_mean(data_out), 5.94998, tol);
    cpl_image_delete(data_out);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, noise,
                                                   &data_out, &noise_out,
                                                   NULL,
                                                   "median",
                                                   -1, -1,
                                                   -1,
                                                   -1, -1));
    cpl_test_abs(cpl_image_get_mean(data_out), 5.94998, tol);
    cpl_test_abs(cpl_image_get_mean(noise_out), 17.2348, tol);
    cpl_image_delete(data_out);
    cpl_image_delete(noise_out);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, noise,
                                                   &data_out, &noise_out,
                                                   slices,
                                                   "median",
                                                   -1, -1,
                                                   -1,
                                                   -1, -1));
    cpl_test_abs(cpl_image_get_mean(data_out), 5.94998, tol);
    cpl_test_abs(cpl_image_get_mean(noise_out), 0.219059, tol);
    cpl_image_delete(data_out);
    cpl_image_delete(noise_out);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, NULL,
                                                   &data_out, NULL,
                                                   NULL,
                                                   "ksigma",
                                                   2, 2,
                                                   2,
                                                   -1, -1));
    cpl_test_abs(cpl_image_get_mean(data_out), 5.95, tol);
    cpl_image_delete(data_out);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, noise,
                                                   &data_out, &noise_out,
                                                   NULL,
                                                   "ksigma",
                                                   2, 2,
                                                   2,
                                                   -1, -1));
    cpl_test_abs(cpl_image_get_mean(data_out), 5.95, tol);
    cpl_test_abs(cpl_image_get_mean(noise_out), 0.0, tol);
    cpl_image_delete(data_out);
    cpl_image_delete(noise_out);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, noise,
                                                   &data_out, &noise_out,
                                                   slices,
                                                   "ksigma",
                                                   2, 2,
                                                   2,
                                                   -1, -1));
    cpl_test_abs(cpl_image_get_mean(data_out), 5.95, tol);
    cpl_test_abs(cpl_image_get_mean(noise_out), 0.0, tol);
    cpl_image_delete(data_out);
    cpl_image_delete(noise_out);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, NULL,
                                                          &data_out, NULL,
                                                          NULL,
                                                          "min_max",
                                                          -1, -1,
                                                          -1,
                                                          1, 1));
    cpl_test_abs(cpl_image_get_mean(data_out), 6.14999, tol);
    cpl_image_delete(data_out);

    cpl_test_eq(CPL_ERROR_NONE, kmclipm_make_image(data, noise,
                                                          &data_out, &noise_out,
                                                          NULL,
                                                          "min_max",
                                                          -1, -1,
                                                          -1,
                                                          1, 1));
    cpl_test_abs(cpl_image_get_mean(data_out), 6.14999, tol);
    cpl_test_abs(cpl_image_get_mean(noise_out), 0.2, tol);
    cpl_image_delete(data_out);
    cpl_image_delete(noise_out);

    cpl_imagelist_delete(data);
    cpl_imagelist_delete(noise);
    cpl_vector_delete(slices);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_combine_vector()
{
    float           tol         = 0.01;

    cpl_vector      *identified = NULL,
                    *data       = NULL,
                    *noise      = NULL,
                    *dupd       = NULL,
                    *dupn       = NULL,
                    *dupi       = NULL;

    int             new_size    = 0,
                    len         = 10;

    double          stdev       = 0.0,
                    stderr      = 0.0;
    kmclipm_vector  *kvd        = NULL,
                    *kvn        = NULL;
    enum combine_status status  = combine_ok;

    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);

    cpl_test_eq(-1,
                kmclipm_combine_vector(NULL, NULL, NULL,
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();

    data = cpl_vector_new(len);
    kmclipm_test_fill_vector(data, 0.0, 0.5);

    dupd = cpl_vector_duplicate(data);
    kvd = kmclipm_vector_create(dupd);

    cpl_test_eq(-1,
                kmclipm_combine_vector(kvd, NULL, NULL,
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();

    kmclipm_vector_delete(kvd);

    identified = cpl_vector_new(len);
    cpl_vector_fill(identified, 1.0);
    cpl_vector_set(identified, 5, 0);

    dupd = cpl_vector_duplicate(data);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);

    cpl_test_eq(-1, kmclipm_combine_vector(kvd, NULL, NULL,
                                           0.0, 0.0, 0, 0, 0,
                                           &new_size, &stdev, &stderr, -1.0,
                                           &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();

    cpl_test_eq(-1, kmclipm_combine_vector(kvd, NULL, "min_max",
                                   0.0, 0.0, 0, 0, -1,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();

    cpl_test_eq(-1, kmclipm_combine_vector(kvd, NULL, "min_max",
                                   0.0, 0.0, 0, -1, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();


    cpl_test_eq(-1, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                   -1.0, 0.0, 0, -1, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();

    cpl_test_eq(-1, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                   0.0, -1.0, 0, -1, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();

    cpl_test_eq(-1, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                   0.0, 0.0, -1, -1, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status));
    cpl_test_eq(combine_ok, status);
    cpl_error_reset();

    cpl_test_eq(0, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    /* --- valid tests --- */
    noise = cpl_vector_new(len);
    kmclipm_test_fill_vector(noise, 0.0, 0.01);
    cpl_vector_sort(noise, -1);

    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    /* new_size > 2*/
    cpl_test_abs(2.22, kmclipm_combine_vector(kvd, NULL, "average",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);

    cpl_test_abs(20, kmclipm_combine_vector(kvd, NULL, "sum",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(9, new_size);
    cpl_test_abs(1.60295, stdev, tol);
    cpl_test_abs(0.534316, stderr, tol);

    cpl_test_abs(2, kmclipm_combine_vector(kvd, NULL, "median",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(9, new_size);
    cpl_test_abs(1.62019, stdev, tol);
    cpl_test_abs(0.540063, stderr, tol);

    cpl_test_abs(2.22, kmclipm_combine_vector(kvd, NULL, "min_max",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(9, new_size);
    cpl_test_abs(1.60295, stdev, tol);
    cpl_test_abs(0.534316, stderr, tol);

    cpl_test_abs(2.5, kmclipm_combine_vector(kvd, NULL, "min_max",
                                       0.0, 0.0, 0, 2, 3,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(4, new_size);
    cpl_test_abs(0.912871, stdev, tol);
    cpl_test_abs(0.456435, stderr, tol);

    /* applies average */
    cpl_test_abs(2.22, kmclipm_combine_vector(kvd, NULL, "min_max",
                                       0.0, 0.0, 0, 4, 5,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(9, new_size);
    cpl_test_abs(1.60295, stdev, tol);
    cpl_test_abs(0.534316, stderr, tol);

    /* applies average */
    cpl_test_abs(2.22, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                       0.0, 0.0, 3, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(9, new_size);
    cpl_test_abs(1.60295, stdev, tol);
    cpl_test_abs(0.534316, stderr, tol);
    cpl_test_abs(1.875, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                       1, 1, 2, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);

    cpl_test_eq(4, new_size);
    cpl_test_abs(0.853913, stdev, tol);
    cpl_test_abs(0.853913/sqrt(4), stderr, tol);

    /* with noise */
    cpl_test_abs(1.875, kmclipm_combine_vector(kvd, kvn, "ksigma",
                                       1.0, 1.0, 2, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(4, new_size);
    cpl_test_abs(0.853913, stdev, tol);
    cpl_test_abs(0.853913/sqrt(4), stderr, tol);

    /* new_size = 2 */
    cpl_test_abs(2.2222, kmclipm_combine_vector(kvd, kvn, "ksigma",
                                       3.0, 3.0, 3, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(9, new_size);
    cpl_test_abs(1.60295, stdev, tol/100);
    cpl_test_abs(0.5343, stderr, tol/100);

    cpl_test_abs(0.25, kmclipm_combine_vector(kvd, kvn, "min_max",
                                       0.0, 0.0, 0, 7, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.060208, stdev, tol);
    cpl_test_abs(0.0425735, stderr, tol);

    cpl_test_abs(0.25, kmclipm_combine_vector(kvd, kvn, "min_max",
                                       0.0, 0.0, 0, 7, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.060208, stdev, tol/100);
    cpl_test_abs(0.042573, stderr, tol/100);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);

    cpl_vector_set(identified, 2, 0);
    cpl_vector_set(identified, 3, 0);
    cpl_vector_set(identified, 4, 0);
    cpl_vector_set(identified, 6, 0);
    cpl_vector_set(identified, 7, 0);
    cpl_vector_set(identified, 8, 0);
    cpl_vector_set(identified, 9, 0);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(0.25, kmclipm_combine_vector(kvd, NULL, "average",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.3536, stderr, tol);

    cpl_test_abs(0.25, kmclipm_combine_vector(kvd, kvn, "average",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.060208, stdev, tol/100);
    cpl_test_abs(0.042575, stderr, tol/100);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, NULL, "sum",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.3536, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, kvn, "sum",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.120416, stdev, tol/10);
    cpl_test_abs(0.0851469, stderr, tol/100);

    cpl_test_abs(0.25, kmclipm_combine_vector(kvd, NULL, "median",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.3536, stderr, tol);

    cpl_test_abs(0.25, kmclipm_combine_vector(kvd, kvn, "median",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.060208, stdev, tol/100);
    cpl_test_abs(0.0425734, stderr, tol/100);

    /* new_size = 1 */
    cpl_vector_fill(identified, 1.0);
    cpl_vector_set(identified, 5, 0);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(3, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                       1.0, 0.5, 5, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_test_abs(3, kmclipm_combine_vector(kvd, kvn, "ksigma",
                                       1.0, 0.5, 5, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.03, stdev, tol);
    cpl_test_abs(0.03, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, NULL, "min_max",
                                       0.0, 0.0, 0, 7, 1,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, kvn, "min_max",
                                       0.0, 0.0, 0, 7, 1,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.08, stdev, tol);
    cpl_test_abs(0.08, stderr, tol);

    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);

    cpl_vector_set(identified, 0, 0);
    cpl_vector_set(identified, 2, 0);
    cpl_vector_set(identified, 3, 0);
    cpl_vector_set(identified, 4, 0);
    cpl_vector_set(identified, 6, 0);
    cpl_vector_set(identified, 7, 0);
    cpl_vector_set(identified, 8, 0);
    cpl_vector_set(identified, 9, 0);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);


    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, NULL, "average",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, kvn, "average",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.08, stdev, tol);
    cpl_test_abs(0.08, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, NULL, "sum",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, kvn, "sum",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.08, stdev, tol);
    cpl_test_abs(0.08, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, NULL, "median",
                                       0.0, 0.0, 0, 0, 0,
                                       &new_size, &stdev, &stderr, -1.0,
                                       &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_test_abs(0.5, kmclipm_combine_vector(kvd, kvn, "median",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.08, stdev, tol);
    cpl_test_abs(0.08, stderr, tol);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);
    cpl_vector_delete(data);
    cpl_vector_delete(noise);
    cpl_vector_delete(identified);

    cpl_test_error(CPL_ERROR_NONE);

    /*
     * (new) systematic tests
     */

    /* nz = 1, w/o noise, all methods */
    data = cpl_vector_new(1);
    cpl_vector_fill(data, 5);
    noise = cpl_vector_new(1);
    cpl_vector_fill(noise, 0.5);
    identified = cpl_vector_new(1);
    cpl_vector_fill(identified, 1);
    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "median",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "min_max",
                                   0.0, 0.0, 0, 2, 3,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "sum",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                   0.2, 0.2, 3, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    /* nz = 1, with noise, all methods */
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.5, stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "median",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.5, stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "min_max",
                                   0.0, 0.0, 0, 2, 3,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.5, stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "sum",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.5, stderr, tol);
     cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "ksigma",
                                   0.2, 0.2, 3, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
     cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.5, stderr, tol);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);
    cpl_vector_delete(data);
    cpl_vector_delete(noise);
    cpl_vector_delete(identified);

    /* nz = 2, w/o noise, all methods */
    data = cpl_vector_new(2);
    cpl_vector_set(data, 0, 5);
    cpl_vector_set(data, 1, 2);
    noise = cpl_vector_new(2);
    cpl_vector_set(noise, 0, 0.5);
    cpl_vector_set(noise, 1, 0.2);
    identified = cpl_vector_new(2);
    cpl_vector_fill(identified, 1);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(2), stderr, tol);
    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, NULL, "median",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(2), stderr, tol);
    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, NULL, "min_max",
                                   0.0, 0.0, 0, 2, 3,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(2), stderr, tol);
    cpl_test_abs(7, kmclipm_combine_vector(kvd, NULL, "sum",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(2), stderr, tol);

    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                   1.0, 1.0, 3, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(2), stderr, tol);

    /* nz = 2, with noise, all methods */
    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, kvn, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.2693, stdev, tol);
    cpl_test_abs(0.19042, stderr, tol);
    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, kvn, "median",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.2693, stdev, tol);
    cpl_test_abs(0.19042, stderr, tol);
    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, kvn, "min_max",
                                   0.0, 0.0, 0, 2, 3,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.2693, stdev, tol);
    cpl_test_abs(0.19042, stderr, tol);
    cpl_test_abs(7, kmclipm_combine_vector(kvd, kvn, "sum",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.5385, stdev, tol);
    cpl_test_abs(0.380777, stderr, tol);

    cpl_test_abs(3.5, kmclipm_combine_vector(kvd, kvn, "ksigma",
                                   1.0, 1.0, 3, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(2, new_size);
    cpl_test_eq(combine_ok, status);
    cpl_test_abs(0.26925, stdev, tol);
    cpl_test_abs(0.190394, stderr, tol);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);
    cpl_vector_delete(data);
    cpl_vector_delete(noise);
    cpl_vector_delete(identified);

    /* nz = 3, w/o noise, all methods */
    data = cpl_vector_new(3);
    cpl_vector_set(data, 0, 5);
    cpl_vector_set(data, 1, 2);
    cpl_vector_set(data, 2, 8);
    noise = cpl_vector_new(3);
    cpl_vector_set(noise, 0, 0.5);
    cpl_vector_set(noise, 1, 0.2);
    cpl_vector_set(noise, 2, 0.1);
    identified = cpl_vector_new(3);
    cpl_vector_fill(identified, 1);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);

    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "median",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);

    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "min_max",
                                   0.0, 0.0, 0, 2, 3,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);

    cpl_test_abs(15, kmclipm_combine_vector(kvd, NULL, "sum",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "ksigma",
                                   1.0, 1.0, 3, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);

    /* nz = 3, with noise, all methods */
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "median",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "min_max",
                                   0.0, 0.0, 0, 2, 3,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);
    cpl_test_abs(15, kmclipm_combine_vector(kvd, kvn, "sum",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);
    cpl_test_abs(5, kmclipm_combine_vector(kvd, kvn, "ksigma",
                                   1.0, 1.0, 3, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(3, new_size);
    cpl_test_abs(3, stdev, tol);
    cpl_test_abs(3/sqrt(3), stderr, tol);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);
    cpl_vector_delete(data);
    cpl_vector_delete(noise);
    cpl_vector_delete(identified);

    cpl_test_error(CPL_ERROR_NONE);
    cpl_msg_set_level(my_level);

    /*
     * test with infinites
     */
    data = cpl_vector_new(1);
    cpl_vector_fill(data, 5);
    noise = cpl_vector_new(1);
    cpl_vector_fill(noise, 0.5);
    identified = cpl_vector_new(1);
    cpl_vector_fill(identified, 0);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(-1, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_rejected, status);
    cpl_test_eq(0, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_vector_fill(identified, 1);
    cpl_vector_fill(data, NAN);
    cpl_test_abs(-1, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_rejected, status);
    cpl_test_eq(0, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_vector_fill(data, -1./0.);
    cpl_test_abs(-1, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_rejected, status);
    cpl_test_eq(0, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_vector_fill(data, 1./0.);
    cpl_test_abs(-1, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_rejected, status);
    cpl_test_eq(0, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    cpl_test_abs(-1, kmclipm_combine_vector(kvd, kvn, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_rejected, status);
    cpl_test_eq(0, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);
    cpl_vector_delete(data);
    cpl_vector_delete(noise);
    cpl_vector_delete(identified);
    cpl_test_error(CPL_ERROR_NONE);

    /*size = 2, one infinite*/
    data = cpl_vector_new(2);
    cpl_vector_set(data, 0, 5);
    cpl_vector_set(data, 1, NAN);
    identified = cpl_vector_new(2);
    cpl_vector_fill(identified, 1);

    dupd = cpl_vector_duplicate(data);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);

    cpl_test_abs(5, kmclipm_combine_vector(kvd, NULL, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(1, new_size);
    cpl_test_abs(-1.0, stdev, tol);
    cpl_test_abs(-1.0, stderr, tol);

    kmclipm_vector_delete(kvd);
    cpl_vector_delete(data);
    cpl_vector_delete(identified);

    /*size = 3, one infinite*/
    data = cpl_vector_new(3);
    cpl_vector_set(data, 0, 5);
    cpl_vector_set(data, 1, NAN);
    cpl_vector_set(data, 2, 1);
    noise = cpl_vector_new(3);
    cpl_vector_set(noise, 0, 0.5);
    cpl_vector_set(noise, 1, 0.6);
    cpl_vector_set(noise, 2, 0.7);
    identified = cpl_vector_new(3);
    cpl_vector_fill(identified, 1);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(3, kmclipm_combine_vector(kvd, kvn, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.4301, stdev, tol);
    cpl_test_abs(0.3041, stderr, tol);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);
    cpl_vector_delete(data);
    cpl_vector_delete(noise);
    cpl_vector_delete(identified);
    cpl_test_error(CPL_ERROR_NONE);

    /*
     * test for NaNs in data or /and noise
     */

    /* NaN in noise at other position than in data (rather impossible) */
    data = cpl_vector_new(3);
    cpl_vector_set(data, 0, 5);
    cpl_vector_set(data, 1, NAN);
    cpl_vector_set(data, 2, 1);
    noise = cpl_vector_new(3);
    cpl_vector_set(noise, 0, 0.5);
    cpl_vector_set(noise, 1, 0.6);
    cpl_vector_set(noise, 2, NAN);
    identified = cpl_vector_new(3);
    cpl_vector_fill(identified, 1);

    dupd = cpl_vector_duplicate(data);
    dupn = cpl_vector_duplicate(noise);
    dupi = cpl_vector_duplicate(identified);
    kvd = kmclipm_vector_create2(dupd, dupi);
    dupi = cpl_vector_duplicate(identified);
    kvn = kmclipm_vector_create2(dupn, dupi);

    cpl_test_abs(3, kmclipm_combine_vector(kvd, kvn, "average",
                                   0.0, 0.0, 0, 0, 0,
                                   &new_size, &stdev, &stderr, -1.0,
                                   &status), tol);
    cpl_test_eq(combine_ok, status);
    cpl_test_eq(2, new_size);
    cpl_test_abs(0.5, stdev, tol);
    cpl_test_abs(0.5, stderr, tol);

    kmclipm_vector_delete(kvd);
    kmclipm_vector_delete(kvn);
    cpl_vector_delete(data);
    cpl_vector_delete(noise);
    cpl_vector_delete(identified);
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_combine_frames()
{
    float           tol         = 0.01;

    cpl_imagelist   *cube1         = cpl_imagelist_new(),
                    *cube2         = cpl_imagelist_new(),
                    *cube_data_z1  = cpl_imagelist_new(),
                    *cube_data_z2  = cpl_imagelist_new(),
                    *cube_data_z4  = cpl_imagelist_new(),
                    *cube_noise_z1 = cpl_imagelist_new(),
                    *cube_noise_z2 = cpl_imagelist_new(),
                    *cube_noise_z4 = cpl_imagelist_new();

    cpl_image       *img1       = NULL,
                    *img2       = NULL;

    cpl_vector      *identified = NULL;

    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);

    cpl_test_eq(CPL_ERROR_NULL_INPUT,
                kmclipm_combine_frames(NULL, NULL, NULL, NULL,
                                   -1.0, -1.0, -1, -1, -1, NULL, NULL, -1.0));
    cpl_error_reset();

    kmclipm_test_alloc_cube(cube1, 5, 5, 5);
    kmclipm_test_fill_cube(cube1, 0.0, 0.5);

    kmclipm_test_alloc_cube(cube2, 5, 5, 5);
    kmclipm_test_fill_cube(cube2, 0.0, 0.1);

    kmclipm_test_alloc_cube(cube_data_z1, 3, 3, 1);
    kmclipm_test_fill_cube(cube_data_z1, 0.0, 0.5);

    kmclipm_test_alloc_cube(cube_noise_z1, 3, 3, 1);
    kmclipm_test_fill_cube(cube_noise_z1, 0.0, 0.1);

    kmclipm_test_alloc_cube(cube_data_z2, 3, 3, 2);
    kmclipm_test_fill_cube(cube_data_z2, 0.0, 0.5);

    kmclipm_test_alloc_cube(cube_noise_z2, 3, 3, 2);
    kmclipm_test_fill_cube(cube_noise_z2, 0.0, 0.1);

    kmclipm_test_alloc_cube(cube_data_z4, 3, 3, 4);
    kmclipm_test_fill_cube(cube_data_z4, 0.0, 0.5);

    kmclipm_test_alloc_cube(cube_noise_z4, 3, 3, 4);
    kmclipm_test_fill_cube(cube_noise_z4, 0.0, 0.1);

    cpl_test_eq(CPL_ERROR_NULL_INPUT,
                kmclipm_combine_frames(cube1, NULL, NULL, NULL,
                                   -1.0, -1.0, -1, -1, -1,
                                   NULL, NULL, -1.0));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT,
                kmclipm_combine_frames(cube1, NULL, NULL, "gaga",
                                   -1.0, -1.0, -1, -1, -1,
                                   &img1, NULL, -1.0));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT,
                kmclipm_combine_frames(cube1, NULL, NULL, "ksigma",
                                   2.0, -1.0, -1, -1, -1,
                                   &img1, NULL, -1.0));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT,
                kmclipm_combine_frames(cube1, NULL, NULL, "ksigma",
                                   2.0, 1.0, -1, -1, -1,
                                   &img1, NULL, -1.0));
    cpl_error_reset();

    cpl_test_eq(CPL_ERROR_ILLEGAL_INPUT,
                kmclipm_combine_frames(cube1, NULL, NULL, "min_max",
                                   -1.0, -1.0, -1, 1, -1,
                                   &img1, NULL, -1.0));
    cpl_error_reset();

    /* --- valid tests --- */
    /* only one frame, data only*/
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, NULL, NULL, "sum",
                                   -1, -1, -1, -1, -1,
                                   &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, NULL, NULL, "median",
                                       -1, -1, -1, -1, -1,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, NULL, NULL, "min_max",
                                       -1, -1, -1, 0, 0,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, NULL, NULL, "min_max",
                                       -1, -1, -1, 1, 0,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, NULL, NULL, "ksigma",
                                       0, 0, 0, -1, -1,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_image_delete(img1); img1 = NULL;

    /* only one frame, data / noise*/
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, cube_noise_z1, NULL,
                                       "sum",-1, -1, -1, -1, -1,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, cube_noise_z1, NULL,
                                       "sum",-1, -1, -1, -1, -1,
                                       &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.4, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, cube_noise_z1, NULL,
                                       "average",-1, -1, -1, -1, -1,
                                       &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.4, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z1, cube_noise_z1, NULL,
                                       "median", -1, -1, -1, -1, -1,
                                       &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.4, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z4, cube_noise_z4, NULL,
                                       "min_max", -1, -1, -1, 1, 2,
                                       &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 3, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.6, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    /* only two frames, data only*/
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, NULL, NULL, "sum",
                                       -1, -1, -1, -1, -1,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 4.5, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, NULL, NULL, "median",
                                       -1, -1, -1, -1, -1,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2.25, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, NULL, NULL, "min_max",
                                       -1, -1, -1, 0, 0,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2.25, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, NULL, NULL, "min_max",
                                       -1, -1, -1, 1, 0,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, NULL, NULL, "min_max",
                                       -1, -1, -1, 1, 1,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2.25, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, NULL, NULL, "ksigma",
                                       0, 0, 0, -1, -1,
                                       &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2.25, tol);
    cpl_image_delete(img1); img1 = NULL;


    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, NULL, NULL, "ksigma",
                                       1.0, 1.0, 2, -1, -1,
                                       &img1, NULL, -1.0));

    cpl_test_abs(cpl_image_get_mean(img1), 2.25, tol);
    cpl_image_delete(img1); img1 = NULL;

    /* only two frames, data / noise*/
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, cube_noise_z2, NULL, "sum",
                                   -1, -1, -1, -1, -1,
                                   &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 4.5, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, cube_noise_z2, NULL, "sum",
                                   -1, -1, -1, -1, -1,
                                   &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 4.5, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.455269, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, cube_noise_z2, NULL,
                                       "average", -1, -1, -1, -1, -1,
                                       &img1, &img2, -1.0));

    cpl_test_abs(cpl_image_get_mean(img1), 2.25, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.227635, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z2, cube_noise_z2, NULL,
                                       "median", -1, -1, -1, -1, -1,
                                       &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2.25, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.227635, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z4, cube_noise_z4, NULL,
                                       "min_max", -1, -1, -1, 1, 1,
                                       &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2.75, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.276557, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube_data_z4, cube_noise_z4, NULL,
                                       "ksigma", 1, 1, 1, -1, -1,
                                       &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 2.75, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.276557, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    /* data only */
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, NULL, NULL, "ksigma",
                                   2.0, 1.0, 1, -1, -1,
                                   &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 7.25, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, NULL, NULL, "median",
                                   -1.0, -1.0, -1, -1, -1,
                                   &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 7.0, tol);
    cpl_image_delete(img1); img1 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, NULL, NULL, "min_max",
                                   -1.0, -1.0, -1, 1, 1,
                                   &img1, NULL, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 7.0, tol);
    cpl_image_delete(img1); img1 = NULL;

    /* data and noise */
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, cube2, NULL, "ksigma",
                                   2.0, 1.0, 1, -1, -1,
                                   &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 7.25, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.322749, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, cube2, NULL, "median",
                                   -1.0, -1.0, -1, -1, -1,
                                   &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 7.0, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.353553, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, cube2, NULL, "min_max",
                                   -1.0, -1.0, -1, 1, 1,
                                   &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 7.0, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.288675, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    /* data and noise & identified slices */
    identified = cpl_vector_new(5);
    cpl_vector_set(identified, 0, 1.0);
    cpl_vector_set(identified, 1, 1.0);
    cpl_vector_set(identified, 2, 1.0);
    cpl_vector_set(identified, 3, 1.0);
    cpl_vector_set(identified, 4, 0.0);

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, cube2, identified, "ksigma",
                                   2.0, 1.0, 1, -1, -1,
                                   &img1, &img2, -1.0));

    cpl_test_abs(cpl_image_get_mean(img1), 7, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.288675, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, cube2, identified, "median",
                                   -1.0, -1.0, -1, -1, -1,
                                   &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 6.75, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.322749, tol);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_combine_frames(cube1, cube2, identified, "min_max",
                                   -1.0, -1.0, -1, 1, 1,
                                   &img1, &img2, -1.0));
    cpl_test_abs(cpl_image_get_mean(img1), 6.75, tol);
    cpl_test_abs(cpl_image_get_mean(img2), 0.675799 , tol);

    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_imagelist_delete(cube1); cube1 = NULL;
    cpl_imagelist_delete(cube2); cube2 = NULL;
    cpl_imagelist_delete(cube_data_z1); cube_data_z1 = NULL;
    cpl_imagelist_delete(cube_data_z2); cube_data_z2 = NULL;
    cpl_imagelist_delete(cube_data_z4); cube_data_z4 = NULL;
    cpl_imagelist_delete(cube_noise_z1); cube_noise_z1 = NULL;
    cpl_imagelist_delete(cube_noise_z2); cube_noise_z2 = NULL;
    cpl_imagelist_delete(cube_noise_z4); cube_noise_z4 = NULL;
    cpl_vector_delete(identified); identified = NULL;

    /*
     * (new) systematic tests
     */

    /* nz = 1, w/o noise, all methods */
    cube1         = cpl_imagelist_new();
    cube2         = cpl_imagelist_new();
    kmclipm_test_alloc_cube(cube1, 2, 2, 1);
    kmclipm_test_fill_cube(cube1, 1.0, 1.0);
    kmclipm_test_alloc_cube(cube2, 2, 2, 1);
    kmclipm_test_fill_cube(cube2, 0.1, 1.0);
    identified = cpl_vector_new(1);
    cpl_vector_fill(identified, 1);

    kmclipm_combine_frames(cube1, NULL, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.29099, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "median",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.29099, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "min_max",
                           0.0, 0.0, 0, 2, 3, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.29099, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "sum",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.29099, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "ksigma",
                           1.0, 1.0, 3, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.29099, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    /* nz = 1, with noise, all methods */
    kmclipm_combine_frames(cube1, cube2, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.6, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "median",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.6, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "min_max",
                           0.0, 0.0, 0, 2, 3, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.6, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "sum",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.6, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "ksigma",
                           1.0, 1.0, 3, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.6, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_imagelist_delete(cube1); cube1 = NULL;
    cpl_imagelist_delete(cube2); cube2 = NULL;
    cpl_vector_delete(identified); identified = NULL;

    /* nz = 2, w/o noise, all methods */
    cube1         = cpl_imagelist_new();
    cube2         = cpl_imagelist_new();
    kmclipm_test_alloc_cube(cube1, 2, 2, 2);
    kmclipm_test_fill_cube(cube1, 1.0, 1.0);
    kmclipm_test_alloc_cube(cube2, 2, 2, 2);
    kmclipm_test_fill_cube(cube2, 0.1, 1.0);
    identified = cpl_vector_new(2);
    cpl_vector_fill(identified, 1);

    kmclipm_combine_frames(cube1, NULL, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(2), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "median",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(2), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "min_max",
                           0.0, 0.0, 0, 2, 3, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(2), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "sum",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(6, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(2), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "ksigma",
                           1.0, 1.0, 3, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(2), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    /* nz = 2, with noise, all methods */
    kmclipm_combine_frames(cube1, cube2, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);

    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.09244, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "median",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.09244, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "min_max",
                           0.0, 0.0, 0, 2, 3, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.09244, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "sum",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(6, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(2*1.09244, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "ksigma",
                           1.0, 1.0, 3, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.09244, cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_imagelist_delete(cube1); cube1 = NULL;
    cpl_imagelist_delete(cube2); cube2 = NULL;
    cpl_vector_delete(identified); identified = NULL;

    /* nz = 3, w/o noise, all methods */
    cube1         = cpl_imagelist_new();
    cube2         = cpl_imagelist_new();
    kmclipm_test_alloc_cube(cube1, 2, 2, 3);
    kmclipm_test_fill_cube(cube1, 1.0, 1.0);
    kmclipm_test_alloc_cube(cube2, 2, 2, 3);
    kmclipm_test_fill_cube(cube2, 0.1, 1.0);
    identified = cpl_vector_new(3);
    cpl_vector_fill(identified, 1);

    kmclipm_combine_frames(cube1, NULL, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "median",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "min_max",
                           0.0, 0.0, 0, 2, 3, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "sum",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(10.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, NULL, identified, "ksigma",
                           1.0, 1.0, 3, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    /* nz = 3, with noise, all methods */
    kmclipm_combine_frames(cube1, cube2, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "median",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "min_max",
                           0.0, 0.0, 0, 2, 3, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "sum",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(10.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    kmclipm_combine_frames(cube1, cube2, identified, "ksigma",
                           1.0, 1.0, 3, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(3.5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1/sqrt(3), cpl_image_get_mean(img2), 0.01);
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_imagelist_delete(cube1); cube1 = NULL;
    cpl_imagelist_delete(cube2); cube2 = NULL;
    cpl_vector_delete(identified); identified = NULL;

    cpl_test_error(CPL_ERROR_NONE);
    cpl_msg_set_level(my_level);

    /*
     * test with infinites
     */
    cube1         = cpl_imagelist_new();
    cpl_image *aa = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(aa, 1, 1, 1);
    cpl_image_set(aa, 2, 1, NAN);
    cpl_image_reject(aa, 2, 1);
    cpl_image_set(aa, 1, 2, 3);
    cpl_image_set(aa, 2, 2, 4);
    cpl_imagelist_set(cube1, aa, 0);
    identified = cpl_vector_new(1);
    cpl_vector_fill(identified, 1);

    kmclipm_combine_frames(cube1, NULL, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, 0.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.666666, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.52753, cpl_image_get_mean(img2), 0.01);
    cpl_test_eq(1, cpl_image_count_rejected(img1));
    cpl_test_eq(1, cpl_image_count_rejected(img2));
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_image_set(aa, 2, 1, 1./0.);
    kmclipm_combine_frames(cube1, NULL, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, 0.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.666666, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.52753, cpl_image_get_mean(img2), 0.01);
    cpl_test_eq(1, cpl_image_count_rejected(img1));
    cpl_test_eq(1, cpl_image_count_rejected(img2));
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_image_set(aa, 2, 1, -1./0.);
    kmclipm_combine_frames(cube1, NULL, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, 0.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.666666, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(1.52753, cpl_image_get_mean(img2), 0.01);
    cpl_test_eq(1, cpl_image_count_rejected(img1));
    cpl_test_eq(1, cpl_image_count_rejected(img2));
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cube2         = cpl_imagelist_new();
    aa = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(aa, 1, 1, .1);
    cpl_image_set(aa, 2, 1, .2);
    cpl_image_set(aa, 1, 2, .3);
    cpl_image_set(aa, 2, 2, .4);
    cpl_imagelist_set(cube2, aa, 0);
    kmclipm_combine_frames(cube1, cube2, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, 0.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(2.666666, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(0.2666, cpl_image_get_mean(img2), 0.01);
    cpl_test_eq(1, cpl_image_count_rejected(img1));
    cpl_test_eq(1, cpl_image_count_rejected(img2));
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_imagelist_delete(cube1); cube1 = NULL;
    cpl_imagelist_delete(cube2); cube2 = NULL;
    cpl_vector_delete(identified); identified = NULL;

    /*size = 2, one infinite*/
    cube1         = cpl_imagelist_new();
    aa = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(aa, 1, 1, 1);
    cpl_image_set(aa, 2, 1, NAN);
    cpl_image_set(aa, 1, 2, 3);
    cpl_image_set(aa, 2, 2, 4);
    cpl_imagelist_set(cube1, aa, 0);
    aa = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(aa, 1, 1, 5);
    cpl_image_set(aa, 2, 1, 6);
    cpl_image_set(aa, 1, 2, 7);
    cpl_image_set(aa, 2, 2, 8);
    cpl_imagelist_set(cube1, aa, 1);
    identified = cpl_vector_new(2);
    cpl_vector_fill(identified, 1);

    kmclipm_combine_frames(cube1, NULL, identified, "average",
                           0.0, 0.0, 0, 0, 0, &img1, &img2, -1.0);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_abs(5, cpl_image_get_mean(img1), 0.01);
    cpl_test_abs(4/sqrt(2), cpl_image_get_mean(img2), 0.01);
    cpl_test_eq(0, cpl_image_count_rejected(img1));
    cpl_test_eq(1, cpl_image_count_rejected(img2));
    cpl_image_delete(img1); img1 = NULL;
    cpl_image_delete(img2); img2 = NULL;

    cpl_imagelist_delete(cube1); cube1 = NULL;
    cpl_vector_delete(identified); identified = NULL;
}

void test_kmclipm_setup_grid()
{
    double          tol = 0.01;
    gridDefinition  gd;

    /* invalid tests */
    kmclipm_setup_grid(NULL, "NN", 1.9, KMOS_PIX_RESOLUTION, 0.);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    kmclipm_setup_grid(&gd, "gaga", 1.9, KMOS_PIX_RESOLUTION, 0.);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    kmclipm_setup_grid(&gd, "NN", -9.9, KMOS_PIX_RESOLUTION, 0.);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    /* valid tests */
    cpl_test_error(kmclipm_setup_grid(&gd, "NN", 1.9, KMOS_PIX_RESOLUTION, 0.));
    cpl_test_eq(gd.method, NEAREST_NEIGHBOR);

    cpl_test_error(kmclipm_setup_grid(&gd, "lwNN", 1.9, KMOS_PIX_RESOLUTION, 0.));
    cpl_test_eq(gd.method, LINEAR_WEIGHTED_NEAREST_NEIGHBOR);

    cpl_test_error(kmclipm_setup_grid(&gd, "swNN", 1.9, KMOS_PIX_RESOLUTION, 0.));
    cpl_test_eq(gd.method, SQUARE_WEIGHTED_NEAREST_NEIGHBOR);

    cpl_test_error(kmclipm_setup_grid(&gd, "NN", 1.9, KMOS_PIX_RESOLUTION, 0.));
    cpl_test_eq(gd.method, NEAREST_NEIGHBOR);
    cpl_test_abs(gd.neighborHood.distance, 1.9, tol);

    cpl_test_error(CPL_ERROR_NONE);
}
/*
void test_kmclipm_setup_grid_band_auto()
{
    double              tol = 0.01;
    gridDefinition      gd;
    cpl_propertylist    *pl = NULL;
    cpl_table           *tbl = NULL;

*/    /* invalid tests */
/*    kmclipm_setup_grid_band_auto(NULL, NULL, 0, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    kmclipm_setup_grid_band_auto(&gd, NULL, 0, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    pl = cpl_propertylist_new();
    kmclipm_setup_grid_band_auto(&gd, pl, 0, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    tbl = cpl_table_new(2);
    cpl_table_new_column(tbl, "K", CPL_TYPE_FLOAT);
    cpl_table_set_float(tbl, "K", 0, 1.956);
    cpl_table_set_float(tbl, "K", 1, 2.511);
    kmclipm_setup_grid_band_auto(&gd, pl, 0, tbl);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    kmclipm_setup_grid_band_auto(&gd, pl, 1, tbl);
    cpl_test_error(CPL_ERROR_DATA_NOT_FOUND);

*/    /* valid tests */
/*    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    gd.l.dim = 1000;
    kmclipm_setup_grid_band_auto(&gd, pl, 1, tbl);
    cpl_test_abs(gd.l.start, 1.956, tol);
    cpl_test_abs(gd.l.delta, 0.000270996, tol/10000);

    cpl_propertylist_delete(pl);
    cpl_table_delete(tbl);
    cpl_test_error(CPL_ERROR_NONE);
}
*/
void test_kmclipm_setup_grid_band()
{
    double          tol = 0.01;
    gridDefinition  gd;
    cpl_table       *tbl = NULL;

    /* invalid tests */
    kmclipm_setup_grid_band(NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    kmclipm_setup_grid_band(&gd, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    tbl = cpl_table_new(2);
    kmclipm_setup_grid_band(&gd, NULL, tbl);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* valid tests */
    cpl_table_new_column(tbl, "K", CPL_TYPE_FLOAT);
    cpl_table_set_float(tbl, "K", 0, 1.956);
    cpl_table_set_float(tbl, "K", 1, 2.511);
        gd.l.dim = 1000;
    kmclipm_setup_grid_band(&gd, "K", tbl);
    cpl_test_abs(gd.l.start, 1.956, tol);
    cpl_test_abs(gd.l.delta, 0.000270996, tol/10000);

    cpl_table_delete(tbl);
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_update_property_int()
{
    cpl_propertylist *pl = NULL;

    /* --- invalid tests --- */
    cpl_test_eq(CPL_ERROR_NULL_INPUT,
                kmclipm_update_property_int(NULL, NULL, 0, NULL));
    cpl_error_reset();

    pl = cpl_propertylist_new();

    cpl_test_eq(CPL_ERROR_NULL_INPUT,
                kmclipm_update_property_int(pl, NULL, 0, NULL));
    cpl_error_reset();

    /* --- valid tests --- */
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_update_property_int(pl, "name", 2, NULL));
    cpl_test_eq(cpl_propertylist_get_int(pl, "name"), 2);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 1);

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_update_property_int(pl, "name", 8, "gaga"));
    cpl_test_eq(cpl_propertylist_get_int(pl, "name"), 8);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 0);
    cpl_test_eq(strcmp(cpl_propertylist_get_comment(pl, "name"), "gaga"), 0);

    cpl_propertylist_delete(pl);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_update_property_double()
{
    cpl_propertylist    *pl = NULL;

    /* --- invalid tests --- */
    cpl_test_eq(CPL_ERROR_NULL_INPUT,
                kmclipm_update_property_double(NULL, NULL, 0, NULL));
    cpl_error_reset();

    pl = cpl_propertylist_new();

    cpl_test_eq(CPL_ERROR_NULL_INPUT,
                kmclipm_update_property_double(pl, NULL, 0, NULL));
    cpl_error_reset();

    /* --- valid tests --- */
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_update_property_double(pl, "name", 1.1, NULL));
    cpl_test_abs(cpl_propertylist_get_double(pl, "name"), 1.1, DBL_EPSILON);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 1);

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_update_property_double(pl, "name", 2.2, "gaga"));
    cpl_test_abs(cpl_propertylist_get_double(pl, "name"), 2.2, DBL_EPSILON);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 0);
    cpl_test_eq(strcmp(cpl_propertylist_get_comment(pl, "name"), "gaga"), 0);

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_update_property_double(pl, "name", NAN, "gaga"));
    cpl_test_abs(cpl_propertylist_get_double(pl, "name"), 0.0, DBL_EPSILON);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 0);
    cpl_test_eq(strcmp(cpl_propertylist_get_comment(pl, "name"),
                       "INVALID VALUE: was NaN"), 0);

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_update_property_double(pl, "name", 1.0/0.0, "gaga"));
    cpl_test_abs(cpl_propertylist_get_double(pl, "name"), 0.0, DBL_EPSILON);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 0);
    cpl_test_eq(strcmp(cpl_propertylist_get_comment(pl, "name"),
                       "INVALID VALUE: was +Inf"), 0);

    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_update_property_double(pl, "name", -1.0/0.0, "gaga"));
    cpl_test_abs(cpl_propertylist_get_double(pl, "name"), 0.0, DBL_EPSILON);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 0);
    cpl_test_eq(strcmp(cpl_propertylist_get_comment(pl, "name"),
                       "INVALID VALUE: was -Inf"), 0);

    cpl_propertylist_delete(pl);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_update_property_string()
{
    cpl_propertylist *pl        = NULL,
                     *mainh     = NULL;
    char             *keyword   = NULL;
    const char       *tmp_str   = NULL;

    /* --- invalid tests --- */
    kmclipm_update_property_string(NULL, NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    pl = cpl_propertylist_new();

    kmclipm_update_property_string(pl, NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    kmclipm_update_property_string(pl, "name", NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    kmclipm_update_property_string(pl, "name", "aga", NULL);
    cpl_test_eq(strcmp(cpl_propertylist_get_string(pl, "name"), "aga"), 0);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 1);

    kmclipm_update_property_string(pl, "name", "bgb", "gaga");
    cpl_test_eq(strcmp(cpl_propertylist_get_string(pl, "name"), "bgb"), 0);
    cpl_test_eq(cpl_propertylist_get_comment(pl, "name") == NULL, 0);
    cpl_test_eq(strcmp(cpl_propertylist_get_comment(pl, "name"), "gaga"), 0);

    keyword = cpl_sprintf("%s%d%s",IFU_FILTID_PREFIX, 1, IFU_FILTID_POSTFIX);
    mainh = cpl_propertylist_new();
    tmp_str = cpl_propertylist_get_string(mainh, keyword);
    if (tmp_str != NULL) {
        kmclipm_update_property_string(pl,
                                keyword,
                                tmp_str,
                                cpl_propertylist_get_comment(mainh,
                                                             keyword));
        cpl_test_error(CPL_ERROR_NONE);
    } else {
        cpl_error_reset();
    }

    kmclipm_update_property_string(mainh, keyword, "F", "gagacomment");
    tmp_str = cpl_propertylist_get_string(mainh, keyword);
    if (tmp_str != NULL) {
        kmclipm_update_property_string(pl,
                                keyword,
                                tmp_str,
                                cpl_propertylist_get_comment(mainh,
                                                             keyword));
        cpl_test_error(CPL_ERROR_NONE);
    } else {
        cpl_error_reset();
    }
    cpl_test_eq(strcmp(cpl_propertylist_get_string(pl, keyword), "F"), 0);
    cpl_test_eq(cpl_propertylist_get_comment(pl, keyword) == NULL, 0);
    cpl_test_eq(strcmp(cpl_propertylist_get_comment(pl, keyword), "gagacomment"), 0);
    cpl_free(keyword); keyword = NULL;


    cpl_propertylist_delete(pl);
    cpl_propertylist_delete(mainh);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_strip_angle()
{
    double a = 0.0, tol = 0.01;

    /* --- invalid tests --- */
    kmclipm_strip_angle(NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    a = 20;
    kmclipm_strip_angle(&a);
    cpl_test_abs(a, 20, tol);

    a = 190.1;
    kmclipm_strip_angle(&a);
    cpl_test_abs(a, 190.1, tol);

    a = 360;
    kmclipm_strip_angle(&a);
    cpl_test_abs(a, 0, tol);

    a = 400;
    kmclipm_strip_angle(&a);
    cpl_test_abs(a, 40, tol);

    a = -360;
    kmclipm_strip_angle(&a);
    cpl_test_abs(a, 0, tol);

    a = -400;
    kmclipm_strip_angle(&a);
    cpl_test_abs(a, 320, tol);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_reconstruct_nnlut_get_timestamp()
{
/*erwtest*/
    /* --- invalid tests --- */
    /* --- valid tests --- */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_compare_timestamps()
{
/*erwtest*/
    /* --- invalid tests --- */
    kmclipm_compare_timestamps(NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* --- valid tests --- */
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_extract_bounds()
{
    cpl_propertylist    *pl     = NULL;
    int                 *ret    = NULL,
                        i       = 0;

    /* --- invalid tests --- */
    kmclipm_extract_bounds(NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);


    /* --- valid tests --- */
    pl = cpl_propertylist_new();
    cpl_test_nonnull(ret = kmclipm_extract_bounds(pl));
    for (i = 0; i < 2 * KMOS_NR_IFUS; i++) cpl_test_eq(-1, ret[0]);
    cpl_free(ret); ret = NULL;

    cpl_propertylist_update_int(pl, "ESO PRO BOUND IFU24_L", 100);
    cpl_propertylist_update_int(pl, "ESO PRO BOUND IFU24_R", 200);
    cpl_test_nonnull(ret = kmclipm_extract_bounds(pl));
    for (i = 0; i < 2 * (KMOS_NR_IFUS-1); i++) cpl_test_eq(-1, ret[0]);
    cpl_test_eq(100, ret[2*KMOS_NR_IFUS-2]);
    cpl_test_eq(200, ret[2*KMOS_NR_IFUS-1]);
    cpl_free(ret); ret = NULL;

    cpl_propertylist_delete(pl); pl = NULL;
    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_propertylist_load() {
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    cpl_test_null(kmclipm_propertylist_load(NULL, -1));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    cpl_propertylist *pl=cpl_propertylist_new();
    char *my_path = cpl_sprintf("%s/test_data/ttt.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_propertylist_delete(pl); pl = NULL;

    cpl_test_null(kmclipm_propertylist_load(my_path, -1));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    cpl_test_nonnull(pl = kmclipm_propertylist_load(my_path, 0));

    cpl_free(my_path);
    cpl_propertylist_delete(pl); pl = NULL;

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_image_load() {
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    cpl_test_null(kmclipm_image_load(NULL, CPL_TYPE_FLOAT, 0, -1));
    if (((cpl_version_get_major() == 5) && (cpl_version_get_minor() >= 3)) ||
        (cpl_version_get_major() > 5))
    {
        cpl_test_error(CPL_ERROR_NULL_INPUT);
    } else {
        cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    }

    cpl_image *img=cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    char *my_path = cpl_sprintf("%s/test_data/ttt.fits", ".");
    cpl_image_save(img, my_path, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);
    cpl_image_delete(img); img = NULL;

    cpl_test_null(kmclipm_image_load(my_path, CPL_TYPE_FLOAT, 0, -1));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    cpl_test_nonnull(img = kmclipm_image_load(my_path, CPL_TYPE_FLOAT, 0, 0));

    cpl_free(my_path);
    cpl_image_delete(img); img = NULL;

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_image_load_window() {
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    cpl_test_null(kmclipm_image_load_window(NULL, CPL_TYPE_FLOAT, 0, -1, -1, -1, -1, -1));
    if (((cpl_version_get_major() == 5) && (cpl_version_get_minor() >= 3)) ||
        (cpl_version_get_major() > 5))
    {
        cpl_test_error(CPL_ERROR_NULL_INPUT);
    } else {
        cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    }

    cpl_image *img=cpl_image_new(10, 10, CPL_TYPE_FLOAT);
    cpl_test_error(CPL_ERROR_NONE);
    char *my_path = cpl_sprintf("%s/test_data/ttt.fits", ".");
    cpl_image_save(img, my_path, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_image_delete(img); img = NULL;

    cpl_test_null(kmclipm_image_load_window(my_path, CPL_TYPE_FLOAT, 0, -1, -1, -1, -1, -1));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    cpl_test_nonnull(img = kmclipm_image_load_window(my_path, CPL_TYPE_FLOAT, 0, 0, 1, 1, 5, 5));

    cpl_free(my_path);
    cpl_image_delete(img); img = NULL;

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_imagelist_load() {
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    cpl_test_null(kmclipm_imagelist_load(NULL, CPL_TYPE_FLOAT, -1));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    cpl_imagelist *cube=cpl_imagelist_new();
    char *my_path = cpl_sprintf("%s/test_data/ttt.fits", ".");
    cpl_imagelist_save(cube, my_path, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);
    cpl_imagelist_delete(cube); cube = NULL;

    cpl_test_null(kmclipm_imagelist_load(my_path, CPL_TYPE_FLOAT, -1));
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    cpl_test_nonnull(cube = kmclipm_imagelist_load(my_path, CPL_TYPE_FLOAT, 0));

    cpl_free(my_path);
    cpl_imagelist_delete(cube); cube = NULL;

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_table_load() {
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    cpl_test_null(kmclipm_table_load(NULL, -1, 0));
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    cpl_table *tbl=cpl_table_new(5);
    cpl_table_new_column(tbl, "aaa", CPL_TYPE_FLOAT);
    cpl_propertylist *pl=cpl_propertylist_new();
    char *my_path = cpl_sprintf("%s/test_data/ttt.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_propertylist *plsub=cpl_propertylist_new();
    cpl_table_save(tbl, NULL, plsub, my_path, CPL_IO_EXTEND);
    cpl_table_delete(tbl); tbl = NULL;
    cpl_propertylist_delete(pl); pl = NULL;
    cpl_propertylist_delete(plsub); plsub = NULL;

    cpl_test_null(kmclipm_table_load(my_path, -1, 0));
    cpl_test_error(CPL_ERROR_ACCESS_OUT_OF_RANGE);

    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    cpl_test_nonnull(tbl = kmclipm_table_load(my_path, 1, 1));

    cpl_free(my_path);
    cpl_table_delete(tbl); tbl = NULL;

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_image_save()
{
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    kmclipm_image_save(NULL, "img.fits", CPL_BPP_IEEE_FLOAT, NULL,
                       CPL_IO_CREATE, NAN);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    cpl_image *img = cpl_image_new(10,10,CPL_TYPE_FLOAT);
    char *my_path = cpl_sprintf("%s/test_data/imgttt.fits", ".");
    kmclipm_image_save(img, my_path, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE, NAN);

    cpl_image_delete(img);
    cpl_free(my_path);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_imagelist_save()
{
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    kmclipm_imagelist_save(NULL, "img.fits", CPL_BPP_IEEE_FLOAT, NULL,
                           CPL_IO_CREATE, NAN);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    cpl_imagelist *ccc = cpl_imagelist_new();
    char *my_path = cpl_sprintf("%s/test_data/imgttt.fits", ".");
    kmclipm_imagelist_save(ccc, my_path, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE, NAN);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    cpl_image *img = cpl_image_new(10,10,CPL_TYPE_FLOAT);
    cpl_imagelist_set(ccc, img, 0);
    kmclipm_imagelist_save(ccc, my_path, CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE, NAN);

    cpl_free(my_path);
    cpl_imagelist_delete(ccc);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_set_cal_path()
{
    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    kmclipm_set_cal_path(NULL, 2);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    kmclipm_set_cal_path("aaa", 2);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_msg_set_level(my_level);

    /* --- valid tests --- */
    kmclipm_set_cal_path("aaa", TRUE);
    cpl_test_error(CPL_ERROR_NONE);
    kmclipm_set_cal_path("aaa", FALSE);
    cpl_test_error(CPL_ERROR_NONE);

}

/**
    @brief  Executes the unit tests for all functions in the module @ref kmclipm_functions

    @return Error code, 0 for NONE.
 */
int main()
{
    /* For DEVELOPERS:
     * to omit all CPL_MSG_INFO output, apply following statement in the shell
     * before executing "make check":
     *      setenv CPL_MSG_LEVEL warning
     *  (or export CPL_MSG_LEVEL=warning)
     */

    cpl_test_init("<usd-help@eso.org>", my_level);

    char *my_path = cpl_sprintf("%s/test_data", ".");
    mkdir(my_path, 0777);

    cpl_free(my_path);

    cpl_msg_set_level_from_env();
    my_level = cpl_msg_get_level();

    test_kmclipm_load_image_with_extensions();
    test_kmclipm_gaussfit_2d();
    test_kmclipm_reconstruct();
    /* test_kmclipm_shift(); */
    /* test_kmclipm_rotate(); */
    test_kmclipm_reject_deviant();
    test_kmclipm_make_image();
    test_kmclipm_combine_vector();
    test_kmclipm_combine_frames();
    test_kmclipm_setup_grid();
/*    test_kmclipm_setup_grid_band_auto();*/
    test_kmclipm_setup_grid_band();
    test_kmclipm_update_property_int();
    test_kmclipm_update_property_double();
    test_kmclipm_update_property_string();
    test_kmclipm_strip_angle();
    test_kmclipm_reconstruct_nnlut_get_timestamp();
    test_kmclipm_compare_timestamps();
    test_kmclipm_extract_bounds();
    test_kmclipm_propertylist_load();
    test_kmclipm_image_load();
    test_kmclipm_image_load_window();
    test_kmclipm_imagelist_load();
    test_kmclipm_table_load();
    test_kmclipm_image_save();
    test_kmclipm_imagelist_save();
    test_kmclipm_set_cal_path();

    cpl_msg_info("","All tests done.");
    cpl_msg_set_level(CPL_MSG_WARNING);
    return cpl_test_end(0);
}

/** @} */
