/*
 * 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.sis.math;

import java.util.Arrays;
import org.apache.sis.util.Static;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.internal.util.DoubleDouble;

import static java.lang.Math.abs;
import static java.lang.Math.sqrt;
import static java.lang.Float.intBitsToFloat;
import static java.lang.Float.floatToIntBits;
import static java.lang.Float.floatToRawIntBits;
import static java.lang.Double.longBitsToDouble;
import static java.lang.Double.doubleToLongBits;
import static java.lang.Double.doubleToRawLongBits;
import static org.apache.sis.internal.util.Numerics.SIGN_BIT_MASK;
import static org.apache.sis.internal.util.Numerics.SIGNIFICAND_SIZE;


/**
 * Simple mathematical functions in addition to the ones provided in {@link Math}.
 * Some methods in this class are very similar to the standard {@link Math} methods
 * or could be implemented with straightforward formulas.
 * However the methods in this class put an emphasis on:
 *
 * <ul>
 *   <li>Rounding errors:
 *       {@link #magnitude(double[]) magnitude},
 *       {@link #pow10(double) pow10}.</li>
 *   <li>Distinguishing positive zero from negative zero:
 *       {@link #isPositive(double) isPositive},
 *       {@link #isNegative(double) isNegative},
 *       {@link #isSameSign(double, double) isSameSign},
 *       {@link #xorSign(double, double) xorSign}.</li>
 *   <li>Distinguishing the different kinds of NaN numbers:
 *       {@link #toNanFloat(int) toNanFloat},
 *       {@link #toNanOrdinal(float) toNanOrdinal}.</li>
 * </ul>
 *
 * Some additional functions not found in {@code Math} are:
 * {@link #atanh(double) atanh},
 * {@link #nextPrimeNumber(int) nextPrimeNumber}.
 *
 * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
 * @author  Johann Sorel (Geomatys)
 * @version 1.0
 *
 * @see DecimalFunctions
 * @see org.apache.sis.util.Numbers
 *
 * @since 0.3
 * @module
 */
public final class MathFunctions extends Static {
    /**
     * The square root of 2, which is approximated by {@value}.
     *
     * @see Math#sqrt(double)
     */
    public static final double SQRT_2 = 1.4142135623730951;

    /**
     * The logarithm of 2 in base 10, which is approximated by {@value}.
     * This constant is useful for converting a power of 2 to a power of 10 as below:
     *
     * {@preformat java
     *   double exp10 = exp2 * LOG10_2;
     * }
     *
     * @see Math#log10(double)
     * @see #getExponent(double)
     *
     * @since 0.4
     */
    public static final double LOG10_2 = 0.3010299956639812;

    /**
     * The minimal ordinal value for {@code NaN} numbers created by {@link #toNanFloat(int)}.
     * The current value is {@value}.
     *
     * @since 1.0
     */
    public static final int MIN_NAN_ORDINAL = -0x200000;

    /**
     * The maximal ordinal value for {@code NaN} numbers created by {@link #toNanFloat(int)}.
     * The current value is {@value}.
     *
     * @since 1.0
     */
    public static final int MAX_NAN_ORDINAL =  0x1FFFFF;

    /**
     * The beginning of ranges of quiet NaN values.
     * The range is selected in way to restrict ourselves to <cite>quiet</cite> NaN values.
     * The following is an adaptation of evaluator's comments for bug #4471414
     * (http://developer.java.sun.com/developer/bugParade/bugs/4471414.html):
     *
     * <blockquote>
     *    There are actually two types of NaNs, signaling NaNs and quiet NaNs. Java doesn't support the features necessary
     *    to reliably distinguish the two. However, the relevant point is that copying a signaling NaN may (or may not, at
     *    the implementers discretion) yield a quiet NaN — a NaN with a different bit pattern (IEEE 754 6.2). Therefore, on
     *    IEEE 754 compliant platforms it may be impossible to find a signaling NaN stored in an array since a signaling NaN
     *    passed as an argument to binarySearch may get replaced by a quiet NaN.
     * </blockquote>
     *
     * The relevant thresholds are:
     * <ul>
     *   <li>{@code 7F800000}: positive infinity.</li>
     *   <li>{@code 7F800001}: first signaling NaN in the range of positive values.</li>
     *   <li>{@code 7FBFFFFF}: last signaling NaN.</li>
     *   <li>{@code 7FC00000}: first quiet NaN. Also the standard {@link Double#NaN} value.</li>
     *   <li>{@code 7FFFFFFF}: last quiet NaN.</li>
     *   <li>{@code FF800000}: negative infinity.</li>
     *   <li>{@code FF800001}: first signaling NaN in the range of negative values.</li>
     *   <li>{@code FFBFFFFF}: last signaling NaN.</li>
     *   <li>{@code FFC00000}: first quiet NaN in the range of negative values.</li>
     *   <li>{@code FFFFFFFF}: last quiet NaN.</li>
     * </ul>
     *
     * @see #toNanFloat(int)
     * @see #toNanOrdinal(float)
     */
    static final int POSITIVE_NAN = 0x7FC00000,
                     NEGATIVE_NAN = 0xFFC00000;

    /**
     * The highest prime number supported by the {@link #nextPrimeNumber(int)} method.
     * In the current implementation, this value is {@value}. However this limit may
     * change in any future Apache SIS version.
     *
     * <div class="note"><b>Note:</b>
     * The current value is the highest prime number representable as an unsigned 16 bits integer.
     * This is enough for current needs because 16 bits prime numbers are sufficient for finding
     * the divisors of any 32 bits integers.</div>
     *
     * @see #nextPrimeNumber(int)
     */
    public static final int HIGHEST_SUPPORTED_PRIME_NUMBER = 65521;

    /**
     * Maximal length needed for the {@link #primes} array in order to store prime numbers
     * from 2 to 32749 (15 bits) or {@value #HIGHEST_SUPPORTED_PRIME_NUMBER} (16 bits).
     *
     * @see #primeNumberAt(int)
     */
    static final int PRIMES_LENGTH_15_BITS = 3512,
                     PRIMES_LENGTH_16_BITS = 6542;

