hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };