/**************************************************************

mfind.c (C-Munipack project)
Copyright (C) 2003 David Motl, dmotl@volny.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

**************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <limits.h>

#include "cmpack_common.h"
#include "cmpack_mfind.h"
#include "console.h"
#include "comfun.h"
#include "filter.h"

/* List allocation increment */
#define ALLOC_BY		64

/* Filter power */
#define FILTER_POWER		2

/* Filter length (odd number) */
#define FILTER_LEN			(2*(FILTER_POWER)+1)

/* Minimum number of valid measurements */
#define MIN_POINTS			(3 + ((FILTER_LEN)-1))

/* Minimum number of objects */
#define MIN_OBJECTS			3

/* Default threshold in % */
#define DEF_THRESHOLD		60.0

#ifndef min
#define min(a,b)        (((a)<(b))?(a):(b))
#endif
#ifndef max
#define max(a,b)        (((a)>(b))?(a):(b))
#endif

/* List of objects */
struct _CmpackMuniFindList
{
	int count, capacity;
	int *list;
};

/* Master-flat frame context */
struct _CmpackMuniFind
{
	int refcnt;					/**< Reference counter */
	CmpackConsole *con;			/**< Console */
	int aperture;				/**< Aperture index */
	struct _CmpackMuniFindList req_comp;	/**< Empty = Autodetection, list of comparison star identifiers */
	int act_comp;				/** Used comparison star identifier */
	double threshold;			/**< Required image data format */
};

typedef struct _ObjectRec
{
	int object_id, npoints;
	double meanmag, stddev;
} ObjectRec;

static int compare_fn(const void *a, const void *b)
{
	double ma = ((ObjectRec*)a)->meanmag, mb = ((ObjectRec*)b)->meanmag;
	return (ma < mb ? -1 : (ma > mb ? 1 : 0));
}

static void sort_xy_by_x(int count, double *x, double *y)
{
	int i, j, k;

	if (count>1) {
		for (i=0; i<count; i++) {
			for (j=0; j<i; j++) {
				if (x[i] < x[j]) {
					double key = x[i], value = y[i];
					for (k=i-1; k>=j; k--) {
						x[k+1] = x[k];
						y[k+1] = y[k];
					}
					x[j] = key;
					y[j] = value;
					break;
				}
			}
		}
	}
}

/********************   PUBLIC FUNCTIONS   ******************************/

/* Initializes the context */
CmpackMuniFind *cmpack_mfind_init(void)
{
	CmpackMuniFind *lc = (CmpackMuniFind*)cmpack_calloc(1, sizeof(CmpackMuniFind));
	lc->refcnt = 1;
	lc->act_comp = CMPACK_MFIND_AUTO;
	lc->threshold = DEF_THRESHOLD;
	return lc;
}

/* Create and initialize the context */
CmpackMuniFind *cmpack_mfind_reference(CmpackMuniFind *ctx)
{
	ctx->refcnt++;
	return ctx;
}

/* Create and initialize the context */
void cmpack_mfind_destroy(CmpackMuniFind *ctx)
{
	if (ctx) {
		ctx->refcnt--;
		if (ctx->refcnt==0) {
			if (ctx->con) {
				cmpack_con_destroy(ctx->con);
				ctx->con = NULL;
			}
			cmpack_free(ctx->req_comp.list);
			cmpack_free(ctx);
		}
	}
}

/* Attach console */
void cmpack_mfind_set_console(CmpackMuniFind *ctx, CmpackConsole *con)
{
	if (con!=ctx->con) {
		if (ctx->con) 
			cmpack_con_destroy(ctx->con);
		ctx->con = con;
		if (ctx->con) 
			cmpack_con_reference(ctx->con);
	}
}

/* Set aperture */
void cmpack_mfind_set_aperture(CmpackMuniFind *lc, int aperture)
{
	lc->aperture = aperture;
}

/* Set comparison star */
void cmpack_mfind_set_comparison(CmpackMuniFind *lc, int star_id)
{
	cmpack_mfind_clear_comparison(lc);
	cmpack_mfind_add_comparison(lc, star_id);
}

void cmpack_mfind_clear_comparison(CmpackMuniFind *lc)
{
	lc->req_comp.count = 0;
}

