/*
****************************************************************************
******************************************************************************
**<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
**
** All parameter definitions are now in /etc/taQuasarsParams.par
**
****************************************************************************
**/

/* These are still here b/c moving them requires adding an argument to
 * colorselectqso.  However, they are left here because I don't see
 * colorselect qso in the include file, so I'm not sure where to make
 * all the changes. (GTR, 09/05/00) */

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

#define BIGNSIGERRORS 8.0

/* MINDETECT and MAXDETECT are parameters used in flag checking,
 * basically, if the photometric error is smaller than MINDETECT, then
 * the object should have be set BINNED1, otherwise, there is a
 * problem with the image; similarly, if the photometric error is
 * larger than MAXDETECT, then this object should not have BINNED1 */

#define MINDETECT	0.12
#define MAXDETECT	0.25

/*
****************************************************************************
** 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.
** Note that the code does indicate for each low-z quasar candidate whether
** it is in the cap or skirt, even if it doesn't make the distinction between
** the two. 
**
** 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 */
  }

}

/*
****************************************************************************
** REJECT BOX DEFINITIONS
**
**************************************************************************** 
**/

/*
****************************************************************************
** 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 (which turns out to be somewhat bluer than 
** z=2.5-3 quasars).  Objects in this box should not be selected
** unless they are also FIRST sources.
** (Note that objects in this box are only rejected in the main code; this 
** routine only determines whether the objects are in or out of the box.)
**
** Returns 0 (not in box) or 1 (in box)
**
**************************************************************************** 
**/

int cutAstars (float *mag, const TA_QUASARS_PARAMS *params){
  
  int Astarbox = 0;
  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 &&
      (mag[2] - mag[3]) > params->RIMINASTAR && (mag[2] - mag[3]) < params->RIMAXASTAR &&
      (mag[3] - mag[4]) > params->IZMINASTAR && (mag[3] - mag[4]) < params->IZMAXASTAR)
    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 that objects in this box are only rejected in the main code; this 
** routine only determines whether the objects are in or out of the box.)
**
** NOTE: Currently, there are two distinct cuts.
**
**************************************************************************** 
**/

int  midZbox(float *mag, const TA_QUASARS_PARAMS *params){
  int midZtarget = 0;
  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->GRMAXMIDZ2)
    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).
** (Note that objects in this box are only rejected in the main code; this 
** routine only determines whether the objects are in or out of the box.)
**
** Returns 0 (not in box) or 1 (in box)
**
****************************************************************************
**/

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

  int WDbox = 0;
  if (mag[0] - mag[1] > params->UGMINWD && mag[0] - mag[1] < params->UGMAXWD &&
      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 dwarf/
** M star pairs stick out from the stellar locus.  The density of real
** quasars is quite low here. 
** 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).
** (Note that objects in this box are only rejected in the main code; this 
** routine only determines whether the objects are in or out of the box.)
**
** Returns 0 (not in box) or 1 (in box)
**
****************************************************************************
**/

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

   int WMbox = 0;
   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 determines whether an object lies close to the color locus 
