// Copyright (C) 1999-2018
// Smithsonian Astrophysical Observatory, Cambridge, MA, USA
// For conditions of distribution and use, see copyright notice in "copyright"

#include "base.h"
#include "context.h"
#include "fitsimage.h"

#include "projection.h"
extern "C" {
#include "tkbltVector.h"
}

#include "sigbus.h"

void Base::markerAnalysisHistogram(Marker* pp, double** x, double** y,
				  const BBox& bb, int num)
{
  // does not extend across mosaic boundries
  // uses currentContext
  FitsImage* ptr = isInCFits(pp->getCenter(),Coord::REF,NULL);
  if (!ptr)
    ptr = currentContext->cfits;

  int srcw = ptr->width();
  FitsBound* params = ptr->getDataParams(currentContext->secMode());

  double min =DBL_MAX;
  double max =-DBL_MAX;

  // take the bbox and extend to lower/upper pixel boundaries
  Vector ll = (bb.ll*ptr->refToData).floor();
  Vector ur = (bb.ur*ptr->refToData).ceil();

  int msize = int(ur[1]-ll[1])*int(ur[0]-ll[0]);
  double* marr = new double[msize];
  int* mask = new int[msize];
  memset(marr,0,msize*sizeof(double));
  memset(mask,0,msize*sizeof(int));

  // main loop
  SETSIGBUS
    int cnt =0;
    for (int jj=ll[1]; jj<ur[1]; jj++) {
      for (int ii=ll[0]; ii<ur[0]; ii++, cnt++) {
	if (ii>=params->xmin && ii<params->xmax && 
	    jj>=params->ymin && jj<params->ymax) {
	  // shift to center of pixel in DATA
	  Vector rr = Vector(ii,jj)+Vector(.5,.5);
	  Vector ss = rr*ptr->dataToRef;

	  if (pp->isIn(ss,Coord::REF)) {
	    double val =ptr->getValueDouble(long(jj)*srcw+long(ii));
	    // check for nan
	    if (isfinite(val)) {
	      marr[cnt] =val;
	      mask[cnt] =1;
	      if (val<min)
		min =val;
	      if (val>max)
		max =val;
	    }
	  }
	}
      }
    }
  CLEARSIGBUS

  // sanity check
  if (num<1)
    num = 1;

  // we need one extra max,0 value at the end
  int nn = num+1;
  *x = (double*)malloc(nn*sizeof(double));
  *y = (double*)malloc(nn*sizeof(double));
  memset(*x,0,nn*sizeof(double));
  memset(*y,0,nn*sizeof(double));

  double diff = max-min;
  int last = num-1;
  double* xx = *x;
  double* yy = *y;

  if (diff>0) {
    for (int ii=0; ii<nn; ii++)
      xx[ii] = (double)ii/last*diff + min;

    for (int ii=0; ii<msize; ii++) {
      if (mask[ii]) {
	double& val = marr[ii];
	if (val>=min && val<=max)
	  yy[(int)((val-min)/diff*last+.5)]++;
      }
    }
  }
  else {
    for (int ii=0; ii<nn; ii++)
      xx[ii] = min;

    for (int ii=0; ii<msize; ii++)
      if (mask[ii])
	yy[0]++;
  }


  if (marr)
    delete [] marr;
  if (mask)
    delete [] mask;
}

