#Dependencies
"""
!pip install -U sklearn
!pip install pmdarima
!pip install river
!pip install tslearn
!pip install arch
!pip install skorch
"""

#Imports

import json
import math
import calendar
from datetime import timedelta
from datetime import datetime as dt
import numpy as np
import pandas as pd
import warnings
import os
import sys
warnings.simplefilter('ignore')
#os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
#os.environ['CUDA_VISIBLE_DEVICES'] = '-1'

import mlflow
import mlflow.statsmodels
from mlflow import log_metric, log_param, log_metrics, log_params, log_artifact, log_artifacts
import optuna
from urllib.parse import urlparse

from pmdarima.arima import auto_arima as pmautoarima
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX as SARIMA

import statsmodels.api as sm
import statsmodels.graphics as smg
import statsmodels.stats as sm_stats
import statsmodels.tsa.api as tsa
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

import pickle
import joblib
import sklearn
from sklearn import preprocessing
from sklearn.svm import SVR
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score, mean_absolute_percentage_error

import torch
import torch.nn as nn
import torch.optim as optim

import tensorflow as tf
from tensorflow import keras
from keras.models import Sequential
from keras import layers

from river import drift

#visualization
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme()

import logging

logging.basicConfig(level=logging.WARN)
logger = logging.getLogger(__name__)

device = 'cuda' if torch.cuda.is_available() else 'cpu'

def get_data(folder, filename):
    if folder == 'covid':
       df = pd.read_csv('./data/'+folder+'/'+filename, index_col=0)
       #df = df.loc[df.index < '2022-01-01']
       df = df.iloc[:710]
    return df

def fix_outliers(df,q = .99, zero = True):
  # cortar período inicial sem casos
  for k,d in enumerate(df):
    if k > 0 and d > df.iloc[k-1]:
      break
  df = df.iloc[k:]
  # substituir zeros
  if zero == False:
    df = df.mask(df == 0, np.nan).fillna(method='bfill')
  # converter valores extremos para NaN e substituir pelo anterior
  df = df.mask(df > df.quantile(q), np.nan).fillna(method='bfill')
  df = df.mask(df < 0, np.nan).fillna(0)
  return df

def train_val_test_split(data, train_size:float, val_size:float):
    train_data = data.iloc[:int(len(data)*train_size)]
    val_data = data.iloc[int(len(data)*train_size):int(len(data)*(train_size+val_size))]
    test_data = data.iloc[-int(len(data)*(1-train_size-val_size)):]
    return train_data, val_data, test_data

def draw_data(country, train,val,test):
    fig, ax = plt.subplots()
    ax.plot(train, color='black')
    ax.plot(val, color='darkgray')
    ax.plot(test, color='lightgray')
    plt.savefig('outputs/'+country+'/data.png')
    # Log an artifact (output file)
    return True

def get_drifts(data, col, detector='adwin'):
    if (detector == 'adwin'):
        drift_detector = drift.ADWIN(delta=0.001)

    data_diff = data.to_dict()[col]
    drifts =[]
    drift_data = {}

    for k in data_diff:
        #print(k)
        in_drift, in_warning = drift_detector.update(data_diff[k])
        if in_drift:
            drifts.append(k)
            #print(f"Change detected at index {k}, input value: {data[k]}")
    for key, drift_point in enumerate(drifts):
        if key == 0:
            drift_data[key] = data[:drift_point]
        elif key == len(drifts)-1:
            drift_data[key] = data[drifts[key-1]:drift_point]
            drift_data[key+1] = data[drift_point:]
        else:
            drift_data[key] = data[drifts[key-1]:drift_point]

    return drifts, drift_data

def draw_drifts(country, drifts, drift_data, train_data):
  fig, (ax1, ax2) = plt.subplots(2,1,figsize=(18,8), sharex=True)
  for d in range(len(drift_data)):
    ax1.plot(drift_data[d].fillna(method='bfill'))

  #print(drifts)
  #print(train_data.loc[drifts].values[0])
  #print(train_data.loc[drifts].values[0,:])
  #print(train_data.loc[drifts].values[:,0])
  ax1.bar(x=train_data.loc[drifts].index, height=train_data.loc[drifts].values[:,0], width=2, color='r')
  #ax1.annotate(train_data.loc[drifts].index, xy=(10, -100),  xycoords='axes points', xytext=(train_data.loc[drifts].index, -150), textcoords='data')
  """
  for drift_point in pd.to_datetime(drifts, format="%m/%d/%y"):
      print(drift_point)
      print(drift_point.date())
      #ax1.annotate(k, xy=(10, 100),  xycoords='axes points', xytext=(drift_point, -10), textcoords='data')
      #ax1.annotate(drift_point.date().strftime('%Y-%m-%d'), xy=(10, -100),  xycoords='axes points', xytext=(drift_point-delta(days=10), -150), textcoords='data', rotation=90)
      ax1.annotate(drift_point.date(), xy=(10, -100),  xycoords='axes points', xytext=(train_data.loc[drifts].index, -150), textcoords='data')
      #ax1.annotate(k, xy=(10, -5000),  xycoords='axes points', xytext=(dt.strptime(drift_point, "%m/%d/%y")-timedelta(days=20), -5500), textcoords='data')
  """
  ax2.plot(train_data.cumsum())
  plt.savefig('outputs/'+country+'/drifts.png')
  # Log an artifact (output file)

  return True

def random_walk(data):
    return data.shift(1).dropna()

def train_arima(train_data, sarima=False):
    if sarima == True:
        arima_order = pmautoarima(train_data, max_p=7, d=1, max_q=7, m=7, seasonal=True, trace=True, information_criterion='aic', suppress_warnings=True, maxiter = 50, stepwise=True)
        arima_base = ARIMA(train_data, order=arima_order.order, seasonal_order=arima_order.seasonal_order)
        #log_param("order", arima_order.order)
        #log_param("seasonal_order", arima_order.seasonal_order)
    else:
        arima_order = pmautoarima(train_data, max_p=7, d=1, max_q=7, m=7, seasonal=False, trace=True, information_criterion='aic', suppress_warnings=True, maxiter = 50, stepwise=True)
        arima_base = ARIMA(train_data, order=arima_order.order)
        #log_param("order", arima_order.order)
    arima_model = arima_base.fit()
    return arima_model

def update_arima(model, test_data):
    arima_updated = model.apply(test_data)
    return arima_updated

class ELM():
    def __init__(self, input_size, h_size, activation, device=None):
        self._input_size = input_size
        self._h_size = h_size
        self._output_size = 1
        self._device = device
        self.activation_name = activation

        self._alpha = nn.init.uniform_(torch.empty(self._input_size, self._h_size, device=self._device), a=-1., b=1.)
        self._beta = nn.init.uniform_(torch.empty(self._h_size, self._output_size, device=self._device), a=-1., b=1.)

        self._bias = torch.zeros(self._h_size, device=self._device)

        if activation == 'tanh':
            self._activation = torch.tanh
        elif activation == 'relu':
            self._activation = torch.relu
        elif activation == 'sigmoid':
            self._activation = torch.sigmoid

    def predict(self, x):
        h = self._activation(torch.add(x.mm(self._alpha), self._bias))
        out = h.mm(self._beta)

        return out

    def fit(self, x, t):
        temp = x.mm(self._alpha)
        H = self._activation(torch.add(temp, self._bias))

        H_pinv = torch.pinverse(H)
        self._beta = H_pinv.mm(t)

def splitter(data, lags):
    X = pd.DataFrame(tsa.add_lag(data, lags=lags))
    y = X.pop(0)
    return X, y

def normalizer(input, fit=True, transform=1):
    """ pass normalizer to fit """
    MinMax = preprocessing.MinMaxScaler(feature_range=(-1,1))
    if fit == True:
        out = MinMax.fit_transform(input)
        return MinMax, out
    else:
        if transform == -1:
            out = fit.inverse_transform(input)
        else:
            out = fit.transform(input)
        return out

def torcher(data):
    #print(data)
    #print(torch.from_numpy(data).float())
    return torch.from_numpy(data).float()

def torch_data(train_data, val_data, test_data, lags):

    #train_data, val_data, test_data = train_val_test_split(get_data(), 0.7, 0.2)

    X_train = pd.DataFrame(tsa.add_lag(train_data, lags =lags))
    #X_train = pd.DataFrame(tsa.add_lag(train_data.diff().dropna(), lags =14))
    y_train = torch.from_numpy(X_train.pop(0).values.reshape(-1,1)).float()

    X_val = pd.DataFrame(tsa.add_lag(val_data, lags =lags))
    #X_train = pd.DataFrame(tsa.add_lag(train_data.diff().dropna(), lags =14))
    y_val = torch.from_numpy(X_val.pop(0).values.reshape(-1,1)).float()

    #X_test = pd.DataFrame(tsa.add_lag(test_data, lags =14))
    X_test = pd.DataFrame(tsa.add_lag(test_data, lags =lags))
    y_test = torch.from_numpy(X_test.pop(0).values.reshape(-1,1)).float()

    #normalization ELM
    min_value = float(train_data.min())
    base = float(train_data.max()) - float(train_data.min())

    X_train_mm = ((X_train - min_value)/base).to_numpy()
    X_train_mm = torch.from_numpy(X_train_mm).float()
    X_val_mm = ((X_val - min_value)/base).to_numpy()
    X_val_mm = torch.from_numpy(X_val_mm).float()
    X_test_mm = ((X_test - min_value)/base).to_numpy()
    X_test_mm = torch.from_numpy(X_test_mm).float()

    return X_train_mm, y_train, X_val_mm, y_val, X_test_mm, y_test

def objective_elm(trial):
    #h_size = trial.suggest_int("h_size", 2, 20)
    h_size = trial.suggest_categorical('h_size', [8, 16, 32, 64, 100, 200])
    activation = trial.suggest_categorical("activation", ["sigmoid", "tanh", "relu"])
    # Generate the model.
    results = np.zeros(10)

    for i in range(10):
        model = ELM(input_size=14, h_size=h_size, activation=activation, device=device) #variar o input-size (FIX)
        model.fit(X_train_mm, y_train)
        y_pred = model.predict(X_val_mm)
        mse = -mean_squared_error(y_val, y_pred)
        results[i] = mse

    return results.mean()

