// Decompiled by Jad v1.5.8e. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/kpdus/jad.html
// Decompiler options: packimports(3) fieldsfirst 
// Source File Name:   Unt.java

package lt.ktu.gmj.methods;

import lt.ktu.gmj.*;

// Referenced classes of package lt.ktu.gmj.methods:
//			UntMethodBase, RandomPoint

public class Unt extends UntMethodBase
{

	private static final int I100 = 100;
	private static final int I70 = 70;
	private static final int I40 = 40;
	private static final int I10 = 10;
	private static final int I7 = 7;
	private static final int I5 = 5;
	private static final int I3 = 3;
	private static final int K5 = 5;
	private static final double HALF = 0.5D;
	private int n_of_points;
	private int ls[];
	private int ks[];
	private int nx[][];
	private double am[];
	private double ams[];
	private double graff[];
	private double del[];
	private double z[];
	private double work[];
	private double fs[];
	private double x1[];
	private double x2[];
	private double xs[][];
	private double a2[][];
	private int nl[];
	private int nh[];
	private double ab[];

	public Unt()
	{
		super.iterations = 500;
		super.initialPoints = 30;
		n_of_points = 0;
	}

	private Point MakePoint(double x[], Domain domain)
	{
		Point pt = new Point(domain);
		pt.x = new double[x.length];
		System.arraycopy(x, 0, pt.x, 0, pt.x.length);
		return pt;
	}

	private int array_maximum(double x[])
	{
		if(x.length == 0)
			return 0;
		double max_val = x[0];
		int index = 0;
		for(int i = 1; i < x.length; i++)
			if(x[i] > max_val)
			{
				max_val = x[i];
				index = i;
			}

		return index;
	}

	private int array_minimum(double x[])
	{
		if(x.length == 0)
			return 0;
		double min_val = x[0];
		int index = 0;
		for(int i = 1; i < x.length; i++)
			if(x[i] < min_val)
			{
				min_val = x[i];
				index = i;
			}

		return index;
	}

	private void dispersion(double x[], int n_of_pnts, double meandisp[])
	{
		meandisp[0] = 0.0D;
		meandisp[1] = 0.0D;
		for(int i = 0; i < n_of_pnts; i++)
		{
			meandisp[0] += x[i];
			meandisp[1] += x[i] * x[i];
		}

		meandisp[0] /= n_of_pnts;
		meandisp[1] = (meandisp[1] - (double)n_of_pnts * meandisp[0] * meandisp[0]) / (double)(n_of_pnts - 1);
	}

	private double distance(double x[], double y[])
	{
		double dist = 0.0D;
		for(int i = 0; i < x.length; i++)
		{
			double s = x[i] - y[i];
			dist += s * s;
		}

		return Math.sqrt(dist);
	}

	private double fp(double x[], int n, double ab[], int nx[][], int nl[], int n_of_pnts, Task task)
	{
		double y = task.f(MakePoint(x, task.domain())).fx();
		double rr = x[0];
		int k = 9;
		for(int i = 1; i < 10; i++)
		{
			if(rr >= ab[i])
				continue;
			k = i - 1;
			break;
		}

		nl[k]++;
		nx[k][nl[k]] = n_of_pnts + 1;
		return y;
	}

	private double fu(Point point, double zm, double yl, double smds[], int nh[], 
			double am[], double ab[], double a2[][], int kt, int n_of_pnts, double af, 
			int nl[], int nx[][], int nb, int ls[], double za, double da1[], 
			double ams[], double zi, ResultLogger logger)
	{
		double z = Math.abs(zm);
		if(kt < 3)
		{
			if(zm < 0.5D)
				smd(smds, point, nh, am, ab, a2, kt, n_of_pnts, af, nl, nx, nb, ls, za, da1, ams, zi, logger);
			smds[1] = Math.max(r1mach(3), smds[1]);
			if(z > 0.5D)
				return ((smds[0] - zi) + (za - zi) * z) / Math.sqrt(smds[1]);
			else
				return ((smds[0] + yl) - 2D * zi) / Math.sqrt(smds[1]);
		} else
		{
			smd(smds, point, nh, am, ab, a2, kt, n_of_pnts, af, nl, nx, nb, ls, za, da1, ams, zi, logger);
			return smds[0];
		}
	}