    /**
     * The sequence of prime numbers computed so far. Will be expanded as needed.
     * We limit ourself to 16 bits numbers because they are sufficient for computing
     * divisors of any 32 bits number.
     *
     * @see #primeNumberAt(int)
     */
    @SuppressWarnings("VolatileArrayField")     // Because we will not modify array content.
    private static volatile short[] primes = new short[] {2, 3};

    /**
     * Do not allow instantiation of this class.
     */
    private MathFunctions() {
    }

    /**
     * Truncates the given value toward zero. Invoking this method is equivalent to invoking
     * {@link Math#floor(double)} if the value is positive, or {@link Math#ceil(double)} if
     * the value is negative.
     *
     * @param  value  the value to truncate.
     * @return the largest in magnitude (further from zero) integer value which is equals
     *         or less in magnitude than the given value.
     */
    public static double truncate(final double value) {
        return (doubleToRawLongBits(value) & SIGN_BIT_MASK) == 0 ? Math.floor(value) : Math.ceil(value);
    }

    /**
     * Returns the magnitude of the given vector. This is defined by:
     *
     * {@preformat math
     *     sqrt(vector[0]² + vector[1]² + … + vector[length-1]²)
     * }
     *
     * If the given vector contains a NaN value, then the result is NaN.
     *
     * <div class="section">Implementation note</div>
     * In the special case where only one element is different than zero, this method
     * returns directly the {@linkplain Math#abs(double) absolute value} of that element
     * without computing {@code sqrt(v²)}, in order to avoid rounding error. This special case
     * has been implemented because this method is often invoked for computing the length of
     * offset vectors,
     * typically aligned with the axes of a {@linkplain org.opengis.referencing.cs.CartesianCS
     * Cartesian coordinate system}.
     *
     * @param  vector  the vector for which to compute the magnitude.
     * @return the magnitude of the given vector as a positive number, of NaN.
     *
     * @see Math#hypot(double, double)
     */
    public static double magnitude(final double... vector) {
        int i = vector.length;

        // If every elements in the array are zero, returns zero.
        double v1;
        do if (i == 0) return 0;
        while ((v1 = vector[--i]) == 0);

        // We have found a non-zero element. If it is the only one, returns its absolute value.
        double v2;
        do if (i == 0) return abs(v1);
        while ((v2 = vector[--i]) == 0);

        // If there is exactly 2 elements, use Math.hypot which is more robust than our algorithm.
        double v3;
        do if (i == 0) return Math.hypot(v1, v2);
        while ((v3 = vector[--i]) == 0);

        // Usual magnitude computation, but using double-double arithmetic.
        final DoubleDouble sum = new DoubleDouble();
        final DoubleDouble dot = new DoubleDouble();
        sum.setToProduct(v1, v1);
        dot.setToProduct(v2, v2); sum.add(dot);
        dot.setToProduct(v3, v3); sum.add(dot);
        while (i != 0) {
            v1 = vector[--i];
            dot.setToProduct(v1, v1);
            sum.add(dot);
        }
        sum.sqrt();
        return sum.doubleValue();
    }

    /**
     * Returns the unbiased exponent used in the representation of a {@code double}, with correction for
     * sub-normal numbers. This method is related to {@link Math#getExponent(double)} in the following ways:
     *
     * <ul>
     *   <li>For NaN and all values equal or greater than {@link Double#MIN_NORMAL} in magnitude (including
     *       infinities), this method returns results that are identical to {@code Math.getExponent(double)}.</li>
     *   <li>For values smaller than {@link Double#MIN_NORMAL} in magnitude (including zero), the correction
     *       for sub-normal numbers results in return values smaller than what {@code Math.getExponent(double)}
     *       would return.</li>
     * </ul>
     *
     * Special cases:
     * <ul>
     *   <li>If the argument is NaN or infinite, then the result is {@link Double#MAX_EXPONENT} + 1.</li>
     *   <li>If the argument is {@link Double#MAX_VALUE},  then the result is {@value java.lang.Double#MAX_EXPONENT}.</li>
     *   <li>If the argument is {@link Double#MIN_NORMAL}, then the result is {@value java.lang.Double#MIN_EXPONENT}.</li>
     *   <li>If the argument is {@link Double#MIN_VALUE},  then the result is -1074.</li>
     *   <li>If the argument is zero, then the result is -1075.</li>
     * </ul>
     *
     * <div class="section">Identities</div>
     * For any <var>p</var> values in the [-1075 … 1024] range and <var>value</var> = 2<sup>p</sup>:
     * <ul>
     *   <li><code>getExponent(Math.scalb(1.0, p)) == p</code></li>
     *   <li><code>Math.scalb(1.0, getExponent(value)) == value</code></li>
     *   <li><code>Math.floor({@linkplain #LOG10_2} * getExponent(value)) == Math.floor(Math.log10(value))</code></li>
     * </ul>
     *
     * @param  value  the value for which to get the exponent.
     * @return the unbiased exponent, corrected for sub-normal numbers if needed.
     *         Values will be in the [-1075 … 1024] range, inclusive.
     *
     * @see Math#getExponent(double)
     * @see Math#scalb(double, int)
     *
     * @since 0.4
     */
    public static int getExponent(final double value) {
        final long bits = doubleToRawLongBits(value);
        int exponent = (int) ((bits >>> SIGNIFICAND_SIZE) & 0x7FFL);
        if (exponent == 0) {
            /*
             * Number is sub-normal: there is no implicit 1 bit before the significand.
             * We need to search for the position of the first real 1 bit, and fix the
             * exponent accordingly.  Note that numberOfLeadingZeros(…) is relative to
             * 64 bits while the significand size is only 52 bits. The last term below
             * is for fixing this difference.
             */
            exponent -= Long.numberOfLeadingZeros(bits & ((1L << SIGNIFICAND_SIZE) - 1)) - (Long.SIZE - SIGNIFICAND_SIZE);
        }
        return exponent - Double.MAX_EXPONENT;
    }

