'''
Simple driving script to run the Robot Dance model.
'''

print('Loading modules...')
import os
import os.path as path
from optparse import OptionParser
import pandas as pd
import numpy as np
from scipy.integrate import solve_ivp
import scipy.stats as stats
from timeit import default_timer as timer
from matplotlib import gridspec
from matplotlib.patches import Rectangle
from matplotlib.lines import Line2D
from matplotlib import cm
import pylab as plt
from pylab import rcParams
rcParams['figure.figsize'] = 14, 7
print('Loading modules... Ok!')

import prepare_data

class SimpleTimeSeries:
    """Simple time series of one or two steps.
    """
    def __init__(self, rhomin, rhomax, c0, c1, phi1, phi2, sigmaw, initialm1, initial0):
        self.rhomin, self.rhomax = rhomin, rhomax
        self.C0, self.C1 = np.array([c0, 0]), np.array([c1, 0])
        self.A = np.array([[phi1, phi2], [1, 0]])
        self.sigmaw = sigmaw
        self.initial = np.array([initial0, initialm1])
        self.delta = rhomax - rhomin
        self.reset()

    def reset(self):
        self.t = 0
        self.theta = []
        self.Ak = np.array([[1.0, 0], [0, 1]])
        self.state = (self.initial - self.rhomin) / self.delta

    def iterate(self, random=False):
        self.t += 1
        self.theta.append(self.Ak[0, 0])
        self.Ak = self.A @ self.Ak
        self.state = self.C0 + self.C1*self.t + self.A @ self.state
        if random:
            self.state[0] += np.random.normal(scale=self.sigmaw)
        return self.rhomin + self.delta*self.state[0]

    def simulate(self, horizon, random=False):
        self.reset()
        simulation = np.array([self.iterate(random) for i in range(horizon)])
        return simulation

    def get_upper_bound(self, horizon, confidence_level):
        simulation = self.simulate(horizon)
        F1p = stats.norm.ppf(confidence_level)
        upper_bound = simulation[:]
        theta_norm = np.sqrt(np.cumsum(np.array(self.theta)**2))
        upper_bound = simulation + F1p*self.sigmaw*self.delta*theta_norm

        return simulation, upper_bound

    def check_upper_bound(self, horizon, confidence_level, n_runs=1000):
        simulation, upper_bound = self.get_upper_bound(horizon, confidence_level)
        random_sims = []
        n_samples = 0
        n_OK = 0
        for i in range(n_runs):
            simulation = self.simulate(horizon, True)
            random_sims.append(simulation)
            n_samples += horizon
            n_OK += (simulation <= upper_bound).sum()

        return n_OK, n_samples

# To use PyJulia
print('Loading Julia library...')
from julia.api import Julia
jl = Julia(compiled_modules=False)
from julia import Main as Julia
print('Loading Julia library... Ok!')
print('Loading Robot-dance Julia module...')
Julia.eval('include("robot_dance.jl")')
print('Loading Robot-dance Julia module... Ok!')

def get_options():
    '''Get options with file locations from command line.
    '''
    parser = OptionParser()
    parser.add_option("--basic_parameters", dest="basic_prm",
                      default=path.join("data", "basic_parameters.csv"),
                      help="Basic parameters of the SEIR model [default: %default]")
    parser.add_option("--cities_data", dest="cities_data",
                      default=path.join("data", "cities_data.csv"),
                      help="Population and initial state of the cities [default: %default]")
    parser.add_option("--pre_cities_data", dest="pre_cities_data",
                      default=path.join("data", "pre_cities_data.csv"),
                      help="Population and initial state of the cities [default: %default]")
    parser.add_option("--mobility_matrix", dest="mobility_matrix",
                      default=path.join("data", "mobility_matrix.csv"),
                      help="Mobility information [default: %default]")
    parser.add_option("--target", dest="target",
                      default=path.join("data", "target.csv"),
                      help="Maximal infected allowed [default: %default]")
    parser.add_option("--hammer_data", dest="hammer_data",
                      default=path.join("data", "hammer_data.csv"),
                      help="Hammer duration and level [default: %default]")
    options, dummy_args = parser.parse_args()
    return options

def read_data(options, verbosity=0):
    '''Read data from default files and locations.
    '''
    if path.exists(options.basic_prm):
        basic_prm = pd.read_csv(options.basic_prm, header=None, index_col=0, squeeze=True)
    else:
        if verbosity > 0:
            print("The file basic_parameters.csv is missing.")
            print("Using one with the default values from the report.")
        basic_prm = prepare_data.save_basic_parameters()

    if path.exists(options.cities_data):
        cities_data = pd.read_csv(options.cities_data, index_col=0)
    else:
        cities_data = prepare_data.compute_initial_condition_evolve_and_save(
            basic_prm, None, [], 0, 1, options.pre_cities_data)

    if path.exists(options.target):
        target = pd.read_csv(options.target, index_col=0)
    else:
        if verbosity > 0:
            print("Target for infected does not exits, usint 1%.")
        ncities, ndays = len(cities_data.index), int(basic_prm["ndays"])
        target = 0.01*np.ones((ncities, ndays))
        target = prepare_data.save_target(cities_data, target)

    if path.exists(options.mobility_matrix):
        mob_matrix = pd.read_csv(options.mobility_matrix, index_col=0)
        assert np.alltrue(mob_matrix.index == cities_data.index), \
            "Different cities in cities data and mobility matrix."
    else:
        ncities = len(cities_data)
        mob_matrix = pd.DataFrame(data=np.zeros((ncities, ncities)),
            index=cities_data.index, columns=cities_data.index)
        mob_matrix["out"] = np.zeros(ncities)

    if path.exists(options.hammer_data):
        if verbosity > 0:
            print('Reading hammer data...')
        hammer_data = pd.read_csv(options.hammer_data, index_col=0)
        ncities = len(cities_data)
        assert len(hammer_data.index) == ncities, \
            "Different cities in cities data and hammer data"
        if verbosity > 0:
            print('Reading hammer data... Ok!')
    else:
        if verbosity > 0:
            print('Hammer data not found. Using default values')
        hammer_data = prepare_data.save_hammer_data(cities_data)

    return basic_prm, cities_data, mob_matrix, target, hammer_data

def prepare_optimization(basic_prm, cities_data, mob_matrix, target, hammer_data,
    force_dif=1, pools=None, verbosity=0, test_budget=0, tests_off=[],
    tau=3, test_efficacy=0.8, daily_tests=0, proportional_tests=False):
    ncities, ndays = len(cities_data.index), int(basic_prm["ndays"])
    if force_dif is 1:
        force_dif = np.ones((ncities, ndays))

    # Chage ratios in matrix Mt to be in respect to the origin
    population = cities_data["population"].values
    Mt = mob_matrix.values[:,:-1]
    Mt = (Mt.T).copy()
    for c in range(ncities):
        for k in range(ncities):
            Mt[k, c] *= population[k]/population[c]

    Julia.tinc = basic_prm["tinc"]
    Julia.tinf = basic_prm["tinf"]
    Julia.alternate = basic_prm["alternate"]
    Julia.rep = basic_prm["rep"]
    Julia.s1 = cities_data["S1"].values
    Julia.e1 = cities_data["E1"].values
    Julia.i1 = cities_data["I1"].values
    Julia.r1 = cities_data["R1"].values
    Julia.availICU = cities_data["icu_capacity"]
    Julia.time_icu = basic_prm["time_icu"]
    Julia.rho_icu_ts = cities_data.iloc[:, 7:-1].values
    Julia.population = population
    Julia.out = mob_matrix["out"].values
    Julia.M = mob_matrix.values[:, :-1]
    Julia.Mt = Mt
    Julia.ndays = ndays
    Julia.target = target.values
    Julia.min_level = basic_prm["min_level"]
    Julia.force_dif = force_dif
    Julia.hammer_duration = hammer_data["duration"].values
    Julia.hammer_level = hammer_data["level"].values
    Julia.verbosity = verbosity
    Julia.window = basic_prm["window"]
    Julia.test_budget = test_budget
    Julia.tests_off = tests_off
    Julia.tau = tau
    Julia.test_efficacy = test_efficacy
    Julia.daily_tests = daily_tests
    Julia.proportional_tests = proportional_tests
    if pools is None:
        Julia.eval("pools = [[c] for c in 1:length(s1)]")
    else:
        Julia.pools = pools
    Julia.eval("""
        prm = SEIR_Parameters(tinc, tinf, rep, ndays, s1, e1, i1, r1, alternate,
            availICU, time_icu, rho_icu_ts, window, out, sparse(M), sparse(Mt))
        m = window_control_multcities(prm, population, target, force_dif, hammer_duration,
            hammer_level, min_level, pools, verbosity, test_budget, tests_off,
            tau, test_efficacy, daily_tests, proportional_tests);
    """)

    # Check if there is a ramp parameter (delta_rt_max)
    # If so, add ramp constraints to the model
    if 'delta_rt_max' in basic_prm:
        Julia.delta_rt_max = basic_prm["delta_rt_max"]
        Julia.verbosity = verbosity
        Julia.eval("""
            m = add_ramp(m, prm, hammer_duration, delta_rt_max, verbosity)
        """)

def compute_need_icu(ts_parameters, basic_prm):
    F1p = stats.norm.ppf(ts_parameters[-2])
    time_series = SimpleTimeSeries(*ts_parameters[:-2])
    need_icu = [time_series.iterate() for i in range(int(basic_prm["ndays"]))]
    # TODO: not sure p should be hard coded
    theta = np.array(time_series.theta).copy()
    for i in range(int(basic_prm["ndays"])):
        need_icu[i] += F1p*time_series.sigmaw*time_series.delta*np.sqrt((theta[:i + 1]**2).sum())
    return need_icu

def find_feasible_hammer(basic_prm, cities_data, mob_matrix, target, hammer_data,
    out_file=None, incr_all=False, verbosity=0):
    """Find hammer durations for each city such that the optimization problem will
    (hopefully) be feasible
    """

    if verbosity >= 1:
        print('Checking if initial hammer is long enough...')
    ncities, ndays = len(cities_data.index), int(basic_prm["ndays"])

    M = mob_matrix.values[:,:-1]
    out = mob_matrix["out"].values

    tspan = (0,ndays)
    teval = np.arange(0, ndays + 0.01, 1) # 1 day discretization
    y0 = cities_data["S1"].values
    y0 = np.append(y0, cities_data["E1"].values)
    y0 = np.append(y0, cities_data["I1"].values)
    y0 = np.append(y0, cities_data["R1"].values)

    min_rt = basic_prm["min_level"]

    # Hammer data
    hammer_duration = hammer_data["duration"].values
    hammer_level = hammer_data["level"].values

    iter = 0
    feas_model = False
    t_total1 = timer()
    while feas_model == False:
        t_solve1 = timer()
        sol = solve_ivp(_robot_dance_hammer, tspan, y0, t_eval=teval, args=(
                basic_prm["tinc"], basic_prm["tinf"], ncities, M, out, min_rt,
                hammer_level, hammer_duration
            )
        )
        t_solve2 = timer()
        if verbosity >= 2:
            print(f'Time to simulate: {t_solve2-t_solve1:.2g}')

        tsim = sol.t
        isim = sol.y[2*ncities:3*ncities]

        # Get the max number of infected after hammer (usually it's the number
        # of infected immediately after hammer, but this might not work if
        # hammer_duration = 0) (so let's use a safer implementation)
        i_after_hammer = np.zeros(ncities)
        target_hammer = np.zeros(ncities)
        for c in range(ncities):
            need_icu = compute_need_icu(cities_data.iloc[c, 7:], basic_prm)

            target_hammer[c] = 0.7*target.iloc[c][hammer_duration[c] + 1]*cities_data.iloc[c]["icu_capacity"]
            i_after_hammer[c] = basic_prm["time_icu"]*need_icu[hammer_duration[c]]*max(
                isim[c][hammer_duration[c] + 1:])/basic_prm["tinf"]

        feas_model = True
        for c in range(ncities):
            if i_after_hammer[c] > target_hammer[c]:
                if verbosity >= 2:
                    print(f'{cities_data.index[c]} violates number of infected after {hammer_duration[c]} days of hammer (level {hammer_level[c]}): Infected {i_after_hammer[c]:.2g} (target {target_hammer[c]})')
                feas_model = False
            else:
                if verbosity >= 2:
                    print(f'{cities_data.index[c]} is fine after {hammer_duration[c]} days of hammer (infected = {i_after_hammer[c]:.2e}, target = {target_hammer[c]:.2e})')

        if feas_model == False:
            # There is at least one city violating the target after hammer
            if incr_all == False: # Increase hammer_duration only for the city that violates the target the most
                c_i_max = np.argmax(i_after_hammer-target_hammer)
                if verbosity >= 2:
                    print(f'City most distant from target after hammer: {cities_data.index[c_i_max]}')
                if hammer_duration[c_i_max] == ndays:
                    raise ValueError(f'Impossible to get a feasible model (hammer_duration for {cities_data.index[c]} is equal to the simulation horizon). Try increasing ndays or decreasing hammer_level')
                hammer_duration[c_i_max] += basic_prm["window"]
                if verbosity >= 2:
                    print(f'Increasing hammer duration of {cities_data.index[c_i_max]} to {hammer_duration[c_i_max]} days')
            else: # Increase hammer_duration of all cities that violate the target
                for c in range(ncities):
                    if i_after_hammer[c] > target_hammer[c]:
                        if hammer_duration[c] == ndays:
                            raise ValueError(f'Impossible to get a feasible model (hammer_duration for {cities_data.index[c]} is equal to the simulation horizon). Try increasing ndays or decreasing hammer_level')
                        hammer_duration[c] += basic_prm["window"]
                        if verbosity >= 2:
                            print(f'Increasing hammer duration of {cities_data.index[c]} to {hammer_duration[c]} days')
            if verbosity >= 2:
                print('')
        iter += 1

    t_total2 = timer()
    if verbosity >= 1:
        print('')
        print(f'Number of iterations: {iter}')
        print(f'Total time: {t_total2-t_total1} s')
        print()
        print("Hammer data")
        print(hammer_data)

    if out_file is not None:
        if verbosity >= 1:
            print('Saving hammer data file')
        hammer_data.to_csv(out_file)

    if verbosity >= 1:
        print('Checking if initial hammer is long enough... Ok!')

