import java.util.Random;
import java.util.function.DoubleUnaryOperator;
import java.util.function.IntUnaryOperator;

// https://en.wikipedia.org/wiki/Ternary_search
public class TernarySearch {

	// finds maximum of strictly increasing and then strictly decreasing function
	public static double ternarySearch(DoubleUnaryOperator f, double lo, double hi) {
		for (int step = 0; step < 1000; step++) {
			double m1 = lo + (hi - lo) / 3;
			double m2 = hi - (hi - lo) / 3;
			if (f.applyAsDouble(m1) < f.applyAsDouble(m2))
				lo = m1;
			else
				hi = m2;
		}
		return (lo + hi) / 2;
	}

	// finds maximum of strictly increasing and then strictly decreasing function
	public static int ternarySearch(IntUnaryOperator f, int fromInclusive, int toInclusive) {
		int lo = fromInclusive;
		int hi = toInclusive;
		while (hi > lo + 2) {
			int m1 = lo + (hi - lo) / 3;
			int m2 = hi - (hi - lo) / 3;
			if (f.applyAsInt(m1) < f.applyAsInt(m2))
				lo = m1;
			else
				hi = m2;
		}
		int res = lo;
		for (int i = lo + 1; i <= hi; i++)
			if (f.applyAsInt(res) < f.applyAsInt(i))
				res = i;
		return res;
	}

	public static int ternarySearch2(IntUnaryOperator f, int fromInclusive, int toInclusive) {
		int lo = fromInclusive - 1;
		int hi = toInclusive;
		while (hi - lo > 1) {
			int mid = (lo + hi) >>> 1;
			if (f.applyAsInt(mid) < f.applyAsInt(mid + 1)) {
				lo = mid;
			} else {
				hi = mid;
			}
		}
		return hi;
	}

	// random tests
	public static void main(String[] args) {
		System.out.println(ternarySearch((DoubleUnaryOperator) x -> -(x - 2) * (x - 2), -10, 10));

		Random rnd = new Random(1);
		for (int step = 0; step < 1000; step++) {
			int n = rnd.nextInt(20) + 1;
			int p = rnd.nextInt(n);
			int[] a = new int[n];
			final int range = 10;
			a[p] = rnd.nextInt(range);
			for (int i = p - 1; i >= 0; i--)
				a[i] = a[i + 1] - rnd.nextInt(range) - 1;
			for (int i = p + 1; i < n; i++)
				a[i] = a[i - 1] - rnd.nextInt(range) - 1;
			int res1 = ternarySearch((IntUnaryOperator) i -> a[i], 0, a.length - 1);
			int res2 = ternarySearch2(i -> a[i], 0, a.length - 1);
			if (p != res1 || p != res2)
				throw new RuntimeException();
		}
	}
}
