
/*********************************************************************
 * E.S.O. - VLT project
 *
 * "@(#) $Id: kmclipm_vector.c,v 1.12 2013-07-31 10:35:34 aagudo Exp $"
 *
 * Functions that are implemented as recipes. They are also useedd in KMOS
 * data reduction pipeline.
 *
 * who       when        what
 * --------  ----------  ----------------------------------------------
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/*------------------------------------------------------------------------------
 *                                  Includes, etc
 *----------------------------------------------------------------------------*/
#define _ISOC99_SOURCE

#include <math.h>

#include <cpl.h>

#include "kmclipm_priv_error.h"
#include "kmclipm_vector.h"
#include "kmclipm_math.h"

/* CPL_SORT_ASCENDING is introduced only after CPL 5.3.0 */
#ifndef CPL_SORT_ASCENDING
    #define CPL_SORT_ASCENDING 1
#endif

/*------------------------------------------------------------------------------
 *                                  Definitions
 *----------------------------------------------------------------------------*/
/**
    @brief
        Create a new kmclipm_vector.

    @param n    Number of elements of the kmclipm_vector

    @return     1 newly allocated kmclipm_vector or NULL in case of an error

    The returned object must be deallocated using @li kmclipm_vector_delete().
    In opposite to cpl_vector_new, the data values are initialised to zero,
    and all values are not marked as rejected

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_ILLEGAL_INPUT if n is negative or zero
*/
kmclipm_vector* kmclipm_vector_new(int n)
{
    kmclipm_vector *kv = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(n >= 1,
                                  CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            kv = (kmclipm_vector*)cpl_malloc(sizeof(kmclipm_vector)));

        KMCLIPM_TRY_EXIT_IFN(
            kv->data = cpl_vector_new(n));

        KMCLIPM_TRY_EXIT_IFN(CPL_ERROR_NONE ==
            cpl_vector_fill(kv->data, 0));

        KMCLIPM_TRY_EXIT_IFN(
            kv->mask = cpl_vector_new(n));

        KMCLIPM_TRY_EXIT_IFN(CPL_ERROR_NONE ==
            cpl_vector_fill(kv->mask, 1));
    }
    KMCLIPM_CATCH
    {
        if (kv != NULL) {
            cpl_vector_delete(kv->data); kv->data = NULL;
            cpl_vector_delete(kv->mask); kv->mask = NULL;
        }
        cpl_free(kv); kv = NULL;
    }

    return kv;
}

/**
    @brief
        Create a new kmclipm_vector out of a data cpl_vector.

    @param data    The cpl_vector to wrap.

    @return        1 newly allocated kmclipm_vector or NULL in case of an error

    The returned object must be deallocated using @li kmclipm_vector_delete().
    (Do not deallocate data with cpl_vector_delete()!)

    All NaNs and Infs in @c data will be marked as rejected. The rejected data
    value will remain unchanged.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c data is NULL
*/
kmclipm_vector* kmclipm_vector_create(cpl_vector *data)
{
    kmclipm_vector *kv      = NULL;
    int             i       = 0,
                    n       = 0;
    double          *pdata  = NULL,
                    *pmask  = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(data != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            kv = (kmclipm_vector*)cpl_malloc(sizeof(kmclipm_vector)));

        kv->data = data;

        n = cpl_vector_get_size(data);
        KMCLIPM_TRY_EXIT_IFN(
            kv->mask = cpl_vector_new(n));

        KMCLIPM_TRY_EXIT_IFN(CPL_ERROR_NONE ==
            cpl_vector_fill(kv->mask, 1));

        KMCLIPM_TRY_EXIT_IFN(
            pdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < n; i++)
        {
            if (kmclipm_is_nan_or_inf(pdata[i])) {
                /*pdata[i] = NAN;*/
                pmask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        if (kv != NULL) {
            cpl_vector_delete(kv->data); kv->data = NULL;
            cpl_vector_delete(kv->mask); kv->mask = NULL;
        }
        cpl_free(kv); kv = NULL;
    }

    return kv;
}

/**
    @brief
        Create a new kmclipm_vector out of a data and mask cpl_vector.

    @param data    The data cpl_vector to wrap.
    @param mask    The mask cpl_vector to wrap.

    @return        1 newly allocated kmclipm_vector or NULL in case of an error

    The returned object must be deallocated using @li kmclipm_vector_delete().
    (Do not deallocate data or mask with cpl_vector_delete()!)

    All NaNs and Infs in @c data will be marked as rejected. The rejected data
    value will remain unchanged.
    All NaNs and Infs in @c mask are set rejected.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c data or mask is NULL
    @li CPL_ERROR_ILLEGAL_INPUT if the size of @c data and mask isn't the same.
*/
kmclipm_vector* kmclipm_vector_create2(cpl_vector *data, cpl_vector *mask)
{
    kmclipm_vector *kv      = NULL;
    int             i       = 0,
                    n       = 0;
    double          *pdata  = NULL,
                    *pmask  = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG((data != NULL) & (mask != NULL),
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_CHECK_AUTOMSG(cpl_vector_get_size(data) ==
                                  cpl_vector_get_size(mask),
                                  CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            kv = (kmclipm_vector*)cpl_malloc(sizeof(kmclipm_vector)));

        kv->data = data;
        kv->mask = mask;
        n = cpl_vector_get_size(data);

        KMCLIPM_TRY_EXIT_IFN(
            pdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < n; i++)
        {
            if (kmclipm_is_nan_or_inf(pmask[i])) {
                /*pdata[i] = NAN;*/
                pmask[i] = 0.;
            } else if (pmask[i] >= 0.5) {
                if (kmclipm_is_nan_or_inf(pdata[i])) {
                    /*pdata[i] = NAN;*/
                    pmask[i] = 0.;
                } else {
                    pmask[i] = 1;
                }
            } else {
                /*pdata[i] = NAN;*/
                pmask[i] = 0;
            }
        }
    }
    KMCLIPM_CATCH
    {
        if (kv != NULL) {
            cpl_vector_delete(kv->data); kv->data = NULL;
            cpl_vector_delete(kv->mask); kv->mask = NULL;
        }
        cpl_free(kv); kv = NULL;
    }

    return kv;
}

/**
    @brief
        Delete a kmclipm_vector

    @param kv    kmclipm_vector to delete.

    @return     void

    If the kmclipm_vector kv is NULL, nothing is done and no error is set.
*/
void kmclipm_vector_delete(kmclipm_vector *kv)
{
    KMCLIPM_TRY
    {
        if (kv != NULL) {
            cpl_vector_delete(kv->data); kv->data = NULL;
            cpl_vector_delete(kv->mask); kv->mask = NULL;
            cpl_free(kv);
        }
    }
    KMCLIPM_CATCH
    {
    }

}

/**
    @brief
        This function duplicates an existing kmclipm_vector and allocates memory.

    @param kv       Input kmclipm_vector

    @return         A newly allocated kmclipm_vector or NULL in case of an error

    The returned object must be deallocated using kmclipm_vector_delete()

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
kmclipm_vector* kmclipm_vector_duplicate(const kmclipm_vector *kv)
{
    kmclipm_vector *kv_dup  = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            kv_dup = (kmclipm_vector*)cpl_malloc(sizeof(kmclipm_vector)));

        kv_dup->data = cpl_vector_duplicate(kv->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        kv_dup->mask = cpl_vector_duplicate(kv->mask);
        KMCLIPM_TRY_CHECK_ERROR_STATE();
    }
    KMCLIPM_CATCH
    {
        if (kv_dup != NULL) {
            cpl_vector_delete(kv_dup->data); kv_dup->data = NULL;
            cpl_vector_delete(kv_dup->mask); kv_dup->mask = NULL;
        }
        cpl_free(kv_dup); kv_dup = NULL;
    }

    return kv_dup;
}

/**
    @brief
        Set an element of the kmclipm_vector.

    @param kv       Input kmclipm_vector
    @param pos 	    The index of the element (0 to nelem-1)
    @param val 	    The value to set in the vector

    @return         A newly allocated kmclipm_vector or NULL in case of an error

    If the value is Nan or Inf the element is marked as rejected.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ACCESS_OUT_OF_RANGE if the specified position is out of the
    vector
*/
cpl_error_code kmclipm_vector_set(kmclipm_vector *kv, int pos, double val)
{
    cpl_error_code  err    = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_CHECK_AUTOMSG((pos >= 0) &&
                                  (pos < cpl_vector_get_size(kv->data)),
                                  CPL_ERROR_ACCESS_OUT_OF_RANGE);

        cpl_vector_set(kv->data, pos, val);
        if (kmclipm_is_nan_or_inf(val)) {
            cpl_vector_set(kv->mask, pos, 0.);
        } else {
            cpl_vector_set(kv->mask, pos, 1.);
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief Get an element of the kmclipm_vector.

    @param kv       Input kmclipm_vector
    @param pos 	    The index of the element (0 to nelem-1)
    @param rej      1 if the pixel is bad, 0 if good, negative on error

    @return         The element value

    The value is returned regardless if it is rejected or not! By examining
    @c rej the status of the element can be examined.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ACCESS_OUT_OF_RANGE if the specified position is out of the
    vector
*/
double kmclipm_vector_get(const kmclipm_vector *kv, int pos, int *rej)
{
    double  ret = 0.;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_CHECK_AUTOMSG((pos >= 0) && (pos < cpl_vector_get_size(kv->data)),
                                  CPL_ERROR_ACCESS_OUT_OF_RANGE);

        ret = cpl_vector_get(kv->data, pos);
        if (rej != NULL) {
            if (cpl_vector_get(kv->mask, pos) > 0.5) {
                /* value isn't rejected */
                *rej = 0;
            } else {
                /* value is rejected */
                *rej = 1;
            }
        }
    }
    KMCLIPM_CATCH
    {
        ret = 0.;
        if (rej != NULL) {
            *rej = -1;
        }
    }

    return ret;
}

/**
    @brief Get a copy of the mask of kmclipm_vector.

    @param kv       Input kmclipm_vector

    @return         The mask of the input vector

    The output vector contains 1 for non-rejected elements and 0 for rejected
    elements.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ACCESS_OUT_OF_RANGE if the specified position is out of the
    vector
*/
cpl_vector* kmclipm_vector_get_mask(const kmclipm_vector *kv)
{
    cpl_vector  *mask   = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            mask = cpl_vector_duplicate(kv->mask));
    }
    KMCLIPM_CATCH
    {
        cpl_vector_delete(mask); mask = NULL;
    }

    return mask;
}

