class CubicRootFinder {

    public static ImmutableList<Complex> find(double a, double b, double c, double d) {
        if (a == 0) {
            return QuadraticRootFinder.find(b, c, d);
        }

        ImmutableList.Builder<Complex> builder = ImmutableList.builder();

        double f = ((3 * c / a) - (Math.pow(b, 2) / Math.pow(a, 2))) / 3d;
        double g = ((2 * Math.pow(b, 3))/ Math.pow(a, 3) - (9 * b * c) / Math.pow(a, 2) + (27 * d / a)) / 27;
        double h = (Math.pow(g, 2)/4) + (Math.pow(f, 3)/27);

        if ( h > 0 ) {

            double R = -(g/2) + Math.sqrt(h);
            double S = Math.cbrt(R);
            double T = -(g/2) - Math.sqrt(h);
            double U = Math.cbrt(T);

            Complex root1 = new Complex(S + U - (b / (3 * a)), 0d);
            Complex root2 = new Complex(-(S + U)/2 - (b / (3 * a)), ((S-U) * Math.sqrt(3))/2d);
            Complex root3 = new Complex(-(S + U)/2 - (b / (3 * a)), - ((S - U) * Math.sqrt(3))/2d);
            return builder.add(root1).add(root2).add(root3).build();
        }

        if (f == 0 && g == 0 && h == 0) {
            Complex root = new Complex(-Math.cbrt(d/a), 0d);
            return builder.add(root).add(root).add(root).build();
        }

        double i = Math.sqrt(((Math.pow(g, 2)/4) - h));
        double j = Math.cbrt(i);
        double k = Math.acos(- (g / (2 * i)));
        double l = - j;
        double m = Math.cos(k /3);
        double n = Math.sqrt(3) * Math.sin(k / 3);
        double p =  -(b / (3 * a));

        double rootOne = 2 * j * Math.cos(k/3) - (b/ (3 *a));
        double rootTwo = l * (m + n) + p;
        double rootThree = l * (m - n) + p;
        return builder.add(new Complex(rootOne, 0d)).add(new Complex(rootTwo, 0d)).add(new Complex(rootThree, 0d)).build();
    }

    private  CubicRootFinder();

}