1"""Statistical utilities for loop evaluation reporting.

2

3 Provides Wilson 95% binomial confidence intervals for honest uncertainty

4 reporting at small sample sizes where naive ±√(p(1-p)/n) estimates are

5 unreliable near 0 or 1.

6"""

7

8 from __future__ import annotations

9

10 import math

11

12

13 def wilson_ci(k: int, n: int, z: float = 1.96) -> tuple[float, float]:

14 """Compute Wilson binomial confidence interval.

15

16 Formula: (p + z²/2n ± z√(p(1-p)/n + z²/4n²)) / (1 + z²/n)

17

18 Args:

19 k: Number of successes (0 <= k <= n).

20 n: Total trials (n > 0).

21 z: Z-score for confidence level (default 1.96 for 95% CI).

22

23 Returns:

24 (lower, upper) bounds as floats clamped to [0, 1].

25

26 Raises:

27 ValueError: If n <= 0, k < 0, or k > n.

28 """

29 if n <= 0:

30 raise ValueError(f"n must be positive, got {n}")

31 if k < 0 or k > n:

32 raise ValueError(f"k must be in [0, n], got k={k}, n={n}")

33

34 p = k / n

35 z2 = z * z

36 denominator = 1.0 + z2 / n

37 center = (p + z2 / (2.0 * n)) / denominator

38 margin = (z * math.sqrt(p * (1.0 - p) / n + z2 / (4.0 * n * n))) / denominator

39 return max(0.0, center - margin), min(1.0, center + margin)