Searches.java
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.commons.statistics.inference;
- import java.util.function.IntToDoubleFunction;
- /**
- * Search utility methods.
- *
- * @since 1.1
- */
- final class Searches {
- /** Range threshold to use a binary search.
- * The binary search takes O(log(n)) so is used when n is large and a sequential
- * search is slower. */
- private static final int BINARY_SEARCH = 8;
- /** No instances. */
- private Searches() {}
- /**
- * Conduct a search between {@code a} inclusive and {@code b} inclusive
- * to find the lowest index where {@code value <= x}. The values must be
- * in <em>descending</em> order. The method is functionally equivalent to:
- * <pre>
- * {@code
- * i = b + 1
- * while (i > a AND value(i - 1) <= x)
- * i = i - 1
- * return i
- * }</pre>
- *
- * <p>The function is only evaluated between the closed interval {@code [a, b]}.
- * Special cases:
- * <ul>
- * <li>If {@code value(a) <= x} the returned index is {@code a}.
- * <li>If {@code value(b) > x} the returned index is {@code b + 1}.
- * </ul>
- *
- * @param a Lower limit (inclusive).
- * @param b Upper limit (inclusive).
- * @param x Target value.
- * @param value Function to evaluate the value at an index.
- * @return the minimum index where {@code value(i) <= x}.
- */
- static int searchDescending(int a, int b, double x, IntToDoubleFunction value) {
- // Re-use the search for ascending order.
- // Invert the index to find the lowest for the descending order.
- final int offset = a + b;
- return offset - searchAscending(a, b, x, i -> value.applyAsDouble(offset - i));
- }
- /**
- * Conduct a search between {@code a} inclusive and {@code b} inclusive
- * to find the highest index where {@code value <= x}. The values must be
- * in <em>ascending</em> order. The method is functionally equivalent to:
- * <pre>
- * {@code
- * i = a - 1
- * while (i < b AND value(i + 1) <= x)
- * i = i + 1
- * return i
- * }</pre>
- *
- * <p>The function is only evaluated between the closed interval {@code [a, b]}.
- * Special cases:
- * <ul>
- * <li>If {@code value(a) > x} the returned index is {@code a - 1}.
- * <li>If {@code value(b) <= x} the returned index is {@code b}.
- * </ul>
- *
- * @param a Lower limit (inclusive).
- * @param b Upper limit (inclusive).
- * @param x Target value.
- * @param value Function to evaluate the value at an index.
- * @return the maximum index where {@code value(i) <= x}.
- */
- static int searchAscending(int a, int b, double x, IntToDoubleFunction value) {
- // Use a binary search for a large range.
- if (b - a > BINARY_SEARCH) {
- // Edge case as the search never evaluates the end points.
- if (value.applyAsDouble(a) > x) {
- return a - 1;
- }
- if (value.applyAsDouble(b) <= x) {
- return b;
- }
- // value(lo) is always <= x
- // value(hi) is always > x
- int lo = a;
- int hi = b;
- while (lo + 1 < hi) {
- final int mid = (lo + hi) >>> 1;
- if (value.applyAsDouble(mid) <= x) {
- lo = mid;
- } else {
- hi = mid;
- }
- }
- return lo;
- }
- // Sequential search
- int i = a - 1;
- // Evaluate between [a, b]
- while (i < b && value.applyAsDouble(i + 1) <= x) {
- i++;
- }
- return i;
- }
- }