/*
****************************************************************************
******************************************************************************
**<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"
#include "stdlib.h"

/*
****************************************************************************
** PARAMETER DEFINITIONS
**
** All parameter definitions are now in /etc/taQuasarsParams.par
**
****************************************************************************
**/

/*
****************************************************************************
** 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
**
** (OK -- I assume, GTR, 10/09/00)
****************************************************************************
**/

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)
**
** (OK, GTR, 09/22/00)
**************************************************************************** 
**/

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 tested in the main code;
** this routine only determines whether the objects are in or out of
** the box.)
**
** The magnitude of the mid-z candidates is further constrained
** elsewhere in the code as is the distance from the stellar locus.
**
** (OK, GTR, 09/22/00)
**************************************************************************** 
**/

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 &&
      (mag[2] - mag[3]) > params->RIMINMIDZ && (mag[2] - mag[3]) < params->RIMAXMIDZ &&
      (mag[3] - mag[4]) > params->IZMINMIDZ && (mag[3] - mag[4]) < params->IZMAXMIDZ)
    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)
**
** (OK, GTR, 09/22/00)
****************************************************************************
**/

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

  int WDbox = 0;
  if (((mag[0] - mag[1] > params->UGMINWD && mag[0] - mag[1] < params->UGMAXWD) || badamp) &&\
      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).  The cut on
** the g' error is intended to insure that the object in question
** really has these colors and is not in the box simply because of
** large errors.  
**
** (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)
**
** (OK, GTR, 09/22/00)
****************************************************************************
**/

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 &&
       mag[3] - mag[4] > params->IZMINWM && mag[3] - mag[4] < params->IZMAXWM &&
       err[1] < params->GERRMAXWM)
     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.
**
**
** Galaxies occupy a plane, not a line, in the color-color space, and
** the quantity galdist gives the direction along a normal to the
** plane; objects with GALMIN < galdist < GALMAX are considered to lie
** in the plane.  Galaxies are rejected by a combination of galaxyCut
** and other criteria.
**
** (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.)
**
** inGalaxyplane and galdist are returned through the arguments.
**
** NOTE: Currently this subroutine is used only if GALCUTBYPLANE = 1
**************************************************************************** */

int galaxyCut(const TA_CALIB_OBJ *obj, const TA_QUASARS_PARAMS *params,\
	      float *mag, double *galdist, int *inGalaxyplane) {
   double color[3]; 
   int i;
   for(i=0; i<3; i++){
      color[i] = (double)(mag[i] - mag[i+1]);
   }	

   *galdist = (color[0]*params->GALXCOEFF + color[1]*params->GALYCOEFF + color[2]*params->GALZCOEFF);
      
   if( *galdist > params->GALMIN && *galdist < params->GALMAX ) {
     	 *inGalaxyplane = 1;
   }

   return *inGalaxyplane; 
}