def train_elm(x, y):
    #optimization
    study = optuna.create_study(direction="maximize", study_name='elm_study')
    study.optimize(objective_elm, n_trials=50, show_progress_bar=True)
    params = study.best_params
    best_elm = ELM(input_size=14, h_size=params['h_size'], activation=params['activation'], device=device) #variar o input-size (FIX)
    best_elm.fit(x, y)
    return best_elm

def objective_svm(trial):
    svr_k = trial.suggest_categorical('kernel',['linear', 'rbf'])
    #svr_k = trial.suggest_categorical('kernel',['rbf'])
    svr_g = trial.suggest_categorical("gamma", [1, 0.1, 0.01, 0.001]) #auto, scale
    #svr_g = trial.suggest_float("gamma", 0.001, 1, log=True) #auto, scale
    svr_c = trial.suggest_categorical("C", [0.1, 1, 100, 1000, 10000])
    #svr_c = trial.suggest_float("C", 0.1, 10000, log=True)
    svr_e = trial.suggest_categorical("epsilon", [0.1, 0.01, 0.001])
    #svr_e = trial.suggest_float("epsilon",0.001, 0.1, log=True)
    svr_t = trial.suggest_categorical("tolerance", [0.01, 0.001, 0.0001])
    #svr_t = trial.suggest_float("tolerance", 0.0001, 0.01, log=True)
    regressor_obj = sklearn.svm.SVR(kernel=svr_k, gamma=svr_g, C=svr_c, epsilon=svr_e, tol=svr_t)

    score = sklearn.model_selection.cross_val_score(regressor_obj, np.concatenate((X_train_mm, X_val_mm)), np.concatenate((y_train, y_val)),
                                                    n_jobs=-1, cv=TimeSeriesSplit(2, test_size=len(y_val)//2),
                                                    scoring='neg_root_mean_squared_error').mean()
    return score

def train_svm(x, y):
    #optimization
    study = optuna.create_study(direction="maximize", study_name='svr_study')
    study.optimize(objective_svm, n_trials=20, show_progress_bar=True)
    params = study.best_params
    best_svr = SVR(kernel=params['kernel'], C=params['C'], epsilon=params['epsilon'], tol=params['tolerance'])
    best_svr.fit(x, y)
    return best_svr

def objective_lstm(trial):
    model = Sequential()
    model.add(layers.LSTM(units= trial.suggest_categorical('units', [8, 16, 32, 64, 100, 200]), input_shape=(14, 1)))
    #model.add(layers.LSTM(100, input_shape=(14, 1), dropout=0.2, return_sequences=True))
    #model.add(layers.Dropout(0.2))
    #model.add(layers.LSTM(100, dropout=0.2))
    #model.add(layers.Dropout(0.2))
    model.add(layers.Dense(1, activation=trial.suggest_categorical('activation', ['relu', 'linear', 'tanh']),) )
    #model.add(layers.Dense(1, activation='linear') )

    score = np.zeros(3)
    for i in range(3):
        # We compile our model with a sampled learning rate.
        model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001), loss='mse')

        model.fit(
            X_train_mm,
            y_train,
            validation_data=(X_val_mm, y_val),
            shuffle=False,
            batch_size=32,
            epochs=50,
            verbose=False,
            callbacks=[tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3)]
        )

        # Evaluate the model accuracy on the validation set.
        score[i] = model.evaluate(X_val_mm, y_val, verbose=0)
    return -score.mean()

def lstm(input_size, units, activation):
    model = Sequential()
    model.add(layers.LSTM(units= units, input_shape=(input_size, 1)))
    #model.add(layers.Dense(1, activation='linear' ))
    model.add(layers.Dense(1, activation=activation ))
    model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.001), loss='mse')
    return model

def train_lstm(x, y):
    #optimization
    study = optuna.create_study(direction="maximize", study_name='lstm_study')
    study.optimize(objective_lstm, n_trials=20, show_progress_bar=True)
    params = study.best_params
    log_params(study.best_params)
    #best_lstm = lstm(input_size=14, units=params['units'], activation='linear') #variar o input-size (FIX)
    best_lstm = lstm(input_size=14, units=params['units'], activation=params['activation']) #variar o input-size (FIX)
    best_lstm.fit(x,
                y,
                validation_data=(X_val_mm, y_val),
                shuffle=False,
                batch_size=32,
                epochs=100,
                verbose=False,
                callbacks=[tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, restore_best_weights=True)])
    return best_lstm

def eval_metrics(actual, pred):
    rmse = mean_squared_error(actual, pred, squared=False)
    mae = mean_absolute_error(actual, pred)
    r2 = r2_score(actual, pred)
    #mape = mean_absolute_percentage_error(actual, pred)
    #print(actual)
    #print(pred)
    #print(actual.loc[actual[actual.columns[0]] > 0])
    actual_adj = actual.loc[actual[actual.columns[0]] > 0]
    pred_adj = pred.loc[actual_adj.index]
    mape = mean_absolute_percentage_error(actual_adj, pred_adj)
    return {'rmse': rmse, 'mae': mae, 'mape': mape, 'r2':r2}

def draw_predictions(country, predictions, true):
    fig, ax = plt.subplots(figsize=(10,5))
    ax.plot(predictions.reset_index(drop=True), label='pred')
    ax.plot(true.reset_index(drop=True), label='true')
    plt.legend()
    plt.savefig('outputs/'+country+'/prediction.png')

def get_oracle(predictions, true):
  df_error = predictions.iloc[1:].rsub(np.array(true.iloc[1:]), axis=0).abs()
  oracle = {}
  selection = []
  for row in df_error.rank(axis=1).idxmin(axis=1).items():
    oracle[row[0]] = predictions.at[row[0], row[1]]
    selection.append(row[1])
  return selection, pd.Series(oracle)

def post_forecast(preds):
    return preds.mask(preds < 0, 0)

def log_arts(country,model):
    log_artifacts("outputs/"+country+"/data")
    log_artifacts("outputs/"+country+"/preds/"+model)
    log_artifact('outputs/'+country+'/prediction.png')
    return True

