__title__ = "summary document of the model's configuration and outputs"
__author__ = "Manon E. B. Sabot"
__version__ = "1.0 (10.02.2018)"
__email__ = "m.e.b.sabot@gmail.com"

#=======================================================================

import warnings  # ignore these warnings
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=UserWarning)
warnings.filterwarnings("ignore", category=RuntimeWarning)

# import general modules
import argparse  # read in the user input
import os  # check for files, paths
import sys  # check for files, paths
import numpy as np  # array manipulations, math operators
import numpy.ma as ma  # masked arrays
import pandas as pd  # read/write dataframes, csv files
import xarray as xr  # read netcdf
from scipy.stats import percentileofscore as psc  # compute p-ranks
from geopy.geocoders import Nominatim  # match coordinates with country
import docx  # write out a word document
from docx import Document  # write out a word document

# first make sure that modules can be loaded from TractLSM
script_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
sys.path.append(os.path.abspath(os.path.join(script_dir, '..')))

# own modules
from TractLSM import conv  # unit converter
from TractLSM import dparams as dp  # model parameters
from TractLSM.Utils import get_main_dir, read_csv  # locate & read

#=======================================================================

def main(projects):

    """
    Main function: writes a word document with information on:
                   (i) parameters and site-level input data used for the runs
                   (ii) general analysis about the output generated

    Arguments:
    ----------
    projects: array
        names of the repository which contain the output to analyse

    Returns:
    --------
    'summary_predicting_kmax.docx' in the output directory

    """

    write_word_doc(os.path.join('output', 'summary_predicting_kmax.docx'),
                   projects)

    return

#=======================================================================

# ~~~ Other functions are defined here ~~~

def reverse_geocode(lat, lon):

    """
    Finds the country associated with specific geographical coordinates.

    """

    location = Nominatim(user_agent="TractLSM").reverse('%s, %s' % (lat, lon))

    return location.address.rsplit(' ', 1)[1]

def kmax_values(sites, project, cLAI=None):

    """
    Retrieves the site values of kmax present within a specific project.

    Arguments:
    ----------
    sites: array
        names of the sites

    project: string
        name of the repository which contains the kmax data

    cLAI: array
        composite LAI to express kmax per area rather than per LAI

    Returns:
    --------
    kmax: array
        maximum hydraulic conductance [mmol m-2 s-1 MPa-1]

    """

    basedir = get_main_dir()

    while 'src' in basedir:
        basedir = os.path.dirname(get_main_dir())

    iname = os.path.join(os.path.join(os.path.join(basedir, 'input'),
                                      'projects'), project)

    kmax = np.zeros((len(sites), 3))
    confs = ['H', '', 'L']

    for i in range(3):

        for j in range(len(sites)):

            for file in os.listdir(iname):

                    if file.endswith('.csv') and ('actual' in file) and \
                       ('%s%s_' % (sites[j], confs[i]) in file):
                        df, __ = read_csv(os.path.join(iname, file))

                        if cLAI is None:
                            kmax[j, i] = df['kmax'].values[0]

                        else:
                            kmax[j, i] = df['kmax'].values[0] * cLAI[j]

    return kmax

def get_scores(df, sites, projects, kmax=None, climate=None, years=None):

    """
    Retrieves each of the yearly metrics and quantile ranks for all the
    sites and fluxes and projects.

    Arguments:
    ----------
    df: pandas dataframe
        dataframe containing all the performance scores

    sites: array
        site names

    projects: array
        names of the repository which contain the kmax data

    kmax: array
        kmax numbers for the calibration

    climate: array
        which is the best climate for each site

    years: array
        years to select the values from

    Returns:
    --------
    NMSE: array
        normalised mean square errors for each of the variables

    MAE: array
        mean absolute error

    SD: array
        standard deviation

    P5: array
        5th percentile

    P95: array
        95th percentile

    ranks: array
        quantile ranks

    """

    confs = ['', 'H', 'L']  # in opt-high-low order to calculate ranks
    projects = projects + ['control']

    # empty arrays to store the performance scores in
    NMSE = np.zeros((len(sites), len(projects), len(confs), 2, 2, 2))
    MAE = np.zeros((len(sites), len(projects), len(confs), 2, 2, 2))
    SD = np.zeros((len(sites), len(projects), len(confs), 2, 2, 2))
    P5 = np.zeros((len(sites), len(projects), len(confs), 2, 2, 2))
    P95 = np.zeros((len(sites), len(projects), len(confs), 2, 2, 2))

    # loop over ET and An
    for i in range(2):

        # loop over configurations
        for j in range(len(confs)):

            # loop over sites
            for k in range(len(sites)):

                for l in range(len(projects)):

                    subset = df[df['fname'].str.contains('%s%s_' % (sites[k],
                                                                    confs[j]))]

                    if 'sample' in projects[l]:
                        if kmax is not None:
                            subset = (df[df['fname'].str
                                      .contains('%s%s_' % (sites[k],
                                                           kmax[k]))])
                    if 'adjust' in projects[l]:
                        if climate is not None:
                            check = (subset['project']
                                     .str.contains(climate[k].lower()))

                            if any(check):
                                subset = subset[check]

                    sub = subset[subset['project'].str.contains(projects[l])]

                    for m in range(len(sub)):

                        ssub = sub.iloc[m]

                        if years is not None:
                            if any(str(yr) in ssub['fname'] for yr in years):
                                n = 0  # drought or not?

                            else:
                                n = 1

                        else:
                            n = 0

                        if i == 0:  # An
                            if np.isclose(NMSE[k, l, j, i, n, 0], 0.):
                                o = 0  # years

                            else:
                                o = 1

                            NMSE[k, l, j, i, n, o] = ssub['NMSE(An)']
                            MAE[k, l, j, i, n, o] = ssub['MAE(An)']
                            SD[k, l, j, i, n, o] = ssub['SD(An)']
                            P5[k, l, j, i, n, o] = ssub['P5(An)']
                            P95[k, l, j, i, n, o] = ssub['P95(An)']

                        else:  # ET
                            if np.isclose(NMSE[k, l, j, i, n, 0], 0.):
                                o = 0

                            else:
                                o = 1

                            NMSE[k, l, j, i, n, o] = ssub['NMSE(ET)']
                            MAE[k, l, j, i, n, o] = ssub['MAE(ET)']
                            SD[k, l, j, i, n, o] = ssub['SD(ET)']
                            P5[k, l, j, i, n, o] = ssub['P5(ET)']
                            P95[k, l, j, i, n, o] = ssub['P95(ET)']

    # deal with configs that don't apply (e.g. 'high' & 'low' to sample)
    for metric in [NMSE, MAE, SD, P5, P95]:
        metric[:, projects.index('var_kmax_sample'), 1:, :, :, :] = 0.
        metric[:, projects.index('control'), 1:, :, :, :] = 0.

    # mask zeros in array (only one year, or the control)
    NMSE = ma.masked_where(NMSE == 0., NMSE)
    NMSE = ma.mean(NMSE, axis=5)  # average over years
    MAE = ma.masked_where(MAE == 0., MAE)
    MAE = ma.mean(MAE, axis=5)
    SD = ma.masked_where(SD == 0., SD)
    SD = ma.mean(SD, axis=5)
    P5 = ma.masked_where(P5 == 0., P5)
    P5 = ma.mean(P5, axis=5)
    P95 = ma.masked_where(P95 == 0., P95)
    P95 = ma.mean(P95, axis=5)

    # compute the performance ranks now
    ranks = np.zeros((len(sites), len(projects), len(confs), 2, 2, 5))

    # loop over ET and An
    for i in range(2):

        # loop over sites
        for k in range(len(sites)):

            # loop over event types, i.e. drought or not
            for n in range(2):

                # loop over the metrics of performance
                for m, metric in enumerate([NMSE, MAE, SD, P5, P95]):

                    # average the metric within an event category
                    a = metric[k, :, :, i, n]
                    b = a[~ma.getmask(metric[k, :, :, i, n])]

                    # ranks computed in weak quantile regression
                    rk = np.array([psc(b, c, 'weak') / 100. for c in b])

                    track = 0  # tracker for the computed ranks

                    # loop over experiment types
                    for l in range(len(projects)):

                        # loop over configurations
                        for j in range(len(confs)):

                            # there are ranks where metrics are not nan!
                            if a[l, j] == b[track]:
                                ranks[k, l, j, i, n, m] = rk[track]
                                track += 1  # update the tracker

                                if track == len(b):  # all is filled
                                    break

    # average ranking across metrics
    ranks = ma.masked_where(ranks <= 0., ranks)
    ranks = ma.mean(ranks, axis=5)

    return NMSE, MAE, SD, P5, P95, ranks