/*
****************************************************************************
** Limit Correction
**
** At or near the "plate limit" of the Survey, asinh magntidue (aka
** Luptitudes) deviate from normal log magnitudes.  In addition, for
** zero flux objects the errors in the Luptidudes begin to DECREASE.
** As such, we need some code to "fix" the limiting colors of objects
** that have one or more "faint" luptitudes.
**
** The code that tests whether or not an object is in the stellar
** locus is not able to handle the fact that the errors may not be
** correct for objects that are effectively non-detections in a given
** band.  Therefore, we must correct the colors such that the
** limiting magnitude is that corresponding to
** counts + N_sigma*(counts error)
**
** The derivation of the equation used is as follows:
** u' = -2.5*log(counts) + C
** u'_lim = -2.5*log(counts + N_sigma*error) + C
**        = u' + 2.5*log(counts) - 2.5*log(counts+N_sigma*error)
**
** u'_lim - u' = -2.5*log(1+ N_sigma*error/counts)
**
** delta u' (magnitude error) = 2.5*log (counts+err) - 2.5*log(counts)
** delta u' = 2.5*log(1+err/counts)
**
** error/counts = (10^(delta u'/2.5) -1)
**
** Plugging in error/counts gives the equation used below
**
** Note that the way that this code is written is not ideal.  It was
** done this way because of the difficulty of inverting Luptitudes
** back to counts at the time of writing.  A better way could now be
** developed.
**
****************************************************************************
**/

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 { /* limit of prev equation for large error */
      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.  inlocus, closestpoint and
** ellipsedist are returned through the arguments.
**
** Returns -1 (error), 0 (not in locus), or 1 (in locus) -- but this
** return value should not be used, except as an error code.
**
****************************************************************************
**/

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 *inlocus, int *closestpoint, double *ellipsedist, int *inGalaxyklm, const TA_QUASARS_PARAMS *params) {

    double color[4], var[4], covar[3];
    int zero;
    double err0;
    int colorindex;
    int tmpflag1, tmpflag2;
    int det1, det2, flag2_1, flag2_2;
    double object_k,object_l,object_m;
    int dummyclosestpoint=-1;
    double dummyellipsedist=-1;
    int retval = 0;

    /**********************************************************************
     ** 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
     **
     ** ATQU_UNKNOWN means that the object is not detected in either
     ** band and that the color is allowed to move anywhere in order
     ** to place it in the stellar locus.
     **
     ** ATQU_*_LIMIT are for objects detected in one band and not the
     ** other.  Colors are allowed to move in one direction only in
     ** order to place them on the stellar locus.
     **
     *********************************************************************/

    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 a 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];
	/* assumes that magnitudes have independent errors */
        if (colorindex<3) {
	  covar[colorindex] = -err[colorindex+1]*err[colorindex+1];
	}
      } else {
        if (colorindex<3) covar[colorindex] = 0;
	if (det1 == 0) { /* If bluer magnitude in color is not detected */
	  if(det2 == 0) { /* If redder magnitude in color is not detected */
	    var[colorindex] = ATQU_UNKNOWN;
          } else { /* blue not detected, red detected */
	    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 not detected */
            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));
            }
        }
      }

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

      if ((tmpflag2&(AR_DFLAG_BINNED1)) && (err[colorindex+1] > params->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).
     **
     ** (OK, GTR, 09/22/00)
     *********************************************************************/

    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,\
			      &dummyclosestpoint,&dummyellipsedist);

    *closestpoint = dummyclosestpoint;
    *ellipsedist = dummyellipsedist;

    /* A return value of -1 means none of the colors are known */ 

    /**********************************************************************
     ** 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.  This klm cut is
     ** combined with an additional cut in the main body of the code.
     **
     ** 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.  This is necessary because
     ** the colors of stars are not intrinsically redder than about
     ** g-r = 1.6, but the colors of quasars and of stars with large
     ** errors are.  Essentially, we must correct for the fact that
     ** the stellar locus is extrapolated beyond this point since
     ** there are few (if any) redder than this.
     **
     *********************************************************************/

    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), k >
       * galaxyklimit, and source is not a point source, don't target
       * it (see comments above) This rejection is actually coupled
       * with other galaxy rejection criteria in the main part of the
       * code.  This just tells you whether or not the object meets
       * these criteria.*/

      if (type==3  && (object_l>0 && object_k>locusarray.galaxyklimit)) {
	*inGalaxyklm = 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.*/

      /**** Note the change here. (GTR,10/30/00,11/02/00) ****/

      if(object_l>=0 && object_k>locusarray.galaxyklimit && *inlocus==0) {
	
	/* if(type==6 && object_l>=0 && object_k>locusarray.galaxyklimit && *inlocus==0) {} */
	
	/* if(type==6 && object_l>=0 && color[1] > 0.9 && *inlocus==0) {} */
	
	/* 
	   if((type==6) && *inlocus==0 &&\
	   color[1] > 0.9 && color[1] < 1.6 &&\
	   color[2] > 0.6 && color[2] < 2.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, params->BIGNSIGERRORS,\
				  &dummyclosestpoint, &dummyellipsedist);
		
	*closestpoint = dummyclosestpoint;
	*ellipsedist = dummyellipsedist;

	/* Try just rejecting all such objects.  These really
         * shouldn't be quassars anyway! */

	*inlocus = 1;

      } /* End checking point sources in galaxy side of locus */
      
    } /* End eliminating galaxies in ugri */
    
    if (*inlocus > 0) {
      return 1;
    } else {
      return 0;
    }
}

