/*****************************************************************************
******************************************************************************
**<AUTO>
**
** FILE:	serendipity.c
**
**<HTML>
**	This file contains C functions to select spectroscopic targets for
**	the Serendipity Working Group.
**</HTML>
**</AUTO>
**
**
** ENVIRONMENT:
**	ANSI C.
**
******************************************************************************
******************************************************************************
*/
#include <dervish.h>
#include "taCalibObj.h"
#include "taSerendipity.h"
#include "arTargetType.h"


/***************************************************************************
 * below are the private functions which pass/fail an object in each of
 * the Serendipity categories.  Their definitions appear after tha
 * main routine, below.
 */
static int is_serendip_blue_ug(TA_CALIB_OBJ *obj,
                            const TA_SERENDIPITY_PARAMS *params);
static int is_serendip_blue_gr(TA_CALIB_OBJ *obj,
                            const TA_SERENDIPITY_PARAMS *params);
static int is_serendip_red_ri (TA_CALIB_OBJ *obj,
                            const TA_SERENDIPITY_PARAMS *params);
static int is_serendip_red_iz (TA_CALIB_OBJ *obj,
                            const TA_SERENDIPITY_PARAMS *params);
static int is_serendip_distant_gr (TA_CALIB_OBJ *obj,
                            const TA_SERENDIPITY_PARAMS *params);
static int is_serendip_distant_ri (TA_CALIB_OBJ *obj,
                            const TA_SERENDIPITY_PARAMS *params);
static int is_serendip_first (TA_CALIB_OBJ *obj,
                            const TA_SERENDIPITY_PARAMS *params);


/*****************************************************************************
******************************************************************************
**<AUTO EXTRACT>
**
** ROUTINE:	taSerendipityTargetSelect
**
**<HTML>
**	Set the appropriate bit in the primary target bit mask for each
**	Serendipity Working Group target selection criterion this object
**	satisfies.  This routine is responsible for setting the following
**	bits:
**	<menu>
**	<li> AR_TARGET_SERENDIP_BLUE
**	<li> AR_TARGET_SERENDIP_FIRST
**	<li> AR_TARGET_SERENDIP_RED
**	<li> AR_TARGET_SERENDIP_DISTANT
**	<li> AR_TARGET_SERENDIP_EXTRA
**	</menu>
**	It is assumed that the above bits are unset on entry, although other
**	bits may be set.
**</HTML>
**
** RETURNS:
**	-1	error
**	0	not a target by any criteria
**	1-255	is a target by one or more criteria --- priority returned
**		(higher number = higher priority)
**
**</AUTO>
******************************************************************************
******************************************************************************
*/
int
taSerendipityTargetSelect(TA_CALIB_OBJ *obj,
			  /* MOD: calibrated object to test */
			  const TA_SERENDIPITY_PARAMS *params
			  /* IN: tunable parameters*/)
{
  int priority = 0;     /* targeting priority (initialize for a non-target) */

        /* check to see if the object satisfies the BLUE u-g criteria */
        if (is_serendip_blue_ug(obj, params)) {
	        obj->primTarget |= AR_TARGET_SERENDIP_BLUE;
	        priority = priority + params->priorBLUEug;  
        }

        /* check to see if the object satisfies the BLUE g-r criteria */
        if (is_serendip_blue_gr(obj, params)) {
	        obj->primTarget |= AR_TARGET_SERENDIP_BLUE;
	        priority = priority + params->priorBLUEgr;  
        }

        /* check to see if the object satisfies the RED r-i criteria */
        if (is_serendip_red_ri(obj, params)) {
	        obj->primTarget |= AR_TARGET_SERENDIP_RED;
	        priority = priority + params->priorREDri;  
        }

        /* check to see if the object satisfies the RED i-z criteria */
        if (is_serendip_red_iz(obj, params)) {
	        obj->primTarget |= AR_TARGET_SERENDIP_RED;
	        priority = priority + params->priorREDiz;  
        }

        /* check to see if the object satisfies the DISTANT g-r criteria */
        if (is_serendip_distant_gr(obj, params)) {
	        obj->primTarget |= AR_TARGET_SERENDIP_DISTANT;
	        priority = priority + params->priorDISTgr;  
        }

        /* check to see if the object satisfies the DISTANT r-i criteria */
        if (is_serendip_distant_ri(obj, params)) {
	        obj->primTarget |= AR_TARGET_SERENDIP_DISTANT;
	        priority = priority + params->priorDISTri;  
        }

        /* check to see if the object satisfies the FIRST criteria */
        if (is_serendip_first(obj, params)) {
	        obj->primTarget |= AR_TARGET_SERENDIP_FIRST;
	        priority = priority + params->priorFIRST;  
	        /* if morphologically stellar, bump up priority */
	        if (obj->objc_type == AR_OBJECT_TYPE_STAR) {
		priority++;
	        }
        } 
	
  /* Successful return */
  return priority;
}


