/*
 * This file is part of the ESO UVES Pipeline
 * Copyright (C) 2004,2005 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */

/*
 * $Author: amodigli $
 * $Date: 2013-04-16 15:35:24 $
 * $Revision: 1.121 $
 * $Name: not supported by cvs2svn $
 * $Log: not supported by cvs2svn $
 * Revision 1.120  2013/03/08 12:58:23  amodigli
 * moved uves_min_int uves_min_double uves_error_fractionuves_max_int uves_max_int uves_max_double uves_round_double and similar small functions to header to speed-up
 *
 * Revision 1.119  2013/02/12 10:52:38  amodigli
 * removed scaling by exptime as requested by Daniel PIPE-4020
 *
 * Revision 1.118  2011/12/08 13:54:40  amodigli
 * include uves_cpl_size.h for CPL6
 *
 * Revision 1.117  2011/01/11 18:00:16  amodigli
 * fixed compiler warnings (DFS09815)
 *
 * Revision 1.116  2010/12/20 16:35:57  amodigli
 * added uves_table_remove_units(), uves_table_unify_units(), uves_tablenames_unify_units(), uves_tablename_remove_units(), useful in flames/uves to fix a problem on raw order fibre table generated by flames_cal_prep_sff_ofpos that has in an instance units filled by a string like '   ' and is being merged with a table with empty units. CPL5.3 makes a check on table column units consistency
 *
 * Revision 1.115  2010/12/16 16:57:40  amodigli
 * fixed compiler warnings
 *
 * Revision 1.114  2010/12/01 11:32:22  amodigli
 * add uves_image_mflat_detect_blemishes()
 *
 * Revision 1.113  2010/11/26 07:43:11  amodigli
 * added uves_get_wave_map()
 *
 * Revision 1.112  2010/11/25 10:37:59  amodigli
 * changed interface uves_flat_create_normalized_master()
 *
 * Revision 1.111  2010/10/06 07:52:13  amodigli
 * changed interface uves_flat_create_normalized_master2()
 *
 * Revision 1.110  2010/09/29 09:40:05  amodigli
 * fixed compiler warnings (changed function interface)
 *
 * Revision 1.109  2010/09/24 09:32:08  amodigli
 * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
 *
 * Revision 1.107  2010/06/02 09:19:01  amodigli
 * removed chip param from uves_imagelist_get_clean_mean_levels()
 *
 * Revision 1.106  2010/05/18 11:37:44  amodigli
 * added uves_imagelist_get_clean_mean_levels
 *
 * Revision 1.105  2010/05/04 17:21:14  amodigli
 * added uves_flat_create_normalized_master2
 *
 * Revision 1.104  2010/04/07 06:29:19  amodigli
 * fixed compiler warnings
 *
 * Revision 1.103  2010/04/06 17:39:39  amodigli
 * added uves_flat_create_normalized_master()
 *
 * Revision 1.102  2010/03/22 15:56:15  amodigli
 * added uves_ksigma_stack
 *
 * Revision 1.101  2009/08/03 12:46:44  amodigli
 * added uves_image_smooth_y
 *
 * Revision 1.100  2009/07/28 13:48:52  amodigli
 * added smoothing functions
 *
 * Revision 1.99  2008/03/25 08:55:21  amodigli
 * check if inline is defined to remove possible compilation warnings
 *
 * Revision 1.98  2008/02/29 10:26:08  amodigli
 * added uves_rcosmic
 *
 * Revision 1.97  2008/02/15 12:43:21  amodigli
 * added uves_string_tolower uves_string_toupper
 *
 * Revision 1.96  2008/02/04 14:08:58  amodigli
 * added uves_parameterlist_duplicate
 *
 * Revision 1.95  2007/09/11 12:11:49  amodigli
 * added uves_frameset_extract
 *
 * Revision 1.94  2007/08/02 15:18:44  amodigli
 * added uves_frameset_dump
 *
 * Revision 1.93  2007/06/06 08:17:33  amodigli
 * replace tab with 4 spaces
 *
 * Revision 1.92  2007/05/22 11:30:57  jmlarsen
 * Removed MIDAS flag for good
 *
 * Revision 1.91  2007/05/02 13:18:52  jmlarsen
 * Added function to simulate reconstruct raw image
 *
 * Revision 1.90  2007/04/24 12:50:29  jmlarsen
 * Replaced cpl_propertylist -> uves_propertylist which is much faster
 *
 * Revision 1.89  2007/04/12 11:58:07  jmlarsen
 * Check compile time CPL version number
 *
 * Revision 1.88  2007/04/10 07:10:50  jmlarsen
 * uves_spline_hermite(): maintain current array position (for efficiency)
 *
 * Revision 1.87  2007/03/28 11:39:38  jmlarsen
 * Removed MIDAS flag from uves_define_noise
 *
 * Revision 1.86  2007/03/13 15:34:07  jmlarsen
 * Parametrize verbosity of autodegree fitting function
 *
 * Revision 1.85  2007/03/05 10:19:54  jmlarsen
 * Define SPEED_OF_LIGHT
 *
 * Revision 1.84  2007/02/22 15:34:48  jmlarsen
 * Implement gaussian function with linear background
 *
 * Revision 1.83  2006/11/13 12:49:38  jmlarsen
 * Removed re-definition of cpl_table_erase_selected
 *
 * Revision 1.82  2006/11/07 14:05:35  jmlarsen
 * Removed flag to enable/disable FLAMES code generation
 *
 * Revision 1.81  2006/11/06 15:19:42  jmlarsen
 * Removed unused include directives
 *
 * Revision 1.80  2006/11/03 15:01:21  jmlarsen
 * Killed UVES 3d table module and use CPL 3d tables
 *
 * Revision 1.79  2006/10/19 08:23:09  jmlarsen
 * Enabled FLAMES code
 *
 * Revision 1.78  2006/10/12 11:37:28  jmlarsen
 * Temporarily disabled FLAMES code generation
 *
 * Revision 1.77  2006/10/09 13:04:22  jmlarsen
 * Removed message domain parameter of uves_initialize
 *
 * Revision 1.76  2006/09/20 12:53:57  jmlarsen
 * Replaced stringcat functions with uves_sprintf()
 *
 * Revision 1.75  2006/09/08 14:05:39  jmlarsen
 * Added max/min allowed values in autodegree fitting
 *
 * Revision 1.74  2006/08/24 11:43:49  jmlarsen
 * Write recipe start/stop time to header
 *
 * Revision 1.73  2006/08/17 13:56:53  jmlarsen
 * Reduced max line length
 *
 * Revision 1.72  2006/08/11 14:37:30  jmlarsen
 * Implemented workaround for slow cpl_table_erase_selected
 *
 * Revision 1.71  2006/08/11 11:29:11  jmlarsen
 * uves_get_version_binary
 *
 * Revision 1.70  2006/08/08 11:27:18  amodigli
 * upgrade to CPL3
 *
 * Revision 1.69  2006/07/14 12:42:42  jmlarsen
 * Added function uves_strincat_4
 *
 * Revision 1.68  2006/06/22 09:44:02  jmlarsen
 * Added function to remove string prefix
 *
 * Revision 1.67  2006/06/01 14:43:17  jmlarsen
 * Added missing documentation
 *
 * Revision 1.66  2006/05/12 15:12:11  jmlarsen
 * Support minimum RMS in auto-degree fitting
 *
 * Revision 1.65  2006/05/05 13:58:09  jmlarsen
 * Added uves_polynomial_regression_2d_autodegree
 *
 * Revision 1.64  2006/04/24 09:26:37  jmlarsen
 * Added code to compute Moffat profile
 *
 * Revision 1.63  2006/04/06 08:51:43  jmlarsen
 * Allow setting WANT_TIME_MEASURE when ./configure'ing
 *
 * Revision 1.62  2006/03/24 13:48:09  jmlarsen
 * Macro to turn on/off timing info
 *
 * Revision 1.61  2006/03/09 10:55:50  jmlarsen
 * Added timing macros
 *
 * Revision 1.60  2006/02/28 09:15:23  jmlarsen
 * Minor update
 *
 * Revision 1.59  2006/02/21 14:26:54  jmlarsen
 * Minor changes
 *
 * Revision 1.58  2006/02/15 13:19:15  jmlarsen
 * Reduced source code max. line length
 *
 * Revision 1.57  2006/01/12 15:41:14  jmlarsen
 * Moved gauss. fitting to irplib
 *
 * Revision 1.56  2005/12/20 08:11:44  jmlarsen
 * Added CVS  entry
 *
 */