def main():

    exp = "codrift_220518"
    mlflow.set_experiment(exp)

    if sys.argv[1] == 'help':
        print('models: rw, arima, sarima, AS, ASDS, ASO...')

    country = sys.argv[1]
    model = sys.argv[2]
    split = sys.argv[3]
    lags = int(sys.argv[4])
    size = int(sys.argv[5])
    if (model == 'ASDS' or model == 'AEDS' or model == 'ASVDS'):
        K = int(sys.argv[6])
        rname = country+'.'+model+'.'+split+'.'+str(lags)+'.'+str(size)+'.'+str(K)
    else:
        rname =  country+'.'+model+'.'+split

    print(exp)
    print(rname)
    with mlflow.start_run(run_name=rname):

            if not os.path.exists("outputs/"+country+"/data"):
                os.makedirs("outputs/"+country+"/data")
            if not os.path.exists("outputs/"+country+"/preds/"+model):
                os.makedirs("outputs/"+country+"/preds/"+model)
            if not os.path.exists("models/"+country):
                os.makedirs("models/"+country)

            data = get_data('covid', country+'_daily.csv')
            data = fix_outliers(data)
            train_data, val_data, test_data = train_val_test_split(data, 0.7, 0.2)
            #print('train: ', train_data.head(1), train_data.tail(1), len(train_data) )
            #print('val: ', val_data.head(1), val_data.tail(1), len(val_data) )
            #print('test: ', test_data.head(1), test_data.tail(1), len(test_data) )

            train_data.to_csv("outputs/"+country+"/data/train_data.csv")
            val_data.to_csv("outputs/"+country+"/data/val_data.csv")
            test_data.to_csv("outputs/"+country+"/data/test_data.csv")
            draw_data(country, train_data, val_data, test_data)
            log_artifact('outputs/'+country+'/data.png')

            global X_train_mm
            global y_train
            global X_val_mm
            global y_val
            global X_test_mm
            global y_test

            X_train_mm = 0
            y_train = 0
            X_val_mm = 0
            y_val = 0
            X_test_mm = 0
            y_test = 0

            X, y = splitter(data, lags)
            sz = len(X)
            X_train, y_train = X[:int(sz*0.7)], y[:int(sz*0.7)]
            X_val, y_val = X[int(sz*0.7):int(sz*0.9)], y[int(sz*0.7):int(sz*0.9)]
            X_test, y_test = X[int(sz*0.9)-1:], y[int(sz*0.9)-1:]

            if not os.path.exists("models/"+country+"/normx.pkl"):
                normx, X_train_mm = normalizer(X_train)
                normy, y_train_mm = normalizer(y_train.values.reshape(-1,1))
                joblib.dump(normx, "models/"+country+"/normx.pkl")
                joblib.dump(normy, "models/"+country+"/normy.pkl")
            else:
                normx = joblib.load("models/"+country+"/normx.pkl")
                normy = joblib.load("models/"+country+"/normy.pkl")

            X_val_mm = normalizer(X_val, normx)
            X_test_mm = normalizer(X_test, normx)
            y_val_mm = normalizer(y_val.values.reshape(-1,1), normy)
            y_test_mm = normalizer(y_test.values.reshape(-1,1), normy)

            if model == 'rw':
                #with mlflow.start_run(run_name=country+'.'+model+'.'+split):
                #Baseline
                    if split == 'val':
                        y_pred = post_forecast(random_walk(val_data))
                        metrics = eval_metrics(val_data[1:], y_pred)
                        draw_predictions(country, y_pred, val_data)
                    elif split == 'test':
                        y_pred = post_forecast(random_walk(test_data))
                        metrics = eval_metrics(test_data.iloc[size+1:], y_pred.iloc[size:])
                        draw_predictions(country, y_pred, test_data)
                    mlflow.set_tags({'data': country, 'split': split, 'model': model})
                    pd.DataFrame(y_pred).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')

                    log_metrics(metrics)
                    log_arts(country,model)
                    mlflow.end_run()

            if model == 'arima':
                #with mlflow.start_run(run_name=country+'.'+model+'.'+split):
                    if not os.path.exists("models/"+country+"/arima.pkl"):
                        arima = train_arima(train_data)
                        arima.save("models/"+country+"/arima.pkl")
                    else:
                        arima = sm.load("models/"+country+"/arima.pkl")
                    log_params(arima.specification)

                    with open("outputs/"+country+"/arima.txt", "w") as f:
                        f.write(arima.summary().as_text())
                    log_artifact("outputs/"+country+"/arima.txt")
                    if split == 'val':
                        arima = update_arima(arima, val_data)
                        y_pred = post_forecast(arima.predict())
                        metrics = eval_metrics(val_data, y_pred)
                        draw_predictions(country, y_pred, val_data)
                    elif split == 'test':
                        arima = update_arima(arima, test_data)
                        y_pred = post_forecast(arima.predict())
                        #metrics = eval_metrics(y_pred, test_data)
                        test_data.index = y_pred.index
                        print('mape', mean_absolute_percentage_error(test_data, y_pred))
                        metrics = eval_metrics(test_data.iloc[7:], y_pred.iloc[7:])
                        draw_predictions(country, y_pred, test_data)
                    log_metrics(metrics)
                    log_params(arima.specification)
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'size': size})
                    pd.DataFrame(y_pred).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_arts(country,model)
                    mlflow.end_run()

            if model == 'sarima':
                #with mlflow.start_run(run_name=country+'.'+model+'.'+split):
                    if not os.path.exists("models/"+country+"/sarima.pkl"):
                        sarima = train_arima(train_data, sarima=True)
                        sarima.save("models/"+country+"/sarima.pkl")
                    else:
                        sarima = sm.load("models/"+country+"/sarima.pkl")
                    log_params(sarima.specification)

                    if split == 'val':
                        sarima = update_arima(sarima, val_data)
                        y_pred = post_forecast(sarima.predict())
                        metrics = eval_metrics(val_data, y_pred)
                        draw_predictions(country, y_pred, val_data)
                    elif split == 'test':
                        sarima = update_arima(sarima, test_data)
                        y_pred = post_forecast(sarima.predict())
                        metrics = eval_metrics(test_data.iloc[7:], y_pred.iloc[7:])
                        draw_predictions(country, y_pred, test_data)
                    log_metrics(metrics)
                    log_params(sarima.specification)
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'size': size})

                    with open("outputs/"+country+"/sarima.txt", "w") as f:
                        f.write(sarima.summary().as_text())
                    log_artifact("outputs/"+country+"/sarima.txt")
                    pd.DataFrame(y_pred).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_arts(country,model)
                    mlflow.end_run()

            if model == 'svm':
                    #norm, X_train_mm = normalizer(X_train)
                    #X_val_mm = normalizer(X_val, norm)

                    X_train_mm = normalizer(X_train, normx)
                    y_train = normalizer(y_train.values.reshape(-1,1), normy).ravel()
                    X_val_mm = normalizer(X_val, normx)
                    y_val = normalizer(y_val.values.reshape(-1,1), normy).ravel()
                    X_test_mm = normalizer(X_test, normx)

                    #print(X_train_mm)
                    #print(y_train)

                    #print(np.concatenate((X_train_mm, X_val_mm), axis=0), np.concatenate((y_train, y_val), axis=0))
                #with mlflow.start_run(run_name=country+'.'+model+'.'+split):
                    if not os.path.exists("models/"+country+"/svm.pkl"):
                        svm = train_svm(X_train_mm, y_train)
                        joblib.dump(svm, "models/"+country+"/svm.pkl")
                    else:
                        svm = joblib.load("models/"+country+"/svm.pkl")

                    if split == 'test':
                        #y_pred = normalizer(svm.predict(X_test_mm).reshape(-1,1), normy, -1).flatten()
                        y_pred = svm.predict(X_test_mm).reshape(-1,1).flatten()
                        y_pred = post_forecast(pd.DataFrame(y_pred))
                        #y_pred = post_forecast(pd.DataFrame(svm.predict(X_test_mm)))
                        y_test = pd.DataFrame(y_test.reset_index(drop=True))
                        metrics = eval_metrics(y_test.iloc[7:], y_pred.iloc[7:])
                        draw_predictions(country, y_pred, y_test)
                    log_metrics(metrics)
                    log_params(svm.get_params())
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'size': size})

                    pd.DataFrame(y_pred).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_arts(country,model)
                    mlflow.end_run()

            if model == 'elm':
                    #X_train_mm, y_train, X_val_mm, y_val, X_test_mm, y_test = torch_data(train_data, val_data, test_data, lags)

                    #norm, X_train_mm = normalizer(X_train)
                    #X_val_mm = normalizer(X_val, norm)

                    X_train_mm = normalizer(X_train, normx)
                    y_train_mm = normalizer(y_train.values.reshape(-1,1), normy)
                    X_val_mm = normalizer(X_val, normx)
                    y_val_mm = normalizer(y_val.values.reshape(-1,1), normy)

                    X_test_mm = normalizer(X_test, normx)
                    X_train_mm, y_train, X_val_mm, y_val = torcher(X_train_mm), torcher(y_train_mm.reshape(-1,1)), torcher(X_val_mm), torcher(y_val_mm.reshape(-1,1))
                    #X_train_mm, y_train, X_val_mm, y_val = torcher(X_train_mm), torcher(y_train.values.reshape(-1,1)), torcher(X_val_mm), torcher(y_val.values.reshape(-1,1))
                    X_test_mm, y_test= torcher(X_test_mm), torcher(y_test.values.reshape(-1,1))

                #with mlflow.start_run(run_name=country+'.'+model+'.'+split):
                    if not os.path.exists("models/"+country+"/elm.pkl"):
                        elm = train_elm(X_train_mm, y_train)
                        torch.save(elm ,"models/"+country+"/elm.pkl")
                    else:
                        elm = torch.load("models/"+country+"/elm.pkl")

                    if split == 'test':
                        y_pred = normalizer(elm.predict(X_test_mm).numpy().reshape(-1,1), normy, -1).flatten()
                        y_pred = post_forecast(pd.Series(y_pred)).reset_index(drop=True)
                        #y_pred = post_forecast(pd.Series(elm.predict(X_test_mm).numpy().flatten()))
                        y_test = pd.DataFrame(y_test.numpy()).reset_index(drop=True)
                        metrics = eval_metrics(y_test.iloc[7:], y_pred.iloc[7:])
                        draw_predictions(country, y_pred, y_test)
                    log_metrics(metrics)
                    log_params({'h_size': elm._h_size})
                    #log_params({'h_size': elm._h_size, 'activation' :elm.activation_name})
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'size': size})

                    pd.DataFrame(y_pred).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_arts(country,model)
                    mlflow.end_run()

            if model == 'lstm':
                    #X_train_mm, y_train, X_val_mm, y_val, X_test_mm, y_test = torch_data(train_data, val_data, test_data, lags)

                    #norm, X_train_mm = normalizer(X_train)
                    #X_val_mm = normalizer(X_val, norm)

                    X_train_mm = normalizer(X_train, normx)
                    y_train = normalizer(y_train.values.reshape(-1,1), normy)
                    X_val_mm = normalizer(X_val, normx)
                    y_val = normalizer(y_val.values.reshape(-1,1), normy)

                    pd.DataFrame(X_train_mm).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_input_train.csv')
                    pd.DataFrame(y_train).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_output_train.csv')

                    X_test_mm = normalizer(X_test, normx)
                    #X_train_mm, y_train, X_val_mm, y_val = torcher(X_train_mm), torcher(y_train_mm.reshape(-1,1)), torcher(X_val_mm), torcher(y_val_mm.reshape(-1,1))
                    #X_train_mm, y_train, X_val_mm, y_val = torcher(X_train_mm), torcher(y_train.values.reshape(-1,1)), torcher(X_val_mm), torcher(y_val.values.reshape(-1,1))
                    #X_test_mm, y_test= torcher(X_test_mm), torcher(y_test.values.reshape(-1,1))

                #with mlflow.start_run(run_name=country+'.'+model+'.'+split):
                    if not os.path.exists("models/"+country+"/lstm"):
                        lstm = train_lstm(X_train_mm, y_train)
                        lstm.save("models/"+country+"/lstm")
                    else:
                        lstm = keras.models.load_model("models/"+country+"/lstm")

                    if split == 'test':
                        pd.DataFrame(X_test_mm).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_input_test.csv')
                        y_pred = lstm.predict(X_test_mm)
                        pd.DataFrame(lstm.predict(X_test_mm)).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_output_test.csv')
                        y_pred = normalizer(y_pred.reshape(-1,1), normy, -1).flatten()
                        y_pred = post_forecast(pd.Series(y_pred)).reset_index(drop=True)
                        #y_pred = post_forecast(pd.Series(elm.predict(X_test_mm).numpy().flatten()))
                        y_test = pd.DataFrame(y_test).reset_index(drop=True)
                        metrics = eval_metrics(y_test.iloc[7:], y_pred.iloc[7:])
                        draw_predictions(country, y_pred, y_test)
                    log_metrics(metrics)
                    #log_params()
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'size': size})

                    pd.DataFrame(y_pred).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_arts(country,model)
                    mlflow.end_run()

            #ADWIN-SARIMA (AS)
            if model == "AS":
                    detector = 'adwin'
                    drifts, drift_data = get_drifts(train_data, country, detector=detector)
                    draw_drifts(country, drifts, drift_data, train_data)
                    log_artifact('outputs/'+country+'/drifts.png')
                    if not os.path.exists("models/"+country+"/sarimas"):
                        os.makedirs("models/"+country+"/sarimas")
                    sarimas = {}
                    for k, dft in drift_data.items():
                        try:
                            sarimas[k] = sm.load("models/"+country+"/sarimas/sarima"+str(k)+".pkl")
                        except:
                            try:
                                sarima = train_arima(dft, sarima=True)
                                sarima.save("models/"+country+"/sarimas/sarima"+str(k)+".pkl")
                                sarimas[k] = sm.load("models/"+country+"/sarimas/sarima"+str(k)+".pkl")
                            except:
                                k -= 1
                        if split == 'val':
                            sarimas[k] = update_arima(sarimas[k], val_data)
                        elif split == 'test':
                            sarimas[k] = update_arima(sarimas[k], test_data)

                    preds = {}
                    for k, m in sarimas.items():
                        with mlflow.start_run(run_name=country+'.'+model+'.'+split+'.'+str(k), nested=True):
                            preds[k] = m.predict()
                            log_params(m.specification)
                            mlflow.set_tags({'data': country, 'split': split, 'model': model, 'submodel': k, 'drift': detector})
                            if split == 'val':
                                metrics = eval_metrics(val_data, preds[k])
                            elif split == 'test':
                                metrics = eval_metrics(test_data.iloc[size:], preds[k].iloc[size:])
                            log_metrics(metrics)
                    pd.DataFrame(preds).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_artifacts("outputs/"+country+"/data")
                    log_artifacts("outputs/"+country+"/preds/"+model)
                    mlflow.end_run()

            #ADWIN-SARIMA-ORACLE (ASO)
            if model == "ASO":
                submodel = "AS"
                detector = 'adwin'
                if not os.path.exists("outputs/"+country+"/preds/"+submodel):
                    print('execute o modelo AS antes e depois tente novamente')
                else:
                    preds = pd.read_csv("outputs/"+country+"/preds/"+submodel+"/"+submodel+'_'+split+'.csv', index_col=0, parse_dates=True)
                    if split == 'val':
                        true = pd.read_csv("outputs/"+country+"/data/val_data.csv", index_col=0, parse_dates=True)
                    elif split == 'test':
                        true = pd.read_csv("outputs/"+country+"/data/test_data.csv", index_col=0, parse_dates=True)
                    #print(preds)
                    #print(true)
                    #oracle = get_oracle(preds, true)
                    print(preds.shape)
                    print(true.shape)
                    best, oracle = get_oracle(preds, true)
                    pd.Series(best).to_csv("outputs/"+country+"/preds/"+model+"/best.csv")
                    oracle.to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    metrics = eval_metrics(true.iloc[size+1:], oracle.iloc[size:])
                    draw_predictions(country, oracle, true)
                    log_metrics(metrics)
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'drift': detector, 'size': size})
                    log_arts(country,model)
                mlflow.end_run()

        #ADWIN-SARIMA-DYNAMIC-SELECTION (ASDS)
            if model == "ASDS":
                submodel = "AS"
                size = int(sys.argv[5])
                K = int(sys.argv[6])
                lags = int(sys.argv[4])
                if not os.path.exists("outputs/"+country+"/preds/"+submodel):
                    print('execute o modelo AS antes, e tente novamente')
                else:
                    sarimas = {}
                    detector = 'adwin'
                    drifts, drift_data = get_drifts(train_data, country, detector=detector)
                    draw_drifts(country, drifts, drift_data, train_data)
                    log_artifact('outputs/'+country+'/drifts.png')
                    for k, dft in drift_data.items():
                        try:
                            sarimas[k] = sm.load("models/"+country+"/sarimas/sarima"+str(k)+".pkl")
                        except:
                            try:
                                sarima = train_arima(dft, sarima=True)
                                sarima.save("models/"+country+"/sarimas/sarima"+str(k)+".pkl")
                                sarimas[k] = sm.load("models/"+country+"/sarimas/sarima"+str(k)+".pkl")
                            except:
                                k -= 1
                        if split == 'val':
                            sarimas[k] = update_arima(sarimas[k], val_data)
                            data = val_data
                        elif split == 'test':
                            sarimas[k] = update_arima(sarimas[k], test_data)
                            data = test_data

                    data.index = pd.to_datetime(data.index)
                    preds = {}
                    errors = {}
                    selection = {}
                    for w in data.rolling(window=size):
                        if len(w) == size:
                            #print(w)
                            first = w.index[0]
                            last = w.index[-1]
                            #print(first)
                            #print(last)
                            preds[last] = {}
                            errors[last] = {}
                            selection[last] = {}
                            for k, m in sarimas.items():
                                preds[last][k] = m.predict(start=first, end=last)
                                errors[last][k] = mean_squared_error(preds[last][k], w)
                            #print(preds)
                            #print(errors[last])
                            df_error = pd.Series(errors[last]).rank()
                            #print(df_error)
                            for i in range(K):
                                try:
                                    selection[last][i] = df_error.loc[df_error == i+1].index.values[0]
                                except:
                                    #print(['*']*1000)
                                    #print(df_error.idxmin()) # solucao para ranks malucos 1.5, 2 sem 1...
                                    selection[last][i] = df_error.idxmin()
                            # #print(selection[last])
                            #selection[last] = df_error.loc[df_error < K+1].index.values[:K]
                    df_selection = pd.DataFrame(selection).T
                    df_selection.index = pd.to_datetime(df_selection.index)
                    preds_all = pd.read_csv("outputs/"+country+"/preds/"+submodel+"/"+submodel+'_'+split+'.csv', index_col=0, parse_dates=True)
                    preds_selection = {}
                    #print(preds_all)
                    for row in df_selection.iterrows():
                        preds_selection[row[0]] = preds_all.loc[row[0]].iloc[row[1]].mean()
                        #print(row[0])
                        #print(row[1])
                        #print(preds_all.loc[row[0]].iloc[row[1]])
                    preds_selection = pd.Series(preds_selection).T
                    #print(preds_selection)
                    #print(data)
                    #print(data.align(preds_selection, join='right', axis=0))
                    #metrics = eval_metrics(preds_selection, data.reindex_like(preds_selection))
                    metrics = eval_metrics(data.iloc[size:], preds_selection.iloc[1:])
                    draw_predictions(country, preds_selection, data)
                    log_metrics(metrics)
                    df_selection.to_csv("outputs/"+country+"/preds/"+model+"/selection.csv")
                    preds_selection.to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_params({'pool': 'sarimas', 'window_size': size ,'K':K, 'metric':'mse', 'distance': None})
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'drift': detector, 'size': size, 'k': k})
                    log_arts(country,model)
                    mlflow.end_run()

            #ADWIN-SVM (ASV)
            if model == "ASV":
                    detector = 'adwin'
                    drifts, drift_data = get_drifts(train_data, country, detector=detector)
                    draw_drifts(country, drifts, drift_data, train_data)
                    log_artifact('outputs/'+country+'/drifts.png')
                    if not os.path.exists("models/"+country+"/svms"):
                        os.makedirs("models/"+country+"/svms")
                    svms = {}
                    for k, dft in drift_data.items():
                        try:
                            svms[k] = joblib.load("models/"+country+"/svms/svms"+str(k)+".pkl")
                        except:
                            try:
                                X_train, y_train  = splitter(dft, lags)
                                X_val, y_val  = splitter(dft, lags) #gambiarra, é preciso definir o conjunto de validaçao dentro da janela de drift

                                X_train_mm = normalizer(X_train, normx)
                                y_train = normalizer(y_train.values.reshape(-1,1), normy).ravel()
                                X_val_mm = normalizer(X_val, normx)
                                y_val = normalizer(y_val.values.reshape(-1,1), normy).ravel()

                                svm = train_svm(X_train_mm, y_train)
                                joblib.dump(svm, "models/"+country+"/svms/svms"+str(k)+".pkl")
                                svms[k] = joblib.load("models/"+country+"/svms/svms"+str(k)+".pkl")
                            except:
                                k -= 1

                    preds = {}
                    for k, m in svms.items():
                        with mlflow.start_run(run_name=country+'.'+model+'.'+split+'.'+str(k), nested=True):
                            y_pred = normalizer(m.predict(X_test_mm).reshape(-1,1), normy, -1).flatten()
                            #y_pred = m.predict(X_test_mm).reshape(-1,1).flatten()
                            #print(y_pred)
                            y_pred = post_forecast(pd.Series(y_pred)).reset_index(drop=True)
                            preds[k] = y_pred

                            y_test = pd.DataFrame(y_test).reset_index(drop=True)
                            metrics = eval_metrics(y_test.iloc[7:], y_pred.iloc[7:])

                            log_params(m.get_params())
                            mlflow.set_tags({'data': country, 'split': split, 'model': model, 'submodel': k, 'drift': detector})
                            #metrics = eval_metrics(test_data.iloc[size:], preds[k].iloc[size:])
                            log_metrics(metrics)
                    pd.DataFrame(preds).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_artifacts("outputs/"+country+"/data")
                    log_artifacts("outputs/"+country+"/preds/"+model)
                    mlflow.end_run()

            #ADWIN-SVM-DYNAMIC-SELECTION (ASVDS)
            if model == "ASVDS":
                submodel = "ASV"
                size = int(sys.argv[5])
                K = int(sys.argv[6])
                lags = int(sys.argv[4])
                if not os.path.exists("outputs/"+country+"/preds/"+submodel):
                    print('execute o modelo ASV antes, e tente novamente')
                else:
                    detector = 'adwin'
                    drifts, drift_data = get_drifts(train_data, country, detector=detector)
                    draw_drifts(country, drifts, drift_data, train_data)
                    log_artifact('outputs/'+country+'/drifts.png')
                    svms = {}
                    for k, dft in drift_data.items():
                        try:
                            #print('carrega',k)
                            svms[k] = joblib.load("models/"+country+"/svms/svms"+str(k)+".pkl")
                        except:
                            print('erro carrega', k)

                    #X_test_mm = normalizer(X_test, norms[k])
                    #X_test_mm, y_test = torcher(X_test_mm), torcher(y_test.values.reshape(-1,1))
                    #X_test_mm = torcher(X_test_mm)
                    #print('xtest', X_test)
                    #print('ytest', y_test)
                    X_test_mm = normalizer(X_test, normx)
                    preds = {}
                    errors = {}
                    selection = {}
                    for w in range(size,len(y_test)):
                        last = w
                        first = w - size
                        preds[last] = {}
                        errors[last] = {}
                        selection[last] = {}
                        for k, m in svms.items():
                            y_pred = normalizer(m.predict(X_test_mm[first:last]).reshape(-1,1), normy, -1).flatten()
                            #y_pred = post_forecast(pd.Series(y_pred)).reset_index(drop=True)

                            preds[last][k] = pd.Series(y_pred).reset_index(drop=True)
                            #preds[last][k] = m.predict(X_test_mm[first:last])
                            errors[last][k] = mean_squared_error(y_test.iloc[first:last], preds[last][k])
                        #print(preds)
                        #print(y_test.iloc[first:last])
                        #print(errors[last])
                        df_error = pd.Series(errors[last]).rank()
                        #print(df_error)
                        for i in range(K):
                            try:
                                selection[last][i] = df_error.loc[df_error == i+1].index.values[0]
                            except:
                                #print(['*']*1000)
                                #print(df_error.idxmin()) # solucao para ranks malucos 1.5, 2 sem 1...
                                selection[last][i] = df_error.idxmin()
                        # #print(selection[last])
                        #selection[last] = df_error.loc[df_error < K+1].index.values[:K]
                    df_selection = pd.DataFrame(selection).T
                    #df_selection.index = pd.to_datetime(df_selection.index)
                    preds_all = pd.read_csv("outputs/"+country+"/preds/"+submodel+"/"+submodel+'_'+split+'.csv', index_col=0, parse_dates=True)
                    preds_selection = {}
                    #print(preds_all)
                    for row in df_selection.iterrows():
                        preds_selection[row[0]] = preds_all.loc[row[0]].iloc[row[1]].mean()
                        #print(row[0])
                        #print(row[1])
                        #print(preds_all.loc[row[0]].iloc[row[1]])
                    preds_selection = pd.Series(preds_selection).T
                    #print(preds_selection)
                    #print(data)
                    #print(data.align(preds_selection, join='right', axis=0))
                    #metrics = eval_metrics(preds_selection, data.reindex_like(preds_selection))
                    metrics = eval_metrics(pd.DataFrame(y_test).reset_index(drop=True).iloc[size:], preds_selection)
                    draw_predictions(country, preds_selection, data)
                    log_metrics(metrics)
                    pd.DataFrame(errors).to_csv("outputs/"+country+"/preds/"+model+"/errors.csv")
                    pd.DataFrame(preds).to_csv("outputs/"+country+"/preds/"+model+"/preds.csv")
                    df_selection.to_csv("outputs/"+country+"/preds/"+model+"/selection.csv")
                    preds_selection.to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_params({'pool': 'elms', 'window_size': size ,'K':K, 'metric':'mse', 'distance': None})
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'drift': detector, 'size': size, 'k': k})
                    log_arts(country,model)
                    mlflow.end_run()

            #ADWIN-SVM-ORACLE (ASVO)
            if model == "ASVO":
                submodel = "ASV"
                detector = 'adwin'
                if not os.path.exists("outputs/"+country+"/preds/"+submodel):
                    print('execute o modelo AS antes e depois tente novamente')
                else:
                    preds = pd.read_csv("outputs/"+country+"/preds/"+submodel+"/"+submodel+'_'+split+'.csv', index_col=0, parse_dates=True)
                    true = pd.read_csv("outputs/"+country+"/data/test_data.csv", index_col=0, parse_dates=True).reset_index(drop=True)
                    #print(preds.shape)
                    #print(true.shape)
                    #oracle = get_oracle(preds, true)
                    best, oracle = get_oracle(preds, true)
                    pd.Series(best).to_csv("outputs/"+country+"/preds/"+model+"/best.csv")
                    oracle.to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    metrics = eval_metrics(true.iloc[size+1:], oracle.iloc[size:])
                    draw_predictions(country, oracle, true)
                    log_metrics(metrics)
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'drift': detector, 'size': size})
                    log_arts(country,model)
                mlflow.end_run()

            #ADWIN-ELM (AE)
            if model == "AE":
                    detector = 'adwin'
                    drifts, drift_data = get_drifts(train_data, country, detector=detector)
                    draw_drifts(country, drifts, drift_data, train_data)
                    log_artifact('outputs/'+country+'/drifts.png')
                    if not os.path.exists("models/"+country+"/elms"):
                        os.makedirs("models/"+country+"/elms")
                    if not os.path.exists("models/"+country+"/norms"):
                        os.makedirs("models/"+country+"/norms")
                    elms = {}
                    for k, dft in drift_data.items():
                        try:
                            elms[k] = torch.load("models/"+country+"/elms/elm"+str(k)+".pkl")
                        except:
                            try:
                                X_train, y_train  = splitter(dft, lags)
                                X_val, y_val  = splitter(dft, lags) #gambiarra, é preciso definir o conjunto de validaçao dentro da janela de drift

                                X_train_mm = normalizer(X_train, normx)
                                y_train_mm = normalizer(y_train.values.reshape(-1,1), normy)
                                X_val_mm = normalizer(X_val, normx)
                                y_val_mm = normalizer(y_val.values.reshape(-1,1), normy)

                                #print('norm')
                                X_train_mm, y_train, X_val_mm, y_val = torcher(X_train_mm), torcher(y_train_mm.reshape(-1,1)), torcher(X_val_mm), torcher(y_val_mm.reshape(-1,1))
                                #print('torch')
                                elm = train_elm(X_train_mm, y_train)
                                #print('treinamento')

                                torch.save(elm ,"models/"+country+"/elms/elm"+str(k)+".pkl")
                                elms[k] = torch.load("models/"+country+"/elms/elm"+str(k)+".pkl")

                            except:
                                pass
                                #k -= 1
                        #joblib.dump(norm, "models/"+country+"/norms/norm"+str(k)+".pkl")

                    preds = {}
                    for k, m in elms.items():
                        with mlflow.start_run(run_name=country+'.'+model+'.'+split+'.'+str(k), nested=True):
                            X_test_mm = normalizer(X_test, normx)
                            X_test_mm = torcher(X_test_mm)
                            log_params({'h_size': m._h_size, 'activation' :m.activation_name})
                            mlflow.set_tags({'data': country, 'split': split, 'model': model, 'submodel': k, 'drift': detector})
                            y_pred = normalizer(m.predict(X_test_mm).numpy().reshape(-1,1), normy, -1).flatten()
                            y_pred = post_forecast(pd.Series(y_pred)).reset_index(drop=True)
                            #y_pred = post_forecast(pd.Series(m.predict(X_test_mm).numpy().flatten())).reset_index(drop=True)

                            y_test = pd.DataFrame(y_test).reset_index(drop=True)
                            #y_test = pd.DataFrame(y_test.numpy())
                            metrics = eval_metrics(y_test.iloc[7:], y_pred.iloc[7:])
                            log_metrics(metrics)
                            preds[k] = y_pred
                    #print(preds)
                    #print(pd.DataFrame(preds))
                    pd.DataFrame(preds).to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_artifacts("outputs/"+country+"/data")
                    log_artifacts("outputs/"+country+"/preds/"+model)
                    mlflow.end_run()

            #ADWIN-ELM-DYNAMIC-SELECTION (AEDS)
            if model == "AEDS":
                submodel = "AE"
                size = int(sys.argv[5])
                K = int(sys.argv[6])
                lags = int(sys.argv[4])
                if not os.path.exists("outputs/"+country+"/preds/"+submodel):
                    print('execute o modelo AE antes, e tente novamente')
                else:
                    detector = 'adwin'
                    drifts, drift_data = get_drifts(train_data, country, detector=detector)
                    draw_drifts(country, drifts, drift_data, train_data)
                    log_artifact('outputs/'+country+'/drifts.png')
                    elms = {}
                    for k, dft in drift_data.items():
                        try:
                            #print('carrega',k)
                            elms[k] = torch.load("models/"+country+"/elms/elm"+str(k)+".pkl")
                        except:
                            print('erro carrega', k)

                    #X_test_mm = normalizer(X_test, norms[k])
                    #X_test_mm, y_test = torcher(X_test_mm), torcher(y_test.values.reshape(-1,1))
                    #X_test_mm = torcher(X_test_mm)
                    #print('xtest', X_test)
                    #print('ytest', y_test)
                    X_test_mm = normalizer(X_test, normx)
                    X_test_mm = torcher(X_test_mm)
                    preds = {}
                    errors = {}
                    selection = {}
                    for w in range(size,len(y_test)):
                        last = w
                        first = w - size
                        preds[last] = {}
                        errors[last] = {}
                        selection[last] = {}
                        for k, m in elms.items():
                            y_pred = normalizer(m.predict(X_test_mm[first:last]).numpy().reshape(-1,1), normy, -1).flatten()
                            #y_pred = post_forecast(pd.Series(y_pred)).reset_index(drop=True)

                            preds[last][k] = pd.Series(y_pred).reset_index(drop=True)
                            #preds[last][k] = m.predict(X_test_mm[first:last])
                            errors[last][k] = mean_squared_error(y_test.iloc[first:last], preds[last][k])
                        #print(preds)
                        #print(y_test.iloc[first:last])
                        #print(errors[last])
                        df_error = pd.Series(errors[last]).rank()
                        #print(df_error)
                        for i in range(K):
                            try:
                                selection[last][i] = df_error.loc[df_error == i+1].index.values[0]
                            except:
                                #print(['*']*1000)
                                #print(df_error.idxmin()) # solucao para ranks malucos 1.5, 2 sem 1...
                                selection[last][i] = df_error.idxmin()
                        # #print(selection[last])
                        #selection[last] = df_error.loc[df_error < K+1].index.values[:K]
                    df_selection = pd.DataFrame(selection).T
                    #df_selection.index = pd.to_datetime(df_selection.index)
                    preds_all = pd.read_csv("outputs/"+country+"/preds/"+submodel+"/"+submodel+'_'+split+'.csv', index_col=0, parse_dates=True)
                    preds_selection = {}
                    #print(preds_all)
                    for row in df_selection.iterrows():
                        preds_selection[row[0]] = preds_all.loc[row[0]].iloc[row[1]].mean()
                        #print(row[0])
                        #print(row[1])
                        #print(preds_all.loc[row[0]].iloc[row[1]])
                    preds_selection = pd.Series(preds_selection).T
                    #print(preds_selection)
                    #print(data)
                    #print(data.align(preds_selection, join='right', axis=0))
                    #metrics = eval_metrics(preds_selection, data.reindex_like(preds_selection))
                    metrics = eval_metrics(pd.DataFrame(y_test).reset_index(drop=True).iloc[size:], preds_selection)
                    draw_predictions(country, preds_selection, data)
                    log_metrics(metrics)
                    pd.DataFrame(errors).to_csv("outputs/"+country+"/preds/"+model+"/errors.csv")
                    pd.DataFrame(preds).to_csv("outputs/"+country+"/preds/"+model+"/preds.csv")
                    df_selection.to_csv("outputs/"+country+"/preds/"+model+"/selection.csv")
                    preds_selection.to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    log_params({'pool': 'elms', 'window_size': size ,'K':K, 'metric':'mse', 'distance': None})
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'drift': detector, 'size': size, 'k': k})
                    log_arts(country,model)
                    mlflow.end_run()

            #ADWIN-ELM-ORACLE (AEO)
            if model == "AEO":
                submodel = "AE"
                detector = 'adwin'
                if not os.path.exists("outputs/"+country+"/preds/"+submodel):
                    print('execute o modelo AS antes e depois tente novamente')
                else:
                    preds = pd.read_csv("outputs/"+country+"/preds/"+submodel+"/"+submodel+'_'+split+'.csv', index_col=0, parse_dates=True)
                    true = pd.read_csv("outputs/"+country+"/data/test_data.csv", index_col=0, parse_dates=True).reset_index(drop=True)
                    #print(preds.shape)
                    #print(true.shape)
                    #oracle = get_oracle(preds, true)
                    best, oracle = get_oracle(preds, true)
                    pd.Series(best).to_csv("outputs/"+country+"/preds/"+model+"/best.csv")
                    oracle.to_csv("outputs/"+country+"/preds/"+model+"/"+model+'_'+split+'.csv')
                    metrics = eval_metrics(true.iloc[size+1:], oracle.iloc[size:])
                    draw_predictions(country, oracle, true)
                    log_metrics(metrics)
                    mlflow.set_tags({'data': country, 'split': split, 'model': model, 'drift': detector, 'size': size})
                    log_arts(country,model)
                mlflow.end_run()

            """
            tracking_url_type_store = urlparse(mlflow.get_tracking_uri()).scheme

            # Model registry does not work with file store
            if tracking_url_type_store != "file":

                # Register the model
                # There are other ways to use the Model Registry, which depends on the use case,
                # please refer to the doc for more information:
                # https://mlflow.org/docs/latest/model-registry.html#api-workflow
                mlflow.statsmodels.log_model(arima, "model", registered_model_name="arimao")
                mlflow.statsmodels.log_model(sarima, "model", registered_model_name="sarimao")
            else:
                mlflow.statsmodels.log_model(arima, "model")
                mlflow.statsmodels.log_model(sarima, "model")
            """