int Base::markerAnalysisPlot2d(Marker* pp, double** x, double** y, 
			       double** xc, double** yc, 
			       Vector& p1, Vector& p2, int width,
			       Coord::CoordSystem sys, Coord::SkyFrame sky,
			       Marker::AnalysisMethod method)
{
  Vector vv = p2-p1;
  int num = vv.length() +1;
  Vector ss = vv.normalize();
  Vector uu = Vector(-ss[1],ss[0]);
  int cnt[num];

  *x = (double*)malloc(num*sizeof(double));
  *y = (double*)malloc(num*sizeof(double));
  *xc = (double*)malloc(num*sizeof(double));
  *yc = (double*)malloc(num*sizeof(double));

  int mosaic = isMosaic();
  FitsImage* sptr = currentContext->cfits;
  FitsBound* params = sptr->getDataParams(currentContext->secMode());

  if (width==0)
    width =1;

  // main loop

  SETSIGBUS
  for (int ii=0; ii<num; ii++) {
    int found=0;

    (*x)[ii] = ii+1;
    (*y)[ii] = 0;
    (*xc)[ii] = ii+1;
    (*yc)[ii] = 0;
    cnt[ii] = 0;

    for (int jj=0; jj<width; jj++) {
      Vector tt = p1 + ss*ii + uu*jj;

      if (mosaic) {
	sptr = currentContext->cfits;
	params = sptr->getDataParams(currentContext->secMode());
      }

      do {
	Vector zz = tt * sptr->refToData;
	if (zz[0]>=params->xmin && zz[0]<params->xmax && 
	    zz[1]>=params->ymin && zz[1]<params->ymax) {

	  if (!found) {
	    Vector tv = sptr->mapFromRef(tt, sys, sky);
	    (*xc)[ii] = tv[0];
	    (*yc)[ii] = tv[1];
	    found =1;
	  }

	  // check for nan
	  double dd = sptr->getValueDouble(zz);
	  if (isfinite(dd)) {
	    (*y)[ii] += dd;
	    cnt[ii]++;
	  }
	  break;
	}
	else {
	  if (mosaic) {
	    sptr = sptr->nextMosaic();
	    if (sptr)
	      params = sptr->getDataParams(currentContext->secMode());
	  }
	}
      }
      while (mosaic && sptr);
    }
  }
  CLEARSIGBUS

  // average if needed
  if (method == Marker::AVERAGE)
    for (long ii=0; ii<num; ii++)
      if (isfinite((*y)[ii]) && cnt[ii]!=0)
	(*y)[ii] /= cnt[ii];

  return num;
}

int Base::markerAnalysisPlot3d(Marker* pp, double** x, double** y,
			       const BBox& bb, 
			       Coord::CoordSystem sys, Coord::SkyFrame sky,
			       Marker::AnalysisMethod method)
{
  // does not extend across mosaic boundries
  // different, need all slices 
  FitsImage* ptr = isInFits(pp->getCenter(),Coord::REF,NULL);
  if (!ptr)
    ptr = currentContext->fits;

  // if more than 3 axes, walk it forward
  int num = currentContext->calcSlice();
  for (int ii=1; ii<num; ii++)
    if (ptr)
      ptr = ptr->nextSlice();

  FitsBound* params = ptr->getDataParams(currentContext->secMode());
  FitsZBound* zparams=currentContext->getDataParams(currentContext->secMode());

  int srcw = ptr->width();
  int srcd = zparams->zmax - zparams->zmin;

  // slice jump vector
  FitsImage* sjv[srcd];
  FitsImage* sptr = ptr;
  for (int ii=0; ii<zparams->zmin; ii++)
    sptr = sptr->nextSlice();

  for (int ii=0; ii<srcd; ii++) {
    sjv[ii] = sptr;
    sptr = sptr->nextSlice();
  }

  // init
  *x = (double*)malloc(srcd*sizeof(double));
  *y = (double*)malloc(srcd*sizeof(double));
  memset(*x,0,srcd*sizeof(double));
  memset(*y,0,srcd*sizeof(double));

  int* cnt = new int[srcd];
  memset(cnt,0,srcd*sizeof(int));

  // take the bbox and extend to lower/upper pixel boundaries
  Vector ll = (bb.ll*ptr->refToData).floor();
  Vector ur = (bb.ur*ptr->refToData).ceil();

  // mask
  int ss = (ur[0]-ll[0])*(ur[1]-ll[1]);
  bool* msk = new bool[ss];
  long* idx = new long[ss];
  memset(msk,0,ss*sizeof(bool));
  memset(idx,0,ss*sizeof(long));

  bool* mptr=msk;
  long* iptr=idx;
  if (!pp->isFixed()) {
    Matrix bck = pp->bckMatrix();
    for (int jj=ll[1]; jj<ur[1]; jj++) {
      for (int ii=ll[0]; ii<ur[0]; ii++,mptr++,iptr++) {
	if (ii>=params->xmin && ii<params->xmax && 
	    jj>=params->ymin && jj<params->ymax) {
	  // shift to center of pixel in DATA
	  Vector rr = Vector(ii,jj)+Vector(.5,.5);
	  if (pp->isIn(rr*ptr->dataToRef,bck)) {
	    *mptr=1;
	    *iptr=long(jj)*srcw+long(ii);
	  }
	}
      }
    }
  }
  else {
    for (int jj=ll[1]; jj<ur[1]; jj++) {
      for (int ii=ll[0]; ii<ur[0]; ii++,mptr++,iptr++) {
	if (ii>=params->xmin && ii<params->xmax && 
	    jj>=params->ymin && jj<params->ymax) {
	  // shift to center of pixel in DATA
	  Vector rr = Vector(ii,jj)+Vector(.5,.5);
	  if (pp->isIn(rr*ptr->dataToRef,Coord::REF)) {
	    *mptr=1;
	    *iptr=long(jj)*srcw+long(ii);
	  }
	}
      }
    }
  }

  // main loop
  SETSIGBUS
    for (int kk=0; kk<srcd; kk++) {
      double tt = kk+.5+.5+zparams->zmin;
      Vector3d dd = Vector3d(ptr->center(),tt) * Translate3d(-.5,-.5,-.5);
      Vector3d out = ptr->mapFromRef(dd,sys,sky);
      (*x)[kk] = out[2];

      bool* mptr=msk;
      long* iptr=idx;
      for (int ll=0; ll<ss; ll++,mptr++,iptr++) {
	if (*mptr) {
	  double val =sjv[kk]->getValueDouble(*iptr);
	  // check for nan
	  if (isfinite(val)) {
	    (*y)[kk] += val;
	    cnt[kk]++;
	  }
	}
      }
    }
  CLEARSIGBUS

  // average if needed
  if (method == Marker::AVERAGE)
    for (long kk=0; kk<srcd; kk++)
      if (cnt[kk]!=0)
	(*y)[kk] /= cnt[kk];

  if (cnt)
    delete [] cnt;
  if (msk)
    delete [] msk;
  if (idx)
    delete [] idx;

  return srcd;
}

