/* $Id: fors_std_cat.c,v 1.21 2013-09-10 15:19:08 cgarcia Exp $
 *
 * This file is part of the FORS Library
 * Copyright (C) 2002-2010 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013-09-10 15:19:08 $
 * $Revision: 1.21 $
 * $Name: not supported by cvs2svn $
 */

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

#include <fors_std_star.h>
#include <fors_utils.h>
#include <fors_instrument.h>

#include <math.h>
#include <stdbool.h>
#include <string.h>

const char *const FORS_STD_CAT_COLUMN_RA   = "RA";
const char *const FORS_STD_CAT_COLUMN_DEC  = "DEC";
const char *const FORS_STD_CAT_COLUMN_NAME = "OBJECT";

typedef struct band_jacobian {
    char    band;
    double  mag[6]; /* 5 inputs, 1 constant */
    double  col[6]; /* 5 inputs, 1 constant */
} band_jacobian;

static void
fors_std_cat_propagate_uncorrelated_inputs( const double    *in,
                                            const double    *din,
                                            const double    *jacobi_A,
                                            const double    *jacobi_B,
                                            int             size,
                                            double          *out_A,
                                            double          *out_B,
                                            double          *dout_A,
                                            double          *dout_B,
                                            double          *cov_AB);

static cpl_error_code
fors_std_cat_import_generic_star(           const double        *band_values,
                                            const double        *band_errors,
                                            const band_jacobian *jacobians,
                                            int                 nband_values,
                                            int                 nbands,
                                            char                band,
                                            double  *cat_mag,
                                            double  *dcat_mag,
                                            double  *color,
                                            double  *dcolor,
                                            double  *cov_catmag_color);