#ifndef UVES_UTILS_H
#define UVES_UTILS_H


#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif
#include <uves_cpl_size.h>
/*-----------------------------------------------------------------------------
                    Includes
 -----------------------------------------------------------------------------*/

#include <uves_utils_polynomial.h>
#include <uves_extract_iterate.h>
#include <uves_extract_profile.h>
#include <uves_chip.h>

#include <cpl.h>
#include <math.h>

/*-----------------------------------------------------------------------------
                             Defines
 -----------------------------------------------------------------------------*/

/* If __func__ is not declared, use a default function name */
#if defined HAVE_DECL___FUNC__ && !HAVE_DECL___FUNC__
//static const char __func__[] = "<fctid>";
#ifndef __func__
#define __func__ "<fct.id>"
#endif
#endif

#ifndef inline
#define inline /* inline */
#endif

/*
 * The purpose of this target is to
 * decrease the amount of messages
 * printed at the debug level.
 *
 * If set to non-zero, even more messages
 * are printed at the debug level
 * (sometimes 50 - 100 MB)
 *
 */
#ifndef WANT_BIG_LOGFILE
#define WANT_BIG_LOGFILE 0
#endif

/*
 * Set to 1 to show timing
 * information on msg-level = info
 */
#ifndef WANT_TIME_MEASURE
#define WANT_TIME_MEASURE 0
#endif


