/*
****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	quasars.c
**
**<HTML>
**	This file contains C functions to select spectroscopic targets for
**	the Quasars Working Group.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	ANSI C.
**
******************************************************************************
******************************************************************************
*/
#include "astrotools.h"
#include "taCalibObj.h"
#include "taQuasars.h"
#include "arTargetType.h"
#include "arDetectionFlag.h"
#include "arObjectStatus.h"
#include "arObjectType.h"

/*
****************************************************************************
** PARAMETER DEFINITIONS
**
** NOTE: Adam, these can all be moved to the param file.
**
****************************************************************************
**/

/****** DEFINE parameters we use to diagnose various image problems *****/

/* if INTERP_CETNER flag is set, add errInterCenter in the error term
 * in the relavant band */

#define errInterpCenter 0.5

/* We will delete objects with i-z < izMin, in order to delete some
 * bad blends from moving objects, it's a place holder now, waiting
 * for RHL to fix some PHOTO problem */

#define  izMin      -3.70

/* we will delete objects with i < iBright for the moment, due to a
 * known problem with PHOTO (version 5.0) when dealing with removal of
 * cosmic rays for bright stars */

#define  iBright    15.0
#define  iBrightInterp 16.5

/* maximum speed for non-moving object */
#define maxV 0.2
#define maxVSig 3


/**** DEFINE parameters we use to cut various regions in the color space ****/

/* This is a place holder to deal with the asymmetric error at faint
 * end, it will eventually disappear or into para file */

#define  bigNsigerrors 8.00
#define  minDetect 0.12

/*This constant need to be moved to the param file*/
#define ugMaxAstar 1.5

/*This constant need to be moved to the param file*/
#define ugMinHighz 1.5

/* cut out faint UVX quasars selected as HIZ */
#define ugMaxFaint 0.9
#define grMaxFaint 0.8

/* galaxy region cut : this is the BCG region on the color-color diagram,
which we require minGalSig*4 sigma out of stellar locus */
#define grMinGal 1.5
#define grMaxGal 2.0
#define riMinGal 0.4
#define riMaxGal 0.7
#define minGalSig 1.   


/*
****************************************************************************
** CAP/SKIRT DETERMINATION
**
** Determine if object is in cap or skirt.  For now we do a simple cut
** in b (galactic latitude).
**
** NOTE: Currently, no distinction is made between the cap and the
** skirt in the code.  It is here in case we want to change the
** density of quasars in the skirt as compared to the cap; the cap
** region should have less contamination and thus a higher efficiency.
**
** Return 1 for cap, 0 for skirt
**
****************************************************************************
**/

int determineCapSkirt(double l,double b, double limdensity) {
  
  double density;
  if (b<20) b=20.;
  atStarCountsFind(l,b,20,&density);
  if (density < limdensity) {
    return 1; 	/* cap */
  } else {
    return 0;	/* skirt */
  }

}

/*
****************************************************************************
** A STAR REMOVAL
**
** Don't target quasars in the region of color space that is heavily
** contaminated by A stars.
**
** Quasars with redshifts between 2.5 and 3 cross the stellar locus
** through the A star region.  We don't want to miss these objects,
** but we cannot afford to target all of the stellar objects in this
** region.  Therefore we remove the region where A stars are expected
** to be the most dense.  Objects in this box should not be selected
** unless they are also FIRST sources.
**
** Returns 0 (not in box) or 1 (in box)
**
**
**************************************************************************** 
**/

int cutAstars (float *mag, float limit, float deltamag, const TA_QUASARS_PARAMS *params){

  int Astarbox = 0;
  if (params->CUTASTARS == 1){
     if(mag[3] < (limit + deltamag)){
       if ((mag[0] - mag[1]) > params->UGMINASTAR && (mag[0] - mag[1]) < params->UGMAXASTAR &&
           (mag[1] - mag[2]) > params->GRMINASTAR && (mag[1] - mag[2]) < params->GRMAXASTAR)
             Astarbox = 1;
       }
  }
  return Astarbox;
}

/*
****************************************************************************
** MID-Z QUASARS (2.5 < z < 3.0)
**
** Target some mid-z quasar candidates that are in the stellar locus.  
**
** Since the median quasar track actually passes through the stellar
** locus for mid-z quasars, we need to cut into the stellar locus
** somewhat to keep from losing all quasars in this redshift range.
** Objects in this box should be selected even if they are in the
** stellar locus, but not if they have other problems.
**
** NOTE: Currently, there are two distinct cuts.
**
** NOTE: This needs to be fixed.  This subroutine should be distinct
** from the A star rejection routine.  Also all reference to
** ellipsdist needs to be removed.  (GTR 6/27/00).
**
** NOTE: Removed all reference to ellipsdis (GTR, 6/28/00).
**
**************************************************************************** 
**/

int  midZbox(float *mag, float limit, float deltamag, const TA_QUASARS_PARAMS *params){
  int midZtarget = 0;
  if (params->TARGETMIDZ == 1){
     if ((mag[0] - mag[1]) > params->UGMINMIDZ && (mag[0] - mag[1]) < params->UGMAXMIDZ &&
         (mag[1] - mag[2]) > params->GRMINMIDZ && (mag[1] - mag[2]) < params->GRMAXMIDZ)
           midZtarget = 1;
  
     if ((mag[0] - mag[1]) > params->UGMINMIDZ2 && (mag[0] - mag[1]) < params->UGMAXMIDZ2 &&
         (mag[1] - mag[2]) > params->GRMINMIDZ2 && (mag[1] - mag[2]) < params->UGMAXMIDZ2)
           midZtarget = 1;
  }
  return midZtarget;
}

/*
****************************************************************************
** White Dwarf Rejection
**
** Reject very blue objects that are more likely to be white dwarfs
** than quasars.
**
** There is a region of color space where white dwarfs are the
** dominant stellar objects.  We can reject most of this color space
** in order to increase the efficiency of quasars target selection
** without a significant hit in completeness.  Objects in this box
** should not be selected (unless they are FIRST sources).
**
** Returns 0 (not in box) or 1 (in box)
**
****************************************************************************
**/