if __name__ == "__main__":
    main()


# -*- coding: utf-8 -*-
import os
import csv
import ast
from itertools import islice
import matplotlib.pyplot as plt
from sklearn import preprocessing
import numpy

"""
This script is plotting all of bad sample data
"""
if __name__ == '__main__':
    root=os.getcwd()

    bsxs=["bs1","bs2","bs3","bs4","bs5","bs6","bs7","bs8"]

    rstfile=root+"/result/RMSEdata.csv"
    plotdir=root+"/result/grpplot/"
    labels=('nrm>0.9','0.9>nrm>0.6','0.6>nrm>0.4','0.4>nrm>0.2','0.2>nrm>0.1','0.1>nrm')
    colors=("red","pink","orange","yellow", "green","cyan","c","b")

    with open(rstfile, 'r') as rslt:
        readcsv = csv.reader(rslt)
        RMSETC = []
        for row in readcsv:
            RMSETC.append([float(row[1]),float(row[2])])

    #Plotting all data of correlation of force/RMSE and TC error
    dtn=0
    allplotfile=plotdir+"alldata-bs.png"
    fig = plt.figure()
    ax3 = fig.add_subplot(111)
    plt.title("All data")
    ax3.set_xlabel("TC Err (fm 112.1/300K)")
    ax3.set_ylabel("force/RMSE (meV/A)")
    ax3.grid(True)
    plt.rcParams["legend.edgecolor"] ='green'

    for i, bsx in enumerate(bsxs):
        for j in range(0, 10):
            ax3.scatter(RMSETC[dtn][1],RMSETC[dtn][0],c=colors[i],marker='.')
            dtn=dtn+1

    left, right = ax3.get_xlim()
    ax3.set_xlim(-0.1, right*1.2)

    #ax4 is only for plotting legend of all kind of data
    ax4 = ax3.twinx()
    for i, bsx in enumerate(bsxs):
        ax4.scatter(RMSETC[0][1],RMSETC[0][0],c=colors[i],marker='.',label=bsx)
    handler4, label4 = ax4.get_legend_handles_labels()
    ax3.legend(handler4, label4,loc='upper right',title='Sample Grp',bbox_to_anchor=(0.97, 0.85, 0.14, .100),borderaxespad=0.,)
    fig.delaxes(ax4)
    plt.savefig(allplotfile)
    plt.close()


