#!/usr/bin/python3

from math import log, exp, sqrt

sigma_factor = 1  # Use 1-sigma, ie. 68th percentile confidence interval, throughout.
    # Sigma factor used throughout only matters when it comes time to compute the mean.
    # Otherwise, so long as all estimates use the same sigma factor, so will the result.

class Uncertain(object):

    # Represents lognormal distributed uncertainty. (Log of variable is normally distributed).

    # Strictly speaking can't be used to represent probabilities, since these are not lognormally distributed, but we do anyway.

    def __init__(self, mu, sigma2):
        self.mu = mu  # Mean of normal distribution.
        self.sigma2 = sigma2  # Variance of normal distribution.

    def __mul__(x, y):
        # x, y independent.
        if not isinstance(y, Uncertain):
            y = uncertain(y, y)
        return Uncertain(x.mu + y.mu, x.sigma2 + y.sigma2)

    __rmul__ = __mul__

    def __truediv__(x, y):
        if not isinstance(y, Uncertain):
            y = uncertain(y, y)
        return x * Uncertain(- y.mu, y.sigma2)

    __rtruediv__ = __truediv__

    def __repr__(self):
        #return 'mu=' + str(self.mu) + ', sigma2=' + str(sqrt(self.sigma2))
        lower = exp(self.mu - sigma_factor * sqrt(self.sigma2))
        upper = exp(self.mu + sigma_factor * sqrt(self.sigma2))
        return 'uncertain(%g, %g)' % (lower, upper)

    def mean(self):
        return exp(self.mu + self.sigma2 / 2)

def uncertain(lower, upper):
    mu = (log(lower) + log(upper)) / 2
    sigma2 = ((log(upper) - log(lower)) / (2 * sigma_factor)) ** 2
    return Uncertain(mu, sigma2)

if __name__ == '__main__':

    population = 8e9

    ai_threat = uncertain(0.6, 0.8) * uncertain(0.5, 0.8) * uncertain(0.8, 1.0) # Shouldn't really assume lognormal for probabilities.
    print('ai_threat', ai_threat)
    friendly_ai_control_cost = 100 * 20 * 100000
    friendly_ai_control_benefit = uncertain(0.1, 0.3) * uncertain(0.3, 0.8) * uncertain(0.2, 0.3) * ai_threat * population * 2e6
    print('friendly_ai_control', friendly_ai_control_cost, friendly_ai_control_benefit, friendly_ai_control_benefit / friendly_ai_control_cost)
    friendly_ai_control_coopt_cost = uncertain(0.2, 0.4) * friendly_ai_control_cost
    print('friendly_ai_control_coopt', friendly_ai_control_coopt_cost, friendly_ai_control_benefit, friendly_ai_control_benefit / friendly_ai_control_coopt_cost)
    influence_ai_cost = uncertain(2000, 5000) * 20000 * 30
    influence_ai_benefit = uncertain(0.1, 0.3) * ai_threat * population * 2e6
    print('influence_ai', influence_ai_cost, influence_ai_benefit, influence_ai_benefit / influence_ai_cost)
    lobby_ai_cost =  uncertain(10e6, 50e6) * 35
    lobby_ai_benefit = uncertain(2, 5) * ai_threat * population * 2e6 / (80 / 2.0)
    print('lobby_ai', lobby_ai_cost, lobby_ai_benefit, lobby_ai_benefit / lobby_ai_cost)

    antibiotics_benefit_lives = uncertain(127000, 192000) * uncertain(0.4, 0.7) * uncertain(2, 3)
    antibiotics_benefit = antibiotics_benefit_lives * uncertain(10, 20) * 2e6 * 0.8
    antibiotics_cost = uncertain(10e6, 50e6) * 5
    print('antibiotics', antibiotics_cost, antibiotics_benefit_lives, antibiotics_benefit, antibiotics_benefit / antibiotics_cost)

    nano_threat = uncertain(0.5, 0.8) * uncertain(0.5, 0.8) * 1.0
    print('nano_threat', nano_threat)
    nano_cost =  uncertain(3e9, 6e9)
    nano_benefit = uncertain(0.5, 0.9) * nano_threat * 7e9 * 90000
    print('nano', nano_cost, nano_benefit, nano_benefit / nano_cost)