// for annulus regions
int Base::markerAnalysisRadial(Marker* pp, double** x, double** y, double** e, 
			       int num, Vector* annuli, 
			       BBox* bb, Coord::CoordSystem sys)

{
  // does not extend across mosaic boundries
  // uses currentContext
  FitsImage* ptr = isInCFits(pp->getCenter(),Coord::REF,NULL);
  if (!ptr)
    ptr = currentContext->cfits;

  int srcw = ptr->width();
  FitsBound* params = ptr->getDataParams(currentContext->secMode());

  double sum[num];
  memset(sum,0,num*sizeof(double));
  int cnt[num];
  memset(cnt,0,num*sizeof(int));

  for (int kk=0; kk<num; kk++) {
    // take the bbox and extend to lower/upper pixel boundaries
    Vector ll = (bb[kk+1].ll*ptr->refToData).floor();
    Vector ur = (bb[kk+1].ur*ptr->refToData).ceil();

    // main loop
    SETSIGBUS
      for (int jj=ll[1]; jj<ur[1]; jj++) {
	for (int ii=ll[0]; ii<ur[0]; ii++) {
	  if (ii>=params->xmin && ii<params->xmax && 
	      jj>=params->ymin && jj<params->ymax) {
	    // shift to center of pixel in DATA
	    Vector rr = Vector(ii,jj)+Vector(.5,.5);
	    Vector ss = rr*ptr->dataToRef;
	  
	    if (pp->isIn(ss,Coord::REF,kk+1) && !pp->isIn(ss,Coord::REF,kk)) {
	      double val =ptr->getValueDouble(long(jj)*srcw+long(ii));
	      // check for nan
	      if (isfinite(val)) {
		sum[kk] += val;
		cnt[kk]++;
	      }
	    }
	  }
	}
      }
    CLEARSIGBUS
  }

  *x = (double*)malloc(num*sizeof(double));
  *y = (double*)malloc(num*sizeof(double));
  *e = (double*)malloc(num*sizeof(double));

  int unit =0;
  double xaxis =1;
  if (ptr->hasWCS(sys)) {
    double ll = ptr->getWCSSize(sys);

    if (ptr->hasWCSCel(sys)) {
      unit =1;
      xaxis = ll*60*60;
    }
    else {
      unit =2;
      xaxis = ll;
    }
  }
    
  double rr = ptr->getWCSSize(sys);
  double aa = rr*rr;

  for (int kk=0; kk<num; kk++) {
    double err = sqrt(fabs(sum[kk]));
    double area =0;
    double bri =0;
    double brierr =0;
    switch (unit) {
    case 0:
      // pixels
      area = abs(cnt[kk]);
      break;
    case 1:
      // Cel WCS
      area = aa*60*60*60*60*cnt[kk];
      break;
    case 2:
      // Linear WCS
      area = aa*cnt[kk];
      break;
    }

    // area can be zero
    if (area) {
      bri = sum[kk]/area;
      brierr = err/area;
    }

    double rr0 = (annuli[kk+1][0]-annuli[kk][0])/2. +annuli[kk][0];
    double rr1 = (annuli[kk+1][1]-annuli[kk][1])/2. +annuli[kk][1];
    double rad = (rr0 + rr1)/2.;

    (*x)[kk] = rad*xaxis;
    (*y)[kk] = bri;
    (*e)[kk] = brierr;
  }

  return num;
}