static  bool
fors_std_cat_check_band_support(            cpl_error_code (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            int     nvalues,
                                            char    band);

static  bool*
fors_std_cat_determine_required_columns(     cpl_error_code (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            int     nvalues,
                                            char    band);

static cpl_error_code
fors_std_cat_reject_not_required_columns(   cpl_array       *column_names,
                                            cpl_error_code  (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            char            band);

static bool
fors_std_cat_table_check_columns(           const cpl_table *cat_table,
                                            const cpl_array *columns);

static cpl_array    *
fors_std_cat_create_error_column_names(     const cpl_array *colnames);

static cpl_error_code
fors_std_cat_landolt_star_import(           double  v_bv_ub_vr_vi[5],
                                            double  ERR_v_bv_ub_vr_vi[5],
                                            char    band,
                                            double  *cat_mag,
                                            double  *dcat_mag,
                                            double  *color,
                                            double  *dcolor,
                                            double  *cov_catmag_color);

static cpl_array    *
fors_std_cat_landolt_get_column_names(      void);

static cpl_error_code
fors_std_cat_stetson_star_import(           double  u_b_v_r_i[5],
                                            double  ERR_u_b_v_r_i[5],
                                            char    band,
                                            double  *cat_mag,
                                            double  *dcat_mag,
                                            double  *color,
                                            double  *dcolor,
                                            double  *cov_catmag_color);

static cpl_array    *
fors_std_cat_stetson_get_column_names(      void);

static bool
fors_std_cat_check_method_and_columns(      cpl_table   *catalogue,
                                            cpl_array   *colnames,
                                            cpl_error_code  (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            char        band,
                                            const char  *method,
                                            cpl_array   **err_colnames,
                                            bool        *method_supports_band);

#undef cleanup
#define cleanup
/**
 * @brief   Take uncorrelated inputs with errors, 2 Jacobians and compute the outputs.
 * @param   in          Input array
 * @param   din         Input error array
 * @param   jacobi_A    Jacobian array A
 * @param   jacobi_B    Jacobian array B
 * @param   size        Common array size
 * @param   out_A       Output value A
 * @param   out_B       Output value B
 * @param   dout_A      Output error A
 * @param   dout_B      Output error B
 * @param   cov_AB      Output covariance of A and B
 * @return  Nothing
 */
static void
fors_std_cat_propagate_uncorrelated_inputs( const double    *in,
                                            const double    *din,
                                            const double    *jacobi_A,
                                            const double    *jacobi_B,
                                            int             size,
                                            double          *out_A,
                                            double          *out_B,
                                            double          *dout_A,
                                            double          *dout_B,
                                            double          *cov_AB)
{
    int n;
    
    *out_A  = 0;
    *out_B  = 0;
    *dout_A = 0;
    *dout_B = 0;
    *cov_AB = 0;
    
    for (n = 0; n < size; n++)
    {
        /* uncorrelated inputs are assumed! */
        *out_A  += (*jacobi_A) * (*in);
        *out_B  += (*jacobi_B) * (*in);
        *dout_A += (*jacobi_A)*(*jacobi_A) * (*din)*(*din);
        *dout_B += (*jacobi_B)*(*jacobi_B) * (*din)*(*din);
        *cov_AB += (*jacobi_A)*(*jacobi_B) * (*din)*(*din);
        jacobi_A++;
        jacobi_B++;
        in++;
        din++;
    }
    
    *dout_A = sqrt(*dout_A);
    *dout_B = sqrt(*dout_B);
}

#undef cleanup
#define cleanup
/**
 * @brief   Get magnitude and color
 * @param   band_values         Band inputs
 * @param   band_errors         Band errors
 * @param   jacobians           Array of Jacobians for the allowed bands
 * @param   nband_values        Size of @a band_values and @a band_errors
 * @param   nbands              Size of @a jacobians array
 * @param   band                Band specifier, searched in @a jacobians
 * @param   cat_mag             Output catalogue magnitude
 * @param   dcat_mag            Output catalogue magnitude error
 * @param   color               Output color
 * @param   dcolor              Output color error
 * @param   cov_catmag_color    Output covariance of catalogue magnitude and
 *                              color
 * @return  CPL error code
 * 
 * The @a jacobians must have @a nbands entries.
 * 
 * Important:@n
 * Each entry of @a jacobians must have the two arrays @em mag and @em col,
 * which must each be of the size (@a nband_values + 1) !!! The last
 * elements jacobians[i].mag[nband_values] and jacobians[i].col[nband_values]
 * are used as constant (i.e. offset) which needs no band value as weighting.
 * 
 * If @a band could not be found, then CPL_ERROR_UNSUPPORTED_MODE is set and
 * returned.
 */
static cpl_error_code
fors_std_cat_import_generic_star(           const double        *band_values,
                                            const double        *band_errors,
                                            const band_jacobian *jacobians,
                                            int                 nband_values,
                                            int                 nbands,
                                            char                band,
                                            double  *cat_mag,
                                            double  *dcat_mag,
                                            double  *color,
                                            double  *dcolor,
                                            double  *cov_catmag_color)
{
    int ib;
    for (ib = 0; ib <= nbands; ib++)
    {
        if (jacobians[ib].band == band)
        {
            fors_std_cat_propagate_uncorrelated_inputs(
                                            band_values,
                                            band_errors,
                                            jacobians[ib].mag,
                                            jacobians[ib].col,
                                            nband_values,
                                            cat_mag,
                                            color,
                                            dcat_mag,
                                            dcolor,
                                            cov_catmag_color);
            /* constant term */
            *cat_mag += jacobians[ib].mag[nband_values];
            *color   += jacobians[ib].col[nband_values];
            
            return CPL_ERROR_NONE;
        }
    }
    
    cpl_error_set_message(                  cpl_func,
                                            CPL_ERROR_UNSUPPORTED_MODE,
                                            "unknown band \'%c\'",
                                            band);
    return cpl_error_get_code();
}

#undef cleanup
#define cleanup \
do { \
    cpl_free(values); values = NULL; \
    cpl_free(errors); errors = NULL; \
} while (0)
/**
 * @brief   Check whether a band is supported by an import function.
 * @param   import_func Import function pointer
 * @param   nvalues     Value array size
 * @param   band        Band
 * @return  True if supported, false otherwise or in the case of error
 */
static  bool
fors_std_cat_check_band_support(            cpl_error_code (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            int     nvalues,
                                            char    band)
{
    double          *values = NULL,
                    *errors = NULL;
    double          out[5];
    cpl_errorstate  errstat = cpl_errorstate_get();
    
    
    values = cpl_calloc(nvalues, sizeof(*values));
    errors = cpl_calloc(nvalues, sizeof(*errors));
    (*import_func)( values, errors, band,
                    out + 0, out + 1, out + 2, out + 3, out + 4);
    cpl_free(values);
    cpl_free(errors);
    
    if (!cpl_errorstate_is_equal(errstat))
    {
        if (cpl_error_get_code() == CPL_ERROR_UNSUPPORTED_MODE)
        {
            cpl_errorstate_set(errstat);    /* reset error */
        }
        else
        {
            cpl_error_set_where(cpl_func);
        }
        return false;
    }
    else
        return true;
}

#undef cleanup
#define cleanup \
do { \
    cpl_free(required); required = NULL; \
    cpl_free(values); values = NULL; \
    cpl_free(errors); errors = NULL; \
} while (0)
/**
 * @brief   Check which values are required for the import of a star.
 * @param   import_func Import function pointer
 * @param   nvalues     Value array size
 * @param   band        Band
 * @return  Array of booleans, NULL in the case of error
 * 
 * This function respectively sets the values in the input arrays for the
 * import function to 1, and decides whether this value is required if
 * an (absolute) output of the import function exceeds 10*DBL_EPSILON.
 */
static  bool*
fors_std_cat_determine_required_columns(     cpl_error_code (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            int     nvalues,
                                            char    band)
{
    bool            *required = NULL;
    double          *values = NULL,
                    *errors = NULL;
    double          out_offset[5];
    int             n;
    cpl_errorstate  errstat = cpl_errorstate_get();
    
    values = cpl_calloc(nvalues, sizeof(*values));
    errors = cpl_calloc(nvalues, sizeof(*errors));
    required = cpl_calloc(nvalues, sizeof(*required));
    
    /* get offset output values @ all inputs = 0.0 */
    (*import_func)(                         values,
                                            errors,
                                            band,
                                            out_offset + 0,
                                            out_offset + 1,
                                            out_offset + 2,
                                            out_offset + 3,
                                            out_offset + 4);
    
    /* successively switch on inputs
     * (here we assume of course that they contribute independently) */
    for (n = 0; n < nvalues; n++)
    {
        double  out[5];
        int     i;
        
        values[n] = 1;
        errors[n] = 1;
        if (n > 0)
        {
            values[n-1] = 0;
            errors[n-1] = 0;
        }
        
        (*import_func)(                     values,
                                            errors,
                                            band,
                                            out + 0,
                                            out + 1,
                                            out + 2,
                                            out + 3,
                                            out + 4);
        if (!cpl_errorstate_is_equal(errstat))
        {
            cpl_error_set_where(cpl_func);
            cleanup;
            return required;
        }
        
        for (i = 0; i < 5; i++)
            if (fabs(out[i] - out_offset[i]) > 10*DBL_EPSILON)
                required[n] = true;
    }
    
    cpl_free(values);
    cpl_free(errors);
    
    return required;
}

#undef cleanup
#define cleanup \
do { \
    cpl_free(required); required = NULL; \
} while (0)
/**
 * @brief   Erase all column names that are not required for a certain import function and band.
 * @param   column_names    Array with column names
 * @param   import_func     Import function
 * @param   band            Filter band
 * @return  CPL error code
 */
static cpl_error_code
fors_std_cat_reject_not_required_columns(   cpl_array       *column_names,
                                            cpl_error_code  (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            char            band)
{
    bool            *required = NULL;
    int             ncolumns,
                    n;
    cpl_errorstate  errstat = cpl_errorstate_get();
    
    cassure_automsg(                        import_func !=  NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return cpl_error_get_code());
    cassure_automsg(                        column_names !=  NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return cpl_error_get_code());
    cassure_automsg(                        cpl_array_get_type(column_names)
                                            == CPL_TYPE_STRING,
                                            CPL_ERROR_NULL_INPUT,
                                            return cpl_error_get_code());
    
    ncolumns = cpl_array_get_size(column_names);
    required = fors_std_cat_determine_required_columns(
                                            import_func,
                                            ncolumns,
                                            band);
    assure(                                 cpl_errorstate_is_equal(errstat),
                                            return cpl_error_get_code(),
                                            NULL);
    
    for (n = 0; n < ncolumns; n++)
    {
        if (!required[n])
        {
            cpl_array_set_invalid(column_names, n);
        }
        else
        {
            const char *name;
            name = cpl_array_get_string(column_names, n);
            if (name == NULL || name[0] == '\0')
            {
                cpl_error_set_message(      cpl_func,
                                            CPL_ERROR_DATA_NOT_FOUND,
                                            "column %d required, but name not "
                                            "specified",
                                            n);
                cleanup;
                return cpl_error_get_code();
            }
        }
    }
    cpl_free(required);
    
    return (cpl_errorstate_is_equal(errstat) ?
                CPL_ERROR_NONE :
                cpl_error_get_code());
}

#undef cleanup
#define cleanup
/**
 * @brief   Check whether (valid) column names are present in table.
 * @param   cat_table   Catalogue table
 * @param   columns     Column names
 * @return  True, if all (valid) column names are there, false otherwise
 * 
 * Invalid entries of @a columns are ignored.
 */
static bool
fors_std_cat_table_check_columns(           const cpl_table *cat_table,
                                            const cpl_array *columns)
{
    int ncols,
        n;
    cassure_automsg(                        cat_table !=  NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return false);
    cassure_automsg(                        columns !=  NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return false);
    cassure_automsg(                        cpl_array_get_type(columns)
                                            == CPL_TYPE_STRING,
                                            CPL_ERROR_NULL_INPUT,
                                            return false);

    ncols = cpl_array_get_size(columns);
    for (n = 0; n < ncols; n++)
    {
        const char  *cs;
        cs = cpl_array_get_string(columns, n);
        if (cs != NULL
            && (!cpl_table_has_column(cat_table, cs)))
        {
            return false;
        }
    }
    return true;
}

#undef cleanup
#define cleanup \
do { \
    cpl_array_delete(errcolnames); errcolnames = NULL; \
} while (0)
/**
 * @brief   Create an array with the error column names.
 * @param   colnames    Column names
 * @return  The array
 * 
 * Invalid entries are ignored (left invalid).
 */
static cpl_array    *
fors_std_cat_create_error_column_names(     const cpl_array *colnames)
{
    cpl_array   *errcolnames = NULL;
    int         size,
                n;
    
    cassure_automsg(                        colnames !=  NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return errcolnames);
    cassure_automsg(                        cpl_array_get_type(colnames)
                                            == CPL_TYPE_STRING,
                                            CPL_ERROR_NULL_INPUT,
                                            return errcolnames);
    
    size = cpl_array_get_size(colnames);
    
    errcolnames = cpl_array_new(size, CPL_TYPE_STRING);
    for (n = 0; n < size; n++)
    {
        char        estr[10];
        const char  *cs;
        cs = cpl_array_get_string(colnames, n);
        if (cs != NULL)
        {
            snprintf(estr, 9, "ERR_%s", cs);
            cpl_array_set_string(errcolnames, n, estr);
        }
    }
    return errcolnames;
}

#undef cleanup
#define cleanup
/**
 * @brief   Get Landolt magnitude and color
 * @param   v_bv_ub_vr_vi       Landolt inputs, see FORS manual
 * @param   ERR_v_bv_ub_vr_vi   Landolt input errors
 * @param   band                Band specifier, see below
 * @param   cat_mag             Output catalogue magnitude
 * @param   dcat_mag            Output catalogue magnitude error
 * @param   color               Output color
 * @param   dcolor              Output color error
 * @param   cov_catmag_color    Output covariance of catalogue magnitude and
 *                              color
 * @return  CPL error code
 * 
 * @a band may be one of 'U', 'B', 'G', 'V', 'R', 'I'. Otherwise
 * CPL_ERROR_UNSUPPORTED_MODE is returned.
 */
static cpl_error_code
fors_std_cat_landolt_star_import(           double  v_bv_ub_vr_vi[5],
                                            double  ERR_v_bv_ub_vr_vi[5],
                                            char    band,
                                            double  *cat_mag,
                                            double  *dcat_mag,
                                            double  *color,
                                            double  *dcolor,
                                            double  *cov_catmag_color)
{
    cpl_error_code  errc;
    
    static const band_jacobian jacobians[6] =
    { /*                             V   B-V   U-B   V-R   V-I     1(const) */
                    {   'U',    {    1,    1,    1,    0,    0,    0,},
                                {    0,    0,    1,    0,    0,    0,}, },
                    {   'B',    {    1,    1,    0,    0,    0,    0,},
                                {    0,    1,    0,    0,    0,    0,}, },
                                /*(Fukugita et al. 1996, AJ 111, p1748)*/
                    {   'G',    {    1, 0.56,    0,    0,    0,-0.12,},
                                {    0,    1,    0,    0,    0,    0,}, },
                    {   'V',    {    1,    0,    0,    0,    0,    0,},
                                {    0,    1,    0,    0,    0,    0,}, },
                    {   'R',    {    1,    0,    0,   -1,    0,    0,},
                                {    0,    0,    0,    1,    0,    0,}, },
                    {   'I',    {    1,    0,    0,    0,   -1,    0,},
                                {    0,    0,    0,    1,    0,    0,}, },
    };
    
    errc = fors_std_cat_import_generic_star(v_bv_ub_vr_vi,
                                            ERR_v_bv_ub_vr_vi,
                                            jacobians,
                                            5,  /* jac. columns without const */
                                            6,  /* n bands (U, B, G, ...) */
                                            band,
                                            cat_mag,
                                            dcat_mag,
                                            color,
                                            dcolor,
                                            cov_catmag_color);
    if (errc != CPL_ERROR_NONE)
        cpl_error_set_where(cpl_func);
    return errc;
}

#undef cleanup
#define cleanup
/**
 * @brief   Get an array with the names of the typical Landolt columns.
 * @return  The array
 * 
 * The column names correspond to the order of the input values for
 * @ref fors_std_cat_landolt_star_import().
 */
static cpl_array    *
fors_std_cat_landolt_get_column_names(      void)
{
    const char  landolt_columns[5][4] = { "V", "B_V", "U_B", "V_R", "V_I" };
    cpl_array   *columns = NULL;
    int         c;
    
    columns = cpl_array_new(5, CPL_TYPE_STRING);
    
    for (c = 0; c < 5; c++)
        cpl_array_set_string(columns, c, landolt_columns[c]);
    
    return columns;
}

#undef cleanup
#define cleanup
/**
 * @brief   Get Stetson magnitude and color
 * @param   u_b_v_r_i           Stetson inputs, see FORS manual
 * @param   ERR_u_b_v_r_i       Stetson input errors
 * @param   band                Band specifier, see below
 * @param   cat_mag             Output catalogue magnitude
 * @param   dcat_mag            Output catalogue magnitude error
 * @param   color               Output color
 * @param   dcolor              Output color error
 * @param   cov_catmag_color    Output covariance of catalogue magnitude and
 *                              color
 * @return  CPL error code
 * 
 * @a band may be one of 'U', 'B', 'G', 'V', 'R', 'I'. Otherwise
 * CPL_ERROR_UNSUPPORTED_MODE is returned.
 */
static cpl_error_code
fors_std_cat_stetson_star_import(           double  u_b_v_r_i[5],
                                            double  ERR_u_b_v_r_i[5],
                                            char    band,
                                            double  *cat_mag,
                                            double  *dcat_mag,
                                            double  *color,
                                            double  *dcolor,
                                            double  *cov_catmag_color)
{
    cpl_error_code  errc;
    
    static const band_jacobian jacobians[6] =
    { /*                             U     B     V     R     I     1(const) */
                    {   'U',    {    1,    0,    0,    0,    0,    0,},
                                {    1,   -1,    0,    0,    0,    0,}, },
                    {   'B',    {    0,    1,    0,    0,    0,    0,},
                                {    0,    1,   -1,    0,    0,    0,}, },
                                /*(Fukugita et al. 1996, AJ 111, p1748)*/
                    {   'G',    {    0, 0.56,(1.0-0.56),0,   0,-0.12,},
                                {    0,    1,   -1,    0,    0,    0,}, },
                    {   'V',    {    0,    0,    1,    0,    0,    0,},
                                {    0,    1,   -1,    0,    0,    0,}, },
                    {   'R',    {    0,    0,    0,    1,    0,    0,},
                                {    0,    0,    1,   -1,    0,    0,}, },
                    {   'I',    {    0,    0,    0,    0,    1,    0,},
                                {    0,    0,    1,   -1,    0,    0,}, },
    };
    
    errc = fors_std_cat_import_generic_star(u_b_v_r_i,
                                            ERR_u_b_v_r_i,
                                            jacobians,
                                            5,  /* jac. columns without const */
                                            6,  /* n bands (U, B, G, ...) */
                                            band,
                                            cat_mag,
                                            dcat_mag,
                                            color,
                                            dcolor,
                                            cov_catmag_color);
    if (errc != CPL_ERROR_NONE)
        cpl_error_set_where(cpl_func);
    return errc;
}

#undef cleanup
#define cleanup
/**
 * @brief   Get an array with the names of the typical Stetson columns.
 * @return  The array
 * 
 * The column names correspond to the order of the input values for
 * @ref fors_std_cat_stetson_star_import().
 */
static cpl_array    *
fors_std_cat_stetson_get_column_names(      void)
{
    const char  stetson_columns[5][2] = { "U", "B", "V", "R", "I" };
    cpl_array   *columns = NULL;
    int         c;
    
    columns = cpl_array_new(5, CPL_TYPE_STRING);
    
    for (c = 0; c < 5; c++)
        cpl_array_set_string(columns, c, stetson_columns[c]);
    
    return columns;
}

#undef cleanup
#define cleanup \
do { \
    if (err_colnames != NULL) \
        { cpl_array_delete(*err_colnames); *err_colnames = NULL; } \
    cat_type_detected = false; \
} while (0)
/**
 * @brief   Check whether the catalogue provides all required data for a method.
 * @param   colnames                Array with column names for import function
 * @param   import_func             Value import function
 * @param   method                  Method string for success message
 * @param   filename                Filename for success message
 * @param   band                    Optical band
 * @param   err_colnames            Output array with error value column names
 * @param   method_supports_band    Output flag whether the optical band is
 *                                  supported by the import function,
 *                                  to judge what caused a false return
 * @return  True if success, false otherwise
 * 
 * In the case of error, @a method_supports_band is undefined.
 */
static bool
fors_std_cat_check_method_and_columns(      cpl_table   *catalogue,
                                            cpl_array   *colnames,
                                            cpl_error_code  (*import_func)(
                                                double  *values,
                                                double  *errors,
                                                char    band,
                                                double  *out_A,
                                                double  *dout_A,
                                                double  *out_B,
                                                double  *dout_B,
                                                double  *out_cov),
                                            char        band,
                                            const char  *method,
                                            cpl_array   **err_colnames,
                                            bool        *method_supports_band)
{
    bool            band_supported = false,
                    cat_type_detected = false;
    int             ncols;
    cpl_errorstate  errstat = cpl_errorstate_get();
    
    cassure_automsg(                        catalogue != NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return cat_type_detected);
    cassure_automsg(                        colnames != NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return cat_type_detected);
    cassure_automsg(                        cpl_array_get_type(colnames)
                                            == CPL_TYPE_STRING,
                                            CPL_ERROR_NULL_INPUT,
                                            return cat_type_detected);
    cassure_automsg(                        import_func != NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return cat_type_detected);
    cassure_automsg(                        method != NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return cat_type_detected);
    cassure_automsg(                        err_colnames != NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return cat_type_detected);
    
    cleanup;
    
    ncols = cpl_array_get_size(colnames);
    band_supported = fors_std_cat_check_band_support(
                                            import_func,
                                            ncols,
                                            band);
    if (band_supported)
    {
        int n;
        /* determine the required values for the import function,
         * and set other column names to NULL */
        fors_std_cat_reject_not_required_columns(
                                            colnames,
                                            import_func,
                                            band);
        
        /* for column names != NULL, prepend "ERR_" */
        *err_colnames = fors_std_cat_create_error_column_names(colnames);
        
        for (n = 0; n < ncols; n++)
        {
            const char  *s[2];
            int         i;
            s[0] = cpl_array_get_string(colnames, n);
            s[1] = cpl_array_get_string(*err_colnames, n);
            if (s[0] != NULL)
                for (i = 0; i < 2; i++)
                {
                    cpl_msg_debug(cpl_func, "Required %s column for band %c: "
                                            "%s (%sfound)",
                                            method, band, s[i],
                                            (   cpl_table_has_column(
                                                    catalogue, s[i]) ?
                                                "" : "not ")
                                            );
                }
        }
        
        /* check presence of column names != NULL */
        cat_type_detected = (   fors_std_cat_table_check_columns(
                                            catalogue,
                                            colnames)
                                && fors_std_cat_table_check_columns(
                                            catalogue,
                                            *err_colnames));
    }
    if (method_supports_band != NULL)
        *method_supports_band = band_supported;
    
    assure(cpl_errorstate_is_equal(errstat), return cat_type_detected, NULL);
    
    return cat_type_detected;
}

#undef cleanup
#define cleanup \
do { \
    fors_std_star_list_delete(&stdlist, fors_std_star_delete); \
    fors_std_star_delete(&std_star); \
    cpl_array_delete(columns); columns = NULL; \
    cpl_array_delete(err_columns); err_columns = NULL; \
    cpl_array_delete(frame_error_messages); frame_error_messages = NULL; \
    cpl_table_delete(cat_table); cat_table = NULL; \
    cpl_free(band_values); band_values = NULL; \
    cpl_free(band_errors); band_errors = NULL; \
} while (0)
/**
 * @brief   Import standard star catalogues.
 * @param   cat_frames  Set of catalogue frames
 * @param   band        Filter band
 * @param   color_term  Colour correction term
 * @param   dcolor_term Colour correction term error
 */
fors_std_star_list *
fors_std_cat_load(                          const cpl_frameset  *cat_frames,
                                            char            band,
                                            bool            require_all_frames,
                                            double          color_term,
                                            double          dcolor_term)
{
    fors_std_star_list  *stdlist = NULL;
    fors_std_star       *std_star = NULL;
    cpl_array           *columns = NULL,
                        *err_columns = NULL,
                        *frame_error_messages = NULL;
    char                **frame_error_strings = NULL;
    cpl_table           *cat_table = NULL;
    const cpl_frame     *cat_frame;
    double              *band_values = NULL,
                        *band_errors = NULL;
    int                 iframe,
                        last_imethod = -1,
                        n_cat_entries = 0;
    bool                printed_warning = false,
                        checked_support = false,
                        printed_supported = false;
    cpl_errorstate      errstat = cpl_errorstate_get();
    
    struct method {
        cpl_array*      (*get_column_names_func)(void);
        cpl_error_code  (*star_import_func)(
                        double  *values,
                        double  *errors,
                        char    band,
                        double  *cat_mag,
                        double  *dcat_mag,
                        double  *color,
                        double  *dcolor,
                        double  *cov_catmag_color);
        const char      name[10];
        bool            band_supported;
    }           methods[2] = {  {   fors_std_cat_landolt_get_column_names,
                                    fors_std_cat_landolt_star_import,
                                    "Landolt",
                                    false},
                                {   fors_std_cat_stetson_get_column_names,
                                    fors_std_cat_stetson_star_import,
                                    "Stetson",
                                    false}
                        };
    
    /* check input */
    cassure_automsg(                        cat_frames != NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return stdlist);
    
    cassure(                                !fors_instrument_filterband_is_none(
                                                band),
                                            CPL_ERROR_ILLEGAL_INPUT,
                                            return stdlist,
                                            "no optical/filter band specified");
    cassure(                             !fors_instrument_filterband_is_unknown(
                                                band),
                                            CPL_ERROR_ILLEGAL_INPUT,
                                            return stdlist,
                                            "optical/filter band is unknown");
    
    stdlist = fors_std_star_list_new();
    /* error message container, don't abuse the error history for that since
     * its size is limited */
    frame_error_messages = cpl_array_new(   cpl_frameset_get_size(cat_frames),
                                            CPL_TYPE_STRING);
    frame_error_strings = cpl_array_get_data_string(frame_error_messages);
    
    /* import all frames */
    for (cat_frame = cpl_frameset_get_first_const(cat_frames), iframe = 0;
         cat_frame != NULL;
         cat_frame = cpl_frameset_get_next_const(cat_frames), iframe++)
    {
        int         ncolumns,
                    row,
                    nrows,
                    imethod,
                    nmethods;
        const char  **column_value_names,
                    **column_error_names;
        const char  *filename;
        bool        cat_type_detected = false;
        
        filename = cpl_frame_get_filename(cat_frame);
        cassure(                            filename != NULL,
                                            CPL_ERROR_NULL_INPUT,
                                            return stdlist,
                                            "filename of frame %d is NULL",
                                            iframe);

        cpl_table_delete(cat_table);
        cat_table = cpl_table_load(filename, 1, 1);
        if (!cpl_errorstate_is_equal(errstat))
        {
            frame_error_strings[iframe] = cpl_sprintf(
                                            "could not load FITS table");
            if (require_all_frames)
            {
                cassure(                    0,
                                            CPL_ERROR_DATA_NOT_FOUND,
                                            return stdlist,
                                            "%s: %s",
                                            filename,
                                            frame_error_strings[iframe]);
            }
            else
            {
                /* reset last error */
                cpl_errorstate_set(errstat);
                cpl_msg_warning(            cpl_func, "Skipping %s (%s)",
                                            filename,
                                            frame_error_strings[iframe]);
                continue;   /* skip this frame */
            }
        }
        
        /* determine the type of the catalogue, and
         * accordingly get the names of the required columns.
         * For the import functions, we keep the array with the correct
         * order of the input column names, but we just invalidate the
         * non-required column names. */
        nmethods = sizeof(methods)/sizeof(*methods);
        for (imethod = 0; imethod < nmethods; imethod++)
        {
            cpl_array_delete(columns);
            columns = methods[imethod].get_column_names_func();
            
            cat_type_detected = fors_std_cat_check_method_and_columns(
                                            cat_table,
                                            columns,    /* is modified */
                                            methods[imethod].star_import_func,
                                            band,
                                            methods[imethod].name,
                                            &err_columns,
                                            &(methods[imethod].band_supported));
            passure(cpl_errorstate_is_equal(errstat), return stdlist);
            if (cat_type_detected)
                break;
        }
        if (!cat_type_detected)
        {
            if (!checked_support)   /* FIXME: this should be checked in another
                                       function, called before looping over
                                       frames */
            {
                bool band_generally_supported = false;
                for (imethod = 0; imethod < nmethods; imethod++)
                {
                    band_generally_supported |= methods[imethod].band_supported;
                }
                if (!band_generally_supported)
                {
                    cpl_error_set_message(  cpl_func,
                                            CPL_ERROR_UNSUPPORTED_MODE,
                                            "Optical band %c not supported",
                                            band);
                    cleanup;
                    return stdlist;
                }
                checked_support = true;
            }
            
            /* create an error message for this frame */
            if (!printed_supported)
            {
                char    *supported_methods = NULL;
                for (imethod = 0; imethod < nmethods; imethod++)
                {
                    if (methods[imethod].band_supported)
                    {
                        if (supported_methods == NULL)
                        {
                            supported_methods = cpl_sprintf(
                                            "%s",
                                            methods[imethod].name);
                        }
                        else    /* strcat... */
                        {
                            char    *s;
                            s = cpl_sprintf("%s, %s",
                                            supported_methods,
                                            methods[imethod].name);
                            cpl_free(supported_methods);
                            supported_methods = s;
                        }
                    }
                }
                cpl_msg_warning(            cpl_func,
                                            "Import of band %c supported for: "
                                            "%s",
                                            band,
                                            supported_methods);
                cpl_free(supported_methods);
                printed_supported = true;
            }
            
            frame_error_strings[iframe] = cpl_sprintf(
                                            "no cat. data for band %c found",
                                            band);
            if (require_all_frames)
            {
                cpl_error_set_message(      cpl_func,
                                            CPL_ERROR_DATA_NOT_FOUND,
                                            "%s: %s",
                                            filename,
                                            frame_error_strings[iframe]);
                cleanup;
                return stdlist;
            }
            else
            {
                cpl_msg_warning(            cpl_func, "Skipping %s (%s)",
                                            filename,
                                            frame_error_strings[iframe]);
                continue;   /* skip this frame */
            }
        }
        else
        {
            cpl_msg_info(                   cpl_func,
                                            "Loading %s catalogue from %s",
                                            methods[imethod].name,
                                            filename);
        }
        if (last_imethod >= 0 && last_imethod != imethod && !printed_warning)
        {
            cpl_msg_warning(                cpl_func,
                                            "Merging different types of "
                                            "catalogues");
            printed_warning = true;
        }
        last_imethod = imethod;
        
        /* prepare the actual import of catalogue values */
        ncolumns = cpl_array_get_size(columns);
        
        cpl_free(band_values);
        band_values = cpl_calloc(ncolumns, sizeof(*band_values));
        cpl_free(band_errors);
        band_errors = cpl_calloc(ncolumns, sizeof(*band_errors));
        
        column_value_names = cpl_array_get_data_string_const(columns);
        column_error_names = cpl_array_get_data_string_const(err_columns);
        passure(cpl_errorstate_is_equal(errstat), return stdlist);
        
        /* done with preparation, import stars from table rows */
        nrows = cpl_table_get_nrow(cat_table);
        n_cat_entries += nrows;
        for (row = 0; row < nrows; row++)
        {
            int     ib,
                    isnull;
            bool    valid;
            
            /* read all required values from table, ignore others to:
             * a) save time, and
             * b) use stars that were observed in the required bands but not
             *    in others */
            valid = true;
            for (ib = 0; ib < ncolumns; ib++)
            {
                if (column_value_names[ib] != NULL) /* if required */
                {
                    band_values[ib] = cpl_table_get(
                                            cat_table,
                                            column_value_names[ib],
                                            row,
                                            &isnull);
                    valid &= (isnull == 0);
                    
                    passure(column_error_names[ib] != NULL, return stdlist);
                    band_errors[ib] = cpl_table_get(
                                            cat_table,
                                            column_error_names[ib],
                                            row,
                                            &isnull);
                    valid &= (isnull == 0);
                }
            }
            
            if (!valid)
                continue;
            
            std_star = fors_std_star_new_from_table(
                                            cat_table,
                                            row,
                                            FORS_STD_CAT_COLUMN_RA,
                                            FORS_STD_CAT_COLUMN_DEC,
                                            NULL, NULL, /* corrected mag */
                                            NULL, NULL, /* catalogue mag */
                                            NULL, NULL, /* color */
                                            NULL,       /* covariance */
                                            NULL, NULL, /* x, y */
                                            FORS_STD_CAT_COLUMN_NAME);
            assure(                         std_star != NULL,
                                            return stdlist,
                                            NULL);
            
            methods[imethod].star_import_func(
                                            band_values,
                                            band_errors,
                                            band,
                                            &(std_star->cat_magnitude),
                                            &(std_star->dcat_magnitude),
                                            &(std_star->color),
                                            &(std_star->dcolor),
                                            &(std_star->cov_catm_color));
            assure(                         cpl_errorstate_is_equal(errstat),
                                            return stdlist,
                                            NULL);
            
            fors_std_star_compute_corrected_mag(
                                            std_star,
                                            color_term,
                                            dcolor_term);
            
            fors_std_star_list_insert(stdlist, std_star);
            std_star = NULL;
        }
    }
    
    if (!require_all_frames)
    {
        cassure(                            cpl_array_has_invalid(
                                                frame_error_messages),
                                            CPL_ERROR_DATA_NOT_FOUND,
                                            return stdlist,
                                            "No valid catalogue frame found");
    }
    
    /* Fixing the fact that the list object supports no "append" */
    /*fors_std_star_list_reverse(stdlist);*/
    
    cpl_msg_info(                           cpl_func,
                                            "Found %d catalogue standard stars "
                                            "for band %c (of %d catalogue "
                                            "entries)",
                                            fors_std_star_list_size(stdlist),
                                            band,
                                            n_cat_entries);
    
    fors_std_star_list  *retval = stdlist;
    stdlist = NULL;
    cleanup;
    return retval;
}

/*#undef cleanup
#define cleanup \
do { \
    cpl_array_delete(stetson_columns); stetson_columns = NULL; \
    cpl_frame_delete(cat_frame); cat_frame = NULL; \
} while (0)
*/
/**
 * @brief   Create a standard star catalogue in Stetson format.
 * @param   stdl    Standard star list
 * @param   band    Optical (filter) band
 * @return  Catalogue frame
 */
/*cpl_frame   *
fors_std_cat_test_create_stetson_format(    const fors_std_star_list    *stdl,
                                            char                        band)
{
    cpl_array       *stetson_columns = NULL;
    cpl_frame       *cat_frame = NULL;
    cpl_errorstate  errstat = cpl_errorstate_get();
    
    stetson_columns = fors_std_cat_stetson_get_column_names();
    assure(cpl_errorstate_is_equal(errstat), return cat_frame, NULL);
    fors_std_cat_reject_not_required_columns(
                                            stetson_columns,
                                            fors_std_cat_stetson_star_import,
                                            band);
    assure(cpl_errorstate_is_equal(errstat), return cat_frame, NULL);
    ...
    
}*/

/*----------------------------------------------------------------------------*/
/* old deprecated code, kept for safety */
/*----------------------------------------------------------------------------*/

const char *const FORS_DATA_STD_MAG[FORS_NUM_FILTER] =
{"U",
 "B",
 //"G",
 "V",  /* G uses V */
 "V",
 "R",
 "I",
 "Z"};

const char *const FORS_DATA_STD_DMAG[FORS_NUM_FILTER] =
{"ERR_U",
 "ERR_B",
 //"ERR_G",
 "ERR_V", /* G uses V */
 "ERR_V",
 "ERR_R",
 "ERR_I",
 "ERR_Z"};

const char *const FORS_DATA_STD_COL[FORS_NUM_FILTER] = 
{"U_B",
 "B_V",
 "B_V",
 "B_V",
 "V_R",
 "V_R",
 "?Z?"};

const char *const FORS_DATA_STD_RA   = "RA";
const char *const FORS_DATA_STD_DEC  = "DEC";
const char *const FORS_DATA_STD_NAME = "OBJECT";

#undef cleanup
#define cleanup \
do { \
    cpl_table_delete(t); \
    cpl_free((void *)ERR_B1); \
    cpl_free((void *)ERR_C1); \
    cpl_free((void *)ERR_C2); \
} while(0)

/**
 * @brief   Build catalogue
 * @param   cat_frames        pointing to one or more FITS files
 * @param   setting           instrument setting
 * @param   color_term        color term
 * @param   dcolor_term       color term error
 * @return list of standard stars
 *
 * This function builds a catalogue of standard stars and
 * patterns
 */
fors_std_star_list *
fors_std_cat_load_old(const cpl_frameset *cat_frames,
                  /*const fors_setting *setting,*/
                  char  optical_band,
                  double color_term, double dcolor_term)
{
    fors_std_star_list *c = NULL;
    cpl_table *t = NULL;
    const char *filename;
    const char *ERR_B1 = NULL;
    const char *ERR_C1 = NULL;
    const char *ERR_C2 = NULL;

    assure( cat_frames != NULL, return c, NULL );
    /*assure( setting != NULL, return c, NULL );*/

    /* For each input table
           if it contains the required column, then load 
    */
    c = fors_std_star_list_new();

    const cpl_frame *cat_frame;

    for (cat_frame = cpl_frameset_get_first_const(cat_frames);
         cat_frame != NULL;
         cat_frame = cpl_frameset_get_next_const(cat_frames)) {
        
        
        filename = cpl_frame_get_filename(cat_frame);
        assure( filename != NULL, return c, NULL );

        cpl_table_delete(t);
        t = cpl_table_load(filename, 1, 1);
        assure( !cpl_error_get_code(), return c, "Could not load FITS catalogue %s",
                filename);
        
        assure( cpl_table_has_column(t, FORS_DATA_STD_RA), return c, 
                "%s: Missing column %s", filename, FORS_DATA_STD_RA);
        
        assure( cpl_table_get_column_type(t, FORS_DATA_STD_RA) == CPL_TYPE_DOUBLE, 
                return c,
                "%s: Column %s type is %s, double expected", filename, FORS_DATA_STD_RA,
                fors_type_get_string(cpl_table_get_column_type(t, FORS_DATA_STD_RA)));
        
        
        assure( cpl_table_has_column(t, FORS_DATA_STD_DEC), return c, 
                "%s: Missing column %s", filename, FORS_DATA_STD_DEC);
        
        assure( cpl_table_get_column_type(t, FORS_DATA_STD_DEC) == CPL_TYPE_DOUBLE, 
                return c, 
                "%s: Column %s type is %s, double expected", filename, FORS_DATA_STD_DEC,
                fors_type_get_string(cpl_table_get_column_type(t, FORS_DATA_STD_DEC)));


        assure( cpl_table_has_column(t, FORS_DATA_STD_NAME), return c, 
                "%s: Missing column %s", filename, FORS_DATA_STD_NAME);
        
        assure( cpl_table_get_column_type(t, FORS_DATA_STD_NAME) == CPL_TYPE_STRING, 
                return c,
                "%s: Column %s type is %s, string expected", filename, 
                FORS_DATA_STD_NAME,
                fors_type_get_string(cpl_table_get_column_type(t, FORS_DATA_STD_NAME)));
        

        /*const char *B1 = FORS_DATA_STD_MAG[setting->filter];
        assure( B1 != NULL, return c, NULL );*/
        char B1[2] = {'\0', '\0'};
        /* *B1 = fors_instrument_filterband_get_by_setting(setting);*/
        *B1 = optical_band;
        
        if ( cpl_table_has_column(t, B1) ) {
            
            /* 
                The error propagation depends on which
                error bars are available, which are assumed to be
                uncorrelated

                Pseudo code:

                given band B1 and color term C1-C2

                If have ERR_B1 and ERR_C1 and ERR_C2
                    // Stetson like
                    B1   + c(C1 - B1)      =  (1-c)B1  + c C1
                   (1-c)^2errB1^2 + c^2 errC1^2
                    +errc^2 * (C1-B1)^2

                    B1   + c(B1 - C2)      =  (1+c)B1  - c C2     

                   (1+c)^2errB1^2 + c^2 errC2^2
                    +errc^2 (B1-C2)^2

                    B1   + c(C1 - C2)
                    errB1^2 + c^2 errC1^2 + c^2 errC2^2
                    +errc^2 (C1-C2)^2

                    Special case:
                    G = V + 0.56*(B-V) - 0.12  (Fukugita et al. 1996, AJ 111, p1748)

                    magG   = G + c*(B-V) = V + (0.56+c)*(B-V) - 0.12
                           = (1 - 0.56 - c)*V  + (0.56+c)*B - 0.12

                    errG^2 = (1 - 0.56 - c)^2*errV^2  + (0.56+c)^2*errB^2
                             +errc^2 (B-V)^2
                else
                    // Landolt like
                    magU  = V      +    (B-V)   + (U-B) + c(U-B)
                    err^2 = errV^2 + err(B-V)^2 + (1+c)^2 err(U-B)^2
                          + errc^2 (U-B)^2

                    magB  = V      +    (B-V) + c(B-V)
                    err^2 = errV^2 + (1+c)^2 err(B-V)^2
                          + errc^2 (B-V)^2

                    magG  = V + (0.56+c)*(B-V) - 0.12  (Fukugita et al. 1996, AJ 111, p1748)
                    err^2 = errV^2 + (0.56+c)^2*err(B-V)^2
                          + errc^2 (B-V)^2

                    magV  = V      + c(B-V)
                    err^2 = errV^2 + c^2 err(B-V)^2
                          + errc^2 (B-V)^2

                    magR  = V - (V-R) + c (V-R) = V + (c-1)(V-R)
                    err^2 = errV^2 + (c-1)^2 err(V-R)^2
                          + errc^2 (V-R)^2

                    magI  = V      -    (V-I)   + c      (V-R)
                    err^2 = errV^2 + err(V-I)^2 + c^2 err(V-R)^2
                          + errc^2 (V-R)^2
            */

            /* Find other bands, C1, C2 */
            const char *col;/* =  FORS_DATA_STD_COL[setting->filter];*/
            switch (*B1)
            {
                case    'U': col = "U_B"; break;
                case    'B': col = "B_V"; break;
                case    'G': col = "B_V"; break;
                case    'V': col = "B_V"; break;
                case    'R': col = "V_R"; break;
                case    'I': col = "V_R"; break;
                case    'Z': col = "?Z?"; break;
                default: col = "";
            }
            double coeff = -color_term;  /* per convention */
            double dcoeff = dcolor_term;

            assure( strlen(col) == strlen("X_Y"), return c,
                    "Color term column must have format 'X_Y', is '%s'",
                    col );
            
            assure( col[1] == '_', return c,
                    "Color term column must have format 'X_Y', is '%s'",
                    col );
            
            char C1[2] = {'\0', '\0'};
            char C2[2] = {'\0', '\0'};

            *C1 = col[0];
            *C2 = col[2];

            ERR_B1 = cpl_sprintf("ERR_%s", B1);
            ERR_C1 = cpl_sprintf("ERR_%s", C1);
            ERR_C2 = cpl_sprintf("ERR_%s", C2);

            /* Catalog data */
            double cat_mag, dcat_mag, color;
            
            /* Color corrected magnitude */
            double mag, dmag;

            int i;
            for (i = 0; i < cpl_table_get_nrow(t); i++) {
                if (cpl_table_has_column(t, ERR_B1) &&
                    cpl_table_has_column(t, ERR_C1) &&
                    cpl_table_has_column(t, ERR_C2)) {
                    
                    /* Stetson, four cases */
/*                    if (setting->filter == FILTER_G) {*/
                    if (*B1 == 'G') {
                        if (cpl_table_is_valid(t, "V", i) &&
                            cpl_table_is_valid(t, "B", i) &&
                            cpl_table_is_valid(t, "ERR_B", i) &&
                            cpl_table_is_valid(t, "ERR_V", i)) {
                            double     v = cpl_table_get_float(t, "V", i, NULL);
                            double     b = cpl_table_get_float(t, "B", i, NULL);
                            double err_b = cpl_table_get_float(t, "ERR_B", i, NULL);
                            double err_v = cpl_table_get_float(t, "ERR_V", i, NULL);

                            color = b-v;
                            mag     = (1-0.56-coeff) * v + (0.56+coeff)*b - 0.12;
                            cat_mag = (1-0.56      ) * v + (0.56      )*b - 0.12;

                            dmag = 
                                sqrt(
                                    (1-0.56-coeff)*(1-0.56-coeff)*err_v*err_v +
                                    (0.56  +coeff)*(0.56  +coeff)*err_b*err_b +
                                    +
                                    dcoeff*dcoeff*color*color);

                            dcat_mag = sqrt(
                                (1-0.56-0)*(1-0.56-0)*err_v*err_v +
                                (0.56  +0)*(0.56  +0)*err_b*err_b);
                        }
                    }
                    else if (*B1 == *C2) {
                        if (cpl_table_is_valid(t, B1, i) &&
                            cpl_table_is_valid(t, C1, i) &&
                            cpl_table_is_valid(t, ERR_B1, i) &&
                            cpl_table_is_valid(t, ERR_C1, i)) {
                            double     b1 = cpl_table_get_float(t, B1, i, NULL);
                            double     c1 = cpl_table_get_float(t, C1, i, NULL);
                            double err_b1 = cpl_table_get_float(t, ERR_B1, i, NULL);
                            double err_c1 = cpl_table_get_float(t, ERR_C1, i, NULL);

                            color = c1-b1;

                            cat_mag = b1;
                            dcat_mag = err_b1;

                            mag = 
                                (1-coeff) * b1
                                +  coeff  * c1;
                            dmag = 
                                sqrt(
                                    (1-coeff)*(1-coeff)*err_b1*err_b1 +
                                           coeff*coeff *err_c1*err_c1
                                    +
                                    dcoeff*dcoeff*color*color);
                        }
                        else continue;
                    }
                    else if (*B1 == *C1) {
                        if (cpl_table_is_valid(t, B1, i) &&
                            cpl_table_is_valid(t, C2, i) &&
                            cpl_table_is_valid(t, ERR_B1, i) &&
                            cpl_table_is_valid(t, ERR_C2, i)) {
                            double     b1 = cpl_table_get_float(t, B1, i, NULL);
                            double     c2 = cpl_table_get_float(t, C2, i, NULL);
                            double err_b1 = cpl_table_get_float(t, ERR_B1, i, NULL);
                            double err_c2 = cpl_table_get_float(t, ERR_C2, i, NULL);

                            color = b1-c2;
                            
                            cat_mag = b1;
                            dcat_mag = err_b1;
                            
                            mag = 
                                (1+coeff) * b1
                                -  coeff  * c2;
                            dmag = 
                                sqrt(
                                    (1+coeff)*(1+coeff)*err_b1*err_b1 +
                                    coeff*coeff *err_c2*err_c2
                                    +
                                    dcoeff*dcoeff*color*color);

                        }
                        else continue;
                    }
                    else {
                        /* All different */
                        if (cpl_table_is_valid(t, B1, i) &&
                            cpl_table_is_valid(t, C1, i) &&
                            cpl_table_is_valid(t, C2, i) &&
                            cpl_table_is_valid(t, ERR_B1, i) &&
                            cpl_table_is_valid(t, ERR_C1, i) &&
                            cpl_table_is_valid(t, ERR_C2, i)) {
                            double     b1 = cpl_table_get_float(t, B1, i, NULL);
                            double     c1 = cpl_table_get_float(t, C1, i, NULL);
                            double     c2 = cpl_table_get_float(t, C2, i, NULL);
                            double err_b1 = cpl_table_get_float(t, ERR_B1, i, NULL);
                            double err_c1 = cpl_table_get_float(t, ERR_C1, i, NULL);
                            double err_c2 = cpl_table_get_float(t, ERR_C2, i, NULL);

                            color = c1-c2;

                            cat_mag = b1;
                            dcat_mag = err_b1;
                            
                            mag = b1
                                + coeff * c1
                                - coeff * c2;
                            dmag = sqrt(
                                err_b1*err_b1 +
                                coeff*coeff * err_c1*err_c1 +
                                coeff*coeff * err_c2*err_c2 +
                                dcoeff*dcoeff * color*color);
                        }
                        else continue;
                    }
                } /* If stetson, else */
/*                else if (setting->filter == FILTER_G) {*/
                else if (*B1 == 'G') {
                    if (cpl_table_is_valid(t, "V", i) &&
                        cpl_table_is_valid(t, "B_V", i) &&
                        cpl_table_is_valid(t, "ERR_V", i) &&
                        cpl_table_is_valid(t, "ERR_B_V", i)) {
                        double     v   = cpl_table_get_float(t, "V", i, NULL);
                        double    bv   = cpl_table_get_float(t, "B_V", i, NULL);
                        double err_v   = cpl_table_get_float(t, "ERR_V", i, NULL);
                        double err_b_v = cpl_table_get_float(t, "ERR_B_V", i, NULL);

                        color = bv;

                        cat_mag = v + (0.56)*(bv) - 0.12;

                        mag  = v + (0.56+coeff)*(bv) - 0.12;
                        dmag = 
                            sqrt(
                                err_v*err_v +
                                (0.56+coeff)*(0.56+coeff)*err_b_v*err_b_v +
                                +
                                dcoeff*dcoeff*bv*bv);
                        dcat_mag = 
                            sqrt(
                                err_v*err_v +
                                (0.56)*(0.56)*err_b_v*err_b_v);
                    }
                }
                else switch (*B1) {
                    /* Landolt, every band is a special case */
                case 'U':
                    if (cpl_table_is_valid(t, "U", i) &&
                        cpl_table_is_valid(t, "U_B", i) &&
                        cpl_table_is_valid(t, "ERR_V", i) &&
                        cpl_table_is_valid(t, "ERR_B_V", i) &&
                        cpl_table_is_valid(t, "ERR_U_B", i)) {
                        
                        double err_v  = cpl_table_get_float(t, "ERR_V", i, NULL);
                        double err_bv = cpl_table_get_float(t, "ERR_B_V", i, NULL);
                        double err_ub = cpl_table_get_float(t, "ERR_U_B", i, NULL);
                        double ub     = cpl_table_get_float(t, "U_B", i, NULL);

                        color = ub;

                        cat_mag = cpl_table_get_float(t, "U", i, NULL);
                        
                        mag = cat_mag + coeff * ub;
                        dmag = sqrt(err_v*err_v + err_bv*err_bv+
                                    (1+coeff)*(1+coeff)*err_ub*err_ub +
                                    dcoeff*dcoeff*ub*ub);
                        
                        dcat_mag = sqrt(err_v*err_v + err_bv*err_bv+
                                        err_ub*err_ub);
                    } 
                    else continue; /* to next for(...) iteration */
                    break; /* out of switch */
                case 'B':
                    if (cpl_table_is_valid(t, "B", i) &&
                        cpl_table_is_valid(t, "B_V", i) &&
                        cpl_table_is_valid(t, "ERR_V", i) &&
                        cpl_table_is_valid(t, "ERR_B_V", i)) {
                        
                        double err_v  = cpl_table_get_float(t, "ERR_V", i, NULL);
                        double err_bv = cpl_table_get_float(t, "ERR_B_V", i, NULL);
                        double     bv = cpl_table_get_float(t, "B_V", i, NULL);

                        color = bv;
                        cat_mag = cpl_table_get_float(t, "B", i, NULL);

                        mag = cat_mag +
                            coeff * bv;
                        dmag = sqrt(err_v*err_v +
                                    (1+coeff)*(1+coeff)*err_bv*err_bv +
                                    dcoeff*dcoeff*bv*bv);
                        dcat_mag = sqrt(err_v*err_v +
                                        err_bv*err_bv);
                    }
                    else continue;
                    break;
                case 'V':
                    if (cpl_table_is_valid(t, "V", i) &&
                        cpl_table_is_valid(t, "B_V", i) &&
                        cpl_table_is_valid(t, "ERR_V", i) &&
                        cpl_table_is_valid(t, "ERR_B_V", i)) {
                        
                        double err_v  = cpl_table_get_float(t, "ERR_V", i, NULL);
                        double err_bv = cpl_table_get_float(t, "ERR_B_V", i, NULL);
                        double     bv = cpl_table_get_float(t, "B_V", i, NULL);

                        color = bv;
                        cat_mag = cpl_table_get_float(t, "V", i, NULL);
                        dcat_mag = err_v;
                        
                        mag = cat_mag + coeff * bv;
                        dmag = sqrt(err_v*err_v +
                                    coeff*coeff*err_bv*err_bv +
                                    dcoeff*dcoeff*bv*bv);
                    }
                    else continue;
                    break;
                case 'R':
                    if (cpl_table_is_valid(t, "R", i) &&
                        cpl_table_is_valid(t, "V_R", i) &&
                        cpl_table_is_valid(t, "ERR_V", i) &&
                        cpl_table_is_valid(t, "ERR_V_R", i)) {
                        
                        double err_v  = cpl_table_get_float(t, "ERR_V", i, NULL);
                        double err_vr = cpl_table_get_float(t, "ERR_V_R", i, NULL);
                        double     vr = cpl_table_get_float(t, "V_R", i, NULL);

                        color = vr;
                        cat_mag = cpl_table_get_float(t, "R", i, NULL);
                        mag = cat_mag + coeff * vr;
                        dmag = sqrt(err_v*err_v +
                                    (1-coeff)*(1-coeff)*err_vr*err_vr +
                                    dcoeff*dcoeff*vr*vr);
                        dcat_mag = sqrt(err_v*err_v + err_vr*err_vr);
                    }
                    else continue;
                    break;
                case 'I':
                    if (cpl_table_is_valid(t, "I", i) &&
                        cpl_table_is_valid(t, "V_R", i) &&
                        cpl_table_is_valid(t, "ERR_V", i) &&
                        cpl_table_is_valid(t, "ERR_V_I", i) &&
                        cpl_table_is_valid(t, "ERR_V_R", i)) {
                        
                        double err_v  = cpl_table_get_float(t, "ERR_V", i, NULL);
                        double err_vi = cpl_table_get_float(t, "ERR_V_I", i, NULL);
                        double err_vr = cpl_table_get_float(t, "ERR_V_R", i, NULL);
                        double     vr = cpl_table_get_float(t, "V_R", i, NULL);

                        color = vr;
                        cat_mag = cpl_table_get_float(t, "I", i, NULL);
                        mag = cat_mag + coeff * vr;
                        dmag = sqrt(err_v*err_v + err_vi*err_vi+
                                    coeff*coeff*err_vr*err_vr +
                                    dcoeff*dcoeff*vr*vr);
                        dcat_mag = sqrt(err_v*err_v + err_vi*err_vi);
                    }
                    else continue;
                    break;
                default:
                    assure( false, return c, 
                            "Unknown filter: %s", B1);
                    break;
                }
                
                double ra = cpl_table_get_double(t, FORS_DATA_STD_RA,
                                                 i, NULL);
                double dec = cpl_table_get_double(t, FORS_DATA_STD_DEC,
                                                  i, NULL);

                const char *std_name = cpl_table_get_string(t, FORS_DATA_STD_NAME,
                                                            i);
                
                fors_std_star_list_insert(
                    c, fors_std_star_new(ra, dec, mag, dmag, 
                                         cat_mag, dcat_mag,
                                         color, -1, -1, std_name));

            } /* for each table row */

#if 0  /* This is old code which removes doublets but is slow (O(n^2)),
        * Doublets will be removed during identification, after
        * selecting the (relatively few) stars that fall inside the CCD
        */
                    double nearest_dist;
                    if (fors_std_star_list_size(c) > 0) {
                        fors_std_star *nearest = fors_std_star_list_min_val(
                            c,
                            (fors_std_star_list_func_eval)
                            fors_std_star_dist_arcsec,
                            std);
                        
                        nearest_dist = fors_std_star_dist_arcsec(std, nearest);
                        cpl_msg_debug(cpl_func, "min dist = %f arcseconds",
                                      nearest_dist);
                    }
                    
                    if (fors_std_star_list_size(c) == 0 || nearest_dist > 5) {
                        fors_std_star_list_insert(c, std);
                    }
                    else {
                        fors_std_star_delete(&std);
                    }
#endif
        } /* if has column B1 */
        else {
            cpl_msg_info(cpl_func, "Skipping catalog %s, no column %s",
                         filename, B1);
        }
    } /* for each frame */
    
    cpl_msg_info(cpl_func, "Found %d catalogue standards",
                 fors_std_star_list_size(c));

    /* fors_std_cat_print(c); */

    cleanup;
    return c;
}
