1 package org.opentrafficsim.core.math;
2
3 import org.djunits.value.ValueRuntimeException;
4
5 /**
6 * Solvers for simple equations.
7 * <p>
8 * Copyright (c) 2013-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
9 * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
10 * </p>
11 * @author Alexander Verbraeck
12 * @author Peter Knoppers
13 */
14 public final class Solver
15 {
16 /**
17 *
18 */
19 private Solver()
20 {
21 // cannot be instantiated.
22 }
23
24 /**
25 * Solve quadratic equation <cite>ax<sup>2</sup>+bx+c=0</cite> for <cite>x</cite>. Degenerate case <cite>a == 0</cite> is
26 * allowed.
27 * @param a the coefficient of <cite>x<sup>2</sup></cite>
28 * @param b the coefficient of <cite>x</cite>
29 * @param c intercept
30 * @return array with zero, one, or two elements (depending on the number of solutions of the equation)
31 */
32 public static double[] solve(final double a, final double b, final double c)
33 {
34 if (Math.abs(a) < 1E-8) // rounding errors will yield incorrect solutions if we allow very small a
35 {
36 // Degenerate; linear equation
37 return solve(b, c);
38 }
39 // Quadratic equation
40 double discriminant = b * b - 4 * a * c;
41 if (discriminant < 0)
42 {
43 return new double[0];
44 }
45 if (0 == discriminant)
46 {
47 return new double[] {-b / 2 / a};
48 }
49 discriminant = Math.sqrt(discriminant);
50 return new double[] {(-b + discriminant) / 2 / a, (-b - discriminant) / 2 / a};
51 }
52
53 /**
54 * Solve a quadratic or linear equation and return the solution that is closest (but not less than) a boundary.
55 * @param lowerBound minimum value of good solution
56 * @param a quadratic coefficient
57 * @param b linear coefficient
58 * @param c value of the quadratic function for x==0
59 * @return the solution that is closest (but not less than) a boundary
60 * @throws ValueRuntimeException if there is no acceptable solution
61 */
62 public static double firstSolutionAfter(final double lowerBound, final double a, final double b, final double c)
63 throws ValueRuntimeException
64 {
65 double[] solutions = solve(a, b, c);
66 if (0 == solutions.length)
67 {
68 throw new ValueRuntimeException("No solutions");
69 }
70 else if (1 == solutions.length)
71 {
72 if (solutions[0] >= lowerBound)
73 {
74 return solutions[0];
75 }
76 throw new ValueRuntimeException("Only one solution and it is before lowerBound");
77 }
78 // Two solutions
79 if (solutions[0] < lowerBound && solutions[1] < lowerBound)
80 {
81 throw new ValueRuntimeException("Both solutions are before lowerBound");
82 }
83 if (solutions[0] < lowerBound)
84 {
85 return solutions[1];
86 }
87 if (solutions[1] < lowerBound)
88 {
89 return solutions[0];
90 }
91 return Math.min(solutions[0], solutions[1]);
92 }
93
94 /**
95 * Solve linear equation <cite>ax+b=0</cite> for <cite>x</cite>.
96 * @param a the coefficient of <cite>x</cite>
97 * @param b intercept
98 * @return array with one or zero elements (depending on the number of solutions of the equation). The case where both
99 * <cite>a</cite> and <cite>b</cite> are zero returns an array of length 0.
100 */
101 public static double[] solve(final double a, final double b)
102 {
103 if (0 == a)
104 {
105 // Degenerate; no solution (or infinitely many solutions)
106 return new double[0];
107 }
108 return new double[] {-b / a};
109 }
110
111 }