/************************************************************************
 * the routines which follow are "private": they are not visible to
 *   functions outside this source code file.  Each is called from the
 *   the function "taSerendipityTargetSelect" above, and decides whether
 *   the given "TA_CALIB_OBJ" structure contains data which satisfies
 *   one of the Serendipity sub-categories' criteria.
 */



/* BLUE in u-g
 * return 1 if the given object satisfies the u-g "blue" criteria 
 *                  basically, it's stellar and blue in u-g
 * return 0 otherwise
 */

static int
is_serendip_blue_ug(
	TA_CALIB_OBJ *obj,
	const TA_SERENDIPITY_PARAMS *params
	)
{
	float umag, uerr;
	float gmag, gerr;
	float rmag, rerr;
        float gfib, rfib, ifib;
	float u_minus_g, g_minus_r;
	float umgerr;
	float umghi;
        int uflags, gflags, rflags;

	shAssert(obj != NULL);
	shAssert(obj->detection != NULL);
	shAssert(params != NULL);

	umag = obj->detection[TA_U].psfCounts.val;
	uerr = obj->detection[TA_U].psfCounts.err;
	gmag = obj->detection[TA_G].psfCounts.val;
	gerr = obj->detection[TA_G].psfCounts.err;
	rmag = obj->detection[TA_R].psfCounts.val;
	rerr = obj->detection[TA_R].psfCounts.err;
        uflags = obj->detection[TA_U].flags;
        gflags = obj->detection[TA_G].flags;
	gfib = obj->detection[TA_G].fiberCounts.val;
	rfib = obj->detection[TA_R].fiberCounts.val;
	ifib = obj->detection[TA_I].fiberCounts.val;
	u_minus_g = umag - gmag;
	g_minus_r = gmag - rmag;
        umgerr = sqrt (uerr*uerr + gerr*gerr);
        umghi = u_minus_g + (params->sigmaBLUEug * umgerr);

	/* must not too bright for spectra */
	if ((gfib < params->gMinSpec) || (rfib < params->rMinSpec)
           || (ifib < params->iMinSpec) ) {
		return(0);
	}

        /* check to see if object flags warrant consideration */
        if (uflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

        if (gflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

	/* must be stellar; TODO consider if star_l more flexible */
	if (obj->objc_type != AR_OBJECT_TYPE_STAR) {
		return(0);
	}

	/* must be within a range of u-band mag */
	if ((umag < params->uMinBLUEug) || (umag > params->uMaxBLUEug)) {
		return(0);
	}
		
	/* must be within a range of g-band mag */
	if ((gmag < params->gMinBLUEug) || (gmag > params->gMaxBLUEug)) {
		return(0);
	}
		
	/* must have small errors in u and g mags */
	if ((uerr > params->uerrMaxBLUEug) || (gerr > params->gerrMaxBLUEug)) {
		return(0);
	}
		
	/* must be bluer than a certain u-g color;
         *     range allows for eliminating some junk 
         */
	if ((umghi > params->ugMaxBLUE) || (u_minus_g  < params->ugMinBLUE)) {
		return(0);
	}
		
	/* must be bluer than a certain g-r color, for red-leak only
         * currently turned off
         */ 
/*	if (g_minus_r > params->grMax) {
 *		return(0);
 *	}
 */

	/* must be bright enough in g OR very bright in u for spectra */
	if ((umag > params->uSpecBLUEug) && (gmag > params->gSpecBLUEug)) {
		return(0);
	}

	/* 
	 * if the object survives to this point, it satisfies all
	 * criteria for a BLUE in u-g object.  Return 1 to signal success! 
	 */
	return(1);
}




/* BLUE in g-r
 * return 1 if the given object satisfies the g-r "blue" criteria 
 *                  basically, it's stellar and blue in g-r
 * return 0 otherwise
 */

static int
is_serendip_blue_gr(
	TA_CALIB_OBJ *obj,
	const TA_SERENDIPITY_PARAMS *params
	)
{
	float gmag, gerr;
	float rmag, rerr;
        float gfib, rfib, ifib;
	float g_minus_r, gmrerr;
	float gmrhi;
        int gflags, rflags;

	shAssert(obj != NULL);
	shAssert(obj->detection != NULL);
	shAssert(params != NULL);

	gmag = obj->detection[TA_G].psfCounts.val;
	gerr = obj->detection[TA_G].psfCounts.err;
	rmag = obj->detection[TA_R].psfCounts.val;
	rerr = obj->detection[TA_R].psfCounts.err;
	gfib = obj->detection[TA_G].fiberCounts.val;
	rfib = obj->detection[TA_R].fiberCounts.val;
	ifib = obj->detection[TA_I].fiberCounts.val;
	g_minus_r = gmag - rmag;
        gmrerr = sqrt (gerr*gerr + rerr*rerr);
        gmrhi = g_minus_r + (params->sigmaBLUEgr * gmrerr);
        gflags = obj->detection[TA_G].flags;
        rflags = obj->detection[TA_R].flags;

	/* must not too bright for spectra */
	if ((gfib < params->gMinSpec) || (rfib < params->rMinSpec)
           || (ifib < params->iMinSpec) ) {
		return(0);
	}

        /* check to see if object flags warrant consideration */
        if (gflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

        if (rflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

	/* must be stellar; TODO consider if star_l more flexible */
	if (obj->objc_type != AR_OBJECT_TYPE_STAR) {
		return(0);
	}

	/* must be within a range of g-band mag */
	if ((gmag < params->gMinBLUEgr) || (gmag > params->gMaxBLUEgr)) {
		return(0);
	}
		
	/* must be within a range of r-band mag */
	if ((rmag < params->rMinBLUEgr) || (rmag > params->rMaxBLUEgr)) {
		return(0);
	}
		
	/* must have small errors in g and r mags */
	if ((gerr > params->gerrMaxBLUEgr) || (rerr > params->rerrMaxBLUEgr)) {
		return(0);
	}
		
	/* must be bluer than a certain g-r color
         *  range allows for eliminating some junk 
         */
	if ((gmrhi > params->grMaxBLUE) || (g_minus_r < params->grMinBLUE)) {
		return(0);
	}
		
	/* 
	 * if the object survives to this point, it satisfies all
	 * criteria for a BLUE in g-r object.  Return 1 to signal success! 
	 */
	return(1);
}





/*
 * return 1 if the given object satisfies the "red" r-i criteria 
 *                  basically, it's stellar and red in r-i
 * return 0 otherwise
 */

static int
is_serendip_red_ri(
	TA_CALIB_OBJ *obj,
	const TA_SERENDIPITY_PARAMS *params
	)
{
	float rmag, rerr;
	float imag, ierr;
        float gfib, rfib, ifib;
	float r_minus_i, rmierr;
	float rmilo;
        int rflags, iflags;

	shAssert(obj != NULL);
	shAssert(obj->detection != NULL);
	shAssert(params != NULL);

	rmag = obj->detection[TA_R].psfCounts.val;
	rerr = obj->detection[TA_R].psfCounts.err;
	imag = obj->detection[TA_I].psfCounts.val;
	ierr = obj->detection[TA_I].psfCounts.err;
	gfib = obj->detection[TA_G].fiberCounts.val;
	rfib = obj->detection[TA_R].fiberCounts.val;
	ifib = obj->detection[TA_I].fiberCounts.val;
	r_minus_i = rmag - imag;
        rmierr = sqrt (rerr*rerr + ierr*ierr);
        rmilo = r_minus_i - (params->sigmaREDri * rmierr);
        rflags = obj->detection[TA_R].flags;
        iflags = obj->detection[TA_I].flags;

	/* must not too bright for spectra */
	if ((gfib < params->gMinSpec) || (rfib < params->rMinSpec)
           || (ifib < params->iMinSpec) ) {
		return(0);
	}

        /* check to see if object flags warrant consideration */
        if (rflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

        if (iflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

	/* must be stellar; TODO consider if star_l more flexible */
	if (obj->objc_type != AR_OBJECT_TYPE_STAR) {
		return(0);
	}

	/* must be within a range of r-band mag */
	if ((rmag < params->rMinREDri) || (rmag > params->rMaxREDri)) {
		return(0);
	}
		
	/* must be within a range of i-band mag */
	if ((imag < params->iMinREDri) || (imag > params->iMaxREDri)) {
		return(0);
	}
		
	/* must have small errors in r and i mags */
	if ((rerr > params->rerrMaxREDri) || (ierr > params->ierrMaxREDri)) {
		return(0);
	}
		
	/* must be within a certain range of r-i color */
	if ((rmilo < params->riMinRED) || (r_minus_i > params->riMaxRED)) {
		return(0);
	}

	/* 
	 * if the object survives to this point, it satisfies all
	 * criteria for a RED in r-i object.  Return 1 to signal success! 
	 */
	return(1);

}

/*
 * return 1 if the given object satisfies the "red" i-z criteria 
 *                  basically, it's stellar and red in i-z
 * return 0 otherwise
 */

static int
is_serendip_red_iz(
	TA_CALIB_OBJ *obj,
	const TA_SERENDIPITY_PARAMS *params
	)
{
	float imag, ierr;
	float zmag, zerr;
        float gfib, rfib, ifib;
	float i_minus_z, imzerr;
	float imzlo;
        int iflags, zflags;

	shAssert(obj != NULL);
	shAssert(obj->detection != NULL);
	shAssert(params != NULL);

	imag = obj->detection[TA_I].psfCounts.val;
	ierr = obj->detection[TA_I].psfCounts.err;
	zmag = obj->detection[TA_Z].psfCounts.val;
	zerr = obj->detection[TA_Z].psfCounts.err;
	gfib = obj->detection[TA_G].fiberCounts.val;
	rfib = obj->detection[TA_R].fiberCounts.val;
	ifib = obj->detection[TA_I].fiberCounts.val;
	i_minus_z = imag - zmag;
        imzerr = sqrt (ierr*ierr + zerr*zerr);
        imzlo = i_minus_z - (params->sigmaREDiz * imzerr);
        iflags = obj->detection[TA_I].flags;
        zflags = obj->detection[TA_Z].flags;

	/* must not too bright for spectra */
	if ((gfib < params->gMinSpec) || (rfib < params->rMinSpec)
           || (ifib < params->iMinSpec) ) {
		return(0);
	}

        /* check to see if object flags warrant consideration */
        if (iflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

        if (zflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 


	/* must be stellar; TODO consider if star_l more flexible */
	if (obj->objc_type != AR_OBJECT_TYPE_STAR) {
		return(0);
	}

	/* must be within a range of i-band mag */
	if ((imag < params->iMinREDiz) || (imag > params->iMaxREDiz)) {
		return(0);
	}
		
	/* must be within a range of z-band mag */
	if ((zmag < params->zMinREDiz) || (zmag > params->zMaxREDiz)) {
		return(0);
	}
		
	/* must have small errors in i and z mags */
	if ((ierr > params->ierrMaxREDiz) || (zerr > params->zerrMaxREDiz)) {
		return(0);
	}
		
	/* must be within a certain range of i-z color */
	if ((imzlo < params->izMinRED) || (i_minus_z > params->izMaxRED)) {
		return(0);
	}

	/* must be bright enough in i OR very bright in z for spectra */
	if ((imag > params->iSpecREDiz) && (zmag > params->zSpecREDiz)) {
		return(0);
	}

	/* 
	 * if the object survives to this point, it satisfies all
	 * criteria for a RED in i-z object.  Return 1 to signal success! 
	 */
	return(1);
}



/* DISTANT in g-r
 * return 1 if the given object satisfies the g-r "distant" criteria 
 *                  basically, it's stellar and red in g-r
 *                  temp replacement for DISTANT code
 * return 0 otherwise
 */

static int
is_serendip_distant_gr(
	TA_CALIB_OBJ *obj,
	const TA_SERENDIPITY_PARAMS *params
	)
{
	float gmag, gerr;
	float rmag, rerr;
        float gfib, rfib, ifib;
	float g_minus_r, gmrerr;
	float gmrlo;
        int gflags, rflags;

	shAssert(obj != NULL);
	shAssert(obj->detection != NULL);
	shAssert(params != NULL);

	gmag = obj->detection[TA_G].psfCounts.val;
	gerr = obj->detection[TA_G].psfCounts.err;
	rmag = obj->detection[TA_R].psfCounts.val;
	rerr = obj->detection[TA_R].psfCounts.err;
	gfib = obj->detection[TA_G].fiberCounts.val;
	rfib = obj->detection[TA_R].fiberCounts.val;
	ifib = obj->detection[TA_I].fiberCounts.val;
	g_minus_r = gmag - rmag;
        gmrerr = sqrt (gerr*gerr + rerr*rerr);
        gmrlo = g_minus_r - (params->sigmaDISTgr * gmrerr);
        gflags = obj->detection[TA_G].flags;
        rflags = obj->detection[TA_R].flags;

	/* must not too bright for spectra */
	if ((gfib < params->gMinSpec) || (rfib < params->rMinSpec)
           || (ifib < params->iMinSpec) ) {
		return(0);
	}

        /* check to see if object flags warrant consideration */
        if (gflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

        if (rflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

	/* must be stellar; TODO consider if star_l more flexible */
	if (obj->objc_type != AR_OBJECT_TYPE_STAR) {
		return(0);
	}

	/* must be within a range of g-band mag */
	if ((gmag < params->gMinDISTgr) || (gmag > params->gMaxDISTgr)) {
		return(0);
	}
		
	/* must be within a range of r-band mag */
	if ((rmag < params->rMinDISTgr) || (rmag > params->rMaxDISTgr)) {
		return(0);
	}
		
	/* must have small errors in g and r mags */
	if ((gerr > params->gerrMaxDISTgr) || (rerr > params->rerrMaxDISTgr)) {
		return(0);
	}
		
	/* must be within a certain range of g-r color */
	if ((gmrlo < params->grMinDIST) 
                        || (g_minus_r > params->grMaxDIST)) {
		return(0);
	}

	/* 
	 * if the object survives to this point, it satisfies all
	 * criteria for a DISTANT in g-r object.  Return 1 to signal success! 
	 */
	return(1);
}




/* DISTANT in r-i
 * return 1 if the given object satisfies the r-i "distant" criteria 
 *                  basically, it's stellar and blue in r-i
 *                  temp replacement for DISTANT code
 * return 0 otherwise
 */

static int
is_serendip_distant_ri(
	TA_CALIB_OBJ *obj,
	const TA_SERENDIPITY_PARAMS *params
	)
{
	float rmag, rerr;
	float imag, ierr;
        float gfib, rfib, ifib;
	float r_minus_i;
	float rmierr, rmihi;
        int rflags, iflags;

	shAssert(obj != NULL);
	shAssert(obj->detection != NULL);
	shAssert(params != NULL);

	rmag = obj->detection[TA_R].psfCounts.val;
	rerr = obj->detection[TA_R].psfCounts.err;
	imag = obj->detection[TA_I].psfCounts.val;
	gfib = obj->detection[TA_G].fiberCounts.val;
	rfib = obj->detection[TA_R].fiberCounts.val;
	ifib = obj->detection[TA_I].fiberCounts.val;
	ierr = obj->detection[TA_I].psfCounts.err;
	r_minus_i = rmag - imag;
        rmierr = sqrt (rerr*rerr + ierr*ierr);
        rmihi = r_minus_i + (params->sigmaDISTri * rmierr);
        rflags = obj->detection[TA_R].flags;
        iflags = obj->detection[TA_I].flags;

	/* must not too bright for spectra */
	if ((gfib < params->gMinSpec) || (rfib < params->rMinSpec)
           || (ifib < params->iMinSpec) ) {
		return(0);
	}

        /* check to see if object flags warrant consideration */
        if (rflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

        if (iflags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	} 

	/* must be stellar; TODO consider if star_l more flexible */
	if (obj->objc_type != AR_OBJECT_TYPE_STAR) {
		return(0);
	}

	/* must be within a range of r-band mag */
	if ((rmag < params->rMinDISTri) || (rmag > params->rMaxDISTri)) {
		return(0);
	}
		
	/* must be within a range of i-band mag */
	if ((imag < params->iMinDISTri) || (imag > params->iMaxDISTri)) {
		return(0);
	}
		
	/* must have small errors in r and i mags */
	if ((rerr > params->rerrMaxDISTri) || (ierr > params->ierrMaxDISTri)) {
		return(0);
	}
		
	/* must be bluer than a certain r-i color
         *  range allows for eliminating some junk 
         */
	if ((rmihi > params->riMaxDIST) 
                || (r_minus_i <params->riMinDIST)) {
		return(0);
	}
		
	/* 
	 * if the object survives to this point, it satisfies all
	 * criteria for a DISTANT in r-i object.  Return 1 to signal success! 
	 */
	return(1);
}


/* FIRST source
 * return 1 if the given object satisfies the "first" criteria 
 *                  basically, it's a FIRST match
 * return 0 otherwise
 */

static int
is_serendip_first(
	TA_CALIB_OBJ *obj,
	const TA_SERENDIPITY_PARAMS *params
	)
{
	float gmag, gerr;
	float rmag, rerr;
	float imag, ierr;
        float gfib, rfib, ifib;

	shAssert(obj != NULL);
	shAssert(obj->detection != NULL);
	shAssert(params != NULL);

	gmag = obj->detection[TA_G].fiberCounts.val;
	gerr = obj->detection[TA_G].fiberCounts.err;
	rmag = obj->detection[TA_R].fiberCounts.val;
	rerr = obj->detection[TA_R].fiberCounts.err;
	imag = obj->detection[TA_I].fiberCounts.val;
	ierr = obj->detection[TA_I].fiberCounts.err;
	gfib = obj->detection[TA_G].fiberCounts.val;
	rfib = obj->detection[TA_R].fiberCounts.val;
	ifib = obj->detection[TA_I].fiberCounts.val;

	/* must not too bright for spectra */
	if ((gfib < params->gMinSpec) || (rfib < params->rMinSpec)
           || (ifib < params->iMinSpec) ) {
		return(0);
	}

        /* check to see if object flags warrant consideration */
        if (obj->objc_flags&(AR_DFLAG_BRIGHT | AR_DFLAG_EDGE
                                | AR_DFLAG_BLENDED | AR_DFLAG_CHILD
                                | AR_DFLAG_CR | AR_DFLAG_INTERP
                                | AR_DFLAG_SATUR | AR_DFLAG_NOTCHECKED
                                | AR_DFLAG_SUBTRACTED | AR_DFLAG_BADSKY
                                | AR_DFLAG_TOO_LARGE)) {
		return(0);
	}

        /* must be FIRST source (without lobes) */
        if(obj->firstMatch != 1) { 
               return(0);
        } 

	/* must be star or gal; TODO consider if star_l more flexible */
	if ((obj->objc_type != AR_OBJECT_TYPE_STAR)
                && (obj->objc_type !=AR_OBJECT_TYPE_GALAXY)) {
		return(0);
	}

        /* must be within a mag range accessible to spectra */
        if ( ((gmag < params->gMinFIRST) || (gmag > params->gMaxFIRST)) && 
                ((rmag < params->rMinFIRST) || (rmag > params->rMaxFIRST)) && 
                ((imag < params->iMinFIRST) || (imag > params->iMaxFIRST)) ) {
                return(0);
        }

	/* some error check to avoid some junk */
	if ((gerr > params->gerrMaxFIRST) && (rerr > params->rerrMaxFIRST)
                && (ierr > params->ierrMaxFIRST)) {
		return(0);
	}
		
	/* 
	 * if the object survives to this point, it satisfies all
	 * criteria for a FIRST match object.  Return 1 to signal success! 
	 */
	return(1);
}