/**
    @brief Get the pointer to the mask  of the kmclipm_vector.

    @param kv       Input kmclipm_vector

    @return         Pointer to the mask of the input vector

    The output vector contains 1 for non-rejected elements and 0 for rejected
    elements.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ACCESS_OUT_OF_RANGE if the specified position is out of the
    vector
*/
cpl_vector* kmclipm_vector_get_bpm(kmclipm_vector *kv)
{
    cpl_vector  *mask    = NULL;
    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            mask = kv->mask);
    }
    KMCLIPM_CATCH
    {
        mask = NULL;
    }

    return mask;
}

/**
    @brief Set the rejected elements in an kmclipm_vector as defined in a mask.

    @param kv       Input kmclipm_vector
    @param mask     The mask defining the bad elements.
    @param keep     TRUE: keep already rejected values and just update the
                    non-rejected ones. FALSE: apply the supplied @c mask.

    @return         the @li _cpl_error_code_ or CPL_ERROR_NONE

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv or @c mask is NULL
    @li CPL_ERROR_ILLEGAL_INPUT if @c kv and @c mask don't have same length
*/
cpl_error_code kmclipm_vector_reject_from_mask(kmclipm_vector *kv,
                                               const cpl_vector *mask,
                                               int keep)
{
    int             ret         = CPL_ERROR_NONE,
                    size        = 0,
                    i           = 0;
    double          *pkvmask    = NULL;
    const double    *pmask      = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG((kv != NULL) &&
                                  (mask != NULL),
                                  CPL_ERROR_NULL_INPUT);

        size = cpl_vector_get_size(kv->data);

        KMCLIPM_TRY_CHECK_AUTOMSG(size == cpl_vector_get_size(mask),
                                  CPL_ERROR_ILLEGAL_INPUT);
        KMCLIPM_TRY_CHECK_AUTOMSG((keep == 0) || (keep == 1),
                                  CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));
        KMCLIPM_TRY_EXIT_IFN(
            pmask = cpl_vector_get_data_const(mask));

        for (i = 0; i < size; i++) {
            if ((!keep) ||
                ((keep) && (pkvmask[i] > 0.5)))
            {
                pkvmask[i] = pmask[i];
            }
        }
        KMCLIPM_TRY_CHECK_ERROR_STATE();
    }
    KMCLIPM_CATCH
    {
        ret = cpl_error_get_code();
    }

    return ret;
}