    /**
     * Computes the result of {@code base} argument raised to the power given by {@code exponent} argument.
     * This method computes the same value than {@link Math#pow(double, double)} but using only integer arithmetic.
     * The result must be representable as a 64 bits integers ({@code long} primitive type),
     * otherwise an {@link ArithmeticException} is thrown. The result is guaranteed exact,
     * in contrast to results represented as {@code double} floating point values
     * which may be approximate for magnitudes greater than 2<sup>52</sup>.
     * This method may also be faster.
     *
     * <div class="note"><b>Implementation note:</b> this method uses
     * <a href="https://en.wikipedia.org/wiki/Exponentiation_by_squaring">exponentiation by squaring</a> technic.</div>
     *
     * The type of the {@code base} argument is {@code long} for convenience, since this method is used in contexts
     * where relatively large integers are handled. However any value greater than the capacity of {@code int} type
     * is guaranteed to fail with {@link ArithmeticException} unless {@code exponent} is 0 or 1.
     * Likewise any {@code exponent} value greater than 62 is guaranteed to fail unless {@code base} is 0 or 1.
     *
     * @param  base      the value to raise to an exponent.
     * @param  exponent  the exponent, as zero or positive number.
     * @return the value <var>base</var><sup><var>exponent</var></sup> as a 64 bits integer.
     * @throws ArithmeticException if the given exponent is negative, or if the result overflow integer arithmetic.
     *
     * @see Math#pow(double, double)
     *
     * @since 1.0
     */
    public static long pow(long base, int exponent) {
        long result = 1;
        if (exponent >= 1) {
            if ((exponent & 1) != 0) {
                result = base;
            }
            while ((exponent >>>= 1) != 0) {
                base = Math.multiplyExact(base, base);
                if ((exponent & 1) != 0) {
                    result = Math.multiplyExact(result, base);
                }
            }
        } else if (exponent < 0) {
            throw new ArithmeticException(Errors.format(Errors.Keys.NegativeArgument_2, "exponent", exponent));
        }
        return result;
    }

    /**
     * Computes 10 raised to the power of <var>x</var>. This method is faster and slightly more accurate
     * than invoking <code>{@linkplain Math#pow(double, double) Math.pow}(10, x)</code>.
     *
     * <div class="note"><b>Note:</b>
     * This method has been defined because the standard {@code Math.pow(10, x)} method does not always return
     * the closest IEEE floating point representation. Slight departures (1 or 2 ULP) are often allowed in math
     * functions for performance reasons. The most accurate calculations are usually not necessary, but the base
     * 10 is a special case since it is used for scaling axes or formatting human-readable output.</div>
     *
     * Special cases:
     * <ul>
     *   <li>If <var>x</var> is equals or lower than -324, then the result is 0.</li>
     *   <li>If <var>x</var> is equals or greater than 309, then the result is {@linkplain Double#POSITIVE_INFINITY positive infinity}.</li>
     *   <li>If <var>x</var> is in the [0 … 18] range inclusive, then the result is exact.</li>
     *   <li>For all other <var>x</var> values, the result is the closest IEEE 754 approximation.</li>
     * </ul>
     *
     * @param  x  the exponent.
     * @return 10 raised to the given exponent.
     *
     * @see #pow10(double)
     * @see #LOG10_2
     * @see DecimalFunctions
     */
    public static double pow10(final int x) {
        return DecimalFunctions.pow10(x);
    }

    /**
     * Computes 10 raised to the power of <var>x</var>. Invoking this method is equivalent to invoking
     * <code>{@linkplain Math#pow(double, double) Math.pow}(10, x)</code>, but is slightly more accurate
     * in the special case where the given argument is an integer.
     *
     * @param  x  the exponent.
     * @return 10 raised to the given exponent.
     *
     * @see #pow10(int)
     * @see Math#pow(double, double)
     * @see Math#log10(double)
     */
    public static double pow10(final double x) {
        final int ix = (int) x;
        if (ix == x) {
            return DecimalFunctions.pow10(ix);
        } else {
            return Math.pow(10, x);
        }
    }

    /**
     * Returns the inverse hyperbolic sine of the given value.
     * This is the inverse of the {@link Math#sinh(double)} method.
     *
     * @param  x  the value for which to compute the inverse hyperbolic sine.
     * @return the inverse hyperbolic sine of the given value.
     *
     * @see Math#sinh(double)
     *
     * @since 0.6
     */
    public static double asinh(final double x) {
        return Math.log(x + sqrt(x*x + 1));
    }

    /**
     * Returns the inverse hyperbolic cosine of the given value.
     * This is the inverse of the {@link Math#cosh(double)} method.
     *
     * @param  x  the value for which to compute the inverse hyperbolic cosine.
     * @return the inverse hyperbolic cosine of the given value.
     *
     * @see Math#cosh(double)
     *
     * @since 0.6
     */
    public static double acosh(final double x) {
        return Math.log(x + sqrt(x*x - 1));
    }

    /**
     * Returns the inverse hyperbolic tangent of the given value.
     * This is the inverse of the {@link Math#tanh(double)} method.
     * The range of input values shall be in the [-1 … 1].
     * Special cases:
     *
     * <ul>
     *   <li>For <var>x</var> = NaN, this method returns a {@linkplain Double#isNaN(double) NaN} value.</li>
     *   <li>For <var>x</var> = -1, this method returns {@linkplain Double#NEGATIVE_INFINITY negative infinity}.</li>
     *   <li>For <var>x</var> = +1, this method returns {@linkplain Double#POSITIVE_INFINITY positive infinity}.</li>
     * </ul>
     *
     * @param  x  the value for which to compute the inverse hyperbolic tangent.
     * @return the inverse hyperbolic tangent of the given value.
     *
     * @see Math#tanh(double)
     */
    public static double atanh(final double x) {
        /*
         * The classical formulas is log((1+x)/(1-x))/2, but the following is more
         * accurate if the (1+x)/(1-x) ratio is close to 1, i.e. if x is close to 0.
         * This is often the case in Apache SIS since x is often a value close to the
         * Earth excentricity, which is a small value (0 would be a perfect sphere).
         */
        return 0.5 * Math.log1p(2*x / (1-x));
    }