def check_error_optim(basic_prm, cities_data, mob_matrix, dir_output, verbosity=0):
    """ Checks error between optimization and simulation
    """
    ncities, ndays = len(cities_data.index), int(basic_prm["ndays"])

    M = mob_matrix.values[:,:-1]
    out = mob_matrix["out"].values

    tspan = (1,ndays)
    teval = range(1,ndays+1)
    y0 = cities_data["S1"].values
    y0 = np.append(y0, cities_data["E1"].values)
    y0 = np.append(y0, cities_data["I1"].values)
    y0 = np.append(y0, cities_data["R1"].values)

    Julia.eval("s = value.(m[:s]); e = value.(m[:e]); i = value.(m[:i]); r = value.(m[:r])")
    Julia.eval("rt = expand(value.(m[:rt]), prm)")
    t_in = teval
    rt_in = Julia.rt
    if verbosity >= 1:
        print('Simulating robot-dance control...')
    sol = solve_ivp(_robot_dance_simul, tspan, y0, t_eval=teval, args=(basic_prm["tinc"], \
                                                                        basic_prm["tinf"], \
                                                                        ncities, \
                                                                        M, \
                                                                        out, \
                                                                        t_in, \
                                                                        rt_in))
    if verbosity >= 1:
        print('Simulating robot-dance control... Ok!')
    s_sim = sol.y[:ncities]
    e_sim = sol.y[ncities:2*ncities]
    i_sim = sol.y[2*ncities:3*ncities]
    r_sim = sol.y[3*ncities:]

    if verbosity >= 1:
        print('Plotting errors...')
    for (i,c) in enumerate(cities_data.index):
        fig = plt.figure()
        plt.plot(Julia.s[i], label="robot-dance")
        plt.plot(s_sim[i], label="simulation")
        plt.legend()
        plt.title(f'{c}, Susceptible')
        plt.savefig(f'{dir_output}/{c}_s.png')

        fig = plt.figure()
        plt.plot(Julia.e[i], label="robot-dance")
        plt.plot(e_sim[i], label="simulation")
        plt.legend()
        plt.title(f'{c}, Exposed')
        plt.savefig(f'{dir_output}/{c}_e.png')

        fig = plt.figure()
        plt.plot(Julia.i[i], label="robot-dance")
        plt.plot(i_sim[i], label="simulation")
        plt.legend()
        plt.title(f'{c}, Infected')
        plt.savefig(f'{dir_output}/{c}_i.png')

        fig = plt.figure()
        plt.plot(Julia.r[i], label="robot-dance")
        plt.plot(r_sim[i], label="simulation")
        plt.legend()
        plt.title(f'{c}, Removed')
        plt.savefig(f'{dir_output}/{c}_r.png')
    if verbosity >= 1:
        print('Plotting errors... Ok!')

    fig = plt.figure()
    for (i,c) in enumerate(cities_data.index):
        plt.plot(rt_in[i], label=c)
    plt.legend()
    plt.grid()
    plt.title('Control rt')
    plt.savefig(f'{dir_output}/rt.png')

    rt_diff = []
    for (i,c) in enumerate(cities_data.index):
        rt_diff.append(np.diff(rt_in[i]))

    fig = plt.figure()
    for (i,c) in enumerate(cities_data.index):
        plt.plot(rt_diff[i], label=c)
    plt.legend()
    plt.grid()
    plt.title('Diff rt')
    plt.savefig(f'{dir_output}/diff_rt.png')

    plt.show()

    if verbosity >= 1:
        print('Saving errors table...')
    df = pd.DataFrame(columns=['s_norm_1', 'e_norm_1', 'i_norm_1', 'r_norm_1', 's_norm_inf', 'e_norm_inf', 'i_norm_inf', 'r_norm_inf'], index=cities_data.index)
    for (i,c) in enumerate(cities_data.index):
        df.loc[c, 's_norm_1'] = np.linalg.norm(s_sim[i]-Julia.s[i], ord=1)
        df.loc[c, 'e_norm_1'] = np.linalg.norm(e_sim[i]-Julia.e[i], ord=1)
        df.loc[c, 'i_norm_1'] = np.linalg.norm(i_sim[i]-Julia.i[i], ord=1)
        df.loc[c, 'r_norm_1'] = np.linalg.norm(r_sim[i]-Julia.r[i], ord=1)
        df.loc[c, 's_norm_inf'] = np.linalg.norm(s_sim[i]-Julia.s[i], ord=np.inf)
        df.loc[c, 'e_norm_inf'] = np.linalg.norm(e_sim[i]-Julia.e[i], ord=np.inf)
        df.loc[c, 'i_norm_inf'] = np.linalg.norm(i_sim[i]-Julia.i[i], ord=np.inf)
        df.loc[c, 'r_norm_inf'] = np.linalg.norm(r_sim[i]-Julia.r[i], ord=np.inf)
    df.to_csv(f'{dir_output}/error_discretization.csv')
    if verbosity >= 1:
        print('Saving errors table... Ok!')

def _robot_dance_only_eqs(s,e,i,r,rt,tinc,tinf,ncities,M,out):
    """SEIR equations for the robot-dance model
    """
    alpha = 2/3

    enter = np.zeros(ncities)
    for c1 in range(ncities):
        for c2 in range(ncities):
            enter[c1] += M[c2,c1]*(1-i[c2])

    p_day = np.zeros(ncities)
    for c in range(ncities):
        p_day[c] = (1-out[c]) + out[c]*i[c] + enter[c]

    t1 = np.zeros(ncities)
    for c1 in range(ncities):
        for c2 in range(ncities):
            t1[c1] += rt[c2]*M[c1,c2]*s[c1]*i[c2]/p_day[c2]

    ds_day = -1/tinf * alpha * (rt * (1-out) * s * i / p_day + t1)
    ds_night = -1/tinf * (1-alpha) * (rt * s * i)
    ds = ds_day + ds_night

    de = -ds - 1/tinc*e
    di = 1/tinc*e - 1/tinf*i
    dr = 1/tinf*i

    dy = np.array([ds,de,di,dr]).flatten()
    return dy

def _robot_dance_simul(t,y,tinc,tinf,ncities,M,out, t_in, rt_in):
    """SEIR equations for the robot-dance model with control rt given by the optimization model
    """
    s = y[:ncities]
    e = y[ncities:2*ncities]
    i = y[2*ncities:3*ncities]
    r = y[3*ncities:]

    # Interpolate rt for each city for the current t
    rt = np.zeros(ncities)
    for c in range(ncities):
        rt[c] = np.interp(t, t_in, rt_in[c])

    dy = _robot_dance_only_eqs(s,e,i,r,rt,tinc,tinf,ncities,M,out)
    return dy

def _robot_dance_hammer(t,y,tinc,tinf,ncities,M,out,min_rt,hammer_level,hammer_duration):
    """SEIR equations for the robot-dance model with initial hammer and min_rt later
    # (used to get a hammer duration such that the optimization problem will be feasible)
    """
    s = y[:ncities]
    e = y[ncities:2*ncities]
    i = y[2*ncities:3*ncities]
    r = y[3*ncities:]

    rt = np.zeros(ncities)
    for c in range(ncities):
        if t <= hammer_duration[c]:
            # Enforce hammer in the initial period
            rt[c] = hammer_level[c]
        else:
            # Enforce min rt (not as strict as hammer) for the rest of horizon
            rt[c] = min_rt

    dy = _robot_dance_only_eqs(s,e,i,r,rt,tinc,tinf,ncities,M,out)
    return dy

def save_result(basic_prm, cities_data, target, filename):
    """Save the result of a run for further processing.
    """
    cities_names = cities_data.index
    n_cities = len(cities_names)
    Julia.eval("s = value.(m[:s]); e = value.(m[:e]); i = value.(m[:i]); r = value.(m[:r])")
    Julia.eval("rt = expand(value.(m[:rt]), prm)")
    n = len(Julia.s[0, :])
    Julia.eval("test = value.(m[:test])")
    df = []

    for i in range(n_cities):
        c = cities_names[i]
        df.append([c, "s"] + list(Julia.s[i, :]))
        df.append([c, "e"] + list(Julia.e[i, :]))
        df.append([c, "i"] + list(Julia.i[i, :]))
        df.append([c, "r"] + list(Julia.r[i, :]))
        df.append([c, "rt"] + list(Julia.rt[i, :]))
        df.append([c, "rel. test"] + list(Julia.test[i, :]))
        df.append([c, "test"] + list(Julia.test[i, :]*cities_data.loc[c, "population"]))

        # Information on ICU
        icu_capacity = cities_data.loc[c, "population"]*cities_data.loc[c, "icu_capacity"]
        df.append([c, "icu_capacity"] + list(icu_capacity*np.ones(n)))
        icu_target = icu_capacity * target.loc[c, :]
        df.append([c, "target_icu"] + list(icu_target))
        rho_icu = SimpleTimeSeries(*cities_data.iloc[i, 7:-2])
        confidence = cities_data.loc[c, "confidence"]
        mean_icu, upper_icu = rho_icu.get_upper_bound(n, confidence)
        df.append([c, "mean_rho_icu"] + list(mean_icu))
        df.append([c, "upper_rho_icu"] + list(upper_icu))
        mean_icu = cities_data.loc[c, "time_icu"] / basic_prm["tinf"] * mean_icu * cities_data.loc[c, "population"] * Julia.i[i, :]
        df.append([c, "mean_used_icu"] + list(mean_icu))
        upper_icu = cities_data.loc[c, "time_icu"] / basic_prm["tinf"] * upper_icu * cities_data.loc[c, "population"] * Julia.i[i, :]
        df.append([c, "upper_used_icu"] + list(upper_icu))

    df = pd.DataFrame(df, columns=["City", "Variable"] + list(range(len(Julia.s[0,:]))))
    df.set_index(["City", "Variable"], inplace=True)
    df.to_csv(filename)
    return df

def optimize_and_show_results(basic_prm, figure_file, data_file, cities_data, target, verbosity=0):
    """Optimize and save figures and data for further processing.
    """

    large_cities = cities_data.index
    population = cities_data["population"].values

    if verbosity >= 1:
        print('Solving Robot-dance...')

    Julia.eval("""
        optimize!(m)
        pre_rt = value.(m[:rt]); i = value.(m[:i])
        rt = expand(pre_rt, prm)
        test = value.(m[:test])
    """)

    if verbosity >= 1:
        print('Solving Robot-dance... Ok!')
        print("Total tests used ", end="")
        print((Julia.test.T*population).sum())

    bins = [0]
    bins.extend(plt.linspace(1.0, 0.95*basic_prm["rep"], 5))
    bins.append(basic_prm["rep"])

    stats = pd.DataFrame(index=large_cities)
    changes_rt = []
    for (i, c) in enumerate(large_cities):
        changes_rt.append(len(np.diff(Julia.rt[i]).nonzero()[0]) + 1)
    stats["Rt changes"] = changes_rt

    i_avg, max_i = [], []
    for (i, c) in enumerate(large_cities):
        maximum = 100*Julia.i[i, Julia.hammer_duration[i]:].max()
        average = 100*sum(Julia.i[i])/len(Julia.i[i])
        max_i.append(f"{maximum:.3f}%")
        i_avg.append(f"{average:.3f}%")
    stats["Avg. I"] = i_avg
    stats["Max I"] = max_i

    total, mean = [], []
    for (i,c) in enumerate(large_cities):
        rt = Julia.rt[i]
        inds = np.nonzero(rt >= bins[-2])[0]
        count_open_total = len(inds)
        thresh_open = np.nonzero(np.diff(inds) > 1)[0] + 1
        thresh_open = np.insert(thresh_open, 0, 0)
        thresh_open = np.append(thresh_open, len(inds))
        count_open = np.diff(thresh_open)
        total.append(count_open_total)
        mean.append(np.mean(count_open))
    stats["Open"] = total
    stats["Mean open"] = mean

    total, mean = [], []
    for (i,c) in enumerate(large_cities):
        rt = Julia.rt[i]
        inds = np.nonzero(rt < bins[2])[0]
        count_open_total = len(inds)
        thresh_open = np.nonzero(np.diff(inds) > 1)[0] + 1
        thresh_open = np.insert(thresh_open, 0, 0)
        thresh_open = np.append(thresh_open, len(inds))
        count_open = np.diff(thresh_open)
        total.append(count_open_total)
        mean.append(np.mean(count_open))
    stats["Closed"] = total
    stats["Mean closed"] = mean

    if verbosity >= 2:
        print()
        print("Statistics")
        print(stats)

    # Before saving anything, check if directory exists
    # Lets assume all output files are in the same directory
    dir_output = path.split(figure_file)[0]
    if not path.exists(dir_output):
        os.makedirs(dir_output)

    if verbosity >= 1:
        print('Saving output files...')

    result = save_result(basic_prm, cities_data, target, data_file)

    if verbosity >= 1:
        print('Saving output files... Ok!')

    if verbosity >= 1:
        print("Ploting result...")

    name, extension = os.path.splitext(figure_file)

    figure_file =  name + "-rt" + extension
    plot_result(basic_prm, result, figure_file, Julia.hammer_duration, cities_data["start_date"][0], type="rt")
    plt.savefig(figure_file, dpi=150, bbox_inches='tight')

    figure_file =  name + "-test" + extension
    plot_result(basic_prm, result, figure_file, Julia.hammer_duration, cities_data["start_date"][0], type="test")
    plt.savefig(figure_file, dpi=150, bbox_inches='tight')

    if verbosity >= 1:
        print("Ploting result... OK!")

    return stats

