package bsearch.space;

import org.nlogo.util.MersenneTwisterFast;

/** characterizes a parameter that has a continuous (floating point) range on the interval [A,B) */
public strictfp class DoubleContinuousSpec extends ParameterSpec {
	private double dMin;
	private double dMax;
	
	
	public DoubleContinuousSpec(String name, double min, double max) {
		super(name);
		this.dMin = min;
		this.dMax = max;
	}

	public double getMin()
	{
		return dMin;
	}
	public double getMax()	
	{
		return dMax;
	}
	
	public double enforceValidRange(double d)
	{
		if (d < dMin)
			return dMin;
		else if (d > dMax)
			return dMax;
		else
			return d;		
	}

	@Override
	public Double generateRandomValue(MersenneTwisterFast rng) {
		return rng.nextDouble() * (dMax - dMin) + dMin;
	}

	/**
	 * @param obj - Double - parameter value to be mutated 
	 * @param mutStrength - controls the magnitude of the Gaussian mutation.
	 * (not to be confused with the mutation-rate, which controls the likelihood of mutation).
		For example, mutStrength=0.1 corresponds to a stdDev of 10% of the parameter's range
		This means that 68% of the time, the mutation will fall within +/- 10%
		and 95% of the time, the mutation will fall within +/- 20%.
	 * @param rng - random number generator
	 * @returns the result of adding Gaussian noise to the given parameter value, clamped to this parameter's min/max range. 
	 */
	@Override
	public Double mutate(Object obj, double mutStrength, MersenneTwisterFast rng) {
		double mutStdDev = (dMax - dMin) * mutStrength;	
		return enforceValidRange((Double)obj + mutStdDev * rng.nextGaussian());
	}

	/**
	 * Since this is a continuous real-valued representation, the size of the search space
	 * becomes nearly infinite.  For now, we use the special value -1 for this case.
	 */
	@Override
	public int choiceCount()
	{
		return -1 ;
	}	

	@Override
	public String toString()
	{
		return "[ \"" + name + "\" [ " + dMin + " \"C\" " + dMax + " ]]";
	}

	@Override
	public Object getValueFromChoice(long choice, long maxNumChoices) {
		return dMin + (dMax - dMin) * choice / (maxNumChoices - 1);	
	}
	@Override
	public long getChoiceIndexFromValue(Object val, long maxNumChoices) {
		if (!(val instanceof Number))
		{
			throw new IllegalStateException("Type mismatch: can't represent a non-number using this double-valued parameter specification.");
		}
		double dVal = ((Number) val).doubleValue();
		return (long) StrictMath.round((dVal - dMin) * (maxNumChoices - 1) / (dMax - dMin));
	}
}
