/// <summary> /// This function returns the complex arctangent of the complex number /// a, arctan(a)}. The branch cuts are on the imaginary axis, /// below -i and above i . /// </summary> /// <param name="a">The function argument.</param> /// <returns>The complex arctangent of the complex number a.</returns> public static Complex Atan(Complex a) { double R = a.Re, I = a.Im; Complex z; if (I == 0) { z = new Complex(Math.Atan(R), 0); } else { /* FIXME: This is a naive implementation which does not fully * take into account cancellation errors, overflow, underflow * etc. It would benefit from the Hull et al treatment. */ double r = hypot(R, I); double imag; double u = 2 * I / (1 + r * r); /* FIXME: the following cross-over should be optimized but 0.1 * seems to work ok */ if (Math.Abs(u) < 0.1) { imag = 0.25 * (RMath.Log1p(u) - RMath.Log1p(-u)); } else { double A = hypot(R, I + 1); double B = hypot(R, I - 1); imag = 0.5 * Math.Log(A / B); } if (R == 0) { if (I > 1) { z = new Complex(M_PI_2, imag); } else if (I < -1) { z = new Complex(-M_PI_2, imag); } else { z = new Complex(0, imag); } } else { z = new Complex(0.5 * Math.Atan2(2 * R, ((1 + r) * (1 - r))), imag); } } return(z); }
/// <summary> /// Return log |z|. /// </summary> /// <param name="z">The complex function argument.</param> /// <returns>log |z|, i.e. the natural logarithm of the absolute value of z.</returns> public static double LogAbs(Complex z) { double xabs = Math.Abs(z.Re); double yabs = Math.Abs(z.Im); double max, u; if (xabs >= yabs) { max = xabs; u = yabs / xabs; } else { max = yabs; u = xabs / yabs; } // Handle underflow when u is close to 0 return(Math.Log(max) + 0.5 * RMath.Log1p(u * u)); }
/// <summary> /// This function returns the complex arcsine of the complex number a, /// arcsin(a)}. The branch cuts are on the real axis, less than -1 /// and greater than 1. /// </summary> /// <param name="a">The function argument.</param> /// <returns>the complex arcsine of the complex number a.</returns> public static Complex Asin(Complex a) { double R = a.Re, I = a.Im; Complex z; if (I == 0) { z = Asin(R); } else { double x = Math.Abs(R), y = Math.Abs(I); double r = hypot(x + 1, y), s = hypot(x - 1, y); double A = 0.5 * (r + s); double B = x / A; double y2 = y * y; double real, imag; const double A_crossover = 1.5, B_crossover = 0.6417; if (B <= B_crossover) { real = Math.Asin(B); } else { if (x <= 1) { double D = 0.5 * (A + x) * (y2 / (r + x + 1) + (s + (1 - x))); real = Math.Atan(x / Math.Sqrt(D)); } else { double Apx = A + x; double D = 0.5 * (Apx / (r + x + 1) + Apx / (s + (x - 1))); real = Math.Atan(x / (y * Math.Sqrt(D))); } } if (A <= A_crossover) { double Am1; if (x < 1) { Am1 = 0.5 * (y2 / (r + (x + 1)) + y2 / (s + (1 - x))); } else { Am1 = 0.5 * (y2 / (r + (x + 1)) + (s + (x - 1))); } imag = RMath.Log1p(Am1 + Math.Sqrt(Am1 * (A + 1))); } else { imag = Math.Log(A + Math.Sqrt(A * A - 1)); } z = new Complex((R >= 0) ? real : -real, (I >= 0) ? imag : -imag); } return(z); }