from stats.special import ndtr, gammaincc
#: ndtr at x = -37, -36, ..., 8
X_FIRST: Num = -37.0
#: gammaincc(50, x) at x = 2, 6, ..., 198
GAMMA_A: Num = 50.0
G_FIRST: Num = 2.0
G_STEP: Num = 4.0
#: the 52 fraction bits of a double
MANTISSA_BITS: Num = 52.0
#: an error below half an ulp is drawn at half an ulp, so the log axis keeps it
FLOOR_ULPS: Num = 0.5
#: mpmath 1.3 at 50 digits
NDTR_REF = [
5.7255712225245771e-300, 4.182624065797283e-284, 1.1249107064724062e-268, 1.1138987855743794e-253,
4.0611856209158549e-239, 5.4520806035123956e-225, 2.6952500812005002e-211, 4.9067139271481872e-198,
3.2897852667043802e-185, 8.1238694696594273e-173, 7.3894810068850185e-161, 2.4760633155033892e-149,
3.056696706382561e-138, 1.390392118549703e-127, 2.3306370062206488e-117, 1.439892435145079e-107,
3.2792780189790361e-98, 2.7536241186062337e-89, 8.5272239526309772e-81, 9.7409489189371508e-73,
4.1059962020989065e-65, 6.3887544005380868e-58, 3.6709661993127508e-51, 7.7935368191928e-45,
6.1171643995498803e-39, 1.776482112077679e-33, 1.9106595744986757e-28, 7.6198530241605255e-24,
1.1285884059538405e-19, 6.2209605742717839e-16, 1.279812543885835e-12, 9.8658764503769809e-10,
2.8665157187919391e-07, 3.1671241833119924e-05, 0.0013498980316300946, 0.022750131948179209,
0.15865525393145705, 0.5, 0.84134474606854293, 0.97724986805182079,
0.9986501019683699, 0.99996832875816688, 0.99999971334842808, 0.9999999990134123,
0.99999999999872013, 0.99999999999999933,
]
#: mpmath 1.3 at 50 digits
GAMMAINCC_REF = [
1, 1, 1, 0.99999999999992395,
0.99999999955337127, 0.99999978899055419, 0.99998114193181264, 0.99948110853745198,
0.99402988373533308, 0.96466050177837293, 0.87497595646063264, 0.70334011478380931,
0.48119168452795674, 0.27487846595841797, 0.13078911799345455, 0.052198762557077498,
0.017672963010813177, 0.0051405024585058938, 0.0013007315489960967, 0.00028971073386788583,
5.7412990414952422e-05, 1.0222192761323587e-05, 1.6494985017178249e-06, 2.4312057998027738e-07,
3.2959820415723037e-08, 4.1357707654804985e-09, 4.8302877569944258e-10, 5.2774685848268011e-11,
5.4186357236607356e-12, 5.24990731134568e-13, 4.8175817872982797e-14, 4.2013890297384922e-15,
3.4928719863110788e-16, 2.7760149089469877e-17, 2.1146094546381355e-18, 1.5475166359434589e-19,
1.0903906992356408e-20, 7.4121008573228765e-22, 4.8698645725456705e-23, 3.0977951844508907e-24,
1.9109014283190268e-25, 1.1447586051548684e-26, 6.6692220024328739e-28, 3.7833539091181249e-29,
2.092363506004163e-30, 1.1293788445128826e-31, 5.9557639847527617e-33, 3.0715330056443397e-34,
1.5505694556415278e-35, 7.6686696893997725e-37,
]
#| The error of v against the reference in units in the last place of the
#| reference (a normal double: its spacing is 2^(exponent - 52)).
def ulps(v: Num, ref: Num) -> Num {
spacing = 2.0 ^ (floor(log(abs(ref)) / log(2.0)) - MANTISSA_BITS)
return max(abs(v - ref) / spacing, FLOOR_ULPS)
}
#: ndtr over its grid
x = [X_FIRST + i for i to len(NDTR_REF)]
e_ndtr = [ulps(ndtr(x[i]), NDTR_REF[i]) for i to len(NDTR_REF)]
#: gammaincc(50, x) over its grid
g = [G_FIRST + G_STEP * i for i to len(GAMMAINCC_REF)]
e_gamma = [ulps(gammaincc(GAMMA_A, g[i]), GAMMAINCC_REF[i]) for i to len(GAMMAINCC_REF)]
show max(e_ndtr)
show max(e_gamma)
figure("ndtr(x) against mpmath, 50 digits", "x", "error (ulps)")
plot(x, e_ndtr, "ndtr")
style("ndtr", "noline circle")
yscale("log")
figure("gammaincc(50, x) against mpmath, 50 digits", "x", "error (ulps)")
plot(g, e_gamma, "gammaincc(50, x)")
style("gammaincc(50, x)", "noline circle s2")
yscale("log")
#: the worst errors lib/stats/accuracy.json records over its full sets of points
NDTR_WORST_ULPS: Num = 4.7
GAMMAINCC_WORST_ULPS: Num = 1137.6
test "no worse than the full accuracy table" {
assert_lt(max(e_ndtr), NDTR_WORST_ULPS)
assert_lt(max(e_gamma), GAMMAINCC_WORST_ULPS)
}
# SPDX-License-Identifier: MIT OR Apache-2.0
# SPDX-FileCopyrightText: 2026 The Viv authors