    /**
     * Returns {@code true} if the given value is positive, <em>excluding</em> negative zero.
     * Special cases:
     *
     * <ul>
     *   <li>If the value is {@code +0.0}, returns {@code true}</li>
     *   <li>If the value is {@code -0.0}, returns <b>{@code false}</b></li>
     *   <li>If the value is {@link Double#isNaN(double) NaN}, returns {@code false}</li>
     * </ul>
     *
     * As seen from the above cases, this method distinguishes positive zero from negative zero.
     * The handling of zero values is the difference between invoking {@code isPositive(double)}
     * and testing if (<var>value</var> {@literal >= 0}).
     *
     * @param  value  the value to test.
     * @return {@code true} if the given value is positive, excluding negative zero.
     *
     * @see #isPositiveZero(double)
     * @see #isNegative(double)
     */
    public static boolean isPositive(final double value) {
        return (doubleToRawLongBits(value) & SIGN_BIT_MASK) == 0 && !Double.isNaN(value);
    }

    /**
     * Returns {@code true} if the given value is the positive zero ({@code +0.0}).
     * This method returns {@code false} for the negative zero ({@code -0.0}).
     * This method is equivalent to the following code, but potentially faster:
     *
     * {@preformat java
     *   return (value == 0) && isPositive(value);
     * }
     *
     * @param  value  the value to test.
     * @return {@code true} if the given value is +0.0 (not -0.0).
     *
     * @see #isPositive(double)
     * @see #isNegativeZero(double)
     *
     * @since 0.4
     */
    public static boolean isPositiveZero(final double value) {
        return doubleToRawLongBits(value) == 0L;
    }

    /**
     * Returns {@code true} if the given value is negative, <em>including</em> negative zero.
     * Special cases:
     *
     * <ul>
     *   <li>If the value is {@code +0.0}, returns {@code false}</li>
     *   <li>If the value is {@code -0.0}, returns <b>{@code true}</b></li>
     *   <li>If the value is {@link Double#isNaN(double) NaN}, returns {@code false}</li>
     * </ul>
     *
     * As seen from the above cases, this method distinguishes positive zero from negative zero.
     * The handling of zero values is the difference between invoking {@code isNegative(double)}
     * and testing if (<var>value</var> {@literal < 0}).
     *
     * @param  value  the value to test.
     * @return {@code true} if the given value is negative, including negative zero.
     *
     * @see #isNegativeZero(double)
     * @see #isPositive(double)
     */
    public static boolean isNegative(final double value) {
        return (doubleToRawLongBits(value) & SIGN_BIT_MASK) != 0 && !Double.isNaN(value);
    }

    /**
     * Returns {@code true} if the given value is the negative zero ({@code -0.0}).
     * This method returns {@code false} for the positive zero ({@code +0.0}).
     * This method is equivalent to the following code, but potentially faster:
     *
     * {@preformat java
     *   return (value == 0) && isNegative(value);
     * }
     *
     * @param  value  the value to test.
     * @return {@code true} if the given value is -0.0 (not +0.0).
     *
     * @see #isNegative(double)
     * @see #isPositiveZero(double)
     *
     * @since 0.4
     */
    public static boolean isNegativeZero(final double value) {
        return doubleToRawLongBits(value) == SIGN_BIT_MASK;
    }

    /**
     * Returns {@code true} if the given values have the same sign, differentiating positive
     * and negative zeros.
     * Special cases:
     *
     * <ul>
     *   <li>{@code +0.0} and {@code -0.0} are considered to have opposite sign</li>
     *   <li>If any value is {@link Double#isNaN(double) NaN}, returns {@code false}</li>
     * </ul>
     *
     * @param  v1  the first value.
     * @param  v2  the second value, to compare the sign with the first value.
     * @return {@code true} if the given values are not NaN and have the same sign.
     *
     * @see Math#signum(double)
     */
    public static boolean isSameSign(final double v1, final double v2) {
        return !Double.isNaN(v1) && !Double.isNaN(v2) &&
                ((doubleToRawLongBits(v1) ^ doubleToRawLongBits(v2)) & SIGN_BIT_MASK) == 0;
    }

    /**
     * Returns the first floating-point argument with the sign reversed if the second floating-point
     * argument is negative. This method is similar to <code>{@linkplain Math#copySign(double,double)
     * Math.copySign}(value, sign)</code> except that the sign is combined with an <cite>exclusive
     * or</cite> operation instead than being copied.
     *
     * <p>This method makes no guarantee about whether {@code NaN} values are handled as positive
     * or negative numbers. This is the same policy than {@link Math#copySign(double, double)}.</p>
     *
     * @param  value  the parameter providing the value that may need a sign change.
     * @param  sign   the parameter providing the sign to <cite>xor</cite> with the value.
     * @return the provided value with its sign reversed if the {@code sign} parameter is negative.
     *
     * @see Math#copySign(double, double)
     */
    public static double xorSign(final double value, final double sign) {
        return longBitsToDouble(doubleToRawLongBits(value) ^
                (doubleToRawLongBits(sign) & SIGN_BIT_MASK));
    }

    /**
     * Returns {@code true} if the given values are {@linkplain Float#equals(Object) equal}
     * or if their difference is not greater than the given threshold. More specifically:
     *
     * <ul>
     *   <li>If both values are {@linkplain Float#POSITIVE_INFINITY positive infinity}, or
     *       if both values are {@linkplain Float#NEGATIVE_INFINITY negative infinity},
     *       then this method returns {@code true}.</li>
     *   <li>If both values {@linkplain Float#isNaN(float) are NaN}, then this method returns {@code true}.
     *       Note that this method does not differentiate the various NaN values.</li>
     *   <li>Otherwise, this method returns the result of the {@code abs(v1 - v2) <= ε} comparison.</li>
     * </ul>
     *
     * @param  v1  the first value to compare.
     * @param  v2  the second value to compare.
     * @param  ε   the tolerance threshold, which must be positive.
     * @return {@code true} if both values are equal given the tolerance threshold.
     */
    public static boolean epsilonEqual(final float v1, final float v2, final float ε) {
        return (abs(v1 - v2) <= ε) || floatToIntBits(v1) == floatToIntBits(v2);
    }