import os
import glob
import shutil
import re
import gi
gi.require_version('Limba', '1.0')
gi.require_version('AppStream', '1.0')
from gi.repository import Limba
from gi.repository import AppStream
from hashlib import sha256

from ..utils import build_cpt_path
from ..repository.models import *
from ..user import User
from ..extensions import db
from ..utils import get_current_time
from .dscfile import DSCFile

class IPKImporter():
    def __init__(self, search_dir):
        self._import_dir = search_dir
        self._asmdata = AppStream.Metadata()
        self._asmdata.set_locale("C")

        self._xdg_cat_map = { 'AudioVideo': self._category_by_id("multimedia"),
                        'Audio': self._category_by_id("multimedia"),
                        'Video': self._category_by_id("multimedia"),
                        'Development': self._category_by_id("development"),
                        'Education': self._category_by_id("education"),
                        'Game': self._category_by_id("games"),
                        'Graphics': self._category_by_id("graphics"),
                        'Network': self._category_by_id("network"),
                        'Office': self._category_by_id("customization"),
                        'Science': self._category_by_id("science"),
                        'Settings': self._category_by_id("tools"),
                        'System': self._category_by_id("system"),
                        'Utility': self._category_by_id("tools"),

                        'Arcade': self._category_by_id("arcade")
                      }

    def _category_by_id(self, cat_name):
        return Category.query.filter_by(idname=cat_name).one()

    def _map_categories(self, cpt):
        xdg_cats = cpt.get_categories()
        if cpt.get_kind() != AppStream.ComponentKind.DESKTOP:
            return [Category.query.filter_by(idname="components").one()]
        if not xdg_cats:
            return [Category.query.filter_by(idname="other").one()]

        cats = list()
        for xcat in xdg_cats:
            cat = self._xdg_cat_map.get(xcat)
            if cat:
                cats.append(cat)
        if len(cats) == 0:
            return [Category.query.filter_by(idname="other").one()]

        return cats

    def _import_package(self, pkg_fname, sha256sum, dsc):
        pkg = Limba.Package()
        pkg.open_file(pkg_fname)

        if pkg.has_embedded_packages():
            self._reject_dsc("Package contains embedded packages. This is not allowed in repositories.", dsc)
            return

        pki = pkg.get_info()

        cpt_xml = pkg.get_appstream_data()

        self._asmdata.clear_components()
        self._asmdata.parse_data(cpt_xml)
        cpt = self._asmdata.get_component()

        pkgid = pkg.get_id()
        cptname = pki.get_name()
        arch = pki.get_architecture()

        dest_pkgfname = "%s_%s.ipk" % (pkgid.replace("/", "-"), arch)

        cpt_desc = cpt.get_description()
        if not cpt_desc:
            cpt_desc = "