def plot_result(basic_prm, result, figure_file, hammer_duration, start_date=None, subset=None, type="test"):
    """Plot result in a single figure.
    """
    # TODO: Put this constant out
    sars_over_cov = 4.0
    sick_tested = 0.4
    sars_over_cov *= sick_tested/basic_prm["tinf"]

    # Get data
    if subset is None:
        cities = result.index.get_level_values(0).unique()
    else:
        cities = subset

    max_city_len = max(6, np.max([len(c) for c in cities]))
    window = int(basic_prm["window"])
    if start_date is not None:
        start_date = pd.Timestamp(start_date)

    # Find the maximal infected rates
    ncities = len(cities)
    max_i = np.zeros((ncities, 2))
    for j in range(ncities):
        city_name = cities[j]
        i, rt = result.loc[city_name, "i"], result.loc[city_name, "rt"]
        max_i[j, 0] = i.max()
        end_hammer = hammer_duration[j]
        max_i[j, 1] = i.iloc[end_hammer:].max()
    max_i[:, 0] = max_i[:, 0].max()

    # Create figure
    fig = plt.figure(figsize=(15, 1*ncities), constrained_layout=False)

    gs = gridspec.GridSpec(ncities, 2, height_ratios=max_i[:, 0], width_ratios=[0.82, 0.18],
        hspace=0, wspace=0)
    # Colors for rt
    bins = [0]
    bins.extend(plt.linspace(1.0, 0.95*basic_prm["rep"], 5))
    bins.append(basic_prm["rep"])
    bins = np.array(bins)
    colors = ['orangered','darkorange','gold','blue','green','aliceblue']
    levels = ['Severe','High','Elevated','Moderate','Low','Open']
    test_colors = cm.get_cmap('jet', 100*sars_over_cov)

    # Plot the legend
    ax = plt.subplot(gs[:, 1])
    if type == "rt":
        legend_elements = [Line2D([0], [0], color=colors[i], lw=4, label=levels[i]) for i in range(len(colors))]
        ax.legend(handles=legend_elements, loc='upper right')
        ax.set_axis_off()
    else:
        n_values = 200 + 1
        values = sars_over_cov*plt.linspace(1, 0, n_values)
        ax.imshow(values.reshape(n_values, 1), cmap=test_colors, aspect=0.1, alpha=0.7)
        ax.set_xticks([])
        ax.set_yticks(plt.arange(0, n_values, 50))
        ax.set_yticklabels([f"{v:.2f}" for v in values[::50]])
        ax.yaxis.tick_right()

    for j in range(ncities):
        # Get data
        city_name = cities[j]
        i, rt = result.loc[city_name, "i"], result.loc[city_name, "rt"]
        test = result.loc[city_name, "rel. test"] / (sars_over_cov*i)
        ndays = len(i) - 1

        # Prepare figure
        ax = plt.subplot(gs[j, 0])

        # Plot infected
        # ax.plot([0, ndays], [max_i[j, 1], max_i[j, 1]], color="k", alpha=0.15)
        ax.plot(i, color="k")
        # # Show the absolute maximal level before hammer
        # if max_i[j, 0] >= 1.2*max_i[j, 1]:
        #     ax.plot([0, ndays], [max_i[j, 0], max_i[j, 0]], color="k", alpha=0.15)

        if type == "rt":
            # Plot target R0(t)
            for d in range(0, len(rt) - 1, window):
                color_ind = np.searchsorted(bins, rt.iloc[d]) - 1
                r = Rectangle((d, 0), min(window, ndays - d), 1.1*max_i[j, 0],
                            color=colors[color_ind])
                ax.add_patch(r)
        else:
            # Plot target test
            for d in range(0, len(test) - 1):
                r = Rectangle((d, 0), 1, 1.1*max_i[j, 0], color=test_colors(test[d]),
                    alpha=0.7, linewidth=0)
                ax.add_patch(r)

        # Set up figure
        ax.set_xticks([])
        ax.set_xticklabels([])
        ylabel_format = "{{:^{}s}}".format(max_city_len)
        ax.set_ylabel(ylabel_format.format(city_name), rotation=0, horizontalalignment="left", labelpad=2.5*max_city_len)
        if max_i[j, 0] >= 1.2*max_i[j, 1]:
            # # Show absoltute maximal level before hammer
            # ax.set_yticks(max_i[j, :])
            # ax.set_yticklabels(["{:.2f}%".format(100*max_i[j, k]) for k in [0, 1]])
            ax.set_yticks([max_i[j, 1]])
            ax.set_yticklabels(["{:.2f}%".format(100*max_i[j, 1])])
        else:
            ax.set_yticks([max_i[j, 1]])
            ax.set_yticklabels(["{:.2f}%".format(100*max_i[j, 1])])

        ax.yaxis.set_label_position("left")
        ax.yaxis.tick_right()
        # ax.tick_params(axis = "y", which = "both", left = False, right = False)

        ax.spines['left'].set_color("darkgrey")
        ax.spines['right'].set_color("darkgrey")
        ax.spines['top'].set_color("darkgrey")
        ax.spines['bottom'].set_color("darkgrey")
        #ax.spines['bottom'].set_visible(False)

        ax.set_xlim(0, ndays)
        ax.set_ylim(0, 1.1*max_i[j, 0])

        if j == 0:
            if type == "rt":
                ax.set_title("Infection level and target rt")
            else:
                ax.set_title("Infection level and target testing")

    if start_date is None:
        ax.set_xticks(np.arange(0, ndays, 30))
    else:
        ticks = pd.date_range(start_date, start_date + ndays*pd.to_timedelta("1D"), freq="2MS")
        ticks = list(ticks)
        if ticks[0] <= start_date + pd.to_timedelta("10D"):
            ticks[0] = start_date
        else:
            ticks = [start_date] + ticks
        ax.set_xticks([(i - start_date).days for i in ticks])
        labels = [i.strftime('%m/%Y') for i in ticks]
        ax.set_xticklabels(labels, rotation=45, ha='right')

def main():
    """Allow call from the command line.
    """
    verbosity = 1 # 0: print nothing, 1: print min info (with solver progress),
                  # 2: even more detailed
    dir_output = "results"
    options = get_options()
    basic_prm, cities_data, mob_matrix, target, hammer_data = read_data(options)
    ncities, ndays = len(cities_data.index), int(basic_prm["ndays"])
    force_dif = np.ones((ncities, ndays))
    find_feasible_hammer(basic_prm, cities_data, mob_matrix, target, hammer_data,
        out_file=options.hammer_data, incr_all=True, verbosity=verbosity)
    prepare_optimization(basic_prm, cities_data, mob_matrix, target, hammer_data, force_dif,
        tests_off=[], verbosity=verbosity)
    optimize_and_show_results(basic_prm, f"{dir_output}/cmd_res.png",
        f"{dir_output}/cmd_res.csv", cities_data, target, verbosity=verbosity)
    # check_error_optim(basic_prm, cities_data, mob_matrix, dir_output)

if __name__ == "__main__":
    main()


# My billing system using python