/**
    @brief
        Count the number of rejected elements in a kmclipm_vector.

    @param kv    The input kmclipm_vector

    @return      The number of rejected values or -1 if the input vector is NULL

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
int kmclipm_vector_count_rejected(const kmclipm_vector *kv)
{
    int     cnt     = 0,
            i       = 0;
    double  *pmask = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < cpl_vector_get_size(kv->mask); i++) {
            if (pmask[i] == 0) {
                cnt++;
            }
        }
    }
    KMCLIPM_CATCH
    {
        cnt = -1;
    }

    return cnt;
}

/**
    @brief
        Count the number of non-rejected elements in a kmclipm_vector.

    @param kv    The input kmclipm_vector

    @return      The number of rejected values or -1 if the input vector is NULL

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
int kmclipm_vector_count_non_rejected(const kmclipm_vector *kv)
{
    int     cnt     = 0,
            i       = 0,
            size    = 0;
    double  *pmask = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        size = cpl_vector_get_size(kv->data);

        KMCLIPM_TRY_EXIT_IFN(
            pmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < size; i++) {
            if (pmask[i] == 0) {
                cnt++;
            }
        }

        cnt = size - cnt;
    }
    KMCLIPM_CATCH
    {
        cnt = -1;
    }

    return cnt;
}

/**
    @brief
        Test if a value is good or bad.

    @param kv    The input kmclipm_vector
    @param n     The position of the value in the vector (first value is 0)

    @return      1 if the value is rejected, 0 if it is good, -1 on error

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ACCESS_OUT_OF_RANGE if the specified position is out of the
    vector
*/
int kmclipm_vector_is_rejected(const kmclipm_vector *kv, int n)
{
    int     ret     = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_CHECK_AUTOMSG((n >= 0) && (n < cpl_vector_get_size(kv->data)),
                                  CPL_ERROR_ACCESS_OUT_OF_RANGE);

        if (cpl_vector_get(kv->mask, n) > 0.5) {
            ret = FALSE;
        } else {
            ret = TRUE;
        }
    }
    KMCLIPM_CATCH
    {
        ret = -1;
    }

    return ret;
}

/**
    @brief
        Set a value as rejected in a kmclipm_vector.

    @param kv    The input kmclipm_vector
    @param n     The position of the value in the vector (first value is 0)

    @return      the @li _cpl_error_code_ or CPL_ERROR_NONE

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ACCESS_OUT_OF_RANGE if the specified position is out of the
    vector
*/
cpl_error_code kmclipm_vector_reject(kmclipm_vector *kv, int n)
{
    int     ret     = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_CHECK_AUTOMSG((n >= 0) && (n < cpl_vector_get_size(kv->data)),
                                  CPL_ERROR_ILLEGAL_INPUT);

/*        cpl_vector_set(kv->data, n, NAN);*/
        cpl_vector_set(kv->mask, n, 0);
        KMCLIPM_TRY_CHECK_ERROR_STATE();
    }
    KMCLIPM_CATCH
    {
        ret = cpl_error_get_code();
    }

    return ret;
}

/**
    @brief
        Extract a sub_vector from a kmclipm_vector.

    @param kv       The input kmclipm_vector
    @param istart 	Start index (from 0 to number of elements - 1)
    @param istop 	Stop index (from 0 to number of elements - 1)

    @return      A newly allocated kmclipm_vector or NULL in case of an error

    Rejected elements are also extracted.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ILLEGAL_INPUT if istop <= istart
*/
kmclipm_vector* kmclipm_vector_extract(const kmclipm_vector *kv,
                                       int istart,
                                       int istop)
{
    kmclipm_vector  *kv_out = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_CHECK_AUTOMSG(istop > istart,
                                  CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            kv_out = (kmclipm_vector*)cpl_malloc(sizeof(kmclipm_vector)));

        KMCLIPM_TRY_EXIT_IFN(
            kv_out->data = cpl_vector_extract(kv->data, istart, istop, 1));

        KMCLIPM_TRY_EXIT_IFN(
            kv_out->mask = cpl_vector_extract(kv->mask, istart, istop, 1));
    }
    KMCLIPM_CATCH
    {
        if (kv_out != NULL) {
            cpl_vector_delete(kv_out->data); kv_out->data = NULL;
            cpl_vector_delete(kv_out->mask); kv_out->mask = NULL;
        }
        cpl_free(kv_out); kv_out = NULL;
    }

    return kv_out;
}