// for panda regions
int Base::markerAnalysisPanda(Marker* pp, double** x, double** y, double** e, 
			       int num, Vector* annuli, 
			       int na, double* angles,
			       BBox* bb, Coord::CoordSystem sys)

{
  // does not extend across mosaic boundries
  // uses currentContext
  FitsImage* ptr = isInCFits(pp->getCenter(),Coord::REF,NULL);
  if (!ptr)
    ptr = currentContext->cfits;

  int srcw = ptr->width();
  FitsBound* params = ptr->getDataParams(currentContext->secMode());

  double sum[num][na];
  memset(sum,0,num*na*sizeof(double));
  int cnt[num][na];
  memset(cnt,0,num*na*sizeof(int));

  for (int kk=0; kk<num; kk++) {
    // take the bbox and extend to lower/upper pixel boundaries
    Vector ll = (bb[kk+1].ll*ptr->refToData).floor();
    Vector ur = (bb[kk+1].ur*ptr->refToData).ceil();

    // main loop
    SETSIGBUS
      for (int qq=0; qq<na; qq++) {

	for (int jj=ll[1]; jj<ur[1]; jj++) {
	  for (int ii=ll[0]; ii<ur[0]; ii++) {
	    if (ii>=params->xmin && ii<params->xmax && 
		jj>=params->ymin && jj<params->ymax) {
	      // shift to center of pixel in DATA
	      Vector rr = Vector(ii,jj)+Vector(.5,.5);
	      Vector ss = rr*ptr->dataToRef;
	  
	      if (pp->isIn(ss,Coord::REF,kk+1,qq) && 
		  !pp->isIn(ss,Coord::REF,kk,qq)) {
		double val =ptr->getValueDouble(long(jj)*srcw+long(ii));
		// check for nan
		if (isfinite(val)) {
		  sum[kk][qq] += val;
		  cnt[kk][qq]++;
		}
	      }
	    }
	  }
	}
      }
    CLEARSIGBUS
  }

  *x = (double*)malloc(num*na*sizeof(double));
  *y = (double*)malloc(num*na*sizeof(double));
  *e = (double*)malloc(num*na*sizeof(double));

  int unit =0;
  double xaxis =1;
  if (ptr->hasWCS(sys)) {
    double ll = ptr->getWCSSize(sys);

    if (ptr->hasWCSCel(sys)) {
      unit =1;
      xaxis = ll*60*60;
    }
    else {
      unit =2;
      xaxis = ll;
    }
  }

  double rr = ptr->getWCSSize(sys);
  double aa = rr*rr;

  for (int qq=0; qq<aa; qq++) {
    for (int kk=0; kk<num; kk++) {
      double err = sqrt(fabs(sum[kk][qq]));
      double area =0;
      double bri =0;
      double brierr =0;
      switch (unit) {
      case 0:
	// pixels
	area = abs(cnt[kk][qq]);
	break;
      case 1:
	// Cel WCS
	area = aa*60*60*60*60*cnt[kk][qq];
	break;
      case 2:
	// Linear WCS
	area = aa*cnt[kk][qq];
	break;
      }

      // area can be zero
      if (area) {
	bri = sum[kk][qq]/area;
	brierr = err/area;
      }

      double rr0 = (annuli[kk+1][0]-annuli[kk][0])/2. +annuli[kk][0];
      double rr1 = (annuli[kk+1][1]-annuli[kk][1])/2. +annuli[kk][1];
      double rad = (rr0 + rr1)/2.;

      (*x)[qq*num+kk] = rad*xaxis;
      (*y)[qq*num+kk] = bri;
      (*e)[qq*num+kk] = brierr;
    }
  }

  return num*aa;
}