def fluxes(sites, project, years=None, which='obs'):

    """
    Retrieves each of the yearly carbon and water flux data and performs
    all the appropriate unit conversions.

    Arguments:
    ----------
    sites: array
        site names

    project: string
        name of the repository which contains the flux input

    years: array
        years to select the values from

    which: string
        if 'obs' will look in the input files, else looks in the outputs

    Returns:
    --------
    GPP: array
        Gross Primary Productivity [g C m-2]

    ET: array
        Evapotranspiration [mm]

    ratio1: array
        instantaneous ratio of Es / ET [-]

    ratio2: array
        instantaneous ratio of E / ET [-]

    """

    if which == 'obs':
        basedir = get_main_dir()

        while 'src' in basedir:
            basedir = os.path.dirname(get_main_dir())

        iname = os.path.join(os.path.join(os.path.join(basedir, 'input'),
                                          'projects'), project)
        confs = ['']

        try:

            if len(years) > 1:
                GPP = np.zeros((len(sites, len(years))))
                ET = np.zeros((len(sites), len(years)))

        except TypeError:

            GPP = np.zeros(len(sites))
            ET = np.zeros(len(sites))

    else:
        basedir = get_main_dir()

        while 'src' in basedir:
            basedir = os.path.dirname(get_main_dir())

        iname = os.path.join(os.path.join(os.path.join(basedir, 'output'),
                                          'projects'), project)

        if which == 'calib':
            confs = ['']

            try:

                if len(years) > 1:
                    GPP = np.zeros((len(sites, len(years))))
                    ET = np.zeros((len(sites), len(years)))
                    ratio1 = np.zeros((len(sites), len(years)))
                    ratio2 = np.zeros((len(sites), len(years)))

            except TypeError:

                GPP = np.zeros(len(sites))
                ET = np.zeros(len(sites))
                ratio1 = np.zeros(len(sites))
                ratio2 = np.zeros(len(sites))

        else:
            confs = ['H', '', 'L']

            try:

                if len(years) > 1:
                    GPP = np.zeros((len(sites), 3, len(years)))
                    ET = np.zeros((len(sites), 3, len(years)))
                    ratio1 = np.zeros((len(sites), 3, len(years)))
                    ratio2 = np.zeros((len(sites), 3, len(years)))

            except TypeError:

                GPP = np.zeros((len(sites), 3))
                ET = np.zeros((len(sites), 3))
                ratio1 = np.zeros((len(sites), 3))
                ratio2 = np.zeros((len(sites), 3))

    for i in range(len(confs)):

        for j in range(len(sites)):

            for file in os.listdir(iname):

                find = True

                if file.endswith('.csv') and ('actual' in file) and \
                   ('%s%s_' % (sites[j], confs[i]) in file):
                    df, __ = read_csv(os.path.join(iname, file))

                    if years is not None:
                        try:

                            if len(years) > 1:
                                if any([str(int(y)) in file for y in years]):
                                    find = True

                                else:
                                    find = False

                        except TypeError:

                            if years not in file:
                                find = False

                    if find:
                        year = int(file.split('_')[-1].split('.csv')[0])

                        # restrict to subsets of dates
                        start = pd.to_datetime(year * 1000 + 1, format='%Y%j')
                        end = pd.to_datetime(year * 1000 + int(len(df) / 48.)
                                             + 1, format='%Y%j')

                        dates = pd.date_range(start=start, end=end, freq='D')
                        __, iunique = np.unique(dates.month, return_index=True)
                        unique_months = [dates.month[k] for k in
                                         sorted(iunique)]

                        # restrict between April - Nov for comparison
                        dates_2_keep = np.in1d(dates.month,
                                               unique_months[3:-2])
                        dates_2_keep = np.repeat(dates_2_keep, 48)[:len(df)]
                        df = df.iloc[dates_2_keep]

                        if which == 'obs':
                            mask = np.logical_and(df['PPFD'] > 50.,
                                                  df['LAI'] > 0.001)
                            GPP[j] += (df['GPP'][mask].sum() *
                                       conv.umolCpm2ps_2_gCpm2phlfhr)
                            ET[j] += (df['Qle'][mask].sum() *
                                      conv.Wpm2_2_mmphlfhr)

                        if which == 'sim':
                            GPP[j, i] += (df[df.filter(like='A(').columns]
                                          .sum() *
                                          conv.umolCpm2ps_2_gCpm2phlfhr)
                            ET_std = df['E(std)'] + df['Es(std)']
                            ET_psi = df['E(psi2)'] + df['Es(psi2)']
                            ET[j, i] += [(ET_std.sum() *
                                          conv.mmolH2Opm2ps_2_mmphlfhr),
                                         (ET_psi.sum() *
                                          conv.mmolH2Opm2ps_2_mmphlfhr)]
                            ratio1[j, i] += [(df['Es(std)'] / ET_std).mean(),
                                             (df['Es(psi2)'] / ET_psi).mean()]
                            ratio2[j, i] += [(df['E(std)'] / ET_std).mean(),
                                             (df['E(psi2)'] / ET_psi).mean()]

                        if which == 'calib':
                            GPP[j] += (df['A(psi2)'].sum()
                                       * conv.umolCpm2ps_2_gCpm2phlfhr)
                            ET_psi = df['E(psi2)'] + df['Es(psi2)']
                            ET[j] += (ET_psi.sum() *
                                      conv.mmolH2Opm2ps_2_mmphlfhr)
                            ratio1[j] += (df['Es(psi2)'] / ET_psi).mean()
                            ratio2[j] += (df['E(psi2)'] / ET_psi).mean()

    if which == 'obs':

        return GPP, ET

    else:

        return GPP, ET, ratio1, ratio2