A software component
"

        # we just accept packages for the master repository for now
        repo_name = dsc.get_val('Target')
        repo = None
        try:
            repo = Repository.query.filter_by(name=repo_name).one()
        except:
            self._reject_dsc("Could not find target repository: %s" % (repo_name), dsc)
            return

        repo_pool_path = os.path.join(repo.root_dir, "pool", build_cpt_path (cptname))
        repo_icons_path = os.path.join(repo.root_dir, "assets", build_cpt_path (cptname), pki.get_version(), "icons")
        pkg_dest = os.path.join(repo_pool_path, dest_pkgfname)
        repo_location = os.path.join(build_cpt_path (cptname), dest_pkgfname)

        dbcpt = Component(
            cid=cpt.get_id(),
            kind=AppStream.ComponentKind.to_string(cpt.get_kind()),
            sdk=True if pki.get_kind() == Limba.PackageKind.DEVEL else False,
            name=cpt.get_name(),
            summary=cpt.get_summary(),
            description=cpt_desc,
            developer_name=cpt.get_developer_name(),
            url=cpt.get_url(AppStream.UrlKind.HOMEPAGE),
            xml=cpt_xml,
            repository=repo
            )
        dbcpt.categories = self._map_categories(cpt)
        db.session.add(dbcpt)

        dbpkg = Package(
            name=pki.get_name(),
            version=pki.get_version(),
            kind=PackageKind.SDK if pki.get_kind() == Limba.PackageKind.DEVEL else PackageKind.COMMON,
            fname=repo_location,
            architecture=arch,
            sha256sum=sha256sum,
            dependencies=pki.get_dependencies(),
            component=dbcpt,
            repository=repo
            )
        db.session.add(dbpkg)

        pkg.extract_appstream_icons(repo_icons_path)

        if not os.path.exists(repo_pool_path):
            os.makedirs(repo_pool_path)
        shutil.copyfile(pkg_fname, pkg_dest)

    def _reject_dsc(self, reason, dsc):
        print("REJECT: %s => %s" % (reason, str(dsc)))
        # TODO: Actually reject the package and move it to the morgue

    def _process_dsc(self, dscfile):
        dsc = DSCFile()
        dsc.open(dscfile)

        uploader = dsc.get_val('Uploader')
        if not uploader:
            self._reject_dsc("Uploader field was not set.", dsc)
            return
        m = re.findall(r'<(.*?)>', uploader)
        if not m:
            self._reject_dsc("Unable to get uploader email address.", dsc)
            return

        user = None
        try:
            user = User.query.filter_by(email=m[0]).one()
        except:
            self._reject_dsc("Could not find user '%s'" % (uploader), dsc)
            return

        key = None
        try:
            key = dsc.validate(user.gpghome)
            key = key.replace(' ', '')
        except Exception as e:
            self._reject_dsc("Validation failed: %s" % (str(e)), dsc)
            return

        if key != user.pgpfpr:
            self._reject_dsc("Validation failed: Fingerprint does not match user", dsc)
            return

        # if we are here, everything is fine - we can import the packages if their checksums match
        for sha256sum, fname in dsc.get_files().items():
            real_sha256 = None
            fname_full = os.path.join(self._import_dir, fname)
            with open(fname_full, 'rb') as f:
                real_sha256 = sha256(f.read()).hexdigest()
            if real_sha256 != sha256sum:
                self._reject_dsc("Validation failed: Checksum mismatch for '%s'" % (fname), dsc)
                return
            self._import_package(fname_full, sha256sum, dsc)

    def import_packages(self):
        for fname in glob.glob(self._import_dir+"/*.dsc"):
            self._process_dsc(fname)
        db.session.commit()


from collections import deque
from typing import List, Tuple