from tkinter import *
class my_bill:
    def __init__(self,root):
        self.root=root
        self.root.geometry("1350x800+0+0")
        self.root.title("My billing app")

        self.sada_dhosa=IntVar()
        self.rava_dhosa=IntVar()
        self.paper_dhosa=IntVar()
        self.jini_dhosa=IntVar()
        self.masala_dhosa=IntVar()

        self.masala_uttapam=IntVar()
        self.onion_uttapam=IntVar()
        self.tomato_uttapam=IntVar()
        self.onion_tomato_uttapam=IntVar()

        self.paneer=IntVar()
        self.cheese=IntVar()
        self.sambhar=IntVar()
        self.chatni=IntVar()

        self.subtotal=StringVar()
        self.gst=StringVar()
        self.cgst=StringVar()
        self.total=StringVar()

        title=Label(self.root,text="My Billing System",bd=10,relief=GROOVE,bg="blue",fg="yellow",font=("arial",30,"bold"),pady=2).pack(fill="x")

        F1=LabelFrame(self.root,text="Customer Details",font=("times new roman",15,"bold"),bg="light blue",fg="purple")
        F1.place(x=0,y=75,relwidth=1)

        cname_lbl1=Label(F1,text="Customer Name",fg="white",bg="green",font=("times new roman",15,"bold")).grid(row=0,column=0,padx=20,pady=5)
        cname_txt1=Entry(F1,width=20,font="arial 15",bd=5,relief=SUNKEN).grid(row=0,column=1,pady=5,padx=10)

        cname_lbl2=Label(F1,text="Phone Number",fg="white",bg="green",font=("times new roman",15,"bold")).grid(row=0,column=5,padx=20,pady=5)
        cname_txt2=Entry(F1,width=10,font="arial 15",bd=5,relief=SUNKEN).grid(row=0,column=10,pady=5,padx=10)

        cname_lbl3=Label(F1,text="Bill Number",fg="white",bg="green",font=("times new roman",15,"bold")).grid(row=0,column=15,padx=20,pady=5)
        cname_txt3=Entry(F1,width=20,font="arial 15",bd=5,relief=SUNKEN).grid(row=0,column=20,pady=5,padx=10)

        b1=Button(F1,text="Search",bg='light grey',width=10,bd=8,font=('arial',12,'bold')).grid(row=0,column=25,padx=10,pady=10)

        F2=LabelFrame(self.root,text="DHOSA",bd=10,relief=GROOVE,font=("times new roman",15,"bold"),bg="light blue",fg="purple")
        F2.place(x=5,y=180,relwidth=0.28,height=257)

        cname_lbl4=Label(F2,text="Sada Dhosa",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=0,column=0,padx=20,pady=5)
        cname_txt4=Entry(F2,width=10,textvariable=self.sada_dhosa,font="arial 15",bd=5,relief=SUNKEN).grid(row=0,column=5,pady=5,padx=10)

        cname_lbl5=Label(F2,text="Rava Dhosa",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=1,column=0,padx=20,pady=5)
        cname_txt5=Entry(F2,width=10,textvariable=self.rava_dhosa,font="arial 15",bd=5,relief=SUNKEN).grid(row=1,column=5,pady=5,padx=10)

        cname_lbl6=Label(F2,text="Masala Dhosa",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=2,column=0,padx=20,pady=5)
        cname_txt6=Entry(F2,width=10,textvariable=self.masala_dhosa,font="arial 15",bd=5,relief=SUNKEN).grid(row=2,column=5,pady=5,padx=10)

        cname_lbl7=Label(F2,text="Paper Dhosa",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=3,column=0,padx=20,pady=5)
        cname_txt7=Entry(F2,width=10,textvariable=self.paper_dhosa,font="arial 15",bd=5,relief=SUNKEN).grid(row=3,column=5,pady=5,padx=10)

        cname_lbl8=Label(F2,text="Jini Dhosa",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=4,column=0,padx=20,pady=5)
        cname_txt8=Entry(F2,width=10,textvariable=self.jini_dhosa,font="arial 15",bd=5,relief=SUNKEN).grid(row=4,column=5,pady=5,padx=10)

        F3=LabelFrame(self.root,text="Uttapam",bd=10,relief=GROOVE,font=("times new roman",15,"bold"),bg="light blue",fg="purple")
        F3.place(x=415,y=180,relwidth=0.34,height=257)

        cname_lbl9=Label(F3,text="Masala Uttapam",fg="white",bg="green",width=17,font=("times new roman",15,"bold")).grid(row=0,column=0,padx=20,pady=5)
        cname_txt9=Entry(F3,width=10,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.masala_uttapam).grid(row=0,column=5,pady=5,padx=10)

        cname_lbl10=Label(F3,text="Tomato Uttapam",fg="white",bg="green",width=17,font=("times new roman",15,"bold")).grid(row=1,column=0,padx=20,pady=5)
        cname_txt10=Entry(F3,width=10,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.tomato_uttapam).grid(row=1,column=5,pady=5,padx=10)

        cname_lbl11=Label(F3,text="Onion Tomato Uttapam",fg="white",bg="green",width=17,font=("times new roman",15,"bold")).grid(row=2,column=0,padx=20,pady=5)
        cname_txt11=Entry(F3,width=10,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.onion_tomato_uttapam).grid(row=2,column=5,pady=5,padx=10)

        cname_lbl12=Label(F3,text="Onion Uttapam",fg="white",bg="green",width=17,font=("times new roman",15,"bold")).grid(row=3,column=0,padx=20,pady=5)
        cname_txt12=Entry(F3,width=10,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.onion_uttapam).grid(row=3,column=5,pady=5,padx=10)

        F4=Frame(self.root,bd=10,relief=GROOVE)
        F4.place(x=900,y=180,width=440,height=300)
        bill_title=Label(F4,text="Your Bill",font="arial 15 bold",bd=7,relief=GROOVE).pack(fill=X)

        scroll_y=Scrollbar(F4,orient=VERTICAL)
        self.textarea=Text(F4,yscrollcommand=scroll_y.set)
        scroll_y.pack(side=RIGHT,fill=Y)
        scroll_y.config(command=self.textarea.yview)
        self.textarea.pack()

        F5=LabelFrame(self.root,text="Extra",bd=10,relief=GROOVE,font=("times new roman",15,"bold"),bg="light blue",fg="purple")
        F5.place(x=0,y=440,relwidth=0.65,height=80)

        cname_lbl13=Label(F5,text="cheese" ,fg="white",bg="green",width=6,font=("times new roman",15,"bold")).grid(row=1,column=0,padx=20,pady=5)
        cname_txt13=Entry(F5,width=5,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.cheese).grid(row=1,column=5,pady=5,padx=10)

        cname_lbl14=Label(F5,text="Sambhar" ,fg="white",bg="green",width=6,font=("times new roman",15,"bold")).grid(row=1,column=10,padx=20,pady=5)
        cname_txt14=Entry(F5,width=5,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.sambhar).grid(row=1,column=15,pady=5,padx=10)

        cname_lbl15=Label(F5,text="Chatni" ,fg="white",bg="green",width=6,font=("times new roman",15,"bold")).grid(row=1,column=20,padx=20,pady=5)
        cname_txt15=Entry(F5,width=5,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.chatni).grid(row=1,column=25,pady=5,padx=10)

        cname_lbl16=Label(F5,text="Paneer" ,fg="white",bg="green",width=6,font=("times new roman",15,"bold")).grid(row=1,column=30,padx=20,pady=5)
        cname_txt16=Entry(F5,width=5,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.paneer).grid(row=1,column=35,pady=5,padx=10)

        F6=LabelFrame(self.root,text="Main Bill Menu",bd=10,relief=GROOVE,font=("times new roman",15,"bold"),bg="light blue",fg="purple")
        F6.place(x=0,y=535,relwidth=0.65,height=160)

        cname_lbl17=Label(F6,text="SubTotal",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=1,column=30,padx=20,pady=5)
        cname_txt17=Entry(F6,textvariable=self.subtotal,width=8,font="arial 15",bd=5,relief=SUNKEN).grid(row=1,column=33,pady=5,padx=10)

        cname_lbl18=Label(F6,text="GST",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=1,column=40,padx=20,pady=5)
        cname_txt18=Entry(F6,width=8,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.gst).grid(row=1,column=43,pady=5,padx=10)

        cname_lbl18=Label(F6,text="CGST",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=1,column=50,padx=20,pady=5)
        cname_txt18=Entry(F6,width=8,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.cgst).grid(row=1,column=53,pady=5,padx=10)

        cname_lbl20=Label(F6,text="TOTAL BILL",fg="white",bg="green",width=10,font=("times new roman",15,"bold")).grid(row=4,column=50,padx=20,pady=5)
        cname_txt20=Entry(F6,width=8,font="arial 15",bd=5,relief=SUNKEN,textvariable=self.total).grid(row=4,column=53,pady=5,padx=10)

        F7=Frame(self.root,bd=10,relief=GROOVE)
        F7.place(x=900,y=480,width=440,height=210)

        sub_total_btn=Button(F7,text="Sub Total",command=self.sub_total,bg="cadetblue",fg="white",bd=5,pady=15,width=11,font="arial").grid(row=0,column=0,padx=5,pady=5)
        gbill_btn=Button(F7,text="Generate Bill",bg="cadetblue",fg="white",bd=5,pady=15,width=11,font="arial").grid(row=0,column=10,padx=5,pady=5)
        print_btn=Button(F7,text="Print",bg="cadetblue",fg="white",bd=5,pady=15,width=11,font="arial").grid(row=0,column=15,padx=5,pady=5)
        total_btn=Button(F7,text="Total",command=self.total_bill,bg="cadetblue",fg="white",bd=5,pady=15,width=11,font="arial").grid(row=1,column=0,padx=5,pady=5)
        clear_btn=Button(F7,text="Clear",bg="cadetblue",fg="white",bd=5,pady=15,width=11,font="arial").grid(row=1,column=10,padx=5,pady=5)
        exit_btn=Button(F7,text="Exit",bg="cadetblue",fg="white",bd=5,pady=15,width=11,font="arial").grid(row=1,column=15,padx=5,pady=5)

    def sub_total(self):
        self.st=((self.masala_dhosa.get()*65)+(self.rava_dhosa.get()*80)+(self.jini_dhosa.get()*120)+(self.sada_dhosa.get()*50) +(self.paper_dhosa.get()*90)+(self.masala_uttapam.get())*80+(self.onion_uttapam.get()*85)+(self.tomato_uttapam.get()*85)+(self.onion_tomato_uttapam.get()*90) +(self.cheese.get()*20)+(self.paneer.get()*15)+(self.sambhar.get()*10)+(self.chatni.get()*5))
        self.subtotal.set("Rs."+str(self.st))

    def total_bill(self):
            self.tl=((self.st+float(self.st)*5/100))
            self.total.set("Rs."+str(self.tl))

root=Tk()
obj1=my_bill(root)
root.mainloop()


i = 1
counter = 0
goal = 0

while ( counter < 48 ):

	while i%7 != 0 :
		i+=1
	#print(i,"is a multiple of 7")

	if str(7) in str(i):
		goal = i
		counter+=1
		#print(i,"includes 7")
		print(counter, i)
	i+=1

print("The 48th multiple of 7 that contains 7 is:", goal)


def print_menu():
    print("1 - option one")
    print("2 - option two")

def option1():
     print('Handle option \'Option 1\'')

while(True):
    print_menu()
    option = ""
    try:
        option = int(input("Enter choice: "))
    except:
        print('Wrong input. Please enter a number ...')
    if option == 1:
        print("escolheu um")
    elif option == 2:
        option1()
    elif option == 0:
        exit()
    else:
         print('Invalid option. Please enter a number between 1')


import collections

from absl import logging
import numpy as np
import tensorflow as tf
import tensorflow_federated as tff

from tensorflow_federated.python.research.optimization.shared import fed_avg_schedule

_Batch = collections.namedtuple('Batch', ['x', 'y'])

def _batch_fn(has_nan=False):
  batch = _Batch(
      x=np.ones([1, 784], dtype=np.float32), y=np.ones([1, 1], dtype=np.int64))
  if has_nan:
    batch[0][0, 0] = np.nan
  return batch

def _create_input_spec():
  return _Batch(
      x=tf.TensorSpec(shape=[None, 784], dtype=tf.float32),
      y=tf.TensorSpec(dtype=tf.int64, shape=[None, 1]))

def _uncompiled_model_builder():
  keras_model = tff.simulation.models.mnist.create_keras_model(
      compile_model=False)
  return tff.learning.from_keras_model(
      keras_model=keras_model,
      input_spec=_create_input_spec(),
      loss=tf.keras.losses.SparseCategoricalCrossentropy())

class ModelDeltaProcessTest(tf.test.TestCase):

  def _run_rounds(self, iterative_process, federated_data, num_rounds):
    train_outputs = []
    initial_state = iterative_process.initialize()
    state = initial_state
    for round_num in range(num_rounds):
      iteration_result = iterative_process.next(state, federated_data)
      train_outputs.append(iteration_result.metrics)
      logging.info('Round %d: %s', round_num, iteration_result.metrics)
      state = iteration_result.state
    return state, train_outputs, initial_state

  def test_fed_avg_without_schedule_decreases_loss(self):
    federated_data = [[_batch_fn()]]

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        server_optimizer_fn=tf.keras.optimizers.SGD)

    _, train_outputs, _ = self._run_rounds(iterative_process, federated_data, 5)
    self.assertLess(train_outputs[-1]['loss'], train_outputs[0]['loss'])

  def test_fed_avg_with_custom_client_weight_fn(self):
    federated_data = [[_batch_fn()]]

    def client_weight_fn(local_outputs):
      return 1.0/(1.0 + local_outputs['loss'][-1])

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        server_optimizer_fn=tf.keras.optimizers.SGD,
        client_weight_fn=client_weight_fn)

    _, train_outputs, _ = self._run_rounds(iterative_process, federated_data, 5)
    self.assertLess(train_outputs[-1]['loss'], train_outputs[0]['loss'])

  def test_client_update_with_finite_delta(self):
    federated_data = [_batch_fn()]
    model = _uncompiled_model_builder()
    client_optimizer = tf.keras.optimizers.SGD(0.1)
    client_update = fed_avg_schedule.create_client_update_fn()
    outputs = client_update(model, federated_data,
                            fed_avg_schedule._get_weights(model),
                            client_optimizer)
    self.assertAllEqual(self.evaluate(outputs.client_weight), 1)
    self.assertAllEqual(
        self.evaluate(outputs.optimizer_output['num_examples']), 1)

  def test_client_update_with_non_finite_delta(self):
    federated_data = [_batch_fn(has_nan=True)]
    model = _uncompiled_model_builder()
    client_optimizer = tf.keras.optimizers.SGD(0.1)
    client_update = fed_avg_schedule.create_client_update_fn()
    outputs = client_update(model, federated_data,
                            fed_avg_schedule._get_weights(model),
                            client_optimizer)
    self.assertAllEqual(self.evaluate(outputs.client_weight), 0)

  def test_server_update_with_nan_data_is_noop(self):
    federated_data = [[_batch_fn(has_nan=True)]]

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        server_optimizer_fn=tf.keras.optimizers.SGD)

    state, _, initial_state = self._run_rounds(iterative_process,
                                               federated_data, 1)
    self.assertAllClose(state.model, initial_state.model, 1e-8)

  def test_server_update_with_inf_weight_is_noop(self):
    federated_data = [[_batch_fn()]]
    client_weight_fn = lambda x: np.inf

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        server_optimizer_fn=tf.keras.optimizers.SGD,
        client_weight_fn=client_weight_fn)

    state, _, initial_state = self._run_rounds(iterative_process,
                                               federated_data, 1)
    self.assertAllClose(state.model, initial_state.model, 1e-8)

  def test_fed_avg_with_client_schedule(self):
    federated_data = [[_batch_fn()]]

    @tf.function
    def lr_schedule(x):
      return 0.1 if x < 1.5 else 0.0

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        client_lr=lr_schedule,
        server_optimizer_fn=tf.keras.optimizers.SGD)

    _, train_outputs, _ = self._run_rounds(iterative_process, federated_data, 4)
    self.assertLess(train_outputs[1]['loss'], train_outputs[0]['loss'])
    self.assertNear(
        train_outputs[2]['loss'], train_outputs[3]['loss'], err=1e-4)

  def test_fed_avg_with_server_schedule(self):
    federated_data = [[_batch_fn()]]

    @tf.function
    def lr_schedule(x):
      return 1.0 if x < 1.5 else 0.0

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        server_optimizer_fn=tf.keras.optimizers.SGD,
        server_lr=lr_schedule)

    _, train_outputs, _ = self._run_rounds(iterative_process, federated_data, 4)
    self.assertLess(train_outputs[1]['loss'], train_outputs[0]['loss'])
    self.assertNear(
        train_outputs[2]['loss'], train_outputs[3]['loss'], err=1e-4)

  def test_fed_avg_with_client_and_server_schedules(self):
    federated_data = [[_batch_fn()]]

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        client_lr=lambda x: 0.1 / (x + 1)**2,
        server_optimizer_fn=tf.keras.optimizers.SGD,
        server_lr=lambda x: 1.0 / (x + 1)**2)

    _, train_outputs, _ = self._run_rounds(iterative_process, federated_data, 6)
    self.assertLess(train_outputs[-1]['loss'], train_outputs[0]['loss'])
    train_gap_first_half = train_outputs[0]['loss'] - train_outputs[2]['loss']
    train_gap_second_half = train_outputs[3]['loss'] - train_outputs[5]['loss']
    self.assertLess(train_gap_second_half, train_gap_first_half)

  def test_conversion_from_tff_result(self):
    federated_data = [[_batch_fn()]]

    iterative_process = fed_avg_schedule.build_fed_avg_process(
        _uncompiled_model_builder,
        client_optimizer_fn=tf.keras.optimizers.SGD,
        server_optimizer_fn=tf.keras.optimizers.SGD)

    state, _, _ = self._run_rounds(iterative_process, federated_data, 1)
    converted_state = fed_avg_schedule.ServerState.from_tff_result(state)
    self.assertIsInstance(converted_state, fed_avg_schedule.ServerState)
    self.assertIsInstance(converted_state.model, fed_avg_schedule.ModelWeights)