#if WANT_TIME_MEASURE
#define UVES_TIME_START(what) uves_msg("Timing (%s, l%d) %s start", \
                       __FILE__, __LINE__, what)
#define UVES_TIME_END         uves_msg("Timing (%s, l%d) end", \
                       __FILE__, __LINE__)
#else
#define UVES_TIME_START(what) uves_msg_debug("Timing (%s, l%d) %s start", \
                         __FILE__, __LINE__, what)
#define UVES_TIME_END         uves_msg_debug("Timing (%s, l%d) end", \
                         __FILE__, __LINE__)
#endif


#ifndef stringify
#ifndef make_str
#define stringify(X) #X
#define make_str(X) stringify(X)
#endif
#endif

#define TWOSQRT2LN2 2.35482004503095

#ifndef M_PI 
#define M_PI 3.1415926535897932384626433832795
#endif

#define SPEED_OF_LIGHT  299792458    /* SI-units */

#define COS_DEG(x) cos(((x)/180)*M_PI)
#define SIN_DEG(x) sin(((x)/180)*M_PI)
#define ACOS_DEG(x) (acos(x)*180/M_PI)

/*-----------------------------------------------------------------------------
                                   Prototypes
 -----------------------------------------------------------------------------*/

cpl_image *
uves_create_image(uves_iterate_position *pos, enum uves_chip chip,
                  const cpl_image *spectrum, const cpl_image *sky,
                  const cpl_image *cosmic_image,
                  const uves_extract_profile *profile,
                  cpl_image **image_noise, uves_propertylist **image_header);
cpl_frameset *
uves_frameset_extract(const cpl_frameset *frames,const char *tag);

const char*
uves_string_tolower(char* s);
const char*
uves_string_toupper(char* s);

double uves_gaussrand(void);
double uves_pow_int(double x, int y);

/*----------------------------------------------------------------------------*/
/**
   @brief   Round a number to the nearest integer
   @param   x       The number to round
   @return  Nearest integer

   This is implemented as a function rather than a macro to avoid multiple
   evaluations of expressions that have side effects.
*/
/*----------------------------------------------------------------------------*/
static inline long
uves_round_double(double x)
{
    return (x >=0) ? (long)(x+0.5) : (long)(x-0.5);
}