int cutWDregion(float *mag, float limit, float deltamag, const TA_QUASARS_PARAMS *params){

  int WDbox = 0;
  if (params->CUTWD == 1){ 
     if(mag[3] < limit + deltamag){
       if (mag[1] - mag[2] > params->GRMINWD && mag[1] - mag[2] < params->GRMAXWD &&
           mag[2] - mag[3] > params->RIMINWD && mag[2] - mag[3] < params->RIMAXWD &&
           mag[3] - mag[4] > params->IZMINWD && mag[3] - mag[4] < params->IZMAXWD) 
            WDbox = 1;
     }
  }
  return WDbox;
}

/*
****************************************************************************
** White Dwarf + M Star Rejection
**
** Reject unresolved pairs of white dwarfs and M stars.
**
** There is a region of color space where unresolved white dwarfs and
** M stars stick out from the stellar locus.  The density of real
** quasars is not very high here, but we want to investigate all
** stellar sources outside of the stellar locus as much as possible.
** This cut rejects the region that is most heavily contaminated by
** these objects.  Objects in this box should not be selected (unless
** they are FIRST sources).
**
** Returns 0 (not in box) or 1 (in box)
**
****************************************************************************
**/

int cutWMregion(float *mag, float limit, float deltamag, float *err, const TA_QUASARS_PARAMS *params){

   int WMbox = 0;
   if (params->CUTWM == 1){
      if(mag[3] < limit + deltamag){
        if (mag[1] - mag[2] > params->GRMINWM && mag[1] - mag[2] < params->GRMAXWM &&
            mag[2] - mag[3] > params->RIMINWM && mag[2] - mag[3] < params->RIMAXWM &&
            err[2] < params->RERRMAXWM)
             WMbox = 1;
      }
    }
    return WMbox;
}

/*
****************************************************************************
** GALAXY REGION CUT
** This routine cuts away galaxies which are within specific bounds of the 
** fundamental plane of galaxies. These bounds are set by params->GALMAX and
** params->GALMIN and the orientation of the plane in colorspace is governed
** by params->GALXCOEFF, params->GALYCOEFF and params->GALZCOEFF which are
** the x, y, and z components of the normal to the fundamental plane.
**
** CURRENTLY ALL FUNCTION CALLS TO THIS ARE COMMENTED OUT!!!!!
** USING l>0 CUT IN colorselectqso() FUNCTION,  TO SWITCH COMMENT OUT l>0
****************************************************************************
*/

int galaxyCut(const TA_CALIB_OBJ *obj, const TA_QUASARS_PARAMS *params, float *mag) {
   int color[3]; 
   int inGalaxylocus = 0;
   float galdist;
   int i;
   for(i=0; i<3; i++){
      color[i] = mag[i] - mag[i+1];
   }	
   if(obj->objc_type == 3){
      galdist = (color[0]*params->GALXCOEFF + color[1]*params->GALYCOEFF + color[2]*params->GALZCOEFF);
      
      if( galdist > params->GALMIN && galdist < params->GALMAX)
	 inGalaxylocus = 1;
   }
   return inGalaxylocus; 
}


/*
****************************************************************************
** Limit Correction
**
** NOTE: Heidi, please comment.
**
****************************************************************************
**/

static double limitcorrection(double luptitude, double error, double nsigma) {

#ifdef NOTDEF
     double counts, countserr, newluptitude, newluptitudeerr;
     atLupInvert(luptitude, error, &counts, &countserr);
     atLupApply(counts+nsigma*countserr, countserr, &newluptitude, &newluptitudeerr);
     return luptitude-newluptitude;
#endif

   double corr;
   if (error<10.0) {
      corr = 2.5*log10(1.0 + nsigma*(pow(10.,error/2.5)-1.0) + 0.00001);
   } else {
      corr = 2.5*(log10(nsigma) + error/2.5);
   }

   return corr;
}

/* 
****************************************************************************
** Stellar Locus Determination
**
** Determine whether or not an object is in the stellar locus.
**
** There are 3 sections to this code.  The first calculates the colors
** and errors, handling upper and lower limits, the second deals with
** the ugri color cube (low-redshift quasar candidates), and the third
** deals with the griz color cube (high-redshift quasar candidates).
**
** Returns -1 (error), 0 (not in locus), or 1 (in locus)
**
****************************************************************************
**/

