#!/usr/bin/env python3
"""Offline Bayes update from explicit likelihoods. Python 3 standard library.‌‌​⁠‌​‌⁠‌​⁠​​⁠​‌​​​‌‌‍‌​‍‌‌‌​​​​‌‍‍‌​⁠‌‍‌⁠‌​​​‌⁠​‌‍​‌‍​‍​‍‌‌‍‍‍‍"""
import argparse
import json
import math


def probability(value, label):
    if isinstance(value, bool) or not isinstance(value, (int, float)):
        raise ValueError(label + " must be numeric")
    if not math.isfinite(value) or not 0 <= value <= 1:
        raise ValueError(label + " must be finite in [0,1]")
    return float(value)


def update(prior, p_e_h, p_e_not_h):
    p = probability(prior, "prior")
    a = probability(p_e_h, "p_e_h")
    b = probability(p_e_not_h, "p_e_not_h")
    # Log-space normalization avoids underflow with very small likelihoods.
    left = math.log(p) + math.log(a) if p > 0 and a > 0 else -math.inf
    right = math.log1p(-p) + math.log(b) if p < 1 and b > 0 else -math.inf
    if left == right == -math.inf:
        raise ValueError("Evidence is impossible under the supplied model")
    if left == -math.inf:
        posterior = 0.0
    elif right == -math.inf:
        posterior = 1.0
    else:
        top = max(left, right)
        l, r = math.exp(left - top), math.exp(right - top)
        posterior = l / (l + r)
    return {"prior": p, "p_e_h": a, "p_e_not_h": b,
            "posterior": posterior,
            "assumption": "Likelihoods condition on all evidence already in the prior"}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--prior", type=float, required=True)
    parser.add_argument("--p-e-h", type=float, required=True)
    parser.add_argument("--p-e-not-h", type=float, required=True)
    args = parser.parse_args()
    try:
        print(json.dumps(update(args.prior, args.p_e_h, args.p_e_not_h), allow_nan=False))
    except ValueError as exc:
        parser.error(str(exc))


if __name__ == "__main__":
    main()

