package multinomad.tools.cec2013;

import java.util.ArrayList;
import java.util.Collections;

import multinomad.config.Configuration;
import multinomad.individuals.DoubleIndividual;

public abstract class Basins {
	
	protected static double[][] basins;
	protected ArrayList<SingleBasin> listBasins;
	protected double maxRatioBasinFunction;
	protected double minRatioBasinFunction;
	protected double medianRatioBasinFunction;
	protected double percentile10RatioBasinFunction;
	protected double percentile20RatioBasinFunction;
	protected double percentile30RatioBasinFunction;
	protected double percentile40RatioBasinFunction;
	
	public Basins(double[][] arraybasins) {
		basins = arraybasins;
		listBasins = new ArrayList<SingleBasin>();
		int dim = basins[0].length/2;

		for (int i=0;i<basins.length;i++) {
			
			double[] lowerbounds = new double[dim];
			double[] upperbounds = new double[dim];

			for(int j=0;j<dim;j++) {
				lowerbounds[j] = basins[i][j];
				upperbounds[j] = basins[i][j+dim];
			}
			SingleBasin bas = new SingleBasin(lowerbounds, upperbounds);
			listBasins.add(bas);
		}
		
		Collections.sort(listBasins);

		minRatioBasinFunction = listBasins.get(0).getRatioBvolumeOverFvolume();
		maxRatioBasinFunction = listBasins.get(listBasins.size()-1).getRatioBvolumeOverFvolume();
		if (listBasins.size()%2 == 0) { //even
			int pos = (listBasins.size()-1)/2;
			double first = listBasins.get(pos).getRatioBvolumeOverFvolume();
			double second = listBasins.get(pos+1).getRatioBvolumeOverFvolume();
			medianRatioBasinFunction = (first + second) / 2.0;
			pos = (int)((listBasins.size()-1)*0.10);
			first = listBasins.get(pos).getRatioBvolumeOverFvolume();
			second = listBasins.get(pos+1).getRatioBvolumeOverFvolume();
			percentile10RatioBasinFunction = (first + second) / 2.0;
			pos = (int)((listBasins.size()-1)*0.20);
			first = listBasins.get(pos).getRatioBvolumeOverFvolume();
			second = listBasins.get(pos+1).getRatioBvolumeOverFvolume();
			percentile20RatioBasinFunction = (first + second) / 2.0;
			pos = (int)((listBasins.size()-1)*0.30);
			first = listBasins.get(pos).getRatioBvolumeOverFvolume();
			second = listBasins.get(pos+1).getRatioBvolumeOverFvolume();
			percentile30RatioBasinFunction = (first + second) / 2.0;
			pos = (int)((listBasins.size()-1)*0.40);
			first = listBasins.get(pos).getRatioBvolumeOverFvolume();
			second = listBasins.get(pos+1).getRatioBvolumeOverFvolume();
			percentile40RatioBasinFunction = (first + second) / 2.0;
			
		}else { //odd
			int pos = (listBasins.size()-1)/2;
			medianRatioBasinFunction = listBasins.get(pos).getRatioBvolumeOverFvolume();
			pos = (int)((listBasins.size()-1)*0.10);
			percentile10RatioBasinFunction = listBasins.get(pos).getRatioBvolumeOverFvolume();
			pos = (int)((listBasins.size()-1)*0.20);
			percentile20RatioBasinFunction = listBasins.get(pos).getRatioBvolumeOverFvolume();
			pos = (int)((listBasins.size()-1)*0.30);
			percentile30RatioBasinFunction = listBasins.get(pos).getRatioBvolumeOverFvolume();
			pos = (int)((listBasins.size()-1)*0.40);
			percentile40RatioBasinFunction = listBasins.get(pos).getRatioBvolumeOverFvolume();
		}
		
		
	}
	
	public int getNoOfSampledBasinsIn(ArrayList<DoubleIndividual> listind) {
		int counter = 0 ;
		for (DoubleIndividual ind : listind) {
			for (SingleBasin bas : listBasins) {
				if (! bas.hasBeenSampled() && bas.addIndividualConditional(ind)) {
					counter++;
				}
			}
		}
		return counter;
	}
	
	public ArrayList<DoubleIndividual> getIndividualsInBasins(){
		ArrayList<DoubleIndividual> listIndividuals = new ArrayList<>();
		for (SingleBasin bas : listBasins) {
			for (DoubleIndividual ind : bas.getIndividualsInBasin()) {
				listIndividuals.add(ind);
			}
		}
		return listIndividuals;
	}
	
	public abstract double upperNumberOfSamplesBound(double probability);
	
	public abstract double lowerNumberOfSamplesBound(double probability);
		


	public double upperProbability(int nsamples) {
		double oneMinusRatio = 1-minRatioBasinFunction;
		double powerOfN = Math.pow(oneMinusRatio, nsamples);
		double probability = Math.pow((1-powerOfN),Configuration.f.getNoOptima());
		
		return probability;
	}

	
	public double lowerProbability(int nsamples) {
		double oneMinusRatio = 1-maxRatioBasinFunction;
		double powerOfN = Math.pow(oneMinusRatio, nsamples);
		double probability = Math.pow((1-powerOfN),Configuration.f.getNoOptima());
		
		return probability;
	}
	
	public double estimateProbabilityEq1(int nsamples) {
		double result = 1;
		for (SingleBasin bas : listBasins) {
			double oneMinusRatio = 1-bas.getRatioBvolumeOverFvolume();
			double powerOfN = Math.pow(oneMinusRatio, nsamples);
			result *= (1-powerOfN);
		}
		return result;
	}
	
	public double estimateProbabilityBasedOnMedian(int nsamples) {
		double oneMinusRatio = 1-medianRatioBasinFunction;
		double powerOfN = Math.pow(oneMinusRatio, nsamples);
		double probability = Math.pow((1-powerOfN),Configuration.f.getNoOptima());
		
		return probability;
	}

	public double estimateProbabilityBasedOnPercentile10(int nsamples) {
		double oneMinusRatio = 1-percentile10RatioBasinFunction;
		double powerOfN = Math.pow(oneMinusRatio, nsamples);
		double probability = Math.pow((1-powerOfN),Configuration.f.getNoOptima());
		
		return probability;
	}

	public double estimateProbabilityBasedOnPercentile20(int nsamples) {
		double oneMinusRatio = 1-percentile20RatioBasinFunction;
		double powerOfN = Math.pow(oneMinusRatio, nsamples);
		double probability = Math.pow((1-powerOfN),Configuration.f.getNoOptima());
		
		return probability;
	}
	
	public double estimateProbabilityBasedOnPercentile30(int nsamples) {
		double oneMinusRatio = 1-percentile30RatioBasinFunction;
		double powerOfN = Math.pow(oneMinusRatio, nsamples);
		double probability = Math.pow((1-powerOfN),Configuration.f.getNoOptima());
		
		return probability;
	}
	
	public double estimateProbabilityBasedOnPercentile40(int nsamples) {
		double oneMinusRatio = 1-percentile40RatioBasinFunction;
		double powerOfN = Math.pow(oneMinusRatio, nsamples);
		double probability = Math.pow((1-powerOfN),Configuration.f.getNoOptima());
		
		return probability;
	}




}