** 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 galaxy locus.
** (Note that objects in this box are only rejected in the main code; this 
** routine only determines whether the objects are in or out of the box.)
**
** CURRENTLY ALL FUNCTION CALLS TO THIS ARE COMMENTED OUT!!!!! This
** code needs to be test and we need to decide if we want to use it or
** not.
**
**************************************************************************** */

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. None of us (Gordon, Xiaohui or Michael) 
** actually understand what's going on here. 
**
****************************************************************************
**/

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 determines whether
** the object in question lies outside the ugri color cube (low-redshift 
** quasar candidates), or the griz color cube (high-redshift quasar 
** candidates), and the third does some further checks for the low-z cube, 
** especially for cool stars that are relatively blue in u-g (see more 
** detailed comments below). 
**
** Sets ellipsedist and closestpoint.
**
** 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;
    int det1, det2, flag2_1, flag2_2;
    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]);
  
   /*********************************************************************
    ** set the variance according to whether the object has been detected
    ** in either band related to the color:
    ** det1(2) = 1 if it is detected; 
    **
    ** however, there is one complication: PHOTO sets BINNED1 = 1 if
    ** the object has AR_DFLAG2_DEBLEND_NOPEAK. In fact, it should
    ** really be treated as not detected. det = 0.
    **
    ** Furthermore, experience indicates that the error is larger than
    ** MAXDETECT, and it has DEBLEND_NO_PEAK, that indicates problem,
    ** and the colors associated with this band should be disregarded
    ** by setting the var to be ATQU_UNKNOWN.
 
                                XF 000825
   *********************************************************************/

      tmpflag1 = obj->detection[ta[colorindex]].flags;
      flag2_1 = obj->detection[ta[colorindex]].flags2;
      tmpflag2 = obj->detection[ta[colorindex+1]].flags;
      flag2_2 = obj->detection[ta[colorindex+1]].flags2;

      if( (tmpflag1&AR_DFLAG_BINNED1) && !(flag2_1 & AR_DFLAG2_DEBLEND_NOPEAK))
    		det1=1;
      else 
                det1=0; 

      if( (tmpflag2&AR_DFLAG_BINNED1) && !(flag2_2 & AR_DFLAG2_DEBLEND_NOPEAK))
                det2=1;
      else 
                det2=0;


      /* If both magnitudes are detected */ 
      if ( det1 == 1 && det2 == 1) {
        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 ( det1 == 0 ) {
          /* If redder magnitude in color is fainter than the plate limit */
	  if( det2 == 0 ) {
	    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];
            }
          }
        } 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)); */
        }
      }

      if ( (tmpflag1&(AR_DFLAG_BINNED1)) && (err[colorindex] > MAXDETECT) &&
                (flag2_1 & AR_DFLAG2_DEBLEND_NOPEAK) )
                var[colorindex] = ATQU_UNKNOWN;

      if ( (tmpflag2&(AR_DFLAG_BINNED1)) && (err[colorindex+1] > MAXDETECT) &&
                (flag2_2 & AR_DFLAG2_DEBLEND_NOPEAK) )
                var[colorindex] = ATQU_UNKNOWN;
    }
    
    /**********************************************************************
     ** Stellar locus test
     **
     ** Test to see if this quasar candidate is in the stellar locus.
     **
     ** If the object is a hiz candidate, then
     ** Use the griz color cube instead of the ugri color cube
     ** (i.e zero=1).
     **
     *********************************************************************/

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

    /* Is the object in the griz 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.  This rejection is now done in the main code
     * (GTR, 09/05/00) */

    /**********************************************************************
     ** For the low-z cube, there are two further tests that are done.  
     ** Both require determination of the k,l,m values of the closest point
     ** on the stellar locus; this is what is done by the atQuLocusKlmFromXyz
     ** code.  Extended objects (type == 3) are allowed by the code if they
     ** are of the appropriate color (i.e., blue enough); in this piece of 
     ** code, the determination of blueness is done via the value of k,l,m.
     ** In addition, there seems to be a substantial scattering of stars
     ** blueward of the stellar locus in u-g from the M star region; we get
     ** rid of these by artificially increasing the errors of these objects,
     ** and asking again whether they are consistent with being in the 
     ** locus.
     ** WARNING: The test for extended objects is OBSELETE, and should be
     ** deleted (after suitable testing): a much more straightforward test
     ** for blueness is done in the main code.  It may also be true that the 
     ** need for increasing errors for M stars is also not necessary, but this
     ** will require further testing. 
     *********************************************************************/

    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);

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

      /* NOTE: Bad use of inlocus */
      /* NOTE: The "1" in color[1] > 1 should be changed to a parameter */

      if (type==3  && object_l>0  &&
	  (object_k>locusarray.galaxyklimit || color[1] > 1)) {inlocus = 1;}
     
      /* Red stars with l > 0 appear to be scattered blueward in u-g.  Let's 
	 try to eliminate these by increasing the errors by a factor of two
	 (parameter BIGNSIGERRORS), and asking again whether the object is
	 in the stellar locus.*/

      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);
	
      } /* 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
**	<li> AR_TARGET_QSO_REJECT
**	</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 (MAS, 09/05/00).
**
**</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];
  int colorindex;
  int capobject, index, tmpflag,tmpflag2, TSFLAG;
  float MAGLIMIT;
  int inlocus, inGalaxylocus = 0;
  int ImageProb=0, badCol=0, uvx=0;
  int ta[5], det[5];
  int priority=0;
  int closestpoint;
  double ellipsedist;
  int targetmidZ = 0; 
  int inAstarbox = 0;
  int inWDbox = 0;
  int inWMbox = 0;	
  int i, nchild;
  int intercenter, intercenterI;

  ta[0] = TA_U;
  ta[1] = TA_G;
  ta[2] = TA_R;
  ta[3] = TA_I;
  ta[4] = TA_Z;

/*
*************************************************************************
** 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.  Including
   ** 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.  Note that we do NOT
   ** keep NODEBLEND objects, as such objects are likely to have very
   ** unreliable colors.
   **
   *********************************************************************/

  if ((obj->objc_flags & AR_DFLAG_BLENDED)) {
    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
   **
   ** Throw away things
   ** that have large velocities (if those velocities are significant
   ** with respect to the error).  This *might* be redundant with the 
   ** above. 
   **
   *********************************************************************/

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

  /**********************************************************************
   ** Delete object brighter than i' = 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.  Reddening
   ** correction is NOT applied.
   **
   ** TODO: flag any such object with quasar-like colors with the QSO_REJECT
   ** flag, so that they might be followed up by others.  This will require
   ** moving this check forward to later in the code. 
   **
   *********************************************************************/

  if (obj->detection[ta[3]].psfCounts.val < params->IBRIGHT) return 0;

/*
*************************************************************************
** End FATAL Error checking, begin non-FATAL Error checking
**
** These objects are rejected, but not if they are also
** FIRST sources.
**
** Before doing that, we need to fill the magnitude and magnitude
** error vectors.
**
*************************************************************************
**/

  /**********************************************************************
   ** 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.
   **
   *********************************************************************/
  
  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]]);
  }

  /**********************************************************************
   ** 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.
   **
   ** TODO: Check that these additional errors have reasonable values. 
   **
   *********************************************************************/

  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]);
  }

  /**********************************************************************
   ** Identify objects with image problems, and flag them with ImagProb=1
   **
   ** The various tests that are done here are based on our experience 
   ** and are mostly empirical. 
   *********************************************************************/

  if((obj->objc_flags  & AR_DFLAG_CHILD)!=0) {
    nchild=1;
  } else {
    nchild=0;
  }
  
  intercenter=0;
  intercenterI=0;
  
  /* if object is a child, but its error is small, and if it is not */
  /* detected or is flagged as PEAKCENTER or NOTCHECKED or */
  /* DEBLEND_NOPEAK, then this is probably a deblending problem. */
  
  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 the error in any band is larger than CHILDERROR (currently set
   * to 1.0) (even if not flagged as detected) and it is a child, this
   * is indicative of a deblending problem.  Similarly, if the band is
   * marked as BINNED1 but the error is larger than 0.25 and it is a
   * child, this is also a deblend problem.  However, in the latter
   * case, note that BINNED1 is automatically set if DEBLEND_NOPEAK, so
   * we should not reject in this case */
      
      if(err[colorindex] > params->CHILDERROR && nchild == 1 ) ImageProb=1;
      if(det[colorindex] ==1 && err[colorindex]>MAXDETECT && nchild==1 &&
        !(tmpflag2 & AR_DFLAG2_DEBLEND_NOPEAK)) ImageProb=1;

      /* If object has INTERP_CENTER flag in one of the high S/N band, */
      /* the error is probably an estimate, so add ERRINTERPCENTER to */
      /* the error in quadrature*/

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

  /**********************************************************************
   ** Delete relatively bright objects with INTERP_CENTER set
   **
   ** If object is brighter than IBRIGHTINTERP, and has INTERP_CENTER
   ** in i' it is typically an image problem.  Reddening correction is
   ** not applied.
   **
   ** TODO: This may be obselete, and now fixed in PHOTO; check!
   **
   *********************************************************************/

  if (obj->detection[ta[3]].psfCounts.val < params->IBRIGHTINTERP && intercenterI == 1) 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.
   **
   ** TODO: Some recent CCD electronic changes might have eliminated
   ** some of these defects. Need to revisit this with new data (but
   ** make the code flexible to handle old data as well).
   *********************************************************************/

  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;
  if(obj->camCol == 5 && obj->objc_colc.val < 1032 && obj->objc_colc.val > 1018) ImageProb=1;
    
