/// <summary> /// Computes the square root of a complex number. /// </summary> /// <param name="z">The argument.</param> /// <returns>The square root of the argument.</returns> /// <remarks> /// <para>The image below shows the complex square root function near the origin, using domain coloring.</para> /// <img src="../images/ComplexSqrtPlot.png" /> /// <para>You can see the branch cut extending along the negative real axis from the zero at the origin.</para> /// </remarks> public static Complex Sqrt(Complex z) { if (z.Im == 0.0) { // Handle the degenerate case quickly. // This also eliminates need to worry about Im(z) = 0 in subsequent formulas. if (z.Re < 0.0) { return(new Complex(0.0, Math.Sqrt(-z.Re))); } else { return(new Complex(Math.Sqrt(z.Re), 0.0)); } } else { // To find a fast formula for complex square root, note x + i y = \sqrt{a + i b} implies // x^2 + 2 i x y - y^2 = a + i b, so x^2 - y^2 = a and 2 x y = b. Cross-substitute and // use quadratic formula to solve for x^2 and y^2 to obtain // x = \sqrt{\frac{\sqrt{a^2 + b^2} + a}{2}} y = \pm \sqrt{\frac{\sqrt{a^2 + b^2} - a}{2}} // This gives complex square root in three square roots and a few flops. // Only problem is b << a case, where significant cancelation occurs in one of the formula. // (Which one depends on the sign of a.) Handle that case by series expansion. double p, q; if (Math.Abs(z.Im) < 0.25 * Math.Abs(z.Re)) { double x2 = MoreMath.Sqr(z.Im / z.Re); double t = x2 / 2.0; double s = t; // Find s = \sqrt{1 + x^2} - 1 using binomial expansion for (int k = 2; true; k++) { if (k > Global.SeriesMax) { throw new NonconvergenceException(); } double s_old = s; t *= (1.5 / k - 1.0) * x2; s += t; if (s == s_old) { break; } } if (z.Re < 0.0) { p = -z.Re * s; q = -2.0 * z.Re + p; } else { q = z.Re * s; p = 2.0 * z.Re + q; } } else { double m = ComplexMath.Abs(z); p = m + z.Re; q = m - z.Re; } double x = Math.Sqrt(p / 2.0); double y = Math.Sqrt(q / 2.0); if (z.Im < 0.0) { y = -y; } return(new Complex(x, y)); /* * if (Math.Abs(z.Im) < 0.125 * Math.Abs(z.Re)) { * // We should try to improve this by using a series instead of the full power algorithm. * return (Pow(z, 0.5)); * } else { * // This is a pretty fast formula for a complex square root, basically just * // three square roots and a few flops. * // But if z.Im << z.Re, then z.Re ~ m and it suffers from cancelations. * double m = Abs(z); * double x = Math.Sqrt((m + z.Re) / 2.0); * double y = Math.Sqrt((m - z.Re) / 2.0); * if (z.Im < 0.0) y = -y; * return (new Complex(x, y)); * } */ } }
/// <summary> /// Raises a complex number to an integer power. /// </summary> /// <param name="z">The argument.</param> /// <param name="n">The power.</param> /// <returns>The value of z<sup>n</sup>.</returns> public static Complex Pow(Complex z, int n) { // this is a straight-up copy of MoreMath.Pow with x -> z, double -> Complex if (n < 0) { return(1.0 / Pow(z, -n)); } switch (n) { case 0: // we follow convention that 0^0 = 1 return(1.0); case 1: return(z); case 2: // 1 multiply return(z * z); case 3: // 2 multiplies return(z * z * z); case 4: { // 2 multiplies Complex z2 = z * z; return(z2 * z2); } case 5: { // 3 multiplies Complex z2 = z * z; return(z2 * z2 * z); } case 6: { // 3 multiplies Complex z2 = z * z; return(z2 * z2 * z2); } case 7: { // 4 multiplies Complex z3 = z * z * z; return(z3 * z3 * z); } case 8: { // 3 multiplies Complex z2 = z * z; Complex z4 = z2 * z2; return(z4 * z4); } case 9: { // 4 multiplies Complex z3 = z * z * z; return(z3 * z3 * z3); } case 10: { // 4 multiplies Complex z2 = z * z; Complex z4 = z2 * z2; return(z4 * z4 * z2); } default: return(ComplexMath.Pow(z, (double)n)); } }
/// <summary> /// Raises a complex number to an integer power. /// </summary> /// <param name="z">The argument.</param> /// <param name="n">The power.</param> /// <returns>The value of z<sup>n</sup>.</returns> public static Complex Pow(Complex z, int n) { if (n < 0) { return(1.0 / Pow(z, -n)); } switch (n) { case 0: // We follow convention that 0^0 = 1 return(1.0); case 1: return(z); case 2: // 1 multiply return(Sqr(z)); case 3: // 2 multiplies return(Sqr(z) * z); case 4: { // 2 multiplies Complex z2 = Sqr(z); return(Sqr(z2)); } case 5: { // 3 multiplies Complex z2 = Sqr(z); return(Sqr(z2) * z); } case 6: { // 3 multiplies Complex z2 = Sqr(z); return(Sqr(z2) * z2); } case 7: { // 4 multiplies Complex z3 = Sqr(z) * z; return(Sqr(z3) * z); } case 8: { // 3 multiplies Complex z2 = Sqr(z); Complex z4 = Sqr(z2); return(Sqr(z4)); } case 9: { // 4 multiplies Complex z3 = Sqr(z) * z; return(Sqr(z3) * z3); } case 10: { // 4 multiplies Complex z2 = Sqr(z); Complex z4 = Sqr(z2); return(Sqr(z4) * z2); } case 12: { // 4 multiplies Complex z3 = Sqr(z) * z; Complex z6 = Sqr(z3); return(Sqr(z6)); } case 16: { // 4 multiplies Complex z2 = Sqr(z); Complex z4 = Sqr(z2); Complex z8 = Sqr(z4); return(Sqr(z8)); } // that's all the cases do-able in 4 or fewer complex multiplies default: return(ComplexMath.Pow(z, (double)n)); } }