def shortest_distance(maze: List[List[int]], src: Tuple[int, int], dest: Tuple[int, int]) -> int:
    row = len(maze)
    col = len(maze[0])
    visited = [[False for i in range(col)] for j in range(row)]
    distance = [[float('inf') for i in range(col)] for j in range(row)]
    dx = [-1, 0, 1, 0]
    dy = [0, 1, 0, -1]

    queue = deque()
    queue.append(src)
    visited[src[0]][src[1]] = True
    distance[src[0]][src[1]] = 0

    while queue:
        r, c = queue.popleft()
        for i in range(4):
            rr, cc = r + dx[i], c + dy[i]
            if (rr >= 0) and (rr < row) and (cc >= 0) and (cc < col) and maze[rr][cc] == 1:
                if not visited[rr][cc]:
                    visited[rr][cc] = True
                    distance[rr][cc] = distance[r][c] + 1
                    queue.append((rr, cc))
                else:
                    if distance[r][c] + 1 < distance[rr][cc]:
                        distance[rr][cc] = distance[r][c] + 1
                        queue.append((rr, cc))

    if distance[dest[0]][dest[1]] != float('inf'):
        return distance[dest[0]][dest[1]]
    else:
        return -1

maze = [[1, 0, 1, 1],
        [1, 1, 1, 0],
        [0, 1, 0, 1],
        [1, 1, 1, 1]]
src = (3, 0)
dest = (0, 2)
print(shortest_distance(maze, src, dest))


# Link For Problem: https://leetcode.com/problems/longest-increasing-path-in-a-matrix/

import functools

class Solution:

    """
        DFS + Memoization Approach.

        TC : O(n)

        SC : O(n)
    """

    def longestIncreasingPath(self, matrix: list[list[int]]) -> int:
        R, C = len(matrix), len(matrix[0])

        g = [[[] for _ in range(C)] for _ in range(R)]

        for i in range(R):

            for j in range(C):

                for (r, c) in ((i+1, j), (i-1, j), (i, j+1), (i, j-1)):

                    if 0 <= r < R and 0 <= c < C and matrix[r][c] > matrix[i][j]:
                        g[i][j].append((r, c))

        @functools.lru_cache(None)
        def helper(i, j):
            return 1 + max(helper(r, c) for r, c in g[i][j]) if g[i][j] else 1

        return max(helper(i, j) for i in range(R) for j in range(C))


from typing import Optional
from decimal import Decimal

from validator_collection import validators

from highcharts_core import errors
from highcharts_core.metaclasses import HighchartsMeta
from highcharts_core.utility_classes.gradients import Gradient
from highcharts_core.utility_classes.patterns import Pattern

class DataClass(HighchartsMeta):
    """Definition of ranges for use in a choropleth map."""

    def __init__(self, **kwargs):
        self._color = None
        self._from_ = None
        self._name = None
        self._to = None

        self.color = kwargs.get('color', None)
        self.from_ = kwargs.get('from_', None)
        self.name = kwargs.get('name', None)
        self.to = kwargs.get('to', None)

    @property
    def color(self) -> Optional[str | Gradient | Pattern]:
        """The color of the data class. Defaults to :obj:`None <python:None>`.

        If :obj:`None <python:None>`, the color is pulled from the global or
        chart-specific colors array.

        :rtype: :class:`str <python:str>`, :class:`Gradient`, :class:`Pattern``, or
          :obj:`None <python:None>`
        """
        return self._color

    @color.setter
    def color(self, value):
        from highcharts_core import utility_functions
        self._color = utility_functions.validate_color(value)

    @property
    def from_(self) -> Optional[int | float | Decimal]:
        """The point value at which the data class' value range begins. Defaults to
        :obj:`None <python:None>`.

        .. note::

          The range of each :class:`DataClass` is closed at both ends, but can be
          overridden by a subsequent :class:`DataClass` instance included in the
          :meth:`ColorAxis.data_classes` collection.

        :rtype: numeric or :obj:`None <python:None>`
        """
        return self._from_

    @from_.setter
    def from_(self, value):
        self._from_ = validators.numeric(value, allow_empty = True)

    @property
    def name(self) -> Optional[str]:
        """The name of the data class as it should apear in the legend. Defaults to
        :obj:`None <python:None>`.

        If :obj:`None <python:None>`, it is automatically created based on the
        :meth:`from_ <DataClass.from_>` and :meth:`to <DataClass.to>` values.

        .. hint::

          For full programmatic control, :meth:`Legend.label_formatter` can be used. In
          the formatter (JavaScript) function, ``this.from`` and ``this.to`` can be
          accessed.

        :rtype: :class:`str <python:str>` or :obj:`None <python:None>`
        """
        return self._name

    @name.setter
    def name(self, value):
        self._name = validators.string(value, allow_empty = True)

    @property
    def to(self) -> Optional[int | float | Decimal]:
        """The point value at which the data class' value range ends. Defaults to
        :obj:`None <python:None>`.

        .. note::

          The range of each :class:`DataClass` is closed at both ends, but can be
          overridden by a subsequent :class:`DataClass` instance included in the
          :meth:`ColorAxis.data_classes` collection.

        :rtype: numeric or :obj:`None <python:None>`
        """
        return self._to

    @to.setter
    def to(self, value):
        self._to = validators.numeric(value, allow_empty = True)

    @classmethod
    def _get_kwargs_from_dict(cls, as_dict):
        kwargs = {
            'color': as_dict.get('color', None),
            'from_': as_dict.get('from', None),
            'name': as_dict.get('name', None),
            'to': as_dict.get('to', None)
        }

        return kwargs

    def _to_untrimmed_dict(self, in_cls = None) -> dict:
        untrimmed = {
            'color': self.color,
            'from': self.from_,
            'name': self.name,
            'to': self.to
        }

        return untrimmed


from typing import Dict, Optional, Tuple, TYPE_CHECKING

from UM.Logger import Logger
from UM.Version import Version
from cura.Backups.Backup import Backup

if TYPE_CHECKING:
    from cura.CuraApplication import CuraApplication

class BackupsManager:
    """
    The BackupsManager is responsible for managing the creating and restoring of
    back-ups.

    Back-ups themselves are represented in a different class.
    """

    def __init__(self, application: "CuraApplication") -> None:
        self._application = application

    def createBackup(self) -> Tuple[Optional[bytes], Optional[Dict[str, str]]]:
        """
        Get a back-up of the current configuration.

        :return: A tuple containing a ZipFile (the actual back-up) and a dict containing some metadata (like version).
        """

        self._disableAutoSave()
        backup = Backup(self._application)
        backup.makeFromCurrent()
        self._enableAutoSave()
        # We don't return a Backup here because we want plugins only to interact with our API and not full objects.
        return backup.zip_file, backup.meta_data

    def restoreBackup(self, zip_file: bytes, meta_data: Dict[str, str]) -> None:
        """
        Restore a back-up from a given ZipFile.

        :param zip_file: A bytes object containing the actual back-up.
        :param meta_data: A dict containing some metadata that is needed to restore the back-up correctly.
        """

        if not meta_data.get("cura_release", None):
            # If there is no "cura_release" specified in the meta data, we don't execute a backup restore.
            Logger.log("w", "Tried to restore a backup without specifying a Cura version number.")
            return

        self._disableAutoSave()

        backup = Backup(self._application, zip_file = zip_file, meta_data = meta_data)
        restored = backup.restore()

        if restored:
            # At this point, Cura will need to restart for the changes to take effect.
            # We don't want to store the data at this point as that would override the just-restored backup.
            self._application.windowClosed(save_data = False)

    def _disableAutoSave(self) -> None:
        """Here we (try to) disable the saving as it might interfere with restoring a back-up."""

        self._application.enableSave(False)
        auto_save = self._application.getAutoSave()
        # The auto save is only not created if the application has not yet started.
        if auto_save:
            auto_save.setEnabled(False)
        else:
            Logger.log("e", "Unable to disable the autosave as application init has not been completed")

    def _enableAutoSave(self) -> None:
        """Re-enable auto-save and other saving after we're done."""

        self._application.enableSave(True)
        auto_save = self._application.getAutoSave()
        # The auto save is only not created if the application has not yet started.
        if auto_save:
            auto_save.setEnabled(True)
        else:
            Logger.log("e", "Unable to enable the autosave as application init has not been completed")


from ..math_basics import is_RealIntervalFieldElement # type: ignore
from ..exceptions import InsufficientPrecisionError # type: ignore

from typing import Sequence

__all__ = ['floor_as_intergers', 'SpatialDict']

def floor_as_integers(x) -> Sequence[int]:
    """
    Computes floor of a number or interval, returning a list of integers
    if evaluating floor is ambiguous.

        sage: floor_as_integers(RIF(1.8, 1.9))
        [1]
        sage: floor_as_integers(RIF(1.9, 2.1))
        [1, 2]

        >>> floor_as_integers(1.4)
        [1]
        >>> floor_as_integers(2.01)
        [1, 2]
        >>> floor_as_integers(1.99)
        [1, 2]

    """

    if is_RealIntervalFieldElement(x):
        f = x.floor()
        l = f.lower().round()
        u = f.upper().round() + 1

        n = u - l
        if n > 5:
            raise InsufficientPrecisionError(
                "Too many integers (%d) in given interval to compute floor. "
                "Increasing precision should fix it." % n)

        return list(range(l, u))
    else:
        f = x.floor()
        int_f = int(f)
        d = x - f
        if d < 0.125:
            return [ int_f - 1, int_f ]
        if d > 0.875:
            return [ int_f, int_f + 1 ]
        return [ int_f ]

class _Entry:
    """
    A helper for SpatialDict.

    The implementation of SpatialDict has the same instance of _Entry
    stored for multiple keys so that updating the value for all keys
    can be done by assigning the new value to _Entry.value only once.
    """
    def __init__(self, value):
        self.value = value

class SpatialDict:
    """
    A python dict-like object appropriate for using numerical points (e.g.,
    in the hyperboloid model) as keys. That is, look-ups return
    the same entry for points that are almost but not exactly the
    same due to rounding-errors.

    To achieve this, the points are asumed to be in some lattice
    and the minimal distance between any two points in the lattice
    must be given.
    """

    _scale = 1024

    def __init__(self, min_distance, verified):
        RF = min_distance.parent()

        self._min_distance = min_distance
        self._RF_scale = RF(self._scale)

        if verified:
            self._right_distance_value = min_distance
            self._left_distance_value = 0
        else:
            self._right_distance_value = min_distance * RF(0.125)
            self._left_distance_value = min_distance * RF(0.5)

        self._data = { }

    def setdefault(self, point, default):
        reps_and_ikeys = self._representatives_and_ikeys(point)

        for rep, ikey in reps_and_ikeys:
            for other_rep, entry in self._data.get(ikey, []):
                d = self.distance(rep, other_rep)
                if d < self._right_distance_value:
                    return entry.value
                if not (self._left_distance_value < d):
                    raise InsufficientPrecisionError(
                        "Could neither verify that the two given tiles are "
                        "the same nor that they are distinct. "
                        "Distance between basepoint translates is: %r. "
                        "Injectivty diameter about basepoint is: %r." % (
                            d, self._min_distance))

        entry = _Entry(default)
        for rep, ikey in reps_and_ikeys:
            self._data.setdefault(ikey, []).append((rep, entry))

        return default

    def distance(self, point_0, point_1):
        raise NotImplementedError()

    def representatives(self, point):
        # Applies, e.g., translation by geodesic matrix
        return [ point ]

    def float_hash(self, point):
        raise NotImplementedError()

    def _representatives_and_ikeys(self, point):
        return [
            (rep, ikey)
            for rep in self.representatives(point)
            for ikey in floor_as_integers(self._RF_scale * self.float_hash(rep)) ]