// for simple regions
void Base::markerAnalysisStats(Marker* pp, ostream& str, const BBox& bb,
			       Coord::CoordSystem sys, Coord::SkyFrame sky)
{
  // does not extend across mosaic boundries
  // uses currentContext
  FitsImage* ptr = isInCFits(pp->getCenter(),Coord::REF,NULL);
  if (!ptr)
    ptr = currentContext->cfits;

  int srcw = ptr->width();
  FitsBound* params = ptr->getDataParams(currentContext->secMode());

  double sum =0;
  double sum2 =0;
  int cnt =0;
  double min =DBL_MAX;
  double max =-DBL_MAX;

  // take the bbox and extend to lower/upper pixel boundaries
  Vector ll = (bb.ll*ptr->refToData).floor();
  Vector ur = (bb.ur*ptr->refToData).ceil();

  int msize = int(ur[1]-ll[1])*int(ur[0]-ll[0]);
  double* marr = new double[msize];
  memset(marr,0,msize*sizeof(double));

  // main loop
  SETSIGBUS
    for (int jj=ll[1]; jj<ur[1]; jj++) {
      for (int ii=ll[0]; ii<ur[0]; ii++) {
	if (ii>=params->xmin && ii<params->xmax && 
	    jj>=params->ymin && jj<params->ymax) {
	  // shift to center of pixel in DATA
	  Vector rr = Vector(ii,jj)+Vector(.5,.5);
	  Vector ss = rr*ptr->dataToRef;

	  if (pp->isIn(ss,Coord::REF)) {
	    double val =ptr->getValueDouble(long(jj)*srcw+long(ii));
	    // check for nan
	    if (isfinite(val)) {
	      sum += val;
	      sum2 += val*val;
	      if (cnt<msize)
		marr[cnt] = val;
	      if (val<min)
		min =val;
	      if (val>max)
		max =val;

	      cnt++;
	    }
	  }
	}
      }
    }
  CLEARSIGBUS

  qsort((void*)marr,cnt,sizeof(double),dCompare);
  double median = marr[int(cnt/2.)];
  if (marr)
    delete [] marr;

  int unit = markerAnalysisStats1(pp,ptr,str,sys,sky);
  markerAnalysisStats2(ptr,str,sys,0,cnt,sum,unit);
  markerAnalysisStats3(str);
  markerAnalysisStats4(str,0,cnt,sum,sum2,median,min,max);
}

// for annulus regions
void Base::markerAnalysisStats(Marker* pp, ostream& str, 
			       int num, BBox* bb,
			       Coord::CoordSystem sys, Coord::SkyFrame sky)
{
  // does not extend across mosaic boundries
  // uses currentContext
  FitsImage* ptr = isInCFits(pp->getCenter(),Coord::REF,NULL);
  if (!ptr)
    ptr = currentContext->cfits;

  int srcw = ptr->width();
  FitsBound* params = ptr->getDataParams(currentContext->secMode());

  double sum[num];
  memset(sum,0,num*sizeof(double));
  double sum2[num];
  memset(sum2,0,num*sizeof(double));
  int cnt[num];
  memset(cnt,0,num*sizeof(int));
  double min[num];
  double max[num];
  for (int ii=0; ii<num; ii++) {
    min[ii] =DBL_MAX;
    max[ii] =-DBL_MAX;
  }
  double median[num];
  memset(median,0,num*sizeof(double));

  for (int kk=0; kk<num; kk++) {
    // take the bbox and extend to lower/upper pixel boundaries
    Vector ll = (bb[kk+1].ll*ptr->refToData).floor();
    Vector ur = (bb[kk+1].ur*ptr->refToData).ceil();

    int msize = int(ur[1]-ll[1])*int(ur[0]-ll[0]);
    double* marr = new double[msize];
    memset(marr,0,msize*sizeof(double));

    // main loop
    SETSIGBUS
      for (int jj=ll[1]; jj<ur[1]; jj++) {
	for (int ii=ll[0]; ii<ur[0]; ii++) {
	  if (ii>=params->xmin && ii<params->xmax && 
	      jj>=params->ymin && jj<params->ymax) {
	    // shift to center of pixel in DATA
	    Vector rr = Vector(ii,jj)+Vector(.5,.5);
	    Vector ss = rr*ptr->dataToRef;
	  
	    if (pp->isIn(ss,Coord::REF,kk+1) && !pp->isIn(ss,Coord::REF,kk)) {
	      double val =ptr->getValueDouble(long(jj)*srcw+long(ii));
	      // check for nan
	      if (isfinite(val)) {
		sum[kk] += val;
		sum2[kk] += val*val;
		if (cnt[kk]<msize)
		  marr[cnt[kk]] = val;
		if (val<min[kk])
		  min[kk] =val;
		if (val>max[kk])
		  max[kk] =val;

		cnt[kk]++;
	      }
	    }
	  }
	}
      }
    CLEARSIGBUS

    qsort((void*)marr,cnt[kk],sizeof(double),dCompare);
    median[kk] = marr[int(cnt[kk]/2.)];
    if (marr)
      delete [] marr;
  }

  int unit = markerAnalysisStats1(pp,ptr,str,sys,sky);
  for (int kk=0; kk<num; kk++) 
    markerAnalysisStats2(ptr,str,sys,kk,cnt[kk],sum[kk],unit);

  markerAnalysisStats3(str);
  for (int kk=0; kk<num; kk++)
    markerAnalysisStats4(str,kk,cnt[kk],sum[kk],sum2[kk],
			 median[kk],min[kk],max[kk]);
}