    /**
     * Returns {@code true} if the given values are {@linkplain Double#equals(Object) equal}
     * or if their difference is not greater than the given threshold. More specifically:
     *
     * <ul>
     *   <li>If both values are {@linkplain Double#POSITIVE_INFINITY positive infinity}, or
     *       if both values are {@linkplain Double#NEGATIVE_INFINITY negative infinity},
     *       then this method returns {@code true}.</li>
     *   <li>If both values {@linkplain Double#isNaN(double) are NaN}, then this method returns {@code true}.
     *       Note that this method does not differentiate the various NaN values.</li>
     *   <li>Otherwise, this method returns the result of the {@code abs(v1 - v2) <= ε} comparison.</li>
     * </ul>
     *
     * @param  v1  the first value to compare.
     * @param  v2  the second value to compare.
     * @param  ε   the tolerance threshold, which must be positive.
     * @return {@code true} if both values are equal given the tolerance threshold.
     */
    public static boolean epsilonEqual(final double v1, final double v2, final double ε) {
        return (abs(v1 - v2) <= ε) || doubleToLongBits(v1) == doubleToLongBits(v2);
    }

    /**
     * Returns a {@linkplain Float#isNaN(float) NaN} number for the specified ordinal value.
     * Valid NaN numbers in Java can have bit fields in the ranges listed below:
     *
     * <ul>
     *   <li>[{@code 0x7F800001} … {@code 0x7FFFFFFF}], with
     *        {@code 0x7FC00000} as the bit fields of the standard {@link Float#NaN} value</li>
     *   <li>[{@code 0xFF800001} … {@code 0xFFFFFFFF}]</li>
     * </ul>
     *
     * Some of those bits, named the <cite>payload</cite>, can be used for storing custom information.
     * This method maps some of the payload values to each ordinal value.
     *
     * <p>This method guarantees that {@code toNanFloat(0)} returns the standard {@link Float#NaN} value.
     * For all other {@code ordinal} values, the relationship to the payload values is implementation dependent
     * and may change in any future version of the SIS library. The current implementation restricts the
     * range of allowed ordinal values to a smaller one than the range of all possible values.</p>
     *
     * @param  ordinal  the NaN ordinal value, from {@value #MIN_NAN_ORDINAL} to {@value #MAX_NAN_ORDINAL} inclusive.
     * @return one of the legal {@linkplain Float#isNaN(float) NaN} values as a float.
     * @throws IllegalArgumentException if the specified ordinal is out of range.
     *
     * @see Float#intBitsToFloat(int)
     */
    public static float toNanFloat(final int ordinal) throws IllegalArgumentException {
        ArgumentChecks.ensureBetween("ordinal", MIN_NAN_ORDINAL, MAX_NAN_ORDINAL, ordinal);
        int bits = (ordinal >= 0) ? ordinal : ~ordinal;
        bits = (bits + POSITIVE_NAN) | (ordinal & Integer.MIN_VALUE);
        assert Integer.compareUnsigned(bits, ordinal >= 0 ? POSITIVE_NAN : NEGATIVE_NAN) >= 0 : ordinal;
        final float value = intBitsToFloat(bits);
        assert Float.isNaN(value) && toNanOrdinal(value) == ordinal : ordinal;
        return value;
    }

    /**
     * Returns the ordinal value of the given NaN number.
     * This method is the converse of {@link #toNanFloat(int)}.
     *
     * <p>If the given float is the standard {@link Float#NaN} value, then this method returns 0.
     * For all other values, the relationship between the float payload and the returned ordinal
     * is implementation dependent and may change in any future Apache SIS version.</p>
     *
     * @param  value  the value from which to get the NaN ordinal value.
     * @return the NaN ordinal value of the given floating point value.
     * @throws IllegalArgumentException if the given value is not a NaN value,
     *         or does not use a supported bits pattern.
     */
    public static int toNanOrdinal(final float value) throws IllegalArgumentException {
        final int bits = floatToRawIntBits(value);
        int ordinal = (bits & Integer.MAX_VALUE) - POSITIVE_NAN;
        if (bits < 0) ordinal = ~ordinal;
        if (ordinal >= MIN_NAN_ORDINAL && ordinal <= MAX_NAN_ORDINAL) {
            return ordinal;
        }
        final short resourceKey;
        final Object obj;
        if (Float.isNaN(value)) {
            resourceKey = Errors.Keys.IllegalBitsPattern_1;
            obj = Integer.toHexString(bits);
        } else {
            resourceKey = Errors.Keys.IllegalArgumentValue_2;
            obj = new Object[] {"value", value};
        }
        throw new IllegalArgumentException(Errors.format(resourceKey, obj));
    }

    /**
     * Converts two long bits values containing a IEEE 754 quadruple precision floating point number
     * to a double precision floating point number. About 17 decimal digits of precision may be lost
     * due to the {@code double} type having only half the capacity of quadruple precision type.
     *
     * <p>Some quadruple precision values can not be represented in double precision and are mapped
     * to {@code double} values as below:</p>
     * <ul>
     *   <li>Values having a magnitude less than {@link Double#MIN_VALUE} are mapped to
     *       positive or negative zero.</li>
     *   <li>Values having a magnitude greater than {@link Double#MAX_VALUE} are mapped to
     *       {@link Double#POSITIVE_INFINITY} or {@link Double#NEGATIVE_INFINITY}.</li>
     *   <li>All NaN values are currently collapsed to the single "canonical" {@link Double#NaN} value
     *       (this policy may be revisited in future SIS version).</li>
     * </ul>
     *
     * @param  l0  upper part of the quadruple precision floating point number.
     * @param  l1  lower part of the quadruple precision floating point number.
     * @return double precision approximation.
     *
     * @see <a href="https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format">Quadruple-precision floating-point format on Wikipedia</a>
     *
     * @since 0.7
     */
    public static double quadrupleToDouble(long l0, long l1) {
        // Build double
        long sig = (l0 & 0x8000000000000000L);
        long exp = (l0 & 0x7FFF000000000000L) >> 48;
        l0       = (l0 & 0x0000FFFFFFFFFFFFL);
        if (exp == 0) {
            /*
             * Subnormal number.
             * Since we convert them to double precision, subnormal numbers can not be represented
             * as they are smaller than Double.MIN_VALUE. We map them to zero preserving the sign.
             */
            return Double.longBitsToDouble(sig);
        }
        if (exp == 0x7FFF) {
            /*
             * NaN of infinite number.
             * Mantissa with all bits at 0 is used for infinite.
             * This is the only special number that we can preserve.
             */
            if (l0 == 0 && l1 == 0) {
                return Double.longBitsToDouble(sig | 0x7FF0000000000000L);
            }
            /*
             * Other NaN values might have a meaning (e.g. NaN(1) = forest, NaN(2) = lake, etc.)
             * See above toNanFloat(int) and toNaNOrdinal(float) methods. When truncating the value we
             * might change the meaning, which could cause several issues later. Therefor we conservatively
             * collapse all NaNs to the default NaN for now (this may be revisited in a future SIS version).
             */
            return Double.NaN;
        }
        exp -= (16383 - 1023);      //change from 15 bias to 11 bias
        // Check cases where mantissa excess what double can support
        if (exp < 0)    return Double.NEGATIVE_INFINITY;
        if (exp > 2046) return Double.POSITIVE_INFINITY;

        return Double.longBitsToDouble(sig | (exp << 52) | (l0 << 4) | (l1 >>> 60));
    }