/*****************************************************************************
******************************************************************************
**<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_MAG_OUTLIER
**	<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 targeted.  We currently give the same priority (200) to all 
**	QSO targets.
**
**      FYI, the area of a good frame is given by
**      area = at_scanSeparation*1361.0*0.396/(3600.0) 
**           = 3.140533x10^-2 sq. deg
**
**      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;
  float MIDZBRIGHTLIMIT;
  int ta[5], det[5];
  int priority = 0;
  int inlowzlocus = 0;
  int inhighzlocus = 0;
  int inmidzlocus = 0;
  int inGalaxyplane = 0;
  int ImageProb = 0;
  int badCol = 0;
  int uvx = 0;
  int closestpoint = 0;
  double ellipsedist = 0.0;
  double galdist = 0.0;
  int inGalaxyklm = 0;
  int inGalaxyklmJunk = 0;
  int inGalaxylocus = 0;
  double velocity = 0.0;
  double velocityError = 0.0;
  int targetmidZ = 0; 
  int inAstarbox = 0;
  int inWDbox = 0;
  int inWMbox = 0;	
  int i, nchild;
  int intercenter, intercenterI;
  int retval = 0;
  int badamp = 0;

  int rejReason; /* reason for rejecting a quasar
		    1 = BRIGHT
		    2 = EDGE
		    3 = SATUR
		    4 = BLENDED
		    5 = bad objc_type
		    6 = moving
		    7 = too bright
		    8 = deblend problem
		    9 = ERRINTERPCENTER
		    10 = INTERP_CENTER i' + bright
		    11 = bad column
		    12 = A-star
		    13 = WD
		    14 = WD+M
		    15 = galaxy klm
		    16 = stellar locus ugri
		    17 = stellar locus griz
		    18 = high-z uvx
		    19 = large errors in all bands
		    20 = mid-z random
		    21 = galaxy locus
		    22 = mid-z locus
		    23 = not OK_SCANLINE
		    24 = LOCAL_EDGE (e.g. bad u' amplifier)
                  */

  int accReason; /* reason for accepting quasar
		    -1 = FIRST
		    -2 = mid-z
		    -3 = low-z
		    -4 = low-z mag outlier
		    -5 = high-z ugri
		    -6 = high-z
		    -7 = high-z mag outlier
		    -8 = mid-z bright
		    -9 = FIRST mag outlier
		    -10 = High-z galaxy
		  */

  static int icall=0;	     /* changed after first call */
  static int diagReject;     /* write diagnostics for all rejected objects */
  static int diagAccept;     /* write diagnostics for all accepted objects */
  static FILE *fp,*fp2;      /* diagnostic file pointer */
  int run;
  int rerun;
  int camcol;
  char rejstr[80];
  char accstr[80];
  char temp[6];

  sprintf(rejstr,"quasarsReject");
  sprintf(accstr,"quasarsAccept");
  run = sprintf(temp,"%d",obj->run);
  strcat(rejstr,temp);
  strcat(accstr,temp);
  rerun = sprintf(temp,"%d",obj->rerun);
  strcat(rejstr,"_");
  strcat(accstr,"_");
  strcat(rejstr,temp);
  strcat(accstr,temp);
  camcol = sprintf(temp,"%d",obj->camCol);
  strcat(rejstr,".out");
  strcat(accstr,".out");

  /* on 1st call, open files for writing accept and reject diagnostics */

  if (icall==0) {
    diagReject=params->DIAGREJECT;
    diagAccept=params->DIAGACCEPT;
    if (diagReject)  fp=fopen(rejstr,"w") ;
    if (diagAccept)  fp2=fopen(accstr,"w") ;
  }
  icall++;

  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.
   **
   ** In fact, these may never get used, since "primary" is defined so
   ** that these objects are not given the chance to be targeted.
   **
   ** We now reject on status != OK_SCANLINE.  This is in order to
   ** deal with the area problem.
   **
   ** (OK ???, GTR, 09/22/00)
   *********************************************************************/

  if (obj->objc_flags&AR_DFLAG_BRIGHT) {

    /* Output diagnostic information */
    rejReason=1;
    if (diagReject) {
      taQuasarsLog(fp, obj, 0, 0, 0, rejReason);
    }
    return 0;
  }

  if (obj->objc_flags&AR_DFLAG_EDGE) {
    rejReason=2;
    if (diagReject) {
      taQuasarsLog(fp, obj, 0, 0, 0, rejReason);
    }
    return 0;
  }

  if (obj->objc_flags&AR_DFLAG_SATUR) {
    rejReason=3;
    if (diagReject) {
      taQuasarsLog(fp, obj, 0, 0, 0, rejReason);
    }
    return 0;
  }

  if (!(obj->status&AR_OBJECT_STATUS_OK_SCANLINE)) {
    rejReason=23;
    if (diagReject) {
      taQuasarsLog(fp, obj, 0, 0, 0, rejReason);
    }
    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.
   **
   ** (OK, GTR, 09/22/00)
   *********************************************************************/

  if ((obj->objc_flags & AR_DFLAG_BLENDED)) {
    rejReason=4;
    if (diagReject) {
      taQuasarsLog(fp, obj, 0, 0, 0, rejReason);
    }
    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.
   **
   ** (OK, GTR, 09/22/00)
   *********************************************************************/

  if ((obj->objc_type != 3) && (obj->objc_type != 6)) {
    rejReason=5;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) obj->objc_type, 0, 0, rejReason);
    }
    return 0;
  }

  /**********************************************************************
   ** Disregard moving objects
   **
   ** Objects which are 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.
   **
   ** (Needs more testing, GTR, 10/30/00)
   *********************************************************************/

  velocity = (sqrt(obj->rowv.val*obj->rowv.val + obj->colv.val*obj->colv.val));
  velocityError = (sqrt(obj->rowv.err*obj->rowv.err + obj->colv.err*obj->colv.err));
  
  if ((velocity > params->MAXV) && ((velocity/velocityError) > params->MAXVSIG)) {
    rejReason=6;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) velocity, (double) velocityError, 0, rejReason);
    }

    /**** Note: Look to see how many such objects there are, but don't
     **** reject them for now. ****/

    /* 
    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.
   **
   ** (OK, GTR, 09/22/00)
   *********************************************************************/

  if (obj->detection[ta[3]].psfCounts.val < params->IBRIGHT) {
    rejReason=7;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) obj->detection[ta[3]].psfCounts.val, 0, 0, rejReason);
    }

    /* We no longer return 0 here, since we reject on IBRIGHT elsewhere.
     * This way we can have a record of such objects, but we will not
     * put fibers on them. */

    /* return 0; */
  }

  /**********************************************************************
   ** Delete undetected objects
   **
   ** We don't want to throw away good high-z quasar candidates, even
   ** if they don't meet the official selection criteria.  We also
   ** don't want to keep a zillion QSO_REJECT and QSO_MAG_OUTLIER
   ** objects, so we have to restrict things somehow.  So, we require
   ** that at least one band be a 5 sigma detection (error less than
   ** 0.2).
   **
   ** NOTE: What about faint objects with small errors?
   **
   *********************************************************************/

  if (obj->detection[ta[0]].psfCounts.err > 0.2 &&\
      obj->detection[ta[1]].psfCounts.err > 0.2 &&\
      obj->detection[ta[2]].psfCounts.err > 0.2 &&\
      obj->detection[ta[3]].psfCounts.err > 0.2 &&\
      obj->detection[ta[4]].psfCounts.err > 0.2) {

    rejReason=19;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) obj->detection[ta[0]].psfCounts.err,\
		   (double) obj->detection[ta[1]].psfCounts.err,\
		   (double) obj->detection[ta[2]].psfCounts.err, rejReason);
    }
    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, but this probably does not
   ** hurt.
   **
   *********************************************************************/
  
  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]<params->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] > params->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;
      
      rejReason=9;
      if (diagReject) {
	taQuasarsLog(fp, obj, (double) colorindex, (double) err[colorindex], 0, rejReason);
      }
    }
    
    /* Reject objects on the edge of the bad u3 amp.  Half of the u3
    ** chip doesn't work.  Objects that fall completely within this
    ** region simply won't be detected.  This area of sky is being
    ** kept track of elsewhere.  However, some objects will fall on
    ** the edge.  As such their u' flux is suspect.  The following
    ** modifies the u' errors.  Note that we do NOT set ImageProb,
    ** since we want these object to be detected as radio sources and
    ** as griz sources.  Also, since it is conceivable that this could
    ** happen to another CCD in the future, we have generalized this
    ** rejection to ALL dewars and ALL filters.*/
    
    /* NOTE: Make additional error a parameter */
    
    if ( ( ( !((obj->detection[ta[colorindex]].flags & AR_DFLAG_BINNED1) ||\
	       (obj->detection[ta[colorindex]].flags & AR_DFLAG_BINNED2) ||\
	       (obj->detection[ta[colorindex]].flags & AR_DFLAG_BINNED4)) ||\
	     ((obj->detection[ta[colorindex]].flags & AR_DFLAG_BINNED1) &&\
	      (obj->detection[ta[colorindex]].flags2 & AR_DFLAG2_DEBLEND_NOPEAK)) ) &&\
	   (obj->detection[ta[colorindex]].flags2 & AR_DFLAG2_NOTCHECKED_CENTER) ) ||
	 (obj->objc_flags2 & AR_DFLAG2_LOCAL_EDGE) ) {
      
      /* Large errors will cause the objects to be pushed back onto the stellar locus */
      err[colorindex] = sqrt(err[colorindex]*err[colorindex] + 10.0*10.0);

      /* The above is not enough for g-r<0 objects. */
      badamp = 1;

      rejReason=24;
      if (diagReject) {
	taQuasarsLog(fp, obj, (double) colorindex, obj->objc_colc.val, mag[colorindex], rejReason);
      }
    }
    
  }
  
  if (ImageProb) {
    rejReason=8;
    if (diagReject) {
      taQuasarsLog(fp, obj, 0, 0, 0, rejReason);
    }
  }

  /**********************************************************************
   ** 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 obsolete, and now fixed in PHOTO; check!
   **
   *********************************************************************/

  if (obj->detection[ta[3]].psfCounts.val < params->IBRIGHTINTERP && intercenterI == 1) {
    ImageProb=1;
    rejReason=10;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) obj->detection[ta[3]].psfCounts.val, 0, 0, rejReason);
    }
  }

  /**********************************************************************
   ** 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) ||\
     (obj->camCol==2 && obj->objc_colc.val<1388 && obj->objc_colc.val>1382) ||\
     (obj->camCol==5 && obj->objc_colc.val<1032 && obj->objc_colc.val>1018)) {
    ImageProb=1;
    rejReason=11;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) obj->camCol, 0, 0, rejReason);
    }
  }
  

/*
*************************************************************************
** End non-FATAL Error checking, begin FIRST selection
**
** Objects with ImageProb = 1 are rejected unless they 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
   **
   ** (OK, GTR, 09/22/00)
   *********************************************************************/
   
  capobject = determineCapSkirt(obj->l,obj->b,params->CAPSTARS);

  /**********************************************************************
   ** FIRST Match
   **
   ** If the source is matched to a FIRST source, then firstMatch is
   ** 1.  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).
   **
   ** How does this work?  Here is an outline of what happens.
   ** 1. targetSelectFromChunk in etc/targetSelect.tcl 
   **    reads examples/tsMatchParams.par
   ** 2. targetSelectFromChunk calls firstRead in src/tclCatalogs.c
   ** 3. the FIRST catalog with the matching radii are passed to
   **    taTargetSelectFromObj in src/target.c
   ** 4. taTargetSelectFromObj calls taObjMatchAgainstFirst 
   **    in src/catalogs.c
   **
   ** Note that as of 10/26/00, there are a number of obsolete things
   ** in the FIRST matching process.  In reality only the
   ** "firstRadius" parameter in examples/tsMatchParams.par matters
   ** for anything and there is some obsolete code.
   **
   *********************************************************************/

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

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

      /* If object is not too bright or too faint */
      if (mag[3] < MAGLIMIT &&\
	  obj->detection[ta[3]].psfCounts.val >= params->IBRIGHT) {
        obj->primTarget |= TSFLAG;
        if (obj->firstMatch > 1) { /* Never true, as the code stands */
	  priority = 100;
	} else {
	  priority = 200;
	}
	accReason=-1;
	if (diagAccept) {
	  taQuasarsLog(fp2, obj, (double) mag[3], 0, 0, accReason);
	}
      } else {
	obj->primTarget |= AR_TARGET_QSO_MAG_OUTLIER;
	accReason=-9;
	if (diagAccept) {
	  taQuasarsLog(fp2, obj, (double) mag[3], 0, 0, accReason);
	}
      }
    }
  }