	private void graunt(Point xPnt, Domain domain, double s, double zm, double yl, double smds[], int nh[], double am[], double ab[], double a2[][], int kt, 
			int n_of_pnts, double af, int nl[], int nx[][], int nb, int ls[], 
			double za, double da1[], double ams[], double zi, Point zPnt, 
			double ff[], double del[], ResultLogger logger)
	{
		double y = fu(xPnt, zm, yl, smds, nh, am, ab, a2, kt, n_of_pnts, af, nl, nx, nb, ls, za, da1, ams, zi, logger);
		double eps = 0.029999999999999999D * s;
		kt++;
		zPnt = new Point(xPnt);
label0:
		do
		{
			for(int i = 0; i < xPnt.x.length; i++)
			{
				double x1 = xPnt.x[i];
				double d1 = del[i];
				if(x1 > domain.max[i] - eps || x1 < domain.min[i] + eps)
				{
					ff[i] = 0.0D;
				} else
				{
					zPnt.x[i] = x1 + d1;
					double f1 = fu(zPnt, zm, yl, smds, nh, am, ab, a2, kt, n_of_pnts, af, nl, nx, nb, ls, za, da1, ams, zi, logger);
					zPnt.x[i] = x1 - d1;
					double f2 = fu(zPnt, zm, yl, smds, nh, am, ab, a2, kt, n_of_pnts, af, nl, nx, nb, ls, za, da1, ams, zi, logger);
					ff[i] = (f1 - f2) / (2D * d1);
					zPnt.x[i] = x1;
				}
			}

			double sg = MakePoint(ff, domain).norma();
			if(sg < r1mach(3))
				return;
			do
			{
				double dx1 = s / sg;
				for(int i = 0; i < zPnt.x.length; i++)
				{
					double dx = dx1 * ff[i];
					zPnt.x[i] = increment(xPnt.x[i], domain.min[i], domain.max[i], -dx);
				}

				kt--;
				double f1 = fu(zPnt, zm, yl, smds, nh, am, ab, a2, kt, n_of_pnts, af, nl, nx, nb, ls, za, da1, ams, zi, logger);
				kt++;
				if(f1 < y)
				{
					y = f1;
					xPnt = new Point(zPnt);
					continue label0;
				}
				s *= 0.5D;
			} while(s >= eps);
			return;
		} while(true);
	}

	protected double increment(double x, double a, double b, double dx)
	{
		x += dx;
		if(dx > 0.0D)
			if(x > b)
				return b;
			else
				return x;
		if(x < a)
			return a;
		else
			return x;
	}

	private void integer_array_fill(int dest[], int dim, int sample)
	{
		while(dim-- > 0) 
			dest[dim] = sample;
	}

	private double norm(Point p1, Point p2, double am[])
	{
		double r = 0.0D;
		for(int i = 0; i < p2.x.length; i++)
		{
			double t = p1.x[i] - p2.x[i];
			r += am[i] * t * t;
		}

		return r;
	}

	private static final double r1mach(int i)
	{
		switch(i)
		{
		case 1: // '\001'
			return 4.9406564584124654E-324D;

		case 2: // '\002'
			return 1.7976931348623157E+308D;

		case 3: // '\003'
		case 4: // '\004'
			return 1E-08D;
		}
		return 0.0D;
	}

	private void random_point(double x[], int xs, double a[], int as, double b[], int bs, int n)
	{
		for(int i = 0; i < n; i++)
			x[i + xs] = Math.random() * (b[i + bs] - a[i + as]) + a[i + as];

	}