    /**
     * Returns the <var>i</var><sup>th</sup> prime number.
     * This method returns (2, 3, 5, 7, 11, …) for index (0, 1, 2, 3, 4, …).
     *
     * @param  index  the prime number index, starting at index 0 for prime number 2.
     * @return the prime number at the specified index.
     * @throws IndexOutOfBoundsException if the specified index is too large.
     *
     * @see java.math.BigInteger#isProbablePrime(int)
     */
    static int primeNumberAt(final int index) throws IndexOutOfBoundsException {
        ArgumentChecks.ensureValidIndex(PRIMES_LENGTH_16_BITS, index);
        short[] primes = MathFunctions.primes;
        if (index >= primes.length) {
            synchronized (MathFunctions.class) {
                primes = MathFunctions.primes;
                if (index >= primes.length) {
                    int i = primes.length;
                    int n = Short.toUnsignedInt(primes[i - 1]);
                    // Compute by block of 16 values, for reducing the amount of array resize.
                    primes = Arrays.copyOf(primes, Math.min((index | 0xF) + 1, PRIMES_LENGTH_16_BITS));
                    do {
testNextNumber:         while (true) {      // Simulate a "goto" statement (usually not recommanded...)
                            final int stopAt = (int) sqrt(n += 2);
                            int prime;
                            int j = 0;
                            do {
                                prime = Short.toUnsignedInt(primes[++j]);
                                if (n % prime == 0) {
                                    continue testNextNumber;
                                }
                            } while (prime <= stopAt);
                            primes[i] = (short) n;
                            break;
                        }
                    } while (++i < primes.length);
                    MathFunctions.primes = primes;
                }
            }
        }
        return Short.toUnsignedInt(primes[index]);
    }

    /**
     * Returns the first prime number equals or greater than the given value.
     * Current implementation accepts only values in the
     * [2 … {@value #HIGHEST_SUPPORTED_PRIME_NUMBER}] range.
     *
     * @param  number  the number for which to find the next prime.
     * @return the given number if it is a prime number, or the next prime number otherwise.
     * @throws IllegalArgumentException if the given value is outside the supported range.
     *
     * @see java.math.BigInteger#isProbablePrime(int)
     */
    public static int nextPrimeNumber(final int number) throws IllegalArgumentException {
        ArgumentChecks.ensureBetween("number", 2, HIGHEST_SUPPORTED_PRIME_NUMBER, number);
        final short[] primes = MathFunctions.primes;
        int lower = 0;
        int upper = Math.min(PRIMES_LENGTH_15_BITS, primes.length);
        if (number > Short.MAX_VALUE) {
            lower = upper;
            upper = primes.length;
        }
        int i = Arrays.binarySearch(primes, lower, upper, (short) number);
        if (i < 0) {
            i = ~i;
            if (i >= primes.length) {
                int p;
                do p = primeNumberAt(i++);
                while (p < number);
                return p;
            }
        }
        return Short.toUnsignedInt(primes[i]);
    }

    /**
     * Returns the divisors of the specified number as positive integers. For any value other
     * than {@code O} (which returns an empty array), the first element in the returned array
     * is always {@code 1} and the last element is always the absolute value of {@code number}.
     *
     * @param  number  the number for which to compute the divisors.
     * @return the divisors in strictly increasing order.
     */
    public static int[] divisors(int number) {
        if (number == 0) {
            return ArraysExt.EMPTY_INT;
        }
        number = abs(number);
        int[] divisors = new int[16];
        divisors[0] = 1;
        int count = 1;
        /*
         * Searches for the first divisors among the prime numbers. We stop the search at the
         * square root of 'n' because every values above that point can be inferred from the
         * values before that point, i.e. if n=p1*p2 and p2 is greater than 'sqrt', than p1
         * most be lower than 'sqrt'.
         */
        final int sqrt = (int) sqrt(number);               // Really want rounding toward 0.
        for (int p,i=0; (p=primeNumberAt(i)) <= sqrt; i++) {
            if (number % p == 0) {
                if (count == divisors.length) {
                    divisors = Arrays.copyOf(divisors, count*2);
                }
                divisors[count++] = p;
            }
        }
        /*
         * Completes the divisors past 'sqrt'. The numbers added here may or may not be prime
         * numbers. Side note: checking that they are prime numbers would be costly, but this
         * algorithm doesn't need that.
         */
        int source = count;
        if (count*2 > divisors.length) {
            divisors = Arrays.copyOf(divisors, count*2);
        }
        int d1 = divisors[--source];
        int d2 = number / d1;
        if (d1 != d2) {
            divisors[count++] = d2;
        }
        while (--source >= 0) {
            divisors[count++] = number / divisors[source];
        }
        /*
         * Checks the products of divisors found so far. For example if 2 and 3 are divisors,
         * checks if 6 is a divisor as well. The products found will themself be used for
         * computing new products.
         */
        for (int i=1; i<count; i++) {
            d1 = divisors[i];
            for (int j=i; j<count; j++) {
                d2 = d1 * divisors[j];
                if (number % d2 == 0) {
                    int p = Arrays.binarySearch(divisors, j, count, d2);
                    if (p < 0) {
                        p = ~p;                                 // tild (~) operator, not minus
                        if (count == divisors.length) {
                            divisors = Arrays.copyOf(divisors, count*2);
                        }
                        System.arraycopy(divisors, p, divisors, p+1, count-p);
                        divisors[p] = d2;
                        count++;
                    }
                }
            }
        }
        divisors = ArraysExt.resize(divisors, count);
        assert ArraysExt.isSorted(divisors, true);
        return divisors;
    }

