/*===========================================================================
  Copyright (C) 1995-2009 European Southern Observatory (ESO)
 
  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., 675 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.IDENTIFICATION: routine mo_fitoff
.KEYWORDS:       bulk data frames, mosaicing, offset
.PURPOSE:        Finds the relative offsets in background intensity between
                 every pair of overlapping frames in a mosaic.
                 Construct a mosaic of a number of image frames, the result
                 will a frame with nx subframes in x and ny subframes in y.
                 The subframes must have the same size in x and y.
                 Blank subframes can be indicated on the command line.
.ALGORITHM:      Find the best fitting background offsets for each frame 
                 in a mosaic. This routine uses a chi-square minimization
                 to try to minimize the residual errors between relative 
                 offsets. The routine findoff must be run first to create
                 a file that contains the offsets between each pair of   
                 frames.
                 This routine has three inputs:                         
                    input_array containing the output from mo_offset;
                    exclude_array with subraster not be be included;
                    output_table with the resulting offset for each subraster;

                 Besides the output table, this routine will output the  
                 offset for each frame and the average of the square of 
                 residuals for each frame. At the end it will print the 
                 mean of these and the standard deviation. Bad frames   
                 will have large residuals and can be excluded from the    
                 fit by adding them to the exclude file and rerunning the 
                 routine.  
.VERSION:        950920 RHW Created; 
                            original Mike Regan (U of MD,mregan@astro.umd.edu 
 090723		last modif
-----------------------------------------------------------------------------*/
 
/*
 * Define _POSIX_SOURCE to indicate
 * that this is a POSIX program
 */
#define _POSIX_SOURCE 1

/*
 * definition of the used functions
 */
#include <mpfit.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ftoc.h>
#include <midas_def.h>
#include <computer.h>
#include <ccd_def.h>

#define  NPT    1000
#define  MA     120
#define  SPREAD 0.001

typedef struct {
    double * x;
    double * y;
} priv_data;

void MO_GETOFF(x,a,y,dyda,na)
double x,a[],*y,dyda[];
int na;
{
	int i,j,k,ii,jj;
	double cnt;

	for (k=0;k<na;k++) {
	  dyda[k]=0;
	}

	i = j = 0;

        if (x==0){
	  *y=0.0;}
	else{
	cnt=0.0;
	for (ii=0;ii<na;ii++){
	  for (jj=ii+1;jj<na;jj++){
	    cnt=cnt+1;
	    if (cnt==x){
	      i=ii;
	      j=jj;
	    }}}
	*y = a[j]-a[i];
	dyda[i] = -1;
	dyda[j] = 1;
      }
}

static int get_off(int ndata, int npar, double *p, double *deviates,
                   double **derivs, priv_data *d)
{
    int i;
    double * x = d->x;
    double * y = d->y;
    double * dyda = malloc(npar * sizeof(*dyda));

    /* Compute function deviates */
    for (i=0; i<ndata; i++) {
        double v;
        MO_GETOFF(x[i], p, &v, dyda, npar);
        deviates[i] = y[i] - v;
        if (derivs) {
            int j;
            for (j=0; j<npar; j++) {
                if (derivs[j]) {
                    derivs[j][i] = -dyda[j];
                }
            }
        }
    }
    free(dyda);
    return 0;
}

void fit_offset
(
 float x[], float y[], int count,
 float ain[], int npt
 )
{
    int i;
    priv_data pdata;
    double * a = malloc((npt) * sizeof(*a));
    mp_par * pars = calloc(npt, sizeof(*pars));
    pdata.x = malloc((count) * sizeof(double));
    pdata.y = malloc((count) * sizeof(double));

    for (i = 0; i < count; i++) {
        pdata.x[i] = x[i + 1];
        pdata.y[i] = y[i + 1];
    }

    for (i = 0; i < npt; i++) {
        a[i] = ain[i + 1];
    }

    for (i = 0; i < npt; i++) {
        //pars[i].deriv_debug = 1;
        pars[i].side = 3;
    }

    int s =mpfit((mp_func)&get_off, count, npt,
                  a, pars, NULL, &pdata, NULL);


    for (i = 0; i < npt; i++) {
        ain[i + 1] = a[i];
    }

    free(a);
    free(pdata.x);
    free(pdata.y);
    free(pars);
}

/*
 ++++++++++++++++++++++++++++++++++++++++++++++++++
 * here starts the code of the function
 ---------------------------------------------------
*/

void MO_FITOFF(npt, nover, y, x, iref, jref, excount, exclud, deltai)
int     npt;
int     nover;
float   *y;
float   *x;
int     *iref;
int     *jref;
int     excount;
int     *exclud;
float   *deltai;