	public Result run(ResultLogger logger, Task task)
	{
		Domain domain = task.domain();
		//Result result = new Result();
		Result result = new Result(0, new Point(task.domain()), new SimpleResult(0));
		int n = domain.dimensions().length;
		ks = new int[5];
		nh = new int[5];
		nl = new int[10];
		ab = new double[10];
		ls = new int[super.iterations];
		nx = new int[10][100];
		xs = new double[5][n];
		am = new double[n];
		ams = new double[n];
		graff = new double[n];
		del = new double[n];
		z = new double[n];
		work = new double[super.initialPoints];
		x1 = new double[n];
		x2 = new double[n];
		fs = new double[Math.min(super.initialPoints / 5, 10)];
		a2 = new double[5][n];
		int kau = 0;
		double fi[] = new double[1];
		double dal[] = new double[2];
		double smds[] = new double[2];
		integer_array_fill(ls, super.iterations, 0);
		double s2 = distance(domain.min, domain.max) * 0.5D;
		for(int i = 0; i < domain.dimensions().length; i++)
			del[i] = (domain.max[i] - domain.min[i]) * 0.0001D;

		double af = (domain.max[0] - domain.min[0]) / 10D;
		integer_array_fill(nl, 10, -1);
		for(int i = 0; i < 10; i++)
			ab[i] = domain.min[0] + (double)i * af;

		for(n_of_points = 0; n_of_points < super.initialPoints; n_of_points++)
		{
			int m = n_of_points % 10;
			fi[0] = ab[m] + af;
			random_point(z, 0, ab, m, fi, 0, 1);
			random_point(z, 1, domain.min, 1, domain.max, 1, n - 1);
			Result r = new Result(n_of_points, new Point(MakePoint(z, domain)), new SimpleResult(fp(z, n, ab, nx, nl, n_of_points, task)));
			logger.log(r);
			if(r.value.fx() < result.value.fx())
				result = r;
		}

		af *= 0.5D;
		int k = Math.min(super.initialPoints / 5, 10);
		if(k == 0)
		{
			task.domain().defaultPoint = result.point;
			return result;
		}
		for(int i = 0; i < k; i++)
			fs[i] = logger.logAt(i).value.fx();

		int n1 = array_minimum(fs);
		double za = fs[n1];
		for(int i = k; i < super.initialPoints; i++)
		{
			fi[0] = logger.logAt(i).value.fx();
			if(fi[0] > za)
			{
				fs[n1] = fi[0];
				n1 = array_minimum(fs);
				za = fs[n1];
			}
		}

		fi[0] = Math.abs(result.value.fx() - za);
		if(fi[0] < r1mach(3) || fi[0] > Math.sqrt(r1mach(2)))
		{
			task.domain().defaultPoint = result.point;
			return result;
		}
		int kt = 1;
		vert(n, super.initialPoints, work, nh, am, ab, a2, af, nl, nx, ls, za, dal, ams, result.value.fx(), logger);
		int nb = -1;
		double yl = 0.69999999999999996D * result.value.fx() + 0.29999999999999999D * za;
		int kx = 0;
		int k4 = 3;
		double xl = 0.0D;
		int k2 = (super.iterations + super.initialPoints) / 2;
		Point x1Pnt = new Point(domain);
		Point x2Pnt = new Point(domain);
		Point workPnt = new Point(MakePoint(work, domain));
		Point xsPnt[] = new Point[5];
		for(int tmpv = 0; tmpv < 5; tmpv++)
			xsPnt[tmpv] = new Point(domain);

		for(int i = super.initialPoints; i < k2; i++)
		{
			double f3 = r1mach(2);
			double f4 = r1mach(2);
			kt = 1;
			double zm;
			Point zPnt;
			double f1;
			double f2;
			for(int m = 0; m < 40; m++)
			{
				zPnt = new RandomPoint(domain);
				zm = 0.10000000000000001D;
				f1 = fu(zPnt, zm, yl, smds, nh, am, ab, a2, kt, n_of_points, af, nl, nx, nb, ls, za, dal, ams, result.value.fx(), logger);
				zm = 1.0D;
				f2 = fu(zPnt, zm, yl, smds, nh, am, ab, a2, kt, n_of_points, af, nl, nx, nb, ls, za, dal, ams, result.value.fx(), logger);
				if(f1 < f3)
				{
					f3 = f1;
					x2Pnt = new Point(zPnt);
				}
				if(f2 < f4)
				{
					f4 = f2;
					x1Pnt = new Point(zPnt);
				}
			}

			zm = -1D;
			kt = 1;
			double s1 = s2 / Math.pow(n_of_points, 1.0D / (double)n);
			graunt(x1Pnt, domain, s1, zm, yl, smds, nh, am, ab, a2, kt, n_of_points, af, nl, nx, nb, ls, za, dal, ams, result.value.fx(), workPnt, graff, del, logger);
			Result r = new Result(n_of_points++, x1Pnt, new SimpleResult(f1 = fp(x1Pnt.x, n, ab, nx, nl, n_of_points, task)));
			logger.log(r);
			if(r.value.fx() < result.value.fx())
				result = r;
			zm = -0.10000000000000001D;
			kt = 1;
			s1 = s2 / Math.pow(n_of_points, 1.0D / (double)n);
			graunt(x2Pnt, domain, s1, zm, yl, smds, nh, am, ab, a2, kt, n_of_points, af, nl, nx, nb, ls, za, dal, ams, result.value.fx(), workPnt, graff, del, logger);
			double d1 = x1Pnt.distance(x2Pnt);
			int kxl = 0;
			if(d1 <= 0.01D * s2)
			{
				kxl = 1;
				x2Pnt = new RandomPoint(domain);
			}
			r = new Result(n_of_points++, x2Pnt, new SimpleResult(f2 = fp(x2Pnt.x, n, ab, nx, nl, n_of_points, task)));
			logger.log(r);
			if(r.value.fx() < result.value.fx())
				result = r;
			if(kxl == 1)
			{
				f2 = f1;
				x2Pnt = x1Pnt;
			}
			xl = (((double)(++kx) - 1.0D) * xl + f2) / (double)kx;
			if(k % 10 == 9)
				yl = xl;
			n1 = -1;
			for(int m = 0; m < 5; m++)
			{
				i = nh[m];
				ls[i]++;
				if(ls[i] == 7)
				{
					fi[0] = logger.logAt(i < n_of_points ? i : n_of_points - 1).value.fx();
					if(n1 == -1)
					{
						f3 = fi[0];
						n1 = i;
					} else
					if(fi[0] < f3)
					{
						f3 = fi[0];
						n1 = i;
					}
				}
			}

			if(n1 == -1)
				continue;
			if(f2 > f3)
				zPnt = logger.logAt(n1 < n_of_points ? n1 : n_of_points - 1).point;
			else
				zPnt = x2Pnt;
			kt = 3;
			graunt(zPnt, domain, s1, zm, yl, smds, nh, am, ab, a2, kt, n_of_points, af, nl, nx, nb, ls, za, dal, ams, result.value.fx(), workPnt, graff, del, logger);
			n1 = nh[0];
			fi[0] = logger.logAt(n1 < n_of_points ? n1 : n_of_points - 1).value.fx();
			ls[n1] = 7;
			for(int m = 1; m < 5; m++)
			{
				i = nh[m];
				double rr = logger.logAt(i < n_of_points ? i : n_of_points - 1).value.fx();
				if(rr < fi[0])
				{
					n1 = i;
					fi[0] = rr;
				}
				ls[i] = 7;
			}

			zPnt = logger.logAt(n1 < n_of_points ? n1 : n_of_points - 1).point;
			if(kau != 0)
			{
				for(i = 0; i < kau; i++)
				{
					double rr = norm(xsPnt[i], x1Pnt, am);
					if(rr >= 0.040000000000000001D)
						continue;
					rr = fs[i < fs.length ? i : fs.length - 1];
					if(fi[0] < rr)
					{
						xsPnt[i] = zPnt;
						fs[i < fs.length ? i : fs.length - 1] = fi[0];
					}
					n1 = ks[i];
					if(n1 >= k4)
					{
						task.domain().defaultPoint = result.point;
						return result;
					}
					ks[i] = n1 + 1;
					break;
				}

				if(n1 < k4)
					continue;
			}
			ks[kau] = 1;
			fs[kau < fs.length ? kau : fs.length - 1] = fi[0];
			xsPnt[kau] = zPnt;
			if(++kau >= 5)
			{
				task.domain().defaultPoint = result.point;
				return result;
			}
		}

		task.domain().defaultPoint = result.point;
		return result;
	}