def write_word_doc(fname, projects):

    """
    Writes the final word document. The order of the tables is as
    follows:
        1.  general PFT model parameters
        2.  photosynthesis model parameters

        3.  general site info
        4.  site climate and composite LAI
        5.  site soil parameters
        6.  site plant traits

        7.  site kmax climate values
        8.  site kmax climate range analysis
        9.  site kmax extreme:average climate ratio analysis

        10. ProfitMax NMSEs (close to zero is good)
        11. ProfitMax MAEs (close to zero is good)
        12. ProfitMax SD metrics (close to zero is good)
        13. ProfitMax P5 metrics (close to zero is good)
        14. ProfitMax P95 metrics (close to zero is good)
        15. % improvement on the Calibration metrics compared to the
            Control

        16. ProfitMax "optimal" behaviour quantile ranks
        17. ProfitMax "high" behaviour quantile ranks
        18. ProfitMax "low" behaviour quantile ranks

        19. Total GPP between April-Nov
        20. % improvement on the total GPP compared to the Control
        21. Total ET between April-Nov
        22. % improvement on the total ET compared to the Control
        23. average Es / ET and average E / ET ratios between April-Nov

    Arguments:
    ----------
    fname: string
        name of the summary file to save

    projects: array
        names of the repository which contain the kmax data

    Returns:
    --------
    fname in the directory which harbours TractLSM

    """

    # useful directories
    basedir = get_main_dir()

    while 'src' in basedir:
        basedir = os.path.dirname(get_main_dir())

    Rsites = os.path.join(os.path.join(basedir, 'input'), 'fluxsites')
    Rmet = os.path.join(Rsites, 'met')
    Rout = os.path.join(os.path.join(basedir, 'output'), 'projects')

    # site info
    df, __ = read_csv(os.path.join(Rsites, 'info_sites.csv'))
    df.set_index('Site', inplace=True)

    # declare document
    doc = Document()

    doc.add_heading("Summary of the TractLSM outputs", 0)

    description = """This document summarizes:\n
                     (i) parameters and site-level input data used for the runs
                     (ii) general analysis about the output generated
                  """
    p = doc.add_paragraph(description)
    p.add_run('\nThe following projects are accounted for:\n')

    for project in projects:

        p.add_run('     %s\n' % (project)).bold = True

        if 'sample' in project:
            try:
                fkmax = open(os.path.join(os.path.join(Rout,
                                                       'var_kmax_best_calib'),
                                          'best.txt'), 'r')
                flines = fkmax.readlines()
                kmax_calib = [None] * len(sites)

                for l in flines:

                    site = l.split(':')[0].strip()
                    k = l.split(':')[1].strip()
                    kmax_calib[sites.index(site)] = k

                if not any(kmax_calib):
                    kmax_calib = None

            except IOError:
                kmax_calib = None

        if 'adjust' in project:
            try:  # look for "best" climate
                fclimate = open(os.path.join(os.path.join(Rout,
                                'var_kmax_best_climate'), 'best.txt'), 'r')
                flines = fclimate.readlines()
                climate = [None] * len(sites)

                for l in flines:

                    site = l.split(':')[0].strip()
                    cl = l.split(':')[1].strip()

                    if 'average' in cl:
                        climate[sites.index(site)] = 'Average'

                    else:
                        climate[sites.index(site)] = 'Extreme'

                if not any(climate):
                    climate = None

            except IOError:
                climate = None

    doc.add_heading('General (PFT-based) Model parameters', level=1)
    PFTs = np.unique(df['PFT'][~pd.isna(df['PFT'])])

    t = doc.add_table(rows=9, cols=len(PFTs) + 3)
    t.cell(0, 0).text = 'Abbreviation'
    t.cell(0, 1).text = 'Parameter description'
    t.cell(0, 2).text = 'Value'
    t.cell(0, 2 + len(PFTs)).text = 'Unit'

    track = 0

    for PFT in PFTs:

        t.cell(1, 2 + track).text = str(PFT)

        track += 1

    # rendering of the headers
    t.cell(0, 0).merge(t.cell(1, 0))
    t.cell(0, 1).merge(t.cell(1, 1))
    t.cell(0, 2 + len(PFTs)).merge(t.cell(1, 2 + len(PFTs)))

    for i in range(3, 2 + len(PFTs)):

        t.cell(0, 2).merge(t.cell(0, i))
        t.cell(5, 2).merge(t.cell(5, i))
        t.cell(6, 2).merge(t.cell(6, i))
        t.cell(7, 2).merge(t.cell(7, i))

    # fill the table
    t.cell(2, 0).text = 'w_l_max'
    t.cell(2, 1).text = 'maximum leaf width'
    t.cell(2, 2 + len(PFTs)).text = 'm'

    for i in range(len(PFTs)):

        t.cell(2, 2 + i).text = '{0:.3f}'.format(np.unique(df['max_leaf_width']
                                                 [df['PFT'] == PFTs[i]])[0])

    t.cell(3, 0).text = 'can_int'
    t.cell(3, 1).text = 'canopy intercept'
    t.cell(3, 2 + len(PFTs)).text = '-'

    for i in range(len(PFTs)):

        t.cell(3, 2 + i).text = ('{0:.3f}'
                                 .format(np.unique(df['canopy_intercept']
                                         [df['PFT'] == PFTs[i]])[0]))

    t.cell(4, 0).text = 'alpha_l'
    t.cell(4, 1).text = 'leaf albedo'
    t.cell(4, 2 + len(PFTs)).text = '-'

    for i in range(len(PFTs)):

        t.cell(4, 2 + i).text = '{0:.3f}'.format(np.unique(df['albedo_l']
                                                 [df['PFT'] == PFTs[i]])[0])

    t.cell(5, 0).text = 'alpha_ws'
    t.cell(5, 1).text = 'wet soil albedo'
    t.cell(5, 2 + len(PFTs)).text = '-'
    t.cell(5, 2 + i).text = '{0:.3f}'.format(dp.albedo_ws)

    t.cell(6, 0).text = 'alpha_ds'
    t.cell(6, 1).text = 'dry soil albedo'
    t.cell(6, 2 + len(PFTs)).text = '-'
    t.cell(6, 2 + i).text = '{0:.3f}'.format(dp.albedo_ds)

    t.cell(7, 0).text = 'eps_l'
    t.cell(7, 1).text = 'leaf emissivity'
    t.cell(7, 2 + len(PFTs)).text = '-'
    t.cell(7, 2 + i).text = '{0:.3f}'.format(dp.eps_l)

    t.cell(8, 0).text = 'eps_s'
    t.cell(8, 1).text = 'soil emissivity'
    t.cell(8, 2 + len(PFTs)).text = '-'
    t.cell(8, 2 + i).text = '{0:.3f}'.format(dp.eps_s)

    doc.add_heading('Photosynthetis Model parameters', level=1)

    t = doc.add_table(rows=19, cols=4)
    t.cell(0, 0).text = 'Abbreviation'
    t.cell(0, 1).text = 'Parameter description'
    t.cell(0, 2).text = 'Value'
    t.cell(0, 3).text = 'Unit'

    # fill the table
    t.cell(1, 0).text = 'CO2'
    t.cell(1, 1).text = 'atmospheric CO2 concentration'
    t.cell(1, 2).text = '{0:.2f}'.format(dp.CO2)
    t.cell(1, 3).text = 'Pa'

    t.cell(2, 0).text = 'O2'
    t.cell(2, 1).text = 'atmospheric O2 concentration'
    t.cell(2, 2).text = '{0:.2f}'.format(dp.O2)
    t.cell(2, 3).text = 'kPa'

    t.cell(3, 0).text = 'gamma star'
    t.cell(3, 1).text = 'CO2 compensation point @ 25 degC'
    t.cell(3, 2).text = '{0:.2f}'.format(dp.gamstar25)
    t.cell(3, 3).text = 'Pa'

    t.cell(4, 0).text = 'Kc'
    t.cell(4, 1).text = 'Michaelis-Menton constant for carboxylation'
    t.cell(4, 2).text = '{0:.2f}'.format(dp.Kc25)
    t.cell(4, 3).text = 'Pa'

    t.cell(5, 0).text = 'Ko'
    t.cell(5, 1).text = 'Michaelis-Menton constant for oxygenation'
    t.cell(5, 2).text = '{0:.2f}'.format(dp.Ko25)
    t.cell(5, 3).text = 'kPa'

    t.cell(6, 0).text = 'J:V'
    t.cell(6, 1).text = 'Jmax25 to Vmax25 ratio'
    t.cell(6, 2).text = '{0:.2f}'.format(dp.JV)
    t.cell(6, 3).text = '-'

    t.cell(7, 0).text = 'alpha'
    t.cell(7, 1).text = 'quantum yield of electron transport'
    t.cell(7, 2).text = '{0:.2f}'.format(dp.alpha)
    t.cell(7, 3).text = 'mol(photon).mol(e-)-1'

    t.cell(8, 0).text = 'c1'
    t.cell(8, 1).text = 'curvature of the light response'
    t.cell(8, 2).text = '{0:.4f}'.format(dp.c1)
    t.cell(8, 3).text = '-'

    t.cell(9, 0).text = 'c2'
    t.cell(9, 1).text = 'transition Je vs Jc'
    t.cell(9, 2).text = '{0:.2f}'.format(dp.c2)
    t.cell(9, 3).text = '-'

    t.cell(10, 0).text = 'Ec'
    t.cell(10, 1).text = 'energy of activation of the carboxylation'
    t.cell(10, 2).text = '{0:.2f}'.format(dp.Ec)
    t.cell(10, 3).text = 'J.mol-1'

    t.cell(11, 0).text = 'Eo'
    t.cell(11, 1).text = 'energy of activation of the oxygenation'
    t.cell(11, 2).text = '{0:.2f}'.format(dp.Eo)
    t.cell(11, 3).text = 'J.mol-1'

    t.cell(12, 0).text = 'Ev'
    t.cell(12, 1).text = 'energy of activation of Vcmax'
    t.cell(12, 2).text = '{0:.2f}'.format(dp.Ev)
    t.cell(12, 3).text = 'J.mol-1'

    t.cell(13, 0).text = 'Ej'
    t.cell(13, 1).text = 'energy of activation of Jmax'
    t.cell(13, 2).text = '{0:.2f}'.format(dp.Ej)
    t.cell(13, 3).text = 'J.mol-1'

    t.cell(14, 0).text = 'Egamm'
    t.cell(14, 1).text = 'energy of activation of the CO2 compensation point'
    t.cell(14, 2).text = '{0:.2f}'.format(dp.Egamstar)
    t.cell(14, 3).text = 'J.mol-1'

    t.cell(15, 0).text = 'deltaSv'
    t.cell(15, 1).text = 'Vcmax entropy factor'
    t.cell(15, 2).text = '{0:.2f}'.format(dp.deltaSv)
    t.cell(15, 3).text = 'J.mol-1.K-1'

    t.cell(16, 0).text = 'deltaSj'
    t.cell(16, 1).text = 'Jmax entropy factor'
    t.cell(16, 2).text = '{0:.2f}'.format(dp.deltaSj)
    t.cell(16, 3).text = 'J.mol-1.K-1'

    t.cell(17, 0).text = 'Hdv'
    t.cell(17, 1).text = 'Vcmax rate of decrease above the optimum T'
    t.cell(17, 2).text = '{0:.2f}'.format(dp.Hdv)
    t.cell(17, 3).text = 'J.mol-1'

    t.cell(18, 0).text = 'Hdj'
    t.cell(18, 1).text = 'Jmax rate of decrease above the optimum T'
    t.cell(18, 2).text = '{0:.2f}'.format(dp.Hdj)
    t.cell(18, 3).text = 'J.mol-1'

    doc.add_heading('General Site Info', level=1)

    t = doc.add_table(rows=len(sites) + 1, cols=8)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'Country'
    t.cell(0, 2).text = 'Latitude'
    t.cell(0, 3).text = 'Longitude'
    t.cell(0, 4).text = 'PFT'
    t.cell(0, 5).text = 'Dominant Species'
    t.cell(0, 6).text = 'Data'
    t.cell(0, 7).text = 'Reference'

    for i in range(len(sites)):

        site = sites[i]

        ds = xr.open_dataset(os.path.join(Rmet,
                                          '%sFluxnet_met.nc' % (site)))
        lat = ds.latitude.values[0][0]
        lon = ds.longitude.values[0][0]

        if ds.attrs['Fluxnet_dataset_version'] == 'n/a':
            dataset = 'LT'

        else:
            dataset = 'FN'

        Spp = df.loc[site, 'Dominant Species']

        if len(Spp.split()) > 2:
            Spp = Spp.rsplit(' ', 1)[0]

        t.cell(i + 1, 0).text = site
        try:
            t.cell(i + 1, 1).text = reverse_geocode(lat, lon)

        except Exception:
            pass

        t.cell(i + 1, 2).text = '{0:.2f}'.format(lat)
        t.cell(i + 1, 3).text = '{0:.2f}'.format(lon)
        t.cell(i + 1, 4).text = df.loc[site, 'PFT']
        t.cell(i + 1, 5).text = Spp
        t.cell(i + 1, 6).text = dataset
        t.cell(i + 1, 7).text = ''

    doc.add_heading('Site Climate and composite LAI', level=1)

    t = doc.add_table(rows=len(sites) + 1, cols=8)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'Koeppen Climate Class'
    t.cell(0, 2).text = 'MAP\n(mm/y)'
    t.cell(0, 3).text = 'Tair,avg\n(degC)'
    t.cell(0, 4).text = 'Davg\n(kPa)'
    t.cell(0, 5).text = 'Tair,xx\n(degC)'
    t.cell(0, 6).text = 'Dxx\n(kPa)'
    t.cell(0, 7).text = 'Composite LAI\n(m2/m2)'

    cLAI = []  # store the composite LAI for kmax tables below

    for i in range(len(sites)):

        site = sites[i]

        t.cell(i + 1, 0).text = site
        t.cell(i + 1, 1).text = df.loc[site, 'Koeppen Climate']
        t.cell(i + 1, 2).text = '{0:.2f}'.format(df.loc[site, 'CRU MAP'])
        t.cell(i + 1, 3).text = '{0:.2f}'.format(df.loc[site, 'CRU cTair'])
        t.cell(i + 1, 4).text = '{0:.2f}'.format(df.loc[site, 'CRU cVPD'])
        t.cell(i + 1, 5).text = '{0:.2f}'.format(df.loc[site, 'CRU cTxx'])
        t.cell(i + 1, 6).text = '{0:.2f}'.format(df.loc[site, 'CRU cVPDxx'])
        t.cell(i + 1, 7).text = '{0:.2f}'.format(df.loc[site, 'Composite LAI'])

        cLAI += [df.loc[site, 'Composite LAI']]

    doc.add_heading('Site Soil Parameters', level=1)

    t = doc.add_table(rows=len(sites) + 1, cols=9)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'Psie\n(-MPa)'
    t.cell(0, 2).text = 'k_s,sat\n(m s-1)'
    t.cell(0, 3).text = 'theta_sat\n(m3/m3)'
    t.cell(0, 4).text = 'theta_fc\n(m3/m3)'
    t.cell(0, 5).text = 'theta_wp\n(m3/m3)'
    t.cell(0, 6).text = 'b\n(-)'
    t.cell(0, 7).text = 'r_soil\n(-)'
    t.cell(0, 8).text = 'Z_total\n(m)'

    for i in range(len(sites)):

        site = sites[i]

        t.cell(i + 1, 0).text = site
        t.cell(i + 1, 1).text = '{0:.2e}'.format(-df.loc[site, 'Psie'])
        t.cell(i + 1, 2).text = '{0:.2e}'.format(-df.loc[site, 'hyds'])
        t.cell(i + 1, 3).text = '{0:.2e}'.format(df.loc[site, 'theta_sat'])
        t.cell(i + 1, 4).text = '{0:.2e}'.format(df.loc[site, 'fc'])
        t.cell(i + 1, 5).text = '{0:.2e}'.format(df.loc[site, 'pwp'])
        t.cell(i + 1, 6).text = '{0:.2f}'.format(df.loc[site, 'bch'])
        t.cell(i + 1, 7).text = '{0:.2f}'.format(df.loc[site, 'r_soil'])
        t.cell(i + 1, 8).text = '{0:.2f}'.format(df.loc[site, 'Zbottom'])

    doc.add_heading('Site Plant Traits', level=1)

    t = doc.add_table(rows=len(sites) + 1, cols=6)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'Vcmax25\n(umol/m2/s)'
    t.cell(0, 2).text = 'Rd25\n(umol/m2/s)'
    t.cell(0, 3).text = 'g1\n(kPa1/2)'
    t.cell(0, 4).text = 'P50\n(-MPa)'
    t.cell(0, 5).text = 'P88\n(-MPa)'

    for i in range(len(sites)):

        site = sites[i]

        t.cell(i + 1, 0).text = site
        t.cell(i + 1, 1).text = '{0:.2f}'.format(df.loc[site, 'Vmax25'])
        t.cell(i + 1, 2).text = '{0:.2f}'.format(df.loc[site, 'Rlref'])
        t.cell(i + 1, 3).text = '{0:.2f}'.format(df.loc[site, 'g1'])
        t.cell(i + 1, 4).text = '{0:.2f}'.format(df.loc[site, 'P50'])
        t.cell(i + 1, 5).text = '{0:.2f}'.format(df.loc[site, 'P88'])

    doc.add_heading('Site kmax values', level=1)

    # retrieve the kmax values
    kmax_avg = None
    kmax_xx = None
    kmax = None

    for project in projects:

        if 'average' in project:
            kmax_avg = kmax_values(sites, project, cLAI=cLAI)

        if 'extreme' in project:
            kmax_xx = kmax_values(sites, project, cLAI=cLAI)

    t = doc.add_table(rows=len(sites) + 2, cols=7)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'average scenario'
    t.cell(0, 4).text = 'extreme scenario'
    t.cell(1, 1).text = 'kmax,high\n(mmol/m2/s/MPa)'
    t.cell(1, 2).text = 'kmax,optimal\n(mmol/m2/s/MPa)'
    t.cell(1, 3).text = 'kmax,low\n(mmol/m2/s/MPa)'
    t.cell(1, 4).text = 'kmax,high\n(mmol/m2/s/MPa)'
    t.cell(1, 5).text = 'kmax,optimal\n(mmol/m2/s/MPa)'
    t.cell(1, 6).text = 'kmax,low\n(mmol/m2/s/MPa)'

    # rendering of the headers
    t.cell(0, 0).merge(t.cell(1, 0))
    t.cell(0, 1).merge(t.cell(0, 2))
    t.cell(0, 1).merge(t.cell(0, 3))
    t.cell(0, 4).merge(t.cell(0, 5))
    t.cell(0, 4).merge(t.cell(0, 6))

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i + 2, 0).text = site

        for project in projects:

            if 'average' in project:
                loc = 1
                kmax = kmax_avg[i]

            if 'extreme' in project:
                loc = 4
                kmax = kmax_xx[i]

            if kmax is not None:

                for j in range(len(kmax)):

                    t.cell(i + 2, loc + j).text = '{0:.2f}'.format(kmax[j])

    doc.add_heading('Site kmax range analysis', level=1)

    t = doc.add_table(rows=len(sites) + 4, cols=7)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'average scenario'
    t.cell(0, 3).text = 'extreme scenario'
    t.cell(0, 5).text = 'overall'
    t.cell(1, 1).text = 'min'
    t.cell(1, 2).text = 'max'
    t.cell(1, 3).text = 'min'
    t.cell(1, 4).text = 'max'
    t.cell(1, 5).text = 'min'
    t.cell(1, 6).text = 'max'

    # rendering of the headers
    t.cell(0, 0).merge(t.cell(1, 0))
    t.cell(0, 1).merge(t.cell(0, 2))
    t.cell(0, 3).merge(t.cell(0, 4))
    t.cell(0, 5).merge(t.cell(0, 6))

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i + 2, 0).text = site

        if kmax_avg is not None:
            t.cell(i + 2, 1).text = '{0:.2f}'.format(np.amin(kmax_avg[i]))
            t.cell(i + 2, 2).text = '{0:.2f}'.format(np.amax(kmax_avg[i]))

        if kmax_xx is not None:
            t.cell(i + 2, 3).text = '{0:.2f}'.format(np.amin(kmax_xx[i]))
            t.cell(i + 2, 4).text = '{0:.2f}'.format(np.amax(kmax_xx[i]))

        if (kmax_avg is not None) and (kmax_xx is not None):
            kmax = np.concatenate((kmax_avg[i], kmax_xx[i]))

        elif (kmax_avg is not None) and (kmax_xx is None):
            kmax = kmax_avg[i]

        elif (kmax_xx is not None) and (kmax_avg is None):
            kmax = kmax_xx[i]

        if (kmax_avg is None) and (kmax_xx is None):
            t.cell(i + 2, 5).text = ''
            t.cell(i + 2, 6).text = ''

        else:
            t.cell(i + 2, 5).text = '{0:.2f}'.format(np.amin(kmax))
            t.cell(i + 2, 6).text = '{0:.2f}'.format(np.amax(kmax))

    t.cell(len(sites) + 3, 0).text = 'across sites'

    if kmax_avg is not None:
        t.cell(len(sites) + 3, 1).text = '{0:.2f}'.format(np.amin(kmax_avg))
        t.cell(len(sites) + 3, 2).text = '{0:.2f}'.format(np.amax(kmax_avg))

    if kmax_xx is not None:
        t.cell(len(sites) + 3, 3).text = '{0:.2f}'.format(np.amin(kmax_xx))
        t.cell(len(sites) + 3, 4).text = '{0:.2f}'.format(np.amax(kmax_xx))

    if (kmax_avg is not None) and (kmax_xx is not None):
        kmax = np.concatenate((kmax_avg, kmax_xx))
        t.cell(len(sites) + 3, 5).text = '{0:.2f}'.format(np.amin(kmax))
        t.cell(len(sites) + 3, 6).text = '{0:.2f}'.format(np.amax(kmax))

    doc.add_heading('Site kmax ratio analysis', level=1)

    t = doc.add_table(rows=len(sites) + 4, cols=5)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'extreme:average'
    t.cell(1, 1).text = 'high'
    t.cell(1, 2).text = 'optimal'
    t.cell(1, 3).text = 'low'
    t.cell(1, 4).text = 'overall'

    # rendering of the headers
    t.cell(0, 0).merge(t.cell(1, 0))
    t.cell(0, 1).merge(t.cell(0, 2))
    t.cell(0, 1).merge(t.cell(0, 3))
    t.cell(0, 1).merge(t.cell(0, 4))

    if (kmax_avg is not None) and (kmax_xx is not None):
        ratios = kmax_xx / kmax_avg

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i + 2, 0).text = site

        if (kmax_avg is not None) and (kmax_xx is not None):
            t.cell(i + 2, 1).text = '{0:.2f}'.format(ratios[i, 0])
            t.cell(i + 2, 2).text = '{0:.2f}'.format(ratios[i, 1])
            t.cell(i + 2, 3).text = '{0:.2f}'.format(ratios[i, 2])
            t.cell(i + 2, 4).text = '{0:.2f}'.format(np.mean(ratios[i]))

    t.cell(len(sites) + 3, 0).text = 'across sites'

    if (kmax_avg is not None) and (kmax_xx is not None):
        t.cell(len(sites) + 3, 1).text = ('{0:.2f}'
                                          .format(np.mean(ratios[:, 0])))
        t.cell(len(sites) + 3, 2).text = ('{0:.2f}'
                                          .format(np.mean(ratios[:, 1])))
        t.cell(len(sites) + 3, 3).text = ('{0:.2f}'
                                          .format(np.mean(ratios[:, 2])))
        t.cell(len(sites) + 3, 4).text = ('{0:.2f}'
                                          .format(np.mean(ratios[:, :])))

    # performance scores
    dfp, __ = read_csv(os.path.join(os.path.join(Rout, 'var_kmax'),
                       'perf_scores.csv'))
    dfp.set_index('Site', inplace=True)

    scores = (get_scores(dfp, sites, projects, kmax=kmax_calib,
                         climate=climate, years=[2003, 2006]))

    track = 0

    for perf in ['NMSE', 'MAE', 'SD', 'P5', 'P95']:

        doc.add_heading('ProfitMax optimal %ss' % (perf), level=1)
        score = scores[track]

        t = doc.add_table(rows=len(sites) * 2 + 7, cols=10)
        t.cell(0, 0).text = 'Site Name'
        t.cell(0, 1).text = 'Flux'
        t.cell(0, 2).text = '2003 & 2006'
        t.cell(0, 6).text = '2002 & 2005'
        t.cell(1, 2).text = 'average scenario'
        t.cell(1, 3).text = 'extreme scenario'
        t.cell(1, 4).text = 'control'
        t.cell(1, 5).text = 'calib'
        t.cell(1, 6).text = 'average scenario'
        t.cell(1, 7).text = 'extreme scenario'
        t.cell(1, 8).text = 'control'
        t.cell(1, 9).text = 'calib'

        # rendering of the headers
        t.cell(0, 0).merge(t.cell(1, 0))
        t.cell(0, 2).merge(t.cell(0, 3))
        t.cell(0, 3).merge(t.cell(0, 4))
        t.cell(0, 4).merge(t.cell(0, 5))
        t.cell(0, 6).merge(t.cell(0, 7))
        t.cell(0, 7).merge(t.cell(0, 8))
        t.cell(0, 8).merge(t.cell(0, 9))

        for i in range(len(sites)):

            site = sites[i]
            t.cell(i * 2 + 2, 0).text = site

            ds = dfp.loc[site]

            t.cell(i * 2 + 2, 1).text = 'GPP'
            t.cell(i * 2 + 3, 1).text = 'ET'

            for j in range(2):  # GPP, ET

                for k in range(2):  # events

                    if k == 0:
                        bloc = 2

                    else:
                        bloc = 6

                    t.cell(i * 2 + j + 2, bloc).text = \
                        '{0:.2f}'.format(score[i, 0, 0, j, k])
                    t.cell(i * 2 + j + 2, bloc + 1).text = \
                        '{0:.2f}'.format(score[i, 1, 0, j, k])
                    t.cell(i * 2 + j + 2, bloc + 2).text = \
                        '{0:.2f}'.format(score[i, len(score[0])-1, 0, j, k])

                    if kmax_calib is not None:
                        t.cell(i * 2 + j + 2, bloc + 3).text = \
                            '{0:.2f}'.format(score[i, 2, 0, j, k])

        t.cell(len(sites) * 2 + 2, 0).text = 'across sites'
        t.cell(len(sites) * 2 + 2, 1).text = 'GPP'
        t.cell(len(sites) * 2 + 3, 1).text = 'ET'

        for j in range(2):  # GPP, ET

            for k in range(2):  # events

                if k == 0:
                    bloc = 2

                else:
                    bloc = 6

                t.cell(len(sites) * 2 + j + 2, bloc).text = \
                    '{0:.2f}'.format(np.mean(score[:, 0, 0, j, k]))
                t.cell(len(sites) * 2 + j + 2, bloc + 1).text = \
                    '{0:.2f}'.format(np.mean(score[:, 1, 0, j, k]))
                t.cell(len(sites) * 2 + j + 2, bloc + 2).text = \
                    '{0:.2f}'.format(np.mean(score[:,
                                                   len(score[0])-1, 0, j, k]))

                if kmax_calib is not None:
                    t.cell(len(sites) * 2 + j + 2, bloc + 3).text = \
                        '{0:.2f}'.format(np.mean(score[:, 2, 0, j, k]))

        t.cell((len(sites) + 1) * 2 + 3, 0).text = 'overall'
        t.cell((len(sites) + 1) * 2 + 3, 1).text = 'GPP'
        t.cell((len(sites) + 1) * 2 + 4, 1).text = 'ET'

        for j in range(2):  # GPP, ET

            t.cell((len(sites) + 1) * 2 + j + 3, 2).text = \
                '{0:.2f}'.format(np.mean(score[:, 0, 0, j, :]))
            t.cell((len(sites) + 1) * 2 + j + 3, 3).text = \
                '{0:.2f}'.format(np.mean(score[:, 1, 0, j, :]))
            t.cell((len(sites) + 1) * 2 + j + 3, 4).text = \
                '{0:.2f}'.format(np.mean(score[:, len(score[0])-1, 0, j, :]))

            if kmax_calib is not None:
                t.cell((len(sites) + 1) * 2 + j + 3, 5).text = \
                    '{0:.2f}'.format(np.mean(score[:, 2, 0, j, :]))

        track += 1

    doc.add_heading('% overall improvement on Control metrics', level=1)

    t = doc.add_table(rows=11, cols=4)
    t.cell(0, 0).text = 'metric'
    t.cell(0, 1).text = 'flux'
    t.cell(0, 2).text = 'climate'
    t.cell(0, 3).text = 'calib'

    track1 = 0
    track2 = 1

    for perf in ['NMSE', 'MAE', 'SD', 'P5', 'P95']:

        score = ma.filled(scores[track1], 0.)  # 0. where masked values

        t.cell(track2, 0).text = perf
        t.cell(track2, 0).merge(t.cell(track2 + 1, 0))

        for j in range(2):  # GPP, ET

            if j == 0:
                t.cell(track2, 1).text = 'GPP'

            else:
                t.cell(track2, 1).text = 'ET'

            ctrl = np.mean(score[:, len(score[0])-1, 0, j, :])

            if (kmax_avg is not None) and (kmax_xx is not None):
                climate = np.mean(score[:, 0, 0, j, :] + score[:, 1, 0, j, :])
                climate = (abs(ctrl) - abs(climate)) / abs(ctrl)
                t.cell(track2, 2).text = '{0:.2f}'.format(100. * climate)

            if kmax_calib is not None:
                cal = np.mean(score[:, 2, 0, j, :])
                cal = (abs(ctrl) - abs(cal)) / abs(ctrl)
                t.cell(track2, 3).text = '{0:.2f}'.format(100. * cal)

            track2 += 1

        track1 += 1

    track = 0

    for conf in ['optimal', 'high', 'low']:

        doc.add_heading('ProfitMax %s Quantile Ranks' % (conf), level=1)
        score = ma.filled(scores[-1][:, :, track, :, :], 0.)  # masked

        if conf == 'optimal':
            t = doc.add_table(rows=len(sites) * 2 + 11, cols=8)

        else:
            t = doc.add_table(rows=len(sites) * 2 + 2, cols=8)

        t.cell(0, 0).text = 'Site Name'
        t.cell(0, 1).text = 'Flux'
        t.cell(0, 2).text = '2003 & 2006'
        t.cell(0, 6).text = '2002 & 2005'
        t.cell(1, 2).text = 'climate'
        t.cell(1, 3).text = 'control'
        t.cell(1, 4).text = 'calib'
        t.cell(1, 5).text = 'climate'
        t.cell(1, 6).text = 'control'
        t.cell(1, 7).text = 'calib'

        # rendering of the headers
        t.cell(0, 0).merge(t.cell(1, 0))
        t.cell(0, 2).merge(t.cell(0, 3))
        t.cell(0, 3).merge(t.cell(0, 4))
        t.cell(0, 4).merge(t.cell(0, 5))
        t.cell(0, 6).merge(t.cell(0, 7))

        for i in range(len(sites)):

            site = sites[i]
            t.cell(i * 2 + 2, 0).text = site

            ds = dfp.loc[site]

            t.cell(i * 2 + 2, 1).text = 'GPP'
            t.cell(i * 2 + 3, 1).text = 'ET'

            for j in range(2):

                for k in range(2):

                    if k == 0:
                        bloc = 2

                    else:
                        bloc = 5

                    t.cell(i * 2 + j + 2, bloc).text = \
                        '{0:.2f}'.format(score[i, 0, j, k] + score[i, 1, j, k])

                    if conf == 'optimal':
                        t.cell(i * 2 + j + 2, bloc + 1).text = \
                            '{0:.2f}'.format(score[i, len(score[0])-1, j, k])

                        if kmax_calib is not None:
                            t.cell(i * 2 + j + 2, bloc + 2).text = \
                                '{0:.2f}'.format(score[i, 2, j, k])

        if conf == 'optimal':
            t.cell(len(sites) * 2 + 2, 0).text = 'across sites mean'
            t.cell(len(sites) * 2 + 2, 1).text = 'GPP'
            t.cell(len(sites) * 2 + 3, 1).text = 'ET'

            for j in range(2):  # GPP, ET

                for k in range(2):  # events

                    if k == 0:
                        bloc = 2

                    else:
                        bloc = 5

                    t.cell(len(sites) * 2 + j + 2, bloc).text = \
                        '{0:.2f}'.format(np.mean(score[:, 0, j, k] +
                                                 score[:, 1, j, k]))
                    t.cell(len(sites) * 2 + j + 2, bloc + 1).text = \
                        ('{0:.2f}'
                         .format(np.mean(score[:, len(score[0])-1, j, k])))

                    if kmax_calib is not None:
                        t.cell(len(sites) * 2 + j + 2, bloc + 2).text = \
                            '{0:.2f}'.format(np.mean(score[:, 2, j, k]))

            t.cell((len(sites) + 1) * 2 + 2, 0).text = 'across sites median'
            t.cell((len(sites) + 1) * 2 + 2, 1).text = 'GPP'
            t.cell((len(sites) + 1) * 2 + 3, 1).text = 'ET'

            for j in range(2):  # GPP, ET

                for k in range(2):  # events

                    if k == 0:
                        bloc = 2

                    else:
                        bloc = 5

                    t.cell((len(sites) + 1) * 2 + j + 2, bloc).text = \
                        '{0:.2f}'.format(np.median(score[:, 0, j, k] +
                                                   score[:, 1, j, k]))
                    t.cell((len(sites) + 1) * 2 + j + 2, bloc + 1).text = \
                        '{0:.2f}'.format(np.median(score[:,
                                                   len(score[0])-1, j, k]))

                    if kmax_calib is not None:
                        t.cell((len(sites) + 1) * 2 + j + 2, bloc + 2).text = \
                            '{0:.2f}'.format(np.median(score[:, 2, j, k]))

            t.cell((len(sites) + 2) * 2 + 3, 0).text = 'overall mean'
            t.cell((len(sites) + 2) * 2 + 3, 1).text = 'GPP'
            t.cell((len(sites) + 2) * 2 + 4, 1).text = 'ET'

            for j in range(2):  # GPP, ET

                t.cell((len(sites) + 2) * 2 + j + 3, 2).text = \
                    '{0:.2f}'.format(np.mean(score[:, 0, j, :] +
                                             score[:, 1, j, :]))
                t.cell((len(sites) + 2) * 2 + j + 3, 3).text = \
                    '{0:.2f}'.format(np.mean(score[:, len(score[0])-1, j, :]))

                if kmax_calib is not None:
                    t.cell((len(sites) + 2) * 2 + j + 3, 4).text = \
                        '{0:.2f}'.format(np.mean(score[:, 2, j, :]))

            t.cell((len(sites) + 3) * 2 + 3, 0).text = 'overall median'
            t.cell((len(sites) + 3) * 2 + 3, 1).text = 'GPP'
            t.cell((len(sites) + 3) * 2 + 4, 1).text = 'ET'

            for j in range(2):  # GPP, ET

                t.cell((len(sites) + 3) * 2 + j + 3, 2).text = \
                    '{0:.2f}'.format(np.median(score[:, 0, j, :] +
                                               score[:, 1, j, :]))
                t.cell((len(sites) + 3) * 2 + j + 3, 3).text = \
                    '{0:.2f}'.format(np.median(score[:,
                                                     len(score[0])-1, j, :]))

                if kmax_calib is not None:
                    t.cell((len(sites) + 3) * 2 + j + 3, 4).text = \
                        '{0:.2f}'.format(np.median(score[:, 2, j, :]))

        track += 1

    # retrieve the total values
    GPP_avg, ET_avg, ratio1_ctrl, ratio2_ctrl = [None, None], [None, None], \
                                                [None, None], [None, None]
    GPP_xx, ET_xx = [None, None], [None, None]
    GPP_cal, ET_cal, ratio1_cal, ratio2_cal = [None, None], [None, None], \
                                              [None, None], [None, None]
    GPP, ET = [None, None], [None, None]

    track = 0

    for y in [[2003, 2006], [2002, 2005]]:

        GPP[track], ET[track] = fluxes(sites, 'var_kmax', years=y)

        for project in projects:

            if 'average' in project:
                GPP_avg[track], ET_avg[track], ratio1_ctrl[track], \
                    ratio2_ctrl[track] = fluxes(sites, project, years=y,
                                                which='sim')

            if 'extreme' in project:
                GPP_xx[track], ET_xx[track], __, __ = fluxes(sites, project,
                                                             years=y,
                                                             which='sim')

            if 'sample' in project:
                ksites = [sites[i] + e for i, e in enumerate(kmax_calib)]
                GPP_cal[track], ET_cal[track], ratio1_cal[track], \
                    ratio2_cal[track] = fluxes(ksites, project, years=y,
                                               which='calib')

        track += 1

    doc.add_heading('Total GPP April-November (g C/m2)', level=1)

    t = doc.add_table(rows=len(sites) * 3 + 1, cols=7)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'years'
    t.cell(0, 2).text = 'average scenario'
    t.cell(0, 3).text = 'extreme scenario'
    t.cell(0, 4).text = 'control'
    t.cell(0, 5).text = 'calib'
    t.cell(0, 6).text = 'obs'

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i * 3 + 1, 0).text = site

        track = 0

        for y in [[2003, 2006], [2002, 2005], 'across years']:

            if y == [2003, 2006]:
                loc = 1

            elif y == [2002, 2005]:
                loc = 2

            else:
                loc = 3

            if y != 'across years':
                t.cell(i * 3 + loc, 1).text = '%d & %d' % (y[0], y[1])
                t.cell(i * 3 + loc, 2).text = \
                    '{0:.2f}'.format(GPP_avg[track][i][1][1])
                t.cell(i * 3 + loc, 3).text = \
                    '{0:.2f}'.format(GPP_xx[track][i][1][1])
                t.cell(i * 3 + loc, 4).text = \
                    '{0:.2f}'.format(GPP_avg[track][i][1][0])
                t.cell(i * 3 + loc, 5).text = \
                    '{0:.2f}'.format(GPP_cal[track][i])
                t.cell(i * 3 + loc, 6).text = '{0:.2f}'.format(GPP[track][i])

            else:
                t.cell(i * 3 + loc, 1).text = 'across years'
                t.cell(i * 3 + loc, 2).text = \
                    '{0:.2f}'.format((GPP_avg[0][i][1][1] +
                                      GPP_avg[1][i][1][1]) / 2.)
                t.cell(i * 3 + loc, 3).text = \
                    '{0:.2f}'.format((GPP_xx[0][i][1][1] +
                                      GPP_xx[1][i][1][1]) / 2.)
                t.cell(i * 3 + loc, 4).text = \
                    '{0:.2f}'.format((GPP_avg[0][i][1][0] +
                                      GPP_avg[1][i][1][0]) / 2.)
                t.cell(i * 3 + loc, 5).text = \
                    '{0:.2f}'.format((GPP_cal[0][i] + GPP_cal[1][i]) / 2.)
                t.cell(i * 3 + loc, 6).text = \
                    '{0:.2f}'.format((GPP[0][i] + GPP[1][i]) / 2.)

            track += 1

    doc.add_heading('% total April-Nov improvement on GPP Control', level=1)

    t = doc.add_table(rows=len(sites) + 1, cols=4)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'average scenario'
    t.cell(0, 2).text = 'extreme scenario'
    t.cell(0, 3).text = 'calib'

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i + 1, 0).text = site

        obs = (GPP[0][i] + GPP[1][i]) / 2.
        ctrl = (GPP_avg[0][i][1][0] + GPP_avg[1][i][1][0]) / 2.
        avg = (GPP_avg[0][i][1][1] + GPP_avg[1][i][1][1]) / 2.
        xx = (GPP_xx[0][i][1][1] + GPP_xx[1][i][1][1]) / 2.
        cal = (GPP_cal[0][i] + GPP_cal[1][i]) / 2.

        avg = (abs(obs - ctrl) - abs(obs - avg)) / abs(obs - ctrl)
        xx = (abs(obs - ctrl) - abs(obs - xx)) / abs(obs - ctrl)
        cal = (abs(obs - ctrl) - abs(obs - cal)) / abs(obs - ctrl)

        t.cell(i + 1, 1).text = '{0:.2f}'.format(100. * avg)
        t.cell(i + 1, 2).text = '{0:.2f}'.format(100. * xx)
        t.cell(i + 1, 3).text = '{0:.2f}'.format(100. * cal)

    doc.add_heading('Total ET April-November (mm)', level=1)

    t = doc.add_table(rows=len(sites) * 3 + 1, cols=7)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'years'
    t.cell(0, 2).text = 'average scenario'
    t.cell(0, 3).text = 'extreme scenario'
    t.cell(0, 4).text = 'control'
    t.cell(0, 5).text = 'calib'
    t.cell(0, 6).text = 'obs'

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i * 3 + 1, 0).text = site

        track = 0

        for y in [[2003, 2006], [2002, 2005], 'across years']:

            if y == [2003, 2006]:
                loc = 1

            elif y == [2002, 2005]:
                loc = 2

            else:
                loc = 3

            if y != 'across years':
                t.cell(i * 3 + loc, 1).text = '%d & %d' % (y[0], y[1])
                t.cell(i * 3 + loc, 2).text = \
                    '{0:.2f}'.format(ET_avg[track][i][1][1])
                t.cell(i * 3 + loc, 3).text = \
                    '{0:.2f}'.format(ET_xx[track][i][1][1])
                t.cell(i * 3 + loc, 4).text = \
                    '{0:.2f}'.format(ET_avg[track][i][1][0])
                t.cell(i * 3 + loc, 5).text = \
                    '{0:.2f}'.format(ET_cal[track][i])
                t.cell(i * 3 + loc, 6).text = '{0:.2f}'.format(ET[track][i])

            else:
                t.cell(i * 3 + loc, 1).text = 'across years'
                t.cell(i * 3 + loc, 2).text = \
                    '{0:.2f}'.format((ET_avg[0][i][1][1] +
                                      ET_avg[1][i][1][1]) / 2.)
                t.cell(i * 3 + loc, 3).text = \
                    '{0:.2f}'.format((ET_xx[0][i][1][1] +
                                      ET_xx[1][i][1][1]) / 2.)
                t.cell(i * 3 + loc, 4).text = \
                    '{0:.2f}'.format((ET_avg[0][i][1][0] +
                                      ET_avg[1][i][1][0]) / 2.)
                t.cell(i * 3 + loc, 5).text = \
                    '{0:.2f}'.format((ET_cal[0][i] + ET_cal[1][i]) / 2.)
                t.cell(i * 3 + loc, 6).text = \
                    '{0:.2f}'.format((ET[0][i] + ET[1][i]) / 2.)

            track += 1

    doc.add_heading('% total April-Nov improvement on ET Control', level=1)

    t = doc.add_table(rows=len(sites) + 1, cols=4)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'average scenario'
    t.cell(0, 2).text = 'extreme scenario'
    t.cell(0, 3).text = 'calib'

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i + 1, 0).text = site

        obs = (ET[0][i] + ET[1][i]) / 2.
        ctrl = (ET_avg[0][i][1][0] + ET_avg[1][i][1][0]) / 2.
        avg = (ET_avg[0][i][1][1] + ET_avg[1][i][1][1]) / 2.
        xx = (ET_xx[0][i][1][1] + ET_xx[1][i][1][1]) / 2.
        cal = (ET_cal[0][i] + ET_cal[1][i]) / 2.

        avg = (abs(obs - ctrl) - abs(obs - avg)) / abs(obs - ctrl)
        xx = (abs(obs - ctrl) - abs(obs - xx)) / abs(obs - ctrl)
        cal = (abs(obs - ctrl) - abs(obs - cal)) / abs(obs - ctrl)

        t.cell(i + 1, 1).text = '{0:.2f}'.format(100. * avg)
        t.cell(i + 1, 2).text = '{0:.2f}'.format(100. * xx)
        t.cell(i + 1, 3).text = '{0:.2f}'.format(100. * cal)

    doc.add_heading('Es:ET & E:ET average instantaneous ratios April-November',
                    level=1)

    t = doc.add_table(rows=len(sites) * 3 + 1, cols=6)
    t.cell(0, 0).text = 'Site Name'
    t.cell(0, 1).text = 'years'
    t.cell(0, 2).text = 'control Es:ET'
    t.cell(0, 3).text = 'calib Es:ET'
    t.cell(0, 4).text = 'control E:ET'
    t.cell(0, 5).text = 'calib E:ET'

    for i in range(len(sites)):

        site = sites[i]
        t.cell(i * 3 + 1, 0).text = site

        track = 0

        for y in [[2003, 2006], [2002, 2005], 'across years']:

            if y == [2003, 2006]:
                loc = 1

            elif y == [2002, 2005]:
                loc = 2

            else:
                loc = 3

            if y != 'across years':
                t.cell(i * 3 + loc, 1).text = '%d & %d' % (y[0], y[1])
                t.cell(i * 3 + loc, 2).text = \
                    '{0:.2f}'.format(ratio1_ctrl[track][i][1][0])
                t.cell(i * 3 + loc, 3).text = \
                    '{0:.2f}'.format(ratio1_cal[track][i])
                t.cell(i * 3 + loc, 4).text = \
                    '{0:.2f}'.format(ratio2_ctrl[track][i][1][0])
                t.cell(i * 3 + loc, 5).text = \
                    '{0:.2f}'.format(ratio2_cal[track][i])

            else:
                t.cell(i * 3 + loc, 1).text = 'across years'
                t.cell(i * 3 + loc, 2).text = \
                    '{0:.2f}'.format((ratio1_ctrl[0][i][1][0] +
                                      ratio1_ctrl[1][i][1][0]) / 2.)
                t.cell(i * 3 + loc, 3).text = \
                    '{0:.2f}'.format((ratio1_cal[0][i] +
                                      ratio1_cal[1][i]) / 2.)
                t.cell(i * 3 + loc, 4).text = \
                    '{0:.2f}'.format((ratio2_ctrl[0][i][1][0] +
                                      ratio2_ctrl[1][i][1][0]) / 2.)
                t.cell(i * 3 + loc, 5).text = \
                    '{0:.2f}'.format((ratio2_cal[0][i] +
                                      ratio2_cal[1][i]) / 2.)

            track += 1

    doc.save(fname)

    return