if __name__ == '__main__':
  tf.test.main()


import numpy as np
import scipy.special as ss
import matplotlib.pyplot as plt

def beta_s(x, a, b):
    return x ** (a - 1) * (1 - x) ** (b - 1)

def beta(x, a, b):
    return beta_s(x, a, b) / ss.beta(a, b)

def plot_mcmc(a, b):
    cur = np.random.rand()
    states = [cur]
    for i in range(50000):
        u = np.random.rand()
        next = np.random.rand()
        if u < np.min((beta_s(next, a, b) / beta_s(cur, a, b), 1)):
            states.append(next)
            cur = next
    print(len(states))   # 每一次实验最终接受的数据量是不一定的，states的长度也是不一定的
    x = np.arange(0, 1, 0.01)
    plt.plot(x, beta(x, a, b), lw=2)
    plt.hist(states[-4000:], bins=50, normed=True)  # 截取后4000个数据，保证数据量的一致性

if __name__ == '__main__':
    plt.figure(figsize=(20,5))
    plt.subplot(131)
    plot_mcmc(0.1, 0.1)
    plt.subplot(132)
    plot_mcmc(1, 1)
    plt.subplot(133)
    plot_mcmc(2, 3)
    plt.show()


#פונקציה שמקבלת מחרוזת ומזהה את המילה הארוכה ביותר
def lon_word(text):
    num_of_words = text.split()
    print(num_of_words)
    last_length = 1
    for i in num_of_words:
        new_length = len (i)
        if new_length>last_length:
            last_length = new_length
            longest_word = i
    return longest_word
    #print(longest_word)

print(lon_word("today is friday"))


import pandas as pd

df = pd.read_csv("C:\\Data\\PreppinData\\2021W51 Input.csv",
                     parse_dates=['Order Date'], date_parser=lambda x: pd.to_datetime(x, format='%d/%m/%Y'))
df['Order Date'] = df['Order Date'].dt.date
temp = df['OrderID'].str.split('-', n = 1, expand = True)
df['Store'] = temp[0]
df['OrderID'] = temp[1]
df['Returned'] = df['Return State'].apply(lambda x: int(pd.notna(x)))
df['Unit Price'] = df['Unit Price'].apply(lambda x: float(x[1:]))
df['Sales'] = df['Unit Price']*df['Quantity']
df['Product'] = df['Product Name'].str.lower()

for dim in ['Store', 'Customer', 'Product']:
    df[f'{dim} First Order'] = df['Order Date'].groupby(df[dim]).transform('min')
    df[f'{dim}ID'] = df[[f'{dim} First Order',dim]].apply(tuple,axis=1).rank(method='dense',ascending=True).astype(int)

fact = df[['StoreID', 'CustomerID', 'OrderID', 'Order Date', 'ProductID', 'Returned', 'Quantity', 'Sales']].copy()

store = df[['StoreID', 'Store', 'Store First Order']].drop_duplicates().copy()
store = store.rename(columns={'Store First Order':'First Order'}).sort_values('StoreID')

customer = df.groupby(['CustomerID', 'Customer', 'Customer First Order'], as_index=False).agg({'OrderID':'nunique', 'Product':'count', 'Returned':'sum'})
customer['Return %'] = round(customer['Returned']/customer['Product'], 2)
customer = customer.rename(columns={'Customer First Order':'First Order', 'OrderID':'Number of Orders'})
customer = customer[['CustomerID', 'Customer', 'Return %', 'Number of Orders', 'First Order']].sort_values('CustomerID')

product = df[['ProductID', 'Category', 'Sub-Category', 'Product Name', 'Unit Price', 'Product First Order']].drop_duplicates().copy()
product = product.rename(columns={'Product First Order':'First Sold'}).sort_values('ProductID')


# -*-coding:utf-8- -*-
import os
import shutil
import numpy as np

class Elliptic:
    enumerate(['Jacobi', 'PGS', 'PSOR'])

    def __init__(self):
        self.Gx = 21
        self.Gy = 41
        self.GridSize = 0.05
        self.T0 = 100
        self.Ermax = 0.000001
        self.T = np.random.randn(self.Gy, self.Gx)
        self.Tnew = np.random.randn(self.Gy, self.Gx)
        self.scheme = 'error'
        self.Scheme_name = "Error"
        self.Dirname = "Error"
        self.iter = 0
        self.w = 1.5
        return

    def Scheme_Printer(self):
        print("Solve {0}".format(self.scheme))
        self.Scheme_name = "%s"%self.scheme
        return

    def Dir_Write(self):
        if self.Scheme_name == "PSOR":
            self.dirname = "Elliptic, {0}, w={1}".format(self.Scheme_name, self.w)
        else:
            self.dirname = "Elliptic, {0}".format(self.Scheme_name)
        path = os.getcwd()
        self.dirname = os.path.join(path, self.dirname)
        if os.path.isdir(self.dirname):
            shutil.rmtree(self.dirname)  # 디렉토리가 존재하면 삭제하고 다시 계산# return # 디렉토리가 존재하면 덮어쓰기 #
        os.mkdir(self.dirname)
        return

    def Para_Write(self):
        if self.Scheme_name == "PSOR":
            filename = "{0}/{1}, w = {2}, iter = {3}.csv".format(self.dirname, self.Scheme_name, self.w, self.iter)
        else:
            filename = "{0}/{1}, iter = {2}.csv".format(self.dirname, self.Scheme_name, self.iter)
        file = open(filename, 'w')
        file.write("X,Y,Z,Temperature\n")
        for i in range(self.Gy):
            for j in range(self.Gx):
                data = "%3.3f,%3.3f,%3.3f,%3.3f\n" % \
                       (float(j*self.GridSize), float(i*self.GridSize), 0.0, self.T[i][j])
                file.write(data)
        file.close()
        return

    def get_w(self):
        prompt = """Input w, 1.0 ~ 1.9
Enter w: """
        print(prompt)
        self.w = float(input())
        if 1.0 < self.w < 1.9:
            return
        else:
            print("Wrong Input")
            self.get_w()

    def Initial_Condition(self):
        self.T = np.zeros((self.Gy, self.Gx))
        self.Tnew = np.zeros((self.Gy, self.Gx))
        return

    def Boundary_Condition(self):
        for i in range(self.Gy):
            self.T[i][0] = 0
            self.Tnew[i][0] = 0
            self.T[i][self.Gx-1] = 0
            self.Tnew[i][self.Gx-1] = 0
        for i in range(self.Gx):
            self.T[self.Gy-1][i] = 0
            self.Tnew[self.Gy-1][i] = 0
            self.T[0][i] = self.T0
            self.Tnew[0][i] = self.T0
        return

    def CalErr(self):
        error = 0
        for i in range(1, self.Gy-1):
            for j in range(1, self.Gx-1):
                error += abs(self.Tnew[i][j] - self.T[i][j])
        return error

    def Memcpy(self):
        self.T = self.Tnew.copy()
        return

    def Jacobi(self):
        for i in range(1, self.Gy-1):
            for j in range(1, self.Gx-1):
                self.Tnew[i][j] = (self.T[i+1][j]+self.T[i-1][j]+self.T[i][j+1]+self.T[i][j-1])/4.0
        return

    def Jacobi_Solver(self):
        self.iter = 0
        error = 1.0
        while error > self.Ermax:
            self.Jacobi()
            error = self.CalErr()
            self.Memcpy()
            self.iter += 1
            print("\rIteration = %d"%self.iter, end="")
        return

    def PGS(self):
        for i in range(1, self.Gy-1):
            for j in range(1, self.Gx-1):
                self.Tnew[i][j] = (self.T[i+1][j]+self.Tnew[i-1][j]+self.T[i][j+1]+self.Tnew[i][j-1])/4.0
        return

    def PGS_Solver(self):
        self.iter = 0
        error = 1.0
        while error > self.Ermax:
            self.PGS()
            error = self.CalErr()
            self.Memcpy()
            self.iter += 1
            print("\rIteration = %d"%self.iter, end="")
        return

    def PSOR(self):
        for i in range(1, self.Gy-1):
            for j in range(1, self.Gx-1):
                self.Tnew[i][j] = (1.0 - self.w)*(self.T[i][j]) + self.w*(self.T[i+1][j]+self.Tnew[i-1][j]+self.T[i][j+1]+self.Tnew[i][j-1])/4.0
        return

    def PSOR_Solver(self):
        self.iter = 0
        error = 1.0
        while error > self.Ermax:
            self.PSOR()
            error = self.CalErr()
            self.Memcpy()
            self.iter += 1
            print("\rIteration = %d"%self.iter, end="")
        return

    def Main(self, scheme):
        self.scheme = scheme
        self.Scheme_Printer()
        self.Initial_Condition()
        self.Boundary_Condition()
        self.Dir_Write()
        if scheme == 'Jacobi':
            self.Jacobi_Solver()
        elif scheme == 'PGS':
            self.PGS_Solver()
        elif scheme == 'PSOR':
            self.PSOR_Solver()
        self.Para_Write()
        print()
        return

def main():
    func = Elliptic()
    return func

PyCompute = Elliptic()
print("What Will you Compute?")
prompt = """
1: Jacobi
2: PGS
3: PSOR

Enter Number(1~3): """
ID = 5
I = 1
while I:
    if 0 < ID < 4:
        I = 0
    else:
        print(prompt)
        ID = int(input())
if ID == 1:
    PyCompute.Main('Jacobi')
elif ID == 2:
    PyCompute.Main('PGS')
elif ID == 3:
    PyCompute.Main('PSOR')


import sys
sys.stdin = open("input.txt")

from bisect import bisect_left

n = int(input())
lst = list(map(int, input().split()))
d = []

for i in range(n):
    if not d or lst[i] > d[-1]:
        d.append(lst[i])
    else:
        a = bisect_left(d, lst[i])
        d[a] = min(d[a], lst[i])

print(len(d))


import sys
import re
# 读取文件
def read_file(file):
    f = open(file,'r',encoding = 'utf-8')
    try:
        lines = f.readlines()
        return lines
    except:
        print("read error")
        return []
# 写入文件
def write_file(lines):
    f = open("./result",'w')
    for line in lines:
        f.write(line)
# 主逻辑
def handler(file):
    lines=read_file(file)
    res=[]
    enable_write = False
    for line in lines:
        if re.match(r'^error*',line):
            enable_write = True
        elif re.match(r'\n',line):
            if enable_write is True:
                res.append("\n")
            enable_write = False
        if enable_write is True:
            res.append(line)
    write_file(res)
def main():
    file=sys.argv[1]
    handler(file)
main()


"""Collection of utility functions for helping visualizing results of training pipeline for covid_lit_contra_claims."""

# -*- coding: utf-8 -*-

import os

from matplotlib.lines import Line2D

import numpy as np

import pandas as pd

pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)

fig_dir = "/Users/dnsosa/Desktop/AltmanLab/covid_lit_contra_claims/output/figs"

def train_seq_to_idx(row, R=4):
    """
    Convert training subsequence into an index.

    :param row: DF row
    :param R: total number of datasets used in training
    :return: index of the current subsequence
    """
    # Only takes into account the number of overlaps, not the order
    full_seq, train_subseq = row["train_datasets"], row["Cumulative Training"]

    full_seq_set = set(full_seq.split('_'))
    train_subseq_set = set(train_subseq.split('_'))
    k = len(full_seq_set.intersection(train_subseq_set))
    N = len(full_seq_set)
    return R - N + k

def latest_training(row):
    """Retrieve latest dataset used in training."""
    full_seq = row["Cumulative Training"]
    datasets = full_seq.split("_")
    return datasets[-1]

def tot_num_training_ds(row):
    """Calculate total number of training datasets used."""
    full_seq = row["train_datasets"]
    datasets = full_seq.split("_")
    return len(datasets)

def create_df_from_dir(base_dir, experiment_dir, model_name):
    """
    Create dataframe from directory containing all the results.

    :param base_dir: Base directory
    :param experiment_dir: Directory containing the name of the experiment
    :param model_name: Name of the model evaluating
    :return: Single dataframe compiling all results together
    """
    query_dir = os.path.join(base_dir, experiment_dir, model_name)

    df_list = []
    for root, _, f_names in os.walk(query_dir):
        for f in f_names:
            res_df = pd.read_csv(os.path.join(root, f))
            df_list.append(res_df)
    df = pd.concat(df_list)
    df["cum_train_idx"] = df.apply(lambda row: train_seq_to_idx(row), axis=1)
    df["Latest Training"] = df.apply(lambda row: latest_training(row), axis=1)
    df["tot_num_training_ds"] = df.apply(lambda row: tot_num_training_ds(row), axis=1)

    return df

def lighten_color(in_hex, lighten_percent):
    """Lighten a hex code color by a certain percentage."""
    in_hex = in_hex.lstrip('#')
    rgb = tuple(int(in_hex[i:i + 2], 16) for i in (0, 2, 4))
    light_r, light_g, light_b = [round(val + (255 - val) * lighten_percent) for val in rgb]
    light_hex = f"#{light_r:X}{light_g:X}{light_b:X}"

    return light_hex