static int colorselectqso (TA_CALIB_OBJ *obj, float mag[5], float err[5], int *ta, AR_OBJECT_TYPE type, double lowzlimit[5], TA_QUASARS_LOCUS locusarray, int hizflag, int *closestpoint, double *ellipsedist) {

    double color[4], var[4], covar[3];
    int zero;
    double err0;
    int colorindex, inlocus;
    int tmpflag1, tmpflag2;
    double object_k,object_l,object_m;
    int dummyclosestpoint=-1;
    double dummyellipsedist=-1;

    /**********************************************************************
     ** Calculate the colors and errors
     ** Handle cases that are limits
     **
     ** TODO: document the meaning of variance, covariance arrays (Heidi)
     **
     ** The constants ATQU_UNKNOWN and ATQU_*_LIMIT are defined in
     ** ASTROTOOLS include files
     **
     *********************************************************************/

    for (colorindex = 0; colorindex<4; colorindex++) {

     color[colorindex] = (double) (mag[colorindex] - mag[colorindex+1]);
  
      /* If both magnitudes are detected */ 
      tmpflag1 = obj->detection[ta[colorindex]].flags;
      tmpflag2 = obj->detection[ta[colorindex+1]].flags;
      if ((tmpflag1&(AR_DFLAG_BINNED1))&&(tmpflag2&(AR_DFLAG_BINNED1))) {
        var[colorindex] = err[colorindex]*err[colorindex] + err[colorindex+1]*err[colorindex+1];
        if (colorindex<3) covar[colorindex] = -err[colorindex+1]*err[colorindex+1];
      } else {
        if (colorindex<3) covar[colorindex] = 0;
        /* If bluer magnitude in color is fainter than the plate limit */
        if (!(tmpflag1&(AR_DFLAG_BINNED1))) {
          /* If redder magnitude in color is fainter than the plate limit */
          if (!(tmpflag2&(AR_DFLAG_BINNED1))) {
	    var[colorindex] = ATQU_UNKNOWN;
          } else {
	    var[colorindex] = ATQU_LOWER_LIMIT;
            if (mag[colorindex]<lowzlimit[colorindex]) {
	      color[colorindex] -= limitcorrection(mag[colorindex], err[colorindex], locusarray.nsigmaerrors);
            } else {
	      color[colorindex] = (lowzlimit[colorindex] - limitcorrection(mag[colorindex], err[colorindex], locusarray.nsigmaerrors)) - mag[colorindex+1];
            }
/*
            if (hizflag&&(colorindex==1)) {
              printf("lower limit: %g %g %g -> %g\n", mag[colorindex], err[colorindex], locusarray.nsigmaerrors, limitcorrection(mag[colorindex], err[colorindex], locusarray.nsigmaerrors));
            }
*/
          }
        } else { /* Redder magnitude is fainter than the plate limit*/
            var[colorindex] = ATQU_UPPER_LIMIT;
            if (mag[colorindex+1]<lowzlimit[colorindex+1]) {
	      color[colorindex] += limitcorrection(mag[colorindex+1], err[colorindex+1], locusarray.nsigmaerrors);
            } else {
              color[colorindex] = mag[colorindex] - (lowzlimit[colorindex+1]-limitcorrection(mag[colorindex+1], err[colorindex+1], locusarray.nsigmaerrors));
            }
            /* printf("upper limit: %g %g %g -> %g", mag[colorindex+1], err[colorindex+1], locusarray.nsigmaerrors, limitcorrection(mag[colorindex+1], err[colorindex+1], locusarray.nsigmaerrors)); */
        }
      }
    }

    /**********************************************************************
     ** High-redshift locus test
     **
     ** Test to see if this quasar candidate is in the stellar locus.
     ** Use the griz color cube instead of the ugri color cube
     ** (i.e zero=1).
     **
     ** NOTE: Currently the high-redshift quasars are also run through
     ** these lines of code (there are no brackets around the code,
     ** just around the "zero" stuff), which is not a problem, but it
     ** is a waste of processing time.
     **
     *********************************************************************/

    if (hizflag) {zero=1;} else {zero=0;}

    /* Is the object in the stellar locus? */
    inlocus = atQuLocusRemove(locusarray.lxl, locusarray.lyl, locusarray.lzl,
			      locusarray.lkhatx, locusarray.lkhaty,
			      locusarray.lkhatz, locusarray.la,
			      locusarray.lb, locusarray.theta, locusarray.npts, 
			      color[zero], color[zero+1], color[zero+2], var[zero], covar[zero], 0, var[zero+1],
			      covar[zero+1], var[zero+2], locusarray.mink, locusarray.minkdist, 
			      locusarray.maxk, locusarray.maxkdist, 
			      locusarray.nsigmaerrors,closestpoint,ellipsedist);

    /* A return value of -1 means none of the colors are known, so don't select it */
    if (inlocus==-1) {inlocus = 1;}
    if (hizflag&&(inlocus==0)) {
      /*printf("hizcand: %g %g %g %g %g %g %g %g\n", color[zero], color[zero+1], color[zero+2], var[zero], covar[zero], var[zero+1],covar[zero+1], var[zero+2]);
	printf("maginfo: %g %g %g %g %g\n", mag[0], mag[1], mag[2], mag[3], mag[4]);*/
    }
    
    if (inlocus==0) {
      for (colorindex=0; colorindex<4; colorindex++) {
	/*printf("color: %d, limitcorrection: %6.3g, var: %6.3g, mag: %6.3g, color: %6.3g, err: %6.3g\n", colorindex, limitcorrection, var[colorindex], mag[colorindex], color[colorindex], err[colorindex]);*/
      }
    }

    /**********************************************************************
     ** Low-redshift locus test
     **
     ** Test to see if this quasar candidate is in the stellar locus.
     ** Use the ugri color cube (i.e. zero=0).
     **
     *********************************************************************/

    if (!hizflag) {

      /* Here, k is the distance along the locus, l is the distance
       * along the wider axis of the locus cross section, and m is the
       * distance along the narrower axis if the locus cross section */
      atQuLocusKlmFromXyz(locusarray.lxl, locusarray.lyl, locusarray.lzl,
			  locusarray.lkhatx, locusarray.lkhaty, locusarray.lkhatz,
			  locusarray.theta, locusarray.npts, color[0], color[1], color[2],
			  &object_k, &object_l, &object_m);

      
      /* NOTE: Someone needs to explain why the l>0 was commented out
       * and what the heck the color cut is thre for.  Also, please do
       * NOT embed comments in the code in this fashion. */

      /* If l>=0 (source is on the galaxy side of the locus) and source
       * is not a point source, don't target it */







      /* Replaced type!=6 with type==3 (GTR, 6/27/00) */

      if (type==3  && object_l>0  &&
	  (object_k>locusarray.galaxyklimit || color[1] > 1)) {inlocus = 1;}
     
      /* NOTE: Xiaohui or Heidi, please comments.  Exactly what does "try
       * again" mean, and explain what is meant by bigger errors. */
      
      /* for a star on the wrong side of the stellar locus, try again */
      if(type==6 && object_l>=0 && object_k>locusarray.galaxyklimit&&inlocus==0) {
	
	/* Redo locus calculation with the bigger errors */
	inlocus = atQuLocusRemove(locusarray.lxl, locusarray.lyl, locusarray.lzl,
				  locusarray.lkhatx, locusarray.lkhaty,
				  locusarray.lkhatz, locusarray.la,
				  locusarray.lb, locusarray.theta,
				  locusarray.npts, color[0], color[1], color[2], var[0], covar[0], 0, var[1],
				  covar[1], var[2], locusarray.mink, 
				  locusarray.minkdist, locusarray.maxk,
				  locusarray.maxkdist, bigNsigerrors, 
				  &dummyclosestpoint, &dummyellipsedist);
	/* A return value of -1 means none of the colors are known, so don't select it */
	if (inlocus==-1) {inlocus = 1;}
	
      } /* End checking point sources in galaxy side of locus */
      
    } /* End eliminating galaxies in ugri */
    
    return inlocus;
}