#=======================================================================

if __name__ == "__main__":

    # define the argparse settings to read run set up file
    description = "Plot all the different site fluxes & soil variations for a \
                   given project folder"
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument("project", nargs='+', help='projects where output are')
    args = parser.parse_args()

    projects = []

    for arg in vars(args):

        for attr in getattr(args, arg):

            projects += [attr]

    sites = ['Hyytiala', 'Soroe', 'Loobos', 'Hesse', 'Parco', 'Puechabon',
             'Rocca1', 'Rocca2', 'ElSaler1', 'Espirra']

    main(projects)


import socket
#import threading
from .hashableplayermove import HashablePlayerMove
import atexit

PORT = 51894

class RemoteAI:
	class error(Exception):
		pass

	def __init__(self, host):
		self.host = host
		self.socket = None

	def _send(self, s):
		b = bytes(s,"ascii")
		totalsent = 0
		while totalsent < len(b):
			sent = self.socket.send(b[totalsent:])
			if sent == 0:
				raise RemoteAI.error("Disconnected from server")
			totalsent = totalsent + sent

	def _recv(self):
		buf = bytearray()
		while True:
			c = self.socket.recv(1)
			if c == b'':
				raise RemoteAI.error("Disconnected from server")
			if c == b'\n':
				s = str(buf, "ascii")
				return s
			if c != b'\r':
				buf.append(c[0])

	def _recvAck(self):
		r = self._recv()
		if r != "ack":
			raise RemoteAI.error("Received a bad ACK: "+r)

	def connect(self, me, walls, locations):
		try:
			self.socket = socket.create_connection((self.host,PORT), timeout=9.5)
			#self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
			atexit.register(self.close)
			self.me = me

			self._send("{} {} ".format(me, walls))
			for i in locations:
				if i:
					self._send("{},{} ".format(i[0], i[1]))
				else:
					self._send("inv ")
			self._send("\n")
			self._recvAck()
			return True
		except OSError:
			return False

	def sendMove(self, move):
		if move.move:
			self._send("m {} {},{}\n".format(move.playerId, move.r2, move.c2))
		else:
			self._send("w {} {},{} {},{}\n".format(move.playerId, move.r1, move.c1, move.r2, move.c2))
		self._recvAck()

	def sendInvalidate(self, plyid):
		self._send("i {}\n".format(plyid))
		self._recvAck()

	def getMove(self):
		self._send("g\n")

		typ, loc1, loc2 = self._recv().split()
		r1,c1 = loc1.split(",")
		r2,c2 = loc2.split(",")
		if typ == "m":
			return HashablePlayerMove(self.me, True, int(r1), int(c1), int(r2), int(c2))
		elif typ == "w":
			return HashablePlayerMove(self.me, False, int(r1), int(c1), int(r2), int(c2))
		else:
			raise RemoteAI.error("Invalid data returned from getMove: "+str(strresult))

	def getAdjacent(self, r,c):
		self._send("adj {},{}\n".format(r,c))

		strresult = self._recv().split()
		result = []
		for i in strresult:
			r,c = i.split(",")
			result.append((int(r), int(c)))
		return result

	def getPath(self, r1,c1, r2,c2):
		self._send("path {},{} {},{}\n".format(r1,c1, r2,c2))

		strresult = self._recv().split()
		result = []
		for i in strresult:
			r,c = i.split(",")
			result.append((int(r), int(c)))
		return result

	def close(self):
		if self.socket:
			self.socket.shutdown(socket.SHUT_RDWR)
			self.socket.close()
			self.socket = None