def draw_arrows(df, metric, arrow_prox, arrow_color, highlight_forward_back, in_ax):
    """
    Draw arrows between pie points.

    :param df: Input dataframe
    :param metric: Metric we're plotting
    :param arrow_prox: Proximity (as a fraction of full distance) between the arrow head and tail and the point
    :param arrow_color: Arrow color
    :param highlight_forward_back: Highlight forward and backward sequences?
    :param in_ax: Input matplotlib axis
    """
    forward, backward = "multinli_mednli_mancon_roam", "roam_mancon_mednli_multinli"
    full_seq = (df["train_datasets"].values[0])
    line_width = 4 if ((full_seq in [forward, backward]) and highlight_forward_back) else 1
    if highlight_forward_back:
        if full_seq == forward:
            arrow_color = "#9dbda5"
        elif full_seq == backward:
            arrow_color = "#f0adad"

    for i in range(min(df.cum_train_idx), max(df.cum_train_idx)):
        x1, x2 = i, i + 1
        x1_prox = x2 - (x2 - x1) * arrow_prox
        x2_prox = x1 + (x2 - x1) * arrow_prox
        y1, y2 = df[df["cum_train_idx"] == i][metric].values[0], df[df["cum_train_idx"] == i + 1][metric].values[0]
        y1_prox = y2 - (y2 - y1) * arrow_prox
        y2_prox = y1 + (y2 - y1) * arrow_prox
        in_ax.annotate("", xy=(x1_prox, y1_prox), xycoords='data', xytext=(x2_prox, y2_prox), textcoords='data',
                       arrowprops={'arrowstyle': '<|-, head_width=0.3, head_length=.45',
                                   'connectionstyle': 'arc3',
                                   'facecolor': arrow_color,
                                   'edgecolor': arrow_color,
                                   'lw': line_width})

    return None

def calculate_data_ratios_dist(train_datasets_list, data_ratio):
    """
    Calculate data ratio distributions for pie seq plotting.

    :param train_datasets_list: list of training datasets
    :param data_ratio: input data ratio
    :return: the numerator describing the proportion between datasets
    """
    multiplier_datasets = ["multinli", "mednli", "mancon"]
    # Number of times to apply the multiplier
    ratio_multiplier = len(set(multiplier_datasets).intersection(set(train_datasets_list)))
    n_datasets = len(train_datasets_list)
    dist = np.ones(n_datasets)
    for i, dataset in enumerate(train_datasets_list):
        if dataset in multiplier_datasets:
            dist[i] = data_ratio ** ratio_multiplier
            ratio_multiplier -= 1

    return dist

def draw_pie_scatter(df, metric, ds_color_map, background_color, combined_color, size, alpha, in_ax, is_combined=False):
    """
    Draw pie scatter points.

    :param df: input dataframe
    :param metric: netric to visualize
    :param ds_color_map: map of datasets to colors
    :param background_color: background color of plot
    :param combined_color: color of the "combined" condition highlight
    :param size: size of the circles
    :param alpha: transparency
    :param in_ax: input
    :param is_combined: if True, figure out new way to calculate the data ratio proportions
    """
    num_ft_datasets = max(df.cum_train_idx)
    lowest_train_idx = min(df.cum_train_idx)
    for i in range(lowest_train_idx, num_ft_datasets + 1):
        curr_df = df[df["cum_train_idx"] == i]
        train_so_far = curr_df["Cumulative Training"].values[0]
        all_train_datasets = curr_df["train_datasets"].values[0].split("_")

        xpos, ypos = i, curr_df[metric].values[0]

        # for incremental pie slices
        data_ratio = curr_df["data_ratios"].values[0]
        dist = calculate_data_ratios_dist(all_train_datasets, data_ratio)
        # if is_combined:  # TODO: Fix me if different data ratios
        #    dist = np.ones(4)
        size_boost = 1 + np.log10(np.sum(dist)) / np.log10(11)
        size *= size_boost
        cumsum = np.cumsum(dist)
        cumsum = cumsum / cumsum[-1]
        pie = [0] + cumsum.tolist()

        for j, (r1, r2) in enumerate(zip(pie[:-1], pie[1:])):

            train_j = all_train_datasets[j]
            if train_j in train_so_far:
                color = ds_color_map[train_j]
            else:
                color = background_color

            angles = np.pi / 2 - np.linspace(2 * np.pi * r1, 2 * np.pi * r2, num=25)

            x = [0] + np.cos(angles).tolist()
            y = [0] + np.sin(angles).tolist()

            xy = np.column_stack([x, y])

            if is_combined:
                in_ax.scatter([xpos], [ypos], marker=xy, s=size * 5.5, color=combined_color, zorder=9,
                              alpha=alpha)  # Drawing the border
                in_ax.scatter([xpos], [ypos], marker=xy, s=size * 3, color=color, zorder=10, alpha=alpha)

            else:
                in_ax.scatter([xpos], [ypos], marker=xy, s=size * 1.1, color="black", alpha=alpha)  # Drawing the border
                in_ax.scatter([xpos], [ypos], marker=xy, s=size, color=color, alpha=alpha)

    return None

def draw_custom_legends(ds_color_map, ds_name_map, background_color, combined_color, loc, in_ax, show_combined=False):
    """
    Draw legends.

    :param ds_color_map: dataset to color map
    :param ds_name_map: dataset id to name to display map
    :param background_color: background color for plots
    :param combined_color: color of combined condition
    :param loc: location of legend
    :param in_ax: input axis
    :param show_combined: show combined condition?
    """
    # Train legend
    ds_names = list(ds_color_map.keys())[:4]
    ds_names_nice = [ds_name_map[ds] for ds in ds_names]
    custom_lines = [
        Line2D([0], [0], color='white', marker='o', markerfacecolor=ds_color_map[ds], markeredgecolor=background_color,
               markersize=15) for ds in ds_names]

    if show_combined:
        combined_line = Line2D([0], [0], color='white', marker='o', markerfacecolor="white",
                               markeredgecolor=combined_color, markersize=19)
        custom_lines.append(combined_line)
        ds_names_nice.append("Combined")

    in_ax.legend(custom_lines, ds_names_nice,
                 loc=loc)

    return None

def get_seq_plot_order(in_df, metric):
    """
    Return order to plot each subsequence.

    :param in_df: input df of all conditions
    :param metric: metric to visualize
    :return: ordered list of subsequences
    """
    seqs = set(in_df["train_datasets"])
    num_ds_in_seq = {seq: len(seq.split("_")) for seq in seqs}

    # Order by performance metric
    seq_final_metrics = dict(in_df.groupby("train_datasets")[metric].max())
    ordered_final_metrics = {k: v for k, v in
                             sorted(seq_final_metrics.items(), key=lambda item: (-num_ds_in_seq[item[0]], item[1]))}
    return list(ordered_final_metrics.keys())


import tensorflow.compat.v1 as tf

# logistic regression

# 1. 데이터 준비
# 2. 데이터 분할
# [1, 0] : 1시간 공부 / 0시간 과외 -> [0] : fail
# [8, 1] : 8시간 공부 / 1시간 과외 -> [1] : pass
train_X = [
    [1, 0],
    [2, 0],
    [5, 1],
    [2, 3],
    [3, 3],
    [8, 1],
    [10, 0]
]

train_y = [
    [0],
    [0],
    [0],
    [0],
    [1],
    [1],
    [1],
]
X = tf.placeholder(shape=[None, 2], dtype=tf.float32)
y = tf.placeholder(shape=[None, 1], dtype=tf.float32)

# 3. 준비
# 가설
W = tf.Variable(tf.random_normal([2, 1]), name='weight')
b = tf.Variable(tf.random_normal([1]), name='bias')

logit = tf.matmul(X, W) + b
# actvation function : sigmoid 사용 (0 또는 1)

H = tf.sigmoid(logit)

# loss
loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(logits=logit, labels=y))

# optimizer(Gradient Descent)
learning_rate = 0.1
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train = optimizer.minimize(loss)

# Session
sess = tf.Session()
sess.run(tf.global_variables_initializer())

# 4. 학습
epochs = 10000
for step in range(epochs) :
    _, loss_val, W_val, b_val = sess.run([train, loss, W, b], feed_dict={X: train_X, y: train_y})
    if step % 500 == 0:
        print("W : {}\t b : {} \t loss : {}".format(W_val, b_val, loss_val))

#5. 예측 및 평가
predict = tf.cast(H > 0.5, dtype=tf.float32)

equal = tf.cast(tf.equal(predict, y), dtype=tf.float32)
accuracy = tf.reduce_mean(equal)

# 우리는 지금 데이터의 볼륨이 작아서 학습데이터를 넣어줬지만,
# 사실 test_set()이 필요!

print(sess.run(accuracy, feed_dict={X: train_X, y: train_y}))

print("예측 : ", sess.run(H, feed_dict={X: [[4, 2]]}))


import sys

import optuna
from optuna.trial import TrialState

from torch.utils.data import DataLoader
from lundnet.dgl_dataset import DGLGraphDatasetLund, collate_wrapper_tree

from hyparticlenet.hgnn.train import train

import torch

from tqdm import tqdm
import random

from omegaconf import OmegaConf
import warnings
warnings.filterwarnings("ignore", message="User provided device_type of 'cuda', but CUDA is not available. Disabling")

#NUM_THREADS = 4
#torch.set_num_threads = NUM_THREADS

PATH = '/scratch/gc2c20/data/w_tagging/'
train_dataset = DGLGraphDatasetLund(PATH+'/train_bkg/', PATH+'/train_sig/',
        nev=-1, n_samples=200_000)
valid_dataset = DGLGraphDatasetLund(PATH+'/valid_bkg/', PATH+'/valid_sig/',
        nev=-1, n_samples=20_000)

def objective(trial):
    config_path = 'configs/jets_config.yaml'
    args = OmegaConf.load(config_path)
    args.manifold = 'lorentz'

    args.lr = trial.suggest_float("lr", 1e-4, 1e-1, log=True)
    args.dropout = trial.suggest_float("dropout", 0.1, 0.5)
    args.batch_size = trial.suggest_int("batch_size", 8, 128)
    #args.num_layers = trial.suggest_int("num_layers", 3, 6)
    #args.embed_dim = trial.suggest_int("embed_dim", 3, 100)
    args.num_centroid = trial.suggest_int("num_centroid", 10, 250)
    args.optimizer = trial.suggest_categorical("optimizer", ["Adam", "SGD"])
    args.epochs = trial.suggest_int("epochs", 30, 70)

    collate_fn = collate_wrapper_tree
    train_loader = DataLoader(train_dataset, batch_size=args.batch_size,
        shuffle=True, collate_fn=collate_fn)
    val_loader = DataLoader(dataset=valid_dataset, batch_size=args.batch_size,
        collate_fn=collate_fn)

    accuracy = train(train_loader, val_loader, args)
    return accuracy

if __name__ == '__main__':
    study = optuna.create_study(direction='maximize')
    study.optimize(objective, n_trials=50)

    pruned_trials = study.get_trials(deepcopy=False, states=[TrialState.PRUNED])
    complete_trials = study.get_trials(deepcopy=False, states=[TrialState.COMPLETE])

    print("Study statistics: ")
    print("  Number of finished trials: ", len(study.trials))
    print("  Number of pruned trials: ", len(pruned_trials))
    print("  Number of complete trials: ", len(complete_trials))

    print("Best trial:")
    trial = study.best_trial

    print("  Value: ", trial.value)

    print("  Params: ")
    for key, value in trial.params.items():
        print("    {}: {}".format(key, value))


import streamlit as st

st.set_page_config(page_title="Invoice System", layout="wide")
st.title("Invoice System")

col1, col2 = st.columns(2)
invoice_no = col1.text_input("Invoice Number", key="invoice_no")
invoice_date = col1.text_input("Invoice Date", key="invoice_date")
sold_to = col1.text_input("Sold To", key="sold_to")
ship_to = col1.text_input("Ship To", key="ship_to")
po_no = col1.text_input("PO Number", key="po_no")
payment_terms = col1.radio("Payment Terms", ("30 Days Net", "45 Days Net", "60 Days Net", "Others"), key="payment_terms")
due_date = col1.text_input("Due Date", key="due_date")
total_amt = col1.number_input("Total Amount", key="total_amt")

if st.button("Submit"):
    st.write("Invoice record submitted")


import math
import sys

resualt = 0
visit = []

def is_prime_num(n):
    arr = [True] * (n + 1)
    tmp = []
    arr[0] = False
    arr[1] = False

    for i in range(2, int(math.sqrt(n)+1)):
        if arr[i] == True:
            j = 2

            while (i * j) <= n:
                arr[i*j] = False
                j += 1

    for i in range(4000000):
        if arr[i]:
            tmp.append(i)

    return tmp

def dfs(x, deep):
    global target, data, resualt

    visit[deep] = 1

    if x == target:
        return 1

    if x + data[deep] > target:
        return 0

    return  dfs(x + data[deep], deep + 1) +  dfs(x, deep + 1)

if __name__ == '__main__':
    sys.setrecursionlimit(10**7)
    data = is_prime_num(4000000)

    visit = [0 for _ in data]
    target = int(input())

    print(f'{dfs(0, 0)}')


