"""Two independent educational payout distributions, not provider measurements.

Initial cash1000, fixed stake10, at most1000 wagers. Fully fund each wager;
stop below10 after settlement, including the last allowed wager. No top-ups,
fees, bonus balances, minimum changes or profit stops. Terminal distribution
includes every absorbed balance. No peak drawdown is calculated.
"""
from fractions import Fraction
import json
import logging
import math


def terminal_model(kind, rounds=1000, initial=1000, exact=False):
    if kind not in ('crash150', 'toy_slot'):
        raise ValueError('Unknown educational model')
    if not isinstance(rounds, int) or rounds < 0:
        raise ValueError('Nonnegative integer horizon required')
    if not isinstance(initial, int) or initial < 10 or initial % 10:
        raise ValueError('Initial cash must be a positive multiple of10')
    quantum = 5 if kind == 'crash150' else 10
    outcomes = ((-2, Fraction(53,150)), (1, Fraction(97,150))) if kind == 'crash150' else (
        (-1,Fraction(72,100)), (0,Fraction(20,100)),
        (1,Fraction(6,100)), (31,Fraction(2,100)))
    if not exact:
        outcomes = [(step,float(p)) for step,p in outcomes]
    zero = Fraction(0) if exact else 0.0
    active = {initial // quantum:1-zero}
    stopped = {}
    expected_wagers = zero
    minimum = 10 // quantum
    for _ in range(rounds):
        expected_wagers += sum(active.values(),zero)
        following = {}
        for balance,mass in active.items():
            for step,p in outcomes:
                after = balance + step
                dest = stopped if after < minimum else following
                dest[after] = dest.get(after,zero) + mass*p
        active = following
    terminal = dict(active)
    for balance,mass in stopped.items():
        terminal[balance] = terminal.get(balance,zero)+mass
    total = sum(terminal.values(),zero)
    assert total == 1 if exact else abs(total-1) < 1e-11
    mean = sum((balance*quantum)*mass for balance,mass in terminal.items())
    variance = sum((balance*quantum-mean)**2*mass for balance,mass in terminal.items())
    failure = sum(stopped.values(),zero)
    return dict(expectedWagers=expected_wagers,expectedTurnover=10*expected_wagers,
                meanFinal=mean,varianceFinal=variance,standardDeviationFinal=math.sqrt(float(variance)),
                failure=failure,zeroBalance=stopped.get(0,zero))


if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO,format='%(message)s')
    logging.info(json.dumps({kind:terminal_model(kind) for kind in ('crash150','toy_slot')},indent=2))