from tkinter import Tk, Label, Button, Entry, Text, messagebox
from os import environ
from source.email import Email
from dotenv import load_dotenv

#se crea la ventana
venatana = Tk()

#se pone el titulo, color y tamaño de la ventana
venatana.title("Semana 12")
venatana.geometry("500x400")
venatana.config(bg="#5F6A6A")

#se crean los parametros de los objetos de la ventana
share = Label(venatana, text="Correo: ", font=14, bg="#5F6A6A")
user = Label(venatana, text="Usuario: ", font=14, bg="#5F6A6A")

ce = Entry(venatana, font= 14, bg="white")
user2 = Entry(venatana, font= 14, bg="white")
message1= Text(venatana, font= 14, bg="white")

#Se cre la estructura para HTML
html = """
<!DOCYPE html>

Estimad@ {}

{}

"""

load_dotenv()
#Se cre la funcion para enviar el correo
def sendemail():
    correo = Email()
    correo.mandar_email([ce.get()], "Semana 12 Programacion", message_format=html.format(user2.get(), message1.get('1.0', 'end-1c')), format="html")
    messagebox.showinfo("Aviso", "El correo se ha enviado de manera sastifactoria.")

#se cre el bonton de envio
btnshare = Button(venatana, text="Enviar", command=sendemail, bg="#283747")

#se establecen los parametros de los objetos dentro de la ventana

share.place(x=50, y=10)
ce.place(x=150, y=10, width=290)
user.place(x=50, y=60)
user2.place(x=150, y=60, width=290)
message1.place(x=60, y=110, width=380, height=225)
btnshare.place(x=60, y=350, width=380, height=40)

venatana.mainloop()


# Face Recognition/Identification
import cv2 as cv
import face_recognition as fr
import os

# training data
knownFaces = [
    {
        "file": "JoeBiden.jpg",
        "name": "Joe Biden",
        "code": None,
    },
    {
        "file": "FumioKishida.jpg",
        "name": "Fumio Kishida",
        "code": None,
    },
    {
        "file": "XiJinPing.jpg",
        "name": "Xi Dada",
        "code": None,
    },
    {
        "file": "JustinTrudeau.jpg",
        "name": "Justin Trudeau",
        "code": None,
    },
]

# return the encoding of the face detected in the image file
def train(face):
    # img is using "RGB" color
    img = fr.load_image_file("face-images/known/" + face["file"])
    faceLocations = fr.face_locations(img)
    faceCodes = fr.face_encodings(img, faceLocations)

    if len(faceLocations) > 0:
        top, right, bottom, left = faceLocations[0]
        frame = cv.cvtColor(img, cv.COLOR_RGB2BGR)
        cv.rectangle(frame, (left, top), (right, bottom), (255,0,0), 2)
    cv.imshow(face['name'],frame)

    if len(faceCodes) > 0:
        return faceCodes[0]
    else:
        return None

def recognize(filePath):
    img = fr.load_image_file(filePath)
    frame = cv.cvtColor(img, cv.COLOR_RGB2BGR)

    locations = fr.face_locations(img)
    codes = fr.face_encodings(img, locations)

    knownCodes = [face['code'] for face in knownFaces]

    for loc, code in zip(locations, codes):
        top, right, bottom, left = loc
        # draw face border
        cv.rectangle(frame, (left,top), (right,bottom), (255,0,0), 2)

        # draw face name
        matches = fr.compare_faces(knownCodes, code, tolerance=0.5)
        name = "unknown"
        for i in range(len(matches)):
            if matches[i] == True:
                name = knownFaces[i]['name']
                break
        cv.putText(frame, name, (left, top), cv.FONT_HERSHEY_SIMPLEX, 0.6, (0,0,255), 2)

    cv.imshow('face recog', frame)

# train by known faces
for face in knownFaces:
    face['code'] = train(face)
    #print(face)

# recognize unknow faces
recognize("face-images/unknown/u003.jpg")

cv.waitKey(0)


import numpy as np
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

startPoint = 5

state1 = np.loadtxt('1.0_2.0_state')[:,0].reshape(1,201,1)
encoder_input1 = state1[:,:startPoint,:]
decoder_input1 = state1[:,startPoint:-1,:]
output1 = state1[:,startPoint:,:]

state2 = np.loadtxt('2.0_0.5_state')[:,0].reshape(1,201,1)
encoder_input2 = state2[:,:startPoint,:]
decoder_input2 = state2[:,startPoint:-1,:]
output2 = state2[:,startPoint:,:]

state3 = np.loadtxt('-2.0_1.0_state')[:,0].reshape(1,201,1)
encoder_input3 = state3[:,:startPoint,:]
decoder_input3 = state3[:,startPoint:-1,:]
output3 = state3[:,startPoint:]

state4 = np.loadtxt('-1.0_-1.0_state')[:,0].reshape(1,201,1)
encoder_input4 = state4[:,:startPoint,:]
decoder_input4 = state4[:,startPoint:-1,:]
output4 = state4[:,startPoint:,:]

train_en_input = np.concatenate((encoder_input1,encoder_input2,encoder_input3,encoder_input4), axis=0)
train_de_input = np.concatenate((decoder_input1,decoder_input2,decoder_input3,decoder_input4), axis=0)
train_output = np.concatenate((output1,output2,output3,output4), axis=0)
train_en_input = torch.Tensor(train_en_input)
train_de_input = torch.Tensor(train_de_input)
train_output = torch.Tensor(train_output)
# print(train_en_input.shape,train_de_input.shape,train_output.shape)

stateV = np.loadtxt('0.0_1.0_state')[:,0].reshape(1,201,1)
encoder_inputV = stateV[:,:startPoint,:]
decoder_inputV = stateV[:,startPoint:-1,:]
outputV = stateV[:,startPoint:]
val_en_input = torch.Tensor(encoder_inputV)
val_de_input = torch.Tensor(decoder_inputV)
val_output = torch.Tensor(outputV)

class RNN(nn.Module):
    def __init__(self, input_size=1 , hidden_size=48, output_size=1, n_layers=1):
        super(RNN, self).__init__()

        self.hidden_size = hidden_size
        self.output_size = output_size
        self.input_size = input_size
        self.n_layers = n_layers

        # encoder and decoder have different parameters
        self.encoder = nn.LSTM(input_size, hidden_size, n_layers, batch_first=True)
        self.decoder = nn.LSTM(input_size, hidden_size, n_layers, batch_first=True)

        # self.gru = nn.GRU(input_size, hidden_size, n_layers,batch_first=True)
        self.out = nn.Linear(hidden_size, output_size)

    def forward(self, en_input: torch.Tensor, de_input = None, predict_length=0):
        hidden = None
        if predict_length == 0:
            encoder_outputs, hidden = self.encoder(en_input, hidden)

            encoder_last = self.out(encoder_outputs)[:, -1, :].reshape(-1, 1, 1)

            decoder_outputs,hidden = self.decoder(de_input, hidden)

            decoder_outputs = self.out(decoder_outputs)

            output = torch.cat((encoder_last, decoder_outputs), dim=1)

            return output
        else:
            total_sequence = en_input

            encoder_outputs, hidden = self.encoder(en_input, hidden)

            decoder_input = encoder_outputs[:, -1, :].reshape(-1, 1, self.hidden_size)

            decoder_input = self.out(decoder_input)

            total_sequence = torch.cat((total_sequence, decoder_input),dim=1)

            one_input = decoder_input
            for i in range(predict_length - 1):
                # one_input = total_sequence[-1].reshape(-1, 1, 1)
                one_output, hidden = self.decoder(one_input, hidden)
                one_output = self.out(one_output)
                total_sequence = torch.cat((total_sequence, one_output),dim=1)
                one_input = one_output

            return total_sequence[:,-predict_length:,:]

LSTM_model = RNN()
# LSTM_model = torch.load("model1")

criterion = nn.MSELoss()
optimizer = torch.optim.Adam(LSTM_model.parameters(),lr=0.01)

def train(epochs):
    train_loss = []
    # minimum = 1e6
    for epoch in range(epochs):
        LSTM_model.train()
        output = LSTM_model(en_input = train_en_input,de_input = train_de_input,predict_length = 0)
        loss = criterion(output,train_output)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
        if epoch % 20 == 0:
            LSTM_model.eval()
            val_predict = LSTM_model(en_input=val_en_input,de_input = val_de_input,predict_length = 0)
            val_loss = criterion(val_predict,val_output)
            train_loss.append(loss.tolist())
            print(f"epoch:{epoch},train_loss:{loss},val_loss:{val_loss}")
        torch.save(LSTM_model, "model2")

    return train_loss

trainLoss = train(801)

testName = '0.5_0.5_'
# testName = '-0.5_0.5_'
# testName = '1.0_0.0_'

test_state = torch.Tensor(np.loadtxt(testName+'state').reshape(201,2))
test_input = torch.Tensor(np.loadtxt(testName+'state')[:startPoint,0].reshape(1,startPoint,1))
test_result = np.loadtxt(testName+'state')[startPoint:,0].reshape(201-startPoint,1)

predict_result = LSTM_model(en_input=test_input,predict_length = 201-startPoint)
predict_result = predict_result.detach().numpy()

fig,axes = plt.subplots(2,1)
ax1=axes[0]

ax1.plot(test_result[:,0],label = "real")
ax1.plot(predict_result[:,:,:].reshape(-1),label = "predict")
ax1.legend()

plt.show()

print(np.mean(np.square(test_result-predict_result)))