# -*- coding: utf-8 -*-
from nltk.collocations import BigramCollocationFinder
from nltk.metrics import BigramAssocMeasures
from nltk.probability import FreqDist, ConditionalFreqDist
import itertools
# nltk words processing 可以采取的方法和步骤
# 1 特征提取
#     a 所有词
#     b 双词搭配
#     c 所有词和双词
# 2 特征选择
#     a 计算整个语料中每个词的信息量
#     b 按照信息量倒排，选择排名靠前信息量大的词
#     c 选择这些词作为训练特征

# 说明：
# 一、1,2,3不适合处理短文本（< 10个词）(增大ratio值，会使卡方统计的效果减弱，等于1时相当于没有使用)
# 二、4，5对短文本的处理效果可能会与语料质量关系比较大，语料质量不好会使短文本中的词被过滤所剩无几甚至为空的概率很大
#
# 方法一：所有词作为特征
# 词 -> 特征
def bag_of_words(words):
    return dict([(word, True) for word in words])

# 方法二：双词搭配，卡法统计，取一定比例的词，作为特征
# 词 -> 双词 -> 特征
# score_fn：BigramAssocMeasures.chi_sq - 卡方统计法；BigramAssocMeasures.pmi - 互信息方法
def bigram(words, ratio=0.3, use_fn=True, score_fn=BigramAssocMeasures.chi_sq):
    bigram_finder = BigramCollocationFinder.from_words(words, window_size=2)
    num = int(len(words) * ratio)
    bigrams = bigram_finder.nbest(score_fn, num)
    return bag_of_words(bigrams)

# 方法三：所有词+双词，卡方统计，取一定比例的词，作为特征
# 词 + 双词 -> 特征
def bigram_words(words, ratio=0.3, use_fn=True, score_fn=BigramAssocMeasures.chi_sq):
    bigram_finder = BigramCollocationFinder.from_words(words, window_size=2)
    num = int(len(words) * ratio)
    bigrams = bigram_finder.nbest(score_fn, 20)
    return bag_of_words(words + bigrams)

# 方法四：所有词作为特征，并计算其在所有语料中的信息量
# words: 所有label及其words list， 如：
# {'pos':[['aa','bb', 'cc'], ['dd','ee','ff']], 'neg':[['AA','BB', 'CC'], ['DD','EE','FF']]}
def get_word_scores(label_words, ratio=1):
    label_words_freq = {}
    label_words_count = {}
    all_words_plain = []
    for key, val in label_words.items():
        # -> ['aa','bb', 'cc','dd','ee','ff', 'AA','BB', 'CC', 'DD','EE','FF']
        plain_words = list(itertools.chain(*val))
        all_words_plain.extend(plain_words)
        # -> {'pos':{'aa':1,'bb':1, 'cc':1,'dd':1,'ee':1,'ff':1}, ...}
        freq_dist = FreqDist(plain_words)
        label_words_freq[key] = freq_dist
        count = freq_dist.N()
        # -> {'pos': 3, 'neg': 3}
        label_words_count[key] = count
    all_words_freq = FreqDist(all_words_plain)
    total_count = all_words_freq.N()
    word_scores = {}
    for word, freq in all_words_freq.items():
        score = 0
        for key in label_words.keys():
            score += BigramAssocMeasures.chi_sq(
                label_words_freq[key][word],
                (freq, label_words_count[key]),
                total_count)
        word_scores[word] = score
    return word_scores

# 方法五：所有词+双词作为特征，并计算其在所有语料中的信息量
# words: 所有label及其words list， 如：
# {'pos':[['aa','bb', 'cc'], ['dd','ee','ff']], 'neg':[['AA','BB', 'CC'], ['DD','EE','FF']]}
def get_word_bigrams_scores(label_words, ratio=0.3):
    label_words_freq = {}
    label_words_count = {}
    all_words_plain = []
    for key, val in label_words.items():
        # -> ['aa','bb', 'cc','dd','ee','ff', 'AA','BB', 'CC', 'DD','EE','FF']
        plain_words = list(itertools.chain(*val))
        bigram_finder = BigramCollocationFinder.from_words(plain_words)
        bigrams = bigram_finder.nbest(BigramAssocMeasures.chi_sq, int(len(plain_words) * ratio))
        words_bigrams = plain_words + bigrams
        all_words_plain.extend(words_bigrams)
        # -> {'pos':{'aa':1,'bb':1, 'cc':1,'dd':1,'ee':1,'ff':1}, ...}
        freq_dist = FreqDist(words_bigrams)
        label_words_freq[key] = freq_dist
        count = freq_dist.N()
        # -> {'pos': 3, 'neg': 3}
        label_words_count[key] = count
    all_words_freq = FreqDist(all_words_plain)
    total_count = all_words_freq.N()
    word_scores = {}
    for word, freq in all_words_freq.items():
        score = 0
        for key in label_words.keys():
            score += BigramAssocMeasures.chi_sq(
                label_words_freq[key][word],
                (freq, label_words_count[key]),
                total_count)
        word_scores[word] = score
    return word_scores

def find_best_words(word_scores, ratio=0.3):
    # 把词按信息量倒序排序。number是特征的维度，是可以不断调整直至最优的
    number = int(len(word_scores) * ratio)
    best_vals = sorted(word_scores.items(), key=lambda w_s: w_s[1], reverse=True)[:number]
    best_words = set([w for w, s in best_vals])
    return best_words

def best_word_features(words, best_words):
    return dict([(word, True) for word in words if word in best_words])

# words: {'pos':[['aa','bb', 'cc'], ['dd','ee','ff']], 'neg':[['AA','BB', 'CC'], ['DD','EE','FF']]}
def filter_with_nbest_words(words, nbest):
    features_list = []
    for key, val in words.items():
        for ws in val:
            best_features = dict([(word, True) for word in ws if word in nbest])
            if len(best_features) == 0:
                print('------ len(best_features) = 0')
                continue
            features = [best_features, key]
            features_list.append(features)
    return features_list

# words_list example: [['aa', 'bb', 'cc'], ['dd', 'ee', 'ff'], ['AA', 'BB', 'CC'], ['DD', 'EE', 'FF']]
def label_features(feature_method, words_list, label, extract_ratio=0.3):
    features_list = []
    for words in words_list:
        features = [feature_method(words, extract_ratio), label]
        features_list.append(features)
    return features_list

# 'data' 已经可以直接feed给model进行训练，这里对'data'重新删选feature，然后再恢复成与'data'相同的格式
# 由于时间和编写的原因，可以整合成为一个逻辑，不需要进行两遍操作
# data example:
# [[{'aa':True, 'bb':True, 'cc':True}, 'pos'], [{'AA':True, 'BB':True, 'CC':True}, 'neg']]


""" add additional DAGs folders to be used by airflow to check where to look for dag files """
import os
import json
import importlib
import textwrap
import inspect
from datetime import datetime, timedelta

from airflow.models.baseoperator import BaseOperator
from airflow.models import DagBag

dag_bag = DagBag()

def __add_import(what, imports, module=None, alias=None):
    if imports is None:
        return
    if module is None:
        imp = f'import {what}'
    else:
        imp = f'from {module} import {what}'
    if alias is not None:
        imp += f' as {alias}'
    imports.add(imp)

def stringify_value(value, indentation=0, imports=None):
    if value is None:
        return 'None'
    if isinstance(value, datetime):
        __add_import('datetime', imports)
        return f'datetime.datetime({value.year}, {value.month}, {value.day}, {value.hour}, {value.minute}, {value.second})'
    if isinstance(value, timedelta):
        __add_import('datetime', imports)
        return f'datetime.timedelta(days={value.days}, seconds={value.seconds})'
    if isinstance(value, bool) or isinstance(value, int) or isinstance(value, float):
        return f'{value}'
    if isinstance(value, str) and '\n' in value:
        return textwrap.indent(f'\'\'\'{value}\'\'\'', '    ' * indentation)
    if isinstance(value, str):
        return json.dumps(value)  # f'\'{value}\''
    if isinstance(value, dict):
        value_str = '{  '
        for k, v in value.items():
            value_str += f'"{k}": {stringify_value(v, indentation, imports)}, '
        value_str = value_str[:-2] + '}'
        return value_str
    if isinstance(value, list):
        value_str = '[  '
        for v in value:
            value_str += f'{stringify_value(v, indentation, imports)}, '
        value_str = value_str[:-2] + ']'
        return value_str
    if callable(value):
        return inspect.getsource(value)
    return f'{value}'

def collect_dags_in_dag_bag(dag_bag):
    current_file_path = os.path.join(
        os.path.dirname(__file__), 'kirby/scheduled/dags')
    base_path = os.path.join(current_file_path, 'projects')
    dags_dirs = os.listdir(base_path)

    def process_file(file_path):
        # TODO: Use this method istead of dag_bag.process_file
        if __file__.endswith(os.path.basename(file_path)) or not file_path.endswith('.py') or not os.path.isfile(file_path):
            return
        file_path = file_path.replace(current_file_path, '').strip('/')
        module = importlib.import_module(
            file_path[:-len('.py')].replace('/', '.'))
        # for key, data in inspect.getmembers(module, inspect.isclass):
        # dag_bag.process_file(os.path.join(, f))

    for f in os.listdir(current_file_path):
        if __file__.endswith(f) or not f.endswith('.py'):
            continue
        dag_bag.process_file(os.path.join(current_file_path, f))

    for d in dags_dirs:
        folder_path = os.path.join(base_path, d)
        if not os.path.isdir(folder_path):
            continue
        dags_files = os.listdir(folder_path)
        for f in dags_files:
            if f.endswith('.py'):
                dag_bag.process_file(os.path.join(base_path, d, f))
    for dag_id, dag in dag_bag.dags.items():
        globals()[dag_id] = dag

def get_operators():
    temp_dagbag = DagBag()
    collect_dags_in_dag_bag(temp_dagbag)
    operators = {}
    for dag in temp_dagbag.dags.values():
        for task in dag.tasks:
            task_type = task.task_type
            operators[task_type] = {}
            operators[task_type]['operator_name'] = task_type
            operators[task_type]['operator_module'] = task.__module__
            operators[task_type]['operator_about'] = task.__doc__
            arguments = {}
            classes = [task.__class__]
            super_classes = list(task.__class__.__bases__)
            while super_classes:
                current_class = super_classes.pop()
                if issubclass(current_class, BaseOperator) or current_class == BaseOperator:
                    classes.append(current_class)
                    super_classes += list(current_class.__bases__)
            for task_class in classes:
                for parameter in inspect.signature(task_class.__init__).parameters.values():
                    if parameter.name == 'self':
                        continue
                    formated_param = inspect.formatargspec([parameter])[
                        1:-1].split(':')
                    param = {}
                    param['name'] = parameter.name
                    if len(formated_param) > 1:
                        annotations = formated_param[1].split('=')
                        param['data_type'] = annotations[0]
                        if len(annotations) > 1:
                            param['default'] = annotations[1]
                    elif '=' in formated_param[0]:
                        annotations = formated_param[0].split('=')
                        param['default'] = annotations[1]
                    arguments[param['name']] = {
                        **param,
                        # First class in classes list should have priority for default values
                        **arguments.get(param['name'], {})
                    }
            operators[task_type]['arguments'] = list(arguments.values())
    return operators

def __override_operators(operators, task_instances):
    def constructor_override(op_name, op_init):
        def constructor_override(self, *args, **kwargs):
            # task_type = type(self).__name__
            op_init(self, *args, **kwargs)
            task_id = kwargs.get('task_id')
            dag_id = kwargs.get('dag').dag_id
            task_instances[f'{dag_id}_{task_id}'] = {
                'operator_name': op_name,
                'arguments': {k: stringify_value(v) for k, v in kwargs.items()}
            }
        return constructor_override

    op_resets = {}
    for op in operators.values():
        op_module = importlib.import_module(op['operator_module'])
        op_class = getattr(op_module, op['operator_name'])
        op_resets[op['operator_name'] +
                  op['operator_module']] = op_class.__init__
        op_class.__init__ = constructor_override(
            op['operator_name'], op_class.__init__)
    return op_resets

def __reset_operators(operators, resets):
    for op in operators.values():
        op_module = importlib.import_module(op['operator_module'])
        op_class = getattr(op_module, op['operator_name'])
        op_class.__init__ = resets[op['operator_name'] + op['operator_module']]

def get_task_instances(operators):
    task_instances = {}
    resets = __override_operators(operators, task_instances)
    temp_dagbag = DagBag()
    collect_dags_in_dag_bag(temp_dagbag)
    # for dag in temp_dagbag.dags.values():
    #     for task in dag.tasks:
    #         arguments = filter_operator_arguments(operators[task.task_type], task)
    #         task_instances[f'{dag.dag_id}_{task.task_id}'] = {
    #             'operator_name': task.task_type,
    #             'arguments': arguments
    #         }
    __reset_operators(operators, resets)
    return task_instances

def get_dags():
    temp_dagbag = DagBag()
    collect_dags_in_dag_bag(temp_dagbag)
    return temp_dagbag