// for panda regions
void Base::markerAnalysisStats(Marker* pp, ostream& str, 
			       int num, int aa, BBox* bb, 
			       Coord::CoordSystem sys, Coord::SkyFrame sky)
{
  // does not extend across mosaic boundries
  // uses currentContext
  FitsImage* ptr = isInCFits(pp->getCenter(),Coord::REF,NULL);
  if (!ptr)
    ptr = currentContext->cfits;

  int srcw = ptr->width();
  FitsBound* params = ptr->getDataParams(currentContext->secMode());

  double sum[num][aa];
  memset(sum,0,num*aa*sizeof(double));
  double sum2[num][aa];
  memset(sum2,0,num*aa*sizeof(double));
  int cnt[num][aa];
  memset(cnt,0,num*aa*sizeof(int));
  double min[num][aa];
  double max[num][aa];
  for (int ii=0; ii<num; ii++) {
    for (int jj=0; jj<aa; jj++) {
      min[ii][jj] =DBL_MAX;
      max[ii][jj] =-DBL_MAX;
    }
  }
  double median[num][aa];
  memset(median,0,num*aa*sizeof(double));

  for (int kk=0; kk<num; kk++) {
    // take the bbox and extend to lower/upper pixel boundaries
    Vector ll = (bb[kk+1].ll*ptr->refToData).floor();
    Vector ur = (bb[kk+1].ur*ptr->refToData).ceil();

    int msize = int(ur[1]-ll[1])*int(ur[0]-ll[0]);
    double* marr = new double[msize];

    // main loop
    SETSIGBUS
      for (int qq=0; qq<aa; qq++) {
	memset(marr,0,msize*sizeof(double));

	for (int jj=ll[1]; jj<ur[1]; jj++) {
	  for (int ii=ll[0]; ii<ur[0]; ii++) {
	    if (ii>=params->xmin && ii<params->xmax && 
		jj>=params->ymin && jj<params->ymax) {
	      // shift to center of pixel in DATA
	      Vector rr = Vector(ii,jj)+Vector(.5,.5);
	      Vector ss = rr*ptr->dataToRef;
	  
	      if (pp->isIn(ss,Coord::REF,kk+1,qq) && 
		  !pp->isIn(ss,Coord::REF,kk,qq)) {
		double val =ptr->getValueDouble(long(jj)*srcw+long(ii));
		// check for nan
		if (isfinite(val)) {
		  sum[kk][qq] += val;
		  sum2[kk][qq] += val*val;

		  if (cnt[kk][qq]<msize)
		    marr[cnt[kk][qq]] = val;

		  if (val<min[kk][qq])
		    min[kk][qq] = val;
		  if (val>max[kk][qq])
		    max[kk][qq] = val;

		  cnt[kk][qq]++;
		}
	      }
	    }
	  }
	}

	qsort((void*)marr,cnt[kk][qq],sizeof(double),dCompare);
	median[kk][qq] = marr[int(cnt[kk][qq]/2.)];
      }
    CLEARSIGBUS

    if (marr)
      delete [] marr;
  }

  int unit = markerAnalysisStats1(pp,ptr,str,sys,sky);
  for (int kk=0; kk<num; kk++) 
    for (int qq=0; qq<aa; qq++)
      markerAnalysisStats2(ptr,str,sys,kk*aa+qq,cnt[kk][qq],sum[kk][qq],unit);

  markerAnalysisStats3(str);
  for (int kk=0; kk<num; kk++)
    for (int qq=0; qq<aa; qq++)
      markerAnalysisStats4(str,kk*aa+qq,cnt[kk][qq],sum[kk][qq],sum2[kk][qq],
			   median[kk][qq],min[kk][qq],max[kk][qq]);
}

