Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

319
vendor/pxfm/src/err/erf.rs vendored Normal file
View File

@@ -0,0 +1,319 @@
/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::{dd_fmla, dyad_fmla, f_fmla};
use crate::double_double::DoubleDouble;
use crate::err::erf_poly::{ERF_POLY, ERF_POLY_C2};
use crate::floor::FloorFinite;
/* double-double approximation of 2/sqrt(pi) to nearest */
const TWO_OVER_SQRT_PI: DoubleDouble = DoubleDouble::new(
f64::from_bits(0x3c71ae3a914fed80),
f64::from_bits(0x3ff20dd750429b6d),
);
pub(crate) struct Erf {
pub(crate) result: DoubleDouble,
pub(crate) err: f64,
}
/* for |z| < 1/8, assuming z >= 2^-61, thus no underflow can occur */
#[cold]
fn cr_erf_accurate_tiny(x: f64) -> DoubleDouble {
static P: [u64; 15] = [
0x3ff20dd750429b6d,
0x3c71ae3a914fed80,
0xbfd812746b0379e7,
0x3c6ee12e49ca96ba,
0x3fbce2f21a042be2,
0xbc52871bc0a0a0d0,
0xbf9b82ce31288b51,
0x3c21003accf1355c,
0x3f7565bcd0e6a53f,
0xbf4c02db40040cc3,
0x3f1f9a326fa3cf50,
0xbeef4d25e3c73ce9,
0x3ebb9eb332b31646,
0xbe864a4bd5eca4d7,
0x3e6c0acc2502e94e,
];
let z2 = x * x;
let mut h = f64::from_bits(P[21 / 2 + 4]); /* degree 21 */
for a in (12..=19).rev().step_by(2) {
h = dd_fmla(h, z2, f64::from_bits(P[(a / 2 + 4) as usize]))
}
let mut l = 0.;
for a in (8..=11).rev().step_by(2) {
let mut t = DoubleDouble::from_exact_mult(h, x);
t.lo = dd_fmla(l, x, t.lo);
let mut k = DoubleDouble::from_exact_mult(t.hi, x);
k.lo = dd_fmla(t.lo, x, k.lo);
let p = DoubleDouble::from_exact_add(f64::from_bits(P[(a / 2 + 4) as usize]), k.hi);
l = k.lo + p.lo;
h = p.hi;
}
for a in (1..=7).rev().step_by(2) {
let mut t = DoubleDouble::from_exact_mult(h, x);
t.lo = dd_fmla(l, x, t.lo);
let mut k = DoubleDouble::from_exact_mult(t.hi, x);
k.lo = dd_fmla(t.lo, x, k.lo);
let p = DoubleDouble::from_exact_add(f64::from_bits(P[a - 1]), k.hi);
l = k.lo + p.lo + f64::from_bits(P[a]);
h = p.hi;
}
/* multiply by z */
let p = DoubleDouble::from_exact_mult(h, x);
l = dd_fmla(l, x, p.lo);
DoubleDouble::new(l, p.hi)
}
/* Assuming 0 <= z <= 0x1.7afb48dc96626p+2, put in h+l an accurate
approximation of erf(z).
Assumes z >= 2^-61, thus no underflow can occur. */
#[cold]
#[inline(never)]
pub(crate) fn erf_accurate(x: f64) -> DoubleDouble {
if x < 0.125
/* z < 1/8 */
{
return cr_erf_accurate_tiny(x);
}
let v = (8.0 * x).floor_finite();
let i: u32 = (8.0 * x) as u32;
let z = (x - 0.0625) - 0.125 * v;
/* now |z| <= 1/16 */
let p = ERF_POLY_C2[(i - 1) as usize];
let mut h = f64::from_bits(p[26]); /* degree-18 */
for a in (11..=17).rev() {
h = dd_fmla(h, z, f64::from_bits(p[(8 + a) as usize])); /* degree j */
}
let mut l: f64 = 0.;
for a in (8..=10).rev() {
let mut t = DoubleDouble::from_exact_mult(h, z);
t.lo = dd_fmla(l, z, t.lo);
let p = DoubleDouble::from_exact_add(f64::from_bits(p[(8 + a) as usize]), t.hi);
h = p.hi;
l = p.lo + t.lo;
}
for a in (0..=7).rev() {
let mut t = DoubleDouble::from_exact_mult(h, z);
t.lo = dd_fmla(l, z, t.lo);
/* add p[2*j] + p[2*j+1] to th + tl: we use two_sum() instead of
fast_two_sum because for example for i=3, the coefficient of
degree 7 is tiny (0x1.060b78c935b8ep-13) with respect to that
of degree 8 (0x1.678b51a9c4b0ap-7) */
let v = DoubleDouble::from_exact_add(f64::from_bits(p[(2 * a) as usize]), t.hi);
h = v.hi;
l = v.lo + t.lo + f64::from_bits(p[(2 * a + 1) as usize]);
}
DoubleDouble::new(l, h)
}
/* Assuming 0 <= z <= 5.9215871957945065, put in h+l an approximation
of erf(z). Return err the maximal relative error:
|(h + l)/erf(z) - 1| < err*|h+l| */
#[inline]
pub(crate) fn erf_fast(x: f64) -> Erf {
/* we split [0,5.9215871957945065] into intervals i/16 <= z < (i+1)/16,
and for each interval, we use a minimax polynomial:
* for i=0 (0 <= z < 1/16) we use a polynomial evaluated at zero,
since if we evaluate in the middle 1/32, we will get bad accuracy
for tiny z, and moreover z-1/32 might not be exact
* for 1 <= i <= 94, we use a polynomial evaluated in the middle of
the interval, namely i/16+1/32
*/
if x < 0.0625
/* z < 1/16 */
{
/* the following is a degree-11 minimax polynomial for erf(x) on [0,1/16]
generated by Sollya, with double-double coefficients for degree 1 and 3,
and double coefficients for degrees 5 to 11 (file erf0.sollya).
The maximal relative error is 2^-68.935. */
let z2 = DoubleDouble::from_exact_mult(x, x);
const C: [u64; 8] = [
0x3ff20dd750429b6d,
0x3c71ae3a7862d9c4,
0xbfd812746b0379e7,
0x3c6f1a64d72722a2,
0x3fbce2f21a042b7f,
0xbf9b82ce31189904,
0x3f7565bbf8a0fe0b,
0xbf4bf9f8d2c202e4,
];
let z4 = z2.hi * z2.hi;
let c9 = dd_fmla(f64::from_bits(C[7]), z2.hi, f64::from_bits(C[6]));
let mut c5 = dd_fmla(f64::from_bits(C[5]), z2.hi, f64::from_bits(C[4]));
c5 = dd_fmla(c9, z4, c5);
/* compute c0[2] + c0[3] + z2h*c5 */
let mut t = DoubleDouble::from_exact_mult(z2.hi, c5);
let mut v = DoubleDouble::from_exact_add(f64::from_bits(C[2]), t.hi);
v.lo += t.lo + f64::from_bits(C[3]);
/* compute c0[0] + c0[1] + (z2h + z2l)*(h + l) */
t = DoubleDouble::from_exact_mult(z2.hi, v.hi);
let h_c = v.hi;
t.lo += dd_fmla(z2.hi, v.lo, f64::from_bits(C[1]));
v = DoubleDouble::from_exact_add(f64::from_bits(C[0]), t.hi);
v.lo += dd_fmla(z2.lo, h_c, t.lo);
v = DoubleDouble::quick_mult_f64(v, x);
return Erf {
result: v,
err: f64::from_bits(0x3ba7800000000000),
}; /* err < 2.48658249618372e-21, cf Analyze0() */
}
let v = (16.0 * x).floor_finite();
let i: u32 = (16.0 * x) as u32;
/* i/16 <= z < (i+1)/16 */
/* For 0.0625 0 <= z <= 0x1.7afb48dc96626p+2, z - 0.03125 is exact:
(1) either z - 0.03125 is in the same binade as z, then 0.03125 is
an integer multiple of ulp(z), so is z - 0.03125
(2) if z - 0.03125 is in a smaller binade, both z and 0.03125 are
integer multiple of the ulp() of that smaller binade.
Also, subtracting 0.0625 * v is exact. */
let z = (x - 0.03125) - 0.0625 * v;
/* now |z| <= 1/32 */
let c = ERF_POLY[(i - 1) as usize];
let z2 = z * z;
let z4 = z2 * z2;
/* the degree-10 coefficient is c[12] */
let c9 = dd_fmla(f64::from_bits(c[12]), z, f64::from_bits(c[11]));
let mut c7 = dd_fmla(f64::from_bits(c[10]), z, f64::from_bits(c[9]));
let c5 = dd_fmla(f64::from_bits(c[8]), z, f64::from_bits(c[7]));
/* c3h, c3l <- c[5] + z*c[6] */
let mut c3 = DoubleDouble::from_exact_add(f64::from_bits(c[5]), z * f64::from_bits(c[6]));
c7 = dd_fmla(c9, z2, c7);
/* c3h, c3l <- c3h, c3l + c5*z2 */
let p = DoubleDouble::from_exact_add(c3.hi, c5 * z2);
c3.hi = p.hi;
c3.lo += p.lo;
/* c3h, c3l <- c3h, c3l + c7*z4 */
let p = DoubleDouble::from_exact_add(c3.hi, c7 * z4);
c3.hi = p.hi;
c3.lo += p.lo;
/* c2h, c2l <- c[4] + z*(c3h + c3l) */
let mut t = DoubleDouble::from_exact_mult(z, c3.hi);
let mut c2 = DoubleDouble::from_exact_add(f64::from_bits(c[4]), t.hi);
c2.lo += dd_fmla(z, c3.lo, t.lo);
/* compute c[2] + c[3] + z*(c2h + c2l) */
t = DoubleDouble::from_exact_mult(z, c2.hi);
let mut v = DoubleDouble::from_exact_add(f64::from_bits(c[2]), t.hi);
v.lo += t.lo + dd_fmla(z, c2.lo, f64::from_bits(c[3]));
/* compute c[0] + c[1] + z*(h + l) */
t = DoubleDouble::from_exact_mult(z, v.hi);
t.lo = dd_fmla(z, v.lo, t.lo);
v = DoubleDouble::from_exact_add(f64::from_bits(c[0]), t.hi);
v.lo += t.lo + f64::from_bits(c[1]);
Erf {
result: v,
err: f64::from_bits(0x3ba1100000000000),
} /* err < 1.80414390200020e-21, cf analyze_p(1)
(larger values of i yield smaller error bounds) */
}
/// Error function
///
/// Max ULP 0.5
pub fn f_erf(x: f64) -> f64 {
let z = f64::from_bits(x.to_bits() & 0x7fff_ffff_ffff_ffff);
let mut t = z.to_bits();
let ux = t;
/* erf(x) rounds to +/-1 for RNDN for |x| > 0x4017afb48dc96626 */
if ux > 0x4017afb48dc96626
// |x| > 0x4017afb48dc96626
{
let os = f64::copysign(1.0, x);
const MASK: u64 = 0x7ff0000000000000u64;
if ux > MASK {
return x + x; /* NaN */
}
if ux == MASK {
return os; /* +/-Inf */
}
return f_fmla(-f64::from_bits(0x3c90000000000000), os, os);
}
/* now |x| <= 0x4017afb48dc96626 */
if z < f64::from_bits(0x3c20000000000000) {
/* for x=-0 the code below returns +0 which is wrong */
if x == 0. {
return x;
}
/* tiny x: erf(x) ~ 2/sqrt(pi) * x + O(x^3), where the ratio of the O(x^3)
term to the main term is in x^2/3, thus less than 2^-123 */
let y = TWO_OVER_SQRT_PI.hi * x; /* tentative result */
/* scale x by 2^106 to get out the subnormal range */
let sx = x * f64::from_bits(0x4690000000000000);
let mut p = DoubleDouble::quick_mult_f64(TWO_OVER_SQRT_PI, sx);
/* now compute the residual h + l - y */
p.lo += f_fmla(-y, f64::from_bits(0x4690000000000000), p.hi); /* h-y*2^106 is exact since h and y are very close */
let res = dyad_fmla(p.lo, f64::from_bits(0x3950000000000000), y);
return res;
}
let result = erf_fast(z);
let mut u = result.result.hi.to_bits();
let mut v = result.result.lo.to_bits();
t = x.to_bits();
const SIGN_MASK: u64 = 0x8000000000000000u64;
u ^= t & SIGN_MASK;
v ^= t & SIGN_MASK;
let left = f64::from_bits(u) + f_fmla(result.err, -f64::from_bits(u), f64::from_bits(v));
let right = f64::from_bits(u) + f_fmla(result.err, f64::from_bits(u), f64::from_bits(v));
if left == right {
return left;
}
let a_results = erf_accurate(z);
if x >= 0. {
a_results.to_f64()
} else {
(-a_results.hi) + (-a_results.lo)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_erf() {
assert_eq!(f_erf(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009456563898732),
0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010670589695636709);
assert_eq!(f_erf(0.), 0.);
assert_eq!(f_erf(1.), 0.8427007929497149);
assert_eq!(f_erf(0.49866735123), 0.5193279892991808);
assert_eq!(f_erf(-0.49866735123), -0.5193279892991808);
assert!(f_erf(f64::NAN).is_nan());
assert_eq!(f_erf(f64::INFINITY), 1.0);
assert_eq!(f_erf(f64::NEG_INFINITY), -1.0);
}
}

177
vendor/pxfm/src/err/erf_poly.rs vendored Normal file
View File

@@ -0,0 +1,177 @@
/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#[rustfmt::skip]
pub(crate) static ERF_POLY: [[u64; 13]; 94] = [
[ 0x3fbb0081148a873a, 0xbc2f0295f16ba5d8, 0x3ff1e565bca400d4, 0xbc962d0ac26c78d3, 0xbfbad8189af6013d, 0xbfd7712743c42914, 0x3faaafd4760d7634, 0x3fbba14988b4127e, 0xbf91afcdb244078a, 0xbf99d72ee25cf211, 0x3f719502f7beca8f, 0x3f73b955bfd46624, 0xbf4a4e2d4d32228b ],
[ 0x3fc662a0bdf7a89f, 0xbc4ef7bc5856c2d4, 0x3ff19e5e92b964ab, 0x3c6cca4dec08a640, 0xbfc605f63767bdd6, 0xbfd6582e9b69c9a9, 0x3fb5aa32b580ec64, 0x3fb97594c2593d3e, 0xbf9c69c62749fb7f, 0xbf96fa7f611aacdc, 0x3f7bf1e628a4606e, 0x3f70e50e4329e8a9, 0xbf568ca9c1954b4c ],
[ 0x3fcf190aa85540e2, 0xbc6e522ac9f718e6, 0x3ff135e3075d076b, 0xbc6e2d8ed30e4a48, 0xbfce1e4d4ce2ccfb, 0xbfd4c04e66e0d59b, 0x3fbd2855d59988e8, 0x3fb659a35f29781a, 0xbfa2cf6266a634c8, 0xbf92ef4180b1f3fa, 0x3f823199a6da60e3, 0x3f69e80d13a3368c, 0xbf5ba4e4eff641dd ],
[ 0x3fd3c9aa8b84beda, 0x3c538ec27d3e5820, 0x3ff0ae54fa490723, 0xbc9d016b7bc67433, 0xbfd2c41f99922807, 0xbfd2b900b640a201, 0x3fc1c6c7eef8fa14, 0x3fb277ad7822021e, 0xbfa66c9b2023b9df, 0xbf8bf7e7b4e8559e, 0x3f853005de4b5751, 0x3f60737c6ba405f0, 0xbf606ccc916b15dc ],
[ 0x3fd7e15944d9d3e4, 0xbc695f819cf77862, 0x3ff00abcf3e187a9, 0x3c85860d868dc542, 0xbfd60ec3cf561a89, 0xbfd05599bafe4ecc, 0x3fc451ef6280e70f, 0x3fac06c6e434be6f, 0xbfa8e2d73679096f, 0xbf80ea4a60550d9c, 0x3f86c911882cc99c, 0x3f48c65a9990353b, 0xbf61e8a88301a7b5 ],
[ 0x3fdbccfec24855b8, 0xbc7472ab1c2b898c, 0x3fee9d5a8e4c934e, 0xbc79a002a2814a72, 0xbfd8dfd9939e37af, 0xbfcb588d8dc5bb96, 0x3fc62338788aee97, 0x3fa26cf85bc6dff9, 0xbfaa1bcaa91da902, 0xbf65b4a7d42d0f64, 0x3f86edef7de2b68d, 0xbf4037b458e2da8c, 0xbf5e8d6001a54334 ],
[ 0x3fdf86faa9428f9d, 0x3c79996c0c376e32, 0x3fecfc41e36c7df9, 0xbc79be994724ea34, 0xbfdb2c7dc535b619, 0xbfc5a9de93f9c0d5, 0x3fc7317958d24aae, 0x3f9133e02ab7d777, 0xbfaa155bbde32db8, 0x3f672049c0cc8525, 0x3f85adde5c722d85, 0xbf5b0a7ec5dc80fc, 0xbf5aa9393b806535 ],
[ 0x3fe1855a5fd3dd50, 0x3c88f6964e67d61a, 0x3feb3aafcc27502e, 0xbc7a9dd26edea8a2, 0xbfdcee5ac8e9c531, 0xbfbfa02983c853d1, 0x3fc77cd75ec73100, 0xbf5fa6f82f9333b7, 0xbfa8e0db5528e559, 0x3f800bf7062212bc, 0x3f83319e670adc9f, 0xbf658833e091aa36, 0xbf58f99b6e81e8f5 ],
[ 0x3fe32a54cb8db67b, 0xbc696221f7e18978, 0x3fe96164fafd8de3, 0x3c70887f82841acc, 0xbfde23a7ea0d187e, 0xbfb3f5ee1564be49, 0x3fc70e469de06907, 0xbf93da6878ae6fd8, 0xbfa6a0d076468415, 0x3f88cf081f1fc304, 0x3f7f6d62866525e6, 0xbf6b93149d5701a4, 0xbf51a6c1a9f7ea73 ],
[ 0x3fe4b13713ad3513, 0x3c6e944ee1b212e4, 0x3fe7791b886e7403, 0xbc6da43cb53d911c, 0xbfdecef42310f844, 0xbfa15c3c5ce705df, 0x3fc5f6890affa468, 0xbfa1da642fabd4da, 0xbfa385991202c7eb, 0x3f8fa4f37fc7c6d4, 0x3f77156b4e430998, 0xbf6f546a4377d648, 0xbf432e4e5abb1e1a ],
[ 0x3fe61955607dd15d, 0x3c898ff39319ab83, 0x3fe58a445da7c74c, 0x3c808ec8e156809b, 0xbfdef6c246a12e7e, 0x3f7e83e0da030480, 0x3fc44cc65df8bfc7, 0xbfa87d3c8dd62c82, 0xbf9f9271a8a1d4e2, 0x3f9225234c1c0a0e, 0x3f6c0b0e055a0c48, 0xbf70585251f84919, 0xbf285bfb02436e0f ],
[ 0x3fe762870f720c6f, 0x3c8118b1ba6da9a7, 0x3fe39ccc1b136d5a, 0x3c5faa9371c0dd80, 0xbfdea4feea4e5add, 0x3fa715e595343353, 0x3fc22cdbdb4cdd0c, 0xbfada50ae547e69e, 0xbf975578f87f217d, 0x3f9353319c65f251, 0x3f539db53a2d03d5, 0xbf6fc0364ce17870, 0x3f3272bc18b0f2ce ],
[ 0x3fe88d1cd474a2e0, 0x3c86f571ada77d52, 0x3fe1b7e98fe26217, 0x3c7952bd607eb12e, 0xbfdde65a22ce0587, 0x3fb40686a3f3dc2b, 0x3fbf6b0cb6926c42, 0xbfb09c7caecd317d, 0xbf8da668f759eaea, 0x3f9364e72035e80a, 0xbf4d421975736447, 0xbf6cc98454e96141, 0x3f4a8860fdf17259 ],
[ 0x3fe999d4192a5715, 0xbc8c888a5759a92c, 0x3fdfc3ee5d1524b0, 0xbc527e60faac0278, 0xbfdcc990045b293f, 0x3fbb37338e6ac814, 0x3fba0d11fe9ba61a, 0xbfb19bb2ca3816ba, 0xbf7a0b7d94791f03, 0x3f9274a59774d5e6, 0xbf664adea7b36f57, 0xbf683684bd8ef173, 0x3f538905afd229ff ],
[ 0x3fea89c850b7d54d, 0xbc8e2752ebf0cd02, 0x3fdc40b0729ed548, 0xbc7c4c1c4927306d, 0xbfdb5eaaef09de9d, 0x3fc0847c7dad86af, 0x3fb47de0a4f796ca, 0xbfb1d9de8b54a3ec, 0x3f533252fb810c7c, 0x3f90ab3e329ded2f, 0xbf712d82076274ed, 0xbf6287bb4a78d728, 0x3f557d31bd574da0 ],
[ 0x3feb5e62fce16095, 0x3c7bc3cff4400364, 0x3fd8eed36b886d93, 0x3c7ea7e17b96436d, 0xbfd9b64a06e4b100, 0x3fc2bb6e2c74d4fe, 0x3fadee322c062364, 0xbfb169960d5a983d, 0x3f7feab4ad0bfc14, 0x3f8c76eb94b07a5f, 0xbf7584474ae8f994, 0xbf588df75be9251f, 0x3f54edef50317090 ],
[ 0x3fec194b1d49a184, 0xbc66770a58b27668, 0x3fd5d4fd33729015, 0xbc76db7d76e9e97b, 0xbfd7e0f4f0454d97, 0x3fc444bc66c35bc4, 0x3fa356dbb5432550, 0xbfb0643de6e8c574, 0x3f8b2e1f789415e4, 0x3f86ba6d9f4af32f, 0xbf78138bf4573a6a, 0xbf47e6e52a583322, 0x3f50f87322fa18a3 ],
[ 0x3fecbc54b476248d, 0x3c81a5083b01ec0d, 0x3fd2f7cc3fe6f423, 0x3c79fbb4b774e85d, 0xbfd5ee8429e30a49, 0x3fc52a8395f96270, 0x3f9313759f199499, 0xbfadcf844d90282c, 0x3f91e45f25ab54a1, 0x3f8091cb68a58665, 0xbf78ea40b0ac8b7b, 0xbee6b91b1bf985f2, 0x3f5158d9c0e1c327 ],
[ 0x3fed4970f9ce00d9, 0xbc756704209fca70, 0x3fd059f59af7a906, 0xbc70ce27da57f153, 0xbfd3eda354ddd5ff, 0x3fc57b85ad436067, 0x3f58e90c2a157e8d, 0xbfaa2893b28f4033, 0x3f94d6af4484a1cb, 0x3f74ccee8c8b1f57, 0xbf783304b9e2e312, 0x3f440cb679d0a832, 0x3f4d6b5f4bdef24b ],
[ 0x3fedc29fb60715af, 0x3c8ab029f047a087, 0x3fcbf8e1b1ca2279, 0x3be0426e10a38000, 0xbfd1eb7095e57e16, 0x3fc549ea6f7a013f, 0xbf8b10f20d110552, 0xbfa61420b5b34a55, 0x3f9677b7ea46c6f2, 0x3f624f9940ffd840, 0xbf76304445e5f6ca, 0x3f5222fabfa75bb0, 0x3f3fdcf55be3c03e ],
[ 0x3fee29e22a89d766, 0x3c8bcc9d569ed217, 0x3fc7bd5c7df3fe9c, 0x3c6488f3b06e1394, 0xbfcfe674493fde22, 0x3fc4a9feacf7e222, 0xbf9a0082c90a1b0d, 0xbfa1cf0e7655f99a, 0x3f96e3396f042620, 0xbf33a2d2cdd5650d, 0xbf7334add14b9a31, 0x3f57e12864580191, 0x3f3dae75c3e2be46 ],
[ 0x3fee812fc64db369, 0x3c83c66a6a23d9a5, 0x3fc3fda6bc016994, 0x3c6586ddaff31a18, 0xbfcc1cb27861fc79, 0x3fc3b1051230b982, 0xbfa1e645a2a638ff, 0xbf9b1f643b14fd89, 0x3f964297d7a66c20, 0xbf63e365adfbccae, 0xbf6f2aa2b3ef5ec2, 0x3f5b3339ee2c8c49, 0x3f20ef5710223110 ],
[ 0x3feeca6ccd709544, 0x3c6f3de8f1953470, 0x3fc0b3f52ce8c383, 0x3c6d1234b508bcfb, 0xbfc8885019f5df29, 0x3fc274275fc87eae, 0xbfa57f7386bfd263, 0xbf930769f45aaa8b, 0x3f94c8231709cfee, 0xbf70c2c99c75913f, 0xbf67514483efc090, 0x3f5c3ebcf121a533, 0x3eede2f1801b8480 ],
[ 0x3fef0762fde45ee6, 0x3c89c3612a14fb77, 0x3fbbb1c972f23e50, 0x3c5ba69c564971e1, 0xbfc5341e3c0177b6, 0x3fc107929f6e7528, 0xbfa7e1b362eacfe6, 0xbf873b61e487b8a9, 0x3f92aa763e0343a9, 0xbf759a388fd2272d, 0xbf5eea3c7f50e8de, 0x3f5b5026fd87d0ca, 0xbf30f2c660125dc6 ],
[ 0x3fef39bc242e43e6, 0xbc8dbae0fd9b967d, 0x3fb6c7e64e7281cb, 0x3c5aa87392dc4c20, 0xbfc2274b86833f6e, 0x3fbefb890e5b6633, 0xbfa92c7dbb880b5c, 0xbf74547708842f2b, 0x3f902047ab6c08c4, 0xbf7888355239e9ec, 0xbf50313bb85e86e1, 0x3f58ced9ddf3d834, 0xbf32d520499bd799 ],
[ 0x3fef62fe80272419, 0xbc8b7c2d17fc31d3, 0x3fb297db960e4f63, 0xbc522bea9385fad9, 0xbfbecb83b087b37b, 0x3fbbce18363bbbb9, 0xbfa985aaf97891cb, 0x3f3cd95f2aa8601a, 0x3f8ab9d43270d20f, 0xbf79b93410d46789, 0xbf29b530b472cadf, 0x3f552f54de527458, 0xbf36844d43c7d693 ],
[ 0x3fef848acb544e95, 0xbc8b27aa2c376c3c, 0x3fae1d4cf1e2450a, 0xbc4783e14555c1e9, 0xbfb9e12e1fde7354, 0x3fb8a27806de834f, 0xbfa91674e13a339a, 0x3f73bc75e8f9d448, 0x3f851b4d09ac47b8, 0xbf796dc7b5f9bd66, 0x3f3e16520532bde9, 0x3f50e742b323f434, 0xbf3ac319bfed91d4 ],
[ 0x3fef9f9ba8d3c733, 0x3c8cd5790ff03ab3, 0x3fa83298d717210e, 0x3c4740e2b04276bf, 0xbfb58d101f909971, 0x3fb58f1456f7db5e, 0xbfa808d17b33b814, 0x3f80c1bdce673b10, 0x3f7f5ff1c06e9df2, 0xbf77f26b8865f398, 0x3f4f87060e6f6460, 0x3f48c6056bea9223, 0xbf3e3499a90b84f5 ],
[ 0x3fefb54641aebbc9, 0xbc879975513f67e7, 0x3fa34ac36ad8dafe, 0x3c0902fb5363d360, 0xbfb1c8ec267fe9e2, 0x3fb2a52c5d83c050, 0xbfa68541b2c0582c, 0x3f85afe422155ad5, 0x3f756303c111cd8a, 0xbf7597ead749c06a, 0x3f557b0870a7b4cf, 0x3f3ffc0efb0ac024, 0xbf39e3ea349ab39e ],
[ 0x3fefc67bcf2d7b8f, 0xbc80d2748f976e8c, 0x3f9e85c449e377f3, 0xbc3cb7ccd2616394, 0xbfad177f166cce53, 0x3fafe23b75845cdf, 0xbfa4b120f9dde895, 0x3f88d9906d138bd5, 0x3f69201b7e469e83, 0xbf72aceacb2954f0, 0x3f58d4e8140dc518, 0x3f300a33f7e93047, 0xbf372b7adfeee575 ],
[ 0x3fefd40bd6d7a785, 0x3c860d45e630998f, 0x3f97f5188610ddc8, 0xbc360e8565137ecb, 0xbfa7954423f89a51, 0x3faaf5baae337ae6, 0xbfa2ad77b77d17dc, 0x3f8a7b8c4a8d53fe, 0x3f54593adc5d737a, 0xbf6ef1cf14455c9c, 0x3f5a1a04ce289b4b, 0x3f03d14f37840954, 0xbf350b861df174ee ],
[ 0x3fefdea6e062d0c9, 0xbc764c70f379f670, 0x3f92a875b5ffab56, 0x3c0531231987c3b8, 0xbfa2f3178cd7aa03, 0x3fa68d1c45b96efe, 0xbfa09648dd332653, 0x3f8ad8b148089c02, 0xbf2f00fa01e6ca19, 0xbf68718785b34600, 0x3f59a7b0da775387, 0xbf2090258ede6532, 0xbf2b3980b454d442 ],
[ 0x3fefe6e1742f7cf6, 0xbc8cebced8a49e04, 0x3f8cd5ec93c12432, 0xbc2bb85326a5eff3, 0xbf9e2ff3aaae31e4, 0x3fa2aa4e58242520, 0xbf9d049824fc44db, 0x3f8a34eda0fc336e, 0xbf5682d8d1801582, 0xbf6239bf51e17ea8, 0x3f57e761274bf059, 0xbf301e715d70d49f, 0xbf24d89f3d9c30d5 ],
[ 0x3fefed37386190fb, 0x3c872b1549ea44ee, 0x3f861beae53b72b7, 0x3bf401790f84b248, 0xbf97d6193f2417ad, 0x3f9e947279e4a43b, 0xbf99060301092cdc, 0x3f88d14d4bdaa7f4, 0xbf61f795ac880380, 0xbf59222edb6bd145, 0x3f553f95c7b01615, 0xbf3529b07d094e1d, 0xbf15b533d0382e20 ],
[ 0x3feff20e0a7ba8c2, 0xbc603f86c5a13f78, 0x3f80d1d69569b82d, 0xbc1a5e866bd1366e, 0xbf92a8ca0dc14852, 0x3f98cc071b719c43, 0xbf954a148886e917, 0x3f86e91361df3c9e, 0xbf665c02e0d08291, 0xbf4e94b0adc3b1ca, 0x3f5210781b57b089, 0xbf37b88f8c82fbff, 0xbf068df27e9a1688 ],
[ 0x3feff5b8fb26f5f6, 0xbc87e917ec20b615, 0x3f79646f35a76624, 0xbc1f771f32fd191b, 0xbf8cf68ed932f081, 0x3f93e8735b5b73b1, 0xbf91e1611aabcbea, 0x3f84afd8cd100d70, 0xbf68c72005b1cfcf, 0xbf3c6a7216b336aa, 0x3f4d577412afc2e2, 0xbf3836a0c0e10a99, 0x3eca8f39f410252a ],
[ 0x3feff87b1913e853, 0xbc73ca98afc58454, 0x3f730499b503957f, 0xbbfd1eabb1c04f50, 0xbf86496420203331, 0x3f8fa73d7eb1b70d, 0xbf8daa3005c2d3fe, 0x3f8250942c31c3ad, 0xbf6997578dc240a8, 0xbf03904177639e63, 0x3f46a6ed488a1f54, 0xbf371cf0c5789c7d, 0x3f043cb84231ab1c ],
[ 0x3feffa89fe5b3625, 0x3c8934b2bcb7f9a3, 0x3f6c4412bf4b8f0b, 0xbbcbbcc9dca4ec60, 0xbf8100f34713740d, 0x3f88ebda0768e8e6, 0xbf8850c68e8e5c3c, 0x3f7fdac8346071b3, 0xbf6929de70d00321, 0x3f310c7101bc52d8, 0x3f4070f7e89ec1e2, 0xbf34e4b3dcf4f08d, 0x3f0f0d43b9869b19 ],
[ 0x3feffc10194fcb64, 0x3c8ea14750ac9b59, 0x3f64d78bba8ca5fd, 0x3be4d9a93566b5b4, 0xbf79ba107a459ce4, 0x3f836f273fbd909b, 0xbf83b38708f7bef7, 0x3f7b3fdff1de2112, 0xbf67d55d55d262d8, 0x3f3eae5e05e74fcc, 0x3f35ebc1e53214a9, 0xbf31fd7c1cd5d63e, 0x3f149559a04c8568 ],
[ 0x3feffd2eae369a07, 0xbc683b09df7f7db4, 0x3f5e7f232d9e2630, 0x3bfa26ac725599e5, 0xbf734c7442de142b, 0x3f7e066bed09942f, 0xbf7f914f2c60b9bb, 0x3f76f4662f6be13b, 0xbf65e664591d6604, 0x3f43a1598d880f36, 0x3f2965b2e78a4544, 0xbf2d8db42b193729, 0x3f1449172919598e ],
[ 0x3feffdff92db56e5, 0xbc78aeef4ee0690a, 0x3f56235fbd7a4345, 0xbbe11380fe434056, 0xbf6cb5e029ba8f3d, 0x3f76fa4c7ef470e9, 0xbf7903a08305eeb0, 0x3f730f12c83fdb23, 0xbf639d769a774af1, 0x3f45d79439ceaefd, 0x3f15326883e7dfeb, 0xbf27199782285958, 0x3f147181c8911603 ],
[ 0x3feffe96a78a04a9, 0xbc82816fe4528f9b, 0x3f4fe41cd9bb4eee, 0x3bde3be508cae7ec, 0xbf652d7b2896626a, 0x3f716c192d8803dc, 0xbf739bfce9b4ecc2, 0x3f6f376a554e5dec, 0xbf612e67cb7aa486, 0x3f466d6e460b1614, 0xbed54f70e4bde32b, 0xbf210e125571fe1e, 0x3f12842d46eb9f29 ],
[ 0x3fefff0312b010b5, 0x3c8155dec9cdc96b, 0x3f46caa0d3582fe9, 0xbbc97d95851163fc, 0xbf5efb729f4be121, 0x3f6a2da7cec01564, 0xbf6e6c27ad2b1ce0, 0x3f693b1f34b17723, 0xbf5d8179cd2ad34f, 0x3f45cf51e0add9bb, 0xbf116d8f4b5119c7, 0xbf1768557564f5f5, 0x3f0f4fc9dde73f24 ],
[ 0x3fefff50456dab8c, 0xbc5a197a986f0de0, 0x3f40295ef6591848, 0xbbd262bd83520706, 0xbf5679880e93e5c4, 0x3f637d38e3a705af, 0xbf675b371a264745, 0x3f64231c3bfe3e65, 0xbf58e184d4921105, 0x3f445d5b5a7f77fa, 0xbf1bf8ece4afedd2, 0xbf0ccd677aaa82f7, 0x3f09e5241d5b6b15 ],
[ 0x3fefff86cfd3e657, 0xbc72e06adb26f84e, 0x3f36be02102b3520, 0x3bb448bcfd3cfe0c, 0xbf502b15777eb7c5, 0x3f5cc1d886874d5b, 0xbf61bff70664651d, 0x3f5fc0f76c943696, 0xbf54a22286622d3e, 0x3f4268887688a6e6, 0xbf20fa2692fd7da2, 0xbefcc13d1a82f742, 0x3f04153e6537aae5 ],
[ 0x3fefffad0b901755, 0x3c670d5c9a92b65c, 0x3f2fc0d55470cf51, 0xbbc6f2b03553d4c8, 0xbf47121aff59f6a1, 0x3f5506d6992fc8ff, 0xbf5ab596015fc183, 0x3f58bdd79a098723, 0xbf50d88da9deb868, 0x3f4031cdd07e4507, 0xbf222fc41430a37d, 0xbedb5cc9546afcec, 0x3efd7ea1c7b8fdb6 ],
[ 0x3fefffc7a37857d2, 0xbc797b30fd4b6b48, 0x3f25feada379d8b7, 0xbbc0546c4da57036, 0xbf405304df546ed8, 0x3f4e79c081b79ebc, 0xbf53e5dc1062db15, 0x3f530eb20ccc1f98, 0xbf4b1b06c20a060d, 0x3f3bd52fbd55e0ef, 0xbf2214afb8835b23, 0x3ee19ae9d16650a0, 0x3ef42d933ee154fd ],
[ 0x3fefffd9fdeabcce, 0x3c80c43c3bc59762, 0x3f1e3bcf436a1a95, 0xbba6458a28a3f9b6, 0xbf36e95311166825, 0x3f45e3edf674e2db, 0xbf4d5be6d15abe3a, 0x3f4d07da13e640c2, 0xbf458106cc648748, 0x3f376c840985e5eb, 0xbf2111de112b1a2e, 0x3ef315fc34053fbd, 0x3ee939439a75a553 ],
[ 0x3fefffe68f4fa777, 0x3c32f21786b76440, 0x3f149e17724f4d41, 0x3ba747684f0023e4, 0xbf2fe48c44d2ab81, 0x3f3f2bd95d72a532, 0xbf457389188a71a9, 0x3f45decc4058f7a1, 0xbf40d559cf0f2957, 0x3f33583904af6f83, 0xbf1efd7979333337, 0x3ef904cf9fa5c1f6, 0x3eda13a094bd56a2 ],
[ 0x3fefffef1960d85d, 0xbc8f7cc78053f6ad, 0x3f0be6abbb10a5aa, 0xbb9e50b219d40126, 0xbf260403819b22b8, 0x3f35fff1dde5305e, 0xbf3f0c93c73e7f42, 0x3f404cbf67af6c26, 0xbf3a04893510426c, 0x3f2f66b51a7bc4a0, 0xbf1b410d7f2fd319, 0x3efb99f9eb427956, 0x3ebf26fcffb14441 ],
[ 0x3feffff4db27f146, 0x3c8ddecdd5e1d408, 0x3f02bb5cc22e5db6, 0x3b9c5112eca8acde, 0xbf1e258948829ed1, 0x3f2ec8a8e59d9d5b, 0xbf36425722b9f3cd, 0x3f380a83a7103b4b, 0xbf33dbb9374004f9, 0x3f2913b301d37bde, 0xbf17563b0d94459f, 0x3efbc01eea9a10be, 0xbeb3df26463df6a5 ],
[ 0x3feffff8b500e77c, 0xbc71014e1f83ed4c, 0x3ef8f4ccca7fc90d, 0x3b9a5d4ec8b9de43, 0xbf1478cffe1cd2ed, 0x3f2559f04ad4de62, 0xbf2f9e163b15c466, 0x3f318bda8b8c1315, 0xbf2df381bd3c058e, 0x3f23b94f531bb6be, 0xbf1385f32481ed94, 0x3efa414bd2b7cb3c, 0xbecac2bbe30f8767 ],
[ 0x3feffffb43555b5f, 0x3c8c17f83b8d73a2, 0x3ef07ebd2a2d2844, 0x3b9d1bbdc704f49b, 0xbf0b93e442837f52, 0x3f1d5cf1514977f3, 0xbf263f5eb46877fd, 0x3f295a0411e668b1, 0xbf2652e5f2a88269, 0x3f1e950ddb7f5444, 0xbf0ffeb9383bdb3d, 0x3ef7c24392346fdd, 0xbed1f3b3254d7230 ],
[ 0x3feffffcf23ff5fc, 0xbc8b18a8b25039c4, 0x3ee5a2adfa0b4bc4, 0x3b8eb6d61aaaf95c, 0xbf026c8826ed9e85, 0x3f140473571d5383, 0xbf1f057dbf365c0a, 0x3f22217929fed933, 0xbf207324014ddb42, 0x3f1762758a56d654, 0xbf09ba250c662e90, 0x3ef4c25759179e3d, 0xbed3e800358f1a7b ],
[ 0x3feffffe0bd3e852, 0xbc5d7ece4ab53150, 0x3edc282cd3957eda, 0x3b6eb3cf4fd14280, 0xbef86ad6df7ba401, 0x3f0b0f313eeb65a6, 0xbf156e457745d637, 0x3f19ad1f65a78253, 0xbf17f92ad8542929, 0x3f11a5578c0d30b3, 0xbf04548d876bb0a3, 0x3ef19e60bf53b25a, 0xbed3f1745170e2d3 ],
[ 0x3feffffec2641a9e, 0xbc8e7ba4fdaaa8c8, 0x3ed22df298214423, 0xbb5a9d49552152a4, 0xbef00c902a4d5e27, 0x3f022234eb745941, 0xbf0d57a2be01db67, 0x3f1200c2ffad65f1, 0xbf1147585d43f49a, 0x3f0a4b07aec797e9, 0xbeff9d088bbeff64, 0x3eed2b2be4e42422, 0xbed2bb57c0cf2941 ],
[ 0x3fefffff37d63a36, 0xbc6753e3241c01b0, 0x3ec74adc8f4064d3, 0x3b6de8a904d5c372, 0xbee4ed4228b3da96, 0x3ef81918baca1979, 0xbf03e81c09c29601, 0x3f09004afed1bde9, 0xbf08a40e183ee3fc, 0x3f0359242a8b8c58, 0xbef834b953bcb845, 0x3ee79e345fb0b20d, 0xbed0bb2d323900ce ],
[ 0x3fefffff82cdcf1b, 0x3c8046bbe9897fd5, 0x3ebd9c73698fb1dc, 0x3b588de36481dfb5, 0xbedb11017e7d5893, 0x3eefc0dfadc2c6d6, 0xbefac4e1aa499ac6, 0x3f0131810ab2e2e3, 0xbf01629d94abc864, 0x3efc22a71036c259, 0xbef244452f74de31, 0x3ee2bf17664310c1, 0xbeccd1b31a8349be ],
[ 0x3fefffffb248c39d, 0x3c89b9a41713558c, 0x3eb2acee2f5ecdb8, 0xbb32d1692a9a105c, 0xbed15cc5700a2341, 0x3ee4be757b934819, 0xbef1d6ab6f8cbf7c, 0x3ef76c5a3035bdab, 0xbef847332578dfac, 0x3ef437f23f8d25ff, 0xbeeb305e625a092d, 0x3edd3886ff986fef, 0xbec81f2189b385a2 ],
[ 0x3fefffffd01f36af, 0xbc8d41915db812ef, 0x3ea75fa8dbc84bec, 0x3b3a5cd79572a1a6, 0xbec6186d9fc357c5, 0x3edae02322e08822, 0xbee79082befd50ca, 0x3eef9c26e211b174, 0xbef0c768235c378b, 0x3eecba7164e1064f, 0xbee3f75c28c31ac8, 0x3ed663fcfff77e44, 0xbec3a6da35f36ee6 ],
[ 0x3fefffffe2ba0ea5, 0xbc826cd7908cba2b, 0x3e9d06ad6ecdf971, 0xbb3020b74d9d30fb, 0xbebbe46aa879edb2, 0x3ed143860c49d129, 0xbededabcbc3e620d, 0x3ee52139c87e9c82, 0xbee6f567cd982028, 0x3ee42ebd266abd62, 0xbedcf2f0c6adfb3e, 0x3ed0e2c0ed67786c, 0xbebf50cb81b9b190 ],
[ 0x3fefffffee3cc32c, 0x3c7e429188c949b8, 0x3e91e1e857adc568, 0x3b32439f8a1649bb, 0xbeb1769ce59fb2c8, 0x3ec5fe5d47560794, 0xbed405da04875e51, 0x3edbfc96a938083d, 0xbedf19ff5e59cbe9, 0x3edc0c4d50d275bf, 0xbed4b9df120462ae, 0x3ec916640ee35de4, 0xbeb874483d99c37e ],
[ 0x3feffffff54dab72, 0xbc8a443df643729a, 0x3e85dcd669f2cd34, 0xbb1ceb1ec59e0c28, 0xbea5b11cbd1ee799, 0x3ebbc91a6b1c1839, 0xbec9c2c5d12dfa2c, 0x3ed25d1e3c70364f, 0xbed4dbe26c88e4f7, 0x3ed347bb8350b422, 0xbecd51d3280da8a0, 0x3ec25ed8e5b466b5, 0xbeb2b9c5d3390919 ],
[ 0x3feffffff99b79d2, 0xbc758ff1c425f8de, 0x3e7a854ea14102a9, 0xbb121745e4b4fcb3, 0xbe9aba593e8384ae, 0x3eb167c252a45678, 0xbec06d78ca0424a3, 0x3ec7e0f59fcfa53d, 0xbecbb4d48383b847, 0x3eca39f3ad9a397f, 0xbec47e836879c374, 0x3eba89244d14b829, 0xbeac33e15a6dbe37 ],
[ 0x3feffffffc355dfd, 0x3c688cb60fd4511c, 0x3e6febc107d5efab, 0xbaed9ed10902067c, 0xbe9055a3c70279a4, 0x3ea59ff37766e9a7, 0xbeb4c53adb9dcc4d, 0x3ebec49242997849, 0xbec23927ad6ac54f, 0x3ec1a6e0676c7463, 0xbebc5239f6a88a96, 0x3eb2e991308bf6fa, 0xbea4e276c09fe81b ],
[ 0x3feffffffdc4ad7a, 0xbc8d75de787812d4, 0x3e630f93c3699079, 0xbaf8f941ab38e9da, 0xbe83ce2f890bb01d, 0x3e9aa5010863c83b, 0xbeaa08ef1ca16360, 0x3eb3a4a6af3cafac, 0xbeb7be1e832218f0, 0x3eb784775c30c386, 0xbeb3593046482ce3, 0x3eaa9d448178fbfd, 0xbe9e77bb85451c65 ],
[ 0x3feffffffeb24467, 0x3c8bff89ef33d6dd, 0x3e56961b8d641d07, 0xbaf74a7fc97b1544, 0xbe77d2510f1f969d, 0x3e90476b165ac852, 0xbea02d3a3b9d195e, 0x3ea8db3567bef1df, 0xbeaea3ef4e3a126b, 0x3eaf03b0861a59ac, 0xbeaa250ca467705a, 0x3ea27e9995f6dfcd, 0xbe95e77b673c6d74 ],
[ 0x3fefffffff3e8892, 0x3c5befbf8d294678, 0x3e4a8e405e651ab7, 0x3ab167a2d8cf6b18, 0xbe6c6c40e5083698, 0x3e83ba47a17512fd, 0xbe93ee334beef6ec, 0x3e9f2bf9e6c43e99, 0xbea395c08ac8e281, 0x3ea43ee4b521ccad, 0xbea178f0deeb9b20, 0x3e9964e51b0f0532, 0xbe8f0cc4ecca5c2f ],
[ 0x3fefffffff90b2e3, 0xbc7d82d94a90f1e4, 0x3e3efac5187b2864, 0x3acf1301ae680614, 0xbe60d229044adeee, 0x3e77b5bc9db47d00, 0xbe88588212e670c2, 0x3e935f42db1989fa, 0xbe98cd98865c4ff0, 0x3e9a2b8587c48078, 0xbe971aa2de99af9c, 0x3e913a89805c15d9, 0xbe85b53ca1bcf01a ],
[ 0x3fefffffffc0748f, 0x3c66ef7a9caef280, 0x3e31edfa3c5f5ccb, 0x3ac368f60e2e6cfa, 0xbe53c025a6810c37, 0x3e6c42f78a0989ad, 0xbe7d7c6c3583c6e3, 0x3e87dd6ccb5c93b4, 0xbe8f1ec2f699fdcc, 0x3e90bf7a04407a8c, 0xbe8e3aafe6dfd4e0, 0x3e871bc3a55b63f4, 0xbe7df66b11724e7c ],
[ 0x3fefffffffdbff2a, 0x3c749438981099b2, 0x3e24979ac8b28928, 0xbacc2f44bcf3ce52, 0xbe47015eec37753a, 0x3e60b487791590cf, 0xbe71b44b64c3c995, 0x3e7d23ff3ef8dd83, 0xbe8357d673d1ccfc, 0x3e853a563ce0e9e3, 0xbe83921106a960f6, 0x3e7ea527d318f96e, 0xbe746bd6cea7103d ],
[ 0x3fefffffffebc1a9, 0x3c7e0e5facabfab4, 0x3e177756ec9f78fb, 0x3aae20366d0e0306, 0xbe3a9530780ca70c, 0x3e53962ecb10df65, 0xbe651494525dee64, 0x3e71a2961b90efb0, 0xbe77d35cd0b404bf, 0x3e7aa596d9d73afb, 0xbe791493d8d43ba2, 0x3e74184505343c2d, 0xbe6b7d977f1a3402 ],
[ 0x3feffffffff4b453, 0x3c859b25048a61cc, 0x3e0a887bd2b4404f, 0xba82556d8ad4dd44, 0xbe2e78be33fb01da, 0x3e46c6ef0b68629e, 0xbe58e36e9a44c497, 0x3e65286ee37c531e, 0xbe6d146395886537, 0x3e7090902855d5f0, 0xbe6fd0d1e8fcb6df, 0x3e6a10f65c3c5a7b, 0xbe624888c323daf3 ],
[ 0x3feffffffff9bec8, 0xbc76755054654b62, 0x3dfdc479de0ef004, 0xba9c3434581af3b8, 0xbe21535aee3eb1b2, 0x3e3a4547ed264758, 0xbe4d2308d0dead0f, 0x3e5929d46a9a7edc, 0xbe6195dbfd4afd19, 0x3e646630f49ccd2f, 0xbe63fa4637c64ebc, 0x3e60b98a6e0cfc02, 0xbe58093f032972f3 ],
[ 0x3feffffffffc901c, 0x3c69c951c943961c, 0x3df0916f04b6e18d, 0x3a81bdf9650721ea, 0xbe138b90f78fbe14, 0x3e2e0d7765326885, 0xbe40e9760d0ac127, 0x3e4daad91166722d, 0xbe5513c51b9838ed, 0x3e58e27fb85ba534, 0xbe58d6f6bd99eaff, 0x3e553c31e52fff08, 0xbe4f3bfd31796bc0 ],
[ 0x3feffffffffe202d, 0x3c8a54841f566a61, 0x3de24caf2c32af16, 0x3a802e3358112fa1, 0xbe05dfa962d49548, 0x3e210ca1ff2af812, 0xbe3377c7e98dd9b4, 0x3e4156649e0b5dd2, 0xbe49092f4db426c5, 0x3e4e12a29b227972, 0xbe4e94e18d5271a9, 0x3e4aae38927ee69b, 0xbe441121b0293be1 ],
[ 0x3feffffffffefc57, 0xbc68225a9658ef84, 0x3dd40dfd87456f4f, 0xba7a6d5c55f8e63b, 0xbdf848f101ce14c8, 0x3e132fed47f8dd28, 0xbe2638ff4a6975f2, 0x3e3416d25168a6b8, 0xbe3d78fb22f58668, 0x3e42009c6b4e61ea, 0xbe42a459e59c850b, 0x3e4096a3e8dac0ea, 0xbe397fba69de37d8 ],
[ 0x3fefffffffff748e, 0x3c6ae15e36044aac, 0x3dc5ce9ab1670dd6, 0x3a4cc9bbfb723fc4, 0xbdeabf69bd9866f7, 0x3e056ae1e8abbbbf, 0xbe1927ca04d1a7a8, 0x3e2713d3b07d7a36, 0xbe31318f5d7d717b, 0x3e355ab94fdfd1f4, 0xbe368216fb90717a, 0x3e346ad5ce577d65, 0xbe30065a20073e81 ],
[ 0x3fefffffffffb5b0, 0xbc850fb19119064f, 0x3db7872d9fa10ab2, 0xba57760afdf543a4, 0xbddd39eaac4a0b47, 0x3df7b67ab8af33d6, 0xbe0c3ced54e694ea, 0x3e1a4875d8a47f12, 0xbe23e213e6f5c296, 0x3e2919137301f897, 0xbe2aea6bd9b34930, 0x3e28e06e4ab5925f, 0xbe23ed1d979421b2 ],
[ 0x3fefffffffffd8b3, 0xbc65182469c211e0, 0x3da92ff33023d5c3, 0xba42932180032bd1, 0xbdcfae4fe28d12dd, 0x3dea0a80964d6e97, 0xbdff6f47be478e2a, 0x3e0dad968cdacb13, 0xbe16ca68a8bfdb81, 0x3e1d3a79e5305b4a, 0xbe1fe1534ebf69c7, 0x3e1e01ee76d92779, 0xbe1883ed9069f3fd ],
[ 0x3fefffffffffeb60, 0xbc74d3f53e684bf8, 0x3d9ac0f5f322937a, 0xba38e8ab19224e58, 0xbdc108dc99cf03e5, 0x3ddc5db17016a0c6, 0xbdf159f41ea079c3, 0x3e009ced3e9b7204, 0xbe09e4dace066800, 0x3e10dd5e0e9749b6, 0xbe12b3aa6599d0b5, 0x3e11eb5e8e4ffe8f, 0xbe0dd8955967ed31 ],
[ 0x3feffffffffff542, 0x3c6b57ed63ed8110, 0x3d8c324c20e337e5, 0x3a253fd8abf42ed9, 0xbdb22c6b11327305, 0x3dcea5f66f89cbd4, 0xbde2ff1e0a81bedc, 0x3df270ddbd8e501f, 0xbdfd2992b5c25c93, 0x3e03492d76bdf266, 0xbe05bc7361853dde, 0x3e053121ae3f1d2e, 0xbe01fb0f7e3f242b ],
[ 0x3feffffffffffa73, 0xbc76fead614b7934, 0x3d7d7c593130dd16, 0xba08e78574fe0514, 0xbda33c1e2f16e037, 0x3dc06c53fdc74764, 0xbdd4a029a87915ac, 0x3de44bd86238ff0d, 0xbdf0474ac3a80072, 0x3df5db2a89e9bc47, 0xbdf906f4b51a7f75, 0x3df8d189784c1f50, 0xbdf571a4760f483d ],
[ 0x3feffffffffffd27, 0x3c719e1a84064c56, 0x3d6e9810295890f9, 0x3a0f998d55766fdb, 0xbd943262ab4b77b2, 0x3db1756eae580a28, 0xbdc6359d5b0d251e, 0x3dd626391bd58994, 0xbde203efc6c9f556, 0x3de88c0b111be900, 0xbdec8ca211a38811, 0x3decc911f684d612, 0xbde950e3edf09a71 ],
[ 0x3feffffffffffe8d, 0x3c5e766e2c801398, 0x3d5f7f338086a87b, 0xb9ddfa0c27b527e0, 0xbd8509f766d9f287, 0x3da268e278ede221, 0xbdb7b7b43e9a1b0e, 0x3dc7f7aadab6b398, 0xbdd3c3cc6aafba0b, 0x3ddb52c69b4ab6de, 0xbde0222c438d1182, 0x3de0888e14314f83, 0xbddd96aaea63b362 ],
[ 0x3fefffffffffff45, 0xbc85948eec884df5, 0x3d501647ba79874e, 0x3986d5d39dabc300, 0xbd75be1cf20840dc, 0x3d93418096320daf, 0xbda91e9beb94b447, 0x3db9b762261756a7, 0xbdc57f320a630c91, 0x3dce24b78ce82b11, 0xbdd2112fff5c77aa, 0x3dd2cfdd93a41786, 0xbdd11ea1f35b4d2b ],
[ 0x3fefffffffffffa2, 0x3c6d07509a1a9440, 0x3d404e15ecc7f401, 0xb9d0858e34f7a6a6, 0xbd664ac1f9b95f96, 0x3d83fa8302ade993, 0xbd9a62b70897719e, 0x3dab5c619266e9f0, 0xbdb72de32129cbb8, 0x3dc07ae94305c398, 0xbdc40c45a9e95152, 0x3dc533d127efdf16, 0xbdc39dc242ba4cda ],
[ 0x3fefffffffffffd1, 0x3c83b6fc0b729759, 0x3d3065b9616170e1, 0xb9c49459f5147526, 0xbd56acaa58a8be12, 0x3d748fb92d0947e7, 0xbd8b7ce1a1ea8ea5, 0x3d9cddc552bbebeb, 0xbda8c751cc1a5784, 0x3db1dc79b52007b0, 0xbdb60b3d17e7714c, 0x3db7ac1d379afc28, 0xbdb641ca84798564 ],
[ 0x3fefffffffffffe9, 0xbc55fe91226dd510, 0x3d205ca50205d279, 0xb9c7a281f9edb8e6, 0xbd46e18ec0d42451, 0x3d64fdb051100a15, 0xbd7c66b3f3fe565e, 0x3d8e331281475b54, 0xbd9a42e6965b2b9a, 0x3da3301ef4931960, 0xbda804fcc1524d74, 0x3daa2ef0c13a3daa, 0xbda9028a915f98d3 ],
[ 0x3feffffffffffff5, 0xbc8238f8ed17d9b3, 0x3d10330f0fd69931, 0x39ba2c00e0c6dcba, 0xbd36e8334c65749d, 0x3d5541d561058477, 0xbd6d1ac042ada69e, 0x3d7f54864c5a530e, 0xbd8b984c73c1d301, 0x3d946ec7009c291f, 0xbd99efc2df737760, 0x3d9cb12ac38f37ca, 0xbd9bd54fcd67b8d4 ],
[ 0x3feffffffffffffb, 0xbc8efa4d64f59f62, 0x3cffd3de10d6287a, 0xb99e1fdae91c5cfe, 0xbd26c073be0916e6, 0x3d455a8eab9e129a, 0xbd5d94c87c1bc304, 0x3d701db0818bec24, 0xbd7cbfbe4c0ef6ee, 0x3d859179d8c519c4, 0xbd8bc172710440bd, 0x3d8f26a4f726814e, 0xbd8ead889e052555 ],
[ 0x3feffffffffffffd, 0x3c86be96953fe014, 0x3cef05e82aae2be2, 0xb98070a8237b4337, 0xbd166b44c6d7ddb6, 0x3d35474bd9d072f3, 0xbd4dd1e8c33100cc, 0x3d60711486984913, 0xbd6db2522b66a6ce, 0x3d76919a06329739, 0xbd7d6fe8f87926e8, 0x3d80c1488010ff5c, 0xbd80bf9fa407e9ab ],
[ 0x3fefffffffffffff, 0xbc80fecc5ed770de, 0x3cde00e9148a1d52, 0x394f7a503c7a2ad8, 0xbd05eaaa4200e355, 0x3d25088b6566fced, 0xbd3dd0b48e0f634e, 0x3d50a27116d7478e, 0xbd5e6a3e1d5c214f, 0x3d6769249755a4bc, 0xbd6ef16049050b69, 0x3d71dbf2744f66db, 0xbd721c636bd8f5a9 ],
[ 0x3fefffffffffffff, 0x3c8989c6c5d51227, 0x3ccccaaea71ab110, 0x394152f323a1f3b4, 0xbcf541a2f15eb476, 0x3d149fd53e85cdf3, 0xbd2d9144beee6b4a, 0x3d40b09b02f533a1, 0xbd4ee312fcf48076, 0x3d5812ed2f01f60a, 0xbd601e6391f47ad7, 0x3d62dce8f6b8c896, 0xbd6365d5011db0df ],
];
#[rustfmt::skip]
pub(crate) static ERF_POLY_C2: [[u64; 27]; 47] = [
[ 0x3fcac45e37fe2526, 0x3c648d48536c61e3, 0x3ff16e2d7093cd8c, 0x3c9979a52f906b4d, 0xbfca254428ddb453, 0x3c69c98838a77aea, 0xbfd59b3da8e1e176, 0xbc41f650c25d97b0, 0x3fb988648fe88219, 0xbc55aecf0c7bb6c1, 0x3fb803427310d199, 0xbc5a14576e703eb2, 0xbfa09e7bce5592c9, 0x3c3eb7c7f3e76998, 0xbf9516b205318414, 0xbc2941aa998b1fa4, 0x3f8038d3f3a16b57, 0x3f6e19d52695ad59, 0xbf59542e7ed01428, 0xbf41f9b6e46418dc, 0x3f30796a08a400f4, 0x3f12610d97c70323, 0xbf025d31d73f96d1, 0xbee05e1fa9e02f11, 0x3ed1e616f979139c, 0x3ea9b3d54f1f222a, 0xbe97ad96beea439a ],
[ 0x3fd5da9f415ff23f, 0xbc4a72e51e191950, 0x3ff05fd3ecbec298, 0xbc9f17d49717adf8, 0xbfd477c8e7ee733d, 0xbc792236432236b7, 0xbfd1917b60acab73, 0x3c7c06e6c21b4b3b, 0x3fc322a728d4ed12, 0x3c3ffa8aef321410, 0x3fb04c50a9cd2c12, 0xbc4edd0562dce396, 0xbfa7ce764eeddd86, 0x3c29afeb391c029c, 0xbf868aac5801171d, 0x3c24f9655411fc03, 0x3f862aa895f51cd3, 0x3f56c003c3cedb10, 0xbf6079502dbbafff, 0xbf1d9c7cbb799b47, 0x3f345a995aede3f4, 0x3eb0c04ea8c98fc9, 0xbf057edfa53128d0, 0x3eba96286bf3ef56, 0x3ed3c8ab12e6d24b, 0xbe97454eba0cb203, 0xbe8f02a6f6847617 ],
[ 0x3fddb081ce6e2a48, 0xbc77ff0a3296d9cb, 0x3fedd167c4cf9d2a, 0x3c844f2832f90a97, 0xbfda173acc35a985, 0xbc3c5432c9a22740, 0xbfc889a80f4ad955, 0xbc6f6123bf467942, 0x3fc6c2eea0d17b39, 0xbc61f4935c3cf5b1, 0x3f9b0645438e5d17, 0x3c37a5f08ebaf9d0, 0xbfaa3fd9fcbb6d6d, 0x3c494a1b58b5916f, 0x3f2060b78c935b8e, 0x3bb9cec375875a1c, 0x3f8678b51a9c4b0a, 0xbf51e03bfc8eebb4, 0xbf5e653535cab33f, 0x3f355f31366d2c5c, 0x3f30dcf1445cbb88, 0xbf1098913ad4dcc7, 0xbeff6e252329eeed, 0x3ee41ad0a5afe51d, 0x3ec8fd4609222f1c, 0xbeb4465926de1a35, 0xbea407a1f42b46d4 ],
[ 0x3fe25b8a88b6dd7f, 0x3c89534a3b5bd215, 0x3fea5074e2157620, 0x3c7fad8c0ef6fae6, 0xbfdd9a837e5824e4, 0xbc71d19ec86adc7c, 0xbfb9c41d1d5fae55, 0x3c374c230d6afba4, 0x3fc75bebc1b18d1c, 0x3c501ece95d4dffc, 0xbf86410ad9332666, 0xbc216523d167a40c, 0xbfa7df8890b11fa7, 0x3c4d6a99d1387564, 0x3f84a54816d3608a, 0xbc2f810ad06699cc, 0x3f818f36eb18f3d7, 0xbf68d661c030e174, 0xbf53628ede23e249, 0x3f4438eb2b3c4d27, 0x3f1fd3c13e725e91, 0xbf1991b866a32c87, 0xbee1237c600dab6f, 0x3eea9c701140d4c0, 0x3e71801e61adfdda, 0xbeb785516863e6ce, 0xbe83e033ef590125 ],
[ 0x3fe569243d2b3a9b, 0x3c78eef7012e8df4, 0x3fe681ff24b4ab04, 0xbc5dba6493354c70, 0xbfdef2bed2786b25, 0xbc7ae3f6b6b2b679, 0xbf8a4254557d722f, 0xbc20ff7bffd10053, 0x3fc532415c267962, 0x3c62eacc4bd2e841, 0xbfa558b4c55a835c, 0x3c3c21c40815d70a, 0xbfa1b7ad5b777f1b, 0xbc42115b2bd8d644, 0x3f91201d3bd0e758, 0xbc0b39b845442560, 0x3f72995e3a88a890, 0xbf70294c3e93cdb0, 0xbf3159644a564f83, 0x3f463daf9b3858ef, 0xbf03beeb4a1255ac, 0xbf180c5178c36c72, 0x3eed4f6f5bab7dfa, 0x3ee521deb6d2f46e, 0xbec4ef3208231a8b, 0xbeae7d2b4e06e4a2, 0x3e921536d5b8bdf9 ],
[ 0x3fe7fb9bfaed8078, 0x3c766cf14bcad032, 0x3fe2a8dcede3673b, 0xbc77378e2c70325e, 0xbfde5267029187c0, 0x3c7add23841b110a, 0x3fafe0796bb9d05a, 0xbc37a992e13ce574, 0x3fc0fa23021ad0ac, 0xbc417f4228359928, 0xbfafa21ebca76761, 0x3c3278ca2820f66c, 0xbf931546d5c4edb4, 0x3c136fcf151892a0, 0x3f937e5469efb7a6, 0xbc39553630321d4f, 0x3f2097966e2e87ea, 0xbf6e82ab020887a7, 0x3f4318270c11ae74, 0x3f412652e433da97, 0xbf24dc9bd6368bb8, 0xbf0c441138d4ff53, 0x3efc91d8dc5b66ec, 0x3ecf3ba57b86d474, 0xbecdd3403d11a818, 0xbe731f497a106a7c, 0x3e9436dbcc93d342 ],
[ 0x3fea1551a16aaeaf, 0x3c6a558a46df5f68, 0x3fddfca26f5bbf88, 0xbc6ddcbaf85587b6, 0xbfdc1cd84866038f, 0xbc7200885b97f453, 0x3fbe4c9975da0987, 0xbc5f162e7576c79c, 0x3fb747e31bf47af3, 0xbc56178f12d62ed9, 0xbfb1d1f00109e42a, 0x3c5002b06e023544, 0xbf647654175ceb42, 0x3bd683389ccacfa8, 0x3f91a817c594b8cb, 0x3c336ac477166efb, 0xbf6cb8acd699cca6, 0xbf657b72bf874db6, 0x3f524493dca8b6fa, 0x3f2f556774c6aaf6, 0xbf2b09ec5c8ba626, 0xbed09bd1a09f38e8, 0x3efd149c3e776976, 0xbec8f7c2a6575e92, 0xbec8391d4afaf16a, 0x3ea5a7552081d1d5, 0x3e932d1bb2d1d0ca ],
[ 0x3febbef0fbde6221, 0xbc8322c1148e0d48, 0x3fd75a91a7f4d2ed, 0x3c56eb826a9df85c, 0xbfd8d03ac274201c, 0x3c57a5c56eb7f6a0, 0x3fc3954778d6a0df, 0xbc5863eca74d1838, 0x3fa88e0f7b183fc6, 0x3c4226527d05ce39, 0xbfb0f7c15f75ee13, 0xbc156f74f3513660, 0x3f85e22cfa1aab51, 0x3c24b49a250c6474, 0x3f89ad28c5557c22, 0x3c299920b730ecd5, 0xbf7704ec5d29fc83, 0xbf523360304f19ba, 0x3f543ca3fcdf079d, 0xbf0dcb97a9e04bd4, 0xbf2735e26c43d267, 0x3f0360c3b06ffbb4, 0x3ef29a6b5798e781, 0xbeddbc35e4cf98f5, 0xbeb2f6e8e81287bb, 0x3eaeeb2fdddad355, 0x3e81ae65e387ac52 ],
[ 0x3fed0580b2cfd249, 0x3c84fca6318dfee9, 0x3fd1a0dc51a9934d, 0xbc6ca89d2d78fba4, 0xbfd4ef05a0f95eeb, 0xbc65f7c55a00231c, 0x3fc5648b5dc47417, 0xbc6fb8fa09976e07, 0x3f840fbaba44504c, 0x3bf435c75f61f1e0, 0xbfac0db89d0a41a4, 0xbc11dd02d9441b98, 0x3f9388c3ec056942, 0x3c38e7498172c914, 0x3f7aecb7463cf446, 0xbbe0d6701a009d70, 0xbf78bca53327e075, 0x3f34add4a8239f4a, 0x3f505ce4abd10484, 0xbf3183f198a0b620, 0xbf19cd1a9b9fc69b, 0x3f0d30363021af83, 0x3ecda66f2161c4c6, 0xbedf41f1f238827d, 0x3ea725a07b1177b7, 0x3ea84c3b2483eb6a, 0x3e6e30e89d6e85cd ],
[ 0x3fedf85ea8db188e, 0xbc8f71e8254d11a9, 0x3fc9cb5bd549b111, 0xbc4973e73caa1edc, 0xbfd0ed7443f85c33, 0xbc574bf040302ad8, 0x3fc5066cda84bba9, 0x3c4beb86d9e281a8, 0xbf9419fa10b6ed7d, 0x3c35157491034c58, 0xbfa3f41761d5a941, 0x3c494a1c1f7af153, 0x3f96d1d724baaae4, 0x3c3c41090a704426, 0x3f4e377f5703f7ff, 0xbbea753be0c53963, 0xbf74cc916ad63c27, 0x3f5553ef0d12719f, 0x3f426240f55987fd, 0xbf36bbf0fffb7138, 0xbee320cf6663c40d, 0x3f0a9d4850aaa197, 0xbee17036c4011c91, 0xbed441ea26a91a02, 0x3ebd81eb8e2ef452, 0x3e917d7b798a4322, 0xbe4b7b0dfb2559d0 ],
[ 0x3feea7730ed0bbb9, 0x3c82c5bd7ce1388b, 0x3fc24a7b84d38971, 0x3c6aa0c5e788ed5e, 0xbfca4b118ef01593, 0xbc3238e3e6a99de0, 0x3fc319c7a75f9187, 0x3c4a8f8fff24b0ac, 0xbfa3db5bed47faf6, 0x3c429cf699c8512c, 0xbf97019bda6c2fdd, 0x3c1dd56b84622d88, 0x3f959d3aa402c32e, 0xbc08de701f1e95e8, 0xbf6b324eab9c87a9, 0xbbec3a4329771a44, 0xbf6b4774d37d0dd6, 0x3f5c01377485a844, 0x3f1a5db5f627539b, 0xbf340d9c429b8932, 0x3f0e720d935ef7db, 0x3effc8295ac052de, 0xbeed1ccde95c6551, 0xbeb251c256ca45cb, 0x3ebe892cc5397b1b, 0xbe88f6831febdf3d, 0xbe9aa5ef30a52421 ],
[ 0x3fef21c9f12f0677, 0xbc57efe429672268, 0x3fb92470a61b6965, 0x3c5c6acd40cee352, 0xbfc3a47801c56a57, 0xbc6033705aa16f01, 0x3fc0453f90d3bd35, 0xbc6686e281ba5405, 0xbfa8a7c6a239217b, 0x3c32a988808a7222, 0xbf8075c088031ee3, 0xbc1665bd0a645f40, 0x3f916f9c9c127b80, 0xbc1e1813af47374c, 0xbf774c2fc9bdfe97, 0x3c15cf2dbe53783b, 0xbf5760c522bd5bec, 0x3f5a3cdb656adb44, 0xbf302c3c1ab0a7ba, 0xbf292892013c7e15, 0x3f16e7b268d42034, 0x3ed970751eb9359f, 0xbeeb00b549bbdf58, 0x3ec033f8545bcc6a, 0x3eb2d8b6f0a2204a, 0xbe9c1c1335b105c5, 0x3e661bbb2d003b8a ],
[ 0x3fef74a6d9a38383, 0x3c8c33a329423946, 0x3fb0bf97e95f2a64, 0xbc5446051f6fef82, 0xbfbc435059d09788, 0xbc4b93aeb5e5cf84, 0x3fba3687c1eaf1ad, 0x3c564513fb767a13, 0xbfa9647a30b16824, 0xbc486357831221be, 0x3f66981061dfbb09, 0xbbfccc83193c8742, 0x3f87e8755da47040, 0xbbec1eaeb3371490, 0xbf79be731fdab95d, 0xbc0ab79fedbfccd2, 0x3f23a95ae0a75542, 0x3f5319f780e962d8, 0xbf3b88dd51a4f261, 0xbf1037f168a8f581, 0x3f153fc5e83e3199, 0xbee9d5bf30917222, 0xbee03045c999d17a, 0x3ecb5d376e96179f, 0x3e8c66d2e5aa2274, 0xbe9aef24a52bcaca, 0x3e7b20b678e8a0c6 ],
[ 0x3fefab0dd89d1309, 0xbc8ae61bd9db1bab, 0x3fa5a08e85af27e0, 0x3c4e4f9cfc8c2382, 0xbfb399812926bc23, 0xbc5b782644df6665, 0x3fb4140efb719cb0, 0x3c308fa5a48311e8, 0xbfa7535a61a4193d, 0x3c359e0501c376b2, 0x3f8374c88c7e6abd, 0x3bfc2578bd7e3f00, 0x3f7a40709e010e77, 0xbbf18c33197d9138, 0xbf76dc078888efa7, 0x3c02b49da4c86c70, 0x3f52ee6d200993b0, 0x3f444f175e22a161, 0xbf3c2fb051c92f92, 0x3f0523035ed3964b, 0x3f0bc7b666856fc1, 0xbef574549f39ee50, 0xbebc57f3c47b39d9, 0x3ec8acc76ac31fcd, 0xbe9f70e8b7deaa9a, 0xbe8e1a28a0c1a6a6, 0x3e6bfa0e5b606c5e ],
[ 0x3fefcdacca0bfb73, 0xbc82c33d88729e43, 0x3f9b1160991ff737, 0xbc2d940a504353bc, 0xbfaa38d59456f77d, 0xbc1d625808eb9778, 0x3fad5bd91b6b0123, 0x3c222b86f5e3e16c, 0xbfa3b35dcbc80146, 0x3c482838d776d958, 0x3f89d76b0a0535c7, 0x3c260fda06bca0a0, 0x3f614c887a83a0e6, 0x3be55ef222558d68, 0xbf7117f42cc6e9f4, 0x3bed4213a7e14a18, 0x3f59b477bdad8e08, 0x3f21d219fb0e1bc8, 0xbf35bb59d3ca4fa9, 0x3f18ca373c577821, 0x3ef4a9b74153a4a3, 0xbef424a8a8831410, 0x3ec6ce0877965abc, 0x3ebc1ed3c11b1dd1, 0xbea86b0a731d831a, 0xbe55cea3996396c5, 0x3e9640950bde5eb3 ],
[ 0x3fefe307f2b503d0, 0xbc68a555000387f8, 0x3f906ae13b0d3255, 0xbc388abd7f4be982, 0xbfa0ee3844e59be7, 0xbc40b0ec94b96d83, 0x3fa48b127f8ed8a5, 0x3c3b6a1f18c2c162, 0xbf9f155b4e7d8c3b, 0x3c3adb2d99b0c1fc, 0x3f8aa2c0753d569a, 0x3c29a37b9864b8e6, 0xbf4bbf7e2795837b, 0xbbe4784a66288abf, 0xbf65478d784d271c, 0x3be27115917a7ec0, 0x3f58eae08cdf9546, 0xbf292946556037e6, 0xbf290f27ae61444c, 0x3f1b076b78538f02, 0xbedb2906f1b92d5d, 0xbeea2f66822d4a01, 0x3ed3031c4f7c4a97, 0x3e941708ced2abd0, 0xbea45ffd6deae2a8, 0x3e7e844ebdc8456a, 0x3e7c0bbf2b711595 ],
[ 0x3fefefcce6813974, 0xbc5b27cf5025d1c8, 0x3f834d7dbc76d7e5, 0x3c23780d6e7eb351, 0xbf951cc18621fc23, 0x3c2969629e4b64a6, 0x3f9b925a99886bb7, 0x3c29c8f65efdd1f4, 0xbf971e7d408c8c6f, 0xbc3c5621deaf4cfc, 0x3f87ea58080a81ef, 0xbc22f25b7f384ff3, 0xbf646eb9d203e071, 0x3beff569e38360a4, 0xbf5403333682fa5e, 0xbbe36256a95953a6, 0x3f53b37d5bd14a40, 0xbf36be130822dbdf, 0xbf103d4bcdafd553, 0x3f155848476c8142, 0xbef5492bf3c6eee6, 0xbed3823d4328e9c5, 0x3ed152fefc353e5a, 0xbea5199dbf7bc4c6, 0xbe94dda2bebe08f2, 0x3e83fb850b47210a, 0x3e6bcd1b284c4798 ],
[ 0x3feff733814af88c, 0x3c70a87238cea4fa, 0x3f75ff2750fe7820, 0xbc15f184847ca667, 0xbf896f0575a63ae5, 0x3c295f4139297a96, 0x3f91c5a643f04363, 0xbc16ea87997fba3c, 0xbf904f5caaf2196f, 0x3c119502347d3b54, 0x3f8382a146afb9d2, 0xbc0f93bde902d2d0, 0xbf695cab93aa68d2, 0x3be0f716a5fc18c4, 0xbf2d2fd90fe62928, 0xbbb4e00d5fcc484a, 0x3f49f50fb94c0b86, 0xbf37d7378074399b, 0x3efcc0c9cb9ede1e, 0x3f092a3a29471895, 0xbef7c127858c909a, 0x3eb5a72fde935a48, 0x3ec57b9d90a92106, 0xbeafdb8443754cf7, 0xbe56c7d633eab55a, 0x3e7ddcfc714a2b67, 0xbe89fedf738e84b4 ],
[ 0x3feffb5bdf67fe6f, 0x3c14e830346f6e80, 0x3f684ba3004a50d0, 0xbbf90b93d4632206, 0xbf7d9c2ea85a927d, 0xbc10bcf1ea93cfdc, 0x3f860898536e104a, 0xbc1ab6aa911c445e, 0xbf85eb1c899f0b70, 0x3c21bc22eed1f1fb, 0x3f7d854f73e74c87, 0x3c07a977a3364c40, 0xbf6897719a9d257e, 0xbbeab523e3f93994, 0x3f388cdc8b807c97, 0x3b94875acc7c06a0, 0x3f3b325a11c1f45a, 0xbf3381548f692740, 0x3f12b1fd05559bfa, 0x3ef1ed31cd6feb26, 0xbef29cf593fdf00a, 0x3ed1cea99b59228c, 0x3eaceff221e3598a, 0xbeab0ad4b899b2d9, 0x3e83761b047e21d1, 0x3e696c31c2256049, 0xbe60a714c57f7adf ],
[ 0x3feffd9f78c7524a, 0x3c804ed6ff98e45d, 0x3f5a024365f771bd, 0x3bf3c8f5202cb405, 0xbf70a9732d5284dd, 0xbc11acbd0899ce7e, 0x3f7a4bf47a43042a, 0x3c0e6cb2580d0920, 0xbf7c23802d8a5bb7, 0xbbd9963700abfc80, 0x3f74f40070668329, 0xbc1e1fe1c0e1182a, 0xbf64c9a2c9dccd04, 0x3c080fb9c9cd78c1, 0x3f44f7a50b5bc019, 0xbbd40906b7a1de3a, 0x3f218b04eb90c737, 0xbf2a4c3880c0ea69, 0x3f14b7b82a86f423, 0xbed0bc762b1c2aaa, 0xbee589d6f8892acf, 0x3ed357ab63f7bdf9, 0xbe96675858bbff5e, 0xbe9ea96dcb12a15c, 0x3e88c572fcf5610e, 0xbe3700c93da86dee, 0x3e57ae9ceb75e26e ],
[ 0x3feffed167b12ac2, 0xbc8ddc0ce3ed8fcb, 0x3f4afc85e0f82e12, 0x3bd438f22895e03e, 0xbf6221a9f326bef4, 0xbbf99642b37af330, 0x3f6e3c9aab90bcf4, 0x3bb7dcdfdccc72a0, 0xbf714b1b98141f21, 0x3c1af6edf50eba66, 0x3f6c1c19b9e63d70, 0x3bd4d1e9411f1d28, 0xbf5feac3dbeb5124, 0x3be2400e6ffbc1c8, 0x3f463e88178b0e49, 0x3be3e4ae97774f91, 0xbf04441c86c93f39, 0xbf1c8ceebc5fc50b, 0x3f1125b77a79aa6c, 0xbeeda7be990bc718, 0xbece019960474aff, 0x3ecd229185ef6279, 0xbeacea9fa10885e7, 0xbe8044fd6a2e447a, 0x3e83695f88fc641d, 0xbe60c0dc0ba0d589, 0xbe89194748828b93 ],
[ 0x3fefff6dee89352e, 0x3c8b96c0ba13851d, 0x3f3b23a5a23e4210, 0x3bc727bce1be0014, 0xbf5315107613c673, 0xbbf823f8673f5b7a, 0x3f60c243329a9ca1, 0xbbf65e361cefe652, 0xbf64630116262084, 0xbc00ea6ee40daf79, 0x3f61e84d1022e8cb, 0xbbd9b77b85eed4f0, 0xbf56b41872716325, 0x3bd3e9e001100f64, 0x3f436edde582b265, 0xbbe1cb479a94e148, 0xbf1f7870ebc38e77, 0xbf051ecfdc37801d, 0x3f0711d817e0d3b6, 0xbef0ae90d500d1d8, 0x3eaa85b1bf54920c, 0x3ebfe73958205038, 0xbead222bfef33aa4, 0x3e7833f8b13b1a4e, 0x3e7233b5a19285db, 0xbe61adcf574b7db6, 0x3e7ab10bedc44532 ],
[ 0x3fefffbb8f1049c6, 0x3c7d2c6266b51f26, 0x3f2a740684026555, 0xbba7e24cc3ac5710, 0xbf436d34c8f1c26a, 0xbbe69d73e7d1c977, 0x3f51eb6e14974a25, 0xbbf99b78600e0664, 0xbf5714eb8cc0947f, 0x3bf3613f37c7410b, 0x3f55bec08c01b1d7, 0xbbf3e3a262f6c68a, 0xbf4e4621d82dad12, 0x3bc302878843e2cc, 0x3f3e1b7b564b0e79, 0x3bcf894fc1f14d54, 0xbf224564b69716aa, 0x3ebbf8e3b47f3ccd, 0x3ef8f55a9be1a264, 0xbeeb3b76e6203281, 0x3ec713c795a07e0c, 0x3ea3bb092cfd93e0, 0xbea473b0a8333dee, 0x3e8645526869c143, 0x3e41a343e004b33d, 0xbe576c7e253faad1, 0x3e7e16080963cffe ],
[ 0x3fefffe0e0140857, 0xbc66aa36f86c14dc, 0x3f18fdc1b2dcf7b9, 0x3b87050f50b8f308, 0xbf3322484cf12daa, 0x3bd4cc0408806d4f, 0x3f427dc1bc6cfef5, 0x3beffbb5229f6bb7, 0xbf49202f465eb421, 0x3ba8f3f063b40660, 0x3f493b4c9746835f, 0xbbe04e2d6df2fce5, 0xbf430e9e6142fe9b, 0xbbd0396045094744, 0x3f3555b9d5fb4825, 0x3bd9a40d2ca5ef0b, 0xbf2055983c4ac7a6, 0x3ef68e6c75a5d068, 0x3ee2d4a50d2757ce, 0xbee1de08b56479aa, 0x3ec9110ccc7fe6fd, 0xbe8bb3184d789af8, 0xbe94629a164e82a0, 0x3e8413b087ee5e4d, 0xbe5648d7786f9fbc, 0xbe4293289f8c327d, 0xbe6c283008e726f7 ],
[ 0x3feffff2436a21dc, 0xbc83607959a29d36, 0x3f06e2367dc27f95, 0x3b6d96e6f0151020, 0xbf223c436c36fdab, 0x3bbf0d77fc600a50, 0x3f326bf00867a835, 0xbbdc92e1aecdc750, 0xbf3a51fb50b15f22, 0x3ba248227c6d2260, 0x3f3c0825378fda08, 0x3bd5a8a09c053451, 0xbf36c3dbfe0cbe4a, 0xbbde65769c33f8a1, 0x3f2c1dd1438378df, 0x3ba91bd161f34158, 0xbf194c36a9d7c0dc, 0x3efbf0aab116ca41, 0x3e86bdbd2f103930, 0xbed2b32e8d43ef25, 0x3ec3a7403459770b, 0xbea17411873320fa, 0xbe735bb2691c9b29, 0x3e798313537ed069, 0xbe5cb4b60e85a341, 0x3e02be214cf4c9eb, 0xbe8350a1a851865a ],
[ 0x3feffffa1de8c582, 0x3c8832540129302a, 0x3ef44f21e49054f2, 0x3b8f338cf4086346, 0xbf10d18811478659, 0x3bb914a7a08b6a2b, 0x3f21b964d438f622, 0x3bca52c94c56aaaf, 0xbf2a8d7851f26bf0, 0x3bcc38dbf3ee1223, 0x3f2ddd6df9b6852d, 0xbbc3b0dd7eac9b91, 0xbf29e52b7aac1644, 0x3bc904036dfb5764, 0x3f2165b2034fcab2, 0x3bc27beac4bf3866, 0xbf11b75c3332673a, 0x3ef91a253c42f4e7, 0xbed020b498095051, 0xbebade63f30809ae, 0x3eb89bb0d75e59b7, 0xbea180c78d3dca28, 0x3e6cabfd39b38553, 0x3e66013ffba86cfd, 0xbe564f2b123e1f0b, 0x3e335bf3e5021105, 0xbe8177828ffd35af ],
[ 0x3feffffd8e1a2f22, 0xbc8c10adf6b19989, 0x3ee1783ceac28910, 0xbb87f19d8ee58337, 0xbefe06a8b37e5b93, 0x3b824e8db1358f2e, 0x3f107978c7b8496b, 0x3bbf163b5580927c, 0xbf19d039884f8be5, 0x3bbfce53cd30b1eb, 0x3f1e8d1145e94a54, 0xbbbd0f6e009a99ee, 0xbf1c1f7251172a87, 0xbb83ce0f013dfe90, 0x3f1458b9e0854d68, 0xbbb897cf3950b1a7, 0xbf06eb0557245429, 0x3ef33045cf65279e, 0xbed42c8adf18ab62, 0x3e491109b80f9918, 0x3ea83a9b44249fbf, 0xbe99bcbaf0a8dfd1, 0x3e7900325b58a857, 0x3e34a3cf9c161684, 0xbe50cbcc4d0a916a, 0x3e34275e1b91f084, 0x3e839180c75350e1 ],
[ 0x3fefffff039f9e8f, 0xbc89d1bcd6174e99, 0x3ecd21397ead99cb, 0xbb46abd9c029c47c, 0xbee9f19734d29cf9, 0x3b820c4383da36c1, 0x3efd982bd41d8954, 0x3b8d9bc9988e9666, 0xbf08320fc4836be5, 0x3b7526638b9926a8, 0x3f0e0a1cb1d071f3, 0x3b9d9f5d232bab90, 0xbf0d384223047b9c, 0xbba30d0b2b8a170d, 0x3f0696daf6422bd4, 0x3baba6ac732f399e, 0xbefbb6e2d311a93f, 0x3eea4fcb0ea87efb, 0xbed1c940c5303daf, 0x3ea7469913f4e9c6, 0x3e8ef4b4f8ab67ae, 0xbe8e189c28e8e041, 0x3e7678b281d5bc55, 0xbe49c3bf4e9f2b5d, 0xbe374c9ba997ffed, 0x3e2b4b843f8c7068, 0x3e6c901764507862 ],
[ 0x3fefffff9d446ccc, 0xbc6bb06bab98bc80, 0x3eb789fb715aae95, 0xbaf226d93bf89b40, 0xbed5b333cc7f98f1, 0xbb76bd1091d25440, 0x3ee9b12fdbf90f62, 0x3b8d4b6b0ee9cf46, 0xbef5e06923144d70, 0x3b4c593194857860, 0x3efc6a071925631d, 0xbb8835ef595952e4, 0xbefd178cb0388a82, 0xbb9039272760f01c, 0x3ef7e29d33ac92b6, 0x3b921ff8b0e9d5eb, 0xbeef9203429baad6, 0x3ee094dadeee395c, 0xbeca771cf3500d9f, 0x3eab8fd1c29c21ea, 0xbe5cc8573d7de110, 0xbe7b0362da1722cb, 0x3e6e5eae518f94e9, 0xbe507963addd99a6, 0xbe23f496093d0bef, 0x3e199078a326092d, 0x3e842681ecfe4da1 ],
[ 0x3fefffffda86faa9, 0xbc7d230252d68f26, 0x3ea26f9df8519bd7, 0xbb4e339871c015b7, 0xbec1926290adc888, 0xbb6e36d23dbb2644, 0x3ed5900c02d97304, 0x3b7fa7d21e3ed616, 0xbee3166de6a8c640, 0x3b8b014157867958, 0x3ee9dfcc328729e0, 0x3b820e9fee0b7665, 0xbeebcab1ed5ec38d, 0x3b6d9003794f0fe0, 0x3ee81cd74a57ce17, 0xbb8809fde9c0f6f5, 0xbee106e95b6bf556, 0x3ed379625a71385f, 0xbec1970a5b5bd443, 0x3ea74761c8333ff2, 0xbe80864e125c9951, 0xbe5b83bf9019aa3b, 0x3e60397611c35b28, 0xbe4a25392adb29ac, 0xbe17b832af40d9d4, 0x3df62a02eb79577b, 0x3e8a6da58ffe94f4 ],
[ 0x3feffffff233ee1d, 0x3c8db123ed17221d, 0x3e8bfd7555a3bd68, 0x3b20151cf177b53a, 0xbeab8d7f804d2e73, 0x3b482b366f0bc2dc, 0x3ec17f93e5149289, 0x3b391997bfd26568, 0xbed013b0457d08fa, 0xbb60d6d5a7f06298, 0x3ed6b245d7e1d829, 0xbb79985e02c8ce3b, 0xbed98077548c6951, 0x3b701cd3f1d12c93, 0x3ed7492048ab3ceb, 0xbb70368a0dc0750e, 0xbed17506c7b39cb0, 0x3ec57e94a4c5f5a6, 0xbeb570971200d7db, 0x3ea0a0f956947b21, 0xbe81a9b7bd5bba32, 0x3e451bfc00de3146, 0x3e495b6967f79cbe, 0xbe3fe3c43cb3cf84, 0x3e32f364a7a2dc5f, 0xbdf007442a10cc14, 0xbe7ef5ab6fc5e849 ],
[ 0x3feffffffb127525, 0x3c8504f382db4102, 0x3e74980cb3c80949, 0x3b17fbdd923f8057, 0xbe94ea6ce697296f, 0x3b3ea42f9c9de533, 0x3eab771d9b6f07b8, 0xbb1e9c1ca9662fe8, 0xbeba26c653fad5b8, 0xbb5146c4cee0e898, 0x3ec3302bb89379de, 0x3b64c55b83ef7a68, 0xbec67f42e5264334, 0xbb66779da26b4197, 0x3ec58b4adafb958e, 0x3b68351251b45e84, 0xbec10f576796285a, 0x3eb66ca44250dd07, 0xbea84ee0ada37543, 0x3e953b6065291e6b, 0xbe7c09ebfd0c581c, 0x3e563062625d59c0, 0x3e1e259c60eb7b83, 0xbe2e43802ad25514, 0x3e351bcdabe8cda5, 0xbdf930fc3df6e909, 0xbe881cdd770e1c81 ],
[ 0x3feffffffe4aed5e, 0x3c4389c0f32ad0f0, 0x3e5d5f3a8dea7357, 0x3affa07c18622dd2, 0xbe7ebfb14c9170c0, 0x3b19e40632b4145d, 0x3e94d9228525f449, 0x3b2d35bd7f959136, 0xbea48b536addac5f, 0xbb461ace22b32569, 0x3eaf48ccf23a68e2, 0xbb4ee1d13c79c281, 0xbeb3183b6134cf03, 0x3b4e1f4d5fe2a06c, 0x3eb31efde2215f01, 0xbb564a7021e23fba, 0xbeafd9eeb0f18fdb, 0x3ea63414459ae298, 0xbe99dda81be20b5a, 0x3e88da7d306423c5, 0xbe7303da86a4fc28, 0x3e54f5e1327706b9, 0xbe23efb5eefcbe53, 0xbe131bc5ce1ce65d, 0xbe13eafe1b05c93f, 0xbdf47fc2d9cc851e, 0x3e7d27265006a9df ],
[ 0x3fefffffff6d1e56, 0xbc864d969b4be4c4, 0x3e444d26de513197, 0x3ae76fc20fc4b365, 0xbe65e32de7af8977, 0xbaf888fd6ae18a1c, 0x3e7e9e05b3c8f38a, 0x3b17532141b12aa7, 0xbe8f2f6fa7db5b1d, 0x3b2b3bf498e3462c, 0x3e9899dcace485eb, 0x3b11885a0ae9e878, 0xbe9f34b7eef3c9b2, 0x3b3294a3b618b470, 0x3ea04be030272d14, 0xbb4df83095e40f79, 0xbe9c73bd22571559, 0x3e94edda838439f5, 0xbe89fc860b504677, 0x3e7b0d686a260420, 0xbe672370c2fdbe10, 0x3e4ee29f0d197d25, 0xbe2b4d88d500c5be, 0x3dc96014c45b0178, 0x3e0238f19dc8fd82, 0xbde8d34d46ae6567, 0xbe454105fe4a9cd8 ],
[ 0x3fefffffffd01f89, 0xbc735e8e39884f62, 0x3e2b334fac4b9f99, 0x3ac32178ed1a4971, 0xbe4e2cec6323e50e, 0xbac0e5693f9d4908, 0x3e65c027d5bba36a, 0xbaefc46fb3cc7ae0, 0xbe76df4d024fffbe, 0xbb090fd7226ec57a, 0x3e82aaf7c205b9ea, 0x3b2dbec2005b45a8, 0xbe88902edfbfefdd, 0xbb2c353aca58d08a, 0x3e8ab2ab1b338249, 0x3b2b498186c39105, 0xbe885abe0ff198d3, 0x3e82d32f7c3621eb, 0xbe78c141c71dbc95, 0x3e6b9fa6fbb9b198, 0xbe59db5fe2c2f5b9, 0x3e43b8e07840483e, 0xbe26d95e5070d91d, 0x3dfd7616168b0e49, 0x3e1f2be0744b3a5f, 0xbdd737a375809985, 0xbe7936d4936fb865 ],
[ 0x3feffffffff0dd2b, 0x3c80df73e7d2fc98, 0x3e11a94ff571654f, 0x3abfbf537b47967d, 0xbe34251f33f5578f, 0x3ad4c9cece8f41b2, 0x3e4de6bc1f75bb9b, 0x3a894afb459a3000, 0xbe6036b5fd1c4158, 0xbb0d582afa097896, 0x3e6b58f1385def96, 0xbaf8778854601996, 0xbe72a2347efb2133, 0xbb026f9e1ef0f378, 0x3e7508db866ffe00, 0x3b164de561a68a21, 0xbe73ffea934685b9, 0x3e702ff87b2e2576, 0xbe666e54eae5fa4b, 0x3e5a9ea2195c567d, 0xbe4ae3b91fecafa1, 0x3e36bb883d2e5ed1, 0xbe1ee10e97715c11, 0x3dfe2873d2b77f1f, 0xbdeaf385ae29d57b, 0xbdbd793eecfc2513, 0x3e420d80dcfa68d1 ],
[ 0x3feffffffffb5be5, 0xbc7729d6819c7f34, 0x3df63ac6b4edc88e, 0xba7c45991835da24, 0xbe1a0ce0dc06a706, 0xbab1b72d11da9dab, 0x3e33e380dd7593a5, 0xbad8ad868a7b5674, 0xbe4638bc4fb02cba, 0x3a87a84506fcda40, 0x3e535753ad4c5875, 0x3aead190ab170366, 0xbe5b41f33cafccba, 0x3af0e3539bf61116, 0x3e5fe694e371a659, 0xbad3a84e01866ea8, 0xbe5f8af0121aa0ab, 0x3e5aa77274dab3d8, 0xbe53616fe8f6a259, 0x3e484fddf4c681a1, 0xbe3a3de05d1b8a31, 0x3e2822529aca9f83, 0xbe126c3dfba84378, 0x3df64c287a84aa09, 0xbe0107d2dac5d83b, 0xbd4e251d1ab1d873, 0x3e58f37005f17b42 ],
[ 0x3feffffffffe9eb0, 0xbc5ea527e0bef1e8, 0x3ddb1e5acf351d87, 0x3a5dc96583ba19f0, 0xbe005042a0a5f3c3, 0xbaa2023f0f13867c, 0x3e199ac8fd63c66c, 0xbaabf57c5fd0501a, 0xbe2d72344378e114, 0x3acc77758959af41, 0x3e3a6be9a123435b, 0x3acdab4af8807c36, 0xbe433aacb4bf6dea, 0x3aebd241ea49ac35, 0x3e474b732e7ceaa7, 0x3adc7c89730b0264, 0xbe47e7eab6531ccb, 0x3e450959f2daae39, 0xbe3ffed4cef94261, 0x3e351c7f99f908a2, 0xbe282b5fd5fbedfc, 0x3e17e1c8e715c978, 0xbe051536822c861b, 0x3debe7e4c220ca82, 0x3e2f5bb67c461296, 0x3da1d7cf04529bf0, 0xbe8acc021ab828c4 ],
[ 0x3fefffffffff9a1b, 0xbc66a87270d2450c, 0x3dc0084ff125639d, 0xba58ad61debedc86, 0xbde3ca42adaa26f6, 0x3a8c20c6583dccdd, 0x3dffe73513c67bf8, 0x3a920d28c0c7e686, 0xbe12dd9aa5a2bee3, 0x3aad76d7235461be, 0x3e216ef6b93944a8, 0xbacf07bd785566de, 0xbe2a2d58e9b22b26, 0xbab19e6ea91dd55e, 0x3e306389b9748f25, 0x3adfbcc52565c0be, 0xbe316cdd9eb58ba2, 0x3e2fdd861b55c500, 0xbe29457846c943d2, 0x3e2178f3905f435c, 0xbe1518cf20c53de2, 0x3e06329939a34b66, 0xbdf5ef3ad85e5d3b, 0x3ddf2b41494e49e9, 0x3e2bad43bc0b622d, 0x3da21a45fa9dcebf, 0xbe8790b3d88f69fe ],
[ 0x3fefffffffffe380, 0x3c87ce07114e4fe0, 0x3da25f9ee0b923dc, 0xba4174c43a73a4d1, 0xbdc74105146a5162, 0xba47d0740e56625c, 0x3de33cde4f35d941, 0xba72a344950797c6, 0xbdf760fe7b666392, 0x3a9a8b77c82ed644, 0x3e063a70fd66d485, 0x3aa6b87715649d6d, 0xbe11324f6fb6dfa1, 0x3ab3fc045e39915f, 0x3e163a31a36b815c, 0xba502dec9bc1a700, 0xbe18724ca8970b91, 0x3e172e290891e5de, 0xbe131fc03858aab1, 0x3e0b9e8b0e7fa253, 0xbe01821a002637bd, 0x3df37ba5f3fba5eb, 0xbde3578bf23dc654, 0x3dcfdaf2015d7b54, 0x3df7f6a435069067, 0x3d99d14ee557ec62, 0xbe555f4c743ee571 ],
[ 0x3feffffffffff845, 0x3c7b0edc5a89ab8e, 0x3d846897d4b69fc6, 0x3a2a74852415bb49, 0xbdaa77a4e7dcd735, 0xba434edb43ab7de6, 0x3dc67543695dcc12, 0xba329ae577004af8, 0xbddc05c1e2fc710e, 0x3a5dbbf42d2537a8, 0x3deb639419fedf8e, 0xba8ed72eb9e7a59e, 0xbdf5cfd7eb9bfe87, 0xba7e97db27125fc0, 0x3dfd11578959ba45, 0xba8c0635ac2b5768, 0xbe0082f9e9f7eb37, 0x3e00354ceadad8b3, 0xbdfbc2dee0154fc6, 0x3df4e11efdc66eae, 0xbdebb357c0253f64, 0x3de035f9889bc29c, 0xbdc8e7bdb10b7441, 0x3dbe364571102661, 0xbe212cffcf49a2e8, 0x3d8ee9362bcfec26, 0x3e7cc5b58dd85301 ],
[ 0x3feffffffffffdf8, 0xbc8dcf8b10ff973b, 0x3d65f8b87a31bd85, 0x39d65b265455b658, 0xbd8d2e55024a0fb5, 0x3a2444e1d84cea02, 0x3da9612cc225df4b, 0xba4c784edb664ce7, 0xbdc03ee5f38b9b4d, 0xba691ca8efa41a30, 0x3dd04f2f71e2e96b, 0xba633f36a4e51350, 0xbddab7099f99ced9, 0xba54af7a67f2110c, 0x3de2554b8f609fd1, 0xba729e641eb44218, 0xbde57c87529ca968, 0x3de5cd182c967671, 0xbde3580a2517d57a, 0x3dde3be72b1be982, 0xbdd4e9908689ad08, 0x3dc9a61979d3395b, 0xbdb7b826aadd1c89, 0x3daad3a9fc4a0d1e, 0xbe00e9325ed20970, 0x3d80722198ff452c, 0x3e5c2ef85611aa11 ],
[ 0x3fefffffffffff7b, 0x3c800fa07f7fb612, 0x3d46ed2f2515e933, 0x39d2bc1802a42b92, 0xbd6f2a6c1669c901, 0xba07b3e174cc1840, 0x3d8bc42ba38a13f8, 0x3a2460463d59d3df, 0xbda2391e135afae4, 0xba3bd08c8c5f7b18, 0x3db2c6c24550f64f, 0xba3fdc861a487110, 0xbdbf9a3c1b0d63ec, 0xba5843dc8d9ad3d5, 0x3dc6502546ab341a, 0x3a645f812e48eb98, 0xbdcaf223186006d1, 0x3dcc388dd1764f41, 0xbdc9e65a242b52aa, 0x3dc4fcd2787781eb, 0xbdbe3cbdb20a48d6, 0x3db35639e9fcd410, 0xbd55b8e97774b2c9, 0x3d966ffe6a100bc9, 0xbe15706c390c113e, 0x3d6ff0d11cf61949, 0x3e72054de347e3f8 ],
[ 0x3fefffffffffffdf, 0x3c75669e670f914c, 0x3d272fd93e036cdc, 0x39b1c553d12fbbd0, 0xbd501f450d1e61b2, 0x39cbed807e60c078, 0x3d6d68fb81b2ed89, 0x39dc7ea3c4444cc0, 0xbd83c706aa4d2328, 0x3a2d6d2d51dd414d, 0x3d94e6479565838e, 0x3a350580f36c14c1, 0xbda20e9eb83b3dd9, 0xba104b6334a32fd0, 0x3daa35b9d2fcac80, 0x3a1c07c6978bf2f0, 0xbdb04a134f6e3dcb, 0x3db196579f27ddbe, 0xbdb0ab97aa74c700, 0x3dabf68355f542b1, 0xbda49da25a547134, 0x3d9bd64993a3958e, 0xbdc1193990186399, 0x3d81c0e98335ae18, 0x3e2e08edb685494a, 0x3d5cb9fcc058465b, 0xbe894cacccfb8964 ],
[ 0x3feffffffffffff8, 0x3c70160ef15c497e, 0x3d06ba91ac734786, 0xb9af81d6fa69b5b2, 0xbd3028a39099f4db, 0xb9c83ed68de15404, 0x3d4e292863e1795e, 0xb9db292e812abb68, 0xbd64c4e690fbdd14, 0xba080991e1d4ef25, 0x3d767e6e5ac60fd1, 0x3a01d2ca68dcf0e8, 0xbd83f00d80afa00c, 0xba23e174dc7225ac, 0x3d8db88ee63eb28a, 0x3a28abd97527892f, 0xbd92fe58a1f19368, 0x3d951dbeae22a5c8, 0xbd94a4d54823e0fc, 0x3d91e432d674cfba, 0xbd8b001e26c6e764, 0x3d8328ce695259fe, 0xbdb4e492cf7d4f4c, 0x3d6aaa77d339dc00, 0x3e2366538db382e5, 0x3d4826ad7d581503, 0xbe8056e0810b14da ],
[ 0x3feffffffffffffe, 0x3c759ab24e589a30, 0x3ce5982008db1304, 0xb981cf9bda64b38a, 0xbd0f610e8cde57ac, 0xb99884dcd86f98c8, 0x3d2df2dac2f2d47f, 0xb997f27bf279d988, 0xbd451b17f95fc0b4, 0xb9e063e04485c3e7, 0x3d576996ddc975d7, 0xb9e21489d6648428, 0xbd6546155a972b18, 0xb9b9d3fb518aa7c0, 0x3d70456ed89c4f24, 0x3a1eee772fc32c5e, 0xbd755d6295aa388a, 0x3d786ead99977388, 0xbd789b3d387efa6e, 0x3d76011e175e64f8, 0xbd70cd70515af47b, 0x3d69402199dfdde7, 0xbda806743bc32b08, 0x3d530e561550364a, 0x3e170093985e2c1b, 0x3d331999a27ace63, 0xbe735f54db0f4dbc ],
[ 0x3ff0000000000000, 0xbc8a6d7d18831888, 0x3cc3e296303b2297, 0x39668cf648cfed1c, 0xbced8456ef97c744, 0x398fcded17005500, 0x3d0ccb92e6c24c8d, 0xb9aa704dc202cff2, 0xbd24c1aa8cf1229b, 0x39c652efa61e4ec2, 0x3d37918b6b83c0fb, 0x39b2fb01fb8836dc, 0xbd45f073659de44d, 0xb9e9ceb48a2d1931, 0x3d5134d070b5921e, 0x39d9af3038fcc184, 0xbd5730a2938c09dd, 0x3d5b4091041f5905, 0xbd5c3c44ab8c8421, 0x3d5a06b4f4c3044d, 0xbd5704f511555fe7, 0x3d4fe51fcfc1acba, 0x3d95e7229a07e7cd, 0x3d39f8121f6c3146, 0xbe0692b2f9b3f445, 0x3d1c8c34f73d3823, 0x3e6301e540260d52 ],
];

960
vendor/pxfm/src/err/erfc.rs vendored Normal file
View File

@@ -0,0 +1,960 @@
/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::dd_fmla;
use crate::double_double::DoubleDouble;
use crate::err::erf::{Erf, erf_accurate, erf_fast};
use crate::exponents::{EXP_REDUCE_T0, EXP_REDUCE_T1, ldexp};
use crate::round_ties_even::RoundTiesEven;
use std::hint::black_box;
static ASYMPTOTIC_POLY: [[u64; 13]; 6] = [
[
0x3fe20dd750429b6d,
0x3c61a1feb75a48a8,
0xbfd20dd750429b6c,
0x3fdb14c2f863e403,
0xbff0ecf9db3af35d,
0x400d9eb53ca6eeed,
0xc030a945830d95c8,
0x4056e8a963e2f1f5,
0xc0829b7ccc8f396f,
0x40b15e716e83c27e,
0xc0e1cfdcfbcaf22a,
0x4111986cc7a7e8fe,
0xc1371f7540590a91,
],
[
0x3fe20dd750429ae7,
0x3c863da89e801fd4,
0xbfd20dd750400795,
0x3fdb14c2f57c490c,
0xbff0ecf95c8c9014,
0x400d9e981f2321ef,
0xc030a81482de1506,
0x4056d662420a604b,
0xc08233c96fff7772,
0x40af5d62018d3e37,
0xc0d9ae55e9554450,
0x410052901e10d139,
0xc1166465df1385f0,
],
[
0x3fe20dd75041e3fc,
0xbc7c9b491c4920fc,
0xbfd20dd74e5f1526,
0x3fdb14c1d35a40e0,
0xbff0ecdecd30e86b,
0x400d9b4e7f725263,
0xc030958b5ca8fb39,
0x40563e3179bf609c,
0xc0806bbd1cd2d0fd,
0x40a7b66eb6d1d2f2,
0xc0cce5a4b1afab75,
0x40e8b5c6ae6f773c,
0xc0f5475860326f86,
],
[
0x3fe20dd75025cfe9,
0x3c55a92eef32fb20,
0xbfd20dd71eb9d4e7,
0x3fdb14af4c25db28,
0xbff0ebc78a22b3d8,
0x400d85287a0b3399,
0xc03045f751e5ca1d,
0x4054a0d87ddea589,
0xc07ac6a0981d1eee,
0x409f44822f567956,
0xc0bcba372de71349,
0x40d1a4a19f550ca4,
0xc0d52a580455ed79,
],
[
0x3fe20dd74eb31d84,
0xbc439c4054b7c090,
0xbfd20dd561af98c4,
0x3fdb1435165d9df1,
0xbff0e6b60308e940,
0x400d3ce30c140882,
0xc02f2083e404c299,
0x40520f113d89b42a,
0xc0741433ebd89f19,
0x4092f35b6a3154f6,
0xc0ab020a4313cf3b,
0x40b90f07e92da7ee,
0xc0b6565e1d7665c3,
],
[
0x3fe20dd744b3517b,
0xbc6f77ab25e01ab4,
0xbfd20dcc62ec4024,
0x3fdb125bfa4f66c1,
0xbff0d80e65381970,
0x400ca11fbcfa65b2,
0xc02cd9eaffb88315,
0x404e010db42e0da7,
0xc06c5c85250ef6a3,
0x4085e118d9c1eeaf,
0xc098d74be13d3d30,
0x40a211b1b2b5ac83,
0xc09900be759fc663,
],
];
static ASYMPTOTIC_POLY_ACCURATE: [[u64; 30]; 10] = [
[
0x3fe20dd750429b6d,
0x3c61ae3a912b08f0,
0xbfd20dd750429b6d,
0xbc51ae34c0606d68,
0x3fdb14c2f863e924,
0xbc796c0f4c848fc8,
0xbff0ecf9db3e71b6,
0x3c645d756bd288b0,
0x400d9eb53fad4672,
0xbcac61629de9adf2,
0xc030a945f3d147ea,
0x3cb8fec5ad7ece20,
0x4056e8c02f27ca6d,
0xc0829d1c21c363e0,
0x40b17349b70be627,
0xc0e28a6bb4686182,
0x411602d1662523ca,
0xc14ccae7625c4111,
0x4184237d064f6e0d,
0xc1bb1e5466ca3a2f,
0x41e90ae06a0f6cc1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
],
[
0x3fe20dd750429b6d,
0x3c61adaa62435c10,
0xbfd20dd750429b6d,
0xbc441516126827c8,
0x3fdb14c2f863e90b,
0x3c7a535780ba5ed4,
0xbff0ecf9db3e65d6,
0xbc9089edde27ad07,
0x400d9eb53fa52f20,
0xbcabc9737e9464ac,
0xc030a945f2cd7621,
0xbcc589f28b700332,
0x4056e8bffd7e194e,
0xc0829d18716876e2,
0x40b17312abe18250,
0xc0e287e73592805c,
0x4115ebf7394a39c1,
0xc14c2f14d46d0cf9,
0x4182af3d256f955e,
0xc1b7041659ebd7aa,
0x41e6039c232e2f71,
0xc2070ca15c5a07cb,
0,
0,
0,
0,
0,
0,
0,
0,
],
[
0x3fe20dd750429b6d,
0x3c5d3c35b5d37410,
0xbfd20dd750429b56,
0xbc7c028415f6f81b,
0x3fdb14c2f863c1cf,
0x3c51bb0de6470dbc,
0xbff0ecf9db33c363,
0x3c80f8068459eb16,
0x400d9eb53b9ce57b,
0x3ca20cce33e7d84a,
0xc030a945aa2ec4fa,
0xbcdf6e0fcd7c6030,
0x4056e8b824d2bfaa,
0xc0829cc372a6d0b0,
0x40b1703a99ddd429,
0xc0e2749f9a267cc6,
0x4115856a17271849,
0xc14a8bcb4ba9753f,
0x418035dcce882940,
0xc1b1e5d8c5e6e043,
0x41dfe3b4f365386e,
0xc20398fdef2b98fe,
0x42184234d4f4ea12,
0,
0,
0,
0,
0,
0,
0,
],
[
0x3fe20dd750429b6a,
0x3c8ae622b765e9fd,
0xbfd20dd750428f0e,
0x3c703c6c67d69513,
0x3fdb14c2f8563e8e,
0x3c6766a6bd7aa89c,
0xbff0ecf9d8dedd48,
0x3c90af52e90336e3,
0x400d9eb4aad086fe,
0x3ca640d371d54a19,
0xc030a93f1d01cfe0,
0xbcc68dbd8d9c522c,
0x4056e842e9fd5898,
0xc08299886ef1fb80,
0x40b15e0f0162c9a0,
0xc0e222dbc6b04cd8,
0x411460268db1ebdf,
0xc1474f53ce065fb3,
0x417961ca8553f870,
0xc1a8788395d13798,
0x41d35e37b25d0e81,
0xc1f707b7457c8f5e,
0x4211ff852df1c023,
0xc21b75d0ec56e2cd,
0,
0,
0,
0,
0,
0,
],
[
0x3fe20dd750429a8f,
0xbc766d8dda59bcea,
0xbfd20dd7503fdbab,
0x3c6707bdffc2b3fe,
0x3fdb14c2f6526025,
0xbc27fa4bb9541140,
0xbff0ecf99c417d45,
0xbc9748645ef7af94,
0x400d9eaa9c712a7d,
0x3ca79e478994ebb4,
0xc030a8ef11fbf141,
0x3cbb5c72d69f8954,
0x4056e4653e0455b1,
0xc08286909448e6cf,
0x40b113424ce76821,
0xc0e1346d859e76de,
0x4111f9f6cf2293bf,
0xc14258e6e3b337db,
0x41714029ecd465fb,
0xc19c530df5337a6f,
0x41c34bc4bbccd336,
0xc1e4a37c52641688,
0x420019707cec2974,
0xc21031fe736ea169,
0x420f6b3003de3ddf,
0,
0,
0,
0,
0,
],
[
0x3fe20dd75042756b,
0x3c84ad9178b56910,
0xbfd20dd74feda9e8,
0xbc78141c70bbc8d6,
0x3fdb14c2cb128467,
0xbc709aebaa106821,
0xbff0ecf603921a0b,
0x3c97d3cb5bceaf0b,
0x400d9e3e1751ca59,
0x3c76622ae5642670,
0xc030a686af57f547,
0x3cc083b320aff6b6,
0x4056cf0b6c027326,
0xc0823afcb69443d3,
0x40b03ab450d9f1b9,
0xc0de74cdb76bcab4,
0x410c671b60e607f1,
0xc138f1376d324ce4,
0x4163b64276234676,
0xc18aff0ce13c5a8e,
0x41aef20247251e87,
0xc1cc9f5662f721f6,
0x41e4687858e185e1,
0xc1f4fa507be073c2,
0x41fb99ac35ee4acc,
0xc1f16cb585ee3fa9,
0,
0,
0,
0,
],
[
0x3fe20dd7503e730d,
0x3c84e524a098a467,
0xbfd20dd7498fa6b2,
0x3c260a4e27751c80,
0x3fdb14c061bd2a0c,
0x3c695a8f847d2fc2,
0xbff0ecd0f11b8c7d,
0xbc94126deea76061,
0x400d9b1344463548,
0x3cafe09a4eca9b0e,
0xc030996ea52a87ed,
0xbca924f920db26c0,
0x40567a2264b556b0,
0xc0815dfc2c86b6b5,
0x40accc291b62efe4,
0xc0d81375a78e746a,
0x41033a6f15546329,
0xc12c1e9dc1216010,
0x4152397ea3d43fda,
0xc174661e5b2ea512,
0x4193412367ca5d45,
0xc1ade56b9d7f37c4,
0x41c2851d9722146d,
0xc1d19027baf0c3fe,
0x41d7e7b8b6ab58ac,
0xc1d4c446d56aaf22,
0x41c1492190400505,
0,
0,
0,
],
[
0x3fe20dd74ff10852,
0x3c8a32f26deff875,
0xbfd20dd6f06c491c,
0x3c770c16e1793358,
0x3fdb14a7d5e7fd4a,
0x3c7479998b54db5b,
0xbff0ebbdb3889c5f,
0xbc759b853e11369c,
0x400d89dd249d7ef8,
0xbc84b5edf0c8c314,
0xc0306526fb386114,
0xbc840d04eed7c7e0,
0x40557ff657e429ce,
0xc07ef63e90d38630,
0x40a6d4f34c4ea3da,
0xc0d04542b9e36a54,
0x40f577bf19097738,
0xc119702fe47c736d,
0x413a7ae12b54fdc6,
0xc157ca3f0f7c4fa9,
0x417225d983963cbf,
0xc1871a6eac612f9e,
0x4198086324225e1e,
0xc1a3de68670a7716,
0x41a91674de4dcbe9,
0xc1a6b44cc15b76c2,
0x419a36dae0f30d80,
0xc17cffc1747ea3dc,
0,
0,
],
[
0x3fe20dd74ba8f300,
0xbc59dd256871d210,
0xbfd20dd3593675bc,
0x3c7ec0e7ffa91ad9,
0x3fdb13eef86a077a,
0xbc74fb5d78d411b8,
0xbff0e5cf52a11f3a,
0xbc851f36c779dc8c,
0x400d4417a08b39d5,
0x3c91be9fb5956638,
0xc02f91b9f6ce80c3,
0xbccc9c99dd42829c,
0x405356439f45bb43,
0xc078c0ca12819b48,
0x409efcad2ecd6671,
0xc0c21b0af6fc1039,
0x40e327d215ee30c9,
0xc101fabda96167b0,
0x411d82e4373b315d,
0xc134ed9e2ff591e9,
0x41495c85dcd8eab5,
0xc159f016f0a3d62a,
0x41660e89d918b96f,
0xc16e97be202cba64,
0x4170d8a081619793,
0xc16c5422b4fcfc65,
0x4161131a9dc6aed1,
0xc14a457d9dced257,
0x4123605e980e8b86,
0,
],
[
0x3fe20dd7319d4d25,
0x3c82b02992c3b7ab,
0xbfd20dc29c13ab1b,
0xbc7d78d79b4ad767,
0x3fdb115a57b5ab13,
0xbc6aa8c45be0aa2e,
0xbff0d58ec437efd7,
0xbc5994f00a15e850,
0x400cb1742e229f23,
0xbca8000471d54399,
0xc02d99a5edf7b946,
0xbcbaf76ed7e35cde,
0x4050a8b71058eb28,
0xc072d88289da5bfc,
0x40943ddf24168edb,
0xc0b3e9dfc38b6d1a,
0x40d18d4df97ab3df,
0xc0eb550fc62dcab5,
0x41029cb71f116ed1,
0xc115fc9cc4e854e3,
0x41265915fd0567b1,
0xc1335eb5fca0e46d,
0x413c5261ecc0d789,
0xc14138932dc4eafc,
0x414117d4eb18facd,
0xc13af96163e35eca,
0x4130454a3a63c766,
0xc11c2ebc1d39b44a,
0x40ff3327698e0e6b,
0xc0d094febc3dff35,
],
];
// Approximation for the fast path of exp(z) for z=zh+zl,
// with |z| < 0.000130273 < 2^-12.88 and |zl| < 2^-42.6
// (assuming x^y does not overflow or underflow)
#[inline]
fn q_1(z_dd: DoubleDouble) -> DoubleDouble {
const C: [u64; 5] = [
0x3ff0000000000000,
0x3ff0000000000000,
0x3fe0000000000000,
0x3fc5555555995d37,
0x3fa55555558489dc,
];
let z = z_dd.to_f64();
let mut q = dd_fmla(f64::from_bits(C[4]), z_dd.hi, f64::from_bits(C[3]));
q = dd_fmla(q, z, f64::from_bits(C[2]));
let mut v = DoubleDouble::from_exact_add(f64::from_bits(C[1]), q * z);
v = DoubleDouble::quick_mult(z_dd, v);
DoubleDouble::f64_add(f64::from_bits(C[0]), v)
}
#[inline]
fn exp_1(x: DoubleDouble) -> DoubleDouble {
const INVLOG2: f64 = f64::from_bits(0x40b71547652b82fe); /* |INVLOG2-2^12/log(2)| < 2^-43.4 */
let k = (x.hi * INVLOG2).round_ties_even_finite();
const LOG2_DD: DoubleDouble = DoubleDouble::new(
f64::from_bits(0x3bbabc9e3b39803f),
f64::from_bits(0x3f262e42fefa39ef),
);
let k_dd = DoubleDouble::quick_f64_mult(k, LOG2_DD);
let mut y_dd = DoubleDouble::from_exact_add(x.hi - k_dd.hi, x.lo);
y_dd.lo -= k_dd.lo;
let ki: i64 = k as i64; /* Note: k is an integer, this is just a conversion. */
let mi = (ki >> 12).wrapping_add(0x3ff);
let i2: i64 = (ki >> 6) & 0x3f;
let i1: i64 = ki & 0x3f;
let t1 = DoubleDouble::new(
f64::from_bits(EXP_REDUCE_T0[i2 as usize].0),
f64::from_bits(EXP_REDUCE_T0[i2 as usize].1),
);
let t2 = DoubleDouble::new(
f64::from_bits(EXP_REDUCE_T1[i1 as usize].0),
f64::from_bits(EXP_REDUCE_T1[i1 as usize].1),
);
let mut v = DoubleDouble::quick_mult(t2, t1);
let q = q_1(y_dd);
v = DoubleDouble::quick_mult(v, q);
let scale = f64::from_bits((mi as u64) << 52);
v.hi *= scale;
v.lo *= scale;
v
}
struct Exp {
e: i32,
result: DoubleDouble,
}
fn exp_accurate(x_dd: DoubleDouble) -> Exp {
static E2: [u64; 28] = [
0x3ff0000000000000,
0xb960000000000000,
0x3ff0000000000000,
0xb9be200000000000,
0x3fe0000000000000,
0x3a03c00000000000,
0x3fc5555555555555,
0x3c655555555c78d9,
0x3fa5555555555555,
0x3c455555545616e2,
0x3f81111111111111,
0x3c011110121fc314,
0x3f56c16c16c16c17,
0xbbef49e06ee3a56e,
0x3f2a01a01a01a01a,
0x3b6b053e1eeab9c0,
0x3efa01a01a01a01a,
0x3ec71de3a556c733,
0x3e927e4fb7789f66,
0x3e5ae64567f54abe,
0x3e21eed8eff8958b,
0x3de6124613837216,
0x3da93974aaf26a57,
0x3d6ae7f4fd6d0bd9,
0x3d2ae7e982620b25,
0x3ce94e4ca59460d8,
0x3ca69a2a4b7ef36d,
0x3c6abfe1602308c9,
];
const LOG2INV: f64 = f64::from_bits(0x3ff71547652b82fe);
let k: i32 = unsafe {
(x_dd.hi * LOG2INV)
.round_ties_even_finite()
.to_int_unchecked::<i32>()
};
const LOG2_H: f64 = f64::from_bits(0x3fe62e42fefa39ef);
/* we approximate LOG2Lacc ~ log(2) - LOG2H with 38 bits, so that
k*LOG2Lacc is exact (k has at most 11 bits) */
const LOG2_L: f64 = f64::from_bits(0x3c7abc9e3b398000);
const LOG2_TINY: f64 = f64::from_bits(0x398f97b57a079a19);
let yh = dd_fmla(-k as f64, LOG2_H, x_dd.hi);
/* since |xh+xl| >= 2.92 we have |k| >= 4;
(|k|-1/2)*log(2) <= |x| <= (|k|+1/2)*log(2) thus
1-1/(2|k|) <= |x/(k*log(2))| <= 1+1/(2|k|) thus by Sterbenz theorem
yh is exact too */
let mut t = DoubleDouble::from_full_exact_add(-k as f64 * LOG2_L, x_dd.lo);
let mut y_dd = DoubleDouble::from_exact_add(yh, t.hi);
y_dd.lo = dd_fmla(-k as f64, LOG2_TINY, y_dd.lo + t.lo);
/* now yh+yl approximates xh + xl - k*log(2), and we approximate p(yh+yl)
in h + l */
/* Since |xh| <= 742, we assume |xl| <= ulp(742) = 2^-43. Then since
|k| <= round(742/log(2)) = 1070, |yl| <= 1070*LOG2L + 2^-42 < 2^-42.7.
Since |yh| <= log(2)/2, the contribution of yl is negligible as long
as |i*p[i]*yh^(i-1)*yl| < 2^-104, which holds for i >= 16.
Thus for coefficients of degree 16 or more, we don't take yl into account.
*/
let mut h = f64::from_bits(E2[19 + 8]); // degree 19
for a in (16..=18).rev() {
h = dd_fmla(h, y_dd.hi, f64::from_bits(E2[a + 8])); // degree i
}
/* degree 15: h*(yh+yl)+E2[15 + 8] */
t = DoubleDouble::from_exact_mult(h, y_dd.hi);
t.lo = dd_fmla(h, y_dd.lo, t.lo);
let mut v = DoubleDouble::from_exact_add(f64::from_bits(E2[15 + 8]), t.hi);
v.lo += t.lo;
for a in (8..=14).rev() {
/* degree i: (h+l)*(yh+yl)+E2[i+8] */
t = DoubleDouble::quick_mult(v, y_dd);
v = DoubleDouble::from_exact_add(f64::from_bits(E2[a + 8]), t.hi);
v.lo += t.lo;
}
for a in (0..=7).rev() {
/* degree i: (h+l)*(yh+yl)+E2[2i]+E2[2i+1] */
t = DoubleDouble::quick_mult(v, y_dd);
v = DoubleDouble::from_exact_add(f64::from_bits(E2[2 * a]), t.hi);
v.lo += t.lo + f64::from_bits(E2[2 * a + 1]);
}
Exp { e: k, result: v }
}
#[cold]
fn erfc_asympt_accurate(x: f64) -> f64 {
/* subnormal exceptions */
if x == f64::from_bits(0x403a8f7bfbd15495) {
return dd_fmla(
f64::from_bits(0x0000000000000001),
-0.25,
f64::from_bits(0x000667bd620fd95b),
);
}
let u_dd = DoubleDouble::from_exact_mult(x, x);
let exp_result = exp_accurate(DoubleDouble::new(-u_dd.lo, -u_dd.hi));
/* compute 1/x as double-double */
let yh = 1.0 / x;
/* Newton's iteration for 1/x is y -> y + y*(1-x*y) */
let yl = yh * dd_fmla(-x, yh, 1.0);
// yh+yl approximates 1/x
static THRESHOLD: [u64; 10] = [
0x3fb4500000000000,
0x3fbe000000000000,
0x3fc3f00000000000,
0x3fc9500000000000,
0x3fcf500000000000,
0x3fd3100000000000,
0x3fd7100000000000,
0x3fdbc00000000000,
0x3fe0b00000000000,
0x3fe3000000000000,
];
let mut i = 0usize;
while i < THRESHOLD.len() && yh > f64::from_bits(THRESHOLD[i]) {
i += 1;
}
let p = ASYMPTOTIC_POLY_ACCURATE[i];
let mut u_dd = DoubleDouble::from_exact_mult(yh, yh);
u_dd.lo = dd_fmla(2.0 * yh, yl, u_dd.lo);
/* the polynomial p has degree 29+2i, and its coefficient of largest
degree is p[14+6+i] */
let mut z_dd = DoubleDouble::new(0., f64::from_bits(p[14 + 6 + i]));
for a in (13..=27 + 2 * i).rev().step_by(2) {
/* degree j: (zh+zl)*(uh+ul)+p[(j-1)/2+6]] */
let v = DoubleDouble::quick_mult(z_dd, u_dd);
z_dd = DoubleDouble::from_full_exact_add(f64::from_bits(p[(a - 1) / 2 + 6]), v.hi);
z_dd.lo += v.lo;
}
for a in (1..=11).rev().step_by(2) {
let v = DoubleDouble::quick_mult(z_dd, u_dd);
z_dd = DoubleDouble::from_full_exact_add(f64::from_bits(p[a - 1]), v.hi);
z_dd.lo += v.lo + f64::from_bits(p[a]);
}
/* multiply by yh+yl */
u_dd = DoubleDouble::quick_mult(z_dd, DoubleDouble::new(yl, yh));
/* now uh+ul approximates p(1/x), i.e., erfc(x)*exp(x^2) */
/* now multiply (uh+ul)*(eh+el), after normalizing uh+ul to reduce the
number of exceptional cases */
u_dd = DoubleDouble::from_exact_add(u_dd.hi, u_dd.lo);
let v = DoubleDouble::quick_mult(u_dd, exp_result.result);
/* multiply by 2^e */
/* multiply by 2^e */
let mut res = ldexp(v.to_f64(), exp_result.e);
if res < f64::from_bits(0x0010000000000000) {
/* for erfc(x) in the subnormal range, we have to perform a special
rounding */
let mut corr = v.hi - ldexp(res, -exp_result.e);
corr += v.lo;
/* add corr*2^e */
res += ldexp(corr, exp_result.e);
}
res
}
#[cold]
fn erfc_accurate(x: f64) -> f64 {
if x < 0. {
let mut v_dd = erf_accurate(-x);
let t = DoubleDouble::from_exact_add(1.0, v_dd.hi);
v_dd.hi = t.hi;
v_dd.lo += t.lo;
return v_dd.to_f64();
} else if x <= f64::from_bits(0x3ffb59ffb450828c) {
// erfc(x) >= 2^-6
let mut v_dd = erf_accurate(x);
let t = DoubleDouble::from_exact_add(1.0, -v_dd.hi);
v_dd.hi = t.hi;
v_dd.lo = t.lo - v_dd.lo;
return v_dd.to_f64();
}
// now 0x1.b59ffb450828cp+0 < x < 0x1.b39dc41e48bfdp+4
erfc_asympt_accurate(x)
}
/* Fast path for 0x1.713786d9c7c09p+1 < x < 0x1.b39dc41e48bfdp+4,
using the asymptotic formula erfc(x) = exp(-x^2) * p(1/x)*/
fn erfc_asympt_fast(x: f64) -> Erf {
/* for x >= 0x1.9db1bb14e15cap+4, erfc(x) < 2^-970, and we might encounter
underflow issues in the computation of l, thus we delegate this case
to the accurate path */
if x >= f64::from_bits(0x4039db1bb14e15ca) {
return Erf {
err: 1.0,
result: DoubleDouble::default(),
};
}
let mut u = DoubleDouble::from_exact_mult(x, x);
let e_dd = exp_1(DoubleDouble::new(-u.lo, -u.hi));
/* the assumptions from exp_1 are satisfied:
* a_mul ensures |ul| <= ulp(uh), thus |ul/uh| <= 2^-52
* since |x| < 0x1.9db1bb14e15cap+4 we have
|ul| < ulp(0x1.9db1bb14e15cap+4^2) = 2^-43 */
/* eh+el approximates exp(-x^2) with maximal relative error 2^-74.139 */
/* compute 1/x as double-double */
let yh = 1.0 / x;
/* Assume 1 <= x < 2, then 0.5 <= yh <= 1,
and yh = 1/x + eps with |eps| <= 2^-53. */
/* Newton's iteration for 1/x is y -> y + y*(1-x*y) */
let yl = yh * dd_fmla(-x, yh, 1.0);
/* x*yh-1 = x*(1/x+eps)-1 = x*eps
with |x*eps| <= 2^-52, thus the error on the FMA is bounded by
ulp(2^-52.1) = 2^-105.
Now |yl| <= |yh| * 2^-52 <= 2^-52, thus the rounding error on
yh * __builtin_fma (-x, yh, 1.0) is bounded also by ulp(2^-52.1) = 2^-105.
From [6], Lemma 3.7, if yl was computed exactly, then yh+yl would differ
from 1/x by at most yh^2/theta^3*(1/x-yh)^2 for some theta in [yh,1/x]
or [1/x,yh].
Since yh, 1/x <= 1, this gives eps^2 <= 2^-106.
Adding the rounding errors, we have:
|yh + yl - 1/x| <= 2^-105 + 2^-105 + 2^-106 < 2^-103.67.
For the relative error, since |yh| >= 1/2, this gives:
|yh + yl - 1/x| < 2^-102.67 * |yh+yl|
*/
const THRESHOLD: [u64; 6] = [
0x3fbd500000000000,
0x3fc59da6ca291ba6,
0x3fcbc00000000000,
0x3fd0c00000000000,
0x3fd3800000000000,
0x3fd6300000000000,
];
let mut i = 0usize;
while i < THRESHOLD.len() && yh > f64::from_bits(THRESHOLD[i]) {
i += 1;
}
let p = ASYMPTOTIC_POLY[i];
u = DoubleDouble::from_exact_mult(yh, yh);
/* Since |yh| <= 1, we have |uh| <= 1 and |ul| <= 2^-53. */
u.lo = dd_fmla(2.0 * yh, yl, u.lo);
/* uh+ul approximates (yh+yl)^2, with absolute error bounded by
ulp(ul) + yl^2, where ulp(ul) is the maximal rounding error in
the FMA, and yl^2 is the neglected term.
Since |ul| <= 2^-53, ulp(ul) <= 2^-105, and since |yl| <= 2^-52,
this yields |uh + ul - yh^2| <= 2^-105 + 2^-104 < 2^-103.41.
For the relative error, since |(yh+yl)^2| >= 1/4:
|uh + ul - yh^2| < 2^-101.41 * |uh+ul|.
And relatively to 1/x^2:
yh + yl = 1/x * (1 + eps1) with |eps1| < 2^-102.67
uh + ul = (yh+yl)^2 * (1 + eps2) with |eps2| < 2^-101.41
This yields:
|uh + ul - 1/x| < 2^-100.90 * |uh+ul|.
*/
/* evaluate p(uh+ul) */
let mut zh: f64 = f64::from_bits(p[12]); // degree 23
zh = dd_fmla(zh, u.hi, f64::from_bits(p[11])); // degree 21
zh = dd_fmla(zh, u.hi, f64::from_bits(p[10])); // degree 19
/* degree 17: zh*(uh+ul)+p[i] */
let mut v = DoubleDouble::quick_f64_mult(zh, u);
let mut z_dd = DoubleDouble::from_exact_add(f64::from_bits(p[9]), v.hi);
z_dd.lo += v.lo;
for a in (3..=15).rev().step_by(2) {
v = DoubleDouble::quick_mult(z_dd, u);
z_dd = DoubleDouble::from_exact_add(f64::from_bits(p[((a + 1) / 2) as usize]), v.hi);
z_dd.lo += v.lo;
}
/* degree 1: (zh+zl)*(uh+ul)+p[0]+p[1] */
v = DoubleDouble::quick_mult(z_dd, u);
z_dd = DoubleDouble::from_exact_add(f64::from_bits(p[0]), v.hi);
z_dd.lo += v.lo + f64::from_bits(p[1]);
/* multiply by yh+yl */
u = DoubleDouble::quick_mult(z_dd, DoubleDouble::new(yl, yh));
/* now uh+ul approximates p(1/x) */
/* now multiply (uh+ul)*(eh+el) */
v = DoubleDouble::quick_mult(u, e_dd);
/* Write y = 1/x. We have the following errors:
* the maximal mathematical error is:
|erfc(x)*exp(x^2) - p(y)| < 2^-71.804 * |p(y)| (for i=3) thus
|erfc(x) - exp(-x^2)*p(y)| < 2^-71.804 * |exp(-x^2)*p(y)|
* the error in approximating exp(-x^2) by eh+el:
|eh + el - exp(-x^2)| < 2^-74.139 * |eh + el|
* the fact that we evaluate p on yh+yl instead of 1/x
this error is bounded by |p'| * |yh+yl - 1/x|, where
|yh+yl - 1/x| < 2^-102.67 * |yh+yl|, and the relative
error is bounded by |p'/p| * |yh+yl - 1/x|.
Since the maximal value of |p'/p| is bounded by 27.2 (for i=0),
this yields 27.2 * 2^-102.67 < 2^-97.9
* the rounding errors when evaluating p on yh+yl: this error is bounded
(relatively) by 2^-67.184 (for i=5), see analyze_erfc_asympt_fast()
in erfc.sage
* the rounding error in (uh+ul)*(eh+el): we assume this error is bounded
by 2^-80 (relatively)
This yields a global relative bound of:
(1+2^-71.804)*(1+2^-74.139)*(1+2^-97.9)*(1+2^-67.184)*(1+2^-80)-1
< 2^-67.115
*/
if v.hi >= f64::from_bits(0x044151b9a3fdd5c9) {
Erf {
err: f64::from_bits(0x3bbd900000000000) * v.hi,
result: v,
} /* 2^-67.115 < 0x1.d9p-68 */
} else {
Erf {
result: v,
err: f64::from_bits(0x0010000000000000),
} // this overestimates 0x1.d9p-68 * h
}
}
#[inline]
fn erfc_fast(x: f64) -> Erf {
if x < 0.
// erfc(x) = 1 - erf(x) = 1 + erf(-x)
{
let res = erf_fast(-x);
/* h+l approximates erf(-x), with relative error bounded by err,
where err <= 0x1.78p-69 */
let err = res.err * res.result.hi; /* convert into absolute error */
let mut t = DoubleDouble::from_exact_add(1.0, res.result.hi);
t.lo += res.result.lo;
// since h <= 2, the fast_two_sum() error is bounded by 2^-105*h <= 2^-104
/* After the fast_two_sum() call, we have |t| <= ulp(h) <= ulp(2) = 2^-51
thus assuming |l| <= 2^-51 after the cr_erf_fast() call,
we have |t| <= 2^-50 here, thus the rounding
error on t -= *l is bounded by ulp(2^-50) = 2^-102.
The absolute error is thus bounded by err + 2^-104 + 2^-102
= err + 0x1.4p-102.
The maximal value of err here is for |x| < 0.0625, where cr_erf_fast()
returns 0x1.78p-69, and h=1/2, yielding err = 0x1.78p-70 here.
Adding 0x1.4p-102 is thus exact. */
return Erf {
err: err + f64::from_bits(0x3994000000000000),
result: t,
};
} else if x <= f64::from_bits(0x400713786d9c7c09) {
let res = erf_fast(x);
/* h+l approximates erf(x), with relative error bounded by err,
where err <= 0x1.78p-69 */
let err = res.err * res.result.hi; /* convert into absolute error */
let mut t = DoubleDouble::from_exact_add(1.0, -res.result.hi);
t.lo -= res.result.lo;
/* for x >= 0x1.e861fbb24c00ap-2, erf(x) >= 1/2, thus 1-h is exact
by Sterbenz theorem, thus t = 0 in fast_two_sum(), and we have t = -l
here, thus the absolute error is err */
if x >= f64::from_bits(0x3fde861fbb24c00a) {
return Erf { err, result: t };
}
/* for x < 0x1.e861fbb24c00ap-2, the error in fast_two_sum() is bounded
by 2^-105*h, and since h <= 1/2, this yields 2^-106.
After the fast_two_sum() call, we have |t| <= ulp(h) <= ulp(1/2) = 2^-53
thus assuming |l| <= 2^-53 after the cr_erf_fast() call,
we have |t| <= 2^-52 here, thus the rounding
error on t -= *l is bounded by ulp(2^-52) = 2^-104.
The absolute error is thus bounded by err + 2^-106 + 2^-104
The maximal value of err here is for x < 0.0625, where cr_erf_fast()
returns 0x1.78p-69, and h=1/2, yielding err = 0x1.78p-70 here.
Adding 0x1.4p-104 is thus exact. */
return Erf {
err: err + f64::from_bits(0x3974000000000000),
result: t,
};
}
/* Now THRESHOLD1 < x < 0x1.b39dc41e48bfdp+4 thus erfc(x) < 0.000046. */
/* on a i7-8700 with gcc 12.2.0, for x in [THRESHOLD1,+5.0],
the average reciprocal throughput is about 111 cycles
(among which 20 cycles for exp_1) */
erfc_asympt_fast(x)
}
/// Complementary error function
///
/// Max ulp 0.5
pub fn f_erfc(x: f64) -> f64 {
let t: u64 = x.to_bits();
let at: u64 = t & 0x7fff_ffff_ffff_ffff;
if t >= 0x8000000000000000u64
// x = -NaN or x <= 0 (excluding +0)
{
// for x <= -0x1.7744f8f74e94bp2, erfc(x) rounds to 2 (to nearest)
if t >= 0xc017744f8f74e94bu64
// x = NaN or x <= -0x1.7744f8f74e94bp2
{
if t >= 0xfff0000000000000u64 {
// -Inf or NaN
if t == 0xfff0000000000000u64 {
return 2.0;
} // -Inf
return x + x; // NaN
}
return black_box(2.0) - black_box(f64::from_bits(0x3c90000000000000)); // rounds to 2 or below(2)
}
// for -9.8390953768041405e-17 <= x <= 0, erfc(x) rounds to 1 (to nearest)
if f64::from_bits(0xbc9c5bf891b4ef6a) <= x {
return dd_fmla(-x, f64::from_bits(0x3c90000000000000), 1.0);
}
} else
// x = +NaN or x >= 0 (excluding -0)
{
// for x >= 0x1.b39dc41e48bfdp+4, erfc(x) < 2^-1075: rounds to 0 or 2^-1074
if at >= 0x403b39dc41e48bfdu64
// x = NaN or x >= 0x1.b39dc41e48bfdp+4
{
if at >= 0x7ff0000000000000u64 {
// +Inf or NaN
if at == 0x7ff0000000000000u64 {
return 0.0;
} // +Inf
return x + x; // NaN
}
return black_box(f64::from_bits(0x0000000000000001)) * black_box(0.25); // 0 or 2^-1074 wrt rounding
}
// for 0 <= x <= 0x1.c5bf891b4ef6ap-55, erfc(x) rounds to 1 (to nearest)
if x <= f64::from_bits(0x3c8c5bf891b4ef6a) {
return dd_fmla(-x, f64::from_bits(0x3c90000000000000), 1.0);
}
}
/* now -0x1.7744f8f74e94bp+2 < x < -0x1.c5bf891b4ef6ap-54
or 0x1.c5bf891b4ef6ap-55 < x < 0x1.b39dc41e48bfdp+4 */
let result = erfc_fast(x);
let left = result.result.hi + (result.result.lo - result.err);
let right = result.result.hi + (result.result.lo + result.err);
if left == right {
return left;
}
erfc_accurate(x)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_erfc() {
assert_eq!(f_erfc(1.0), 0.15729920705028513);
assert_eq!(f_erfc(0.5), 0.4795001221869535);
assert_eq!(f_erfc(0.000000005), 0.9999999943581042);
assert_eq!(f_erfc(-0.00000000000065465465423305), 1.0000000000007387);
assert!(f_erfc(f64::NAN).is_nan());
assert_eq!(f_erfc(f64::INFINITY), 0.0);
assert_eq!(f_erfc(f64::NEG_INFINITY), 2.0);
}
}

421
vendor/pxfm/src/err/erfcx.rs vendored Normal file
View File

@@ -0,0 +1,421 @@
/*
* // Copyright (c) Radzivon Bartoshyk 9/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::double_double::DoubleDouble;
use crate::pow_exec::exp_dd_fast;
#[inline]
fn core_erfcx(x: f64) -> DoubleDouble {
if x <= 8. {
// Rational approximant for erfcx generated by Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=Exp[x^2]Erfc[x]
// {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{1, 8},11,11},WorkingPrecision->75,MaxIterations->100]
// num=Numerator[approx];
// den=Denominator[approx];
// coeffs=CoefficientList[num,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// coeffs=CoefficientList[den,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const P: [(u64, u64); 12] = [
(0xbc836faeb9a312bb, 0x3ff000000000ee8e),
(0x3c91842f891bec6a, 0x4002ca20a78aaf8f),
(0x3c7916e8a1c30681, 0x4005e955f70aed5b),
(0x3cabad150d828d82, 0x4000646f5807ad07),
(0xbc6f482680d66d9c, 0x3ff1449e03ed381c),
(0xbc7188796156ae19, 0x3fdaa7e997e3b034),
(0xbc5c8af0642761e3, 0x3fbe836282058d4a),
(0xbc372829be2d072f, 0x3f99a2b2adc2ec05),
(0x3c020cc8b96000ab, 0x3f6e6cc3d120a955),
(0x3bdd138e6c136806, 0x3f3743d6735eaf13),
(0xbb9fbd22f0675122, 0x3ef1c1d36ebe29a2),
(0xb89093cc981c934c, 0xbc43c18bc6385c74),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let x4 = x2 * x2;
let x8 = x4 * x4;
let e0 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[1]),
x,
DoubleDouble::from_bit_pair(P[0]),
);
let e1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[3]),
x,
DoubleDouble::from_bit_pair(P[2]),
);
let e2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[5]),
x,
DoubleDouble::from_bit_pair(P[4]),
);
let e3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[7]),
x,
DoubleDouble::from_bit_pair(P[6]),
);
let e4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[9]),
x,
DoubleDouble::from_bit_pair(P[8]),
);
let e5 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[11]),
x,
DoubleDouble::from_bit_pair(P[10]),
);
let f0 = DoubleDouble::mul_add(x2, e1, e0);
let f1 = DoubleDouble::mul_add(x2, e3, e2);
let f2 = DoubleDouble::mul_add(x2, e5, e4);
let g0 = DoubleDouble::mul_add(x4, f1, f0);
let p_num = DoubleDouble::mul_add(x8, f2, g0);
const Q: [(u64, u64); 12] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbc95d65be031374e, 0x400bd10c4fb1dbe5),
(0x3cb2d8f661db08a0, 0x4016a649ff973199),
(0x3ca32cbcfdc0ea93, 0x4016daab399c1ffc),
(0xbca2982868536578, 0x400fd61ab892d14c),
(0xbca2e29199e17fd9, 0x40001f56c4d495a3),
(0x3c412ce623a1790a, 0x3fe852b582135164),
(0x3c61152eaf4b0dc5, 0x3fcb760564da7cde),
(0xbc1b57ff91d81959, 0x3fa6e146988df835),
(0x3c17183d8445f19a, 0x3f7b06599b5e912f),
(0xbbd0ada61b85ff98, 0x3f449e39467b73d0),
(0xbb658d84fc735e67, 0x3eff794442532b51),
];
let e0 = DoubleDouble::mul_f64_add_f64(
DoubleDouble::from_bit_pair(Q[1]),
x,
f64::from_bits(0x3ff0000000000000),
);
let e1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[3]),
x,
DoubleDouble::from_bit_pair(Q[2]),
);
let e2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[5]),
x,
DoubleDouble::from_bit_pair(Q[4]),
);
let e3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[7]),
x,
DoubleDouble::from_bit_pair(Q[6]),
);
let e4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[9]),
x,
DoubleDouble::from_bit_pair(Q[8]),
);
let e5 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[11]),
x,
DoubleDouble::from_bit_pair(Q[10]),
);
let f0 = DoubleDouble::mul_add(x2, e1, e0);
let f1 = DoubleDouble::mul_add(x2, e3, e2);
let f2 = DoubleDouble::mul_add(x2, e5, e4);
let g0 = DoubleDouble::mul_add(x4, f1, f0);
let p_den = DoubleDouble::mul_add(x8, f2, g0);
return DoubleDouble::div(p_num, p_den);
}
// for large x erfcx(x) ~ 1/sqrt(pi) / x * R(1/x)
const ONE_OVER_SQRT_PI: DoubleDouble =
DoubleDouble::from_bit_pair((0x3c61ae3a914fed80, 0x3fe20dd750429b6d));
let r = DoubleDouble::from_quick_recip(x);
// Rational approximant generated by Wolfram:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=Exp[1/x^2]Erfc[1/x]/x*Sqrt[Pi]
// {err0,approx}=MiniMaxApproximation[f[z],{z,{2^-23,1/8},8,8},WorkingPrecision->75,MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[num,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// coeffs=CoefficientList[den,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const P: [(u64, u64); 9] = [
(0xbb1d2ee37e46a4cd, 0x3ff0000000000000),
(0x3ca2e575a4ce3d30, 0x4001303ab00c8bac),
(0xbccf38381e5ee521, 0x4030a97aeed54c9f),
(0xbcc3a2842df0dd3d, 0x4036f7733c9fd2f9),
(0xbcfeaf46506f16ed, 0x4051c5f382750553),
(0x3ccbb9f5e11d176a, 0x404ac0081e0749e0),
(0xbcf374f8966ae2a5, 0x4052082526d99a5c),
(0x3cbb5530b924f224, 0x402feabbf6571c29),
(0xbcbcdd50a3ca4776, 0x40118726e1f2d204),
];
const Q: [(u64, u64); 9] = [
(0x0000000000000000, 0x3ff0000000000000),
(0x3ca2e4613c9e0017, 0x4001303ab00c8bac),
(0xbcce5f17cf14e51d, 0x4031297aeed54c9f),
(0xbcdf7e0fed176f92, 0x40380a76e7a09bb2),
(0x3cfc57b67a2797af, 0x4053bb22e04faf3e),
(0xbcd3e63b7410b46b, 0x404ff46317ae9483),
(0xbce122c15db2653f, 0x405925ef8a428c36),
(0x3ce174ebe3e52c8e, 0x4040f49acfe692e1),
(0xbcda0e267ce6e2e6, 0x40351a07878bfbd3),
];
let mut p_num = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[8]),
r,
DoubleDouble::from_bit_pair(P[7]),
);
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[6]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[5]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[4]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[3]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[2]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[1]));
p_num = DoubleDouble::mul_add(p_num, r, DoubleDouble::from_bit_pair(P[0]));
let mut p_den = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[8]),
r,
DoubleDouble::from_bit_pair(Q[7]),
);
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[6]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[5]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[4]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[3]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[2]));
p_den = DoubleDouble::mul_add(p_den, r, DoubleDouble::from_bit_pair(Q[1]));
p_den = DoubleDouble::mul_add_f64(p_den, r, f64::from_bits(0x3ff0000000000000));
let v0 = DoubleDouble::quick_mult(ONE_OVER_SQRT_PI, r);
let v1 = DoubleDouble::div(p_num, p_den);
DoubleDouble::quick_mult(v0, v1)
}
/// Scaled complementary error function (exp(x^2)*erfc(x))
pub fn f_erfcx(x: f64) -> f64 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0x7ffu64 << 53 || ux <= 0x7960000000000000u64 {
// x == NaN, x == inf, x == 0, |x| <= f64::EPSILON
if x.is_nan() {
return f64::NAN;
}
if x.to_bits().wrapping_shl(1) == 0 {
return 1.;
}
if x.is_infinite() {
return if x.is_sign_positive() {
0.
} else {
f64::INFINITY
};
}
if ux <= 0x7888f5c28f5c28f6u64 {
// |x| <= 2.2204460492503131e-18
return 1.;
}
// |x| <= f64::EPSILON
use crate::common::f_fmla;
const M_TWO_OVER_SQRT_PI: DoubleDouble =
DoubleDouble::from_bit_pair((0xbc71ae3a914fed80, 0xbff20dd750429b6d));
return f_fmla(
M_TWO_OVER_SQRT_PI.lo,
x,
f_fmla(M_TWO_OVER_SQRT_PI.hi, x, 1.),
);
}
if x.to_bits() >= 0xc03aa449ebc84dd6 {
// x <= -sqrt(709.783) ~ -26.6417
return f64::INFINITY;
}
let ax = x.to_bits() & 0x7fff_ffff_ffff_ffffu64;
if ax <= 0x3ff0000000000000u64 {
// |x| <= 1
// Rational approximant generated by Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=Exp[x^2]Erfc[x]
// {err0,approx}=MiniMaxApproximation[f[z],{z,{-1, 1},10,10},WorkingPrecision->75,MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[num,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// coeffs=CoefficientList[den,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const P: [(u64, u64); 11] = [
(0xbb488611350b1950, 0x3ff0000000000000),
(0xbc86ae482c7f2342, 0x3ff9c5d39e89602f),
(0x3c6702d70b807254, 0x3ff5a4c406d6468b),
(0x3c7fe41fc43cfed5, 0x3fe708e7f401bd0c),
(0x3c73a4a355172c6d, 0x3fd0d9a0c1a7126c),
(0x3c5f4c372faa270f, 0x3fb154722e30762e),
(0xbc04c0227976379e, 0x3f88ecebb62ce646),
(0xbbdc9ea151b9eb33, 0x3f580ea84143877b),
(0xbb6dae7001a91491, 0x3f1c3c5f95579b0a),
(0x3b6aca5e82c52897, 0x3ecea4db51968d9e),
(0x3a41c4edd175d2af, 0x3dbc0dccea7fc8ed),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let x4 = x2 * x2;
let x8 = x4 * x4;
let q0 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[1]),
x,
DoubleDouble::from_bit_pair(P[0]),
);
let q1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[3]),
x,
DoubleDouble::from_bit_pair(P[2]),
);
let q2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[5]),
x,
DoubleDouble::from_bit_pair(P[4]),
);
let q3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[7]),
x,
DoubleDouble::from_bit_pair(P[6]),
);
let q4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(P[9]),
x,
DoubleDouble::from_bit_pair(P[8]),
);
let r0 = DoubleDouble::mul_add(x2, q1, q0);
let r1 = DoubleDouble::mul_add(x2, q3, q2);
let s0 = DoubleDouble::mul_add(x4, r1, r0);
let s1 = DoubleDouble::mul_add(x2, DoubleDouble::from_bit_pair(P[10]), q4);
let p_num = DoubleDouble::mul_add(x8, s1, s0);
const Q: [(u64, u64); 11] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbc7bae414cad99c8, 0x4005e9d57765fdce),
(0x3c8fa553bed15758, 0x400b8c670b3fbcda),
(0x3ca6c7ad610f1019, 0x4004f2ca59958153),
(0x3c87787f336cc4e6, 0x3ff55c267090315a),
(0xbc6ef55d4b2c4150, 0x3fde8b84b64b6f4e),
(0x3c570d63c94be3a3, 0x3fbf0d5e36017482),
(0x3c1882a745ef572e, 0x3f962f73633506c1),
(0xbc0850bb6fc82764, 0x3f65593e0dc46acd),
(0xbbb9dc0097d7d776, 0x3f290545603e2f94),
(0xbb776e5781e3889d, 0x3edb29c49d18cf89),
];
let q0 = DoubleDouble::mul_f64_add_f64(
DoubleDouble::from_bit_pair(Q[1]),
x,
f64::from_bits(0x3ff0000000000000),
);
let q1 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[3]),
x,
DoubleDouble::from_bit_pair(Q[2]),
);
let q2 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[5]),
x,
DoubleDouble::from_bit_pair(Q[4]),
);
let q3 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[7]),
x,
DoubleDouble::from_bit_pair(Q[6]),
);
let q4 = DoubleDouble::mul_f64_add(
DoubleDouble::from_bit_pair(Q[9]),
x,
DoubleDouble::from_bit_pair(Q[8]),
);
let r0 = DoubleDouble::mul_add(x2, q1, q0);
let r1 = DoubleDouble::mul_add(x2, q3, q2);
let s0 = DoubleDouble::mul_add(x4, r1, r0);
let s1 = DoubleDouble::mul_add(x2, DoubleDouble::from_bit_pair(Q[10]), q4);
let p_den = DoubleDouble::mul_add(x8, s1, s0);
let v = DoubleDouble::div(p_num, p_den);
return v.to_f64();
}
let mut erfcx_abs_x = core_erfcx(f64::from_bits(ax));
if x < 0. {
// exp(x^2)erfc(-x) = 2*exp(x^2) - erfcx(|x|)
erfcx_abs_x = DoubleDouble::from_exact_add(erfcx_abs_x.hi, erfcx_abs_x.lo);
let d2x = DoubleDouble::from_exact_mult(x, x);
let expd2x = exp_dd_fast(d2x);
return DoubleDouble::mul_f64_add(expd2x, 2., -erfcx_abs_x).to_f64();
}
erfcx_abs_x.to_f64()
}
#[cfg(test)]
mod tests {
use crate::f_erfcx;
#[test]
fn test_erfcx() {
assert_eq!(f_erfcx(2.2204460492503131e-18), 1.0);
assert_eq!(f_erfcx(-2.2204460492503131e-18), 1.0);
assert_eq!(f_erfcx(-f64::EPSILON), 1.0000000000000002);
assert_eq!(f_erfcx(f64::EPSILON), 0.9999999999999998);
assert_eq!(f_erfcx(-173.), f64::INFINITY);
assert_eq!(f_erfcx(-9.4324165432), 8.718049147018359e38);
assert_eq!(f_erfcx(9.4324165432), 0.059483265496416374);
assert_eq!(f_erfcx(-1.32432512125), 11.200579112797806);
assert_eq!(f_erfcx(1.32432512125), 0.3528722004785406);
assert_eq!(f_erfcx(-0.532431235), 2.0560589406595384);
assert_eq!(f_erfcx(0.532431235), 0.5994337293294584);
assert_eq!(f_erfcx(1e-26), 1.0);
assert_eq!(f_erfcx(-0.500000000023073), 1.952360489253639);
assert_eq!(f_erfcx(-175.), f64::INFINITY);
assert_eq!(f_erfcx(f64::INFINITY), 0.);
assert_eq!(f_erfcx(f64::NEG_INFINITY), f64::INFINITY);
assert!(f_erfcx(f64::NAN).is_nan());
}
}

229
vendor/pxfm/src/err/erfcxf.rs vendored Normal file
View File

@@ -0,0 +1,229 @@
/*
* // Copyright (c) Radzivon Bartoshyk 9/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::f_fmla;
use crate::exponents::core_expdf;
use crate::polyeval::{f_estrin_polyeval8, f_polyeval6};
#[inline]
fn core_erfcx(x: f32) -> f64 {
// x here is already always > 1
let dx = x as f64;
if x < 8. {
// Rational approximant generated by Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=Exp[x^2]Erfc[x]
// {err0,approx,err1}=MiniMaxApproximation[f[z],{z,{1,8},7,7},WorkingPrecision->75,MaxIterations->100]
// num=Numerator[approx];
// den=Denominator[approx];
// coeffs=CoefficientList[num,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// coeffs=CoefficientList[den,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_num = f_estrin_polyeval8(
dx,
f64::from_bits(0x3ff00000804c8f8f),
f64::from_bits(0x3ffb7307ea8fdbeb),
f64::from_bits(0x3ff7081ba7bc735c),
f64::from_bits(0x3fe767338b33532a),
f64::from_bits(0x3fce3c8288507fd6),
f64::from_bits(0x3fa7ca2cb4ae697f),
f64::from_bits(0x3f72b11b0dfb2348),
f64::from_bits(0xbd9f64f0c15c479b),
);
let p_den = f_estrin_polyeval8(
dx,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0x4006c071e850132e),
f64::from_bits(0x400d30326bc347ee),
f64::from_bits(0x40060d8d56bada75),
f64::from_bits(0x3ff56643fc4580eb),
f64::from_bits(0x3fdb0e194e72a513),
f64::from_bits(0x3fb5154759b61be3),
f64::from_bits(0x3f8090b063cce524),
);
return p_num / p_den;
}
// for large x erfcx(x) ~ 1/sqrt(pi) / x * R(1/x)
const ONE_OVER_SQRT_PI: f64 = f64::from_bits(0x3fe20dd750429b6d);
let r = 1. / dx;
// Rational approximant generated by Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=Exp[1/x^2]Erfc[1/x]/x*Sqrt[Pi]
// {err0,approx}=MiniMaxApproximation[f[z],{z,{2^-12,1/8},5,5},WorkingPrecision->75,MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[num,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// coeffs=CoefficientList[den,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_num = f_polyeval6(
r,
f64::from_bits(0x3ff0000000000002),
f64::from_bits(0xbfd09caf2bb541c3),
f64::from_bits(0x40132238367ae454),
f64::from_bits(0xc0060bc62c3711b1),
f64::from_bits(0x40024a90d229158d),
f64::from_bits(0xc0013665d8ff3813),
);
let p_den = f_polyeval6(
r,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0xbfd09caf2bb5101d),
f64::from_bits(0x4015223836772f2c),
f64::from_bits(0xc00715911b5f5f5c),
f64::from_bits(0x4010b66411ec4e1f),
f64::from_bits(0xc00b325c767ed436),
);
(r * ONE_OVER_SQRT_PI) * (p_num / p_den)
}
/// Scaled complementary error function (exp(x^2)*erfc(x))
///
/// ulp 0.5
pub fn f_erfcxf(x: f32) -> f32 {
let ux = x.to_bits().wrapping_shl(1);
if ux >= 0xffu32 << 24 || ux <= 0x6499_999au32 {
// |x| == 0, |x| == inf, |x| == NaN, |x| <= 1.19209290e-08
if ux <= 0x6499_999au32 {
// |x| == 0, |x| <= 1.19209290e-08
return 1.;
}
if x.is_infinite() {
return if x.is_sign_positive() {
0.
} else {
f32::INFINITY
};
}
return f32::NAN; // x == NaN
}
let ax = x.to_bits() & 0x7fff_ffff;
if x <= -9.382415 {
// x <= -9.382415
return f32::INFINITY;
}
if ax <= 0x34000000u32 {
// |x| < ulp(1) we use taylor series at 0
// erfcx(x) ~ 1-(2 x)/Sqrt[\[Pi]]+x^2-(4 x^3)/(3 Sqrt[\[Pi]])+x^4/2-(8 x^5)/(15 Sqrt[\[Pi]])+O[x]^6
#[cfg(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
all(target_arch = "aarch64", target_feature = "neon")
))]
{
use crate::common::f_fmlaf;
const M_TWO_OVER_SQRT_PI: f32 = f32::from_bits(0xbf906ebb);
return f_fmlaf(x, M_TWO_OVER_SQRT_PI, 1.);
}
#[cfg(not(any(
all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "fma"
),
all(target_arch = "aarch64", target_feature = "neon")
)))]
{
use crate::common::f_fmla;
const M_TWO_OVER_SQRT_PI: f64 = f64::from_bits(0xbff20dd750429b6d);
let dx = x as f64;
return f_fmla(dx, M_TWO_OVER_SQRT_PI, 1.) as f32;
}
}
if ax <= 0x3f800000u32 {
// |x| <= 1
let dx = x as f64;
// Generated by Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=Exp[x^2]Erfc[x]
// {err0,approx}=MiniMaxApproximation[f[z],{z,{-1,1},7,7},WorkingPrecision->75,MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[num,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// coeffs=CoefficientList[den,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50},ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_num = f_estrin_polyeval8(
dx,
f64::from_bits(0x3feffffffffffff8),
f64::from_bits(0x3ff26c328bd2dc5f),
f64::from_bits(0x3fe6f91b9fa5f58c),
f64::from_bits(0x3fd09edf3fcf5ee1),
f64::from_bits(0x3faddb3bcedbff91),
f64::from_bits(0x3f7e43b5dd4b7587),
f64::from_bits(0x3f3baab6b3e61d7b),
f64::from_bits(0xbe83e7d629825321),
);
let p_den = f_estrin_polyeval8(
dx,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0x40023d04ee0abc28),
f64::from_bits(0x400252b377263d61),
f64::from_bits(0x3ff510af7f826479),
f64::from_bits(0x3fddfc089c4731ed),
f64::from_bits(0x3fba79b040e28b0a),
f64::from_bits(0x3f8aea2f3579235a),
f64::from_bits(0x3f485d2875b4f88c),
);
return (p_num / p_den) as f32;
}
let erfcx_abs_x = core_erfcx(f32::from_bits(ax));
if x < 0. {
// exp(x^2)erfc(-x) = 2*exp(x^2) - erfcx(|x|)
let dx = x as f64;
return f_fmla(2., core_expdf(dx * dx), -erfcx_abs_x) as f32;
}
erfcx_abs_x as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_erfcx() {
assert_eq!(f_erfcxf(5.19209290e-09), 1.0);
assert_eq!(f_erfcxf(1.19209290e-08), 1.0);
assert_eq!(f_erfcxf(f32::EPSILON), 0.9999999);
assert_eq!(f_erfcxf(12.1), 0.046469606);
assert_eq!(f_erfcxf(7.1), 0.07869752);
assert_eq!(f_erfcxf(1.1), 0.40173045);
assert_eq!(f_erfcxf(-0.23), 1.3232007);
assert_eq!(f_erfcxf(-1.4325), 15.234794);
assert_eq!(f_erfcxf(-10.), f32::INFINITY);
assert_eq!(f_erfcxf(f32::INFINITY), 0.);
assert_eq!(f_erfcxf(f32::NEG_INFINITY), f32::INFINITY);
assert!(f_erfcxf(f32::NAN).is_nan());
}
}

416
vendor/pxfm/src/err/erff.rs vendored Normal file
View File

@@ -0,0 +1,416 @@
/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::f_fmla;
// Polynomials approximating erf(x)/x on ( k/8, (k + 1)/8 ) generated by Sollya
// with:
// > P = fpminimax(erf(x)/x, [|0, 2, 4, 6, 8, 10, 12, 14|], [|D...|],
// [k/8, (k + 1)/8]);
// for k = 0..31.
static COEFFS: [[u64; 8]; 32] = [
[
0x3ff20dd750429b6d,
0xbfd812746b037753,
0x3fbce2f219e8596a,
0xbf9b82cdacb78fda,
0x3f756479297dfda5,
0xbf48b3ac5455ef02,
0xbf7126fcac367e3b,
0x3fb2d0bdb3ba4984,
],
[
0x3ff20dd750429b6d,
0xbfd812746b0379a8,
0x3fbce2f21a03cf2a,
0xbf9b82ce30de083e,
0x3f7565bcad3eb60f,
0xbf4c02c66f659256,
0x3f1f92f673385229,
0xbeedef402648ae90,
],
[
0x3ff20dd750429b34,
0xbfd812746b032dce,
0x3fbce2f219d84aae,
0xbf9b82ce22dcf139,
0x3f7565b9efcd4af1,
0xbf4c021f1af414bc,
0x3f1f7c6d177eff82,
0xbeec9e4410dcf865,
],
[
0x3ff20dd750426eab,
0xbfd812746ae592c7,
0x3fbce2f211525f14,
0xbf9b82ccc125e63f,
0x3f756596f261cfd3,
0xbf4bfde1ff8eeecf,
0x3f1f31a9d15dc5d8,
0xbeea5a4362844b3c,
],
[
0x3ff20dd75039c705,
0xbfd812746777e74d,
0x3fbce2f17af98a1b,
0xbf9b82be4b817cbe,
0x3f7564bec2e2962e,
0xbf4bee86f9da3558,
0x3f1e9443689dc0cc,
0xbee79c0f230805d8,
],
[
0x3ff20dd74f811211,
0xbfd81274371a3e8f,
0x3fbce2ec038262e5,
0xbf9b8265b82c5e1f,
0x3f75615a2e239267,
0xbf4bc63ae023dceb,
0x3f1d87c2102f7e06,
0xbee49584bea41d62,
],
[
0x3ff20dd746d063e3,
0xbfd812729a8a950f,
0x3fbce2cb0a2df232,
0xbf9b80eca1f51278,
0x3f75572e26c46815,
0xbf4b715e5638b65e,
0x3f1bfbb195484968,
0xbee177a565c15c52,
],
[
0x3ff20dd701b44486,
0xbfd812691145f237,
0x3fbce23a06b8cfd9,
0xbf9b7c1dc7245288,
0x3f753e92f7f397dd,
0xbf4ad97cc4acf0b2,
0x3f19f028b2b09b71,
0xbedcdc4da08da8c1,
],
[
0x3ff20dd5715ac332,
0xbfd8123e680bd0eb,
0x3fbce0457aded691,
0xbf9b6f52d52bed40,
0x3f750c291b84414c,
0xbf49ea246b1ad4a9,
0x3f177654674e0ca0,
0xbed737c11a1bcebb,
],
[
0x3ff20dce6593e114,
0xbfd811a59c02eadc,
0x3fbcdab53c7cd7d5,
0xbf9b526d2e321eed,
0x3f74b1d32cd8b994,
0xbf48963143ec0a1e,
0x3f14ad5700e4db91,
0xbed231e100e43ef2,
],
[
0x3ff20db48bfd5a62,
0xbfd80fdd84f9e308,
0x3fbccd340d462983,
0xbf9b196a29287680,
0x3f74210c2c13a0f7,
0xbf46dbdfb4ff71ae,
0x3f11bca2d17fbd71,
0xbecbca36f90c7cf5,
],
[
0x3ff20d64b2f8f508,
0xbfd80b4d4f19fa8b,
0x3fbcb088197262e3,
0xbf9ab51fd02e5b99,
0x3f734e1e5e81a632,
0xbf44c66377b502ce,
0x3f0d9ad25066213c,
0xbec4b0df7dd0cfa1,
],
[
0x3ff20c8fc1243576,
0xbfd8010cb2009e27,
0x3fbc7a47e9299315,
0xbf9a155be5683654,
0x3f7233502694997b,
0xbf426c94b7d81300,
0x3f08094f1de25fb9,
0xbebe0e3d776c6eef,
],
[
0x3ff20a9bd1611bc1,
0xbfd7ec7fbce83f90,
0x3fbc1d757d7317b7,
0xbf992c160cd589f0,
0x3f70d307269cc5c2,
0xbf3fda5b0d2d1879,
0x3f02fdd7b3b14a7f,
0xbeb54eed4a26af5a,
],
[
0x3ff20682834f943d,
0xbfd7c73f747bf5a9,
0x3fbb8c2db4a9ffd1,
0xbf97f0e4ffe989ec,
0x3f6e7061eae4166e,
0xbf3ad36e873fff2d,
0x3efd39222396128e,
0xbead83dacec5ea6b,
],
[
0x3ff1feb8d12676d7,
0xbfd7898347284afe,
0x3fbaba3466b34451,
0xbf9663adc573e2f9,
0x3f6ae99fb17c3e08,
0xbf3602f950ad5535,
0x3ef5e9717490609d,
0xbea3fca107bbc8d5,
],
[
0x3ff1f12fe3c536fa,
0xbfd72b1d1f22e6d3,
0x3fb99fc0eed4a896,
0xbf948db0a87bd8c6,
0x3f673e368895aa61,
0xbf319b35d5301fc8,
0x3ef007987e4bb033,
0xbe9a7edcd4c2dc70,
],
[
0x3ff1db7b0df84d5d,
0xbfd6a4e4a41cde02,
0x3fb83bbded16455d,
0xbf92809b3b36977e,
0x3f639c08bab44679,
0xbf2b7b45a70ed119,
0x3ee6e99b36410e7b,
0xbe913619bb7ebc0c,
],
[
0x3ff1bb1c85c4a527,
0xbfd5f23b99a249a3,
0x3fb694c91fa0d12c,
0xbf9053e1ce11c72d,
0x3f602bf72c50ea78,
0xbf24f478fb56cb02,
0x3ee005f80ecbe213,
0xbe85f2446bde7f5b,
],
[
0x3ff18dec3bd51f9d,
0xbfd5123f58346186,
0x3fb4b8a1ca536ab4,
0xbf8c4243015cc723,
0x3f5a1a8a01d351ef,
0xbf1f466b34f1d86b,
0x3ed5f835eea0bf6a,
0xbe7b83165b939234,
],
[
0x3ff152804c3369f4,
0xbfd4084cd4afd4bc,
0x3fb2ba2e836e47aa,
0xbf8800f2dfc6904b,
0x3f54a6daf0669c59,
0xbf16e326ab872317,
0x3ecd9761a6a755a5,
0xbe70fca33f9dd4b5,
],
[
0x3ff1087ad68356aa,
0xbfd2dbb044707459,
0x3fb0aea8ceaa0384,
0xbf840b516d52b3d2,
0x3f500c9e05f01d22,
0xbf1076afb0dc0ff7,
0x3ec39fadec400657,
0xbe64b5761352e7e3,
],
[
0x3ff0b0a7a8ba4a22,
0xbfd196990d22d4a1,
0x3fad5551e6ac0c4d,
0xbf807cce1770bd1a,
0x3f4890347b8848bf,
0xbf0757ec96750b6a,
0x3eb9b258a1e06bce,
0xbe58fc6d22da7572,
],
[
0x3ff04ce2be70fb47,
0xbfd0449e4b0b9cac,
0x3fa97f7424f4b0e7,
0xbf7ac825439c42f4,
0x3f428f5f65426dfb,
0xbf005b699a90f90f,
0x3eb0a888eecf4593,
0xbe4deace2b32bb31,
],
[
0x3fefbf9fb0e11cc8,
0xbfcde2640856545a,
0x3fa5f5b1f47f8510,
0xbf7588bc71eb41b9,
0x3f3bc6a0a772f56d,
0xbef6b9fad1f1657a,
0x3ea573204ba66504,
0xbe41d38065c94e44,
],
[
0x3feed8f18c99e031,
0xbfcb4cb6acd903b4,
0x3fa2c7f3dddd6fc1,
0xbf713052067df4e0,
0x3f34a5027444082f,
0xbeef672bab0e2554,
0x3e9b83c756348cc9,
0xbe3534f1a1079499,
],
[
0x3fedebd33044166d,
0xbfc8d7cd9053f7d8,
0x3f9ff9957fb3d6e7,
0xbf6b50be55de0f36,
0x3f2e92c8ec53a628,
0xbee5a4b88d508007,
0x3e91a27737559e26,
0xbe2942ae62cb2c14,
],
[
0x3fecfdbf0386f3bd,
0xbfc68e33d93b0dc4,
0x3f9b2683d58f53de,
0xbf65a9174e70d26f,
0x3f269ddd326d49cd,
0xbeddd8f397a8219c,
0x3e86a755016ad4dd,
0xbe1e366e0139187d,
],
[
0x3fec132adb8d7464,
0xbfc475a899f61b46,
0x3f970a431397a77c,
0xbf612e3d35beeee2,
0x3f20c16b05738333,
0xbed4a47f873e144e,
0x3e7d3d494c698c02,
0xbe12302c59547fe5,
],
[
0x3feb2f5fd05555e7,
0xbfc28feefbe03ec7,
0x3f93923acbb3a676,
0xbf5b4ff793cd6358,
0x3f18ea0eb8c913bc,
0xbeccb31ec2baceb1,
0x3e730011e7e80c04,
0xbe0617710635cb1d,
],
[
0x3fea54853cd9593e,
0xbfc0dbdbaea4dc8e,
0x3f90a93e2c20a0fd,
0xbf55c969ff401ea8,
0x3f129e0cc64fe627,
0xbec4160d8e9d3c2a,
0x3e68e7b67594624a,
0xbdfb1cf2c975b09b,
],
[
0x3fe983ceece09ff8,
0xbfbeacc78f7a2d00,
0x3f8c74418410655f,
0xbf51756a050e441e,
0x3f0bff3650f7f548,
0xbebc56c0217d3ada,
0x3e607b4918d0b489,
0xbdf0d4be8c1c50f8,
],
];
/// Error function
///
/// Max ulp 0.5
#[inline]
pub fn f_erff(x: f32) -> f32 {
let x_u = x.to_bits();
let x_abs = x_u & 0x7fff_ffffu32;
if x_abs >= 0x4080_0000u32 {
static ONE: [f32; 2] = [1.0, -1.0];
static SMALL: [f32; 2] = [f32::from_bits(0xb3000000), f32::from_bits(0x33000000)];
let sign = x.is_sign_negative() as usize;
if x_abs >= 0x7f80_0000u32 {
return if x_abs > 0x7f80_0000 { x } else { ONE[sign] };
}
return ONE[sign] + SMALL[sign];
}
// Polynomial approximation:
// erf(x) ~ x * (c0 + c1 * x^2 + c2 * x^4 + ... + c7 * x^14)
let xd = x as f64;
let xsq = xd * xd;
const EIGHT: u32 = 3 << 23;
let idx = unsafe { f32::from_bits(x_abs.wrapping_add(EIGHT)).to_int_unchecked::<usize>() };
let c = COEFFS[idx];
let x4 = xsq * xsq;
let c0 = f_fmla(xsq, f64::from_bits(c[1]), f64::from_bits(c[0]));
let c1 = f_fmla(xsq, f64::from_bits(c[3]), f64::from_bits(c[2]));
let c2 = f_fmla(xsq, f64::from_bits(c[5]), f64::from_bits(c[4]));
let c3 = f_fmla(xsq, f64::from_bits(c[7]), f64::from_bits(c[6]));
let x8 = x4 * x4;
let p0 = f_fmla(x4, c1, c0);
let p1 = f_fmla(x4, c3, c2);
(xd * f_fmla(x8, p1, p0)) as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f_erff_test() {
assert_eq!(f_erff(0.0), 0.0);
assert_eq!(f_erff(1.0), 0.8427008);
assert_eq!(f_erff(0.5), 0.5204999);
assert_eq!(f_erff(f32::INFINITY), 1.0);
assert_eq!(f_erff(f32::NEG_INFINITY), -1.0);
assert!(f_erff(f32::NAN).is_nan());
}
}

345
vendor/pxfm/src/err/erffc.rs vendored Normal file
View File

@@ -0,0 +1,345 @@
/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::{dd_fmla, f_fmla};
use std::hint::black_box;
static ERR0: [u64; 128] = [
0x3ff0000000000000,
0x3ff0163da9fb3335,
0x3ff02c9a3e778061,
0x3ff04315e86e7f85,
0x3ff059b0d3158574,
0x3ff0706b29ddf6de,
0x3ff0874518759bc8,
0x3ff09e3ecac6f383,
0x3ff0b5586cf9890f,
0x3ff0cc922b7247f7,
0x3ff0e3ec32d3d1a2,
0x3ff0fb66affed31b,
0x3ff11301d0125b51,
0x3ff12abdc06c31cc,
0x3ff1429aaea92de0,
0x3ff15a98c8a58e51,
0x3ff172b83c7d517b,
0x3ff18af9388c8dea,
0x3ff1a35beb6fcb75,
0x3ff1bbe084045cd4,
0x3ff1d4873168b9aa,
0x3ff1ed5022fcd91d,
0x3ff2063b88628cd6,
0x3ff21f49917ddc96,
0x3ff2387a6e756238,
0x3ff251ce4fb2a63f,
0x3ff26b4565e27cdd,
0x3ff284dfe1f56381,
0x3ff29e9df51fdee1,
0x3ff2b87fd0dad990,
0x3ff2d285a6e4030b,
0x3ff2ecafa93e2f56,
0x3ff306fe0a31b715,
0x3ff32170fc4cd831,
0x3ff33c08b26416ff,
0x3ff356c55f929ff1,
0x3ff371a7373aa9cb,
0x3ff38cae6d05d866,
0x3ff3a7db34e59ff7,
0x3ff3c32dc313a8e5,
0x3ff3dea64c123422,
0x3ff3fa4504ac801c,
0x3ff4160a21f72e2a,
0x3ff431f5d950a897,
0x3ff44e086061892d,
0x3ff46a41ed1d0057,
0x3ff486a2b5c13cd0,
0x3ff4a32af0d7d3de,
0x3ff4bfdad5362a27,
0x3ff4dcb299fddd0d,
0x3ff4f9b2769d2ca7,
0x3ff516daa2cf6642,
0x3ff5342b569d4f82,
0x3ff551a4ca5d920f,
0x3ff56f4736b527da,
0x3ff58d12d497c7fd,
0x3ff5ab07dd485429,
0x3ff5c9268a5946b7,
0x3ff5e76f15ad2148,
0x3ff605e1b976dc09,
0x3ff6247eb03a5585,
0x3ff6434634ccc320,
0x3ff6623882552225,
0x3ff68155d44ca973,
0x3ff6a09e667f3bcd,
0x3ff6c012750bdabf,
0x3ff6dfb23c651a2f,
0x3ff6ff7df9519484,
0x3ff71f75e8ec5f74,
0x3ff73f9a48a58174,
0x3ff75feb564267c9,
0x3ff780694fde5d3f,
0x3ff7a11473eb0187,
0x3ff7c1ed0130c132,
0x3ff7e2f336cf4e62,
0x3ff80427543e1a12,
0x3ff82589994cce13,
0x3ff8471a4623c7ad,
0x3ff868d99b4492ed,
0x3ff88ac7d98a6699,
0x3ff8ace5422aa0db,
0x3ff8cf3216b5448c,
0x3ff8f1ae99157736,
0x3ff9145b0b91ffc6,
0x3ff93737b0cdc5e5,
0x3ff95a44cbc8520f,
0x3ff97d829fde4e50,
0x3ff9a0f170ca07ba,
0x3ff9c49182a3f090,
0x3ff9e86319e32323,
0x3ffa0c667b5de565,
0x3ffa309bec4a2d33,
0x3ffa5503b23e255d,
0x3ffa799e1330b358,
0x3ffa9e6b5579fdbf,
0x3ffac36bbfd3f37a,
0x3ffae89f995ad3ad,
0x3ffb0e07298db666,
0x3ffb33a2b84f15fb,
0x3ffb59728de5593a,
0x3ffb7f76f2fb5e47,
0x3ffba5b030a1064a,
0x3ffbcc1e904bc1d2,
0x3ffbf2c25bd71e09,
0x3ffc199bdd85529c,
0x3ffc40ab5fffd07a,
0x3ffc67f12e57d14b,
0x3ffc8f6d9406e7b5,
0x3ffcb720dcef9069,
0x3ffcdf0b555dc3fa,
0x3ffd072d4a07897c,
0x3ffd2f87080d89f2,
0x3ffd5818dcfba487,
0x3ffd80e316c98398,
0x3ffda9e603db3285,
0x3ffdd321f301b460,
0x3ffdfc97337b9b5f,
0x3ffe264614f5a129,
0x3ffe502ee78b3ff6,
0x3ffe7a51fbc74c83,
0x3ffea4afa2a490da,
0x3ffecf482d8e67f1,
0x3ffefa1bee615a27,
0x3fff252b376bba97,
0x3fff50765b6e4540,
0x3fff7bfdad9cbe14,
0x3fffa7c1819e90d8,
0x3fffd3c22b8f71f1,
];
static ERFC_COEFFS: [[u64; 16]; 2] = [
[
0x3fec162355429b28,
0x400d99999999999a,
0x3fdda951cece2b85,
0xbff70ef6cff4bcc4,
0x4003d7f7b3d617de,
0xc009d0aa47537c51,
0x4009754ea9a3fcb1,
0xc0027a5453fcc015,
0x3ff1ef2e0531aeba,
0xbfceca090f5a1c06,
0xbfb7a3cd173a063c,
0x3fb30fa68a68fddd,
0x3f555ad9a326993a,
0xbf907e7b0bb39fbf,
0x3f52328706c0e950,
0x3f6d6aa0b7b19cfe,
],
[
0x401137c8983f8516,
0x400799999999999a,
0x3fc05b53aa241333,
0xbfca3f53872bf870,
0x3fbde4c30742c9d5,
0xbfacb24bfa591986,
0x3f9666aec059ca5f,
0xbf7a61250eb26b0b,
0x3f52b28b7924b34d,
0x3f041b13a9d45013,
0xbf16dd5e8a273613,
0x3ef09ce8ea5e8da5,
0x3ed33923b4102981,
0xbec1dfd161e3f984,
0xbe8c87618fcae3b3,
0x3e8e8a6ffa0ba2c7,
],
];
/// Complementary error function
///
/// Max ULP 0.5
pub fn f_erfcf(x: f32) -> f32 {
let ax = f32::from_bits(x.to_bits() & 0x7fff_ffff);
let axd = ax as f64;
let x2 = axd * axd;
let t = x.to_bits();
let at = t & 0x7fff_ffff;
let sgn = t >> 31;
let i: i64 = (at > 0x40051000) as i64;
/* for x < -0x1.ea8f94p+1, erfc(x) rounds to 2 (to nearest) */
if t > 0xc07547cau32 {
// x < -0x1.ea8f94p+1
if t >= 0xff800000u32 {
// -Inf or NaN
if t == 0xff800000u32 {
return 2.0;
} // -Inf
return x + x; // NaN
}
return black_box(2.0) - black_box(f32::from_bits(0x33000000)); // rounds to 2 or nextbelow(2)
}
/* at is the absolute value of x
for x >= 0x1.41bbf8p+3, erfc(x) < 2^-150, thus rounds to 0 or to 2^-149
depending on the rounding mode */
if at >= 0x4120ddfcu32 {
// |x| >= 0x1.41bbf8p+3
if at >= 0x7f800000u32 {
// +Inf or NaN
if at == 0x7f800000u32 {
return 0.0;
} // +Inf
return x + x; // NaN
}
// 0x1p-149f * 0.25f rounds to 0 or 2^-149 depending on rounding
return black_box(f32::from_bits(0x00000001)) * black_box(0.25);
}
if at <= 0x3db80000u32 {
// |x| <= 0x1.7p-4
if t == 0xb76c9f62u32 {
// x = -0x1.d93ec4p-17
return black_box(f32::from_bits(0x3f800085)) + black_box(f32::from_bits(0x33000000)); // exceptional case
}
/* for |x| <= 0x1.c5bf88p-26. erfc(x) rounds to 1 (to nearest) */
if at <= 0x32e2dfc4u32 {
// |x| <= 0x1.c5bf88p-26
if at == 0 {
return 1.0;
}
static D: [f32; 2] = [f32::from_bits(0xb2800000), f32::from_bits(0x33000000)];
return 1.0 + D[sgn as usize];
}
/* around 0, erfc(x) behaves as 1 - (odd polynomial) */
const C: [u64; 5] = [
0x3ff20dd750429b6d,
0xbfd812746b03610b,
0x3fbce2f218831d2f,
0xbf9b82c609607dcb,
0x3f7553af09b8008e,
];
let fw0 = f_fmla(x2, f64::from_bits(C[4]), f64::from_bits(C[3]));
let fw1 = f_fmla(x2, fw0, f64::from_bits(C[2]));
let fw2 = f_fmla(x2, fw1, f64::from_bits(C[1]));
let f0 = x as f64 * f_fmla(x2, fw2, f64::from_bits(C[0]));
return (1.0 - f0) as f32;
}
/* now -0x1.ea8f94p+1 <= x <= 0x1.41bbf8p+3, with |x| > 0x1.7p-4 */
const ILN2: f64 = f64::from_bits(0x3ff71547652b82fe);
const LN2H: f64 = f64::from_bits(0x3f762e42fefa0000);
const LN2L: f64 = f64::from_bits(0x3d0cf79abd6f5dc8);
let jt = dd_fmla(x2, ILN2, -(1024. + f64::from_bits(0x3f70000000000000))).to_bits();
let j: i64 = ((jt << 12) as i64) >> 48;
let sf = ((j >> 7) as u64)
.wrapping_add(0x3ffu64 | (sgn as u64) << 11)
.wrapping_shl(52);
const CH: [u64; 4] = [
0xbfdffffffffff333,
0x3fc5555555556a14,
0xbfa55556666659b4,
0x3f81111074cc7b22,
];
let d = f_fmla(LN2L, j as f64, f_fmla(LN2H, j as f64, x2));
let d2 = d * d;
let e0 = f64::from_bits(ERR0[(j & 127) as usize]);
let fw0 = f_fmla(d, f64::from_bits(CH[3]), f64::from_bits(CH[2]));
let fw1 = f_fmla(d, f64::from_bits(CH[1]), f64::from_bits(CH[0]));
let fw2 = f_fmla(d2, fw0, fw1);
let f = f_fmla(d2, fw2, d);
let ct = ERFC_COEFFS[i as usize];
let z = (axd - f64::from_bits(ct[0])) / (axd + f64::from_bits(ct[1]));
let z2 = z * z;
let z4 = z2 * z2;
let z8 = z4 * z4;
let c = &ct[3..];
let sw0 = f_fmla(z, f64::from_bits(c[1]), f64::from_bits(c[0]));
let sw1 = f_fmla(z, f64::from_bits(c[3]), f64::from_bits(c[2]));
let sw2 = f_fmla(z, f64::from_bits(c[5]), f64::from_bits(c[4]));
let sw3 = f_fmla(z, f64::from_bits(c[7]), f64::from_bits(c[6]));
let zw0 = f_fmla(z2, sw1, sw0);
let zw1 = f_fmla(z2, sw3, sw2);
let sw4 = f_fmla(z, f64::from_bits(c[9]), f64::from_bits(c[8]));
let sw5 = f_fmla(z, f64::from_bits(c[11]), f64::from_bits(c[10]));
let zw2 = f_fmla(z4, zw1, zw0);
let zw3 = f_fmla(z2, sw5, sw4);
let zw4 = f_fmla(z4, f64::from_bits(c[12]), zw3);
let mut s = f_fmla(z8, zw4, zw2);
s = f_fmla(z, s, f64::from_bits(ct[2]));
static OFF: [f64; 2] = [0., 2.];
let r = (f64::from_bits(sf) * f_fmla(-f, e0, e0)) * s;
let y = OFF[sgn as usize] + r;
y as f32
}
#[cfg(test)]
mod tests {
use crate::f_erfcf;
#[test]
fn test_erfc() {
assert_eq!(f_erfcf(0.0), 1.0);
assert_eq!(f_erfcf(0.5), 0.47950011);
assert_eq!(f_erfcf(1.0), 0.1572992);
assert!(f_erfcf(f32::NAN).is_nan());
assert_eq!(f_erfcf(f32::INFINITY), 0.0);
assert_eq!(f_erfcf(f32::NEG_INFINITY), 2.0);
}
}