/*----------------------------------------------------------------------------*/
/**
   @brief   Maximum of two numbers
   @param   x        First number
   @param   y        Second number
   @return  Maximum of @em x and @em y

   See also @c uves_round_double() .
*/
/*----------------------------------------------------------------------------*/
static inline double
uves_max_double(double x, double y)
{
    return (x >=y) ? x : y;
}
/*----------------------------------------------------------------------------*/
/**
   @brief   Maximum of two numbers
   @param   x        First number
   @param   y        Second number  
   @return  Maximum of @em x and @em y
  
   See also @c uves_round_double() .
*/
/*----------------------------------------------------------------------------*/
static inline int
uves_max_int(int x, int y)
{
    return (x >=y) ? x : y;
}

/*----------------------------------------------------------------------------*/
/**
   @brief   Minimum of two numbers
   @param   x        First number
   @param   y        Second number
   @return  Minimum of @em x and @em y
  
   See also @c uves_round_double() .
*/
/*----------------------------------------------------------------------------*/
static inline double
uves_min_double(double x, double y)
{
    return (x <=y) ? x : y;
}
/*----------------------------------------------------------------------------*/
/**
   @brief   Minimum of two numbers
   @param   x        First number
   @param   y        Second number
   @return  Minimum of @em x and @em y
  
   See also @c uves_round_double() .
*/
/*----------------------------------------------------------------------------*/
static inline int
uves_min_int(int x, int y)
{
    return (x <=y) ? x : y;
}

/*----------------------------------------------------------------------------*/
/**
   @brief   Get uncertainty of division
   @param  x  numerator
   @param  y  denominator
   @param  dx  uncertainty (one sigma) of @em x
   @param  dy  uncertainty (one sigma) of @em y
   @param  MIDAS  implement MIDAS formula
   @return  The uncertainty of (x/y)

*/
/*----------------------------------------------------------------------------*/
static inline double
uves_error_fraction(double x, double y, double dx, double dy)
{
    /* Error propagation:
     * sigma(x/y)^2 = (1/y sigma(x))^2 + (-x/y^2 sigma(y))^2 
     */
    return sqrt( dx*dx/(y*y) + x*x*dy*dy/(y*y*y*y) );
}


const char *uves_get_license(void);
cpl_error_code uves_get_version(int *major, int *minor, int *micro);
int uves_get_version_binary(void);

char * uves_initialize(cpl_frameset *frames, const cpl_parameterlist *parlist, 
               const char *recipe_id, const char *short_descr);
cpl_error_code uves_end(const char *recipe_id, const cpl_frameset *frames);

uves_propertylist *uves_initialize_image_header(const char *ctype1, const char *ctype2, const char *cunit1, const char *cunit2, 
                           const char *bunit,const double bscale,
                           double crval1 , double crval2,
                           double crpix1 , double crpix2,
                           double crdelt1, double crdelt2);

cpl_parameterlist* 
uves_parameterlist_duplicate(const cpl_parameterlist* pin);

cpl_image *uves_define_noise(const cpl_image *image, const uves_propertylist *image_header,
                 int ncom, enum uves_chip);
cpl_image *uves_average_images(const cpl_image *image1, const cpl_image *noise1,
                   const cpl_image *image2, const cpl_image *noise2,
                   cpl_image **noise);


cpl_error_code uves_subtract_bias(cpl_image *image, const cpl_image *master_bias);
cpl_error_code uves_subtract_dark(cpl_image *image, const uves_propertylist *image_header,
                  const cpl_image *master_dark, 
                  const uves_propertylist *mdark_header);

polynomial *
uves_polynomial_regression_2d_autodegree(cpl_table *t,
                     const char *X1, const char *X2, const char *Y,
                     const char *sigmaY,
                     const char *polynomial_fit,
                     const char *residual_square, 
                     const char *variance_fit,
                     double *mean_squared_error, double *red_chisq,
                     polynomial **variance, double kappa,
                     int maxdeg1, int maxdeg2, double min_rms,
                                         double min_reject,
                                         bool verbose,
                     const double *min_val,
                     const double *max_val,
                     int npos, double positions[][2]);