import logging
import os
import tempfile
import webbrowser
from typing import List

from whylogs.core import DatasetProfile
from whylogs.util.protobuf import message_to_json

_MY_DIR = os.path.realpath(os.path.dirname(__file__))

logger = logging.getLogger(__name__)

def profile_viewer(profiles: List[DatasetProfile] = None, output_path=None) -> str:
    """
    open a profile viewer loader on your default browser
    """
    try:
        from pybars import Compiler
    except ImportError as e:
        Compiler = None
        logger.debug(str(e))
        logger.debug("Unable to load pybars; install pybars3 to load profile from directly from the current session ")

        index_path = os.path.abspath(os.path.join(_MY_DIR, os.pardir, "viewer", "index.html"))
        webbrowser.open_new_tab(f"file:{index_path}#")
        return None

    # create json output from profiles
    if profiles:
        if len(profiles) > 1:
            logger.warning("More than one profile not implemented yet, default to first profile in the list ")
        profile_jsons = [message_to_json(each_prof.to_summary()) for each_prof in profiles]
    else:
        index_path = os.path.abspath(os.path.join(_MY_DIR, os.pardir, "viewer", "index.html"))
        webbrowser.open_new_tab(f"file:{index_path}#")
        return None

    index_path = os.path.abspath(os.path.join(_MY_DIR, os.pardir, "viewer", "index-hbs-cdn-all-in.html"))

    with open(index_path, "r") as file_with_template:
        source = file_with_template.read()

    # compile templated files
    compiler = Compiler()
    template = compiler.compile(source)
    # replace handlebars for json profiles
    output_index = template({"profile_from_whylogs": profile_jsons[0]})

    if not output_path:
        output_path = tempfile.mkstemp(suffix=".html")[1]
    else:
        output_path = os.path.abspath(os.path.expanduser(output_path))
    with open(output_path, "w") as f:
        f.write(output_index)

    webbrowser.open_new_tab(f"file:{output_path}#")
    return output_path