/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:	taQuasarsTargetSelect
**
**<HTML>
**	Set the appropriate bit in the primary target bit mask for each
**	Quasars Working Group target selection criterion this object
**	satisfies.  This routine is responsible for setting the following
**	bits:
**	<menu>
**	<li> AR_TARGET_QSO_HIZ
**	<li> AR_TARGET_QSO_CAP
**	<li> AR_TARGET_QSO_SKIRT
**	<li> AR_TARGET_QSO_FIRST_CAP
**	<li> AR_TARGET_QSO_FIRST_SKIRT
**	<li> AR_TARGET_QSO_FAINT
**	</menu>
**	It is assumed that the above bits are unset on entry, although other
**	bits may be set.
**	<P>
**	This routine returns the priority of the quasar candidate, in the
**	range 0-255.  On error, -1 is returned.  Zero means it should not 
**	be targetted.  We would like to give the same priority (200) to all 
**	QSO targets except FIRST QSO targets which are selected as being 
**	on a line between two FIRST sources.  The latter type of QSO 
**	receives lower priority (100).  In the future the lower priority
**	will be replaced by a range of priority.
**
**      NOTE: Update me after all changes are made.
**
**</HTML>
**
**</AUTO>
******************************************************************************
*******************************************************************************/
int
taQuasarsTargetSelect(TA_CALIB_OBJ *obj,
		      /* MOD: calibrated object to test */
		      const TA_QUASARS_PARAMS *params
		      /* IN: tunable parameters*/)
{
  
  float mag[5],err[5];
  double syserr[5];
  double lowzlimit[5];
  /* double object_k,object_l,object_m; */
  int colorindex;
  /* double limitcorrection; */
  int capobject, index, tmpflag,tmpflag2, maglimit, tsflag;
  int inlocus, inGalaxylocus = 0;
  int ImageProb=0, badCol=0;
  int ta[5], det[5];
  int priority=0;
  float fseed, random, deltamag;
  int closestpoint;	/* added for debugging purposes - not used to target */
  double ellipsedist;	/* added for debugging purposes - not used to target */
  int targetmidZ = 0; 
  int inAstarbox = 0;
  int inWDbox = 0;
  int inWMbox = 0;	
  int validtarget = 1;
  /**** temp output ****/
  FILE *ftmp;
  int i, nchild;
  int intercenter, intercenterI;

/*
*************************************************************************
** FATAL Errors
**
** These next sections look for objects with fatal errors.  If the
** object is bad, we return 0, which means the object is never
** given a chance to be targeted.
**
*************************************************************************
**/

  /**********************************************************************
   ** Cut "bad" objects
   **
   ** Don't even bother to check objects which are bad.  Inlcuding
   ** bright objects, edge objects and those that are flagged as
   ** saturated.
   **
   *********************************************************************/

  if (obj->objc_flags&(AR_DFLAG_BRIGHT|AR_DFLAG_EDGE|AR_DFLAG_SATUR)) {
    return 0;
  }

  /**********************************************************************
   ** Cut blended objects
   **
   ** Do not select things which are blended but not deblended.
   **
   ** NOTE: This will reject small separation gravitational lenses.
   **
   ** NOTE: Who commented out the NODEBLEND thing?  Please don't embed
   ** comments in the code in this fashion.  Rather, comment out the
   ** entire old line, replace the line and give a comment explaining
   ** the change.  Do we really want to reject ALL blended objects?
   **
   *********************************************************************/

  if ((obj->objc_flags & AR_DFLAG_BLENDED) /* && !(obj->objc_flags & AR_DFLAG_NODEBLEND) */ ) {
    return 0;
  }

  /**********************************************************************
   ** Reject objc_types other than stellar or galaxy
   **
   ** Reject things which don't have either objc_type == 3 (galaxy) or
   ** objc_type == 6 (stellar), because the other types are all error
   ** codes.
   **
   *********************************************************************/

  if ((obj->objc_type != 3) && (obj->objc_type != 6)) {
    return 0;
  }

  /**********************************************************************
   ** Disregard moving objects
   **
   ** Objects which are deblended as moving are probably asteriods,
   ** but certainly not quasars.  However, their colors can be
   ** non-solar if the deblend isn't perfect, so we have to reject
   ** them explicitly.
   **
   *********************************************************************/

  if((obj->objc_flags2 & AR_DFLAG2_DEBLENDED_AS_MOVING))
    return 0;

  /**********************************************************************
   ** Disregard more moving objects
   **
   ** Some objects appear to be moving, but the deblending failed.
   ** Again we must reject such objects explicitly.  Throw away things
   ** that have large velocities (if those velocities are significant
   ** with respect to the error).
   **
   *********************************************************************/

  if( !(obj->objc_flags2 & AR_DFLAG2_BAD_MOVING_FIT) &&
    ( (fabs(obj->rowv.val) > maxV ) && (fabs(obj->rowv.val)/obj->rowv.err > maxVSig) || 
      (fabs(obj->colv.val) > maxV ) && (fabs(obj->colv.val)/obj->colv.err > maxVSig)) )
	return 0;

  /**********************************************************************
   ** Correct magnitudes and errors for galactic reddening
   **
   ** We use the Schlegel maps to correct for galactic reddening.  We
   ** then must add the error in the reddening in quadrature with the
   ** error in the magnitude.
   ** 
   ** NOTE: Schlegel says that the error in the reddening correction
   ** is 15% of the value, which is what is coded.  It may be that
   ** since the errors in different filters are correlated that we now
   ** overestimate the errors in colors.
   **
   *********************************************************************/

  ta[0] = TA_U;
  ta[1] = TA_G;
  ta[2] = TA_R;
  ta[3] = TA_I;
  ta[4] = TA_Z;
  
  for (colorindex=0; colorindex<5; colorindex++) {
    mag[colorindex] = obj->detection[ta[colorindex]].psfCounts.val-obj->reddening[ta[colorindex]]; 
    err[colorindex] = sqrt(obj->detection[ta[colorindex]].psfCounts.err\
			   *obj->detection[ta[colorindex]].psfCounts.err\
			   +0.15*0.15*obj->reddening[ta[colorindex]]\
			   *obj->reddening[ta[colorindex]]);
  }

  /**********************************************************************
   ** Identify objects with image problems
   **
   ** NOTE: Xiaohui or Michael, please comment (in further detail).
   ** Also, please explain why you set ImageProb=1 for some of these
   ** rather than simply returning 0.
   **
   *********************************************************************/

  if((obj->objc_flags  & AR_DFLAG_CHILD)!=0) {
    nchild=1;
  } else {
    nchild=0;
  }
  
  intercenter=0;
  intercenterI=0;
  
  /* if object is a child and has PEAKCENTER or NOTCHECKED flags in high S/N band,
   * there must be a problem of deblending */
  
  for (colorindex=0; colorindex<5; colorindex++) {
      tmpflag=obj->detection[ta[colorindex]].flags;
      tmpflag2=obj->detection[ta[colorindex]].flags2;
      if (tmpflag & AR_DFLAG_BINNED1) det[colorindex]=1;
		else det[colorindex]=0;
      if(mag[colorindex]<23 && err[colorindex]<minDetect) {
          if(nchild == 1)  
	    if (!(tmpflag & AR_DFLAG_BINNED1)||(tmpflag&(AR_DFLAG_PEAKCENTER|AR_DFLAG_NOTCHECKED)) || (tmpflag2 & AR_DFLAG2_DEBLEND_NOPEAK)) ImageProb=1;
    	}

      /* if object has INTERP_CENTER flag in one of the high S/N band, add error
       * by errInterpCenter */

      if((tmpflag2 & AR_DFLAG2_INTERP_CENTER) && det[colorindex]==1)
	{
	err[colorindex] = sqrt ( err[colorindex] * err[colorindex] + errInterpCenter * errInterpCenter);
	intercenter=1;
	if(colorindex==3) intercenterI=1;
	}
  }

  /**********************************************************************
   ** Delete object brighter than iBright (too bright for the spec.)
   **
   ** While we can take spectra of bright objects, they will bleed
   ** into neighboring spectra.  Therefore we have to set a limit on
   ** how bright of an object we will take spectra of.
   **
   *********************************************************************/

  if (mag[3] < iBright) return 0;

  /**********************************************************************
   ** Delete relatively bright objects with INTERP_CENTER set
   **
   ** If object is brightter than iBrightInterp, and has INTERP_CENTER
   ** in i' it is typically an image problem
   **
   *********************************************************************/

  if (mag[3] < iBrightInterp && intercenterI == 1) ImageProb=1;


  /**********************************************************************
   ** Delete some really blue objects.
   **
   ** Disregard objects with i-z < izMin seems to get rid of some bad
   ** deblended children, in particular in the case of close pairs
   **
   *********************************************************************/

  if (mag[3]- mag[4] < izMin) ImageProb=1;

  /**********************************************************************
   ** Delete bad places on CCD (hot columns, etc.)
   **
   ** There are some CCD defects which cause the colors of objects to
   ** be messed up.  Typically, this causes stars to be shifted out of
   ** the stellar locus.  As a result we have to reject objects that
   ** fall in these places explicitly.
   **
   *********************************************************************/

  /* Old defect areas 
     camCol == 5 && olc<1032 && colc>1018
     camCol == 2 && colc<1461 && colc>1451
     camCol == 2 && colc>1382 && colc < 1388 */
  
  /*
    if(obj->camCol == 2 && obj->objc_colc.val < 1461 && obj->objc_colc.val > 1451) ImageProb=1;
    if(obj->camCol == 2 && obj->objc_colc.val < 1388 && obj->objc_colc.val > 1382) ImageProb=1;
  */

  /* Current defect area */
  if(obj->camCol == 5 && obj->objc_colc.val < 1032 && obj->objc_colc.val > 1018) ImageProb=1;

  /** NOTE: Is the any reason why we can't just return a 0 for
   ** ImageProb==1 at this point? **/


  /**********************************************************************
   ** Add systematic errors
   **
   ** Despite our best efforts, the photometric errors will not
   ** incorporate all of the possible error in the determination of
   ** magnitudes for these objects.  As such add some additional error
   ** based on practical experience.
   **
   *********************************************************************/

  syserr[0]=params->SYSUERR;
  syserr[1]=params->SYSGERR;
  syserr[2]=params->SYSRERR;
  syserr[3]=params->SYSIERR;
  syserr[4]=params->SYSZERR;

  for (colorindex=0; colorindex<5; colorindex++) {
    err[colorindex]=sqrt(err[colorindex]*err[colorindex]+syserr[colorindex]*syserr[colorindex]);
  }
  
  lowzlimit[0] = params->LOWZULIMIT;
  lowzlimit[1] = params->LOWZGLIMIT;
  lowzlimit[2] = params->LOWZRLIMIT;
  lowzlimit[3] = params->LOWZILIMIT;
  lowzlimit[4] = params->LOWZZLIMIT;

  /**********************************************************************
   ** Find out if the object is in the CAP or SKIRT
   **
   *********************************************************************/
   
  capobject = determineCapSkirt(obj->l,obj->b,params->CAPSTARS);

  /**********************************************************************
   ** Add Fuzz
   **
   ** Calculate a small random increase in the magnitude limit for
   ** this object.  Note that we use the same random magnitude
   ** increase for FIRST selection and color selection.
   **
   ** NOTE: currently params->magnitudeFuzz=0, so no fuzz
   **
   *********************************************************************/

  fseed = obj->objc_rowc.val * obj->objc_colc.val;
  random = smaRAND(fseed);	/* Seed the random number generator */
  random = smaRAND(0.0);	/* 2nd call to fuzz the magnitude limit */
  deltamag = params->magnitudeFuzz*random;

  /**********************************************************************
   ** FIRST Match
   **
   ** If the source is matched to a FIRST source, then firstMatch is
   ** 1.  If it is on a line adjoining two first sourced, then
   ** firstMatch > 1.  firstDelta tells you how far the first source
   ** position (or line between first sources) is from the optical
   ** object.  Only stellar objects (objc_type==6) are selected.
   **
   ** IMPORTANT: FIRST matching and target selection should occur
   ** after checking for FATAL errors, but before checking for
   ** non-FATAL errors.  FIRST sources should be selected even if they
   ** are in "restricted" regions of colors space, but they should be
   ** rejected if the magnitudes that we would publish would be in
   ** error b/c of a FATAL error (but see below).
   **
   ** NOTE: We do not flag first sources fainter than the
   ** spectroscopic limits for finding FIRST QSOs.  People could still
   ** find these by looking at the firstMatch flag.
   **
   ** NOTE: Why is the cap limit CAPILIMIT, whereas the skirt limit is
   ** SKIRTFIRSTLIMIT.
   **
   ** TODO: Add range of priority for the firstMatch>1 candidates
   **
   *********************************************************************/

  if (obj->objc_type==6) { 		/* Stellar objects only */

    if (obj->firstMatch > 0) {
      /* CAP */
      if (capobject && (mag[3] < params->CAPILIMIT + deltamag)) {
        obj->primTarget |= AR_TARGET_QSO_FIRST_CAP;
        if (obj->firstMatch > 1) {priority = 100;} else {priority = 200;}
      }
      /* SKIRT */
      if (!capobject && (mag[3] < params->SKIRTFIRSTILIMIT + deltamag)) {
        obj->primTarget |= AR_TARGET_QSO_FIRST_SKIRT;
        if (obj->firstMatch > 1) {priority = 100;} else {priority = 200;}
      }
    }
  }
  
  /** If this object has any image problems, do not select it as a
   ** color outlier this is after the FIRST selection **/
  
  /* NOTE: I'm not sure that this should be the case.  Do we really
   * want to target FIRST sources for which the photometry is
   * corrupted? Granted, they are still good quasars candidates, but
   * we don't want to publish bad photometry (GTR, 06/28/00). */

  if(ImageProb==1) return 0;

  /**********************************************************************
   ** Select CAP targets
   **
   *********************************************************************/
  
  /* NOTE: since we may just get rid of the cap/skirt thing, why don't
   * we try to consolidate this code.  We could do something like:
   *
   * if (capobject {
   * maglimit = CAPILIMIT;
   * tsflag = AR_TARGET_QSO_CAP;
   * } else {
   * maglimit = SKIRTILIMIT;
   * tsflag = AR_TARGET_QSO_SKIRT;
   * }
   *
   * then just have one section of code.
   *
   * Adam, can you look into doing this?
   */


  if (capobject) {
    maglimit = params->CAPILIMIT;
     
    inlocus = colorselectqso(obj, mag, err, ta, obj->objc_type, lowzlimit,
			     params->stellarlocus, 0, &closestpoint, &ellipsedist);
    /* printf ("%d, %d, %g\n", inlocus, closestpoint, ellipsedist); */
    if (inlocus<0) return -1;
    
    /*inGalaxylocus = galaxyCut(obj, params, mag); */

    /**********************************************************************
     ** Test for special case color regions
     **
     ** These routines set the in___box bits which will prevent or assure
     ** targetting on objects within those regions
     **
     *********************************************************************/
    if (params->CUTBOXES == 1){ 
    
       inAstarbox = cutAstars( mag, maglimit, deltamag, params);
       targetmidZ = midZbox( mag, maglimit, deltamag, params);

/****** urgent fix, NOT FINAL!!! ********/
/* this will effectively select objects further from the stellar
locus in the midz box; the usage of ellipsedist is incorrect,
but as least it keeps us from selecting too many quasars,
so given the time, we will do it right now and change later */


	if (ellipsedist < params->MINMIDZDIST) targetmidZ = 0;

/*******/
       inWDbox = cutWDregion( mag, maglimit, deltamag, params);
       inWMbox = cutWMregion( mag, maglimit, deltamag, err, params);
       
       if( inAstarbox == 1 || inWDbox == 1 || inWMbox == 1 )
         validtarget = 0; 
    } 
    
    /** Some significant changes were made here (GTR, 6/27/00) */
    
    /*********************************************************************
     ** Select bright CAP objects.
     **
     ** If i' is brighter than the plate limit in the cap, then set
     ** QSO_CAP.  If it is not brighter than the plate limit, but
     ** it is still a good quasar candidate, then set QSO_FAINT,
     ** which sets a target flag, but does not target the object.
     **
     *********************************************************************/
    
    if (mag[3] < maglimit + deltamag) {
      if (targetmidZ || (!inlocus && !inGalaxylocus && validtarget == 1)) {
	obj->primTarget |= AR_TARGET_QSO_CAP;
	priority = 200;

/*** tmp output  
	       ftmp=fopen("q745","a");
       fprintf(ftmp,"%d %d %d %d %d %d %d ",obj->run, obj->camCol, obj->fieldId,
 obj->id, obj->objc_type, nchild,intercenter);
       for(i=0; i<5; ++i)
       fprintf(ftmp, "%5.2f %5.2f ",mag[i],err[i]);
       fprintf(ftmp," 1 %6.3f\n",ellipsedist);
       fclose(ftmp);
       ftmp=fopen("q745_flag","a");
       fprintf(ftmp,"%d %d %d %d ", obj->run, obj->camCol, obj->fieldId, obj->id
);
       for(i=0; i<5; ++i)
       fprintf(ftmp,"%d %d ",obj->detection[i].flags,obj->detection[i].flags2);
       fprintf(ftmp,"\n");
       fclose(ftmp);

****/

      }
    } else {
      if (targetmidZ || (!inlocus && !inGalaxylocus && validtarget == 1)) {
	obj->primTarget |= AR_TARGET_QSO_FAINT;
      }
    }
    
    /**********************************************************************
     ** Select fainter CAP objects if they are z>3 candidates
     **
     ** If the object has u-g color indicating it in
     ** the z>3 region, the magnitude limit is HIZILIMIT.
     ** Set QSO_HIZ instead of QSO_CAP.
     **
     ** No need to set QSO_FAINT here, since that is taken care of
     ** above.
     **
     ** NOTE: The HIZILIMIT in the param file is NOT for this.
     **
     *********************************************************************/
    
    if (mag[3] < params-> HIZILIMIT + deltamag && (mag[0] - mag[1]) > ugMinHighz) {
      if (!inlocus && (params->CUTBOXES == 0||(inAstarbox == inWDbox == inWMbox == 0))) {
	obj->primTarget |= AR_TARGET_QSO_HIZ;
	priority = 200;
      }
    }
    
       
    /*********************************************************************
     ** Select bright SKIRT objects.
     **
     ** If i' is brighter than the plate limit in the skirt, then set
     ** QSO_SKIRT.  If it is not brighter than the plate limit, but
     ** it is still a good quasar candidate, then set QSO_FAINT,
     ** which sets a target flag, but does not target the object.
     **
     *********************************************************************/
    
  } else {
    /* If object is not in CAP, but rather is in the SKIRT */
   maglimit = params->SKIRTILIMIT; 

   inlocus = colorselectqso(obj, mag, err, ta, obj->objc_type, lowzlimit,
                             params->stellarlocus, 0, &closestpoint, &ellipsedist);

    /* printf ("%d, %d, %g\n", inlocus, closestpoint, ellipsedist); */
    if (inlocus<0) return -1;
    /*if (inlocus==0) {printf("id %d\n", obj->id);}*/

   /*inGalaxylocus = galaxyCut(obj, params, mag);*/
 
    /**********************************************************************
     ** Test for special case color regions
     **
     ** These routines set the in___box bits which will prevent
     ** targetting on objects within those regions
     **
     *********************************************************************/
    if (params->CUTBOXES == 1){
   
       inAstarbox = cutAstars( mag, maglimit, deltamag, params);
       targetmidZ = midZbox( mag, maglimit, deltamag, params);

/****** urgent fix, NOT FINAL!!! ********/
/* this will effectively select objects further from the stellar
locus in the midz box; the usage of ellipsedist is incorrect,
but as least it keeps us from selecting too many quasars,
so given the time, we will do it right now and change later */


        if (ellipsedist < params->MINMIDZDIST) targetmidZ = 0;

/******/

       inWDbox = cutWDregion( mag, maglimit, deltamag, params);
       inWMbox = cutWMregion( mag, maglimit, deltamag, err, params);

       if( inAstarbox == 1 || inWDbox == 1 || inWMbox == 1 )
         validtarget = 0;
    }

    
    
    /** Some significant changes were made here (GTR, 6/27/00) */
    
    /*********************************************************************
     ** Select bright SKIRT objects.
     **
     ** If i' is brighter than the plate limit in the skirt, then set
     ** QSO_SKIRT.  If it is not brighter than the plate limit, but
     ** it is still a good quasar candidate, then set QSO_FAINT,
     ** which sets a target flag, but does not target the object.
     **
     *********************************************************************/
    
    if (mag[3] < maglimit + deltamag) {
      if (targetmidZ || (!inlocus && !inGalaxylocus && validtarget == 1)) {
	obj->primTarget |= AR_TARGET_QSO_SKIRT;
	priority = 200;

/*** tmp output 
       ftmp=fopen("q745","a");
       fprintf(ftmp,"%d %d %d %d %d %d %d ",obj->run, obj->camCol, obj->fieldId,
 obj->id, obj->objc_type, nchild,intercenter);
       for(i=0; i<5; ++i)
       fprintf(ftmp, "%5.2f %5.2f ",mag[i],err[i]);
       fprintf(ftmp," 2 %6.3f\n",ellipsedist);
       fclose(ftmp);
       ftmp=fopen("q745_flag","a");
       fprintf(ftmp,"%d %d %d %d ", obj->run, obj->camCol, obj->fieldId, obj->id
);
       for(i=0; i<5; ++i)
       fprintf(ftmp,"%d %d ",obj->detection[i].flags,obj->detection[i].flags2);
       fprintf(ftmp,"\n");
       fclose(ftmp);

***/

      }
    } else {
      if (targetmidZ || !(!inlocus && !inGalaxylocus && validtarget == 1)) {
	obj->primTarget |= AR_TARGET_QSO_FAINT;
      }
    }
    
    /**********************************************************************
     ** Select fainter SKIRT objects if they are z>3 candidates
     **
     ** If the object has u-g color indicating it in
     ** the z>3 region, the magnitude limit is HIZILIMIT.
     ** Set QSO_HIZ instead of QSO_CAP.
     **
     ** No need to set QSO_FAINT here, since that is taken care of
     ** above.
     **
     ** NOTE: The HIZILIMIT in the param file is NOT for this.
     **
     *********************************************************************/
    
    if (mag[3] < params-> HIZILIMIT + deltamag && (mag[0] - mag[1]) > ugMinHighz) {
      if (!inlocus && (params->CUTBOXES == 0||(inAstarbox == inWDbox == inWMbox == 0))) {
	obj->primTarget |= AR_TARGET_QSO_HIZ;
	priority = 200;

       ftmp=fopen("q745","a");
       fprintf(ftmp,"%d %d %d %d %d %d %d ",obj->run, obj->camCol, obj->fieldId,
 obj->id, obj->objc_type, nchild,intercenter);
       for(i=0; i<5; ++i)
       fprintf(ftmp, "%5.2f %5.2f ",mag[i],err[i]);
       fprintf(ftmp," 2 %6.3f\n",ellipsedist);
       fclose(ftmp);
       ftmp=fopen("q745_flag","a");
       fprintf(ftmp,"%d %d %d %d ", obj->run, obj->camCol, obj->fieldId, obj->id
);
       for(i=0; i<5; ++i)
       fprintf(ftmp,"%d %d ",obj->detection[i].flags,obj->detection[i].flags2);
       fprintf(ftmp,"\n");
       fclose(ftmp);


      }
    }
    
       
  }

  /**********************************************************************
   ** Select High-z Quasar Candidates
   **
   ** Look for hiz objects (this is tiled hiz - not the fishing expedition)
   **
   ** NOTE: Heidi, please comment on how this is different from low-z.
   **
   *********************************************************************/

  maglimit = params->HIZILIMIT; 

 
    /*printf("%d %6.3g %6.3g %6.3g %6.3g %6.3g %6.3g %6.3g %6.3g %6.3g %d\n", params->hiznloci,params->hizlocusarray[0].lxl[0],params->hizlocusarray[0].lyl[0],params->hizlocusarray[0].lzl[0],params->hizlocusarray[0].lkhatx[0],params->hizlocusarray[0].lkhaty[0],params->hizlocusarray[0].lkhatz[0],params->hizlocusarray[0].la[0],params->hizlocusarray[0].lb[0],params->hizlocusarray[0].theta[0],params->hizlocusarray[0].npts);*/
    
    inlocus = colorselectqso(obj, mag, err, ta, obj->objc_type, lowzlimit,
			     params->hizlocus, 1, &closestpoint, &ellipsedist);
    if (inlocus<0) return -1;
    
    /**********************************************************************
     ** Add Fuzz
     **
     ** Calculate a small random increase in the magnitude limit for
     ** this object.  Note that we use the same random magnitude
     ** increase for FIRST selection and color selection.
     **
     ** NOTE: currently params->magnitudeFuzz=0, so no fuzz
     **
     ** NOTE: Why do we do this again here (GTR, 06/27/00)?
     **
     *********************************************************************/
    
    random = smaRAND(0.0);        /* 2nd call to fuzz the magnitude limit */
    deltamag = params->magnitudeFuzz*random;
    
    /**********************************************************************
     ** Test for special case color regions
     **
     ** Cut out objects in WD region, in W+M region, and A star region.
     **
     *********************************************************************/
 
    
if (params->CUTBOXES == 1){

       inAstarbox = cutAstars( mag, maglimit, deltamag, params);
       inWDbox = cutWDregion( mag, maglimit, deltamag, params);
       inWMbox = cutWMregion( mag, maglimit, deltamag, err, params);

       if( inAstarbox == 1 || inWDbox == 1 || inWMbox == 1 )
         validtarget = 0;
    }

    /*********************************************************************
     ** Select bright HIZ objects.
     **
     ** If i' is brighter than the hiz plate limit, then set QSO_HIZ.
     ** If it is not brighter than the plate limit, but it is still a
     ** good quasar candidate, then set QSO_FAINT, which sets a target
     ** flag, but does not target the object.  We also require that
     ** these objects all have objc_type==6, since no real high-redshift
     ** quasars should be extended.
     **
     ** NOTE: The HIZILIMIT in the param file is NOT for this.
     **
     *********************************************************************/
    
    if(mag[3] < params->HIZILIMIT + deltamag){
      
      /*********************************************************************
       ** cut out faint UVX quasars selected as HIZ
       **
       ** NOTE: Xiaohui or Michael, please comment.
       **
       ** NOTE: Bad use of "inlocus".
       **
       *********************************************************************/
      
      if (det[0] == 1 && det[1] == 1 && mag[0] - mag[1] < ugMaxFaint &&
	  det[2] == 1 && mag[1] - mag[2] < grMaxFaint)
	inlocus = 1;
      
      /*********************************************************************
       ** cut out high-redshift galaxies
       **
       ** If in z=0.4 galaxy region, increase to 5 sigma
       **
       ** NOTE: Commented out by GTR 06/27/00.  With the new versions of
       ** PHOTO, high-z galaxies should all be flagged as extended
       ** (objc_type==3) and we reject all such objects as high-redshift
       ** quasar candidates.
       **
       *********************************************************************/
      
      /*
	if (det[1] == 1 && det[2] == 1 && mag[1] - mag[2] > grMinGal && mag[1] - mag [2] < grMaxGal &&
	det[2] == 1 && det[3] == 1 && mag[2] - mag[3] > riMinGal && mag[2] - mag [3] < riMaxGal &&
	ellipsedist < minGalSig ) 
	inlocus = 1;
      */
      
      /* Set the target flag */
      if (!inlocus && validtarget == 1 && obj->objc_type == 6) {
	obj->primTarget |= AR_TARGET_QSO_HIZ;
	priority = 200;
	 
      
      }
    } else { 
      /* Set a target flag, but do not target the object */
      if (!inlocus && validtarget == 1 && obj->objc_type == 6) {
	obj->primTarget |= AR_TARGET_QSO_FAINT;
      }
    }
    
    /* Successful return */
    return priority;
}