int Base::markerAnalysisStats1(Marker* pp,FitsImage* ptr, ostream& str, 
			       Coord::CoordSystem sys, Coord::SkyFrame sky)
{
  str << "center=" << setprecision(8) 
      << ptr->mapFromRef(pp->getCenter(),sys,sky) << endl;
  coord.listCoordSystem(str, sys, sky, ptr);
  str << endl;

  switch (sys) {
  case Coord::IMAGE:
  case Coord::PHYSICAL:
  case Coord::DETECTOR:
  case Coord::AMPLIFIER:
    str << endl;
    str << "reg\t" << "sum\t\t" << "error\t" 
	<< "area\t\t" << "surf_bri\t\t" << "surf_err" << endl
	<< "\t" << "\t" << "\t\t" 
	<< "(pix**2)\t\t" << "(sum/pix**2)\t\t" << "(sum/pix**2)" << endl
	<< "---\t" << "---\t\t" << "-----\t" 
	<< "--------\t\t" << "------------\t\t" << "------------" << endl;
    return 0;
  default: 
    {
      double ll = ptr->getWCSSize(sys);
      if (ptr->hasWCSCel(sys)) {
	str << "1 pixel = "<< ll*60*60 << " arcsec";
	str << endl << endl;
	str << "reg\t" << "sum\t\t" << "error\t" 
	    << "area\t\t" << "surf_bri\t\t" << "surf_err" << endl
	    << "\t" << "\t" << "\t\t" 
	    << "(arcsec**2)\t\t" << "(sum/arcsec**2)\t" << "(sum/arcsec**2)" << endl
	    << "---\t" << "---\t\t" << "-----\t" 
	    << "-----------\t\t" << "---------------\t" << "---------------" << endl;
	return 1;
      }
      else {
	str << "1 pixel = "<< ll;
	str << endl << endl;
	str << "reg\t" << "sum\t\t" << "error\t" 
	    << "area\t\t" << "surf_bri\t\t" << "surf_err" << endl
	    << "\t" << "\t" << "\t\t" 
	    << "(pix**2)\t\t" << "(sum/pix**2)\t\t" << "(sum/pix**2)" << endl
	    << "---\t" << "---\t\t" << "-----\t" 
	    << "--------\t\t" << "------------\t\t" << "------------" << endl;
	return 2;
      }
    }
    break;
  }
}

void Base::markerAnalysisStats2(FitsImage* ptr, ostream& str, 
				Coord::CoordSystem sys,
				int kk, int cnt, double sum, int unit)
{
  double area =0;
  switch (unit) {
  case 0:
    // pixels
    area = cnt;
    break;
  case 1:
    {
      // Cel WCS
      double rr = ptr->getWCSSize(sys);
      double aa = rr*rr;
      area = aa*60*60*60*60*cnt;
    }
    break;
  case 2:
    {
      // Linear WCS
      double rr = ptr->getWCSSize(sys);
      double aa = rr*rr;
      area = aa*cnt;
    }
    break;
  }
  double err = sqrt(fabs(sum));
  double bri = sum/area;
  double brierr = err/area;

  str << kk+1 << '\t' 
      << setprecision(8)
      << sum << "\t\t"
      << setprecision(6)
      << err << "\t"
      << area << "\t\t"
      << bri << "\t\t"
      << brierr << endl;
}

void Base::markerAnalysisStats3(ostream& str)
{
  str << endl
      << "reg\t" << "sum\t" << "npix\t" << "mean\t" << "median\t"
      << "min\t" << "max\t" << "var\t" << "stddev\t" << "rms\t" << endl
      << "---\t" << "---\t" << "----\t" << "----\t" << "------\t" 
      << "---\t" << "---\t" << "---\t" << "------\t" << "---\t" << endl;
}