/*
*************************************************************************
** End non-FATAL Error checking, begin FIRST selection
**
** Objects with ImageProb = 1 are rejected unless that are FIRST
** sources.  This is done after the exclusion and inclusion boxes are
** checked.
*************************************************************************
**/

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

  /**********************************************************************
   ** 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 rejection of
   ** 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.
   **
   ** 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)) {
        obj->primTarget |= AR_TARGET_QSO_FIRST_CAP;
        if (obj->firstMatch > 1) {priority = 100;} else {priority = 200;}
      }

      /* SKIRT */
      if (!capobject && (mag[3] < params->SKIRTILIMIT)) {
        obj->primTarget |= AR_TARGET_QSO_FIRST_SKIRT;
        if (obj->firstMatch > 1) {priority = 100;} else {priority = 200;}
      }
    }
  }

/*
*************************************************************************
** End FIRST selection, begin checking exclusion and inclusion regions.
**
*************************************************************************
**/
  
  /**********************************************************************
  ** Test for special case color regions
  **
  ** These routines set the in___box bits which will prevent or assure
  ** targetting on objects within those regions
  **
  *********************************************************************/

  /* Cut A stars */
  inAstarbox = cutAstars(mag, params);
  
  /* Cut white dwarfs */
  inWDbox = cutWDregion(mag, params);
  
  /* Cut WD+M pairs */
  inWMbox = cutWMregion(mag, err, params);
  
  /* If object is in any of the reject boxes, then set QSO_REJECT,
   * this is true even if we don't end up rejecting this object. */
  if ((inAstarbox == 1) || (inWDbox == 1) || (inWMbox == 1)) {
    obj->primTarget |= AR_TARGET_QSO_REJECT;
  }
  
  if (params->CUTBOXES == 1) { 
    /* If object is in any of the reject boxes, do not target */     
    if ((params->CUTASTARS == 1 && inAstarbox == 1) ||\
	(params->CUTWD == 1 && inWDbox == 1) ||\
	(params->CUTWM == 1 && inWMbox == 1)) {
      return 0;
    }
  }

  /* Select mid-z quasars */
  /* This region should be defined so as not to overlap the exclusion
     boxes above */
  if (params->TARGETMIDZ == 1) {
    /* galaxies are not allowed in the mid-z box */
    if (obj->objc_type != 6) 
      targetmidZ = 0;
    else 
      targetmidZ = midZbox(mag, params);
    
    /* We need to refine our definition of midZ objects based on the */
    /* ellipsedist, which comes out of the colorselectqso code.  So */
    /* this next step will happen just after that routine is run; see */
    /* below. */
  }