import sys
import copy
from collections import deque
input = sys.stdin.readline
#아이디어
# 그리디, 종료시간 기준 오름차순 정렬
# while문으로 포인터 하나 설정해서 currEnd보다 작으면 pass, 크면 그거 넣기
# 시간복잡도
# 선형시간
# 변수 사용 계획
# count , i
# n = int(input()) # 계단 갯수
n = int(input())
meetings = []
for _ in range(n):
    meetings.append(list(map(int,input().split())))
meetings.sort(key=lambda x : (x[1],x[0]))

def solution (meetings,n) :
    ans = []
    for x in meetings:
        if not ans :
            ans.append(x)
        else :
            if ans[-1][1] <= x[0]:
                ans.append(x)
    return len(ans)
print(solution(meetings,n))


def count_uniques(nums):
    """
    :type nums: List[int]
    :rtype: int

    Now we need to change this to not count total unique values
    But rather return a new array of unique elements.
    """
    if (nums == None or len(nums) == 0):
        return []
    totalUniqueCount = 0
    newArr = []
    for index, element in enumerate(nums):
        if index == 0:
            totalUniqueCount += 1
            newArr.append(nums[index])
            continue
        if element in newArr:
            continue
        else:
            totalUniqueCount += 1
            newArr.append(nums[index])
            continue

    return newArr, totalUniqueCount