void Base::markerAnalysisStats4(ostream& str, int kk, 
				double cnt, double sum, double sum2, 
				double median, double min, double max)
{
  // up cast int cnt to double to avoid int overflow
  double mean =0;
  double std =0;
  double var =0;
  double rms =0;
  if (cnt) {
    mean = sum/cnt;
    var = fabs(sum2/cnt - (sum*sum)/(cnt*cnt));
    std = sqrt(var);
    rms = sqrt(sum2/cnt);
  }

  str << kk+1 << '\t' 
      << setprecision(8)
      << sum << '\t'
      << cnt << '\t'
      << setprecision(6)
      << mean << '\t'
      << median << '\t'
      << min << '\t'
      << max << '\t'
      << var << '\t'
      << std << '\t'
      << rms << '\t'
      << endl;
}

void Base::bltCut(char* xname, char* yname, Coord::Orientation axis, 
		  const Vector& rr, int thick, Base::CutMethod method)
{
  int size;
  if (axis == Coord::XX)
    size = options->width;
  else
    size = options->height;

  long length = (size+1) * 2;
  double* xx = (double*)malloc(sizeof(double)*length);
  double* yy = (double*)malloc(sizeof(double)*length);

  // check for data or undefined low()/high()
  if (!currentContext->cfits || !isfinite(currentContext->low())) {
    for (int ii=0; ii<=size; ii++) {
      xx[ii*2] = ii;
      xx[ii*2+1] = ii;
      yy[ii*2] = 0;
      yy[ii*2+1] = 0;
    }
  }
  else
    bltCutFits(xx, yy, size, axis, rr, thick, method);

  Blt_Vector* xv;
  if (Blt_GetVector(interp, xname, &xv) != TCL_OK)
    goto error;

  if (Blt_ResetVector(xv, xx, length, length*sizeof(double), TCL_DYNAMIC) != 
      TCL_OK)
    goto error;

  Blt_Vector* yv;
  if (Blt_GetVector(interp, yname, &yv) != TCL_OK)
    goto error;

  if (Blt_ResetVector(yv, yy, length, length*sizeof(double), TCL_DYNAMIC) != 
      TCL_OK)
    goto error;

  return;

 error:
    result = TCL_ERROR;
    return;
}

void Base::bltCutFits(double* xx, double* yy, int size, Coord::Orientation axis,
		      const Vector& r, int thick, Base::CutMethod method)
{
  Vector rr = r * refToWidget;

  FitsImage* sptr = currentContext->cfits;
  FitsBound* params = sptr->getDataParams(currentContext->secMode());

  int mosaic = isMosaic();
  double prev = currentContext->low();

  // main loop

  SETSIGBUS
  for (int ii=0; ii<=size; ii++) {
    double vv =0;
    int cnt =0;

    Vector img;
    int ww = thick/2;
    for (int jj=0; jj<thick; jj++) {

      if (mosaic) {
	sptr = currentContext->cfits;
	params = sptr->getDataParams(currentContext->secMode());
      }

      do {
	if (axis == Coord::XX)
	  img = Vector(1+ii,rr[1]-ww+jj) * sptr->widgetToData;
	else
	  img = Vector(rr[0]-ww+jj,1+ii) * sptr->widgetToData;

	if (img[0]>=params->xmin && img[0]<params->xmax && 
	    img[1]>=params->ymin && img[1]<params->ymax) {
	  double value = sptr->getValueDouble(img);

	  if (isfinite(value)) {
	    vv += value;
	    cnt +=1;
	  }
	  break;
	}
	else {
	  if (mosaic) {
	    sptr = sptr->nextMosaic();
	    if (sptr)
	      params = sptr->getDataParams(currentContext->secMode());
	  }
	}
      }
      while (mosaic && sptr);
    }

    xx[2*ii] = ii;
    xx[2*ii +1] = ii;
    yy[2*ii] = prev;

    switch (method) {
    case Base::AVERAGE:
      if (cnt)
	yy[2*ii +1] = prev = vv/cnt;
      else
	yy[2*ii +1] = prev = currentContext->low();
      break;
    case Base::SUM:
      yy[2*ii +1] = prev = vv;
      break;
    }
      
  }
  CLEARSIGBUS
}