complex.ha (21042B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 // Sections of the code below are based on Go's implementation, which is, in 5 // turn, based on Cephes Math Library. The original C code can be found at 6 // http://netlib.sandia.gov/cephes/c9x-complex/. 7 // 8 // Cephes Math Library Release 2.8: June, 2000 9 // Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier 10 // 11 // The readme file at http://netlib.sandia.gov/cephes/ says: 12 // Some software in this archive may be from the book _Methods and 13 // Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster 14 // International, 1989) or from the Cephes Mathematical Library, a 15 // commercial product. In either event, it is copyrighted by the author. 16 // What you see here may be used freely but it comes with no support or 17 // guarantee. 18 // 19 // The two known misprints in the book are repaired here in the 20 // source listings for the gamma function and the incomplete beta 21 // integral. 22 // 23 // Stephen L. Moshier 24 // moshier@na-net.ornl.gov 25 // 26 // The Go copyright notice: 27 // ==================================================== 28 // Copyright (c) 2009 The Go Authors. All rights reserved. 29 // 30 // Redistribution and use in source and binary forms, with or without 31 // modification, are permitted provided that the following conditions are 32 // met: 33 // 34 // * Redistributions of source code must retain the above copyright 35 // notice, this list of conditions and the following disclaimer. 36 // * Redistributions in binary form must reproduce the above 37 // copyright notice, this list of conditions and the following disclaimer 38 // in the documentation and/or other materials provided with the 39 // distribution. 40 // * Neither the name of Google Inc. nor the names of its 41 // contributors may be used to endorse or promote products derived from 42 // this software without specific prior written permission. 43 // 44 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 45 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 46 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 47 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 48 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 49 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 50 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 51 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 52 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 53 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 54 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 55 // ==================================================== 56 57 use math; 58 use math::checked; 59 60 // A complex number containing a real component and an imaginary component, 61 // represented as two single-precision floating point numbers. 62 export type c64 = (f32, f32); 63 64 // A complex number containing a real component and an imaginary component, 65 // represented as two double-precision floating point numbers. 66 export type c128 = (f64, f64); 67 68 // A tagged union of all complex types. 69 export type complex = (c64 | c128); 70 71 // Converts a [[c64]] to a [[c128]]. 72 export fn c64to128(z: c64) c128 = (z.0: f64, z.1: f64); 73 74 // Truncates a [[c128]] to a [[c64]]. Precision may be lost. 75 export fn c128to64(z: c128) c64 = (z.0: f32, z.1: f32); 76 77 // Adds two complex numbers 78 export fn addc64(a: c64, b: c64) c64 = (a.0 + b.0, a.1 + b.1); 79 80 // Adds two complex numbers. 81 export fn addc128(a: c128, b: c128) c128 = (a.0 + b.0, a.1 + b.1); 82 83 // Subtracts two complex numbers. 84 export fn subc64(a: c64, b: c64) c64 = (a.0 - b.0, a.1 - b.1); 85 86 // Subtracts two complex numbers. 87 export fn subc128(a: c128, b: c128) c128 = (a.0 - b.0, a.1 - b.1); 88 89 // Multiplies two complex numbers. 90 export fn mulc64(a: c64, b: c64) c64 = 91 (a.0 * b.0 - a.1 * b.1, a.1 * b.0 + a.0 * b.1); 92 93 // Multiplies two complex numbers. 94 export fn mulc128(a: c128, b: c128) c128 = 95 (a.0 * b.0 - a.1 * b.1, a.1 * b.0 + a.0 * b.1); 96 97 // Divides two complex numbers. 98 export fn divc64(a: c64, b: c64) c64 = { 99 const denom = b.0 * b.0 + b.1 * b.1; 100 return ( 101 (a.0 * b.0 + a.1 * b.1) / denom, 102 (a.1 * b.0 - a.0 * b.1) / denom, 103 ); 104 }; 105 106 // Divides two complex numbers. 107 export fn divc128(a: c128, b: c128) c128 = { 108 const denom = b.0 * b.0 + b.1 * b.1; 109 return ( 110 (a.0 * b.0 + a.1 * b.1) / denom, 111 (a.1 * b.0 - a.0 * b.1) / denom, 112 ); 113 }; 114 115 // Takes the conjugate of a complex number by negating the imaginary component. 116 export fn conjc64(z: c64) c64 = (z.0, -z.1); 117 118 // Takes the conjugate of a complex number by negating the imaginary component. 119 export fn conjc128(z: c128) c128 = (z.0, -z.1); 120 121 // Takes the absolute value of a complex number. 122 export fn absc128(z: c128) f64 = math::hypotf64(z.0, z.1); 123 124 // Gets the argument, or phase, of a complex number. 125 export fn argc128(z: c128) f64 = math::atan2f64(z.1, z.0); 126 127 // Checks if two complex numbers are equal. Be sure to take floating point 128 // round-off errors into account. 129 export fn equalc64(a: c64, b: c64) bool = a.0 == b.0 && a.1 == b.1; 130 131 // Checks if two complex numbers are equal. Be sure to take floating point 132 // round-off errors into account. 133 export fn equalc128(a: c128, b: c128) bool = a.0 == b.0 && a.1 == b.1; 134 135 // Checks if two complex numbers are equal. Be sure to take floating point 136 // round-off errors into account. 137 export fn equal(a: complex, b: complex) bool = { 138 match (a) { 139 case let a: c64 => 140 return equalc64(a, b as c64); 141 case let a: c128 => 142 return equalc128(a, b as c128); 143 }; 144 }; 145 146 // Returns [[math::E]] raised to the power of z. 147 export fn expc128(z: c128) c128 = { 148 if (math::isinf(z.0)) { 149 if (z.0 > 0f64 && z.1 == 0f64) { 150 return z; 151 }; 152 if (math::isinf(z.1) || math::isnan(z.1)) { 153 if (z.0 < 0f64) { 154 return (0f64, math::copysignf64(0f64, z.1)); 155 } else { 156 return (math::INF, math::NAN); 157 }; 158 }; 159 } else if (math::isnan(z.0) && z.1 == 0f64) { 160 return (math::NAN, z.1); 161 }; 162 return rectc128(math::expf64(z.0), z.1); 163 }; 164 165 // Returns true if the given complex number is infinite. 166 export fn isinf(z: c128) bool = math::isinf(z.0) || math::isinf(z.1); 167 168 // Returns true if the given complex number is NaN -- that is -- either 169 // component is NaN and neither component is an infinity. 170 export fn isnan(z: c128) bool = 171 !isinf(z) && (math::isnan(z.0) || math::isnan(z.1)); 172 173 // Returns the natural logarithm of z. 174 export fn logc128(z: c128) c128 = (math::logf64(absc128(z)), argc128(z)); 175 176 // Negates z. 177 export fn negc64(z: c64) c64 = (-z.0, -z.1); 178 179 // Negates z. 180 export fn negc128(z: c128) c128 = (-z.0, -z.1); 181 182 // Creates a new [[c128]] from the polar coordinates (r, theta). 183 export fn rectc128(r: f64, theta: f64) c128 = 184 (r * math::cosf64(theta), r * math::sinf64(theta)); 185 186 // Returns the polar coordinates of z. 187 export fn polarc128(z: c128) (f64, f64) = (absc128(z), argc128(z)); 188 189 // Returns a raised to the power of b. 190 export fn powc128(a: c128, b: c128) c128 = { 191 if (a.0 == 0f64 && a.1 == 0f64) { 192 if (isnan(b)) { 193 return (math::NAN, math::NAN); 194 } else if (b.0 == 0f64) { 195 return (1f64, 0f64); 196 } else if (b.0 < 0f64) { 197 return (math::INF, if (b.1 == 0f64) 0f64 else math::INF); 198 } else { 199 assert(b.0 > 0f64); 200 return (0f64, 0f64); 201 }; 202 }; 203 const mod = absc128(a); 204 if (mod == 0f64) { 205 return (0f64, 0f64); 206 }; 207 let r = math::powf64(mod, b.0); 208 const phase = argc128(a); 209 let theta = b.0 * phase; 210 if (b.1 != 0f64) { 211 r *= math::expf64(-b.1 * phase); 212 theta += b.1 * math::logf64(mod); 213 }; 214 return rectc128(r, theta); 215 }; 216 217 // Projects z onto the surface of a Riemann Sphere. If z is finite, it projects 218 // to itself. If z is infinite, it projects to positive infinity on the real 219 // axis. 220 export fn projc64(z: c64) c64 = 221 if (!isinf(c64to128(z))) z else (math::INF, math::copysignf32(0f32, z.1)); 222 223 // Projects z onto the surface of a Riemann Sphere. If z is finite, it projects 224 // to itself. If z is infinite, it projects to positive infinity on the real 225 // axis. 226 export fn projc128(z: c128) c128 = 227 if (!isinf(z)) z else (math::INF, math::copysignf64(0f64, z.1)); 228 229 // Returns the square root of z. 230 export fn sqrtc128(z: c128) c128 = { 231 if (z.1 == 0f64) { 232 if (z.0 == 0f64) { 233 return (0f64, z.1); 234 }; 235 if (z.0 < 0f64) { 236 return (0f64, math::copysignf64(math::sqrtf64(-z.0), z.1)); 237 }; 238 return (math::sqrtf64(z.0), z.1); 239 }; 240 if (math::isinf(z.1)) { 241 return (math::INF, z.1); 242 }; 243 if (z.0 == 0f64) { 244 if (z.1 < 0f64) { 245 const r = math::sqrtf64(-0.5 * z.1); 246 return (r, -r); 247 } else { 248 const r = math::sqrtf64(0.5 * z.1); 249 return (r, r); 250 }; 251 }; 252 let a = z.0, b = z.1; 253 const scale = if (math::absf64(a) > 4f64 || math::absf64(b) > 4f64) { 254 a *= 0.25; 255 b *= 0.25; 256 yield 2f64; 257 } else { 258 a *= 1.8014398509481984e16; // 2**54 259 b *= 1.8014398509481984e16; 260 yield 7.450580596923828125e-9; // 2**-27 261 }; 262 let r = math::hypotf64(a, b); 263 const t = if (a > 0f64) { 264 const t = math::sqrtf64(0.5 * r + 0.5 * a); 265 r = scale * math::absf64(0.5 * b / t); 266 yield t * scale; 267 } else { 268 r = math::sqrtf64(0.5 * r - 0.5 * a); 269 const t = scale * math::absf64(0.5 * b / r); 270 r *= scale; 271 yield t; 272 }; 273 return (t, if (b < 0f64) -r else r); 274 }; 275 276 // Returns the sine of z, in radians. 277 export fn sinc128(z: c128) c128 = { 278 if (z.1 == 0f64 && (math::isinf(z.0) || math::isnan(z.0))) { 279 return (math::NAN, z.1); 280 } else if (math::isinf(z.1)) { 281 if (z.0 == 0f64) { 282 return z; 283 } else if (math::isinf(z.0) || math::isnan(z.0)) { 284 return (math::NAN, z.1); 285 }; 286 } else if (z.0 == 0f64 && math::isnan(z.1)) { 287 return z; 288 }; 289 const shch = sinhcosh(z.1); 290 return (math::sinf64(z.0) * shch.1, math::cosf64(z.0) * shch.0); 291 }; 292 293 // Returns the hyperbolic sine of z. 294 export fn sinhc128(z: c128) c128 = { 295 if (z.0 == 0f64 && (math::isinf(z.1) || math::isnan(z.1))) { 296 return (z.0, math::NAN); 297 } else if (math::isinf(z.0)) { 298 if (z.1 == 0f64) { 299 return z; 300 } else if (math::isinf(z.1) || math::isnan(z.1)) { 301 return (z.0, math::NAN); 302 }; 303 } else if (z.1 == 0f64 && math::isnan(z.0)) { 304 return (math::NAN, z.1); 305 }; 306 const shch = sinhcosh(z.0); 307 return (math::cosf64(z.1) * shch.0, math::sinf64(z.1) * shch.1); 308 }; 309 310 // Returns the arcsine, in radians, of z. 311 export fn asinc128(z: c128) c128 = { 312 if (z.1 == 0f64 && math::absf64(z.0) <= 1f64) { 313 return (math::asinf64(z.0), z.1); 314 } else if (z.0 == 0f64 && math::absf64(z.1) <= 1f64) { 315 return (z.0, math::asinhf64(z.1)); 316 } else if (math::isnan(z.1)) { 317 if (z.0 == 0f64) { 318 return (z.0, math::NAN); 319 } else if (math::isinf(z.0)) { 320 return (math::NAN, z.0); 321 } else { 322 return (math::NAN, math::NAN); 323 }; 324 } else if (math::isinf(z.1)) { 325 if (math::isnan(z.0)) { 326 return z; 327 } else if (math::isinf(z.0)) { 328 return (math::copysignf64(math::PI / 4f64, z.0), z.1); 329 } else { 330 return (math::copysignf64(0f64, z.0), z.1); 331 }; 332 } else if (math::isinf(z.0)) { 333 return (math::copysignf64(math::PI / 2f64, z.0), 334 math::copysignf64(z.0, z.1)); 335 }; 336 const ct = (-z.1, z.0); // i * z 337 const zz = mulc128(z, z); 338 const z1 = (1f64 - zz.0, -zz.1); // 1 - z * z 339 const z2 = sqrtc128(z1); // z2 = sqrt(1 - z * z) 340 const w = logc128(addc128(ct, z2)); 341 return (w.1, -w.0); // -i * w 342 }; 343 344 // Returns the inverse hyperbolic sine of z. 345 export fn asinhc128(z: c128) c128 = { 346 if (z.1 == 0f64 && math::absf64(z.0) <= 1f64) { 347 return (math::asinhf64(z.0), z.1); 348 } else if (z.0 == 0f64 && math::absf64(z.1) <= 1f64) { 349 return (z.0, math::asinf64(z.1)); 350 } else if (math::isinf(z.0)) { 351 if (math::isinf(z.1)) { 352 return (z.0, math::copysignf64(math::PI / 4f64, z.1)); 353 } else if (math::isnan(z.1)) { 354 return z; 355 } else { 356 return (z.0, math::copysignf64(0f64, z.1)); 357 }; 358 } else if (math::isnan(z.0)) { 359 if (z.1 == 0f64) { 360 return z; 361 } else if (math::isinf(z.1)) { 362 return (z.1, z.0); 363 } else { 364 return (math::NAN, math::NAN); 365 }; 366 } else if (math::isinf(z.1)) { 367 return (math::copysignf64(z.1, z.0), 368 math::copysignf64(math::PI / 2f64, z.1)); 369 }; 370 const zz = mulc128(z, z); 371 const z1 = (1f64 + zz.0, zz.1); // 1 + z * z 372 return logc128(addc128(z, sqrtc128(z1))); // log(x + sqrt(1 + x * x)) 373 }; 374 375 // Returns the cosine of z, in radians. 376 export fn cosc128(z: c128) c128 = { 377 if (z.1 == 0f64 && (math::isinf(z.0) || math::isnan(z.0))) { 378 return (math::NAN, -z.1 * math::copysignf64(0f64, z.0)); 379 } else if (math::isinf(z.1)) { 380 if (z.0 == 0f64) { 381 return (math::INF, -z.0 * math::copysignf64(0f64, z.1)); 382 } else if (math::isinf(z.0) || math::isnan(z.0)) { 383 return (math::INF, math::NAN); 384 }; 385 } else if (z.0 == 0f64 && math::isnan(z.1)) { 386 return (math::NAN, 0f64); 387 }; 388 const shch = sinhcosh(z.1); 389 return (math::cosf64(z.0) * shch.1, -math::sinf64(z.0) * shch.0); 390 }; 391 392 // Returns the hyperbolic cosine of z. 393 export fn coshc128(z: c128) c128 = { 394 if (z.0 == 0f64 && (math::isinf(z.1) || math::isnan(z.1))) { 395 return (math::NAN, z.0 * math::copysignf64(0f64, z.1)); 396 } else if (math::isinf(z.0)) { 397 if (z.1 == 0f64) { 398 return (math::INF, z.1 * math::copysignf64(0f64, z.0)); 399 } else if (math::isinf(z.1) || math::isnan(z.1)) { 400 return (math::INF, math::NAN); 401 }; 402 } else if (z.1 == 0f64 && math::isnan(z.0)) { 403 return (math::NAN, z.1); 404 }; 405 const shch = sinhcosh(z.0); 406 return (math::cosf64(z.1) * shch.1, math::sinf64(z.1) * shch.0); 407 }; 408 409 // Returns the arccosine, in radians, of z. 410 export fn acosc128(z: c128) c128 = { 411 const w = asinc128(z); 412 return (math::PI / 2f64 - w.0, -w.1); 413 }; 414 415 // Returns the inverse hyperbolic cosine of z. 416 export fn acoshc128(z: c128) c128 = { 417 if (z.0 == 0f64 && z.1 == 0f64) { 418 return (0f64, math::copysignf64(math::PI / 2f64, z.1)); 419 }; 420 const w = acosc128(z); 421 if (w.1 <= 0f64) { 422 return (-w.1, w.0); // i * w 423 }; 424 return (w.1, -w.0); // -i * w 425 }; 426 427 fn sinhcosh(x: f64) (f64, f64) = { 428 if (math::absf64(x) <= 0.5) { 429 return (math::sinhf64(x), math::coshf64(x)); 430 }; 431 let e = math::expf64(x); 432 const ei = 0.5 / e; 433 e *= 0.5; 434 return (e - ei, e + ei); 435 }; 436 437 // reducePi reduces the input argument x to the range (-Pi/2, Pi/2]. 438 // x must be greater than or equal to 0. For small arguments it 439 // uses Cody-Waite reduction in 3 f64 parts based on: 440 // "Elementary Function Evaluation: Algorithms and Implementation" 441 // Jean-Michel Muller, 1997. 442 // For very large arguments it uses Payne-Hanek range reduction based on: 443 // "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit" 444 // K. C. Ng et al, March 24, 1992. 445 fn reducePi(x: f64) f64 = { 446 // reduceThreshold is the maximum value of x where the reduction using 447 // Cody-Waite reduction still gives accurate results. This threshold 448 // is set by t*PIn being representable as a f64 without error 449 // where t is given by t = floor(x * (1 / Pi)) and PIn are the leading partial 450 // terms of Pi. Since the leading terms, PI1 and PI2 below, have 30 and 32 451 // trailing zero bits respectively, t should have less than 30 significant bits. 452 // t < 1<<30 -> floor(x*(1/Pi)+0.5) < 1<<30 -> x < (1<<30-1) * Pi - 0.5 453 // So, conservatively we can take x < 1<<30. 454 const reduceThreshold = (1u64 << 30): f64; 455 if (math::absf64(x) < reduceThreshold) { 456 // Use Cody-Waite reduction in three parts. 457 // PI1, PI2 and PI3 comprise an extended precision value of PI 458 // such that PI ~= PI1 + PI2 + PI3. The parts are chosen so 459 // that PI1 and PI2 have an approximately equal number of trailing 460 // zero bits. This ensures that t*PI1 and t*PI2 are exact for 461 // large integer values of t. The full precision PI3 ensures the 462 // approximation of PI is accurate to 102 bits to handle cancellation 463 // during subtraction. 464 const PI1 = 3.141592502593994; // 0x400921fb40000000 465 const PI2 = 1.5099578831723193e-07; // 0x3e84442d00000000 466 const PI3 = 1.0780605716316238e-14; // 0x3d08469898cc5170 467 let t = x / math::PI; 468 t += 0.5; 469 t = (t: i64): f64; 470 return ((x - t*PI1) - t*PI2) - t*PI3; 471 }; 472 // Must apply Payne-Hanek range reduction 473 const mask: u64 = 0x7FF; 474 const shift: u64 = 64 - 11 - 1; 475 const bias: u64 = 1023; 476 const fracMask: u64 = (1u64 << shift) - 1; 477 478 // Extract out the integer and exponent such that, 479 // x = ix * 2 ** exp. 480 let ix: u64 = math::f64bits(x); 481 let exp: u64 = (ix >> shift & mask) - bias - shift; 482 ix &= fracMask; 483 ix |= 1u64 << shift; 484 485 // mPi is the binary digits of 1/Pi as a u64 array, 486 // that is, 1/Pi = Sum mPi[i]*2^(-64*i). 487 // 19 64-bit digits give 1216 bits of precision 488 // to handle the largest possible float64 exponent. 489 let mPi: [_]u64 = [ 490 0x0000000000000000, 491 0x517cc1b727220a94, 492 0xfe13abe8fa9a6ee0, 493 0x6db14acc9e21c820, 494 0xff28b1d5ef5de2b0, 495 0xdb92371d2126e970, 496 0x0324977504e8c90e, 497 0x7f0ef58e5894d39f, 498 0x74411afa975da242, 499 0x74ce38135a2fbf20, 500 0x9cc8eb1cc1a99cfa, 501 0x4e422fc5defc941d, 502 0x8ffc4bffef02cc07, 503 0xf79788c5ad05368f, 504 0xb69b3f6793e584db, 505 0xa7a31fb34f2ff516, 506 0xba93dd63f5f2f8bd, 507 0x9e839cfbc5294975, 508 0x35fdafd88fc6ae84, 509 0x2b0198237e3db5d5, 510 ]; 511 // Use the exponent to extract the 3 appropriate u64 digits from mPi, 512 // B ~ (z0, z1, z2), such that the product leading digit has the exponent -64. 513 // Note, exp >= 50 since x >= reduceThreshold and exp < 971 for maximum f64. 514 let digit: u64 = (exp + 64): u64 / 64; 515 let bitshift: u64 = (exp + 64): u64 % 64; 516 let z0: u64 = (mPi[digit] << bitshift) | (mPi[digit+1] >> (64 - bitshift)); 517 let z1: u64 = (mPi[digit+1] << bitshift) | (mPi[digit+2] >> (64 - bitshift)); 518 let z2: u64 = (mPi[digit+2] << bitshift) | (mPi[digit+3] >> (64 - bitshift)); 519 // Multiply mantissa by the digits and extract the upper two digits (hi, lo). 520 let (z2hi, z2lo) = math::mulu64(z2, ix); 521 let (z1hi, z1lo) = math::mulu64(z1, ix); 522 let z0lo: u64 = z0 * ix; 523 let (lo, c) = checked::addu64(z1lo, z2hi); 524 let hi = z0lo + z1hi + (if (c) 1u64 else 0u64); 525 // Find the magnitude of the fraction. 526 let lz: u8 = math::leading_zeros_u64(hi); 527 let e: u64 = (bias - (lz + 1)): u64; 528 // Clear implicit mantissa bit and shift into place. 529 hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1))); 530 hi >>= 64 - shift; 531 // Include the exponent and convert to a float. 532 hi |= e << shift; 533 x = math::f64frombits(hi); 534 // map to (-Pi/2, Pi/2] 535 if (x > 0.5) { 536 x -= 1f64; 537 }; 538 return math::PI * x; 539 }; 540 541 // Taylor series expansion for cosh(2y) - cos(2x) 542 fn tanSeries(z: c128) f64 = { 543 const MACHEP = 1f64/(1u64 << 53): f64; 544 let x = math::absf64(2f64 * z.0); 545 let y = math::absf64(2f64 * z.1); 546 x = reducePi(x); 547 x = x * x; 548 y = y * y; 549 550 let x2 = 1f64; 551 let y2 = 1f64; 552 let f = 1f64; 553 let rn = 0f64; 554 let d = 0f64; 555 556 for (true) { 557 rn += 1f64; 558 f *= rn; 559 rn += 1f64; 560 f *= rn; 561 x2 *= x; 562 y2 *= y; 563 let t = y2 + x2; 564 t /= f; 565 d += t; 566 567 rn += 1f64; 568 f *= rn; 569 rn += 1f64; 570 f *= rn; 571 x2 *= x; 572 y2 *= y; 573 t = y2 - x2; 574 t /= f; 575 d += t; 576 if (!(math::absf64(t/d) > MACHEP)) { 577 // Caution: Use ! and > instead of <= for correct behavior if t/d is NaN. 578 // See issue https://github.com/golang/go/issues/17577. 579 break; 580 }; 581 }; 582 return d; 583 }; 584 585 // Returns the tangent of x. 586 export fn tanc128(x: c128) c128 = { 587 if (math::isinf(x.1)) { 588 if (math::isinf(x.0) || math::isnan(x.0)) { 589 return (math::copysignf64(0f64, x.0), math::copysignf64(1f64, x.1)); 590 }; 591 return (math::copysignf64(0f64, math::sinf64(2f64*x.0)), math::copysignf64(1f64, x.1)); 592 }; 593 if (x.0 == 0f64 && math::isnan(x.1)) { 594 return x; 595 }; 596 let d = math::cosf64(2f64*x.0) + math::coshf64(2f64*x.1); 597 if (math::absf64(d) < 0.25f64) { 598 d = tanSeries(x); 599 }; 600 if (d == 0f64) { 601 return (math::INF, math::INF); 602 }; 603 return (math::sinf64(2f64*x.0)/d, math::sinhf64(2f64*x.1)/d); 604 }; 605 606 // Returns the hyperbolic tangent of x. 607 export fn tanhc128(x: c128) c128 = { 608 if (math::isinf(x.0)) { 609 if (math::isinf(x.1) || math::isnan(x.1)) { 610 return (math::copysignf64(1f64, x.0), math::copysignf64(0f64, x.1)); 611 }; 612 return (math::copysignf64(1f64, x.0), math::copysignf64(0f64, math::sinf64(2f64*x.1))); 613 }; 614 if (x.1 == 0f64 && math::isnan(x.0)) { 615 return x; 616 }; 617 let d = math::coshf64(2f64*x.0) + math::cosf64(2f64*x.1); 618 if (d == 0f64) { 619 return (math::INF, math::INF); 620 }; 621 return (math::sinhf64(2f64*x.0)/d, math::sinf64(2f64*x.1)/d); 622 }; 623 624 // Returns the inverse tangent of x. 625 export fn atanc128(x: c128) c128 = { 626 if (x.1 == 0f64) { 627 return (math::atanf64(x.0), x.1); 628 } else if (x.0 == 0f64 && math::absf64(x.1) <= 1f64) { 629 return (x.0, math::atanhf64(x.1)); 630 } else if (math::isinf(x.1) || math::isinf(x.0)) { 631 if (math::isnan(x.0)) { 632 return (math::NAN, math::copysignf64(0f64, x.1)); 633 }; 634 return (math::copysignf64(math::PI/2f64, x.0), math::copysignf64(0f64, x.1)); 635 } else if (math::isnan(x.0) || math::isnan(x.1)) { 636 return (math::NAN, math::NAN); 637 }; 638 let x2 = x.0 * x.0; 639 let a = 1f64 - x2 - x.1 * x.1; 640 if (a == 0f64) { 641 return (math::NAN, math::NAN); 642 }; 643 let t = 0.5f64 * math::atan2f64(2f64*x.0, a); 644 let w = reducePi(t); 645 t = x.1 - 1f64; 646 let b = x2 + t*t; 647 if (b == 0f64) { 648 return (math::NAN, math::NAN); 649 }; 650 t = x.1 + 1f64; 651 let c = (x2 + t*t) / b; 652 return (w, 0.25f64*math::logf64(c)); 653 }; 654 655 // Returns the inverse hyperbolic tangent of x. 656 export fn atanhc128(x: c128) c128 = { 657 let z = (-x.1, x.0); // z = i * x 658 z = atanc128(z); 659 return (z.1, -z.0); // z = -i * z 660 };