print(count_uniques([0,0,1,1,1,2,2,3,3,4]))

# ----------- PSEUDO CODE -------------  #

# if the arr is empty
    # return 0
# for every element in the given arr of numbers
    # if its the first element
        # append it to the new arr
        # increment unique count
        # continue
    # if element is in our new arr
        # continue
    # else
        # increment unique count
        # append it to the new arr
        # continue


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
import heapq as hq
class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        new=[]

        for i in (lists):
            while i!=None:
                new.append(i.val)
                i=i.next
        print(new)
        hq.heapify(new)
        print(new)
        head=None
        if len(new)==0:
            return None
        while len(new)!=0:
            news=ListNode(hq.heappop(new),head)
            head=news
        heads=None
        current=news
        while current!=None:
            nexts=current.next
            current.next=heads
            heads=current
            current=nexts
        current=heads
        return current


import pandas as pd
import json
import math

def calculate_mean(data):
    sum = 0
    len1 = 0
    for val in data:
        sum += val
        len1 += 1
    mean = sum/len1
    return mean

data = pd.read_csv("./Datasets/salary.csv")
header = data.columns.values
# print(data[header[0]])
# print(data["Years of Experience"])

X = data[header[0]]
Y = data[header[1]]
X = X.truncate(0,4899)
Y = Y.truncate(0,4899)
X_mean = calculate_mean(X)
Y_mean = calculate_mean(Y)
upper = 0
lower = 0
for index in range(len(X)):
    upper += (X[index] - X_mean) * (Y[index]-Y_mean)
    lower += math.pow(X[index]-X_mean, 2)
m = upper/lower
c = Y_mean - (m * X_mean)

trained_data = {}
trained_data['m'] = m
trained_data['c'] = c
trained_data["Y_mean"] = Y_mean
with open('./trained/trained_data.txt', 'w') as file:
    file.write(json.dumps(trained_data))


import torch
import numpy as np
from dataloader import PointCloudMNISTdataset, collate_graphs
from model import Net
import sys
from torch.utils.data import Dataset, DataLoader

def evaluate_on_dataset(path_to_ds=None,imin=-1,imax=-1):

	data = PointCloudMNISTdataset(path_to_ds)
	if imin == -1:
		imin = int(len(data)*0.9)
	if imax == -1:
		imax = len(data)
	test_ds = torch.utils.data.Subset(data,range(imin,imax))

	dataloader = DataLoader(test_ds,batch_size=1,collate_fn=collate_graphs)

	net = Net()

	state_dict = torch.load('trained_model.pt',map_location=torch.device('cpu'))
	net.load_state_dict(state_dict)

	total = 0
	correct = 0

	if torch.cuda.is_available():
		net.cuda()

	net.eval()

	n_batches = 0
	with torch.no_grad():
		for batched_g,y in dataloader:
			n_batches+=1

			if torch.cuda.is_available():
				batched_g = batched_g.to(torch.device('cuda'))
				y = y.cuda()
			pred = net(batched_g)

			pred = torch.argmax(pred,dim=1)

			correct+=len(torch.where(pred==y)[0])
			total+=len(y)

	return correct/total

if __name__ == "__main__":

	accuracy = evaluate_on_dataset(sys.argv[1])

	print(accuracy)


#
# @lc app=leetcode.cn id=455 lang=python3
#
# [455] 分发饼干
#

# @lc code=start
class Solution:
    def findContentChildren(self, g: List[int], s: List[int]) -> int:
        s.sort()
        g.sort()
        res = 0
        # 优先匹配胃口小的
        for i in range(len(s)):
            if res < len(g) and s[i] >= g[res]:
                res += 1
        return res
# @lc code=end


'''
This is the entry-point to the cli application.
'''
import click

from clokta.role_assumer import RoleAssumer

@click.command()
@click.version_option()
@click.option('--verbose', '-v', is_flag=True, help='Show detailed')
@click.option('--profile', '-p', required=True, help='Configuration profile')
def assume_role(profile, verbose=False):
    ''' Click point of entry '''
    assumer = RoleAssumer(profile=profile, verbose=verbose)
    assumer.assume_role()


#!/usr/bin/env python3

import psycopg2
from datetime import datetime

def get_data(cur, sql):

    cur.execute(sql)
    result = cur.fetchall()
    return (result)

def get_three_most_popular_articles(cur):

    """
    Input parameter: cursor of the database session
    Return parameter: None

    Query 1:
    What are the most popular three articles of all time
    """
    sql = "with log_detail as (select substr(path,10) path,id from log"\
        + " where length(path)>5)"   \
        + " ,art_det as (select a.title,det.id"\
        + " from log_detail det left join articles a "\
        + "on det.path = a.slug)"\
        + " select title,count(id) as access_count from "\
        + "art_det group by title"\
        + " order by 2 desc limit 3"
    res = get_data(cur, sql)
    print("The result of the most popular three articles of all time:\n")
    for rw in res:
        print("\"{0}\" --- {1} views".format(rw[0], rw[1]))

def get_most_popular_author(cur):

    """
    Input parameter: cursor of the database session
    Return parameter: None

    Query 2:
    What are the most popular articles author of all time
    """
    sql = "select a.name ,a.count_views from ("\
        + "select name ,count(id) as count_views "\
        + "from v_author_articles_access"\
        + " group by name order by 2 desc) a"
    res = get_data(cur, sql)
    print("The result of the most popular articles author of all time:\n")
    for rw in res:
        print("{} --- {} views".format(rw[0], rw[1]))

def get_days_with_onepc_failed(cur):

    """
    Input parameter: cursor of the database session
    Return parameter: None

    Query 3:
    On which days did more than 1% of requests lead to errors?
    """
    sql = "with day_rec as (select date_trunc('day', time) "\
        + "as days, status,id from log)"\
        + ",total as (select days,count(id) as total from "\
        + "day_rec group by days),"\
        + "error_total as (select days,count(id) as "\
        + "err_total from day_rec where status ="\
        + "'404 NOT FOUND' group by days ),"\
        + "err_percent as ("\
        + "select a.days,a.total,b.err_total,"\
        + "((b.err_total*1.00)/(a.total*1.00))*100.0 as "\
        + "percent from "\
        + "total a join error_total b on a.days = b.DAYS)"\
        + " select days ,round(percent,1) as err_txt "\
        + "from err_percent where percent>1"
    res = get_data(cur, sql)
    print("On which days did more than 1% of requests lead to errors:\n")
    for rw in res:
        print("{} --- {}% errors".format(
            datetime.strftime(rw[0], '%B %d, %Y'), rw[1]))

def main():

    conn = psycopg2.connect("dbname=news")
    cur = conn.cursor()
    get_three_most_popular_articles(cur)
    print("\n")
    get_most_popular_author(cur)
    print("\n")
    get_days_with_onepc_failed(cur)
    conn.close()

if __name__ == "__main__":
    main()


import sys
import argparse
from os import mkdir, getpid
from os.path import isfile, isdir
import logging
import json
from datetime import date

if not isdir("./logs"):
    try:
        mkdir("./logs")
    except Exception as e:
        print("Failed to make log dir")

logfile = './logs/TrapHandiCap.log'

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s | %(levelname)s | %(funcName)s | %(module)s | %(message)s')

file_handler = logging.FileHandler(logfile)
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(formatter)

logger.addHandler(file_handler)

logger.info("Logging started...")

class Calculator:
    """def __init__(self) -> None:
        self.number_of_scores = args["scores"]
        self.max_average = 24.00
        self.average = None
        self.handicap = None
        #self.data_file = self.init_data
        if not isfile(args["file"]):
            open(args["file"], 'x')
        self.data_file = args["file"]
        self.scores = self.load_data(self.data_file)"""
    def __init__(self) -> None:
        self.max_average = 24.00
        self.average = None
        self.handicap = None

    def __str__(self) -> str:
        if self.average != None or self.handicap != None:
            return f'Calculations({self.average}, {self.handicap})'
        else:
            return "No Calculation"

    def load_data(self, data_file) -> dict:
        try:
            file = open(data_file, 'r')
        except OSError as e:
            #logger.exception("Failed to read file " + str(data_file))
            print("Failed to read file " + str(data_file))
            sys.exit()
        data = json.load(file)
        #logger.info("Data loaded...")
        return data

    def sort_data(self) -> None:
        pass

    def calculate_average(self, scores: tuple) -> None:
        if len(scores) < self.number_of_scores:
            # If there are less than
            self.average = 0
            #logger.debug("The number of scores to calculate the average was less than number of scores to be used to calculate")
        else:
            # Calculate the average
            self.average = sum(scores) / len(scores)

    def calculate_handicap(self) -> None:
        if self.average > self.max_average:
            self.handicap = 0
        else:
            h = self.max_average - self.average
            self.handicap = round(h)

    def write_to_file(self, new_data: dict) -> None:
        with open(self.data_file, 'r+') as file:
            file_data = json.load(file)
            file_data['Scores'].append(new_data)
            file.seek(0)
            json.dump(file_data, file, indent=4)


# https://leetcode.com/problems/missing-number

from typing import List

class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        res = 0
        for num in nums:
            res ^= num
        for num in range(len(nums) + 1):
            res ^= num
        return res

    def missingNumber_gauss(self, nums: List[int]) -> int:
        # res = sum(range(len(nums) + 1))         # O(n)
        res = (len(nums) * (len(nums) + 1)) // 2  # O(1)
        res -= sum(nums)
        return res


a, b, c = [float(i) for i in input().split()]

if a >= b and a >= c:
    maior = a
    meio = b
    menor = c
elif b >= a and b >= c:
    maior = b
    meio = a
    menor = c
else:
    maior = c
    meio = a
    menor = b

if maior >= meio + menor:
    print("NAO FORMA TRIANGULO")
else:
    if (maior ** 2 == meio ** 2 + menor ** 2):
        print("TRIANGULO RETANGULO")

    if maior ** 2 > meio ** 2 + menor ** 2:
        print("TRIANGULO OBTUSANGULO")

    if maior ** 2 < meio ** 2 + menor ** 2:
        print("TRIANGULO ACUTANGULO")

    if maior == meio and meio == menor and maior == menor:
        print("TRIANGULO EQUILATERO")

    if (maior == meio and maior != menor and menor != meio) or (meio == menor and maior != meio and maior != menor) or (maior == menor and menor != meio and maior != meio):
        print("TRIANGULO ISOSCELES")


import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_ID, CONF_PIN, \
    CONF_RESOLUTION, CONF_UNIT_OF_MEASUREMENT, UNIT_CELSIUS, \
    CONF_ICON, ICON_THERMOMETER, CONF_ACCURACY_DECIMALS

MULTI_CONF = True
AUTO_LOAD = ['sensor']

CONF_ONE_WIRE_ID = 'one_wire_id'
CONF_AUTO_SETUP_SENSORS = 'auto_setup_sensors'
CONF_SENSOR_NAME_TEMPLATE = 'sensor_name_template'
SENSOR_NAME_TEMPLATE_DEFAULT = '%s.%s'

dallas_ns = cg.esphome_ns.namespace('dallas')
DallasComponent = dallas_ns.class_('DallasComponent', cg.PollingComponent)
ESPOneWire = dallas_ns.class_('ESPOneWire')