/*
*************************************************************************
** 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
  **
  ** (OK, GTR, 09/22/00)
  *********************************************************************/

  /* Cut A stars */
  inAstarbox = cutAstars(mag, params);
  if (inAstarbox) {
    rejReason=12;
    if (diagReject) {
      taQuasarsLog(fp,obj,(double) (mag[0]-mag[1]), (double) (mag[1]-mag[2]),\
		   (double) (mag[2]-mag[3]),rejReason);
    }  
  }

  /* Cut white dwarfs */
  inWDbox = cutWDregion(mag, badamp, params);
  if (inWDbox) {
    rejReason=13;
    if (diagReject) {
      taQuasarsLog(fp,obj,(double) (mag[0]-mag[1]), (double) (mag[1]-mag[2]),\
		   (double) (mag[2]-mag[3]),rejReason);
    }  
  }

  /* Cut WD+M pairs */
  inWMbox = cutWMregion(mag, err, params);
  if (inWMbox) {
    rejReason=14;
    if (diagReject) {
      taQuasarsLog(fp,obj,(double) (mag[1]-mag[2]), (double) (mag[2]-mag[3]),\
		   (double) err[2], rejReason);
    }  
  }

  /* 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) {
    if (obj->objc_type != 6) { /* galaxies are not allowed in the mid-z box */
      targetmidZ = 0;
    } else {
      targetmidZ = midZbox(mag, params);

      /***************************************     
       * Sparse sample mid-z quasar candidates
       * 
       * Most (97%) of mid-z targets are not quasars, so we sparse
       * sample this region in order to keep our overall efficiency
       * high.  The sparse sampling is accomplished by truncating the
       * RA to one decimal place and looking at the tenths digit in
       * the RA value.  If this value is equal to 7 (for good luck)
       * then keep the object.  Otherwise reject it.  This keeps 10%
       * of all mid-z targets.  If TARGETMIDZSOUTH == 1, then keep
       * everything.
       *
       ***************************************/
       
      if (params->TARGETMIDZSOUTH != 1 && targetmidZ == 1) {
	if ((int)(10.0*(obj->ra-(int)obj->ra)) != 7) {
	  /* if ((int)(10.0*(obj->ra-(int)obj->ra)) != 7 &&\
	     (int)(10.0*(obj->ra-(int)obj->ra)) != 3) {}
	  */
	  targetmidZ = 0;
	  rejReason=20;
	  if (diagReject) {
	    taQuasarsLog(fp, obj, (double) obj->ra, 0, 0, rejReason);
	  }
	}
      }
    }
    
    /* 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 testing mid-z colors */


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

  /**************************************************************************
   * 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.
   **************************************************************************/
  
  retval = colorselectqso(obj, mag, err, ta, obj->objc_type, lowzlimit,\
			   params->lowzlocus, 0, &inlowzlocus, &closestpoint,\
			   &ellipsedist, &inGalaxyklm, params);

  if (inlowzlocus != 0) {
    rejReason=16;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) inlowzlocus,\
		   (double) closestpoint, (double) ellipsedist, rejReason);
      taQuasarsLog(fp, obj, (double) err[0],\
		   (double) err[1], (double) err[2], rejReason);
      taQuasarsLog(fp, obj, (double) err[3],\
		   (double) err[4], (double) mag[3], rejReason);
    }  
  }

  /**********************************************************************
   * If an object is extended and is on the "galaxy" side of the
   * stellar locus, then we want to reject it.  The test to see if the
   * object is extended is done in colorselectqso, so there is no need
   * to require objc_type==3 here.  If inGalaxyklm = 1, then the
   * object should be rejected.
   **********************************************************************/

  if (inGalaxyklm != 0) {
    rejReason=15;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) inGalaxyklm, 0, 0, rejReason);
    }  
  }

  /**********************************************************************
   * Extended objects should be selected if they are blue enough in
   * u-g (they may be Seyferts); here we cut out redder objects.  For
   * red objects, we ask if they are in the galaxy "plane".  If so,
   * inGalaxyplane = 1, and the object should be rejected.
   *********************************************************************/

  if (obj->objc_type == 3 && (mag[0] - mag[1]) > params->UGMINGALAXY) {
    retval = galaxyCut(obj, params, mag, &galdist, &inGalaxyplane);
  }

  if (inGalaxyplane != 0) {
    rejReason=21;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) inGalaxyplane, (double) galdist,\
		   (double) (mag[0] - mag[1]), rejReason);
    }  
  }

  /**********************************************************************
   * Combine inGalaxyklm and inGalaxyplane to reject galaxies.  Or use
   * inGalaxyklm and a color cut depending on the GALCUTBYPLANE
   * parameter.  These are combined to create inGalaxylocus.
   *********************************************************************/

  if (params->GALCUTBYPLANE == 1) {
    if (inGalaxyplane || inGalaxyklm) {
      inGalaxylocus = 1; 
    }
  } else {
    if ((obj->objc_type == 3 && (mag[0] - mag[1]) > params->UGMINGALAXY) ||\
	inGalaxyklm) {
      inGalaxylocus = 1;
    }
  }

  if (obj->objc_type == 3 && (mag[0] - mag[1]) > params->UGMINGALAXY &&\
      inGalaxylocus != 1) {
    accReason=-10;
    if (diagAccept) {
      taQuasarsLog(fp, obj, (double) params->GALCUTBYPLANE,\
		   (double) (mag[0] - mag[1]), (double) galdist, rejReason);
    }  
  }
    
  /********************************************************************** 
   * Do a bit more refinement of the definition of the mid-z
   * candidates.
   *
   * If we are going to target this object as a mid-z candidate, we
   * require that it is not in the mid-z locus, which is the same as
   * the low-z locus, but uses only a 2 sigma width and has a
   * maxklimit of half that of the low-z locus.
   *********************************************************************/

  if (targetmidZ == 1) {
    
    retval = colorselectqso(obj, mag, err, ta, obj->objc_type, lowzlimit,\
			    params->midzlocus, 0, &inmidzlocus, &closestpoint,\
			    &ellipsedist, &inGalaxyklmJunk, params);
    
    if (inmidzlocus) {targetmidZ=0;}
    
    if (inmidzlocus) {
      rejReason=22;
      if (diagReject) {
	taQuasarsLog(fp, obj, (double) inmidzlocus, (double) closestpoint, (double) ellipsedist, rejReason);
      }  
    }
  }

  /*********************************************************************
  ** 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_MAG_OUTLIER, which sets a target flag, but does not target the
  ** object.  
  **
  *********************************************************************/
  
  /* If object is brighter than i' magnitude limit and fainter than the
   * bright i' limit of the spectrographs. */

  if (mag[3] < MAGLIMIT &&\
      obj->detection[ta[3]].psfCounts.val >= params->IBRIGHT) {
    
    /* If object is in mid-z box OR is not in the stellar locus, and
     * not in either of the galaxy regions (inGalaxyplane +
     * inGalaxyklm = inGalaxylocus -- all defined earlier), then
     * select it. */

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

      /* dianostics for color-selected candidates */
      if (!inlowzlocus && !inGalaxylocus) {
	accReason=-3;
	if (diagAccept) {
	  taQuasarsLog(fp2, obj, (double) mag[3], 0, 0, accReason);
	}
      }

      /* diagnostics for mid-z candidates */
      if (targetmidZ) {
	accReason=-2;
	if (diagAccept) {
	  taQuasarsLog(fp2, obj, (double) mag[3], 0, 0, accReason);
	}
      }
    }
    
  } else { 
    
    /* If the object is otherwise ok, but is too faint or too bright,
     * set QSO_MAG_OUTLIER */

    if (targetmidZ || (!inlowzlocus && !inGalaxylocus)) {
      obj->primTarget |= AR_TARGET_QSO_MAG_OUTLIER;
      accReason=-4;
      if (diagAccept) {
	taQuasarsLog(fp2, obj, (double) mag[3], 0, 0, accReason);
      }
    }

  }
  
  /**********************************************************************
  ** 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_MAG_OUTLIER here, since that is taken care of
  ** above; indeed, most such objects will be flagged as
  ** QSO_MAG_OUTLIER 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
  **
  ** (OK, GTR, 09/22/00)
  *********************************************************************/
  
  if (mag[3] < params->HIZMAGLIMIT &&\
      obj->detection[ta[3]].psfCounts.val >= params->IBRIGHT &&\
      (mag[0]-mag[1]) > params->UGMINHIGHZ) {

    if (!inlowzlocus && obj->objc_type == 6 && !badamp) {
      obj->primTarget |= AR_TARGET_QSO_HIZ;
      priority = 200;
      accReason=-5;
      if (diagAccept) {
	taQuasarsLog(fp2, obj, (double) mag[3], (double) (mag[0]-mag[1]),\
		     0, accReason);
      }
    }

  }
  
  /**********************************************************************
   ** Select High-z Quasar Candidates from the griz color cube.
   *********************************************************************/
  
  retval = colorselectqso(obj, mag, err, ta, obj->objc_type, lowzlimit,\
			  params->hizlocus, 1, &inhighzlocus, &closestpoint,\
			  &ellipsedist, &inGalaxyklm, params);

  if (inhighzlocus != 0) {
    rejReason=17;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) inhighzlocus, (double) closestpoint,\
		   (double) ellipsedist, rejReason);
      taQuasarsLog(fp, obj, (double) err[0],\
		   (double) err[1], (double) err[2], rejReason);
      taQuasarsLog(fp, obj, (double) err[3],\
		   (double) err[4], (double) mag[3], rejReason);
    }  
  }
  
  /*********************************************************************
   ** 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 the faint ones with a color cut. 
   **
   ** The constraint that the object be brighter than MAGLIMIT means
   ** that UVX objects from the griz color cube are only selected to
   ** the low-z magnitude limit.
   **
   ** (NOTE: This needs work.  GTR, 10/05/00)
   ** (NOTE: Changed on 10/17/00 and should now be ok.  GTR)
   *********************************************************************/

  if (det[0] == 1 && det[1] == 1 &&\
      (mag[0]-mag[1]) < params->UGMAXFAINT &&\
      mag[3] >= MAGLIMIT && err[0] < 0.2 && err[1] < 0.2) { 
    uvx = 1;
    rejReason=18;
    if (diagReject) {
      taQuasarsLog(fp, obj, (double) (mag[0]-mag[1]),\
		   (double) err[0], (double) err[1], rejReason);
    }
  }

  /*********************************************************************
   ** Select bright HIZ objects.
   **
   ** If i' is brighter than the hiz plate limit and fainter than the
   ** bright limit of the spectrographs, then set QSO_HIZ.  If it is
   ** not brighter than the plate limit, but it is still a good quasar
   ** candidate, then set QSO_MAG_OUTLIER, 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: Change this to use galaxy cut!
   **
   *********************************************************************/
    
    if (mag[3] < params->HIZMAGLIMIT &&\
	obj->detection[ta[3]].psfCounts.val >= params->IBRIGHT) {
      
      /* Set the target flag */
      if (!inhighzlocus && !uvx && obj->objc_type == 6) {
	obj->primTarget |= AR_TARGET_QSO_HIZ;
	priority = 200;
	accReason=-6;
	if (diagAccept) {
	  taQuasarsLog(fp2, obj, (double) mag[3], 0, 0, accReason);
	}
      }

    } else { 
      /* Set a target flag, but do not target the object */
      if (!inhighzlocus && !uvx && obj->objc_type == 6) {
	obj->primTarget |= AR_TARGET_QSO_MAG_OUTLIER;
	accReason=-7;
	if (diagAccept) {
	  taQuasarsLog(fp2, obj, (double) mag[3], 0, 0, accReason);
	}
      }
    }

    /* Successful return */
    return priority;
}

  
/*********************************************************************
 ** Diagnostic Output
 **
 ** Output some diagnostic information about those objects that are
 ** accepted and rejected.
 **
 ** (OK, GTR, 10/05/00)
 *********************************************************************/

void taQuasarsLog(
		  FILE *fp,
		  TA_CALIB_OBJ *obj, 
		  double val1,
		  double val2,
		  double val3,
		  int reason
		  )
{
  fprintf(fp,"%d %d %d %d %d %d %.3f %.3f %.3f %.3f %.3f %.7f %.7f %.2f %.2f %.2f\n",
	  reason,
	  obj->run,obj->rerun,obj->camCol,obj->field->field,obj->id, 
	  obj->objc_rowc.val,obj->objc_colc.val,
	  obj->detection[TA_I].psfCounts.val,obj->detection[TA_I].psfCounts.err,
	  obj->reddening[TA_I],
	  obj->ra,obj->dec,val1,val2,val3);
}








