# Path: scratch/probability.py
# def normal_cdf(x: float, mu: float = 0, sigma: float = 1) -> float:
#     return (1 + math.erf((x - mu) / math.sqrt(2) / sigma)) / 2
# 
# Path: scratch/probability.py
# def inverse_normal_cdf(p: float,
#                        mu: float = 0,
#                        sigma: float = 1,
#                        tolerance: float = 0.00001) -> float:
#     """Find approximate inverse using binary search"""
# 
#     # if not standard, compute standard and rescale
#     if mu != 0 or sigma != 1:
#         return mu + sigma * inverse_normal_cdf(p, tolerance=tolerance)
# 
#     low_z = -10.0                      # normal_cdf(-10) is (very close to) 0
#     hi_z  =  10.0                      # normal_cdf(10)  is (very close to) 1
#     while hi_z - low_z > tolerance:
#         mid_z = (low_z + hi_z) / 2     # Consider the midpoint
#         mid_p = normal_cdf(mid_z)      # and the cdf's value there
#         if mid_p < p:
#             low_z = mid_z              # Midpoint too low, search above it
#         else:
#             hi_z = mid_z               # Midpoint too high, search below it
# 
#     return mid_z


from typing import Tuple
from scratch.probability import normal_cdf
from scratch.probability import inverse_normal_cdf
from typing import List
import math
import random

def normal_approximation_to_binomial(n: int, p: float) -> Tuple[float, float]:
    """Returns mu and sigma corresponding to a Binomial(n, p)"""
    mu = p * n
    sigma = math.sqrt(p * (1 - p) * n)
    return mu, sigma


# The normal cdf _is_ the probability the variable is below a threshold

# Path: scratch/probability.py
# def normal_cdf(x: float, mu: float = 0, sigma: float = 1) -> float:
#     return (1 + math.erf((x - mu) / math.sqrt(2) / sigma)) / 2
# 
# Path: scratch/probability.py
# def inverse_normal_cdf(p: float,
#                        mu: float = 0,
#                        sigma: float = 1,
#                        tolerance: float = 0.00001) -> float:
#     """Find approximate inverse using binary search"""
# 
#     # if not standard, compute standard and rescale
#     if mu != 0 or sigma != 1:
#         return mu + sigma * inverse_normal_cdf(p, tolerance=tolerance)
# 
#     low_z = -10.0                      # normal_cdf(-10) is (very close to) 0
#     hi_z  =  10.0                      # normal_cdf(10)  is (very close to) 1
#     while hi_z - low_z > tolerance:
#         mid_z = (low_z + hi_z) / 2     # Consider the midpoint
#         mid_p = normal_cdf(mid_z)      # and the cdf's value there
#         if mid_p < p:
#             low_z = mid_z              # Midpoint too low, search above it
#         else:
#             hi_z = mid_z               # Midpoint too high, search below it
# 
#     return mid_z


# Path: scratch/inference.py
from typing import Tuple
from scratch.probability import normal_cdf
from scratch.probability import inverse_normal_cdf
from typing import List
import math
import random


def normal_approximation_to_binomial(n: int, p: float) -> Tuple[float, float]:
    """Returns mu and sigma corresponding to a Binomial(n, p)"""
    mu = p * n
    sigma = math.sqrt(p * (1 - p) * n)
    return mu, sigma


# The normal cdf _is_ the probability the variable is below a threshold

normal_probability_below = normal_cdf