/* End rejecting color boxes and targeting mid-z quasars */


  /**********************************************************************
   ** Reject objects with non-FATAL errors
   **
   ** If this object has any image problems, do not select it as a color
   ** outlier.  This is AFTER the QSO_FIRST and QSO_REJECT are set.
   **
   *********************************************************************/
  
  if(ImageProb==1) return 0;

/*
*************************************************************************
** Begin COLOR selection
**
*************************************************************************
**/

  lowzlimit[0] = params->LOWZULIMIT;
  lowzlimit[1] = params->LOWZGLIMIT;
  lowzlimit[2] = params->LOWZRLIMIT;
  lowzlimit[3] = params->LOWZILIMIT;
  lowzlimit[4] = params->LOWZZLIMIT;

  /**********************************************************************
   ** Select CAP/SKIRT targets
   **
   *********************************************************************/

  
  /* Consolidated CAP/SKIRT code (GTR, 08/14/00).  Need to check to
   * make sure that the TSFLAG thing works.  At the moment, the
   magnitude limits for the two are the same. */

  if (capobject) {
    MAGLIMIT = params->CAPILIMIT;
    TSFLAG = AR_TARGET_QSO_CAP;
  } else {
    MAGLIMIT = params->SKIRTILIMIT;
    TSFLAG = AR_TARGET_QSO_SKIRT;
  }

  /**********************************************************************
   * Extended objects are selected if they are blue enough in u-g (they
   * may be Seyferts); here we cut out redder objects.  Note that this
   * piece of code makes obselete the code in colorselectqso that checks
   * for galaxies.
   *********************************************************************/

  if (obj->objc_type == 3 && (mag[0] - mag[1]) > params->UGMINGALAXY ) inGalaxylocus = 1;

  /**************************************************************************
   * This is the routine that *actually* determines whether an object is
   * in the stellar locus!!!!!  Perhaps the most important line of the
   * code.  This is for the low-z color cube.
   **************************************************************************/
  
  inlocus = colorselectqso(obj, mag, err, ta, obj->objc_type, lowzlimit,
			   params->stellarlocus, 0, &closestpoint, &ellipsedist);

  if (inlocus<0) return 0;
  

    /* Here, we do a bit more refinement of the definition of the */
    /* mid-z candidates, now that we have the parameter ellipsedist in */
    /* hand. 
       We require that in the midZ box, the candidate has 
       ellipsedist larger than params->MINMIDZDIST.
       
       ellipsedist is defined as the SQUARE of the ratio between
       
       a) the distance from the object to the nearest locus point, and
       b) the width of the selected locus, which is the convolution of
       nsigmaselect * stellar_locus and
       nsigmaerror  * photometric error of the object.
       
       Therefore, ellipsedist = 1 means that the object is 4-sigma away
       from the stellar locus; 
       
       ellipsedist = MINMIDZDIST means that the object
       is 4 * sqrt(MINMIDZDIST) from the stellar locus.
       currently, we set MINMIDZDIST = 0.3; i.e., we are selecting
       objects that are at least 2.2 sigma away from the
       stellar locus in the mid-z region 
       
       XF 000825
    */

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

  /*********************************************************************
  ** Select bright CAP/SKIRT objects.
  **
  ** If i' is brighter than the plate limit in the cap, then set
  ** QSO_CAP/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 object is brighter than i' magnitude limit */

  if (mag[3] < MAGLIMIT) {
    
    /* If object is in mid-z box OR is not in the stellar locus, and
     * not in the galaxy locus then select it. */
    /* inGalaxylocus means extended objects too red to be AGN; it is */
    /* set earlier in the code. */

    if (targetmidZ || (!inlocus && !inGalaxylocus)) {
      obj->primTarget |= TSFLAG;
      priority = 200;
    }

  } else {

    /* If the object is otherwise ok, but is too faint, set QSO_FAINT */
    if (targetmidZ || (!inlocus && !inGalaxylocus)) {
      obj->primTarget |= AR_TARGET_QSO_FAINT;
    }

  }
  
  /**********************************************************************
  ** Select fainter CAP/SKIRT objects if they are z>3 candidates
  **
  ** If the object is an outlier in ugri, and has u-g color indicating
  ** it is in the z>3 region, then we want to go a bit deeper, and set
  ** the magnitude limit to HIZMAGLIMIT.
  ** For these objects, set QSO_HIZ instead of QSO_CAP/QSO_SKIRT.
  **
  ** No need to set QSO_FAINT here, since that is taken care of
  ** above; indeed, most such objects will be flagged as QSO_FAINT in
  ** fact.  We are just trying to make sure that these get 
  ** flagged as QSO_HIZ in addition to being flagged as
  ** low-z candidates so that a fainter magnitude limit is used.
  **  
  ** No galaxy (type == 3) is allowed to be high-z
  **
  ** Careful not to use HIZILIMIT instead of HIZMAGLIMIT, which are
  ** two different things.
  **
  **
  *********************************************************************/
  
  if (mag[3] < params->HIZMAGLIMIT && (mag[0] - mag[1]) > params->UGMINHIGHZ) {

    if (!inlocus && obj->objc_type == 6) {
      obj->primTarget |= AR_TARGET_QSO_HIZ;
      priority = 200;
    }

  }
  
  /**********************************************************************
   ** Now we select from the griz color cube. 
   ** Select High-z Quasar Candidates
   **
   ** Look for hiz objects 
   **
   ** WARNING, the lowzlimit vector is used in this call to test the
   ** high-z locus!!! (GTR 08/28/00)
   ** This is misleading, but the so-called "lowzlimit" vector is
   ** actually applicable to both cubes. 
   **
   *********************************************************************/

  /*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 0;
  
  /*********************************************************************
   ** cut out faint UVX quasars selected as HIZ
   **
   ** A UVX quasar can still be an outlier in the griz cube, but we
   ** don't want to select them to the griz magnitude limit.  So we
   ** explicitly remove these with a color cut. 
   **
   *********************************************************************/
      
  if (det[0] == 1 && det[1] == 1 && mag[0] - mag[1] < params->UGMAXFAINT &&
      det[2] == 1 && mag[1] - mag[2] < params->GRMAXFAINT)
    uvx = 1;

  /*********************************************************************
   ** 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.
   **
   ** Do NOT use HIZILIMIT in the param file for this.
   ** Instead use HIZMAGLIMIT
   **
   *********************************************************************/
    
    if (mag[3] < params->HIZMAGLIMIT) {
      
      /* Set the target flag */
      if (!inlocus && !uvx && 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 && !uvx && obj->objc_type == 6) {
	obj->primTarget |= AR_TARGET_QSO_FAINT;
      }
    }
    
    /* Successful return */
    return priority;
}
