class BinarySearch {

    public static <T> T find(DoubleFunction<T> pointAtValue, ToDoubleFunction<T> comparator, double lowerBound, double upperBound) {
        Preconditions.checkArgument(lowerBound <= upperBound, "lower bound " + lowerBound + " must not be greater than upper bound " + upperBound);
        
        T lowerBoundPoint = pointAtValue.apply(lowerBound);
        double lowerBoundComp = comparator.applyAsDouble(lowerBoundPoint);
        if (lowerBoundComp == 0) {
            return lowerBoundPoint;
        }
        Preconditions.checkArgument(lowerBoundComp < 0, "Invalid search parameter");

        T upperBoundPoint = pointAtValue.apply(upperBound);
        double upperBoundComp = comparator.applyAsDouble(upperBoundPoint);
        if (upperBoundComp == 0) {
            return upperBoundPoint;
        }
        Preconditions.checkArgument(upperBoundComp > 0, "Invalid search parameter");

        while (true) {
            double mid = (lowerBound + upperBound) / 2.0;
            T newPoint = pointAtValue.apply(mid);
            double comp = comparator.applyAsDouble(newPoint);
            Preconditions.checkState(mid > lowerBound && mid < upperBound);

            if (comp == 0) {
                return newPoint;
            } else if (comp < 0) {
                lowerBound = mid;
            } else {
                upperBound = mid;
            }
        }
    }

    private  BinarySearch();

}