692
vendor/pxfm/src/err/inverf.rs vendored Normal file
View File

@@ -0,0 +1,692 @@
/*
* // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::logs::fast_log_dd;
use crate::polyeval::{f_polyeval4, f_polyeval5};
#[cold]
fn inverf_0p06_to_0p75(x: f64) -> f64 {
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const P: [(u64, u64); 10] = [
(0xbc3e06eda42202a0, 0x3f93c2fc5d00e0c8),
(0xbc6eb374406b33b4, 0xbfc76fcfd022e3ff),
(0xbc857822d7ffd282, 0x3fe6f8443546010a),
(0x3c68269c66dfb28a, 0xbff80996754ceb79),
(0x3c543dce8990a9f9, 0x3ffcf778d5ef0504),
(0xbc72fc55f73765f6, 0xbff433be821423d0),
(0xbc66d05fb37c8592, 0x3fdf15f19e9d8da4),
(0x3c56dfb85e83a2c5, 0xbfb770b6827e0829),
(0x3bff1472ecdfa403, 0x3f7a98a2980282bb),
(0x3baffb33d69d6276, 0xbf142a246fd2c07c),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let vz = DoubleDouble::full_add_f64(x2, -0.5625);
let vx2 = vz * vz;
let vx4 = vx2 * vx2;
let vx8 = vx4 * vx4;
let p0 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[1]),
DoubleDouble::from_bit_pair(P[0]),
);
let p1 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[3]),
DoubleDouble::from_bit_pair(P[2]),
);
let p2 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[5]),
DoubleDouble::from_bit_pair(P[4]),
);
let p3 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[7]),
DoubleDouble::from_bit_pair(P[6]),
);
let p4 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[9]),
DoubleDouble::from_bit_pair(P[8]),
);
let q0 = DoubleDouble::mul_add(vx2, p1, p0);
let q1 = DoubleDouble::mul_add(vx2, p3, p2);
let r0 = DoubleDouble::mul_add(vx4, q1, q0);
let num = DoubleDouble::mul_add(vx8, p4, r0);
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const Q: [(u64, u64); 10] = [
(0xbc36337f24e57cb9, 0x3f92388d5d757e3a),
(0xbc63dfae43d60e0b, 0xbfc6ca7da581358c),
(0xbc77656389bd0e62, 0x3fe7c82ce417b4e0),
(0xbc93679667bef2f0, 0xbffad58651fd1a51),
(0x3ca2c6cb9eb17fb4, 0x4001bdb67e93a242),
(0xbc9b58961ba253bc, 0xbffbdaeff6fbb81c),
(0x3c7861f549c6aa61, 0x3fe91b12cf47da3a),
(0xbc696dfd665b2f5e, 0xbfc7c5d0ffb7f1da),
(0x3c1552b0ec0ba7b3, 0x3f939ada247f7609),
(0xbbcaa226fb7b30a8, 0xbf41be65038ccfe6),
];
let p0 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[1]),
DoubleDouble::from_bit_pair(Q[0]),
);
let p1 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[3]),
DoubleDouble::from_bit_pair(Q[2]),
);
let p2 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[5]),
DoubleDouble::from_bit_pair(Q[4]),
);
let p3 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[7]),
DoubleDouble::from_bit_pair(Q[6]),
);
let p4 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[9]),
DoubleDouble::from_bit_pair(Q[8]),
);
let q0 = DoubleDouble::mul_add(vx2, p1, p0);
let q1 = DoubleDouble::mul_add(vx2, p3, p2);
let r0 = DoubleDouble::mul_add(vx4, q1, q0);
let den = DoubleDouble::mul_add(vx8, p4, r0);
let r = DoubleDouble::div(num, den);
let k = DoubleDouble::quick_mult_f64(r, x);
k.to_f64()
}
#[inline]
fn inverf_asympt_small(z: DoubleDouble, zeta_sqrt: DoubleDouble, x: f64) -> f64 {
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Exp[-1/(x^2)]*(-1+Exp[1/(x^2)])]/(Sqrt[-Log[1-(Exp[-1/(x^2)]*(-1+Exp[1/(x^2)]))]] )
// {err0, approx,err1}=MiniMaxApproximation[f[z],{z,{0.2,0.9999999},10,10},WorkingPrecision->90]
// num=Numerator[approx];
// den=Denominator[approx];
// poly=num;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const P: [(u64, u64); 11] = [
(0x3c936555853a8b2c, 0x3ff0001df06a2515),
(0x3cea488e802db3c3, 0x404406ba373221da),
(0xbce27d42419754e3, 0x407b0442e38a9597),
(0xbd224a407624cbdf, 0x409c9277e31ef446),
(0x3d4f16ce65d6fea0, 0x40aec3ec005b1d8a),
(0x3d105bc37bc61b58, 0x40b46be8f860f4d9),
(0x3d5ca133dcdecaa0, 0x40b3826e6a32dad7),
(0x3d1d52013ba8aa38, 0x40aae93a603cf3ea),
(0xbd07a75306df0fc3, 0x4098ab8357dc2e51),
(0x3d1bb6770bb7a27e, 0x407ebead00879010),
(0xbbfcbff4a9737936, 0x3f8936117ccbff83),
];
let z2 = DoubleDouble::quick_mult(z, z);
let z4 = DoubleDouble::quick_mult(z2, z2);
let z8 = DoubleDouble::quick_mult(z4, z4);
let q0 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[1]),
z,
DoubleDouble::from_bit_pair(P[0]),
);
let q1 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[3]),
z,
DoubleDouble::from_bit_pair(P[2]),
);
let q2 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[5]),
z,
DoubleDouble::from_bit_pair(P[4]),
);
let q3 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[7]),
z,
DoubleDouble::from_bit_pair(P[6]),
);
let q4 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[9]),
z,
DoubleDouble::from_bit_pair(P[8]),
);
let r0 = DoubleDouble::mul_add(z2, q1, q0);
let r1 = DoubleDouble::mul_add(z2, q3, q2);
let s0 = DoubleDouble::mul_add(z4, r1, r0);
let s1 = DoubleDouble::mul_add(z2, DoubleDouble::from_bit_pair(P[10]), q4);
let num = DoubleDouble::mul_add(z8, s1, s0);
// See numerator generation above:
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const Q: [(u64, u64); 11] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbc75b1109d4a3262, 0x40440782efaab17f),
(0x3d1f7775b207d84f, 0x407b2da74b0d39f2),
(0xbd3291fdbab49501, 0x409dac8d9e7c90b2),
(0xbd58d8fdd27707a9, 0x40b178dfeffa3192),
(0xbd57fc74ad705ce0, 0x40bad19b686f219f),
(0x3d4075510031f2cd, 0x40be70a598208cea),
(0xbd5442e109152efb, 0x40b9683ef36ae330),
(0x3d5398192933962e, 0x40b04b7c4c3ca8ee),
(0x3d2d04d03598e303, 0x409bd0080799fbf1),
(0x3d2a988eb552ef44, 0x40815a46f12bafe3),
];
let q0 = DoubleDouble::mul_add_f64(
DoubleDouble::from_bit_pair(Q[1]),
z,
f64::from_bits(0x3ff0000000000000),
);
let q1 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[3]),
z,
DoubleDouble::from_bit_pair(Q[2]),
);
let q2 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[5]),
z,
DoubleDouble::from_bit_pair(Q[4]),
);
let q3 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[7]),
z,
DoubleDouble::from_bit_pair(Q[6]),
);
let q4 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[9]),
z,
DoubleDouble::from_bit_pair(Q[8]),
);
let r0 = DoubleDouble::mul_add(z2, q1, q0);
let r1 = DoubleDouble::mul_add(z2, q3, q2);
let s0 = DoubleDouble::mul_add(z4, r1, r0);
let s1 = DoubleDouble::mul_add(z2, DoubleDouble::from_bit_pair(Q[10]), q4);
let den = DoubleDouble::mul_add(z8, s1, s0);
let r = DoubleDouble::div(num, den);
let k = DoubleDouble::quick_mult(r, zeta_sqrt);
f64::copysign(k.to_f64(), x)
}
// branch for |x| > 0.9999 for extreme tail
#[cold]
fn inverf_asympt_long(z: DoubleDouble, zeta_sqrt: DoubleDouble, x: f64) -> f64 {
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Exp[-1/(x^2)]*(-1+Exp[1/(x^2)])]/(Sqrt[-Log[1-(Exp[-1/(x^2)]*(-1+Exp[1/(x^2)]))]] )
// {err0, approx}=MiniMaxApproximation[f[z],{z,{0.2,0.9999999},13,13},WorkingPrecision->90]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=num;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const P: [(u64, u64); 14] = [
(0x3c97612f9b24a614, 0x3ff0000ba84cc7a5),
(0xbcee8fe2da463412, 0x40515246546f5d88),
(0x3d2fa4a2b891b526, 0x40956b6837159b11),
(0x3d5d673ffad4f817, 0x40c5a1aa3be58652),
(0x3d8867a1e5506f88, 0x40e65ebb1e1e7c75),
(0xbd9bbc0764ed8f5b, 0x40fd2064a652e5c2),
(0xbda78e569c0d237f, 0x410a385c627c461c),
(0xbdab3123ebc465d7, 0x4110f05ca2b65fe5),
(0x3d960def35955192, 0x4110bb079af2fe08),
(0xbd97904816054836, 0x410911c24610c11c),
(0xbd937745e9192593, 0x40fc603244adca35),
(0xbd65fbc476d63050, 0x40e6399103188c21),
(0xbd61016ef381cce6, 0x40c6482b44995b89),
(0x3c326105c49e5a1a, 0xbfab44bd8b4e3138),
];
let z2 = z * z;
let z4 = z2 * z2;
let z8 = z4 * z4;
let g0 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[1]),
DoubleDouble::from_bit_pair(P[0]),
);
let g1 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[3]),
DoubleDouble::from_bit_pair(P[2]),
);
let g2 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[5]),
DoubleDouble::from_bit_pair(P[4]),
);
let g3 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[7]),
DoubleDouble::from_bit_pair(P[6]),
);
let g4 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[9]),
DoubleDouble::from_bit_pair(P[8]),
);
let g5 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[11]),
DoubleDouble::from_bit_pair(P[10]),
);
let g6 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[13]),
DoubleDouble::from_bit_pair(P[12]),
);
let h0 = DoubleDouble::mul_add(z2, g1, g0);
let h1 = DoubleDouble::mul_add(z2, g3, g2);
let h2 = DoubleDouble::mul_add(z2, g5, g4);
let q0 = DoubleDouble::mul_add(z4, h1, h0);
let q1 = DoubleDouble::mul_add(z4, g6, h2);
let num = DoubleDouble::mul_add(z8, q1, q0);
// See numerator generation above:
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const Q: [(u64, u64); 14] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbcfc7b886ee61417, 0x405152838f711f3c),
(0xbd33f933c14e831a, 0x409576cb78cab36e),
(0x3d33fb09e2c4898a, 0x40c5e8a2c7602ced),
(0x3d7be430c664bf7e, 0x40e766fdc8c7638c),
(0x3dac662e74cdfc0e, 0x4100276b5f47b5f1),
(0x3da67d06e82a8495, 0x410f843887f8a24a),
(0x3dbbf2e22fc2550a, 0x4116d04271703e08),
(0xbdb2fb3aed100853, 0x4119aff4ed32b74b),
(0x3dba75e7b7171c3c, 0x4116b5eb8bf386bd),
(0x3dab2d8b8c1937eb, 0x410f71c38e84cb34),
(0xbda4e2e8a50b7370, 0x4100ca04b0f36b94),
(0xbd86ed6df34fdaf9, 0x40e9151ded4cf4b7),
(0x3d6938ea702c0328, 0x40c923ee1ab270c4),
];
let g0 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[1]),
DoubleDouble::from_bit_pair(Q[0]),
);
let g1 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[3]),
DoubleDouble::from_bit_pair(Q[2]),
);
let g2 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[5]),
DoubleDouble::from_bit_pair(Q[4]),
);
let g3 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[7]),
DoubleDouble::from_bit_pair(Q[6]),
);
let g4 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[9]),
DoubleDouble::from_bit_pair(Q[8]),
);
let g5 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[11]),
DoubleDouble::from_bit_pair(Q[10]),
);
let g6 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[13]),
DoubleDouble::from_bit_pair(Q[12]),
);
let h0 = DoubleDouble::mul_add(z2, g1, g0);
let h1 = DoubleDouble::mul_add(z2, g3, g2);
let h2 = DoubleDouble::mul_add(z2, g5, g4);
let q0 = DoubleDouble::mul_add(z4, h1, h0);
let q1 = DoubleDouble::mul_add(z4, g6, h2);
let den = DoubleDouble::mul_add(z8, q1, q0);
let r = DoubleDouble::div(num, den);
let k = DoubleDouble::quick_mult(r, zeta_sqrt);
f64::copysign(k.to_f64(), x)
}
/// Inverse error function
///
/// ulp 0.5
pub fn f_erfinv(x: f64) -> f64 {
let ax = x.to_bits() & 0x7fff_ffff_ffff_ffff;
if ax >= 0x3ff0000000000000u64 || ax <= 0x3cb0000000000000u64 {
// |x| >= 1, |x| == 0, |x| <= f64::EPSILON
if ax == 0 {
// |x| == 0
return 0.;
}
if ax <= 0x3cb0000000000000u64 {
// |x| <= f64::EPSILON
// inverf(x) ~ Sqrt[Pi]x/2+O[x]^3
const SQRT_PI_OVER_2: f64 = f64::from_bits(0x3fec5bf891b4ef6b);
return x * SQRT_PI_OVER_2;
}
// |x| > 1
if ax == 0x3ff0000000000000u64 {
// |x| == 1
return if x.is_sign_negative() {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
return f64::NAN; // x == NaN, x = Inf, x > 1
}
let z = f64::from_bits(ax);
if ax <= 0x3f8374bc6a7ef9db {
// 0.0095
// for small |x| using taylor series first 3 terms
// Generated by SageMath:
// from mpmath import mp, erf
//
// mp.prec = 100
//
// def inverf_series(n_terms):
// from mpmath import taylor
// series_erf = taylor(mp.erfinv, 0, n_terms)
// return series_erf
//
// ser = inverf_series(10)
// for i in range(1, len(ser), 2):
// k = ser[i]
// print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
let z2 = DoubleDouble::from_exact_mult(z, z);
let p = f_fmla(
z2.hi,
f64::from_bits(0x3fb62847c47dda48),
f64::from_bits(0x3fc053c2c0ab91c5),
);
let mut r = DoubleDouble::mul_f64_add(
z2,
p,
DoubleDouble::from_bit_pair((0xbc33ea2ef8dde075, 0x3fcdb29fb2fee5e4)),
);
r = DoubleDouble::mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b)),
);
// (rh + rl) * z = rh * z + rl*z
let v = DoubleDouble::quick_mult_f64(r, z);
return f64::copysign(v.to_f64(), x);
} else if ax <= 0x3faeb851eb851eb8 {
// 0.06
// for |x| < 0.06 using taylor series first 5 terms
// Generated by SageMath:
// from mpmath import mp, erf
//
// mp.prec = 100
//
// def inverf_series(n_terms):
// from mpmath import taylor
// series_erf = taylor(mp.erfinv, 0, n_terms)
// return series_erf
//
// ser = inverf_series(10)
// for i in range(1, len(ser), 2):
// k = ser[i]
// print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
let z2 = DoubleDouble::from_exact_mult(z, z);
let p = f_polyeval4(
z2.hi,
f64::from_bits(0x3fb62847c47dda48),
f64::from_bits(0x3fb0a13189c6ef7a),
f64::from_bits(0x3faa7c85c89bb08b),
f64::from_bits(0x3fa5eeb1d488e312),
);
let mut r = DoubleDouble::mul_f64_add(
z2,
p,
DoubleDouble::from_bit_pair((0x3c2cec68daff0d80, 0x3fc053c2c0ab91c5)),
);
r = DoubleDouble::mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc33ea2ef8dde075, 0x3fcdb29fb2fee5e4)),
);
r = DoubleDouble::mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b)),
);
// (rh + rl) * z = rh * z + rl*z
let v = DoubleDouble::quick_mult_f64(r, z);
return f64::copysign(v.to_f64(), x);
}
if ax <= 0x3fe8000000000000u64 {
// |x| < 0.75
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const P: [(u64, u64); 5] = [
(0xbc3e06eda42202a0, 0x3f93c2fc5d00e0c8),
(0xbc6eb374406b33b4, 0xbfc76fcfd022e3ff),
(0xbc857822d7ffd282, 0x3fe6f8443546010a),
(0x3c68269c66dfb28a, 0xbff80996754ceb79),
(0x3c543dce8990a9f9, 0x3ffcf778d5ef0504),
];
let x2 = DoubleDouble::from_exact_mult(x, x);
let vz = DoubleDouble::full_add_f64(x2, -0.5625);
let ps_num = f_polyeval5(
vz.hi,
f64::from_bits(0xbff433be821423d0),
f64::from_bits(0x3fdf15f19e9d8da4),
f64::from_bits(0xbfb770b6827e0829),
f64::from_bits(0x3f7a98a2980282bb),
f64::from_bits(0xbf142a246fd2c07c),
);
let mut num = DoubleDouble::mul_f64_add(vz, ps_num, DoubleDouble::from_bit_pair(P[4]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[3]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[2]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[1]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[0]));
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const Q: [(u64, u64); 5] = [
(0xbc36337f24e57cb9, 0x3f92388d5d757e3a),
(0xbc63dfae43d60e0b, 0xbfc6ca7da581358c),
(0xbc77656389bd0e62, 0x3fe7c82ce417b4e0),
(0xbc93679667bef2f0, 0xbffad58651fd1a51),
(0x3ca2c6cb9eb17fb4, 0x4001bdb67e93a242),
];
let ps_den = f_polyeval5(
vz.hi,
f64::from_bits(0xbffbdaeff6fbb81c),
f64::from_bits(0x3fe91b12cf47da3a),
f64::from_bits(0xbfc7c5d0ffb7f1da),
f64::from_bits(0x3f939ada247f7609),
f64::from_bits(0xbf41be65038ccfe6),
);
let mut den = DoubleDouble::mul_f64_add(vz, ps_den, DoubleDouble::from_bit_pair(Q[4]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[3]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[2]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[1]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[0]));
let r = DoubleDouble::div(num, den);
let k = DoubleDouble::quick_mult_f64(r, z);
let err = f_fmla(
k.hi,
f64::from_bits(0x3c70000000000000), // 2^-56
f64::from_bits(0x3c40000000000000), // 2^-59
);
let ub = k.hi + (k.lo + err);
let lb = k.hi + (k.lo - err);
if ub == lb {
return f64::copysign(k.to_f64(), x);
}
return inverf_0p06_to_0p75(x);
}
let q = DoubleDouble::from_full_exact_add(1.0, -z);
let mut zeta = fast_log_dd(q);
zeta = DoubleDouble::from_exact_add(zeta.hi, zeta.lo);
zeta = -zeta;
let zeta_sqrt = zeta.fast_sqrt();
let rz = zeta_sqrt.recip();
if z < 0.9999 {
inverf_asympt_small(rz, zeta_sqrt, x)
} else {
inverf_asympt_long(rz, zeta_sqrt, x)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_erfinv() {
assert!(f_erfinv(f64::NEG_INFINITY).is_nan());
assert!(f_erfinv(f64::INFINITY).is_nan());
assert!(f_erfinv(f64::NAN).is_nan());
assert_eq!(f_erfinv(f64::EPSILON), 1.9678190753608283e-16);
assert_eq!(f_erfinv(-0.5435340000000265), -0.5265673336010599);
assert_eq!(f_erfinv(0.5435340000000265), 0.5265673336010599);
assert_eq!(f_erfinv(0.001000000000084706), 0.0008862271575416209);
assert_eq!(f_erfinv(-0.001000000000084706), -0.0008862271575416209);
assert_eq!(f_erfinv(0.71), 0.7482049711849852);
assert_eq!(f_erfinv(-0.71), -0.7482049711849852);
assert_eq!(f_erfinv(0.41), 0.381014610957532);
assert_eq!(f_erfinv(-0.41), -0.381014610957532);
assert_eq!(f_erfinv(0.32), 0.29165547581744206);
assert_eq!(f_erfinv(-0.32), -0.29165547581744206);
assert_eq!(f_erfinv(0.82), 0.9480569762323499);
assert_eq!(f_erfinv(-0.82), -0.9480569762323499);
assert_eq!(f_erfinv(0.05), 0.044340387910005497);
assert_eq!(f_erfinv(-0.05), -0.044340387910005497);
assert_eq!(f_erfinv(0.99), 1.8213863677184494);
assert_eq!(f_erfinv(-0.99), -1.8213863677184494);
assert_eq!(f_erfinv(0.9900000000867389), 1.8213863698392927);
assert_eq!(f_erfinv(-0.9900000000867389), -1.8213863698392927);
assert_eq!(f_erfinv(0.99999), 3.123413274341571);
assert_eq!(f_erfinv(-0.99999), -3.123413274341571);
}
}

704
vendor/pxfm/src/err/inverfc.rs vendored Normal file
View File

@@ -0,0 +1,704 @@
/*
* // Copyright (c) Radzivon Bartoshyk 9/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::logs::{fast_log_d_to_dd, fast_log_dd};
use crate::polyeval::{f_polyeval4, f_polyeval5};
#[cold]
fn inverf_0p06_to_0p75(x: DoubleDouble) -> DoubleDouble {
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const P: [(u64, u64); 10] = [
(0xbc3e06eda42202a0, 0x3f93c2fc5d00e0c8),
(0xbc6eb374406b33b4, 0xbfc76fcfd022e3ff),
(0xbc857822d7ffd282, 0x3fe6f8443546010a),
(0x3c68269c66dfb28a, 0xbff80996754ceb79),
(0x3c543dce8990a9f9, 0x3ffcf778d5ef0504),
(0xbc72fc55f73765f6, 0xbff433be821423d0),
(0xbc66d05fb37c8592, 0x3fdf15f19e9d8da4),
(0x3c56dfb85e83a2c5, 0xbfb770b6827e0829),
(0x3bff1472ecdfa403, 0x3f7a98a2980282bb),
(0x3baffb33d69d6276, 0xbf142a246fd2c07c),
];
let x2 = DoubleDouble::quick_mult(x, x);
let vz = DoubleDouble::full_add_f64(x2, -0.5625);
let vx2 = vz * vz;
let vx4 = vx2 * vx2;
let vx8 = vx4 * vx4;
let p0 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[1]),
DoubleDouble::from_bit_pair(P[0]),
);
let p1 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[3]),
DoubleDouble::from_bit_pair(P[2]),
);
let p2 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[5]),
DoubleDouble::from_bit_pair(P[4]),
);
let p3 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[7]),
DoubleDouble::from_bit_pair(P[6]),
);
let p4 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(P[9]),
DoubleDouble::from_bit_pair(P[8]),
);
let q0 = DoubleDouble::mul_add(vx2, p1, p0);
let q1 = DoubleDouble::mul_add(vx2, p3, p2);
let r0 = DoubleDouble::mul_add(vx4, q1, q0);
let num = DoubleDouble::mul_add(vx8, p4, r0);
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const Q: [(u64, u64); 10] = [
(0xbc36337f24e57cb9, 0x3f92388d5d757e3a),
(0xbc63dfae43d60e0b, 0xbfc6ca7da581358c),
(0xbc77656389bd0e62, 0x3fe7c82ce417b4e0),
(0xbc93679667bef2f0, 0xbffad58651fd1a51),
(0x3ca2c6cb9eb17fb4, 0x4001bdb67e93a242),
(0xbc9b58961ba253bc, 0xbffbdaeff6fbb81c),
(0x3c7861f549c6aa61, 0x3fe91b12cf47da3a),
(0xbc696dfd665b2f5e, 0xbfc7c5d0ffb7f1da),
(0x3c1552b0ec0ba7b3, 0x3f939ada247f7609),
(0xbbcaa226fb7b30a8, 0xbf41be65038ccfe6),
];
let p0 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[1]),
DoubleDouble::from_bit_pair(Q[0]),
);
let p1 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[3]),
DoubleDouble::from_bit_pair(Q[2]),
);
let p2 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[5]),
DoubleDouble::from_bit_pair(Q[4]),
);
let p3 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[7]),
DoubleDouble::from_bit_pair(Q[6]),
);
let p4 = DoubleDouble::mul_add(
vz,
DoubleDouble::from_bit_pair(Q[9]),
DoubleDouble::from_bit_pair(Q[8]),
);
let q0 = DoubleDouble::mul_add(vx2, p1, p0);
let q1 = DoubleDouble::mul_add(vx2, p3, p2);
let r0 = DoubleDouble::mul_add(vx4, q1, q0);
let den = DoubleDouble::mul_add(vx8, p4, r0);
let r = DoubleDouble::div(num, den);
DoubleDouble::quick_mult(r, x)
}
#[inline]
fn inverf_asympt_small(z: DoubleDouble, zeta_sqrt: DoubleDouble) -> DoubleDouble {
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Exp[-1/(x^2)]*(-1+Exp[1/(x^2)])]/(Sqrt[-Log[1-(Exp[-1/(x^2)]*(-1+Exp[1/(x^2)]))]] )
// {err0, approx,err1}=MiniMaxApproximation[f[z],{z,{0.2,0.9999999},10,10},WorkingPrecision->90]
// num=Numerator[approx];
// den=Denominator[approx];
// poly=num;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const P: [(u64, u64); 11] = [
(0x3c936555853a8b2c, 0x3ff0001df06a2515),
(0x3cea488e802db3c3, 0x404406ba373221da),
(0xbce27d42419754e3, 0x407b0442e38a9597),
(0xbd224a407624cbdf, 0x409c9277e31ef446),
(0x3d4f16ce65d6fea0, 0x40aec3ec005b1d8a),
(0x3d105bc37bc61b58, 0x40b46be8f860f4d9),
(0x3d5ca133dcdecaa0, 0x40b3826e6a32dad7),
(0x3d1d52013ba8aa38, 0x40aae93a603cf3ea),
(0xbd07a75306df0fc3, 0x4098ab8357dc2e51),
(0x3d1bb6770bb7a27e, 0x407ebead00879010),
(0xbbfcbff4a9737936, 0x3f8936117ccbff83),
];
let z2 = DoubleDouble::quick_mult(z, z);
let z4 = DoubleDouble::quick_mult(z2, z2);
let z8 = DoubleDouble::quick_mult(z4, z4);
let q0 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[1]),
z,
DoubleDouble::from_bit_pair(P[0]),
);
let q1 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[3]),
z,
DoubleDouble::from_bit_pair(P[2]),
);
let q2 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[5]),
z,
DoubleDouble::from_bit_pair(P[4]),
);
let q3 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[7]),
z,
DoubleDouble::from_bit_pair(P[6]),
);
let q4 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(P[9]),
z,
DoubleDouble::from_bit_pair(P[8]),
);
let r0 = DoubleDouble::mul_add(z2, q1, q0);
let r1 = DoubleDouble::mul_add(z2, q3, q2);
let s0 = DoubleDouble::mul_add(z4, r1, r0);
let s1 = DoubleDouble::mul_add(z2, DoubleDouble::from_bit_pair(P[10]), q4);
let num = DoubleDouble::mul_add(z8, s1, s0);
// See numerator generation above:
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const Q: [(u64, u64); 11] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbc75b1109d4a3262, 0x40440782efaab17f),
(0x3d1f7775b207d84f, 0x407b2da74b0d39f2),
(0xbd3291fdbab49501, 0x409dac8d9e7c90b2),
(0xbd58d8fdd27707a9, 0x40b178dfeffa3192),
(0xbd57fc74ad705ce0, 0x40bad19b686f219f),
(0x3d4075510031f2cd, 0x40be70a598208cea),
(0xbd5442e109152efb, 0x40b9683ef36ae330),
(0x3d5398192933962e, 0x40b04b7c4c3ca8ee),
(0x3d2d04d03598e303, 0x409bd0080799fbf1),
(0x3d2a988eb552ef44, 0x40815a46f12bafe3),
];
let q0 = DoubleDouble::mul_add_f64(
DoubleDouble::from_bit_pair(Q[1]),
z,
f64::from_bits(0x3ff0000000000000),
);
let q1 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[3]),
z,
DoubleDouble::from_bit_pair(Q[2]),
);
let q2 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[5]),
z,
DoubleDouble::from_bit_pair(Q[4]),
);
let q3 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[7]),
z,
DoubleDouble::from_bit_pair(Q[6]),
);
let q4 = DoubleDouble::mul_add(
DoubleDouble::from_bit_pair(Q[9]),
z,
DoubleDouble::from_bit_pair(Q[8]),
);
let r0 = DoubleDouble::mul_add(z2, q1, q0);
let r1 = DoubleDouble::mul_add(z2, q3, q2);
let s0 = DoubleDouble::mul_add(z4, r1, r0);
let s1 = DoubleDouble::mul_add(z2, DoubleDouble::from_bit_pair(Q[10]), q4);
let den = DoubleDouble::mul_add(z8, s1, s0);
let r = DoubleDouble::div(num, den);
DoubleDouble::quick_mult(r, zeta_sqrt)
}
// branch for |x| > 0.9999 for extreme tail
#[cold]
fn inverf_asympt_long(z: DoubleDouble, zeta_sqrt: DoubleDouble) -> DoubleDouble {
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Exp[-1/(x^2)]*(-1+Exp[1/(x^2)])]/(Sqrt[-Log[1-(Exp[-1/(x^2)]*(-1+Exp[1/(x^2)]))]] )
// {err0, approx}=MiniMaxApproximation[f[z],{z,{0.2,0.9999999},13,13},WorkingPrecision->90]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=num;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const P: [(u64, u64); 14] = [
(0x3c97612f9b24a614, 0x3ff0000ba84cc7a5),
(0xbcee8fe2da463412, 0x40515246546f5d88),
(0x3d2fa4a2b891b526, 0x40956b6837159b11),
(0x3d5d673ffad4f817, 0x40c5a1aa3be58652),
(0x3d8867a1e5506f88, 0x40e65ebb1e1e7c75),
(0xbd9bbc0764ed8f5b, 0x40fd2064a652e5c2),
(0xbda78e569c0d237f, 0x410a385c627c461c),
(0xbdab3123ebc465d7, 0x4110f05ca2b65fe5),
(0x3d960def35955192, 0x4110bb079af2fe08),
(0xbd97904816054836, 0x410911c24610c11c),
(0xbd937745e9192593, 0x40fc603244adca35),
(0xbd65fbc476d63050, 0x40e6399103188c21),
(0xbd61016ef381cce6, 0x40c6482b44995b89),
(0x3c326105c49e5a1a, 0xbfab44bd8b4e3138),
];
let z2 = z * z;
let z4 = z2 * z2;
let z8 = z4 * z4;
let g0 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[1]),
DoubleDouble::from_bit_pair(P[0]),
);
let g1 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[3]),
DoubleDouble::from_bit_pair(P[2]),
);
let g2 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[5]),
DoubleDouble::from_bit_pair(P[4]),
);
let g3 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[7]),
DoubleDouble::from_bit_pair(P[6]),
);
let g4 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[9]),
DoubleDouble::from_bit_pair(P[8]),
);
let g5 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[11]),
DoubleDouble::from_bit_pair(P[10]),
);
let g6 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(P[13]),
DoubleDouble::from_bit_pair(P[12]),
);
let h0 = DoubleDouble::mul_add(z2, g1, g0);
let h1 = DoubleDouble::mul_add(z2, g3, g2);
let h2 = DoubleDouble::mul_add(z2, g5, g4);
let q0 = DoubleDouble::mul_add(z4, h1, h0);
let q1 = DoubleDouble::mul_add(z4, g6, h2);
let num = DoubleDouble::mul_add(z8, q1, q0);
// See numerator generation above:
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
const Q: [(u64, u64); 14] = [
(0x0000000000000000, 0x3ff0000000000000),
(0xbcfc7b886ee61417, 0x405152838f711f3c),
(0xbd33f933c14e831a, 0x409576cb78cab36e),
(0x3d33fb09e2c4898a, 0x40c5e8a2c7602ced),
(0x3d7be430c664bf7e, 0x40e766fdc8c7638c),
(0x3dac662e74cdfc0e, 0x4100276b5f47b5f1),
(0x3da67d06e82a8495, 0x410f843887f8a24a),
(0x3dbbf2e22fc2550a, 0x4116d04271703e08),
(0xbdb2fb3aed100853, 0x4119aff4ed32b74b),
(0x3dba75e7b7171c3c, 0x4116b5eb8bf386bd),
(0x3dab2d8b8c1937eb, 0x410f71c38e84cb34),
(0xbda4e2e8a50b7370, 0x4100ca04b0f36b94),
(0xbd86ed6df34fdaf9, 0x40e9151ded4cf4b7),
(0x3d6938ea702c0328, 0x40c923ee1ab270c4),
];
let g0 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[1]),
DoubleDouble::from_bit_pair(Q[0]),
);
let g1 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[3]),
DoubleDouble::from_bit_pair(Q[2]),
);
let g2 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[5]),
DoubleDouble::from_bit_pair(Q[4]),
);
let g3 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[7]),
DoubleDouble::from_bit_pair(Q[6]),
);
let g4 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[9]),
DoubleDouble::from_bit_pair(Q[8]),
);
let g5 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[11]),
DoubleDouble::from_bit_pair(Q[10]),
);
let g6 = DoubleDouble::mul_add(
z,
DoubleDouble::from_bit_pair(Q[13]),
DoubleDouble::from_bit_pair(Q[12]),
);
let h0 = DoubleDouble::mul_add(z2, g1, g0);
let h1 = DoubleDouble::mul_add(z2, g3, g2);
let h2 = DoubleDouble::mul_add(z2, g5, g4);
let q0 = DoubleDouble::mul_add(z4, h1, h0);
let q1 = DoubleDouble::mul_add(z4, g6, h2);
let den = DoubleDouble::mul_add(z8, q1, q0);
let r = DoubleDouble::div(num, den);
DoubleDouble::quick_mult(r, zeta_sqrt)
}
#[inline]
fn erf_core(x: DoubleDouble) -> DoubleDouble {
// x is always positive, here, should be filtered out before the call
if x.hi <= 0.0095 {
// 0.0095
// for small |x| using taylor series first 3 terms
// Generated by SageMath:
// from mpmath import mp, erf
//
// mp.prec = 100
//
// def inverf_series(n_terms):
// from mpmath import taylor
// series_erf = taylor(mp.erfinv, 0, n_terms)
// return series_erf
//
// ser = inverf_series(10)
// for i in range(1, len(ser), 2):
// k = ser[i]
// print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
let z2 = DoubleDouble::quick_mult(x, x);
let p = f_fmla(
z2.hi,
f64::from_bits(0x3fb62847c47dda48),
f64::from_bits(0x3fc053c2c0ab91c5),
);
let mut r = DoubleDouble::mul_f64_add(
z2,
p,
DoubleDouble::from_bit_pair((0xbc33ea2ef8dde075, 0x3fcdb29fb2fee5e4)),
);
r = DoubleDouble::mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b)),
);
// (rh + rl) * z = rh * z + rl*z
let v = DoubleDouble::quick_mult(r, x);
return v;
} else if x.hi <= 0.06 {
// 0.06
// for |x| < 0.06 using taylor series first 5 terms
// Generated by SageMath:
// from mpmath import mp, erf
//
// mp.prec = 100
//
// def inverf_series(n_terms):
// from mpmath import taylor
// series_erf = taylor(mp.erfinv, 0, n_terms)
// return series_erf
//
// ser = inverf_series(10)
// for i in range(1, len(ser), 2):
// k = ser[i]
// print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
let z2 = DoubleDouble::quick_mult(x, x);
let p = f_polyeval4(
z2.hi,
f64::from_bits(0x3fb62847c47dda48),
f64::from_bits(0x3fb0a13189c6ef7a),
f64::from_bits(0x3faa7c85c89bb08b),
f64::from_bits(0x3fa5eeb1d488e312),
);
let mut r = DoubleDouble::mul_f64_add(
z2,
p,
DoubleDouble::from_bit_pair((0x3c2cec68daff0d80, 0x3fc053c2c0ab91c5)),
);
r = DoubleDouble::mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc33ea2ef8dde075, 0x3fcdb29fb2fee5e4)),
);
r = DoubleDouble::mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b)),
);
// (rh + rl) * z = rh * z + rl*z
let v = DoubleDouble::quick_mult(r, x);
return v;
}
if x.hi <= 0.75 {
// |x| < 0.75
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const P: [(u64, u64); 5] = [
(0xbc3e06eda42202a0, 0x3f93c2fc5d00e0c8),
(0xbc6eb374406b33b4, 0xbfc76fcfd022e3ff),
(0xbc857822d7ffd282, 0x3fe6f8443546010a),
(0x3c68269c66dfb28a, 0xbff80996754ceb79),
(0x3c543dce8990a9f9, 0x3ffcf778d5ef0504),
];
let x2 = DoubleDouble::quick_mult(x, x);
let vz = DoubleDouble::full_add_f64(x2, -0.5625);
let ps_num = f_polyeval5(
vz.hi,
f64::from_bits(0xbff433be821423d0),
f64::from_bits(0x3fdf15f19e9d8da4),
f64::from_bits(0xbfb770b6827e0829),
f64::from_bits(0x3f7a98a2980282bb),
f64::from_bits(0xbf142a246fd2c07c),
);
let mut num = DoubleDouble::mul_f64_add(vz, ps_num, DoubleDouble::from_bit_pair(P[4]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[3]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[2]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[1]));
num = DoubleDouble::mul_add(vz, num, DoubleDouble::from_bit_pair(P[0]));
// Generated in Wolfram Mathematica:
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[x]/x
// g[x_] =f[Sqrt[x]];
// {err0,approx}=MiniMaxApproximation[g[z],{z,{0.06,0.75},9,9},WorkingPrecision->75, MaxIterations->100]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]];
const Q: [(u64, u64); 5] = [
(0xbc36337f24e57cb9, 0x3f92388d5d757e3a),
(0xbc63dfae43d60e0b, 0xbfc6ca7da581358c),
(0xbc77656389bd0e62, 0x3fe7c82ce417b4e0),
(0xbc93679667bef2f0, 0xbffad58651fd1a51),
(0x3ca2c6cb9eb17fb4, 0x4001bdb67e93a242),
];
let ps_den = f_polyeval5(
vz.hi,
f64::from_bits(0xbffbdaeff6fbb81c),
f64::from_bits(0x3fe91b12cf47da3a),
f64::from_bits(0xbfc7c5d0ffb7f1da),
f64::from_bits(0x3f939ada247f7609),
f64::from_bits(0xbf41be65038ccfe6),
);
let mut den = DoubleDouble::mul_f64_add(vz, ps_den, DoubleDouble::from_bit_pair(Q[4]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[3]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[2]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[1]));
den = DoubleDouble::mul_add(vz, den, DoubleDouble::from_bit_pair(Q[0]));
let r = DoubleDouble::div(num, den);
let k = DoubleDouble::quick_mult(r, x);
let err = f_fmla(
k.hi,
f64::from_bits(0x3c70000000000000), // 2^-56
f64::from_bits(0x3c40000000000000), // 2^-59
);
let ub = k.hi + (k.lo + err);
let lb = k.hi + (k.lo - err);
if ub == lb {
return k;
}
return inverf_0p06_to_0p75(x);
}
let q = DoubleDouble::full_add_f64(-x, 1.0);
let mut zeta = fast_log_dd(q);
zeta = DoubleDouble::from_exact_add(zeta.hi, zeta.lo);
zeta = -zeta;
let zeta_sqrt = zeta.fast_sqrt();
let rz = zeta_sqrt.recip();
if x.hi < 0.9999 {
inverf_asympt_small(rz, zeta_sqrt)
} else {
inverf_asympt_long(rz, zeta_sqrt)
}
}
#[cold]
fn inverfc_extra_small(x: f64) -> DoubleDouble {
// Reversed order for erfinv with direct identity without subtraction.
let q = x;
let mut zeta = fast_log_d_to_dd(q);
zeta = DoubleDouble::from_exact_add(zeta.hi, zeta.lo);
zeta = -zeta;
let zeta_sqrt = zeta.fast_sqrt();
let rz = zeta_sqrt.recip();
if x >= 0.0001 {
inverf_asympt_small(rz, zeta_sqrt)
} else {
inverf_asympt_long(rz, zeta_sqrt)
}
}
/// Complementary inverse error function
pub fn f_erfcinv(x: f64) -> f64 {
let ix = x.to_bits();
if ix >= 0x4000000000000000u64 || ix == 0 {
// |x| == NaN, x == inf, |x| == 0, x < 0
if ix.wrapping_shl(1) == 0 {
return f64::INFINITY;
}
if ix == 0x4000000000000000u64 {
return f64::NEG_INFINITY;
}
return f64::NAN; // x == NaN, x == Inf, x > 2
}
if x == 1. {
return 0.;
}
// we compute erfcinv through identity
// erfcinv(x) = -erfinv(1-x)
static SIGN: [f64; 2] = [1.0, -1.0];
if x < 0.1 {
return inverfc_extra_small(x).to_f64();
}
let dx = if x > 1. {
DoubleDouble::from_full_exact_sub(2., x)
} else {
DoubleDouble::new(0., x)
};
let sign = SIGN[(x > 1.) as usize];
let mut dx = DoubleDouble::full_add_f64(-dx, 1.);
dx = DoubleDouble::from_exact_add(dx.hi, dx.lo);
erf_core(dx).to_f64() * sign
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inverfc() {
assert_eq!(f_erfcinv(0.12), 1.0993909519492193);
assert_eq!(f_erfcinv(1.0000000000027623e-13), 5.261512368864527);
assert_eq!(f_erfcinv(1.0001200000182189), -0.00010634724760131264);
assert_eq!(f_erfcinv(0.7001200000182189), 0.2723481758403576);
assert_eq!(f_erfcinv(1.5231200000182189), -0.502985998867995);
assert_eq!(f_erfcinv(1.99545434324323243), -2.0064739778442213);
assert_eq!(f_erfcinv(1.), 0.);
assert!(f_erfcinv(2.05).is_nan());
assert!(f_erfcinv(-0.01).is_nan());
assert!(f_erfcinv(f64::NAN).is_nan());
assert!(f_erfcinv(f64::NEG_INFINITY).is_nan());
assert!(f_erfcinv(f64::INFINITY).is_nan());
}
}

80
vendor/pxfm/src/err/inverfcf.rs vendored Normal file
View File

@@ -0,0 +1,80 @@
/*
* // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::err::inverff::erfinv_core;
/// Complementary inverse error function
///
/// Max ulp 0.5
pub fn f_erfcinvf(x: f32) -> f32 {
let ix = x.to_bits();
let ux = ix.wrapping_shl(1);
if ix >= 0x4000_0000u32 || ux == 0 {
if x.is_infinite() {
return f32::INFINITY;
}
if ux == 0 {
return f32::INFINITY;
}
if ix == 0x3f80_0000u32 {
return 0.;
}
// x > 2
if ix == 0x4000_0000u32 {
// x == 2.
return f32::NEG_INFINITY;
}
return f32::NAN; // x == NaN, x < 0
}
let z = x as f64;
static SIGN: [f32; 2] = [1.0, -1.0];
// inferfc(x) = -inverf(1-x)
// ax doesn't need to be extremely accurate,
// it's just boundary detection so will do subtraction in f32
erfinv_core(1. - z, (1. - x).abs().to_bits(), SIGN[(x > 1.) as usize])
}
#[cfg(test)]
mod tests {
use super::f_erfcinvf;
#[test]
fn m_test() {
assert_eq!(f_erfcinvf(2.), f32::NEG_INFINITY);
assert!(f_erfcinvf(-1.).is_nan());
assert_eq!(f_erfcinvf(0.), f32::INFINITY);
assert!(f_erfcinvf(2.1).is_nan());
assert_eq!(f_erfcinvf(0.5), 0.47693628);
assert_eq!(f_erfcinvf(1.5), -0.47693628);
assert_eq!(f_erfcinvf(0.002), 2.1851242);
assert_eq!(f_erfcinvf(1.002), -0.0017724329);
assert!(f_erfcinvf(f32::NAN).is_nan());
}
}

359
vendor/pxfm/src/err/inverff.rs vendored Normal file
View File

@@ -0,0 +1,359 @@
/*
* // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::logs::simple_fast_log;
use crate::polyeval::{
f_estrin_polyeval7, f_estrin_polyeval8, f_estrin_polyeval9, f_polyeval3, f_polyeval5,
f_polyeval10, f_polyeval11,
};
#[inline]
pub(crate) fn erfinv_core(z: f64, ax: u32, sign: f32) -> f32 {
if ax <= 0x3c1ba5e3u32 {
// 0.0095
// for small |x| using taylor series first 3 terms
let z2 = z * z;
// Generated by SageMath:
// from mpmath import mp, erf
//
// mp.prec = 100
//
// def inverf_series(n_terms):
// from mpmath import taylor
// series_erf = taylor(mp.erfinv, 0, n_terms)
// return series_erf
//
// ser = inverf_series(10)
// for i in range(1, len(ser), 2):
// k = ser[i]
// print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
let p = f_polyeval3(
z2,
f64::from_bits(0x3fec5bf891b4ef6b),
f64::from_bits(0x3fcdb29fb2fee5e4),
f64::from_bits(0x3fc053c2c0ab91c5),
) * z;
return f32::copysign(p as f32, sign);
} else if ax <= 0x3d75c28fu32 {
// 0.06
// for |x| < 0.06 using taylor series first 5 terms
let z2 = z * z;
// Generated by SageMath:
// from mpmath import mp, erf
//
// mp.prec = 100
//
// def inverf_series(n_terms):
// from mpmath import taylor
// series_erf = taylor(mp.erfinv, 0, n_terms)
// return series_erf
//
// ser = inverf_series(10)
// for i in range(1, len(ser), 2):
// k = ser[i]
// print("f64::from_bits(" + double_to_hex(RealField(100)(k)) + "),")
let p = f_polyeval5(
z2,
f64::from_bits(0x3fec5bf891b4ef6b),
f64::from_bits(0x3fcdb29fb2fee5e4),
f64::from_bits(0x3fc053c2c0ab91c5),
f64::from_bits(0x3fb62847c47dda48),
f64::from_bits(0x3fb0a13189c6ef7a),
) * z;
return f32::copysign(p as f32, sign);
}
if ax <= 0x3f400000u32 {
// |x| <= 0.75
let z2 = z * z;
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
//
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Sqrt[x]]/Sqrt[x]
// {err0, approx}=MiniMaxApproximation[f[z],{z,{0.06,0.75},8,7},WorkingPrecision->70]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=num;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let r = z2 - 0.5625;
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,8}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_num = f_estrin_polyeval9(
r,
f64::from_bits(0x3fa329348a73d9d4),
f64::from_bits(0xbfd2cb089b644580),
f64::from_bits(0x3fed229149f732d6),
f64::from_bits(0xbff6a233d2028bff),
f64::from_bits(0x3ff268adbfbb6023),
f64::from_bits(0xbfddac401c7d70f4),
f64::from_bits(0x3fb3b1bd759d5046),
f64::from_bits(0xbf67aeb45bad547e),
f64::from_bits(0xbf01ccc7434d381b),
);
// x0=SetPrecision[0.5625,75];
// NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,7}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_den = f_estrin_polyeval8(
r,
f64::from_bits(0x3fa1aac2ee4b1413),
f64::from_bits(0xbfd279342e281c99),
f64::from_bits(0x3feef89a353c6d1b),
f64::from_bits(0xbffa8f1b7cd6d0a7),
f64::from_bits(0x3ff89ce6289819a1),
f64::from_bits(0xbfe7db5282a4a2e1),
f64::from_bits(0x3fc543f9a928db4a),
f64::from_bits(0xbf888fd2990e88db),
);
let k = (p_num / p_den) * z;
f32::copysign(k as f32, sign)
} else if ax <= 0x3f580000u32 {
// |x| <= 0.84375
let z2 = z * z;
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
//
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Sqrt[x]]/Sqrt[x]
// {err0, approx}=MiniMaxApproximation[f[z],{z,{0.75,0.84375},6,6},WorkingPrecision->70]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// poly=num;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let r = z2 - 0.84375;
// x0=SetPrecision[0.84375,75];
// NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_num = f_polyeval10(
r,
f64::from_bits(0x3f116d07e62cbb74),
f64::from_bits(0xbf5c38d390052412),
f64::from_bits(0x3f92d6f96f84efe3),
f64::from_bits(0xbfbac9189cae446b),
f64::from_bits(0x3fd5dd124fb25677),
f64::from_bits(0xbfe49845d46b80ab),
f64::from_bits(0x3fe556c4913f60f8),
f64::from_bits(0xbfd59e527704e33b),
f64::from_bits(0x3fb07614a5e6c9f1),
f64::from_bits(0xbf60ce54a2d8a789),
);
// x0=SetPrecision[0.84375,75];
// NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_den = f_polyeval10(
r,
f64::from_bits(0x3f09fbdd1c987d1e),
f64::from_bits(0xbf5602ad17d419f4),
f64::from_bits(0x3f8efe31ea5bc71d),
f64::from_bits(0xbfb77e5f1bd26730),
f64::from_bits(0x3fd4c3f03e4f5478),
f64::from_bits(0xbfe5aa87dfc5e757),
f64::from_bits(0x3fe9c6406f9abc0b),
f64::from_bits(0xbfdff2f008b4db05),
f64::from_bits(0x3fc1123be5319800),
f64::from_bits(0xbf83be49c2d5cb9e),
);
let k = (p_num / p_den) * z;
f32::copysign(k as f32, sign)
} else if ax <= 0x3f700000u32 {
// |x| <= 0.9375
// First step rational approximant is generated, but it's ill-conditioned, thus
// we're using taylor expansion to create Newton form at the point.
//
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Sqrt[x]]/Sqrt[x]
// {err0, approx}=MiniMaxApproximation[f[z],{z,{0.84375,0.9375},10,9},WorkingPrecision->70]
// num=Numerator[approx][[1]];
// den=Denominator[approx][[1]];
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let x2 = z * z;
let r = x2 - 0.87890625;
// x0=SetPrecision[0.87890625,75];
// NumberForm[Series[num[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[num[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_num = f_polyeval11(
r,
f64::from_bits(0x3ec70f1cbf8a758b),
f64::from_bits(0xbf1c9dff87b698d0),
f64::from_bits(0x3f5dfe7be00cc21c),
f64::from_bits(0xbf913fd09c5a3682),
f64::from_bits(0x3fb7ab0095693976),
f64::from_bits(0xbfd3b3ca6a3c9919),
f64::from_bits(0x3fe3533be6d1d8c8),
f64::from_bits(0xbfe48208ef308ac7),
f64::from_bits(0x3fd361a82dab69d1),
f64::from_bits(0xbfa2401965a98195),
f64::from_bits(0xbf54ba4d14ca54e3),
);
// x0=SetPrecision[0.87890625,75];
// NumberForm[Series[den[x],{x,x0,50}], ExponentFunction->(Null&)]
// coeffs=Table[SeriesCoefficient[den[x],{x,x0,k}],{k,0,9}];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let p_den = f_polyeval10(
r,
f64::from_bits(0x3ec0699f391e2327),
f64::from_bits(0xbf151ec184941078),
f64::from_bits(0x3f5717bb379a3c6e),
f64::from_bits(0xbf8beed3755c3484),
f64::from_bits(0x3fb46148b4a431ef),
f64::from_bits(0xbfd25690b7bc76fa),
f64::from_bits(0x3fe3f1b2f4ee0d9d),
f64::from_bits(0xbfe888a7a4511975),
f64::from_bits(0x3fdd84db18f2a240),
f64::from_bits(0xbfb844807521be56),
);
let f = z * (p_num / p_den);
f32::copysign(f as f32, sign)
} else {
// Rational approximation generated by Wolfram Mathematica:
// for inverf(x) = sqrt(-log(1-x))*R(1/sqrt(-log(1-x)))
//
// <<FunctionApproximations`
// ClearAll["Global`*"]
// f[x_]:=InverseErf[Exp[-1/(x^2)]*(-1+Exp[1/(x^2)])]/(Sqrt[-Log[1-(Exp[-1/(x^2)]*(-1+Exp[1/(x^2)]))]] )
// {err0, approx,err1}=MiniMaxApproximation[f[z],{z,{0.2,0.9999999},7,6},WorkingPrecision->90]
// num=Numerator[approx];
// den=Denominator[approx];
// poly=num;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
// poly=den;
// coeffs=CoefficientList[poly,z];
// TableForm[Table[Row[{"'",NumberForm[coeffs[[i+1]],{50,50}, ExponentFunction->(Null&)],"',"}],{i,0,Length[coeffs]-1}]]
let zeta = -simple_fast_log(1. - z);
let zeta_sqrt = zeta.sqrt();
let rcp_zeta = (1. / zeta) * zeta_sqrt;
let p_num = f_estrin_polyeval8(
rcp_zeta,
f64::from_bits(0x3ff00072876c578e),
f64::from_bits(0x40314e00c10282da),
f64::from_bits(0x404f4a1412af03f6),
f64::from_bits(0x404c895cc0d9b1b3),
f64::from_bits(0x404545794620bfaf),
f64::from_bits(0x403264d21ea21354),
f64::from_bits(0x3fc5a5141dd19237),
f64::from_bits(0xbf8c2e49707c21ec),
);
let p_den = f_estrin_polyeval7(
rcp_zeta,
f64::from_bits(0x3ff0000000000000),
f64::from_bits(0x403151312c313d77),
f64::from_bits(0x405032345fa3d0cd),
f64::from_bits(0x4053e0a81d4c5f09),
f64::from_bits(0x4054fa20c5e0731c),
f64::from_bits(0x404620d7f94d4804),
f64::from_bits(0x4035d7400867b81f),
);
let r = zeta_sqrt * (p_num / p_den);
f32::copysign(r as f32, sign)
}
}
/// Inverse error function
///
/// Max ulp 0.5
pub fn f_erfinvf(x: f32) -> f32 {
let ax = x.to_bits() & 0x7fff_ffff;
if ax >= 0x3f800000u32 || ax <= 0x3400_0000u32 {
// |x| >= 1, |x| == 0, |x| <= f32::EPSILON
if ax == 0 {
// |x| == 0
return 0.;
}
if ax <= 0x3400_0000u32 {
// |x| <= f32::EPSILON
// inverf(x) ~ Sqrt[Pi]x/2+O[x]^3
const SQRT_PI_OVER_2: f64 = f64::from_bits(0x3fec5bf891b4ef6b);
return (x as f64 * SQRT_PI_OVER_2) as f32;
}
if ax == 0x3f800000u32 {
// |x| == 1
return if x.is_sign_negative() {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
// |x| > 1
return f32::NAN; // |x| == NaN, |x| == Inf, |x| > 1
}
let z = f32::from_bits(ax) as f64;
erfinv_core(z, ax, x)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f_test_inv_erff() {
assert!(f_erfinvf(f32::NEG_INFINITY).is_nan());
assert!(f_erfinvf(f32::INFINITY).is_nan());
assert!(f_erfinvf(-1.1).is_nan());
assert!(f_erfinvf(1.1).is_nan());
assert_eq!(f_erfinvf(f32::EPSILON), 1.05646485e-7);
assert_eq!(f_erfinvf(-1.), f32::NEG_INFINITY);
assert_eq!(f_erfinvf(1.), f32::INFINITY);
assert_eq!(f_erfinvf(0.002), 0.0017724558);
assert_eq!(f_erfinvf(-0.002), -0.0017724558);
assert_eq!(f_erfinvf(0.02), 0.017726395);
assert_eq!(f_erfinvf(-0.02), -0.017726395);
assert_eq!(f_erfinvf(0.05), 0.044340387);
assert_eq!(f_erfinvf(-0.05), -0.044340387);
assert_eq!(f_erfinvf(0.5), 0.47693628);
assert_eq!(f_erfinvf(-0.5), -0.47693628);
assert_eq!(f_erfinvf(0.76), 0.8308411);
assert_eq!(f_erfinvf(-0.76), -0.8308411);
assert_eq!(f_erfinvf(0.92), 1.2379221);
assert_eq!(f_erfinvf(-0.92), -1.2379221);
assert_eq!(f_erfinvf(0.97), 1.5344859);
assert_eq!(f_erfinvf(-0.97), -1.5344859);
assert_eq!(f_erfinvf(0.99), 1.8213866);
assert_eq!(f_erfinvf(-0.99), -1.8213866);
assert_eq!(f_erfinvf(0.7560265), 0.82385886);
}
}

56
vendor/pxfm/src/err/mod.rs vendored Normal file
View File

@@ -0,0 +1,56 @@
/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#![deny(unreachable_pub)]
mod erf;
mod erf_poly;
mod erfc;
mod erfcx;
mod erfcxf;
mod erff;
mod erffc;
mod inverf;
mod inverfc;
mod inverfcf;
mod inverff;
mod rerf;
mod rerf_poly;
mod rerff;
pub use erf::f_erf;
pub use erfc::f_erfc;
pub use erfcx::f_erfcx;
pub use erfcxf::f_erfcxf;
pub use erff::f_erff;
pub use erffc::f_erfcf;
pub use inverf::f_erfinv;
pub use inverfc::f_erfcinv;
pub use inverfcf::f_erfcinvf;
pub use inverff::f_erfinvf;
pub use rerf::f_rerf;
pub use rerff::f_rerff;

233
vendor/pxfm/src/err/rerf.rs vendored Normal file
View File

@@ -0,0 +1,233 @@
/*
* // Copyright (c) Radzivon Bartoshyk 8/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::f_fmla;
use crate::double_double::DoubleDouble;
use crate::err::rerf_poly::RERF_HARD;
use crate::polyeval::f_polyeval7;
#[cold]
#[inline(never)]
fn rerf_poly_tiny_hard(x: f64, z2: DoubleDouble) -> f64 {
// Polynomial for x/erf(x)
// Generated by Sollya.
// d = [0, 1/16];
// f = x/erf(x);
// Q = fpminimax(f, [|0, 2, 4, 6, 8, 10, 12, 14, 16, 18|], [|107...|], d);
// See ./notes/r_erf_tiny_hard.sollya
const C: [(u64, u64); 10] = [
(0xbc8618f13eb7ca89, 0x3fec5bf891b4ef6b),
(0xbc6d7696fe4a7cd0, 0x3fd2e7fb0bcdf4f2),
(0xbc0cb8b926064434, 0x3f842aa561ecc102),
(0x3c1cd94c2f3e6f09, 0xbf75207c7ef80727),
(0xbbb35c4effe3c87c, 0x3f2db4a8d7c32472),
(0x3bbf1d1edd1e109a, 0x3f20faa7a99a4d3d),
(0xbb9e05d21f4e1755, 0xbef3adb84631c39c),
(0x3b6ee5dc31565280, 0xbec366647cacdcc9),
(0x3b3698f8162c5fac, 0x3eaabb9db9f3b048),
(0xbb026f5401fce891, 0xbe66cd40349520b6),
];
let mut p = DoubleDouble::mul_add(
z2,
DoubleDouble::from_bit_pair(C[9]),
DoubleDouble::from_bit_pair(C[8]),
);
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[7]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[6]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[5]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[4]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[3]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[2]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[1]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(C[0]));
p = DoubleDouble::from_exact_add(p.hi, p.lo);
let z = DoubleDouble::div_dd_f64(p, x);
z.to_f64()
}
#[inline]
fn rerf_poly_tiny(z: f64, x: f64) -> f64 {
let z2 = DoubleDouble::from_exact_mult(z, z);
// Polynomial for x/erf(x)
// Generated by Sollya.
// d = [0, 1/16];
// f = x/erf(x);
// Q = fpminimax(f, [|0, 2, 4, 6, 8, 10, 12, 14, 16, 18|], [|107, 107, 107, D...|], d);
// See ./notes/r_erf_tiny.sollya
let p = f_polyeval7(
z2.hi,
f64::from_bits(0xbf75207c7ef80727),
f64::from_bits(0x3f2db4a8d7c36a03),
f64::from_bits(0x3f20faa7a8db7f27),
f64::from_bits(0xbef3adae94983bb2),
f64::from_bits(0xbec3b05fe5c49f32),
f64::from_bits(0x3ed67902690892be),
f64::from_bits(0xbf3090033375e5ee),
);
let mut r = DoubleDouble::quick_mul_f64_add(
z2,
p,
DoubleDouble::from_bit_pair((0xbc0cb29fd910c494, 0x3f842aa561ecc102)),
);
r = DoubleDouble::quick_mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc6d7696ff4f712a, 0x3fd2e7fb0bcdf4f2)),
);
r = DoubleDouble::quick_mul_add(
z2,
r,
DoubleDouble::from_bit_pair((0xbc8618f13eb7ca11, 0x3fec5bf891b4ef6b)),
);
r = DoubleDouble::from_exact_add(r.hi, r.lo);
r = DoubleDouble::div_dd_f64(r, x);
let err = f_fmla(
r.hi,
f64::from_bits(0x3c10000000000000), // 2^-62
f64::from_bits(0x3b90000000000000), // 2^-70
);
let ub = r.hi + (r.lo + err);
let lb = r.hi + (r.lo - err);
if ub == lb {
return r.to_f64();
}
rerf_poly_tiny_hard(x, z2)
}
#[inline]
fn rerf_poly_hard(x: f64, z2: DoubleDouble, idx: usize) -> f64 {
let c = &RERF_HARD[idx];
let mut p = DoubleDouble::mul_add(
z2,
DoubleDouble::from_bit_pair(c[10]),
DoubleDouble::from_bit_pair(c[9]),
);
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[8]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[7]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[6]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[5]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[4]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[3]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[2]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[1]));
p = DoubleDouble::mul_add(z2, p, DoubleDouble::from_bit_pair(c[0]));
p = DoubleDouble::from_exact_add(p.hi, p.lo);
let z = DoubleDouble::div_dd_f64(p, x);
z.to_f64()
}
/// Computes 1/erf(x)
///
/// Max ulp 0.5001
pub fn f_rerf(x: f64) -> f64 {
let z = f64::from_bits(x.to_bits() & 0x7fff_ffff_ffff_ffff);
let t = z.to_bits();
let ux = t;
/* 1/erf(x) rounds to +/-1 for RNDN for |x| > 0x4017afb48dc96626 */
if ux > 0x4017afb48dc96626
// |x| > 0x4017afb48dc96626
{
let os = f64::copysign(1.0, x);
const MASK: u64 = 0x7ff0000000000000u64;
if ux > MASK {
return x + x; /* NaN */
}
if ux == MASK {
return os; /* +/-Inf */
}
return f_fmla(-f64::from_bits(0x3c90000000000000), os, os);
}
/* now |x| <= 0x4017afb48dc96626 */
if z < f64::from_bits(0x3c20000000000000) {
// |x| < 0.0000000000000000004336808689942018
/* for x=-0 the code below returns +0 which is wrong */
if x == 0. {
return if x.is_sign_negative() {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if z.to_bits() <= 0x38b7f12369dedu64 {
return if x.is_sign_negative() {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
/* double-double approximation of 2/sqrt(pi) to nearest */
const SQRT_PI_OVER_2: DoubleDouble = DoubleDouble::new(
f64::from_bits(0xbc8618f13eb7ca89),
f64::from_bits(0x3fec5bf891b4ef6b),
);
/* tiny x is Taylor Series: 1/erf(x) ~ sqrt(pi)/(2 * x) + O(x^3), where the ratio of the O(x^3)
term to the main term is in x^2/3, thus less than 2^-123 */
/* scale x by 2^106 to get out the subnormal range */
let sx = x * f64::from_bits(0x4690000000000000);
let mut prod = DoubleDouble::div_dd_f64(SQRT_PI_OVER_2, sx);
// scale back by 2^106, since we're performed the division
prod = DoubleDouble::quick_mult_f64(prod, f64::from_bits(0x4690000000000000));
return prod.to_f64();
}
if z.to_bits() < 0x3fb0000000000000u64 {
return rerf_poly_tiny(z, x);
}
const SIXTEEN: u64 = 4 << 52;
let idx =
unsafe { f64::from_bits(z.to_bits().wrapping_add(SIXTEEN)).to_int_unchecked::<usize>() };
let z2 = DoubleDouble::from_exact_mult(z, z);
rerf_poly_hard(x, z2, idx)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_erf() {
assert_eq!(f_rerf(65.), 1.0);
assert_eq!(f_rerf(3.), 1.0000220909849995);
assert_eq!(f_rerf(-3.), -1.0000220909849995);
assert_eq!(f_rerf(-0.03723630312089732), -23.811078627277197);
assert_eq!(
f_rerf(0.0000000000000000002336808689942018),
3.7924667486354975e18
);
assert_eq!(f_rerf(2.000225067138672), 1.004695025872889);
assert_eq!(f_rerf(0.), f64::INFINITY);
assert_eq!(f_rerf(-0.), f64::NEG_INFINITY);
assert!(f_rerf(f64::NAN).is_nan());
}
}

1331
vendor/pxfm/src/err/rerf_poly.rs vendored Normal file

File diff suppressed because it is too large Load Diff

468
vendor/pxfm/src/err/rerff.rs vendored Normal file
View File

@@ -0,0 +1,468 @@
/*
* // Copyright (c) Radzivon Bartoshyk 7/2025. All rights reserved.
* //
* // Redistribution and use in source and binary forms, with or without modification,
* // are permitted provided that the following conditions are met:
* //
* // 1. Redistributions of source code must retain the above copyright notice, this
* // list of conditions and the following disclaimer.
* //
* // 2. Redistributions in binary form must reproduce the above copyright notice,
* // this list of conditions and the following disclaimer in the documentation
* // and/or other materials provided with the distribution.
* //
* // 3. Neither the name of the copyright holder nor the names of its
* // contributors may be used to endorse or promote products derived from
* // this software without specific prior written permission.
* //
* // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use crate::common::f_fmla;
// Polynomials approximating x/erf(x) on ( k/8, (k + 1)/8 ) generated by Sollya and SageMath
// ```text
// def build_sollya_script(idx):
// return f"""
// d = [{idx}/8, {idx + 1}/8];
//
// f = x/erf(x);
// Q = fpminimax(f, [|0, 2, 4, 6, 8, 10, 12, 14|], [|D...|], d);
//
// for i from 0 to degree(Q) by 2 do {{
// write(coeff(Q, i)) >> "coefficients.txt";
// write("\\n") >> "coefficients.txt";
// }};
// """
//
// def load_coefficients(filename):
// with open(filename, "r") as f:
// return [RealField(500)(line.strip()) for line in f if line.strip()]
//
// def call_sollya_on_interval(idx):
// sollya_script = build_sollya_script(idx)
// with open("tmp_interval.sollya", "w") as f:
// f.write(sollya_script)
// import subprocess
// if os.path.exists("coefficients.txt"):
// os.remove("coefficients.txt")
// try:
// result = subprocess.run(
// ["sollya", "tmp_interval.sollya"],
// check=True,
// capture_output=True,
// text=True
// )
// except subprocess.CalledProcessError as e:
// return
//
// def print_coeffs(poly):
// print("[")
// for i in range(len(poly)):
// coeff = poly[i]
// print(double_to_hex(coeff), ",")
// print("],")
//
// print(f"static COEFFS: [[u64; 8]; 32] = [")
// for i in range(0, 32):
// call_sollya_on_interval(i)
// coeffs = load_coefficients(f"coefficients.txt")
// print_coeffs(coeffs)
// print("];")
// ```
static COEFFS: [[u64; 8]; 32] = [
[
0x3fec5bf891b4ef6b,
0x3fd2e7fb0bcdee7f,
0x3f842aa5641a200a,
0xbf752081ae81d16e,
0x3f2e1a191fb85592,
0xbf203a20ad500043,
0x3f861a864f719e76,
0xbfc79f68bad20bd1,
],
[
0x3fec5bf891b4ef6b,
0x3fd2e7fb0bcdf45b,
0x3f842aa561f35512,
0xbf75207c8167ac1d,
0x3f2db4b119b4ce20,
0x3f20fa28737c4219,
0xbef38e74cca2219a,
0xbec5d70713fc621e,
],
[
0x3fec5bf891b4ef30,
0x3fd2e7fb0bce1c0f,
0x3f842aa56138541f,
0xbf75207c6197eb7c,
0x3f2db4799120e074,
0x3f20fc28d915a6e9,
0xbef3ea5b479dc053,
0xbebbffe6df8ec372,
],
[
0x3fec5bf891b4bf18,
0x3fd2e7fb0bde166f,
0x3f842aa53c721766,
0xbf7520796733bbec,
0x3f2db21eebf4144f,
0x3f210545cc78d0f0,
0xbef48ad7e4aa2d10,
0xbeb24a043ad31907,
],
[
0x3fec5bf891ab16e9,
0x3fd2e7fb0dc7b919,
0x3f842aa29d8381e7,
0xbf7520592585d601,
0x3f2da30df1566e43,
0x3f212780ff325aa6,
0xbef5e98ea9819e42,
0xbe9849d52099dcb9,
],
[
0x3fec5bf890ddfa8d,
0x3fd2e7fb28aab312,
0x3f842a8a461f0eb7,
0xbf751f93b2d27114,
0x3f2d66789eed5f95,
0x3f21818ff1832f50,
0xbef84264724049ef,
0x3e9df12b02e82a5a,
],
[
0x3fec5bf887f64fa4,
0x3fd2e7fbfcc05f75,
0x3f842a02323e2099,
0xbf751c86d291ced6,
0x3f2cbd5653cde433,
0x3f223299b32b8583,
0xbefb7fc6e286cd94,
0x3eb49676cb3da393,
],
[
0x3fec5bf84f8e2488,
0x3fd2e7ffe83d2974,
0x3f842821c5cc659c,
0xbf7514805a6196e3,
0x3f2b723680f64bb5,
0x3f233416dcfcd366,
0xbefefe55300afaa7,
0x3ebf0c475fb71e7a,
],
[
0x3fec5bf7999e6afe,
0x3fd2e809c6d4caa7,
0x3f84247256be4a56,
0xbf750838db0c0cf5,
0x3f29e7e867267388,
0x3f24226adee5ce74,
0xbf00c0830af2bf01,
0x3ec26fb6b18e628b,
],
[
0x3fec5bf801fc5ad5,
0x3fd2e80618e8941e,
0x3f84254c04b0b234,
0xbf7509d7cf351201,
0x3f2a01829944820c,
0x3f241d7bb0c7e2de,
0xbf00c2d844916d26,
0x3ec2817d39abc26b,
],
[
0x3fec5c0938a12f13,
0x3fd2e7706c510d79,
0x3f8448392db86aae,
0xbf75526e9c6046f0,
0x3f2facd0bc0e7862,
0x3f21fc4093e1e6b7,
0xbefdf54af68ba968,
0x3ebfe348fc246c15,
],
[
0x3fec5c6dcdadc5d8,
0x3fd2e495072afff3,
0x3f84d6f390564d4d,
0xbf764a7e85749c85,
0x3f37effb62caee80,
0x3f19cb39bc236ae6,
0xbef6d7035785e8f3,
0x3eb755aa2e58fc52,
],
[
0x3fec5dd74381acff,
0x3fd2dbe68140f116,
0x3f86459e1acfda0f,
0xbf7865203923a03d,
0x3f43665053a48049,
0x3f0409e353b761ea,
0xbeeb0b00f567c9f8,
0x3eabc33000611b25,
],
[
0x3fec6175431226d1,
0x3fd2c8dcbb0babcc,
0x3f88f5bfd61e5d2e,
0xbf7bc60de8dff620,
0x3f4d9b7076c7767c,
0xbf0106584fac3547,
0xbed0a56cd1030deb,
0x3e970ee11e7beb48,
],
[
0x3fec68445d99a8e9,
0x3fd2a9d608dbfea2,
0x3f8cc072ddf22cb6,
0xbf7fe5f2efdc5f5c,
0x3f5431d1deff38bc,
0xbf197220e4a1dda8,
0x3ec9e2469e6c1c67,
0x3e4be72535d53d7b,
],
[
0x3fec713c415bf088,
0x3fd28610e83aa38c,
0x3f9049ee1942f46b,
0xbf81c513d165d6fd,
0x3f585bc13e0fcaba,
0xbf22715362e30768,
0x3ede6bfa3c69e8e3,
0xbe852cd85f8dea5b,
],
[
0x3fec770e08b47107,
0x3fd2716324b22047,
0x3f91460d403e6b9c,
0xbf829ab46375f10d,
0x3f5a0e7f00c76fb5,
0xbf2484890f2d7eeb,
0x3ee207b21bbd8496,
0xbe8bbee036671b6a,
],
[
0x3fec6f4a2d01088d,
0x3fd288e494bc89b7,
0x3f905203788a2821,
0xbf81eab2727ce365,
0x3f58ddba75a3c100,
0xbf2347c9a317a175,
0x3ee099c93ce5f44f,
0xbe88e9f9c064f833,
],
[
0x3fec4c9bbce50c7d,
0x3fd2e8175b0e1837,
0x3f89a2d1518c7a4c,
0xbf7f3fa91859127e,
0x3f55431c495b1077,
0xbf1fc1af665bb1f8,
0x3eda0f1d735195cb,
0xbe827b8d6fa224ed,
],
[
0x3fec03cce39d7213,
0x3fd39c2316e290bf,
0x3f7b674438899313,
0xbf783644c88c71fb,
0x3f5047a3da485180,
0xbf1748b54f823d57,
0x3ed20c86d3302f22,
0xbe77f94cafe045a8,
],
[
0x3feb913f0adf6c4b,
0x3fd49c4cedae09fc,
0xbf4a6dea9778f474,
0xbf7006dc4e6c8125,
0x3f461483c254fa5f,
0xbf0e75052760bf18,
0x3ec65425869bc096,
0xbe6bc2df9fbc0f82,
],
[
0x3feafbeda3b7d400,
0x3fd5cb900ee1fb5e,
0xbf8228d16e87de3d,
0xbf6011d44e155bf5,
0x3f3993b736442257,
0xbf017c7ee5efa6ad,
0x3eb886e337d2e3c2,
0xbe5cba4b79e90043,
],
[
0x3fea54849d309eba,
0x3fd701afa55e3d21,
0xbf90c72bb2e2799f,
0xbf33c92573294e34,
0x3f265284f7a6d53a,
0xbef09f09298ed1e8,
0x3ea7153a46cb2e27,
0xbe49ef6ec79265fd,
],
[
0x3fe9b128df667870,
0x3fd816d295a867cb,
0xbf9713f11ea84a26,
0x3f4edcbdd63903bb,
0x3ef44f54fc7a6024,
0xbed45da547d2fcb8,
0x3e9049754d57a9a7,
0xbe32aba05ca26a69,
],
[
0x3fe927f49edf4ace,
0x3fd8ecd207c6a7d1,
0xbf9b8cd215124008,
0x3f5cbab209dd389d,
0xbf12a8920ea6230f,
0x3eb442dfce60b0e2,
0x3e52494e415c7728,
0xbe09a1b1bbb9cee4,
],
[
0x3fe8ca3d7437d06f,
0x3fd973c08b6d33fb,
0xbf9e272ca1fccc06,
0x3f61efd00e2016b6,
0xbf1e6dab18e9d45a,
0x3ed0b446e3469be1,
0xbe7503c584488bed,
0x3e069968660290a4,
],
[
0x3fe8a1f4b154f663,
0x3fd9a9a8b81692d4,
0xbf9f1e9312dd4501,
0x3f632b4d20599404,
0xbf2119c1b5e43b24,
0x3ed42b9874284d56,
0xbe7c17cc1eef4b9d,
0x3e117f0a9057a689,
],
[
0x3fe8b15bfcf78f33,
0x3fd99720c884ab33,
0xbf9ed2265979f5a6,
0x3f62d3c30432692b,
0xbf20a17346c37362,
0x3ed36538f2d21c31,
0xbe7aac6bb10f8b90,
0x3e1061d3a1737044,
],
[
0x3fe8f479e98cb825,
0x3fd94ab3f8d0c80c,
0xbf9da7afe85abf94,
0x3f618fe28f71a3d4,
0xbf1df723b2a63e38,
0x3ed0d190252a7f7c,
0xbe7631fdd49272b0,
0x3e0a17567cab4a94,
],
[
0x3fe9636d647b61c0,
0x3fd8d4aaba0e0212,
0xbf9bf904574e56ea,
0x3f5fb68684d8555d,
0xbf19d06f9cf17bbf,
0x3ecb92b9f0b8acf3,
0xbe7145bde0c499ae,
0x3e033cf1cb08ce4c,
],
[
0x3fe9f4c3301b6d33,
0x3fd844100b4598b3,
0xbf9a0b94e19be990,
0x3f5c0ed55c70532f,
0xbf15a786c9e62b23,
0x3ec5e3f05a04f5c6,
0xbe69ea9db2e37883,
0x3dfb3e5ad2cd0fb2,
],
[
0x3fea9f469c75536c,
0x3fd7a51b3d9eda10,
0xbf980f63a2cb486c,
0x3f5887f72a9f07e0,
0xbf11e4d454f2f994,
0x3ec113d0aed8bdef,
0xbe6311f84083acf4,
0x3df2e4dc2e50e3fa,
],
];
/// Computes 1/erf(x)
///
/// Max ulp 0.5
pub fn f_rerff(x: f32) -> f32 {
let x_u = x.to_bits();
let x_abs = x_u & 0x7fff_ffffu32;
if x == 0. {
return if x.is_sign_negative() {
f32::NEG_INFINITY
} else {
f32::INFINITY
};
}
if x_abs >= 0x4080_0000u32 {
static ONE: [f32; 2] = [1.0, -1.0];
static SMALL: [f32; 2] = [f32::from_bits(0xb3000000), f32::from_bits(0x33000000)];
let sign = x.is_sign_negative() as usize;
if x_abs >= 0x7f80_0000u32 {
return if x_abs > 0x7f80_0000 { x } else { ONE[sign] };
}
return ONE[sign] + SMALL[sign];
}
// Polynomial approximation see [COEFFS] for generation:
// 1/erf(x) ~ (c0 + c1 * x^2 + c2 * x^4 + ... + c7 * x^14) / x
let xd = x as f64;
let xsq = xd * xd;
const EIGHT: u32 = 3 << 23;
let idx = unsafe { f32::from_bits(x_abs.wrapping_add(EIGHT)).to_int_unchecked::<usize>() };
let c = COEFFS[idx];
let x4 = xsq * xsq;
let c0 = f_fmla(xsq, f64::from_bits(c[1]), f64::from_bits(c[0]));
let c1 = f_fmla(xsq, f64::from_bits(c[3]), f64::from_bits(c[2]));
let c2 = f_fmla(xsq, f64::from_bits(c[5]), f64::from_bits(c[4]));
let c3 = f_fmla(xsq, f64::from_bits(c[7]), f64::from_bits(c[6]));
let x8 = x4 * x4;
let p0 = f_fmla(x4, c1, c0);
let p1 = f_fmla(x4, c3, c2);
((f_fmla(x8, p1, p0)) / xd) as f32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f_erff_test() {
assert!(f_rerff(f32::NAN).is_nan());
assert_eq!(f_rerff(0.0), f32::INFINITY);
assert_eq!(f_rerff(-0.0), f32::NEG_INFINITY);
assert_eq!(f_rerff(0.015255669), 58.096153);
assert_eq!(f_rerff(1.0), 1.1866608);
assert_eq!(f_rerff(0.5), 1.9212301);
}
}