void cmpack_mfind_add_comparison(CmpackMuniFind *lc, int star_id)
{
	if (star_id >= 0) {
		int i = lc->req_comp.count;
		if (lc->req_comp.count >= lc->req_comp.capacity) {
			lc->req_comp.capacity += ALLOC_BY;
			lc->req_comp.list = (int*)cmpack_realloc(lc->req_comp.list, lc->req_comp.capacity * sizeof(int));
		}
		lc->req_comp.list[i] = star_id;
		lc->req_comp.count++;
	}
}

/* Set output image data format */
int cmpack_mfind_get_comparison(CmpackMuniFind *lc)
{
	return lc->act_comp;
}

/* Set output image data format */
void cmpack_mfind_set_threshold(CmpackMuniFind *lc, double threshold)
{
	lc->threshold = threshold;
}

/* Make mag-dev graph from read-all file */
int cmpack_mfind(CmpackMuniFind *lc, CmpackFrameSet *fset, CmpackTable **ptable,
	CmpackMFindFlags flags)
{
	char msg[1024];
	int res, count, ap_index, nobjects, nframes, *comp, var, ncmp, cvalid, var_is_comp;
	int i, j, valid, npoints, nmeasur, nreferr, nrequired, nwritten, nbadpoints, cmp_npoints;
	double *fmag, *fjd, sum, mean, stddev;
	CmpackTable *table;
	CmpackCatObject obj;
	CmpackPhtAperture aperture;
	CmpackPhtData info, vinfo;
	CmpackFrameInfo finfo;
	ObjectRec *tmp;

	*ptable = NULL;

	/* Check parameters */
	if (lc->threshold<0 || lc->threshold>100) {
		printout(lc->con, 0, "Invalid value of threshold"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Print configuration parameters */
	if (is_debug(lc->con)) {
		printout(lc->con, 1, "Munifind parameters:");
		if (lc->req_comp.count > 0)
			printparvi(lc->con, "Comparison star", 1, lc->req_comp.count, lc->req_comp.list);
		else
			printpars(lc->con, "Comparison star", 1, "autodetection");
		printpard(lc->con, "Threshold", 1, lc->threshold, 1);
	}

	/* Check number of frames */
	nframes = cmpack_fset_frame_count(fset);
	if (nframes<MIN_POINTS) {
		printout(lc->con, 0, "Not enough frames in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check number of objects */
	nobjects = cmpack_fset_object_count(fset);
	if (nobjects<MIN_OBJECTS) {
		printout(lc->con, 0, "Not enough objects in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check aperture */
	ap_index = cmpack_fset_find_aperture(fset, lc->aperture);
	if (ap_index<0) {
		printout(lc->con, 0, "Invalid aperture identifier"); 
		return CMPACK_ERR_AP_NOT_FOUND;
	} 
	aperture.id = -1;
	cmpack_fset_get_aperture(fset, ap_index, CMPACK_PA_ID | CMPACK_PA_RADIUS, &aperture);

	/* Check comparison star */
	lc->act_comp = -1;
	comp = (int*)cmpack_malloc(max(lc->req_comp.count, 1)*sizeof(int));
	if (lc->req_comp.count == 0) {
		if (cmpack_mfind_autocomp(lc, fset, &lc->act_comp, flags)!=0) {
			printout(lc->con, 0, "Failed to find the comparison star"); 
			return CMPACK_ERR_REF_NOT_FOUND;
		}
		comp[0] = cmpack_fset_find_object(fset, lc->act_comp);
		if (comp[0] < 0) {
			printout(lc->con, 0, "The specified comparison star does not exist.");
			cmpack_free(comp);
			return CMPACK_ERR_REF_NOT_FOUND;
		}
		ncmp = 1;
	} else {
		/* Comparison star is specified by a caller */
		/* Set actual comparison if there is only one comparison star */
		if (lc->req_comp.count == 1)
			lc->act_comp = lc->req_comp.list[0];
		for (i = 0; i < lc->req_comp.count; i++) {
			comp[i] = cmpack_fset_find_object(fset, lc->req_comp.list[i]);
			if (comp[i] < 0) {
				printout(lc->con, 0, "The specified comparison star does not exist.");
				cmpack_free(comp);
				return CMPACK_ERR_REF_NOT_FOUND;
			}
		}
		ncmp = lc->req_comp.count;
	}
	
	/* Check the comparison stars, number of valid comparison star measurements */
	cmp_npoints = 0;
	res = cmpack_fset_rewind(fset);
	while (res==0) {
		cvalid = 1;
		for (i = 0; i < ncmp; i++) {
			info.mag_valid = 0;
			cmpack_fset_get_data(fset, comp[i], ap_index, &info);
			cvalid = cvalid && info.mag_valid;
		}
		if (cvalid)
			cmp_npoints++;
		res = cmpack_fset_next(fset);
	}
	if (cmp_npoints<MIN_POINTS) {
		printout(lc->con, 0, "The specified comparison star does not have enough valid measurements."); 
		cmpack_free(comp);
		return CMPACK_ERR_REF_NOT_FOUND;
	}

	/* Compute minimum required measurements */
	nreferr = nframes - cmp_npoints;
	nrequired = (int)(0.01 * lc->threshold * cmp_npoints);
	if (nrequired<MIN_POINTS) 
		nrequired = MIN_POINTS;

	/* computing average and std. dev. for each star */
	fmag = (double*)cmpack_malloc(nframes*sizeof(double));
	fjd  = (double*)cmpack_malloc(nframes*sizeof(double));
	tmp = (ObjectRec*)cmpack_malloc(nobjects*sizeof(ObjectRec));
	nwritten = nbadpoints = nmeasur = 0;
	for (var=0; var<nobjects; var++) {
		valid = 0;
		mean = stddev = 0.0;
		obj.id = -1;
		cmpack_fset_get_object(fset, var, CMPACK_OM_ID, &obj);
		var_is_comp = 0;
		for (i = 0; i < ncmp; i++) {
			if (var == comp[i]) {
				var_is_comp = 1;
				break;
			}
		}
		if (!var_is_comp) {
			/* Make light curve v - c */
			count = 0;
			res = cmpack_fset_rewind(fset);
			while (res==0) {
				double cmag;
				int n, cok = 0;
				vinfo.mag_valid = 0;
				cmpack_fset_get_frame(fset, CMPACK_FI_JULDAT, &finfo);
				cmpack_fset_get_data(fset, var, ap_index, &vinfo);
				if (ncmp == 1) {
					CmpackPhtData cinfo;
					cinfo.mag_valid = 0;
					cmpack_fset_get_data(fset, comp[0], ap_index, &cinfo);
					cok = cinfo.mag_valid;
					cmag = cinfo.magnitude;
				}
				else {
					cmag = 0;
					n = 0;
					for (i = 0; i < ncmp; i++) {
						CmpackPhtData cinfo;
						cinfo.mag_valid = 0;
						cmpack_fset_get_data(fset, comp[i], ap_index, &cinfo);
						if (cinfo.mag_valid) {
							cmag += pow(10.0, -0.4*cinfo.magnitude);
							n++;
						}
					}
					if (n == ncmp) {
						cmag = -2.5*log10(cmag / n);
						cok = 1;
					}
				}
				if (cok && vinfo.mag_valid) {
					fjd[count] = finfo.juldat;
					fmag[count] = vinfo.magnitude - cmag;
					count++;
				}
				res = cmpack_fset_next(fset);
			}
			if (count>=nrequired) {
				/* Sort by JD, apply median filtering */
				sort_xy_by_x(count, fjd, fmag);
				median_filter(FILTER_LEN, fmag, &count);
				/* Mean value */
				sum = 0.0;
				for (j=0; j<count; j++) 
					sum += fmag[j];
				mean = sum/count;
				/* Standard deviation */
				sum = 0.0;
				for (j=0; j<count; j++) 
					sum += pow(fmag[j]-mean, 2.0);
				stddev = sqrt(sum/(count-1));
				valid = 1;
			}
			npoints = count;
		} else {
			npoints = cmp_npoints;
		}
		nbadpoints += (cmp_npoints - npoints);
		/* Store data to table */
		if (valid) {
			tmp[nwritten].object_id = obj.id;
			tmp[nwritten].meanmag = mean;
			tmp[nwritten].stddev = stddev;
			tmp[nwritten].npoints = npoints;
			nwritten++;
		}
	}
	cmpack_free(fmag);
	cmpack_free(fjd);

	/* Sort records in tmp by mean magnitude */
	qsort(tmp,nwritten,sizeof(ObjectRec),compare_fn);

	/* Init table */
	table = cmpack_tab_init(CMPACK_TABLE_MAGDEV);
	if (lc->act_comp>=0)
		cmpack_tab_pkyi(table, "Reference star", lc->act_comp);
	cmpack_tab_pkyi(table, "Aperture", aperture.id);
	cmpack_tab_add_column_int(table, "INDEX", 0, INT_MAX, -1);
	cmpack_tab_add_column_dbl(table, "MEAN_MAG", MAG_PRECISION, -99.0, 99.0, INVALID_MAG);
	cmpack_tab_add_column_dbl(table, "STDEV", MAG_PRECISION, 0.0, 9.0, INVALID_MAGERR);
	cmpack_tab_add_column_int(table, "GOODPOINTS", 0, INT_MAX, 0);

	/* Create table data */
	for (i=0; i<nwritten; i++) {
		cmpack_tab_append(table);
		cmpack_tab_ptdi(table, 0, tmp[i].object_id);
		cmpack_tab_ptdd(table, 1, tmp[i].meanmag);
		cmpack_tab_ptdd(table, 2, tmp[i].stddev);
		cmpack_tab_ptdi(table, 3, tmp[i].npoints); 
	}
	cmpack_free(tmp);
	
	/* Print messages */
	sprintf(msg,"Aperture:                           %d (%.4f pxl)", aperture.id, aperture.radius);
	printout(lc->con, 1, msg);
	sprintf(msg,"Number of analyzed stars:           %d (w/o reference star)", nobjects-1);
	printout(lc->con, 1, msg);
	sprintf(msg,"Number of source files:             %d", nframes);
	printout(lc->con, 1, msg);
	sprintf(msg,"Total number of data points:        %d", nmeasur);
	printout(lc->con, 1, msg);
	if (lc->act_comp>=0)
		sprintf(msg,"Reference star used:                %d", lc->act_comp);
	else
		sprintf(msg, "Reference star used:                (%d stars)", lc->req_comp.count);
	printout(lc->con, 1, msg);
	sprintf(msg,"Reference star errors:              %d (%.0f%%)", nreferr, (nmeasur>0 ? 100.0*nreferr/nmeasur : 100.0));
	printout(lc->con, 1, msg);
	sprintf(msg,"Number of good points required:     %d", nrequired);
	printout(lc->con, 1, msg);
	sprintf(msg,"Number of stars written to output:  %d (%.0f%%)", nwritten, 100.0*nwritten/(nobjects-1));
	printout(lc->con, 1, msg);
	sprintf(msg,"Number of points ruled out:         %d (%.0f%%)", nbadpoints, (nmeasur>0 ? 100.0*nbadpoints/nmeasur : 100.0));
	printout(lc->con, 1, msg);
	
	cmpack_free(comp);

	*ptable = table;
    return 0;
}

int cmpack_mfind_autocomp(CmpackMuniFind *lc, CmpackFrameSet *fset, 
	int *star_id, CmpackMFindFlags flags)
{
	static const int nrequired = MIN_POINTS;

	int res, c, v, count, ap_index, nobjects, nframes, count2;
	int i, j, vframes, *scnt, *sok, cmp_npoints;
	double sum, mean, stddev, q0, *fjd, *fmag, *sdev;
	CmpackCatObject obj;
	CmpackPhtAperture aperture;
	CmpackFrameInfo finfo;

	if (star_id)
		*star_id = -1;

	/* Check number of frames */
	nframes = cmpack_fset_frame_count(fset);
	if (nframes<MIN_POINTS) {
		printout(lc->con, 0, "Not enough frames in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check number of objects */
	nobjects = cmpack_fset_object_count(fset);
	if (nobjects<MIN_OBJECTS) {
		printout(lc->con, 0, "Not enough objects in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check aperture */
	ap_index = cmpack_fset_find_aperture(fset, lc->aperture);
	if (ap_index<0) {
		printout(lc->con, 0, "Invalid aperture identifier"); 
		return CMPACK_ERR_AP_NOT_FOUND;
	} 
	aperture.id = -1;
	cmpack_fset_get_aperture(fset, ap_index, CMPACK_PA_ID, &aperture);

	/* Check comparison star */
	fjd = (double*)cmpack_malloc(nframes*sizeof(double));
	fmag = (double*)cmpack_malloc(nframes*sizeof(double));
	sdev = (double*)cmpack_calloc(nobjects, sizeof(double));
	scnt = (int*)cmpack_calloc(nobjects, sizeof(int));
	sok = (int*)cmpack_calloc(nobjects, sizeof(int));
	/* Compute the maximum number of valid frames */
	vframes = 0;
	for (i=0; i<nobjects; i++) {
		count = 0;
		res = cmpack_fset_rewind(fset);
		while (res==0) {
			CmpackPhtData info;
			info.mag_valid = 0;
			cmpack_fset_get_data(fset, i, ap_index, &info);
			if (info.mag_valid) 
				count++;
			res = cmpack_fset_next(fset);
		}
		if (vframes < count)
			vframes = count;
	}
	/* Take stars that have maximum number of valid measurements */
	while (vframes>0) {
		count2 = 0;
		memset(sok, 0, nobjects*sizeof(int));
		for (i=0; i<nobjects; i++) {
			/* Count number of measurements */
			count = 0;
			res = cmpack_fset_rewind(fset);
			while (res==0) {
				CmpackPhtData info;
				info.mag_valid = 0;
				cmpack_fset_get_data(fset, i, ap_index, &info);
				if (info.mag_valid)
					count++;
				res = cmpack_fset_next(fset);
			}
			/* If count is maximum number, select it */
			if (count>=vframes) {
				sok[i] = 1;
				count2++;
			}
		}
		/* If less than three stars was found, decrease the number of frames required */
		if (count2>2)
			break;
		vframes--;
	}
	/* Select star with lowest rank */
	for (c=0; c<nobjects; c++) {
		if (sok[c]) {
			for (v=c+1; v<nobjects; v++) {
				if (sok[v]) {
					/* Make light curve v-c */
					count = 0;
					res = cmpack_fset_rewind(fset);
					while (res==0) {
						CmpackPhtData vinfo, cinfo;
						vinfo.mag_valid = cinfo.mag_valid = 0;
						cmpack_fset_get_frame(fset, CMPACK_FI_JULDAT, &finfo);
						cmpack_fset_get_data(fset, c, ap_index, &cinfo);
						cmpack_fset_get_data(fset, v, ap_index, &vinfo);
						if (vinfo.mag_valid && cinfo.mag_valid) {
							fjd[count] = finfo.juldat;
							fmag[count] = vinfo.magnitude - cinfo.magnitude;
							count++;
						}
						res = cmpack_fset_next(fset);
					}
					if (count>=nrequired) {
						/* Sort by JD, apply median filtering */
						sort_xy_by_x(count, fjd, fmag);
						median_filter(FILTER_LEN, fmag, &count);
						/* Mean value */
						sum = 0.0;
						for (j=0; j<count; j++) 
							sum += fmag[j];
						mean = sum/count;
						/* Standard deviation */
						sum = 0.0;
						for (j=0; j<count; j++) 
							sum += pow(fmag[j]-mean, 2.0);
						stddev = sqrt(sum/(count-1));
						sdev[c] += stddev;
						scnt[c]++;
						sdev[v] += stddev;
						scnt[v]++;
					}
				}
			}
		}
	}
	/* Find lowest */
	j = -1; q0 = 99.9e9;
	for (i=0; i<nobjects; i++) {
		if (sok[i] && scnt[i]>0) {
			sdev[i] /= scnt[i];
			if (sdev[i] < q0) {
				j = i;
				q0 = sdev[i];
			}
		}
	}
	c = j;
	cmpack_free(fjd);
	cmpack_free(fmag);
	cmpack_free(sdev);
	cmpack_free(scnt);
	/* Find object identifier for the comparison star*/
	cmpack_free(sok);
	obj.id = -1;
	cmpack_fset_get_object(fset, c, CMPACK_OM_ID, &obj);

	/* Check comparison star */
	cmp_npoints = 0;
	res = cmpack_fset_rewind(fset);
	while (res==0) {
		CmpackPhtData info;
		info.mag_valid = 0;
		cmpack_fset_get_data(fset, c, ap_index, &info);
		if (info.mag_valid) 
			cmp_npoints++;
		res = cmpack_fset_next(fset);
	}
	if (cmp_npoints<MIN_POINTS) {
		printout(lc->con, 0, "Invalid comparison star"); 
		return CMPACK_ERR_REF_NOT_FOUND;
	}

	if (star_id)
		*star_id = obj.id;
	return 0;
}

int cmpack_mfind_jdrange(CmpackMuniFind *lc, CmpackFrameSet *fset, 
	double *jdmin, double *jdmax, CmpackMFindFlags flags)
{
	int i, res, *comp, ncmp, ap_index, nobjects, nframes, cmp_npoints;
	double min_jd, max_jd;
	CmpackPhtAperture aperture;
	CmpackFrameInfo finfo;

	if (jdmin)
		*jdmin = 0;
	if (jdmax)
		*jdmax = 0;

	/* Check number of frames */
	nframes = cmpack_fset_frame_count(fset);
	if (nframes<MIN_POINTS) {
		printout(lc->con, 0, "Not enough frames in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check number of objects */
	nobjects = cmpack_fset_object_count(fset);
	if (nobjects<MIN_OBJECTS) {
		printout(lc->con, 0, "Not enough objects in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check aperture */
	ap_index = cmpack_fset_find_aperture(fset, lc->aperture);
	if (ap_index<0) {
		printout(lc->con, 0, "Invalid aperture identifier"); 
		return CMPACK_ERR_AP_NOT_FOUND;
	} 
	aperture.id = -1;
	cmpack_fset_get_aperture(fset, ap_index, CMPACK_PA_ID, &aperture);

	/* Check comparison star */
	lc->act_comp = -1;
	comp = (int*)cmpack_malloc(max(lc->req_comp.count, 1) * sizeof(int));
	if (lc->req_comp.count == 0) {
		if (cmpack_mfind_autocomp(lc, fset, &lc->act_comp, flags)!=0) {
			printout(lc->con, 0, "Failed to find the comparison star"); 
			return CMPACK_ERR_REF_NOT_FOUND;
		}
		/* Check comparison star */
		comp[0] = cmpack_fset_find_object(fset, lc->act_comp);
		if (comp[0] < 0) {
			printout(lc->con, 0, "The specified comparison star does not exist.");
			return CMPACK_ERR_REF_NOT_FOUND;
		}
		ncmp = 1;
	} else {
		/* Comparison star is specified by a caller */
		/* Set actual comparison if there is only one comparison star */
		if (lc->req_comp.count == 1) 
			lc->act_comp = lc->req_comp.list[0];
		for (i = 0; i < lc->req_comp.count; i++) {
			comp[i] = cmpack_fset_find_object(fset, lc->req_comp.list[i]);
			if (comp[i] < 0) {
				printout(lc->con, 0, "The specified comparison star does not exist.");
				cmpack_free(comp);
				return CMPACK_ERR_REF_NOT_FOUND;
			}
		}
		ncmp = lc->req_comp.count;
	}

	cmp_npoints = 0;
	min_jd = 1e10;    
	max_jd = 0;
	res = cmpack_fset_rewind(fset);
	while (res==0) {
		int n = 0;
		for (i = 0; i < ncmp; i++) {
			CmpackPhtData cinfo;
			cinfo.mag_valid = 0;
			cmpack_fset_get_data(fset, comp[i], ap_index, &cinfo);
			if (cinfo.mag_valid)
				n++;
		}
		cmpack_fset_get_frame(fset, CMPACK_FI_JULDAT, &finfo);
		if (finfo.juldat>0 && n == ncmp) {
			double jd = finfo.juldat;
			if (jd < min_jd)
				min_jd = jd;
			if (jd > max_jd)
				max_jd = jd;
			cmp_npoints++;
		}
		res = cmpack_fset_next(fset);
	}
	if (cmp_npoints < 1) {
		printout(lc->con, 0, "The specified comparison star does not have enough valid measurements."); 
		return CMPACK_ERR_REF_NOT_FOUND;
	}

	if (jdmin)
		*jdmin = min_jd;
	if (jdmax)
		*jdmax = max_jd;
	return 0;
}

int cmpack_mfind_magrange(CmpackMuniFind *lc, CmpackFrameSet *fset, 
	double *magrange, CmpackMFindFlags flags)
{
	int i, n, res, cok, var, *comp, ap_index, nobjects, nframes, ncmp, cmp_npoints;
	double min_mag, max_mag, range, max_range, cmag;
	CmpackPhtAperture aperture;

	if (magrange)
		*magrange = 0;

	/* Check number of frames */
	nframes = cmpack_fset_frame_count(fset);
	if (nframes<MIN_POINTS) {
		printout(lc->con, 0, "Not enough frames in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check number of objects */
	nobjects = cmpack_fset_object_count(fset);
	if (nobjects<MIN_OBJECTS) {
		printout(lc->con, 0, "Not enough objects in the input data"); 
		return CMPACK_ERR_INVALID_PAR;
	} 

	/* Check aperture */
	ap_index = cmpack_fset_find_aperture(fset, lc->aperture);
	if (ap_index<0) {
		printout(lc->con, 0, "Invalid aperture identifier"); 
		return CMPACK_ERR_AP_NOT_FOUND;
	} 
	aperture.id = -1;
	cmpack_fset_get_aperture(fset, ap_index, CMPACK_PA_ID, &aperture);

	/* Check comparison star */
	lc->act_comp = -1;
	comp = (int*)cmpack_malloc(max(lc->req_comp.count, 1) * sizeof(int));
	if (lc->req_comp.count == 0) {
		if (cmpack_mfind_autocomp(lc, fset, &lc->act_comp, flags)!=0) {
			printout(lc->con, 0, "Failed to find the comparison star"); 
			return CMPACK_ERR_REF_NOT_FOUND;
		}
		/* Check comparison star */
		comp[0] = cmpack_fset_find_object(fset, lc->act_comp);
		if (comp[0] < 0) {
			printout(lc->con, 0, "The specified comparison star does not exist.");
			return CMPACK_ERR_REF_NOT_FOUND;
		}
		ncmp = 1;
	} else {
		/* Comparison star is specified by a caller */
		/* Set actual comparison if there is only one comparison star */
		if (lc->req_comp.count == 1)
			lc->act_comp = lc->req_comp.list[1];
		for (i = 0; i < lc->req_comp.count; i++) {
			comp[i] = cmpack_fset_find_object(fset, lc->req_comp.list[i]);
			if (comp[i] < 0) {
				printout(lc->con, 0, "The specified comparison star does not exist.");
				cmpack_free(comp);
				return CMPACK_ERR_REF_NOT_FOUND;
			}
		}
		ncmp = lc->req_comp.count;
	}

	/* Check comparison star */
	cmp_npoints = 0;
	res = cmpack_fset_rewind(fset);
	while (res==0) {
		int n = 0;
		for (i = 0; i < ncmp; i++) {
			CmpackPhtData cinfo;
			cinfo.mag_valid = 0;
			cmpack_fset_get_data(fset, comp[i], ap_index, &cinfo);
			if (cinfo.mag_valid)
				n++;
		}
		if (n == ncmp) 
			cmp_npoints++;
		res = cmpack_fset_next(fset);
	}
	if (cmp_npoints < 1) {
		printout(lc->con, 0, "The specified comparison star does not have enough valid measurements."); 
		return CMPACK_ERR_REF_NOT_FOUND;
	}

	max_range = 0;
	for (var=0; var<nobjects; var++) {
		int var_is_comp = 0;
		for (i = 0; i < ncmp; i++) {
			if (var == comp[i]) {
				var_is_comp = 1;
				break;
			}
		}
		if (!var_is_comp) {
			/* Make light curve v-c */
			res = cmpack_fset_rewind(fset);
			min_mag = 999.9;
			max_mag = -999.9;
			while (res==0) {
				CmpackPhtData vinfo;
				vinfo.mag_valid = 0;
				cok = 0;
				if (ncmp == 1) {
					CmpackPhtData cinfo;
					cinfo.mag_valid = 0;
					cmpack_fset_get_data(fset, comp[0], ap_index, &cinfo);
					cok = cinfo.mag_valid;
					cmag = cinfo.magnitude;
				}
				else {
					cmag = 0;
					n = 0;
					for (i = 0; i < ncmp; i++) {
						CmpackPhtData cinfo;
						cinfo.mag_valid = 0;
						cmpack_fset_get_data(fset, comp[i], ap_index, &cinfo);
						if (cinfo.mag_valid) {
							cmag += pow(10.0, -0.4*cinfo.magnitude);
							n++;
						}
					}
					if (n == ncmp) {
						cmag = -2.5*log10(cmag / n);
						cok = 1;
					}
				}
				cmpack_fset_get_data(fset, var, ap_index, &vinfo);
				if (vinfo.mag_valid && cok) {
					double mag = vinfo.magnitude - cmag;
					if (mag < min_mag)
						min_mag = mag;
					if (mag > max_mag)
						max_mag = mag;
				}
				res = cmpack_fset_next(fset);
			}
			range = max_mag - min_mag;
			if (range > max_range)
				max_range = range;
		}
	}

	if (magrange)
		*magrange = max_range;
	return 0;
}