def filter_operator_arguments(operator, self):
    '''
    OBS: This method assumes that we assign the parameters to self!
    '''
    arguments = operator['arguments']
    res = {}
    for i, arg in enumerate(arguments):
        res[arg['name']] = {
            'name': arg['name'],
            'value': stringify_value(getattr(self, arg['name'])),
            'default': arg.get('default') or '"None"'
        }
    return res

def filter_operator_arguments2(operator, task_locals):
    arguments = operator['arguments']
    res = {}
    for i, arg in enumerate(arguments):
        if len(task_locals.get('args', [])) > i:
            res[arg] = task_locals['args'][i]
        elif task_locals['kwargs'].get(arg['name']) is not None:
            res[arg['name']] = task_locals['kwargs'][arg['name']]
        elif task_locals.get(arg['name']) is not None and arg['name'] not in ['args', 'kwargs']:
            res[arg['name']] = task_locals[arg['name']]
        elif arg['name'] == 'kwargs' and 'kwargs' in task_locals:
            res[arg['name']] = task_locals['kwargs']
            # for param, value in task_locals['kwargs'].items():
            #     res[param] = value
    return res


import FWCore.ParameterSet.Config as cms

process = cms.Process("SKIM")

process.maxEvents = cms.untracked.PSet(
    input = cms.untracked.int32(-1)
)

evtListFile = open("candEvts.txt","r").readlines()
evtList = []
for k in evtListFile:
    evtList.append(k.rstrip())

fileListFile = open("fileList.txt","r").readlines()
fileList = []
for k in fileListFile:
    fileList.append(k.rstrip())

process.source = cms.Source("PoolSource",
    fileNames = cms.untracked.vstring(fileList),

    eventsToProcess = cms.untracked.VEventRange(evtList)
)

process.load("Configuration.EventContent.EventContent_cff")
process.hltPoolOutput = cms.OutputModule("PoolOutputModule",
    fileName = cms.untracked.string('file:candEvts.root'),
    overrideInputFileSplitLevels=cms.untracked.bool(True)
)
process.HLTOutput = cms.EndPath( process.hltPoolOutput)


import os
from key_NOT_FOR_GITHUB import key
import openai
from flask import Flask, redirect, render_template, request, url_for

app = Flask(__name__)
openai.api_key = key

# Classifiers are the easiest models to get started with.
# For classification problems we suggest using !____ada____!, which generally tends to perform only
# very slightly worse than more capable models once fine-tuned,
# whilst being significantly faster and cheaper.

@app.route("/", methods=("GET", "POST"))
def index():
    if request.method == "POST":
        animal = request.form["animal"]
        response = openai.Completion.create(
            model="davinci:ft-personal-2023-06-28-00-18-49",
            prompt=generate_prompt(),
            temperature=0.5,
            max_tokens=50,
        )
        return redirect(url_for("index", result=response.choices[0].text))

    result = request.args.get("result")
    print(result)
    return render_template("index.html", result=result)

def generate_prompt():
    return """Suggest one and only one color for an input:
Input: You are bad->
Color: Red###
Input: You are pretty->
Color: Pink###
Input: {}
"""

if __name__ == '__main__':
    app.run(host='localhost', port=5000, debug=True)

dish_tags = "FlavorConnect\jsons\dish-tags-row-splitted.json"

dish_tags_query = f"""{dish_tags}"""

query = f"""Use the below tags on the food preferences to answer the subsequent question. If the answer cannot be found, write "I don't know."

Tags:
\"\"\"
{dish_tags_query}
\"\"\"

Question: I want to eat hummus"""

response = openai.ChatCompletion.create(
    messages=[
        {'role': 'system', 'content': 'You answer questions about the food preferences.'},
        {'role': 'user', 'content': query},
    ],
    model="ada:ft-personal-2023-06-28-23-38-01",
    temperature=0,
)

print(response['choices'][0]['message']['content'])


import pytest
#fixtures provided via conftest

from bayesiansafety.core import BayesianNetwork
from bayesiansafety.faulttree import BayesianFaultTree
from bayesiansafety.synthesis.hybrid import HybridConfiguration
from bayesiansafety.synthesis.hybrid import HybridBuilder

def test_instantiate_success(fixture_bn_confounder_param, fixture_ft_elevator_model):
    bn_inst, _ = fixture_bn_confounder_param("BN_A")
    ft_inst, _ = fixture_ft_elevator_model

    name = "test"
    shared_nodes = {"BN_A":["NODE_A"]}
    couplings = {"BN_A":[("NODE_A", "AND_TOP")]}
    pbf_states = {"BN_A":[("NODE_A", "STATE_NODE_A_No")]}

    config = HybridConfiguration(name=name, shared_nodes=shared_nodes, ft_coupling_points=couplings, pbf_states=pbf_states)

    asso_bns = {"BN_A":bn_inst}
    builder = HybridBuilder(ft_inst=ft_inst, configuration=config, associated_bns=asso_bns)

    assert builder.ft_inst == ft_inst
    assert builder.configuration  == config
    assert builder.associated_bns  == asso_bns

@pytest.mark.parametrize("observations, expected_edges",
[ (None                                        , [("BN_A_NODE_A", "AND_TOP"), ("BN_A_NODE_B", "OR_SIG_HI")] ),
  ({"BN_A":[("NODE_C", "STATE_NODE_C_Yes")]}, [("BN_A_NODE_A", "AND_TOP"), ("BN_A_NODE_B", "OR_SIG_HI")] ),
])
def test_get_extended_fault_tree_successful(observations, expected_edges, fixture_bn_confounder_param, fixture_ft_elevator_model,):
    bn_inst, _ = fixture_bn_confounder_param("BN_A")
    ft_inst, _ = fixture_ft_elevator_model

    name = "test"
    shared_nodes = {"BN_A":["NODE_A", "NODE_B"]}
    couplings = {"BN_A":[("NODE_A", "AND_TOP"), ("NODE_B", "OR_SIG_HI")]}
    pbf_states = {"BN_A":[("NODE_A", "STATE_NODE_A_No"), ("NODE_B", "STATE_NODE_B_Yes")]}

    config = HybridConfiguration(name=name, shared_nodes=shared_nodes, ft_coupling_points=couplings, pbf_states=pbf_states)

    asso_bns = {"BN_A":bn_inst}
    builder = HybridBuilder(ft_inst=ft_inst, configuration=config, associated_bns=asso_bns)

    ext_ft_inst = builder.get_extended_fault_tree(bn_observations=observations)

    assert isinstance(ext_ft_inst, BayesianFaultTree)
    for src, dest in expected_edges:
        assert ext_ft_inst.model.has_edge(src, dest)

@pytest.mark.parametrize("at_time, fix, shareds, couplings, states,  expected_edges",
[    (    0,
        True,
        {"BN_A":["NODE_A", "NODE_B"]},
        {"BN_A":[("NODE_A", "AND_TOP"), ("NODE_B", "OR_SIG_HI")]},
        {"BN_A":[("NODE_A", "STATE_NODE_A_No"), ("NODE_B", "STATE_NODE_B_Yes")]},
        {"BN_A": [("BN_A_NODE_A", "AND_TOP"), ("BN_A_NODE_B", "OR_SIG_HI"),
            # check also linked BN nodes
            ("BN_A_NODE_A", "BN_A_NODE_B"), ("BN_A_NODE_A", "BN_A_NODE_C")]}
    ),

    (    123,
        True,
        {"BN_A":["NODE_A", "NODE_B"]},
        {"BN_A":[("NODE_A", "AND_TOP"), ("NODE_B", "OR_SIG_HI")]},
        {"BN_A":[("NODE_A", "STATE_NODE_A_No"), ("NODE_B", "STATE_NODE_B_Yes")]},
        {"BN_A": [("BN_A_NODE_A", "AND_TOP"), ("BN_A_NODE_B", "OR_SIG_HI"),
            # check also linked BN nodes
            ("BN_A_NODE_A", "BN_A_NODE_B"), ("BN_A_NODE_A", "BN_A_NODE_C")]}
    ),

    (    456,
        True,
        {"BN_A":["NODE_A", "NODE_B"], "BN_B":["NODE_B"]},
        {"BN_A":[("NODE_A", "AND_TOP"), ("NODE_B", "OR_SIG_HI")], "BN_B":[("NODE_B", "OR_SIG_LO")]},
        {"BN_A":[("NODE_A", "STATE_NODE_A_No"), ("NODE_B", "STATE_NODE_B_Yes")], "BN_B":[("NODE_B", "STATE_NODE_B_Yes")]},
        {"BN_A": [("BN_A_NODE_A", "AND_TOP"), ("BN_A_NODE_B", "OR_SIG_HI"),
            # check also linked BN nodes
            ("BN_A_NODE_A", "BN_A_NODE_B"), ("BN_A_NODE_A", "BN_A_NODE_C")],
         "BN_B":[("BN_B_NODE_B", "OR_SIG_LO"),
            # check also linked BN nodes
            ("BN_B_NODE_A", "BN_B_NODE_B"), ("BN_B_NODE_A", "BN_B_NODE_C")]}
    ),

    (    789,
        False,
        {"BN_A":["NODE_A", "NODE_B"], "BN_B":["NODE_B"]},
        {"BN_A":[("NODE_A", "AND_TOP"), ("NODE_B", "OR_SIG_HI")], "BN_B":[("NODE_B", "OR_SIG_LO")]},
        {"BN_A":[("NODE_A", "STATE_NODE_A_No"), ("NODE_B", "STATE_NODE_B_Yes")], "BN_B":[("NODE_B", "STATE_NODE_B_Yes")]},
        [("BN_A_NODE_A", "AND_TOP"), ("BN_A_NODE_B", "OR_SIG_HI"), ("BN_B_NODE_B", "OR_SIG_LO"),
            # check also linked BN nodes
            ("BN_A_NODE_A", "BN_A_NODE_B"), ("BN_A_NODE_A", "BN_A_NODE_C"),
            ("BN_B_NODE_A", "BN_B_NODE_B"), ("BN_B_NODE_A", "BN_B_NODE_C")]
    )
])
def test_get_hybrid_networks_returns_correct(at_time, fix, shareds, couplings, states,  expected_edges, fixture_bn_confounder_param, fixture_ft_elevator_model,):
    ft_inst, _ = fixture_ft_elevator_model
    config = HybridConfiguration(name=ft_inst.name, shared_nodes=shareds, ft_coupling_points=couplings, pbf_states=states)

    asso_bns = {}
    for bn_name in shareds.keys():
        bn_inst, _ = fixture_bn_confounder_param(bn_name)
        asso_bns[bn_name] = bn_inst

    builder = HybridBuilder(ft_inst=ft_inst, configuration=config, associated_bns=asso_bns)

    hybrid_networks = builder.get_hybrid_networks(at_time=at_time, fix_other_bns=fix)

    assert all(isinstance(bn, BayesianNetwork) for bn in hybrid_networks.values())
    assert all(str(at_time) in bn_name for bn_name in hybrid_networks.keys())

    if fix is True:
        for bn_name, exp_edges in expected_edges.items():
            for hybrid_name, bn_inst in hybrid_networks.items():
                if bn_name in hybrid_name:
                    for src, dest in exp_edges:
                        assert bn_inst.has_edge(src, dest)

    else:
        assert len(hybrid_networks) == 1
        first_bn_name = next(iter(hybrid_networks))

        for src, dest in expected_edges:
            assert hybrid_networks[first_bn_name].has_edge(src, dest)

def test_get_hybrid_networks_negative_time_raises_excpetion(fixture_hybrid_config, fixture_bn_confounder_param, fixture_ft_elevator_model,):
    invalid_time = -123
    bn_inst, _ = fixture_bn_confounder_param("BN_A")
    ft_inst, _ = fixture_ft_elevator_model
    config = fixture_hybrid_config("test")

    asso_bns = {"BN_A":bn_inst}
    builder = HybridBuilder(ft_inst=ft_inst, configuration=config, associated_bns=asso_bns)

    expected_exc_substring = "A time stamp for evaluation can not be negative but was requested at time"

    with pytest.raises(ValueError) as e:
        assert builder.get_hybrid_networks(at_time=invalid_time, fix_other_bns=True)

    assert expected_exc_substring in str(e.value)


DOMAIN = "pills"

CONF_PILL_NAME = "pill_name"
CONF_PILL_SIZE = "pill_size"
CONF_PILL_VENDOR = "pill_vendor"
CONF_SENSOR_BEFORE_EMPTY = "pill_warning_days_before_empty"
CONF_PILLS = "pills"
CONF_PILL_AGENT = "pill_agent"

PILLS_MORNING = "pills_taking_morning"
PILLS_NOON = "pills_taking_noon"
PILLS_EVENING = "pills_taking_evening"
PILLS_NIGHT = "pills_taking_night"
PILLS_SUPPLY = "pills_supply"
UNIT = "pcs."

ATTR_DAILY = "daily"
ATTR_DAYS_REMAINING = "days_remaining"

SERVICE_TAKE = "take_pills"

STRING_PROBLEM_ENTITY = "binary_sensor"
STRING_SENSOR_ENTITY = "sensor_entity"

STRING_SUPPLY_ENTITY = "supply_entity"
STRING_MORNING_ENTITY = "morning_entity"
STRING_NOON_ENTITY = "noon_entity"
STRING_EVENING_ENTITY = "evening_entity"
STRING_NIGHT_ENTITY = "night_entity"