// Bayes.java

package lt.ktu.gmj.methods;

import lt.ktu.gmj.*;

public class Bayes extends IPMethodBase {
	static private final int NOT_SEC_POINT = 0;

	static private final int SEC_POINT = 1;

	static private final int PAR1 = 10;

	static private final int PAR2 = 50;

	static private final double VAL = 1e-6;

	static private final double COEF = 100 * 1e-16;

	static private final double RMAX = 0.5 * Double.MAX_VALUE;

	private Point x2;

	private Point mz;

	private int ix;

	public Bayes() {
		iterations = 100;
		initialPoints = 10;
	}

	public void setParameters(int _iterations, int _initialPoints) {
		iterations = _iterations;
		initialPoints = _initialPoints;
	}

	public Result run(ResultLogger logger, Task task) {
		Domain domain = task.domain();
		Result result = new Result();
		ix = NOT_SEC_POINT;

		for (int iteration = 0; iteration < initialPoints; iteration++) {
			Point pt = new LPPoint(domain, iteration);
			Result r = new Result(iteration, pt, task.f(pt));
			logger.log(r);

			if (r.value < result.value)
				result = r;
		}

		for (int iteration = initialPoints; iteration < iterations; iteration++) {
			Point pt = mig2f2(logger, task, result, iteration);
			Result r = new Result(iteration, pt, task.f(pt));
			logger.log(r);

			if (r.value < result.value)
				result = r;
		}

		return result;
	}

	private Point mig2f2(ResultLogger logger, Task task, Result result, int m) {
		Point z = null;
		Domain domain = task.domain();

		int k2 = Math.min(PAR2, Math.max(PAR1, m)) * domain.dimensions();
		double fmin = 0;
		double ffm = 0;

		if (ix == NOT_SEC_POINT) {
			x2 = new RandomPoint(domain);
			k2--;
			ix = SEC_POINT;
		}
		double f2 = fiap1(x2, logger, fmin, result, m);

		for (int k = 0; k < k2; k++) {
			mz = new RandomPoint(domain);
			double fi = fiap1(mz, logger, fmin, result, m);

			if (k == 0) {
				if (fi >= f2) {
					z = x2;
					x2 = mz;
					ffm = f2;
					fmin = f2 = fi;
				} else {
					z = mz;
					ffm = fi;
					fmin = f2;
				}
			} else {
				if (fi < f2) {
					if (fi < ffm) {
						x2 = z;
						z = mz;
						fmin = f2 = ffm;
						ffm = fi;
					} else {
						x2 = mz;
						fmin = f2 = fi;
					}
				}
			}
		}

		return z;
	}

	private double fiap1(Point x, ResultLogger logger, double fmin,
			Result result, int m) {
		double fm0 = logger.logAt(0).value;
		double fmC = result.value;
		double yk = fmC - Math.max(VAL, COEF * Math.abs(fmC));
		double fii = Math.min(RMAX, RMAX / (fm0 - yk));

		for (int j = 0; j < m; j++) {
			Result jResult = logger.logAt(j); // logger.iterationResult(j);
			double dd = x.distance(jResult.point);
			double d = dd * dd;
			double pp = jResult.value - yk;

			if (d < Math.min(RMAX, pp * fii)) {
				fii = d / pp;
				if (fii <= -fmin)
					return -fii;
			}
		}

		return -fii;
	}

}
