#!/usr/bin/env python

"""
Script to measure DecoTengu overall performance for different
configurations and few diving scenarios.

The script report times in milliseconds.
"""

import argparse
from decimal import Decimal
import itertools
import logging
from pprint import pprint
import time

logging.basicConfig(level=logging.ERROR)

import decotengu
from decotengu.alt.naive import DecoStopStepper
from decotengu.alt.tab import tab_engine
from decotengu.alt.bisect import BisectFindFirstStop
from decotengu.alt.decimal import DecimalContext

COUNT = 5 * 10 ** 1

parser = argparse.ArgumentParser(description='DecoTengu performance script')
parser.add_argument('iter', type=int, default=COUNT, help='number of iterations')
args = parser.parse_args()


def create_engine(type=float, stepper=False):
    engine = decotengu.create(validate=False)
    if stepper:
        engine._deco_stop = DecoStopStepper(engine)
    engine.ascent_rate = type(10)
    engine.descent_rate = type(10)
    engine.model.gf_low = type(0.3)
    engine.model.gf_high = type(0.85)
    return engine


def dive_shallow(type=float, stepper=False):
    """
    Shallow dive profile on Air. No gas mix switches.
    """
    engine = create_engine(type=type, stepper=stepper)
    engine.add_gas(type(0), type(21), type(0))
    return engine, type(17), type(90)


def dive_u260(type=float, stepper=False):
    """
    Nitrox dive with one gas switch.

    The gas mix switch is performed at 22m, before first decompression stop
    at 18m.
    """
    engine = create_engine(type=type, stepper=stepper)
    engine.add_gas(type(0), type(27), type(0))
    engine.add_gas(type(22), type(50), type(0))
    return engine, type(45), type(25)


def dive_he(type=float, stepper=False):
    """
    Trimix dive with two gas mix switches.
    """
    engine = create_engine(type=type, stepper=stepper)
    engine.add_gas(type(0), type(18), type(45))
    engine.add_gas(type(22), type(50), type(0))
    engine.add_gas(type(6), type(100), type(0))

    return engine, type(68), type(20)


def dive_deepstop(type=float, stepper=False):
    """
    Trimix dive with three gas mix switches.

    This is dive profile presented in Baker "Deep Stops" paper.

    See figure 3, page 7 of the paper for the dive profile and
    decompression stops information.

    The first gas mix switch is performed at 33m, after first deco stop at
    57m.
    """
    engine = create_engine(type=type, stepper=stepper)

    engine.model.gf_low = type(0.2)
    engine.model.gf_high = type(0.75)
    engine.add_gas(type(0), type(13), type(50))
    engine.add_gas(type(33), type(36), type(0))
    engine.add_gas(type(21), type(50), type(0))
    engine.add_gas(type(9), type(80), type(0))

    return engine, type(90), type(20)


def run(engine, depth, t):
    t1 = time.clock()
    for i in range(args.iter):
        data = engine.calculate(depth, t, descent=False)
        tuple(data)
    t2 = time.clock()
    return t2 - t1
    print('{}: {:.2f}'.format(name, t2 - t1))


def print_result(engine, name, t):
    print('{},{},{:.2f}'.format(engine, name, t))


names = (
    'Standard', 'Standard + Stepper', 'Standard + Bisect', 'Tabular',
    'Tabular + Stepper', 'Tabular + Decimal',
)
scenarios = tuple('Scenario {}'.format(i) for i in range(1, 5))
dives = dive_shallow, dive_u260, dive_he, dive_deepstop

results = {}
for n, s in itertools.product(names, scenarios):
    results[n] = {}
    results[n][s] = {}

for scenario, dive in zip(scenarios, dives):
    engine, depth, t = dive()
    rt = run(engine, depth, t)
    results['Standard'][scenario] = rt

    engine, depth, t = dive(stepper=True)
    rt = run(engine, depth, t)
    results['Standard + Stepper'][scenario] = rt

    engine, depth, t = dive()
    engine._find_first_stop = BisectFindFirstStop(engine)
    rt = run(engine, depth, t)
    results['Standard + Bisect'][scenario] = rt

    engine, depth, t = dive()
    tab_engine(engine)
    rt = run(engine, depth, t)
    results['Tabular'][scenario] = rt

    engine, depth, t = dive(stepper=True)
    tab_engine(engine)
    rt = run(engine, depth, t)
    results['Tabular + Stepper'][scenario] = rt

    with DecimalContext() as ctx:
        engine, depth, t = dive(type=Decimal)
        tab_engine(engine)
        rt = run(engine, depth, t)
        results['Tabular + Decimal'][scenario] = rt

s = ''.join('{:>12}'.format(s) for s in scenarios)
print(' ' * 20 + s)

for n in names:
    t = '{:>20}'.format(n)
    s = ''.join('{:>12.1f}'.format(results[n][s] / args.iter * 1000) for s in scenarios)
    print(t + s)

# vim: sw=4:et:ai