	private void smd(double smds[], Point point, int nh[], double am[], double ab[], double a2[][], int kt, 
			int n_of_pnts, double af, int nl[], int nx[][], int nb, int ls[], 
			double za, double dal[], double ams[], double zi, ResultLogger logger)
	{
		double at[] = new double[5];
		double an[] = new double[5];
		if(kt % 2 == 0)
		{
			for(int i = 0; i < 5; i++)
				at[i] = norm(point, logger.logAt(nh[i] < n_of_points ? nh[i] : n_of_points - 1).point, am);

		} else
		if(n_of_pnts > 70)
		{
			double rr = point.x[0];
			int k = 8;
			for(int i = 1; i < 10; i++)
			{
				if(rr >= ab[i])
					continue;
				k = i - 1;
				if(k != 0 && rr < ab[k] + af)
					k--;
				break;
			}

			int ks = -1;
			int kr = 1;
			int kn = nl[k];
			for(int i = 0; i < 5; i++)
			{
				int n2;
				do
				{
					if(ks >= kn)
					{
						ks = -1;
						kr = 2;
						k++;
						kn = nl[k];
					}
					ks++;
					n2 = nx[k][ks];
				} while(n2 == nb);
				nh[i] = n2;
				at[i] = norm(point, logger.logAt(n2 < n_of_points ? n2 : n_of_points - 1).point, am);
			}

			int m = array_maximum(at);
			double rm = at[m];
			while(kr != 2 || ks != kn) 
			{
				if(ks >= kn)
				{
					ks = -1;
					kr = 2;
					k++;
					kn = nl[k];
				}
				ks++;
				int n2 = nx[k][ks];
				if(n2 != nb)
				{
					rr = norm(point, logger.logAt(n2 < n_of_points ? n2 : n_of_points - 1).point, am);
					if(rr < rm)
					{
						nh[m] = n2;
						at[m] = rr;
						m = array_maximum(at);
						rm = at[m];
					}
				}
			}
		} else
		{
			int n2 = 0;
			for(int i = 0; i < 5; i++)
			{
				if(n2 == nb)
					n2++;
				nh[i] = n2;
				at[i] = norm(point, logger.logAt(n2 < n_of_points ? n2 : n_of_points - 1).point, am);
				n2++;
			}

			int m = array_maximum(at);
			double rm = at[m];
			for(int i = n2; i < n_of_pnts; i++)
				if(i != nb)
				{
					double rr = norm(point, logger.logAt(i < n_of_points ? i : n_of_points - 1).point, am);
					if(rr < rm)
					{
						nh[m] = i;
						at[m] = rr;
						m = array_maximum(at);
						rm = at[m];
					}
				}

		}
		for(int i = 0; i < 5; i++)
		{
			double rr = at[i];
			double rm = Math.sqrt(rr);
			at[i] = rm;
			an[i] = Math.max(r1mach(3), Math.exp(-rr / 0.29999999999999999D) / (rm + 0.001D));
		}

		if(kt < 3)
		{
			double v;
			double v2;
			double v3;
			double s = v = v2 = v3 = 0.0D;
			for(int j = 0; j < 5; j++)
			{
				int i = nh[j];
				smds[1] = 0.0D;
				if(nb != -1 && logger.logAt(i < n_of_points ? i : n_of_points - 1).value.fx() > za)
					return;
				double rr = at[j];
				double b1 = an[j];
				s += b1 * logger.logAt(i < n_of_points ? i : n_of_points - 1).value.fx();
				if(ls[i < ls.length ? i : ls.length - 1] < 7)
				{
					v3 += b1;
					v2 += rr * b1;
				} else
				{
					double rm = rr / 5D;
					double r1 = Math.max(r1mach(3), Math.exp((-rm * rm) / 0.29999999999999999D) / (rm + 0.001D));
					v3 += r1;
					v2 += r1 * rm;
				}
				v += b1;
			}

			smds[0] = s / v;
			smds[1] = (dal[0] * v2) / v3;
		} else
		{
			double s;
			double v;
			if(kt == 3)
			{
				double rm = r1mach(3);
				for(int i = 0; i < 5; i++)
				{
					double b1 = 0.0D;
					int j = nh[i];
					double z = logger.logAt(j < n_of_points ? j : n_of_points - 1).value.fx();
					for(int l1 = 0; l1 < point.x.length; l1++)
					{
						double r3 = ams[l1];
						double r2 = logger.logAt(j < n_of_points ? j : n_of_points - 1).point.x[l1];
						s = v = 0.0D;
						for(int k = 0; k < 5; k++)
							if(k != i)
							{
								int k1 = nh[k];
								double r1 = an[k];
								double rr = norm(logger.logAt(j < n_of_points ? j : n_of_points - 1).point, logger.logAt(k1 < n_of_points ? k1 : n_of_points - 1).point, am);
								v += r1;
								s += ((logger.logAt(k1 < n_of_points ? k1 : n_of_points - 1).value.fx() - z) * (logger.logAt(k1 < n_of_points ? k1 : n_of_points - 1).point.x[l1] - r2) * r1 * r3) / rr;
							}

						a2[i][l1] = dal[1] = s / v;
						b1 += dal[1] * dal[1];
					}

					rm = Math.max(rm, b1);
				}

				dal[1] = (0.5D * (za - zi)) / Math.sqrt(rm);
			}
			s = v = 0.0D;
			for(int i = 0; i < 5; i++)
			{
				double rr = at[i];
				int j = nh[i];
				double b1 = an[i];
				double dz = 0.0D;
				for(int k = 0; k < point.x.length; k++)
					dz += a2[i][k] * (point.x[k] - logger.logAt(j < n_of_points ? j : n_of_points - 1).point.x[k]) * ams[k];

				v += b1;
				s += b1 * (logger.logAt(j < n_of_points ? j : n_of_points - 1).value.fx() + (dz * dal[1]) / (dal[1] + rr));
			}

			smds[0] = s / v;
		}
	}

