class SumatraMath {

	public static List<Double> cubicFunctionRoots(final double a3, final double b2, final double c1, final double d0)
	{
		// Verify preconditions.
		if (isEqual(a3, 0.0))
		{
			return quadraticFunctionRoots(b2, c1, d0);
		}
		
		List<Double> roots = new ArrayList<>();
		
		// normalize coefficients
		double a = b2 / a3;
		double b = c1 / a3;
		double c = d0 / a3;
		
		// commence solution
		double aOver3 = a / 3.0;
		double q = ((3 * b) - (a * a)) / 9.0;
		double qCube = q * q * q;
		double r = ((9 * a * b) - (27 * c) - (2 * a * a * a)) / 54.0;
		double rSqr = r * r;
		double d = qCube + rSqr;
		
		if (d < 0.0)
		{
			// Three unequal real roots.
			double theta = SumatraMath.acos(r / SumatraMath.sqrt(-qCube));
			double sqrtQ = SumatraMath.sqrt(-q);
			double x1 = (2.0 * sqrtQ * SumatraMath.cos(theta / 3.0)) - aOver3;
			double x2 = (2.0 * sqrtQ * SumatraMath.cos((theta + AngleMath.PI_TWO) / 3.0)) - aOver3;
			double x3 = (2.0 * sqrtQ * SumatraMath.cos((theta + (AngleMath.PI * 4.0)) / 3.0)) - aOver3;
			roots.add(x1);
			roots.add(x2);
			roots.add(x3);
		} else if (d > 0.0)
		{
			// One real root.
			double sqrtD = SumatraMath.sqrt(d);
			double s = Math.cbrt(r + sqrtD);
			double t = Math.cbrt(r - sqrtD);
			double x1 = (s + t) - aOver3;
			roots.add(x1);
		} else
		{
			// Three real roots, at least two equal.
			double cbrtR = Math.cbrt(r);
			double x1 = (2 * cbrtR) - aOver3;
			double x2 = -cbrtR - aOver3;
			roots.add(x1);
			roots.add(x2);
		}
		
		return roots;
	}

	private  SumatraMath();

}