/**
    @brief
        Creates a cpl_vector out of a kmclipm_vector with non-rejected values.

    @param kv    The input kmclipm_vector

    @return      The extracted cpl_vector or NULL.

    The length of the returned cpl_vector may be, depending on the number of
    rejected values shorter than the input kmclipm_vector!
    If all values are rejected NULL is returned but no error is set.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
cpl_vector* kmclipm_vector_create_non_rejected(const kmclipm_vector *kv)
{
    int             cnt         = 0,
                    i           = 0,
                    j           = 0,
                    n           = 0;
    const double    *pkvmask    = NULL,
                    *pkvdata    = NULL;
    double          *pret       = NULL;
    cpl_vector      *ret        = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        n = cpl_vector_get_size(kv->data);
        cnt = kmclipm_vector_count_rejected(kv);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        if (n-cnt > 0) {
            KMCLIPM_TRY_EXIT_IFN(
                ret = cpl_vector_new(n-cnt));
            KMCLIPM_TRY_EXIT_IFN(
                pret = cpl_vector_get_data(ret));
            KMCLIPM_TRY_EXIT_IFN(
                pkvdata = cpl_vector_get_data_const(kv->data));
            KMCLIPM_TRY_EXIT_IFN(
                pkvmask = cpl_vector_get_data_const(kv->mask));
            for (i = 0; i < n; i++) {
                if (pkvmask[i] > 0.5) {
                    pret[j++] = pkvdata[i];
                }
            }
        }
    }
    KMCLIPM_CATCH
    {
        cpl_vector_delete(ret); ret = NULL;
    }

    return ret;
}

/**
    @brief
        Assert that rejected values on both vectors are the same.

    @param kv1   1st input kmclipm_vector
    @param kv2   2nd input kmclipm_vector

    @return      CPL_ERROR_NONE or error code.

    It is asserted that all rejected elements in @c kv1 are also rejected in
    @c kv2 and vice versa.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if any of the inputs is NULL
    @li CPL_ERROR_ILLEGAL_INPUT if the inputs don't have the same length
*/
cpl_error_code     kmclipm_vector_adapt_rejected(kmclipm_vector *kv1,
                                                 kmclipm_vector *kv2)
{
    cpl_error_code  err         = CPL_ERROR_NONE;
    int             size        = 0,
                    i           = 0;
    double          *pkv1mask   = NULL,
                    *pkv2mask   = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG((kv1 != NULL) && (kv2 != NULL),
                                  CPL_ERROR_NULL_INPUT);

        size = cpl_vector_get_size(kv1->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_CHECK_AUTOMSG(size == cpl_vector_get_size(kv2->data),
                       CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkv1mask = cpl_vector_get_data(kv1->mask));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2mask = cpl_vector_get_data(kv2->mask));

        for (i = 0; i < size; i++)  {
            if (pkv1mask[i] < 0.5) {
                pkv2mask[i] = 0.;
            } else if (pkv2mask[i] < 0.5) {
                pkv1mask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Add a kmclipm_vector to another.

    @param kv1      First kmclipm_vector (modified)
    @param kv2      Second kmclipm_vector

    @return         CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    The second vector is added to the first one. The input first vector is modified.

    The input vectors must have the same size.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if any input pointer is NULL
    @li CPL_ERROR_INCOMPATIBLE_INPUT if kv1 and kv2 have different sizes
*/
cpl_error_code kmclipm_vector_add(kmclipm_vector *kv1, const kmclipm_vector *kv2)
{
    int             i           = 0,
                    size        = 0;
    double          *pkv1data   = NULL,
                    *pkv1mask   = NULL,
                    *pkv2data   = NULL,
                    *pkv2mask   = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG((kv1 != NULL) && (kv2 != NULL),
                                  CPL_ERROR_NULL_INPUT);

        size = cpl_vector_get_size(kv1->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_CHECK_AUTOMSG(size == cpl_vector_get_size(kv2->data),
                       CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkv1data = cpl_vector_get_data(kv1->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv1mask = cpl_vector_get_data(kv1->mask));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2data = cpl_vector_get_data(kv2->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2mask = cpl_vector_get_data(kv2->mask));

        for (i = 0; i < size; i++) {
            if ((pkv1mask[i] > 0.5) && (pkv2mask[i] > 0.5)) {
                /* add elements (if element in kv1 is rejected it stays anyway
                   rejected) */
                pkv1data[i] += pkv2data[i];

                if (kmclipm_is_nan_or_inf(pkv1data[i])) {
                    /*pkvdata[i] = NAN;*/
                    pkv1mask[i] = 0.;
                }
            } else {
                /* element of kv2 is rejected, reject also element in kv1 */
                pkv1mask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Subtract two kmclipm_vectors.

    @param kv1  First kmclipm_vector (modified)
    @param kv2 	Second kmclipm_vector

    @return     CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    The second vector is subtracted from the first one. The input first vector
    is modified.

    The input vectors must have the same size.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if any input pointer is NULL
    @li CPL_ERROR_INCOMPATIBLE_INPUT if kv1 and kv2 have different sizes
*/
cpl_error_code kmclipm_vector_subtract(kmclipm_vector *kv1, const kmclipm_vector *kv2)
{
    int             i           = 0,
                    size        = 0;
    double          *pkv1data   = NULL,
                    *pkv1mask   = NULL,
                    *pkv2data   = NULL,
                    *pkv2mask   = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG((kv1 != NULL) && (kv2 != NULL),
                                  CPL_ERROR_NULL_INPUT);

        size = cpl_vector_get_size(kv1->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_CHECK_AUTOMSG(size == cpl_vector_get_size(kv2->data),
                       CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkv1data = cpl_vector_get_data(kv1->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv1mask = cpl_vector_get_data(kv1->mask));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2data = cpl_vector_get_data(kv2->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2mask = cpl_vector_get_data(kv2->mask));

        for (i = 0; i < size; i++) {
            if ((pkv1mask[i] > 0.5) && (pkv2mask[i] > 0.5)) {
                /* subtract elements (if element in kv1 is rejected it stays anyway
                   rejected) */
                pkv1data[i] -= pkv2data[i];

                if (kmclipm_is_nan_or_inf(pkv1data[i])) {
                    /*pkvdata[i] = NAN;*/
                    pkv1mask[i] = 0.;
                }
            } else {
                /* element of kv2 is rejected, reject also element in kv1 */
                pkv1mask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Multiply two kmclipm_vectors.

    @param kv1  First kmclipm_vector (modified)
    @param kv2 	Second kmclipm_vector

    @return         CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    The input first vector is modified.

    The input vectors must have the same size.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if any input pointer is NULL
    @li CPL_ERROR_INCOMPATIBLE_INPUT if kv1 and kv2 have different sizes
*/
cpl_error_code kmclipm_vector_multiply(kmclipm_vector *kv1, const kmclipm_vector *kv2)
{
    int             i           = 0,
                    size        = 0;
    double          *pkv1data   = NULL,
                    *pkv1mask   = NULL,
                    *pkv2data   = NULL,
                    *pkv2mask   = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG((kv1 != NULL) && (kv2 != NULL),
                                  CPL_ERROR_NULL_INPUT);

        size = cpl_vector_get_size(kv1->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_CHECK_AUTOMSG(size == cpl_vector_get_size(kv2->data),
                       CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkv1data = cpl_vector_get_data(kv1->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv1mask = cpl_vector_get_data(kv1->mask));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2data = cpl_vector_get_data(kv2->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2mask = cpl_vector_get_data(kv2->mask));

        for (i = 0; i < size; i++) {
            if ((pkv1mask[i] > 0.5) && (pkv2mask[i] > 0.5)) {
                /* multiply elements (if element in kv1 is rejected it stays anyway
                   rejected) */
                pkv1data[i] *= pkv2data[i];

                if (kmclipm_is_nan_or_inf(pkv1data[i])) {
                    /*pkvdata[i] = NAN;*/
                    pkv1mask[i] = 0.;
                }
            } else {
                /* element of kv2 is rejected, reject also element in kv1 */
                pkv1mask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Divide two kmclipm_vectors element-wise.

    @param kv1  First kmclipm_vector (modified)
    @param kv2 	Second kmclipm_vector

    @return     CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    The first vector is divided by the second one. In opposite to
    cpl_vector_divide(), a division by zero doesn't throw an error but results
    in a rejected element of the vector

    The input vectors must have the same size.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if any input pointer is NULL
    @li CPL_ERROR_INCOMPATIBLE_INPUT if kv1 and kv2 have different sizes
*/
cpl_error_code kmclipm_vector_divide(kmclipm_vector *kv1, const kmclipm_vector *kv2)
{
    int             i           = 0,
                    size        = 0;
    double          *pkv1data   = NULL,
                    *pkv1mask   = NULL,
                    *pkv2data   = NULL,
                    *pkv2mask   = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG((kv1 != NULL) && (kv2 != NULL),
                                  CPL_ERROR_NULL_INPUT);

        size = cpl_vector_get_size(kv1->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_CHECK_AUTOMSG(size == cpl_vector_get_size(kv2->data),
                       CPL_ERROR_ILLEGAL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkv1data = cpl_vector_get_data(kv1->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv1mask = cpl_vector_get_data(kv1->mask));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2data = cpl_vector_get_data(kv2->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkv2mask = cpl_vector_get_data(kv2->mask));

        for (i = 0; i < size; i++) {
            if ((pkv1mask[i] > 0.5) && (pkv2mask[i] > 0.5)) {
                /* divide elements (if element in kv1 is rejected it stays anyway
                   rejected) */
                pkv1data[i] /= pkv2data[i];

                if (kmclipm_is_nan_or_inf(pkv1data[i])) {
                    /*pkvdata[i] = NAN;*/
                    pkv1mask[i] = 0.;
                }
            } else {
                /* element of kv2 is rejected, reject also element in kv1 */
                pkv1mask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Elementwise addition of a scalar to a kmclipm_vector.

    @param kv       kmclipm_vector to modify
    @param addend 	Number to add

    @return         CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    Add a number to each element of the kmclipm_vector.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
cpl_error_code kmclipm_vector_add_scalar(kmclipm_vector *kv, double addend)
{
    int             i           = 0,
                    n           = 0;
    double          *pkvmask    = NULL,
                    *pkvdata    = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        n = cpl_vector_get_size(kv->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < n; i++) {
            if (pkvmask[i] > 0.5) {
                pkvdata[i] += addend;
            }
            if (kmclipm_is_nan_or_inf(pkvdata[i])) {
                /*pkvdata[i] = NAN;*/
                pkvmask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

cpl_error_code  kmclipm_vector_subtract_scalar(
                                kmclipm_vector *kv,
                                double subtrahend);

/**
    @brief
        Elementwise subtraction of a scalar from a kmclipm_vector.

    @param kv           kmclipm_vector to modify
    @param subtrahend 	Number to subtract

    @return         CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    Subtract a number from each element of the kmclipm_vector.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
cpl_error_code kmclipm_vector_subtract_scalar(kmclipm_vector *kv,
                                              double subtrahend)
{
    int             i           = 0,
                    n           = 0;
    double          *pkvmask    = NULL,
                    *pkvdata    = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        n = cpl_vector_get_size(kv->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < n; i++) {
            if (pkvmask[i] > 0.5) {
                pkvdata[i] -= subtrahend;
            }
            if (kmclipm_is_nan_or_inf(pkvdata[i])) {
                /*pkvdata[i] = NAN;*/
                pkvmask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Elementwise multiplication of a scalar to a kmclipm_vector.

    @param kv       kmclipm_vector to modify
    @param factor 	Number to multiply with

    @return         CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    Multiply each element of the kmclipm_vector with a number.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
cpl_error_code kmclipm_vector_multiply_scalar(kmclipm_vector *kv, double factor)
{
    int             i           = 0,
                    n           = 0;
    double          *pkvmask    = NULL,
                    *pkvdata    = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        n = cpl_vector_get_size(kv->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < n; i++) {
            if (pkvmask[i] > 0.5) {
                pkvdata[i] *= factor;
            }
            if (kmclipm_is_nan_or_inf(pkvdata[i])) {
                /*pkvdata[i] = NAN;*/
                pkvmask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Elementwise division of a kmclipm_vector and a scalar.

    @param kv       kmclipm_vector to modify
    @param dividend Number to divide with

    @return         CPL_ERROR_NONE or the relevant @li _cpl_error_code_ on error

    Divide each element of the kmclipm_vector by a number.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
cpl_error_code kmclipm_vector_divide_scalar(kmclipm_vector *kv, double dividend)
{
    int             i           = 0,
                    n           = 0;
    double          *pkvmask    = NULL,
                    *pkvdata    = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        n = cpl_vector_get_size(kv->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < n; i++) {
            if (pkvmask[i] > 0.5) {
                pkvdata[i] /= dividend;
            }
            if (kmclipm_is_nan_or_inf(pkvdata[i])) {
                /*pkvdata[i] = NAN;*/
                pkvmask[i] = 0.;
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Calculates the absolute of an vector inplace.

    @param kv    The vector.

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    The vector is checked elementwise if the data is negative. If so its sign
    is changed.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if @c data is NULL.
*/
cpl_error_code kmclipm_vector_abs(kmclipm_vector *kv)
{
    cpl_error_code  ret_error  = CPL_ERROR_NONE;

    double          *pkvdata = NULL,
                    *pkvmask = NULL;

    int             i        = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));

        for (i = 0; i < cpl_vector_get_size(kv->data);i++) {
            if ((pkvmask[i] > 0.5) && (pkvdata[i] < 0.0)) {
                pkvdata[i] = -pkvdata[i];
            }
        }
    }
    KMCLIPM_CATCH
    {
        ret_error = cpl_error_get_code();
    }

    return ret_error;
}

/**
    @brief
        Get the size of the kmclipm_vector.

    @param kv       Input kmclipm_vector

    @return The size or -1 in case of an error

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
int kmclipm_vector_get_size(const kmclipm_vector *kv)
{
    int ret_val = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        ret_val = cpl_vector_get_size(kv->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();
    }
    KMCLIPM_CATCH
    {
        ret_val = -1;
    }

    return ret_val;
}

/**
    @brief
        Compute the mean value of non-rejected kmclipm_vector elements.

    @param kv       Input kmclipm_vector

    @return Mean value of vector elements or 0 on error or when all values are
            rejected.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
double kmclipm_vector_get_mean(const kmclipm_vector *kv)
{
    cpl_vector  *vec    = NULL;
    double      val     = 0.;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        vec = kmclipm_vector_create_non_rejected(kv);
        if (vec != NULL) {
            val = cpl_vector_get_mean(vec);
            KMCLIPM_TRY_CHECK_ERROR_STATE();
        }
    }
    KMCLIPM_CATCH
    {
        val = 0.;
    }
    cpl_vector_delete(vec); vec = NULL;

    return val;
}

/**
    @brief
        Compute the median of the elements of a vector.

    @param kv    Input Vector.
    @param type  The method to calculate ther median with:
                 KMCLIPM_STATISTICAL:
                 The returned value is the lower of the two center elements of
                 the sorted input vector.
                 KMCLIPM_ARITHMETIC: The arithmetic mean of the two center
                 elements of the sorted input vector is calculated.

    @return Median value of the vector elements or 0 on error or when all values
            are rejected.

    Unlike @li cpl_vector_get_median this function doesn't modify the order of
    the elements of the input vector!

  Possible cpl_error_code set in this function:
  @li CPL_ERROR_NULL_INPUT    if @c vec is NULL.
*/
double kmclipm_vector_get_median(const kmclipm_vector *kv,
                                 const enum medianType type)
{
    cpl_vector  *vec    = NULL;
    double      val     = 0.;
    int         size    = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        vec = kmclipm_vector_create_non_rejected(kv);

        if (vec != NULL) {
            size = cpl_vector_get_size(vec);
#if KMCLIPM_GET_INSTALLED_CPL_VERSION >= 6
            if ((type == KMCLIPM_STATISTICAL) && ((size % 2) == 0)) {
                /* statistical method and even size */
                cpl_vector_sort(vec, CPL_SORT_ASCENDING);

                val = cpl_vector_get(vec, size/2 - 1);
            } else {
                /* arithmetic method or uneven size */
                val = cpl_vector_get_median(vec);
            }
#else
            if ((type == KMCLIPM_STATISTICAL) || ((size % 2) == 1)) {
                /* statistical method or uneven size */
                val = cpl_vector_get_median(vec);
            } else {
                double *pvec   = NULL;
                /* arithmetic method and even size */
                cpl_vector_sort(vec, CPL_SORT_ASCENDING);
                KMCLIPM_TRY_CHECK_ERROR_STATE();

                KMCLIPM_TRY_EXIT_IFN(
                    pvec = cpl_vector_get_data(vec));

                /* take average of middle two values */
                val = (pvec[size/2]+pvec[size/2-1]) / 2;
            }

#endif
        }
    }
    KMCLIPM_CATCH
    {
        val = 0.;
    }
    cpl_vector_delete(vec); vec = NULL;

    return val;
}

/**
    @brief
        Remove a certain percentage of brightest pixels.

    @param kv            Input Vector.
    @param percentage    The percentage of brightest pixels to remove (between 0 and 1).

    @return The cut vector.

  Possible cpl_error_code set in this function:
  @li CPL_ERROR_NULL_INPUT    if @c vec is NULL.
*/
kmclipm_vector* kmclipm_vector_cut_percentian(const kmclipm_vector *kv,
                                             double percentage)
{
    kmclipm_vector  *ret_vec    = NULL;
    cpl_vector      *vec        = NULL,
                    *cut_vec    = NULL;
    int             size        = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);
        KMCLIPM_TRY_CHECK_AUTOMSG((percentage >= 0.0) && (percentage < 1.0),
                                  CPL_ERROR_ILLEGAL_INPUT);

        vec = kmclipm_vector_create_non_rejected(kv);

        if (vec != NULL) {
            /* sort vector */
            cpl_vector_sort(vec, CPL_SORT_ASCENDING);
            KMCLIPM_TRY_CHECK_ERROR_STATE();

            /* cut percentage wanted */
            size = cpl_vector_get_size(vec);
#if KMCLIPM_GET_INSTALLED_CPL_VERSION >= 6
            cpl_size tmp = (cpl_size)rint((1.-percentage)*(double)size-1.);
#else
            int tmp = (int)rint((1.-percentage)*(double)size-1.);
#endif
            KMCLIPM_TRY_EXIT_IFN(
                cut_vec = cpl_vector_extract(vec, 0, tmp, 1));
            KMCLIPM_TRY_EXIT_IFN(
                ret_vec = kmclipm_vector_create(cut_vec));
        }
    }
    KMCLIPM_CATCH
    {
        kmclipm_vector_delete(ret_vec); ret_vec = NULL;
    }
    cpl_vector_delete(vec); vec = NULL;

    return ret_vec;
}

/**
    @brief
        Compute the sum of non-rejected kmclipm_vector elements.

    @param kv       Input kmclipm_vector

    @return         Sum of vector elements or 0 on error or when all values are
                    rejected.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
double kmclipm_vector_get_sum(const kmclipm_vector *kv)
{
    int             i           = 0,
                    n           = 0;
    double          sum         = 0,
                    *pkvmask    = NULL,
                    *pkvdata    = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        n = cpl_vector_get_size(kv->data);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < n; i++) {
            if (pkvmask[i] > 0.5) {
                sum += pkvdata[i];
            }
        }
    }
    KMCLIPM_CATCH
    {
        sum = 0;
    }

    return sum;
}

/**
    @brief
        Compute the bias-corrected standard deviation of a vectors elements.

    @param kv       Input kmclipm_vector

    @return         standard deviation of the elements or -1 on error.

    S(n-1) = sqrt[sum((xi-mean)^2) / (n-1)] (i=1 -> n)

    The length of @c kv must be at least 2.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ILLEGAL_INPUT if the number of non-rejected values in @c kv is
    less than 2
*/
double kmclipm_vector_get_stdev(const kmclipm_vector *kv)
{
    cpl_vector  *vec    = NULL;
    double      val     = 0.;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        vec = kmclipm_vector_create_non_rejected(kv);
        if (vec != NULL) {
            val = cpl_vector_get_stdev(vec);
            KMCLIPM_TRY_CHECK_ERROR_STATE();
        }
    }
    KMCLIPM_CATCH
    {
        val = -1.;
    }
    cpl_vector_delete(vec); vec = NULL;

    return val;
}

/**
    @brief
        Compute the bias-corrected standard deviation of a vectors elements using median.

    @param kv       Input kmclipm_vector

    @return         standard deviation using median of the elements or -1 on error.

    S(n-1) = sqrt[sum((xi-median)^2) / (n-1)] (i=1 -> n)

    The length of @c kv must be at least 2.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
    @li CPL_ERROR_ILLEGAL_INPUT if the number of non-rejected values in @c kv is
    less than 2
*/
double kmclipm_vector_get_stdev_median(const kmclipm_vector *kv)
{
    int             i           = 0,
                    n           = 0;
    double          stdev       = 0,
                    median      = 0,
                    *pkvmask    = NULL,
                    *pkvdata    = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        n = kmclipm_vector_count_non_rejected(kv);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_CHECK_AUTOMSG(n >= 2,
                                  CPL_ERROR_ILLEGAL_INPUT);

        median = kmclipm_vector_get_median(kv, KMCLIPM_ARITHMETIC);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));
        for (i = 0; i < cpl_vector_get_size(kv->data); i++) {
            if (pkvmask[i] > 0.5) {
                stdev += pow(pkvdata[i]-median, 2);
            }
        }

        stdev /= (n - 1);
        stdev = sqrt(stdev);
    }
    KMCLIPM_CATCH
    {
        stdev = -1;
    }

    return stdev;
}

/**
    @brief Get the maximum of the kmclipm_vector and its position.

    @param kv       Input kmclipm_vector
    @param pos 	    (Output) The index of the max element (first value is 0), is
                    set to -1 if all elements are rejected. If set to NULL it
                    won't be returned.

    @return         The element value

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
double kmclipm_vector_get_max(const kmclipm_vector *kv, int *pos)
{
    double       val        = -DBL_MAX;
    const double *pkvdata   = NULL,
                 *pkvmask   = NULL;
    int          i          = 0,
                 n          = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data_const(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data_const(kv->mask));

        n = cpl_vector_get_size(kv->data);
        if (kmclipm_vector_count_rejected(kv) == n) {
            val = 0.;
            if (pos != NULL) {
                *pos = -1;
            }
        } else {
            for (i = 0; i < n; i++) {
                if ((pkvmask[i] > 0.5) && (val < pkvdata[i])) {
                    val = pkvdata[i];
                    if (pos != NULL) {
                        *pos = i;
                    }
                }
            }
        }
        KMCLIPM_TRY_CHECK_ERROR_STATE();
    }
    KMCLIPM_CATCH
    {
        val = 0.;
        if (pos != NULL) {
            *pos = -1;
        }
    }

    return val;
}

/**
    @brief Get the minimum of the kmclipm_vector and its position.

    @param kv       Input kmclipm_vector
    @param pos 	    (Output) The index of the min element (first value is 0), is
                    set to -1 if all elements are rejected. If set to NULL it
                    won't be returned.

    @return         The element value

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT if @c kv is NULL
*/
double kmclipm_vector_get_min(const kmclipm_vector *kv, int *pos)
{
    double       val        = DBL_MAX;
    const double *pkvdata   = NULL,
                 *pkvmask   = NULL;
    int          i          = 0,
                 n          = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data_const(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data_const(kv->mask));

        n = cpl_vector_get_size(kv->data);
        if (kmclipm_vector_count_rejected(kv) == n) {
            val = 0.;
            if (pos != NULL) {
                *pos = -1;
            }
        } else {
            for (i = 0; i < n; i++) {
                if ((pkvmask[i] > 0.5) && (val > pkvdata[i])) {
                    val = pkvdata[i];
                    if (pos != NULL) {
                        *pos = i;
                    }
                }
            }
        }
        KMCLIPM_TRY_CHECK_ERROR_STATE();
    }
    KMCLIPM_CATCH
    {
        val = 0.;
        if (pos != NULL) {
            *pos = -1;
        }
    }

    return val;
}

/**
    @brief
        Compute the elementwise power of the vector.

    @param kv       The vector to be modified in place.
    @param exponent The Exponent.

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    With a vector-element being zero, this function returns also zero.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if @c kv is NULL.
*/
cpl_error_code kmclipm_vector_power(kmclipm_vector *kv, double exponent)
{
    int             i           = 0,
                    n           = 0;
    double          *pkvmask    = NULL,
                    *pkvdata    = NULL;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        if (exponent == 0.0) {
            /* create vector filled with ones */
            kmclipm_vector_multiply_scalar(kv, 0.0);
            KMCLIPM_TRY_CHECK_ERROR_STATE();

            kmclipm_vector_add_scalar(kv, 1.0);
            KMCLIPM_TRY_CHECK_ERROR_STATE();
        } else {
            n = cpl_vector_get_size(kv->data);
            KMCLIPM_TRY_CHECK_ERROR_STATE();

            KMCLIPM_TRY_EXIT_IFN(
                pkvdata = cpl_vector_get_data(kv->data));
            KMCLIPM_TRY_EXIT_IFN(
                pkvmask = cpl_vector_get_data(kv->mask));
            for (i = 0; i < n; i++) {
                if (pkvmask[i] > 0.5) {
                    pkvdata[i] = pow(pkvdata[i], exponent);
                }
                if (kmclipm_is_nan_or_inf(pkvdata[i])) {
                    /*pkvdata[i] = NAN;*/
                    pkvmask[i] = 0.;
                }
            }
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Fill a kmclipm_vector.

    @param kv      The vector to be filled with the value val.
    @param val     Value used to fill the kmclipm_vector.

    If val is an invalid value all elements of kv will be rejected.

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if @c vec is NULL.
*/
cpl_error_code kmclipm_vector_fill(kmclipm_vector *kv, double val)
{
    int             i           = 0;
    cpl_error_code  err         = CPL_ERROR_NONE;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        for (i = 0; i < kmclipm_vector_get_size(kv); i++) {
            kmclipm_vector_set(kv, i, val);
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        Flip the values of a vector.

    @param kv    The vector.

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    The vector is flipped elementwise from both sides.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if @c kv is NULL.
*/
cpl_error_code kmclipm_vector_flip(kmclipm_vector* kv)
{
    cpl_error_code  ret_error   = CPL_ERROR_NONE;
    double          *pkvdata    = NULL,
                    *pkvmask    = NULL,
                    tmp_dbl     = 0;
    int             i           = 0,
                    half_size   = 0,
                    size        = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data(kv->mask));

        size = cpl_vector_get_size(kv->data);
        half_size = floor(size / 2);

        for (i = 0; i < half_size;i++) {
            tmp_dbl = pkvdata[i];
            pkvdata[i] = pkvdata[size-1-i];
            pkvdata[size-1-i] = tmp_dbl;

            tmp_dbl = pkvmask[i];
            pkvmask[i] = pkvmask[size-1-i];
            pkvmask[size-1-i] = tmp_dbl;
        }
    }
    KMCLIPM_CATCH
    {
        ret_error = cpl_error_get_code();
    }

    return ret_error;
}

/**
    @brief
        Calculates the histogram of a vector.

    This implementation follows the one used in IDL. This means that in the
    highest bin can only be found values corresponding to the maximum value in
    the input vector.

    @param kv       Input vector.
    @param nbins    The number of bins to produce.

    @return
        A vector of size @c nbins containing the histogram of input data @c d .

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if @c d is NULL.
    @li CPL_ERROR_ILLEGAL_INPUT if @c nbis is <= 0.

*/
kmclipm_vector* kmclipm_vector_histogram(const kmclipm_vector *kv, int nbins)
{
    int             j           = 0,
                    pos         = 0;
    const double    *pkvdata    = NULL,
                    *pkvmask    = NULL;
    double          *phdata     = NULL,
                    binwidth    = 0.0,
                    hmin        = 0.0,
                    hmax        = 0.0;
    kmclipm_vector  *h          = NULL;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        KMCLIPM_TRY_CHECK_AUTOMSG(nbins > 0,
                                  CPL_ERROR_ILLEGAL_INPUT);

        hmin = kmclipm_vector_get_min(kv, NULL);
        hmax = kmclipm_vector_get_max(kv, NULL);
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        binwidth = (hmax - hmin) / (nbins - 1);

        KMCLIPM_TRY_EXIT_IFN(
            pkvdata = cpl_vector_get_data_const(kv->data));
        KMCLIPM_TRY_EXIT_IFN(
            pkvmask = cpl_vector_get_data_const(kv->mask));

        KMCLIPM_TRY_EXIT_IFN(
            h = kmclipm_vector_new(nbins));
        KMCLIPM_TRY_EXIT_IFN(
            phdata = cpl_vector_get_data(h->data));

        for (j = 0; j < cpl_vector_get_size(kv->data); j++) {
            if (pkvmask[j] > 0.5) {
                pos = floor((pkvdata[j] - hmin) / binwidth);
                phdata[pos]++;
            }
        }
    }
    KMCLIPM_CATCH
    {
        kmclipm_vector_delete(h); h = NULL;
    }

    return h;
}

/**
    @brief
      Override for cpl_vector_load().

    @param filename   Name of the input file
    @param position   Extension number in the file.

    @return The function returns the newly created kmclipm_vector or NULL if an
            error occurred.

    This is an override for cpl_vector_load(), just giving a proper error
    message if the input file isn't found. The errors returned are the same as
    with @c cpl_vector_load
*/
kmclipm_vector* kmclipm_vector_load(const char *filename, int position)
{
    cpl_vector      *vec    = NULL;
    kmclipm_vector  *kv     = NULL;

    KMCLIPM_TRY
    {
        vec = cpl_vector_load(filename, position);
        if (cpl_error_get_code() != CPL_ERROR_NONE) {
            if (cpl_error_get_code() == CPL_ERROR_FILE_IO) {
                cpl_msg_error("", "File not found: %s", filename);
            } else {
                cpl_msg_error("", "Problem loading file '%s' (%s --> Code %d)",
                              filename, cpl_error_get_message(),
                              cpl_error_get_code());
            }
        }
        KMCLIPM_TRY_CHECK_ERROR_STATE();

        KMCLIPM_TRY_EXIT_IFN(
            kv = kmclipm_vector_create(vec));
    }
    KMCLIPM_CATCH
    {
        kmclipm_vector_delete(kv);
    }

    return kv;
}

/**
    @brief
      Override for cpl_vector_save().

    @param kv       kmclipm_vector to write to disk or NULL
    @param filename Name of the file to write
    @param bpp      Bits per pixel
    @param pl       Property list for the output header or NULL
    @param mode     The desired output options
    @param rej_val  The rejected values are filled with this value.
                    If -1 is provided, the rejected values in the internal
                    cpl-bad pixel mask are ignored.

    @return CPL_ERROR_NONE or the relevant _cpl_error_code_ on error

    This is an override for cpl_vector_save(), just giving a proper error
    message if the input file isn't found and rejecting all infinite values
    (NaN, +/-Inf). The errors returned are the same as with @c cpl_vector_save
*/
cpl_error_code kmclipm_vector_save(const kmclipm_vector *kv,
                                   const char *filename,
                                   cpl_type_bpp bpp,
                                   const cpl_propertylist *pl,
                                   unsigned mode,
                                   double rej_val)
{
    cpl_error_code  err         = CPL_ERROR_NONE;
    kmclipm_vector  *kv_dup     = NULL;
    double          *pkvdata    = NULL,
                    *pkvmask    = NULL;
    int             n           = 0,
                    i           = 0;

    KMCLIPM_TRY
    {
        KMCLIPM_TRY_CHECK_AUTOMSG(kv != NULL,
                                  CPL_ERROR_NULL_INPUT);

        if ((rej_val != -1) || (kmclipm_is_nan_or_inf(rej_val))) {
            KMCLIPM_TRY_EXIT_IFN(
                kv_dup = kmclipm_vector_duplicate(kv));
            /* set rejected values to NaN */
            KMCLIPM_TRY_EXIT_IFN(
                pkvdata = cpl_vector_get_data(kv_dup->data));
            KMCLIPM_TRY_EXIT_IFN(
                pkvmask = cpl_vector_get_data(kv_dup->mask));
            n = cpl_vector_get_size(kv_dup->data);
            for (i = 0; i < n; i++) {
                if (pkvmask[i] < 0.5) {
                    pkvdata[i] = rej_val;
                }
            }

            err = cpl_vector_save(kv_dup->data, filename, bpp, pl, mode);
            KMCLIPM_TRY_CHECK_ERROR_STATE();

        } else {
            err = cpl_vector_save(kv->data, filename, bpp, pl, mode);
            KMCLIPM_TRY_CHECK_ERROR_STATE();
        }
        kmclipm_vector_delete(kv_dup); kv_dup = NULL;
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}

/**
    @brief
        All values contained in @c vec are printed for debugging purposes.

    @param kv The vector to print.

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.
*/
cpl_error_code kmclipm_vector_dump(const kmclipm_vector *kv)
{
    int             i           = 0,
                    n           = 0;
    cpl_error_code  err         = CPL_ERROR_NONE;
    const double    *pkvdata   = NULL,
                    *pkvmask   = NULL;

    KMCLIPM_TRY
    {
        if (kv == NULL) {
            cpl_msg_debug("", "     ====== START KMCLIPM_VECTOR ======");
            cpl_msg_debug("", "             empty vector");
            cpl_msg_debug("", "     ====== END KMCLIPM_VECTOR ========");
        } else {
            n = cpl_vector_get_size(kv->data);
            KMCLIPM_TRY_CHECK(n == cpl_vector_get_size(kv->mask),
                              CPL_ERROR_ILLEGAL_INPUT,
                              NULL,
                              "data and mask not of same size!");

            KMCLIPM_TRY_EXIT_IFN(
                pkvdata = cpl_vector_get_data_const(kv->data));
            KMCLIPM_TRY_EXIT_IFN(
                pkvmask = cpl_vector_get_data_const(kv->mask));

            cpl_msg_debug("", "     ====== START KMCLIPM_VECTOR ======");
            cpl_msg_debug("", "     #\tdata:\tmask:");
            cpl_msg_debug("", "     ---------------------");
            for (i = 0; i < n; i++) {
                cpl_msg_debug("", "     %d\t%g\t%g", i, pkvdata[i], pkvmask[i]);
            }
            cpl_msg_debug("", "     ====== END KMCLIPM_VECTOR ========");
            KMCLIPM_TRY_CHECK_ERROR_STATE();
        }
    }
    KMCLIPM_CATCH
    {
        err = cpl_error_get_code();
    }

    return err;
}