	private void vert(int n, int n_of_pnts, double x[], int nh[], double am[], double ab[], double a2[][], 
			double af, int nl[], int nx[][], int ls[], double za, 
			double dal[], double ams[], double zi, ResultLogger logger)
	{
		double t_fs[] = new double[2];
		double smds[] = new double[2];
		int kt = 1;
		for(int k = 0; k < n; k++)
		{
			t_fs[0] = 0.0D;
			for(int m = 0; m < n_of_pnts; m++)
				x[m] = logger.logAt(m).point.x[k];

			dispersion(x, n_of_pnts, t_fs);
			am[k] = t_fs[1] = (double)n_of_pnts / (t_fs[1] * (double)(n_of_pnts - 1));
			ams[k] = Math.sqrt(t_fs[1]);
		}

		dal[0] = 1.0D;
		double v;
		t_fs[1] = v = 0.0D;
		for(int nb = 0; nb < n_of_pnts; nb++)
		{
			double fi = logger.logAt(nb).value.fx();
			if(fi < za)
			{
				Point point = new Point(logger.logAt(nb).point);
				smd(smds, point, nh, am, ab, a2, kt, n_of_pnts, af, nl, nx, nb, ls, za, dal, ams, zi, logger);
				t_fs[1] += smds[1] * (smds[0] - fi) * (smds[0] - fi);
				v += smds[1] * smds[1];
			}
		}

		dal[0] = t_fs[1] / v;
	}
}