polynomial *
uves_polynomial_regression_2d(cpl_table *t,
                  const char *X1, const char *X2, const char *Y, 
                  const char *sigmaY,
                  int degree1, int degree2,
                  const char *polynomial_fit, const char *residual_square, 
                  const char *variance_fit,
                  double *mse, double *red_chisq,
                  polynomial **variance, double kappa,
                              double min_reject);

polynomial *uves_polynomial_regression_1d(cpl_table *t, 
                      const char *X, const char *Y, const char *sigmaY, 
                      int degree, 
                      const char *polynomial_fit, const char *residual_square,
                      double *mean_squared_error, const double kappa);

const char *uves_remove_string_prefix(const char *s, const char *prefix);

double uves_spline_hermite_table( double xp, const cpl_table *t, const char *column_x, 
                  const char *column_y, int *istart );

double uves_spline_hermite( double xp, const double *x, const double *y, int n, int *istart );

double uves_spline_cubic(double xp, double *x, float *y, float *y2, int n, int *kstart );

int uves_absolute_order(int first_abs_order, int last_abs_order, int relative_order);

double uves_average_reject(cpl_table *t,
               const char *column,
               const char *residual2,
               double kappa);


cpl_table *uves_ordertable_traces_new(void);
cpl_error_code uves_ordertable_traces_add(cpl_table *traces, 
                      int fibre_ID, double fibre_offset, int fibre_mask);
bool uves_table_is_sorted_double(const cpl_table *t, const char *column, const bool reverse);

int uves_moffat(const double x[], const double a[], double *result);
int uves_moffat_derivative(const double x[], const double a[], double result[]);
int uves_gauss(const double x[], const double a[], double *result);
int uves_gauss_derivative(const double x[], const double a[], double result[]);
int uves_gauss_linear(const double x[], const double a[], double *result);
int uves_gauss_linear_derivative(const double x[], const double a[], double result[]);
void uves_check_version(void);
void uves_frameset_dump(cpl_frameset* set);
cpl_error_code
uves_rcosmic(cpl_image* ima,
             cpl_image** flt,
             cpl_image** out,
             cpl_image** msk,
             const double sky,
             const double ron,
             const double gain,
             const int ns,
             const double rc);

cpl_image *
uves_image_smooth_x(cpl_image * inp, const int r);
cpl_image *
uves_image_smooth_y(cpl_image * inp, const int r);

cpl_image *
uves_image_smooth_mean_x(cpl_image * inp, const int r);

cpl_image *
uves_image_smooth_median_x(cpl_image * inp, const int r);
cpl_image *
uves_image_smooth_fft(cpl_image * inp, const int fx);

cpl_image *
uves_ksigma_stack(const cpl_imagelist *imlist, double klow, double khigh, int kiter);


cpl_image *
uves_flat_create_normalized_master(cpl_imagelist * flats,
				   const cpl_table *ordertable,
                                   const polynomial* order_locations,
				   const cpl_vector* gain_vals,
				   double* fnoise);

cpl_image *
uves_flat_create_normalized_master2(cpl_imagelist * flats,
                                   const cpl_table *ordertable,
                                   const polynomial* order_locations,
                                    const cpl_image* mflat);

cpl_vector * 
uves_imagelist_get_clean_mean_levels(cpl_imagelist* iml, double kappa);
cpl_error_code
uves_imagelist_subtract_values(cpl_imagelist** iml, cpl_vector* values);
cpl_image *
uves_get_wave_map(cpl_image * ima_sci,
		  const char *context,
                  const cpl_parameterlist *parameters,
		  const cpl_table *ordertable,
		  const cpl_table *linetable,
		  const polynomial* order_locations,
		  const polynomial *dispersion_relation,
		  const int first_abs_order,
		  const int last_abs_order,
		  const int slit_size);

cpl_image*
uves_image_mflat_detect_blemishes(const cpl_image* flat, 
                                  const uves_propertylist* head);


cpl_error_code
uves_table_remove_units(cpl_table **table);
cpl_error_code
uves_table_unify_units(cpl_table **table2,  cpl_table **table1);
cpl_error_code
uves_tablenames_unify_units(const char* tname2, const char* tname1);
cpl_error_code
uves_tablename_remove_units(const char* tname);

#endif