    /**
     * Returns the positive divisors which are common to all the specified numbers.
     * The returned array always starts with value 1, unless the given value is 0
     * in which case this method returns an empty array.
     *
     * @param  numbers  the numbers for which to compute the divisors, in any order.
     * @return the divisors common to all the given numbers, in strictly increasing order.
     */
    public static int[] commonDivisors(final int... numbers) {
        if (numbers.length == 0) {
            return ArraysExt.EMPTY_INT;
        }
        /*
         * Get the smallest value. We will compute the divisors only for this value,
         * since we know that any value greater that the minimal value can not be a
         * common divisor.
         */
        int minValue = Integer.MAX_VALUE;
        for (int i=0; i<numbers.length; i++) {
            final int n = abs(numbers[i]);
            if (n <= minValue) {
                minValue = n;
            }
        }
        int[] divisors = divisors(minValue);
        /*
         * Tests if the divisors we just found are also divisors of all other numbers.
         * Removes those which are not.
         */
        int count = divisors.length;
        for (int i=0; i<numbers.length; i++) {
            final int n = abs(numbers[i]);
            if (n != minValue) {
                for (int j=count; --j>0;) {         // Do not test j==0, since divisors[0] ==  1.
                    if (n % divisors[j] != 0) {
                        System.arraycopy(divisors, j+1, divisors, j, --count - j);
                    }
                }
            }
        }
        return ArraysExt.resize(divisors, count);
    }

    /**
     * Returns the real (non-complex) roots of a polynomial equation having the given coefficients.
     * This method returns the <var>x</var> values for which <var>y</var>=0 in the following equation:
     *
     * <blockquote><var>y</var> =
     * <var>c<sub>0</sub></var> +
     * <var>c<sub>1</sub></var>⋅<var>x</var> +
     * <var>c<sub>2</sub></var>⋅<var>x</var><sup>2</sup> +
     * <var>c<sub>3</sub></var>⋅<var>x</var><sup>3</sup> + … +
     * <var>c<sub>n</sub></var>⋅<var>x</var><sup>n</sup>
     * </blockquote>
     *
     * Current implementation can resolve polynomials described by a maximum of 5 coefficients, ignoring
     * leading and trailing zeros. They correspond to linear, quadratic, cubic and quartic polynomials.
     *
     * @param  coefficients  the <var>c<sub>0</sub></var>, <var>c<sub>1</sub></var>, <var>c<sub>2</sub></var>, …
     *                       <var>c<sub>n</sub></var> coefficients, in that order.
     * @return the non-complex roots, or an empty array if none.
     * @throws UnsupportedOperationException if given arguments contain more non-zero coefficients than this method can handle.
     *
     * @see java.awt.geom.QuadCurve2D#solveQuadratic(double[])
     * @see java.awt.geom.CubicCurve2D#solveCubic(double[])
     *
     * @since 1.0
     */
    public static double[] polynomialRoots(final double... coefficients) {
        int upper = coefficients.length;
        while (upper > 0) {
            double a = coefficients[--upper];                   // Coefficient of the term with highest exponent.
            if (a == 0) {
                continue;                                       // Search the last non-zero coefficient.
            }
            double c;                                           // Coefficient of the term with lowest exponent.
            int lower = 0;
            while ((c = coefficients[lower]) == 0) lower++;
            switch (upper - lower) {
                /*
                 * c = 0
                 *
                 * Can not compute x. We could return an arbitrary value if c = 0, but we rather return
                 * an empty array for keeping the number of root equals to the highest exponent.
                 */
                case 0: {
                    break;
                }
                /*
                 * ax + c = 0    →    x = -c/a
                 */
                case 1: {
                    final double x = -c / a;
                    if (Double.isNaN(x)) break;
                    return new double[] {x};
                }
                /*
                 * ax² + bx + c = 0    →    x = (-b ± √[b² - 4ac]) / (2a)
                 *
                 * Above equation is numerically unstable. More stable algorithm is given
                 * by Numerical Recipes §5.6 (quadratic equation), which is applied below.
                 */
                case 2: {
                    final double b = coefficients[lower + 1];
                    final double q = -0.5 * (b + Math.copySign(sqrt(b*b - 4*a*c), b));
                    final double x1 = q/a;
                    final double x2 = c/q;
                    if (Double.isNaN(x1) && Double.isNaN(x2)) break;
                    return (x1 != x2) ? new double[] {x1, x2} : new double[] {x1};
                }
                /*
                 * x³ + ax² + bx + c = 0
                 *
                 * Numerical Recipes §5.6 (cubic equation) is applied below.
                 * Solution usually have either 1 or 3 roots.
                 */
                case 3: {
                    return refineRoots(coefficients, solveCubic(
                                       coefficients[lower + 2] / a,
                                       coefficients[lower + 1] / a,
                                       c / a, false));
                }
                /*
                 * x⁴ + ax³ + bx² + cx + d = 0
                 *
                 * https://dlmf.nist.gov/1.11 in "Quartic equations" section.
                 * This algorithm reduces the equation to a cubic equation.
                 */
                case 4: {
                    double b,d;
                    d = c / a;
                    c = coefficients[lower + 1] / a;
                    b = coefficients[lower + 2] / a;
                    a = coefficients[lower + 3] / a;
                    final double a2 = a*a;
                    final double p = -3./8   * (a2)     +  b;
                    final double q =  1./8   * (a2*a)   -  1./2  * (a*b)   +  c;
                    final double r = -3./256 * (a2*a2)  +  1./16 * (a2*b)  -  1./4 * (a*c) + d;
                    final double[] roots = solveCubic(-2*p, p*p-4*r, q*q, true);
                    if (roots.length != 4) break;
                    for (int i=0; i<3; i++) {
                        roots[i] = sqrt(-roots[i]);
                    }
                    if (isPositive(q)) {
                        for (int i=0; i<3; i++) {
                            roots[i] = -roots[i];
                        }
                    }
                    final double α = roots[0], β = roots[1], γ = roots[2];
                    final double s = α + β + γ;
                    if (Double.isNaN(s)) break;
                    roots[0] = s/2 - (a /= 4);
                    roots[1] = (+α - β - γ)/2 - a;
                    roots[2] = (-α + β - γ)/2 - a;
                    roots[3] = (-α - β + γ)/2 - a;
                    return refineRoots(coefficients, removeDuplicated(roots));
                }
                default: {
                    throw new UnsupportedOperationException();
                }
            }
            break;
        }
        return ArraysExt.EMPTY_DOUBLE;
    }

