//=====================================================================
// File:    TracePeakFinder.java
// Class:   TracePeakFinder
// Package: AFLPcore
//
// Author:  Philip DeCamp
// Date:    June 1, 2001
// Contact: decamp@portalofevil.com
//
// Genographer v1.0 - Computer assisted scoring of gels.
// Copyright (C) 1998  Montana State University
// 
// 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; version 2
// of the License.
//
// 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.
//
// The GNU General Public License is distributed in the file GPL
//=====================================================================

package AFLPcore;

/**
 * This is a simple algorithm that is used to find peaks in a set of trace data.
 * There is a separate file, PeakLocate.java, that was created with this purpose
 * in mind, but that wouldn't have done exactly what I wanted.
 * The algorithm has three steps.
 * 1) Find a point that is high than the points on either side of it and check
 *		if it's above the minimum threshold.
 * 2) Check the points thirty samples on either side of it to make sure there's
 *		not a nearby point that's even higher.
 * 3) Make sure it meets the minimum width requirement.
 * 
 * It's pretty simple, and fairly effective.  The peaks on molecular weight
 * standards tend to be very well defined.  
 */
public class TracePeakFinder{
	
	
	//I made a zillion "find" methods for this thing.  Most of them are pretty
	//unnecessary.  I'll only define the parameters for the bottom one.
	public static double find(double [] trace){
		return find(trace, 0, 50, 3);
	}
	
	public static double find(double [] trace, int offset){
		return find(trace, offset, 50, 3);
	}
	
	public static double find(double [] trace, int offset, double minPeakValue){
		return find(trace, offset, minPeakValue, 3);
	}
	
	/**
	 * This is the one method in this class, really.  Quite simple, it searches
	 * through the data until it finds a peak, then returns the index of that number.
	 * parameters:
	 * 
	 * @trace: the array of doubles that constitutes the trace data
	 * 
	 * @offset: the index into the trace data from which to start searching for
	 *		a peak.
	 * 
	 * @minPeakValue: the minimum value a point must be to be considered a peak.
	 * 
	 * @minPeakWidth: the minimum width a peak must be.
	 */
	
	public static long find(double [] trace, int offset, 
							  double minPeakValue, double minPeakWidth)
	{
		offset++;
		boolean flag;
		double temp;
		int count;  

		//keep going until we run out of data, in which case this method returns
		//a -1 to signify that no more peaks were found.		while(offset < trace.length - 1){
		  
		//If the point behind the current offset and the point in front of the current
		//offset are both lower, and the point meets the minimum peak value, then
		//this point is further analyzed.		if(trace[offset - 1] < trace[offset] && trace[offset + 1] <= trace[offset] &&
									trace[offset] >= minPeakValue){			flag = true;
			//Makes sure there's no point nearby that is even higher.			for(int i = offset - 30; i < offset + 31; i++){
				if( i >= 0 && i < trace.length){					if(trace[offset] < trace[i])
						flag = false;
				}			}			if(flag == true){
				count = 0;				temp = 0;
				for(long i = offset - (long)minPeakWidth; i < offset; i++)
					if(i >= 0)						if(trace[(int)i] > trace[(int)i + 1])
							flag = false;					
								temp = trace[offset];
				for(long i = offset + 1; i <= offset + (long)minPeakWidth; i++)					if(i < trace.length)
						if(trace[(int)i-1] < trace[(int)i])
							flag = false;
									if(flag == true)					return offset;
			}		}
		offset++;
	}	return -1;  }
}