# see http://en.wikipedia.org/wiki/Cubic_equation
SetHelp ("CubicFormula", "equation_solving",
         "Find roots of a cubic polynomial (given as vector of coefficients)")
function CubicFormula(pol) = (
	if not IsPoly(pol) or elements(pol) != 4 or pol@(4) == 0 then
		(error("CubicFormula: argument 1 must be a cubic polynomial");bailout);
	c := pol@(1) / pol@(4);
	b := pol@(2) / pol@(4);
	a := pol@(3) / pol@(4);

    p := b - (a^2)/3;
    q := c + (2*a^3 - 9*a*b)/27;
    
    if p == 0 then
    	if q == 0 then
    		return [-a/3,-a/3,-a/3]
    	else
    		u := q^(1/3)
    else if q == 0 then
    	u := sqrt(p/3)
    else
    	u := ( q/2 + sqrt(((q^2) / 4) + (p^3)/27) )^(1/3);

    omega1 := -(1/2)+1i*sqrt(3)/2;
    omega2 := -(1/2)-1i*sqrt(3)/2;
    
    # If real coefficients and the discriminant is negative
    # or if q == 0, then we have all real roots 
    if (IsReal(a) and IsReal(b) and IsReal(c)) then (
	DELTA := - a^2*b^2 + 4*b^3 + 4*a^3*c + 27*c^2 - 18*a*b*c;
    	# If the discriminant is negative
    	# or if q == 0, then we have all real roots 
	if DELTA <= 0 or q == 0 then
		[RealPart (p/(3*u) - u - a/3)
		 RealPart (p/(3*u*omega1) - u*omega1 - a/3)
		 RealPart (p/(3*u*omega2) - u*omega2 - a/3)]
	# if the discriminant is positive then we have one real root
	else #if DELTA > 0 then
		[RealPart (p/(3*u) - u - a/3)
		 p/(3*u*omega1) - u*omega1 - a/3
		 p/(3*u*omega2) - u*omega2 - a/3]
    ) else
    	[p/(3*u) - u - a/3
     	 p/(3*u*omega1) - u*omega1 - a/3
     	 p/(3*u*omega2) - u*omega2 - a/3]
)

## see planetmath
SetHelp ("QuarticFormula", "equation_solving",
         "Find roots of a quartic polynomial (given as vector of coefficients)")
function QuarticFormula(pol) = (
	if not IsPoly(pol) or elements(pol) != 5 or pol@(5) == 0 then
		(error("QuarticFormula: argument 1 must be a quartic polynomial");bailout);
	d := pol@(1) / pol@(5);
	c := pol@(2) / pol@(5);
	b := pol@(3) / pol@(5);
	a := pol@(4) / pol@(5);
	
	# resolvent cubic
	t := CubicFormula([c^2+a^2*d-a*b*c,b^2+a*c-4*d,-2*b,1]);

	# compute r1 * r2 and r3 * r4
	tt := t@(2)+t@(3)-t@(1);
	A := sqrt(tt^2 - 16*d);
	r1r2 := ((tt) + A)/4;
	r3r4 := ((tt) - A)/4;

	# note that it is hard to tell apart r1 + r2 and r3 + r4,
	# hence we'll try both below
	A := sqrt(a^2 - 4*t@(1));
	r1pr2 := (-a + A)/2;
	r3pr4 := (-a - A)/2;

	# compare the minus of the correct symetric function of the roots
	# to coeff c and see which is better and use that
	if (|r1r2*r3pr4 + r1pr2*r3r4 + c| > |r1r2*r1pr2 + r3pr4*r3r4 + c|) then (
		r1pr2 := (-a - A)/2;
		r3pr4 := (-a + A)/2
	);
    	
	A1 := sqrt(r1pr2^2 -4*r1r2);
	A2 := sqrt(r3pr4^2 -4*r3r4);
	[(r1pr2 + A1)/2
	 (r1pr2 - A1)/2
	 (r3pr4 + A2)/2
	 (r3pr4 - A2)/2]
)

SetHelp ("PolynomialRoots", "equation_solving",
         "Find roots of a polynomial (given as vector of coefficients)")
function PolynomialRoots(p) = (
	if not IsPoly(p) then
		(error("PolynomialRoots: argument 1 must be a polynomial");bailout);
	p = TrimPoly (p);
	if elements(p) < 2 or elements(p) > 5 then
		(error("PolynomialRoots: Solving for polynomials only of degrees 1 through 4 are implemented");bailout);
	
	if elements(p) == 2 then
		[-p@(1)/p@(2)]
	else if elements(p) == 3 then
		QuadraticFormula (p)
	else if elements(p) == 4 then
		CubicFormula (p)
	else # if elements(p) == 5 then
		QuarticFormula (p)
)