CONFIG_SCHEMA = cv.Schema({
    cv.GenerateID(): cv.declare_id(DallasComponent),
    cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire),
    cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
    cv.Optional(CONF_AUTO_SETUP_SENSORS, default=False): cv.boolean,
    cv.Optional(CONF_SENSOR_NAME_TEMPLATE, default=SENSOR_NAME_TEMPLATE_DEFAULT): cv.string_strict,
    cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
    cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_CELSIUS): cv.string_strict,
    cv.Optional(CONF_ICON, default=ICON_THERMOMETER): cv.icon,
    cv.Optional(CONF_ACCURACY_DECIMALS, default=1): cv.int_,
}).extend(cv.polling_component_schema('60s'))

def to_code(config):
    pin = yield cg.gpio_pin_expression(config[CONF_PIN])
    one_wire = cg.new_Pvariable(config[CONF_ONE_WIRE_ID], pin)
    var = cg.new_Pvariable(config[CONF_ID], one_wire)
    if CONF_AUTO_SETUP_SENSORS in config:
        cg.add(var.set_auto_setup_sensors(config[CONF_AUTO_SETUP_SENSORS]))
    if CONF_SENSOR_NAME_TEMPLATE in config:
        cg.add(var.set_sensor_name_template(config[CONF_SENSOR_NAME_TEMPLATE]))
    if CONF_RESOLUTION in config:
        cg.add(var.set_resolution(config[CONF_RESOLUTION]))
    if CONF_UNIT_OF_MEASUREMENT in config:
        cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
    if CONF_ICON in config:
        cg.add(var.set_icon(config[CONF_ICON]))
    if CONF_ACCURACY_DECIMALS in config:
        cg.add(var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
    yield cg.register_component(var, config)


"""
Implementation of Linear Programming IRL by Ng and Russell, 2000
(c) 2018 Aaron Snoswell
"""

import math
import copy
import numpy as np

from cvxopt import matrix, solvers

def lp_irl(T, gamma, l1, *, Rmax=1.0):
    """
    Implements Linear Programming IRL by NG and Russell, 2000

    Given a transition matrix T(s, a, s') encoding a stationary, deterministic
    policy and a discount factor gamma finds a reward vector R(s) for which
    the policy is optimal.

    This method uses the Linear Programming IRL algorithm by Ng and Russell,
    2000 (http://ai.stanford.edu/~ang/papers/icml00-irl.pdf). See
    https://www.inf.ed.ac.uk/teaching/courses/rl/slides17/8_IRL.pdf for a more
    accessible overview.

    @param T - A sorted transition matrix T(s, a, s') encoding a stationary
        deterministic policy. The structure of T must be that the 0th action
        T[:, 0, :] corresponds to the expert policy, and T[:, i, :], i != 0
        corresponds to the ith non-expert action at each state
    @param gamma - The expert's discount factor
    @param l1 - L1 regularization weight for LP optimisation objective
        function
    @param Rmax - Maximum reward value

    @return A reward vector for which the given policy is optimal
    @return A result object from the optimiser

    TODO: Adjust L1 norm constraint generation to allow negative rewards in
    the final vector.
    """

    # Measure size of state and action sets
    n = T.shape[0]
    k = T.shape[1]

    # Compute the discounted transition matrix term
    T_disc_inv = np.linalg.inv(np.identity(n) - gamma * T[:, 0, :])

    # Formulate the linear programming problem constraints
    # NB: The general form for adding a constraint looks like this
    # c, A_ub, b_ub = f(c, A_ub, b_ub)

    # Prepare LP constraint matrices
    c = np.zeros(shape=[1, n], dtype=float)
    A_ub = np.zeros(shape=[0, n], dtype=float)
    b_ub = np.zeros(shape=[0, 1])

    def add_optimal_policy_constraints(c, A_ub, b_ub):
        """
        Add constraints to ensure the expert policy is optimal
        This will add (k-1) * n extra constraints
        """
        for i in range(k - 1):
            constraint_rows = -1 * (T[:, 0, :] - T[:, i, :]) @ T_disc_inv
            A_ub = np.vstack((A_ub, constraint_rows))
            b_ub = np.vstack((b_ub, np.zeros(shape=[constraint_rows.shape[0], 1])))
        return c, A_ub, b_ub

    def add_costly_single_step_constraints(c, A_ub, b_ub):
        """
        Augment the optimisation objective to add the costly-single-step
        degeneracy heuristic
        This will add n extra optimisation variables and (k-1) * n extra
        constraints
        NB: Assumes the true optimisation variables are first in the objective
        function
        """

        # Expand the c vector add new terms for the min{} operator
        c = np.hstack((c, -1 * np.ones(shape=[1, n])))
        css_offset = c.shape[1] - n
        A_ub = np.hstack((A_ub, np.zeros(shape=[A_ub.shape[0], n])))

        # Add min{} operator constraints
        for i in range(k - 1):
            # Generate the costly single step constraint terms
            constraint_rows = -1 * (T[:, 0, :] - T[:, i, :]) @ T_disc_inv

            # constraint_rows is nxn - we need to add the min{} terms though
            min_operator_entries = np.identity(n)

            # And we have to make sure we put the min{} operator entries in
            # the correct place in the A_ub matrix
            num_padding_cols = css_offset - n
            padding_entries = np.zeros(shape=[constraint_rows.shape[0], num_padding_cols])
            constraint_rows = np.hstack((constraint_rows, padding_entries, min_operator_entries))

            # Finally, add the new constraints
            A_ub = np.vstack((A_ub, constraint_rows))
            b_ub = np.vstack((b_ub, np.zeros(shape=[constraint_rows.shape[0], 1])))

        return c, A_ub, b_ub

    def add_l1norm_constraints(c, A_ub, b_ub, l1):
        """
        Augment the optimisation objective to add an l1 norm regularisation
        term z += l1 * ||R||_1
        This will add n extra optimisation variables and 2n extra constraints
        NB: Assumes the true optimisation variables are first in the objective
        function
        """

        # We add an extra variable for each each true optimisation variable
        c = np.hstack((c, l1 * np.ones(shape=[1, n])))
        l1_offset = c.shape[1] - n

        # Don't forget to resize the A_ub matrix to match
        A_ub = np.hstack((A_ub, np.zeros(shape=[A_ub.shape[0], n])))

        # Now we add 2 new constraints for each true optimisation variable to
        # enforce the absolute value terms in the l1 norm
        for i in range(n):

            # An absolute value |x1| can be enforced via constraints
            # -x1 <= 0             (i.e., x1 must be positive or 0)
            #  x1 + -xe1 <= 0
            # Where xe1 is the replacement for |x1| in the objective
            #
            # TODO ajs 04/Apr/2018 This enforces that R must be positive or 0,
            # but I was under the impression that it was also possible to
            # enforce an abs operator without this requirement - e.g. see
            # http://lpsolve.sourceforge.net/5.1/absolute.htm
            constraint_row_1 = [0] * A_ub.shape[1]
            constraint_row_1[i] = -1
            A_ub = np.vstack((A_ub, constraint_row_1))
            b_ub = np.vstack((b_ub, [[0]]))

            constraint_row_2 = [0] * A_ub.shape[1]
            constraint_row_2[i] = 1
            constraint_row_2[l1_offset + i] = -1
            A_ub = np.vstack((A_ub, constraint_row_2))
            b_ub = np.vstack((b_ub, [[0]]))

        return c, A_ub, b_ub

    def add_rmax_constraints(c, A_ub, b_ub, Rmax):
        """
        Add constraints for a maximum R value Rmax
        This will add n extra constraints
        """
        for i in range(n):
            constraint_row = [0] * A_ub.shape[1]
            constraint_row[i] = 1
            A_ub = np.vstack((A_ub, constraint_row))
            b_ub = np.vstack((b_ub, Rmax))
        return c, A_ub, b_ub

    # Compose LP optimisation problem
    c, A_ub, b_ub = add_optimal_policy_constraints(c, A_ub, b_ub)
    c, A_ub, b_ub = add_costly_single_step_constraints(c, A_ub, b_ub)
    c, A_ub, b_ub = add_rmax_constraints(c, A_ub, b_ub, Rmax)
    c, A_ub, b_ub = add_l1norm_constraints(c, A_ub, b_ub, l1)

    # Solve for a solution

    # NB: cvxopt.solvers.lp expects a 1d c vector
    from cvxopt import matrix, solvers
    res = solvers.lp(matrix(c[0, :]), matrix(A_ub), matrix(b_ub))

    def normalize(vals):
        """
        Helper function to normalize a vector to the range (0, 1)
        """
        min_val = np.min(vals)
        max_val = np.max(vals)
        return (vals - min_val) / (max_val - min_val)

    # Extract the true optimisation variables and re-scale
    rewards = Rmax * normalize(res['x'][0:n]).T

    return rewards, res

if __name__ == "__main__":

    def build_sorted_transition_matrix(S, A, T, pi):
        """
        Given a vector of states S, a vector of actions A, a transition matrix
        T(s-a, s') and a policy dictionary, builds a sorted transition matrix
        T(s, a, s'), where the 0th action T(:, 0, :) corresponds to the expert
        policy, and the ith action T(:, i, :), i!=0 corresponds to the ith non
        -expert action at each state
        """

        # Build the compact form Transition matrix
        n = len(S)
        k = len(A)

        # Helper function to get a transition probability
        si = lambda s: S.tolist().index(s)
        ai = lambda a: A.tolist().index(a)
        trans = lambda s1, a, s2: T[si(s1) * k + ai(a), si(s2)]

        Tfull = np.zeros(shape=[n, k, n])
        for s_from in S:
            # Build vector of actions, sorted with the expert one first
            expert_action = pi[s_from]
            sorted_actions = np.append([expert_action], np.delete(A, ai(expert_action)))
            for a in sorted_actions:
                for s_to in S:
                    Tfull[si(s_from), ai(a), si(s_to)] = trans(s_from, a, s_to)

        return Tfull

    # Sample problem for lp_irl
    T = build_sorted_transition_matrix(
        np.array(["s0", "s1", "s2"]),
        np.array(["b", "o"]),
        np.array([[0,    0.4, 0.6 ],
                  [0,    0,   1   ],
                  [0,    0,   1   ],
                  [0,    0,   1   ],
                  [1,    0,   0   ],
                  [1,    0,   0   ]]),
        {
            "s0": "b",
            "s1": "o",
            "s2": "o"
        }
    )

    # Try LP IRL
    print(T)
    rewards, _ = lp_irl(T, 0.9, l1=10)
    print(rewards)


import math

def quadratic(a,b,c):
    if not isinstance(a,(int,float)) and isinstance(b,(int,float)) and isinstance(c,(int,float)):
        raise TypeError('bad operand type')
    if a == 0:
        return 'a不能为0'
    n = float(b * b - 4 * a * c)
    if n < 0:
        return '方程无解'
    elif n == 0:
        x = int(-b/(2*a))
        return '方程的解为：x=%.1f'% x

    else:
        x1 = (-b + math.sqrt(n))/(2*a)
        x2 = (-b - math.sqrt(n))/(2*a)
        return '方程的解为：x1=%.1f,x2=%.1f'%(x1,x2)

print(quadratic(1,1,1))

def my_abs(x):
    if not isinstance(x, (int, float)):
        raise TypeError('bad operand type')
    if x >= 0:
        return x
    else:
        return -x

def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

n = my_abs(-20)
print(n)

x, y = move(100, 100, 60, math.pi / 6)
print(x, y)

# TypeError: bad operand type:
#my_abs('123')