{
int     count;
int     i, io, ip;
int     iran;
int     itst;
int     j,k;
int     mfit;
int     numsub;
int     exclude;

float   avgoff;
float   sum;
float   stdev,avgerr;
int     newindex[300];
int     xcount;
int     ex[300];

float   *cerror;
float   *error;
float   **diff;

char    line[80];

static float a[MA+1];

/*
 Allocated memory

 later on, always indexing from 1 to npt/mfit is used... (KB) 
*/

error  = (float *) osmmget(SQR(NPT)/2 * sizeof(float));
cerror = (float *) osmmget(SQR(NPT)/2 * sizeof(float));
diff = (float **) osmmget((npt+1) * sizeof(float *));
for (i = 0; i <= npt; i++)
   diff[i] = (float *) osmmget((npt+1) * sizeof(float));

/*
 Initialize the array
 */
for (i=1; i<=npt; i++)
   for (j=i+1; j<=npt; j++)
      diff[i][j] = 0.0;

/*
 Stores the input in the arrays
 */
count = 0;
for (ip = 1; ip <= nover; ip++)
   {
   i            = iref[ip];
   j            = jref[ip];
   diff[i][j]   = y[ip];
   cerror[ip]   = 0.0;
   count++;
   }
SCTPUT(" ");
sprintf(line, "Pairs of subrasters before editing = %d",count);
SCTPUT(line);

if (excount > 0) {
   for (iran = 1; iran <= excount; iran++)
     ex[iran] = exclud[iran-1];
   sprintf(line, "Number of subrasters deleted = %d", excount);
   SCTPUT(line);

   numsub = 0;
   for (i = 1; i <= npt; i++) {
      exclude = 0;
      for (j = 1; j <= excount; j++) {
         if (i == ex[j]) {
	    exclude = 1;
            numsub++;
         }
      }

      if (exclude == 1)
         newindex[i] = 0;
      else
         newindex[i] = i-numsub; 
   }

   for (i=1; i<=npt; i++)
      for (j=i+1; j<=npt; j++)
         diff[newindex[i]][newindex[j]] = diff[i][j];

   npt    = npt - excount;
   count  = 0;
   xcount = 1;
   for (i=1; i<=npt; i++){
      for (j=i+1; j<=npt; j++){
         if (diff[i][j] != 0.0){
	    y[count+1] = diff[i][j];
	    x[count+1] = xcount;
	    count++;
         }
      xcount++;
      } 
   }
}
sprintf(line, "Pairs of subrasters after deletion = %d",count);
SCTPUT(line);
SCTPUT(" ");

mfit = npt;
for (i = 1; i <= mfit; i++) a[i]=0;

fit_offset(x, y, count, a, npt);

for (i=1; i<=npt; i++){
   for (j=i+1; j<=npt; j++){
      if (diff[i][j] != 0.0){
	 error[i] = error[i] + SQR((diff[i][j]-(a[j]-a[i])));
	 cerror[i]++;
	 cerror[j]++;
	 error[j] = error[j] + SQR((diff[i][j]-(a[j]-a[i])));
      }
   }
}

sum = 0;
for (i=1; i<=npt; i++) sum+=a[i];
avgoff = sum/(float) npt;
SCTPUT(" ");    
sprintf(line, "Average offset = %f",avgoff);
SCTPUT(line);    

sum    = 0;
numsub = 0;
for (i=1; i<=npt+excount; i++) {
   exclude=0;
   for (j=1; j<=excount; j++){
      if (i == ex[j]) {
	 exclude=1;
	 numsub++;
         }
      }

   if (exclude==0) {
      a[i-numsub] = a[i-numsub]-avgoff;
      sprintf(line, "frame %d offset %6.1f error = %6.2f",i,
	      a[i-numsub],error[i-numsub]/cerror[i-numsub]);
      SCTPUT(line); 
      sum+=error[i-numsub]/cerror[i-numsub];
      }	  
   }
avgerr  = sum/(float) npt;
sum = 0;
for (i=1; i<=npt; i++)
   sum += SQR(avgerr-(error[i]/cerror[i]));
stdev = sqrt(sum/(npt-1));
sprintf(line, "Average error= %f , Std Dev = %f",avgerr,stdev);
SCTPUT(line); 

sum    = 0;
count  = 1;
numsub = 0;
a[0]   = 0;

for (io=0; io<npt+excount; io++)
   {
   deltai[io] = 0.0;
   exclude    = 0;

   for (i=1; i<=excount; i++){
      if (count == ex[i]) 
         {
         exclude = 1;
         numsub++;
         }
      }
	      
   if (exclude == 0) {
      deltai[io] = a[count-numsub];
      }
   count++;
   }

osmmfree(error);
osmmfree(cerror);
for (i=0; i<=npt; i++)
   {
   osmmfree(diff[i]);
   }
osmmfree(diff);
}


