Solver.java

package org.opentrafficsim.core.math;

import org.djunits.value.ValueRuntimeException;

/**
 * Solvers for simple equations.
 * <p>
 * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
 * </p>
 * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
 * @author <a href="https://tudelft.nl/staff/p.knoppers-1">Peter Knoppers</a>
 */
public final class Solver
{
    /**
     * 
     */
    private Solver()
    {
        // cannot be instantiated.
    }

    /**
     * Solve quadratic equation <cite>ax<sup>2</sup>+bx+c=0</cite> for <cite>x</cite>. Degenerate case <cite>a == 0</cite> is
     * allowed.
     * @param a double; the coefficient of <cite>x<sup>2</sup></cite>
     * @param b double; the coefficient of <cite>x</cite>
     * @param c double;
     * @return double[]; array with zero, one, or two elements (depending on the number of solutions of the equation)
     */
    public static double[] solve(final double a, final double b, final double c)
    {
        if (Math.abs(a) < 1E-8) // rounding errors will yield incorrect solutions if we allow very small a
        {
            // Degenerate; linear equation
            return solve(b, c);
        }
        // Quadratic equation
        double discriminant = b * b - 4 * a * c;
        if (discriminant < 0)
        {
            return new double[0];
        }
        if (0 == discriminant)
        {
            return new double[] {-b / 2 / a};
        }
        discriminant = Math.sqrt(discriminant);
        return new double[] {(-b + discriminant) / 2 / a, (-b - discriminant) / 2 / a};
    }

    /**
     * Solve a quadratic or linear equation and return the solution that is closest (but not less than) a boundary.
     * @param lowerBound double; minimum value of good solution
     * @param a double; quadratic coefficient
     * @param b double; linear coefficient
     * @param c double; value of the quadratic function for x==0
     * @return double; the solution that is closest (but not less than) a boundary
     * @throws ValueRuntimeException if there is no acceptable solution
     */
    public static double firstSolutionAfter(final double lowerBound, final double a, final double b, final double c)
            throws ValueRuntimeException
    {
        double[] solutions = solve(a, b, c);
        if (0 == solutions.length)
        {
            throw new ValueRuntimeException("No solutions");
        }
        else if (1 == solutions.length)
        {
            if (solutions[0] >= lowerBound)
            {
                return solutions[0];
            }
            throw new ValueRuntimeException("Only one solution and it is before lowerBound");
        }
        // Two solutions
        if (solutions[0] < lowerBound && solutions[1] < lowerBound)
        {
            throw new ValueRuntimeException("Both solutions are before lowerBound");
        }
        if (solutions[0] < lowerBound)
        {
            return solutions[1];
        }
        if (solutions[1] < lowerBound)
        {
            return solutions[0];
        }
        return Math.min(solutions[0], solutions[1]);
    }

    /**
     * Solve linear equation <cite>ax+b=0</cite> for <cite>x</cite>.
     * @param a double; the coefficient of <cite>x</cite>
     * @param b double;
     * @return double[]; array with one or zero elements (depending on the number of solutions of the equation). The case where
     *         both <cite>a</cite> and <cite>b</cite> are zero returns an array of length 0.
     */
    public static double[] solve(final double a, final double b)
    {
        if (0 == a)
        {
            // Degenerate; no solution (or infinitely many solutions)
            return new double[0];
        }
        return new double[] {-b / a};
    }

}