    /**
     * Solves cubic equation x³ + ax² + bx + c = 0. The solution before simplification has either 1 or 3 roots.
     * The {@code quartic} argument specifies whether this cubic equation is used as a step for solving a quartic equation:
     *
     * <ul>
     *   <li>If {@code true}, then we are interested only in the 3 roots solution and we do not check for duplicated values.
     *       The length of returned array is 4 for allowing the caller to reuse the same array.</li>
     *   <li>If {@code false}, then this method may simplify the 3 roots to 2 roots if two of them are equal,
     *       or may return the 1 root solution. The length of returned array is the number of roots.</li>
     * </ul>
     */
    private static double[] solveCubic(double a, double b, double c, final boolean quartic) {
        final double Q = (a*a - 3*b) / 9;                           // Q from Numerical Recipes 5.6.10.
        final double R = (a*(a*a - 4.5*b) + 13.5*c) / 27;           // R from Numerical Recipes 5.6.10.
        final double Q3 = Q*Q*Q;
        final double R2 = R*R;
        a /= 3;                                                     // Last term of Numerical Recipes 5.6.12, 17 and 18.
        if (R2 < Q3) {
            /*
             * Numerical Recipes 5.6.11 and 5.6.12 uses acos(R/sqrt(Q³)). It is possible to rewrite as
             * atan2(sqrt(Q3 - R2), R) using the  cos(θ) = 1/√[1 + tan²θ]  trigonometric identity, but
             * this substitution seems to decrease accuracy instead of increasing it in our tests.
             */
            b = Math.acos(R/sqrt(Q3)) / 3;                          // θ from Numerical recipes 5.6.11, then b = θ/3.
            c = -2 * sqrt(Q);                                       // First part of Numerical Recipes 5.6.12.
            double[] roots = new double[quartic ? 4 : 3];
            roots[2] = c*Math.cos(b - 2*Math.PI/3) - a;             // TODO: try Math.fma with JDK9.
            roots[1] = c*Math.cos(b + 2*Math.PI/3) - a;
            roots[0] = c*Math.cos(b) - a;
            if (!quartic) {
                roots = removeDuplicated(roots);
            }
            return roots;
        }
        if (!quartic) {
            b = -Math.copySign(Math.cbrt(abs(R) + sqrt(R2 - Q3)), R);       // A from Numerical Recipes 5.6.15.
            final double x = (b == 0 ? 0 : b + Q/b) - a;
            if (!Double.isNaN(x)) {
                return new double[] {x};
            }
        }
        return ArraysExt.EMPTY_DOUBLE;
    }

    /**
     * Remove duplicated values in the given array. This method is okay only for very small arrays (3 or 4 elements).
     * Duplicated values should be very rare and occur mostly as a consequence of rounding errors while computing the
     * roots of polynomial equations. Because if the algebraic solution has less roots than what we would expect from
     * the largest exponent (for example ax² + bx = 0 has only one root instead of two), then {@link #polynomialRoots}
     * should have reduced the equation to a lower degrees (ax + b = 0 in above example), in which case there is no
     * duplicated roots to remove.
     */
    private static double[] removeDuplicated(double[] roots) {
        int i = 1;
next:   while (i < roots.length) {
            for (int j=i; --j >= 0;) {
                if (roots[j] == roots[i]) {
                    roots = ArraysExt.remove(roots, i, 1);
                    continue next;
                }
            }
            i++;
        }
        return roots;
    }

    /**
     * Tries to improves accuracy of polynomial roots by applying small displacements
     * to the <var>x</var> values using ∂y/∂x derivative around those values.
     *
     * <div class="note"><b>Purpose:</b>
     * this refinement is significant in a {@link org.apache.sis.referencing.GeodesicsOnEllipsoid}
     * test checking the value of an μ(x²,y²) function.</div>
     *
     * @param  coefficients  the user-specified coefficients.
     * @param  roots         the roots. This array will be modified in place.
     * @return {@code roots}.
     */
    private static double[] refineRoots(final double[] coefficients, final double[] roots) {
        for (int i=0; i < roots.length; i++) {
            double ymin = Double.POSITIVE_INFINITY;
            double x = roots[i];
            double dx;
            do {
                double px = 1;                              // Power of x: 1, x¹, x², x³, …
                double dy = 0;                              // First derivative of polynomial at x.
                double y  = coefficients[0];                // Value of polynomial at x.
                double ey = 0, edy = 0;                     // Error terms for Kahan summation algorithm.
                for (int j=1; j<coefficients.length; j++) {
                    final double c = coefficients[j];
                    double s;
                    s = c * (px *  i) + edy; edy = s + (dy - (dy += s));        // Kahan summation of dy.
                    s = c * (px *= x) +  ey;  ey = s + ( y - ( y += s));        // Kahan summation of y.
                }
                if (!(ymin > (ymin = abs(y)))) break;       // If result not better than previous result, stop.
                roots[i] = x;
                dx = y/dy;
            } while (x != (x -= dx) && Double.isFinite(x));
        }
        return roots;
    }
}
