import numpy as np
import pandas as pd
import missingno as msno
import re

paths = {
        "caqueta": '../../data/raw_2/caqueta_data_2.csv',
        "narino": '../../data/raw_2/Narino_data_2.csv',
        "putumayo": '../../data/raw_2/Putumayo_data_2.csv',
        "caqueta": '../../data/raw/caqueta_data.csv',
        "narino": '../../data/raw/Narino_data.csv',
        "putumayo": '../../data/raw/Putumayo_data.csv',
        "caqueta_2": '../../data/raw_2/caqueta_data_2.csv',
        "narino_2": '../../data/raw_2/Narino_data_2.csv',
        "putumayo_2": '../../data/raw_2/Putumayo_data_2.csv',
    }

common_drops = [
        "Año", "Mes", "Programa", "Evento", "Afiliados", "OrigenBD", "DesDepto", "CodMpio", "DescMpio",
        "Latitud_Y_Mpio", "Longitud_X_Mpio", "tipo_usuario", "Estado", "tipo_identifiCAcion", "Documento", "ConCAtenar", "nombre1",
        "nombre2", "apellido1", "apellido2", "FechaNac", "CiclosV", "DescrCiclosV", "QuinQ", "DescQuinQ","Género", "EnfoqueDif",
        "Hecho Victimizante", "RUV", "Nivel_Educativo", "Ocupación", "Tipo de afiliado", "Estado_Civil", "Discapacidad",
        "Grado de Discapacidad", "MUNICIPIO DONDE VIVE", "DIRECCIÓN DE DONDE VIVE", "TELEFONOS DE CONTACTO", "Zona",
        "Cód_poblado", "Nombre_poblado", "Latitud_Afiliado", "Longitud_Afiliado", "Validación_Dirección_Afiliado",
        "CodDepto_IPS", "DesDepto_IPS", "CodMpio_IPS", "DescMpio_IPS", "CodIPS", "Nombre_IPS", "Dirección_IPS",
        "Barrio_IPS", "Teléfono_IPS", "Latitud_IPS", "Longitud_IPS", "CONSUMO DE TABACO",
        "EL USUARIO CUENTA CON ATENCIÓN POR PARTE DEL EQUIPO MULTIDISCIPLINARIO DE LA SALUD ",
        "CONTROLADO/NO CONTROLADO",
        "PACIENTES CON 5 Ó MÁS MDCTOS FORMULADOS AL MES", "FECHA DE ENTREGA DE MEDICAMENTOS",
        "MODALIDAD DE ENTREGA DE MEDICAMENTOS",
        "PACIENTE I10X Y E119 QUE NO RECIBEN TRAMIENTO", "FECHA INGRESO AL PROGRAMA", "FECHA DEL SEGUNDO CONTROL",
        "FECHA DX CONFIRMADO DE HIPERTENSIÓN ARTERIAL", "DX CONFIRMADO DE DIABETES MELLITUS",
        "FECHA DX CONFIRMADO DE DIABETES MELLITUS", "FECHA DIAGNÓSTICO DISLIPIDEMIAS", "TENSION SISTOLICA",
        "TENSION DIASTOLICA", "VALORACIÓN PODOLÓGICA (PIE EN DIABETICOS) POR MÉDICO GENERAL",
        "REALIZA ACTIVIDAD FISICA",
        "TABAQUISMO", "RESULTADO", "FACTORES DE RIESGO", "NUTRICIÓN", "TRABAJO SOCIAL", "MEDICINA INTERNA",
        "PSICOLOGIA",
        "NEFROLOGIA", "OFTALMOLOGÍA", "ENDOCRINOLOGIA", "CARDIOLOGIA", "ELECTROCARDIOGRAMA", "NEFROPROTECCIÓN",
        "TERAPIA SUSTITUCIÓN DIALÍTICA", "TALLER EDUCATIVO ENTREGA CARTILLAS", "FECHA_CLASIF_ERC",
        "FECHA DEL UROANALISIS",
        "FECHA DE ULTIMO SEGUIMIENTO ", "ETIOLOGIA DE LA ERC", "MODALIDAD COMO SE HACE EL SEGUIMIENTO DEL PACIENTE",
        "FECHA DE PRÓXIMO CONTROL ", "CAUSAS DE INASISTENCIA", "DISMINUYO/ AUMENTO ML"
        ]
narino_putumayo_drops = [
        "FECHA NUTRUCIÓN", "FECHA TRABAJO SOCIAL", "FECHA MEDICINA INTERNA", "FECHA PISCOLOGIA", "FECHA NEFROLOGIA",
        "FECHA OFTALMOMOGIA", "FECHA ENDOCRINOLOGIA", "FECHA CARDIOLOGIA", "FECHA ELECTROCARDIOGRAMA",
        "FECHA NEFROPROTECCIÓN"
        ]
narino_drops = [
        "REPETIDO"
        ]
putumayo_drops = [
        "FECHA DE TOMA DE CREATININA SÉRICA", "FECHA DE TOMA DE GLICEMIA 100 MG/DL_DIC",
        "FECHA DE TOMA DE COLESTEROL TOTAL > 200 MG/DL_DIC",
        "FECHA DE TOMA DE LDL > 130 MG/DL_DIC", "FECHA DE TOMA DE HDL HOMBRE - 40 MG/DL Y HDL MUJER - 50 MG/DL_DIC",
        "FECHA DE TOMA DE ALBUMINURIA/CREATINURIA", "FECHA DE TOMA DE HEMOGLOBINA GLICOSILADA > DE 7%",
        "FECHA DE TOMA DE HEMOGRAMA",
        "FECHA DE TOMA DE POTASIO", "FECHA DE TOMA DE MICROALBINURIA", "FECHA DE TOMA DE CREATINURIA", "OBSERVACIONES",
        "FECHA DE TOMA DE TGD > 150 MG/DL_DIC","POTASIO", "HEMOGRAMA"
        ]

class Cleaning:

    def __init__(self, saving_path):
        global paths
        global common_drops
        global narino_putumayo_drops
        global narino_drops
        global putumayo_drops

        drops = [len(common_drops), len(narino_putumayo_drops), len(narino_drops), len(putumayo_drops)]
        print("Number of drops defined:")
        print("common_drops len: {}".format(len(common_drops)))
        print("narino_putumayo_drops len: {}".format(len(narino_putumayo_drops)))
        print("narino_drops len: {}".format(len(narino_drops)))
        print("putumayo_drops len: {}".format(len(putumayo_drops)))
        print("total drops: {}".format(sum(drops)))

        self.saving_path = saving_path
        self.caqueta_df = None
        self.narino_df = None
        self.putumayo_df = None
        self.first_caqueta_clean = None
        self.first_putumayo_clean = None
        self.first_narino_clean = None
        self.df = None
        self.unified_df = None
        self.df_after_fix = None
        self.df_clean = None

    def read_csv(self):
        self.caqueta_df = pd.read_csv(paths["caqueta"],low_memory=False)
        self.narino_df = pd.read_csv(paths["narino"],low_memory=False)
        self.putumayo_df = pd.read_csv(paths["putumayo"],low_memory=False)
        self.caqueta_df_2 = pd.read_csv(paths["caqueta_2"],low_memory=False)
        self.narino_df_2 = pd.read_csv(paths["narino_2"],low_memory=False)
        self.putumayo_df_2 = pd.read_csv(paths["putumayo_2"],low_memory=False)

    def drop_columns(self):
        caqueta_df_clean = self.caqueta_df.drop(common_drops, axis=1)
        caqueta_df_clean = self.caqueta_df_2.drop(common_drops, axis=1)

        narino_df_clean = self.narino_df.drop(common_drops, axis=1)
        narino_df_clean = self.narino_df_2.drop(common_drops, axis=1)
        narino_df_clean['Edad'] = self.narino_df['Edad']
        narino_df_clean = narino_df_clean.drop(narino_drops, axis=1)
        narino_df_clean = narino_df_clean.drop(narino_putumayo_drops, axis=1)

        putumayo_df_clean = self.putumayo_df.drop(common_drops, axis=1)
        putumayo_df_clean = self.putumayo_df_2.drop(common_drops, axis=1)
        putumayo_df_clean = putumayo_df_clean.drop(putumayo_drops, axis=1)
        putumayo_df_clean = putumayo_df_clean.drop(narino_putumayo_drops, axis=1)

        self.putumayo_df = putumayo_df_clean
        self.narino_df = narino_df_clean
        self.caqueta_df = caqueta_df_clean

        self.first_caqueta_clean = caqueta_df_clean
        self.first_putumayo_clean = putumayo_df_clean
        self.first_narino_clean = narino_df_clean

    def concat_dfs(self):
        self.df = pd.concat([self.putumayo_df, self.narino_df, self.caqueta_df], axis=0)
        self.df = self.df.sample(frac=1).reset_index(drop=True)
        self.unified_df = self.df

    def replace_blanks(self):
        self.df = self.df.rename(columns=lambda x: x.strip())
        self.df = self.df.replace(r'^\s*$', np.nan, regex=True)
        self.df["FechaNovedadFallecido"] = self.df["FechaNovedadFallecido"].fillna('no aplica')
        self.df["Coomorbilidad"] = self.df["Coomorbilidad"].fillna('no aplica')
        self.df["Condición de Discapacidad"] = self.df["Condición de Discapacidad"].fillna('no aplica')
        self.df["Tipo de Discapacidad"] = self.df["Tipo de Discapacidad"].fillna('no aplica')
        self.df["OTROS ANTIDIABETICOS"] = self.df["OTROS ANTIDIABETICOS"].fillna('no aplica')
        self.df["OTROS TRATAMIENTOS"] = self.df["OTROS TRATAMIENTOS"].fillna('no aplica')
        self.df["OTROS FARMACOS ANTIHIPERTENSIVOS"] = self.df["OTROS FARMACOS ANTIHIPERTENSIVOS"].fillna('no aplica')
        self.df = self.df.replace("#DIV/0!", np.nan)
        self.df = self.df.replace("#NUM!", np.nan)
        self.df = self.df.replace("#VALUE!", np.nan)

    def data_types(self):
        self.df['Grupo de Riesgo'].astype('object').dtypes
        self.df['CodDepto'].astype('float64').dtypes
        self.df['FechaNovedadFallecido'].astype('object').dtypes
        self.df['Edad'].astype('float64').dtypes
        self.df['Cod_Género'].astype('float64').dtypes
        self.df['Tipo de Discapacidad'].astype('object').dtypes
        self.df['Condición de Discapacidad'].astype('object').dtypes
        self.df['Pertenencia Étnica'].astype('object').dtypes
        self.df['Coomorbilidad'].astype('object').dtypes
        self.df['ADHERENCIA AL TRATAMIENTO'].astype('object').dtypes
        self.df['Fumador Activo'].astype('float64').dtypes
        self.df['CONSUMO DE ALCOHOL'].astype('object').dtypes
        self.df['ENTREGA DE MEDICAMENTO OPORTUNA'].astype('object').dtypes
        self.df['FARMACOS ANTIHIPERTENSIVOS'].astype('object').dtypes
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].astype('object').dtypes
        self.df['RECIBE IECA'].astype('object').dtypes
        self.df['RECIBE ARA II'].astype('object').dtypes
        self.df['ESTATINA'].astype('object').dtypes
        self.df['ANTIDIABETICOS'].astype('object').dtypes
        self.df['OTROS ANTIDIABETICOS'].astype('object').dtypes
        self.df['OTROS TRATAMIENTOS'].astype('object').dtypes
        self.df['OTROS DIAGNÓSTICOS'].astype('object').dtypes
        self.df['PESO'].astype('float64').dtypes
        self.df['TALLA'].astype('float64').dtypes
        self.df['IMC'].astype('float64').dtypes
        self.df['OBESIDAD'].astype('object').dtypes
        self.df['CALCULO DE RIESGO DE Framingham (% a 10 años)'].astype('float64').dtypes
        self.df['Clasificación de RCV Global'].astype('object').dtypes
        self.df['DX CONFIRMADO DE HIPERTENSIÓN ARTERIAL'].astype('float64').dtypes
        self.df['CÓD_DIABETES'].astype('float64').dtypes
        self.df['CLASIFICACION DIABETES'].astype('object').dtypes
        self.df['DIAGNÓSTICO DISLIPIDEMIAS'].astype('object').dtypes
        self.df['CÓD_ANTEDECENTE'].astype('float64').dtypes
        self.df['PRESION ARTERIAL'].astype('object').dtypes
        self.df['COLESTEROL ALTO'].astype('object').dtypes
        self.df['HDL ALTO'].astype('object').dtypes
        self.df['CLASIFICACIÓN DE RIESGO CARDIOVASCULAR'].astype('object').dtypes
        self.df['CALCULO TFG'].astype('float64').dtypes
        self.df['CLASIFICACIÓN ESTADIO'].astype('object').dtypes
        self.df['CREATININA SÉRICA (HOMBRES > 1.7 MG/DL - MUJERES > 1.4 MG/DL) _DIC'].astype('float64').dtypes
        self.df['GLICEMIA 100 MG/DL_DIC'].astype('float64').dtypes
        self.df['COLESTEROL TOTAL > 200 MG/DL_DIC'].astype('float64').dtypes
        self.df['LDL > 130 MG/DL_DIC'].astype('float64').dtypes
        self.df['HDL HOMBRE - 40 MG/DL Y HDL MUJER - 50 MG/DL_DIC'].astype('float64').dtypes
        self.df['TGD > 150 MG/DL_DIC'].astype('float64').dtypes
        self.df['ALBUMINURIA/CREATINURIA'].astype('float64').dtypes
        self.df['HEMOGLOBINA GLICOSILADA > DE 7%'].astype('float64').dtypes
        self.df['MICROALBINURIA'].astype('float64').dtypes
        self.df['CREATINURIA'].astype('float64').dtypes
        self.df['UROANALIS'].astype('object').dtypes
        self.df['PERIMETRO ABDOMINAL'].astype('float64').dtypes
        self.df['Complicación Cardiaca'].astype('object').dtypes
        self.df['Complicación Cerebral'].astype('object').dtypes
        self.df['Complicación Retinianas'].astype('object').dtypes
        self.df['Complicación Vascular'].astype('object').dtypes
        self.df['Complicación Renales'].astype('object').dtypes

    @staticmethod
    def trim_all_columns(df):
        trim_strings = lambda x: x.strip() if isinstance(x, str) else x
        return df.applymap(trim_strings)

    def fix_plus_sign(self,df):
        df = df.replace('-', '+', regex=True)
        df = df.replace(r'\s*\+\s*', '+', regex=True)

        return df

    def fixed_data(self):

        self.df = self.fix_plus_sign(self.df)
        self.df = self.trim_all_columns(self.df)

        #df upper case
        self.df = self.df.apply(lambda x: x.astype(str).str.upper())

        #ldl > 130
        self.df['LDL > 130 MG/DL_DIC'] = self.df['LDL > 130 MG/DL_DIC'].str.replace('1845\+01\+01', '0', regex=True)
        self.df['LDL > 130 MG/DL_DIC'] = self.df['LDL > 130 MG/DL_DIC'].str.replace('4*9', '9', regex=True)
        self.df['LDL > 130 MG/DL_DIC'] = self.df['LDL > 130 MG/DL_DIC'].str.replace('14*28', '28', regex=True)
        self.df['LDL > 130 MG/DL_DIC'] = self.df['LDL > 130 MG/DL_DIC'].str.replace('14\*9', '14', regex=True)
        self.df['LDL > 130 MG/DL_DIC'] = self.df['LDL > 130 MG/DL_DIC'].str.replace('109\+', '109', regex=True)

        # comas por puntos en float
        comma_col_list = [ 'CodDepto', 'Edad', 'Cod_Género', 'Fumador Activo', 'PESO', 'TALLA', 'IMC',
                        'CALCULO DE RIESGO DE Framingham (% a 10 años)', 'DX CONFIRMADO DE HIPERTENSIÓN ARTERIAL',
                        'CÓD_DIABETES', 'CÓD_ANTEDECENTE', 'CALCULO TFG', 'CREATININA SÉRICA (HOMBRES > 1.7 MG/DL - MUJERES > 1.4 MG/DL) _DIC', 'GLICEMIA 100 MG/DL_DIC',
                        'COLESTEROL TOTAL > 200 MG/DL_DIC', 'LDL > 130 MG/DL_DIC', 'HDL HOMBRE - 40 MG/DL Y HDL MUJER - 50 MG/DL_DIC', 'TGD > 150 MG/DL_DIC', 'ALBUMINURIA/CREATINURIA',
                        'HEMOGLOBINA GLICOSILADA > DE 7%','MICROALBINURIA', 'CREATINURIA', 'PERIMETRO ABDOMINAL' ]

        for col_name in comma_col_list:
            self.df[col_name] = self.df[col_name].str.replace(',', '.', regex=True)

        #Pertenencia Étnica
        self.df['Pertenencia Étnica'] = self.df['Pertenencia Étnica'].str.upper()

        #comorbilidad
        self.df['Coomorbilidad'] = self.df['Coomorbilidad'].str.replace('IHPOTIROIDISMO', 'HIPOTIROIDISMO', regex=True)
        self.df['Coomorbilidad'] = self.df['Coomorbilidad'].str.replace('HIPOTIROIDIDMO', 'HIPOTIROIDISMO', regex=True)
        self.df['Coomorbilidad'] = self.df['Coomorbilidad'].str.replace('IRC', 'ERC', regex=True)
        self.df['Coomorbilidad'] = self.df['Coomorbilidad'].str.replace('ERCE5', 'ERC', regex=True)
        self.df['Coomorbilidad'] = self.df['Coomorbilidad'].str.replace('ANSIEDAD DEPRESION', 'ANSIEDAD+DEPRESION', regex=True)
        self.df['Coomorbilidad'] = self.df['Coomorbilidad'].str.replace('PREDIADETES', 'PREDIABETES', regex=True)

        #Creatinina Sérica
        self.df['CREATININA SÉRICA (HOMBRES > 1.7 MG/DL - MUJERES > 1.4 MG/DL) _DIC'] = self.df['CREATININA SÉRICA (HOMBRES > 1.7 MG/DL - MUJERES > 1.4 MG/DL) _DIC'].str.replace('1..02', '1.02', regex=True)

        #'HDL HOMBRE - 40 MG/DL Y HDL MUJER - 50 MG/DL_DIC'
        self.df['HDL HOMBRE - 40 MG/DL Y HDL MUJER - 50 MG/DL_DIC'] = self.df['HDL HOMBRE - 40 MG/DL Y HDL MUJER - 50 MG/DL_DIC'].str.replace('46\+', '46', regex=True)

        #'TGD > 150 MG/DL_DIC'
        self.df['TGD > 150 MG/DL_DIC'] = self.df['TGD > 150 MG/DL_DIC'].str.replace('260\+80', '260', regex=True)

        #'HEMOGLOBINA GLICOSILADA > DE 7%'
        self.df['HEMOGLOBINA GLICOSILADA > DE 7%'] = self.df['HEMOGLOBINA GLICOSILADA > DE 7%'].str.replace('28\+07\+2022', '0', regex=True)

        #microalbinuria
        self.df['MICROALBINURIA'] = self.df['MICROALBINURIA'].str.replace('>300', '300', regex=True)

        #creatinuria
        self.df['CREATINURIA'] = self.df['CREATINURIA'].str.replace('1845\+01\+01', '0', regex=True)

        #OTROS DIAGNÓSTICOS
        self.df['OTROS DIAGNÓSTICOS'] = self.df['OTROS DIAGNÓSTICOS'].str.replace(', NO ESPECIFICADO', '', regex=True)
        self.df['OTROS DIAGNÓSTICOS'] = self.df['OTROS DIAGNÓSTICOS'].str.replace(', NO ESPECIFICADA', '', regex=True)
        self.df['OTROS DIAGNÓSTICOS'] = self.df['OTROS DIAGNÓSTICOS'].str.replace(', ASI DESCRITA', '', regex=True)
        self.df['OTROS DIAGNÓSTICOS'] = self.df['OTROS DIAGNÓSTICOS'].str.replace(' DEBIDA A EXCESO DE CALORIAS', '', regex=True)
        self.df['OTROS DIAGNÓSTICOS'] = self.df['OTROS DIAGNÓSTICOS'].str.replace(', SIN OTRA ESPECIFICACION', '', regex=True)

        #FARMACOS ANTIHIPERTENSIVOS
        self.df['FARMACOS ANTIHIPERTENSIVOS'] = self.df['FARMACOS ANTIHIPERTENSIVOS'].str.replace(' O ', '+', regex=True)
        self.df['FARMACOS ANTIHIPERTENSIVOS'] = self.df['FARMACOS ANTIHIPERTENSIVOS'].str.replace(' Ó ', '+', regex=True)
        self.df['FARMACOS ANTIHIPERTENSIVOS'] = self.df['FARMACOS ANTIHIPERTENSIVOS'].str.replace(' UNICAMENTE', '', regex=True)
        self.df['FARMACOS ANTIHIPERTENSIVOS'] = self.df['FARMACOS ANTIHIPERTENSIVOS'].str.replace('HIDROCLOROTIAZIDA (HCTZ)', 'HCTZ', regex=True)
        self.df['FARMACOS ANTIHIPERTENSIVOS'] = self.df['FARMACOS ANTIHIPERTENSIVOS'].str.replace('HIDROCLOROTIAZIDA', 'HCTZ', regex=True)
        self.df['FARMACOS ANTIHIPERTENSIVOS'] = self.df['FARMACOS ANTIHIPERTENSIVOS'].str.replace('HCTZ (HCTZ)', 'HTCZ', regex=True)

        #OTROS FARMACOS ANTIHIPERTENSIVOS
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('/', '+', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace(',', '+', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace(r'\bNO\b', 'NO APLICA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace(r'\s*(?:1|2|MG|MGR|0|3|5|7|8|10|20|25|40|50|80|100|200)\s*', ' ', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace(r'\sX\s', '', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace(r'\sX$', '', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace(r'\+$', '', regex=True)
        self.df = self.trim_all_columns(self.df)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('NO APLICA APLICA', 'NO APLICA' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('METROPROLOL', 'METOPROLOL' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ROSUBASTATINA', 'ROSUVASTATINA' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('SACUBITRILO+VVALSARTAN', 'SACUBITRILO+VALSARTAN' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('GNFIBROXILO', 'GEMFIBROZILO' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('GENFIBROZILO+ATROVASTATINA+EZOMEPRAZOL', 'GEMFIBROZILO+ATROVASTATINA+EZOMEPRAZOL' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('METFORMINA+ROSUVASTATIBNA', 'METFORMINA+ROSUVASTATINA' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('VIDAGLIPTINA', 'VILDAGLIPTINA' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('METOPROLOL   COMPRIMIDO', 'METOPROLOL', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('CLOGIDRATO', 'CLORIDRATO' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ATORVASTAINA', 'ATORVASTATINA' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN METOPROLOL', 'LOSARTAN+METOPROLOL' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('VILDAGLIPTINA', 'VIDALGLIPTINA' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN . ACETIL SALICILICO', 'LOSARTAN+ACETIL SALICILICO' , regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN   HCT', 'LOSARTAN+HCT')
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN HCT', 'LOSARTAN+HCT')
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTANX', 'LOSARTAN', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSATRAN', 'LOSARTAN', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARATAN', 'LOSARTAN', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSATAN', 'LOSARTAN', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('KOSARTAN', 'LOSARTAN', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('VALSARTAN HCT', 'VALSARTAN+HCT', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('NEBIDOLOL', 'NEBIVOLOL', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ACIDO ACETIL SALICILICO   M', 'ACIDO ACETILSALICILICO', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ENALAPRIL METFORMINA', 'ENALAPRIL+METFORMINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ATORVASTATINA   Y LEVO', 'ATORVASTATINA+LEVO', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ENALPRIL', 'ENALAPRIL', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSAR HCT AMLODIPINO ASA', 'LOSARTAN+HCT+AMLODIPINO+ASA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ATORVATATINA', 'ATORVASTATINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN+HCT ASA', 'LOSARTAN+HCT+ASA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('VALSARTAN6', 'VALSARTAN', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('VIDALGLIPTINAX', 'VIDALGLIPTINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ENALAPRIL   LEVOTIROX', 'ENALAPRIL+LEVOTIROXINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('INS GLARGINA', 'INSULINA GLARGINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('INSULINA GLARGINA+NIDAGLIPTINA+ESPIRINOLATONA+LEVOT', 'INSULINA GLARGINA+VILDAGLIPTINA+ESPIRONOLACTONA+LEVOT', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ENALAPRIL HCT', 'ENALAPRIL+HCT', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ENALAPRIL  INS DEGLUDEC+DEPAGLIFOZINA', 'ENALAPRIL+INSULINA DEGLUDEC+DEPAGLIFLOZINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN AMLOD ASA HCT', 'LOSARTAN+AMOLDIPINO+ASA+HCT', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ENALAPRIL MALEATOX', 'ENALAPRIL+MALEATO', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('INSULINA GLARGINA VIDALGLIPTINA', 'INSULINA+GLARGINA+VIDALGLIPTINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN   METFORMINA', 'LOSARTAN+METFORMINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('METFORMINAX    VLDAGLIPTINA', 'METFORMINA+VIDALGLIPTINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN  Y METFORMINA', 'LOSARTAN+METFORMINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('INS DEGLUDEC', 'INSULINA DEGLUDEC', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN+HCT+MEFT', 'LOSARTAN+HCT+METFORMINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('ENALAPRIL  INSULINA DEGLUDEC+DEPAGLIFOZINA', 'ENALAPRIL+INSULINA DEGLUDEC+DEPAGLIFOZINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('VALSARTANX 6', 'VALSARTAN', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('METROPOLOL', 'METOPROLOL', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('IBERSERTAN+AMLOD', 'IBERSERTAN+AMLODIPINO', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('HIDROCLOROTIAZIDA   TABLETA', 'HIDROCLOROTIAZIDA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('METOPROLOL UNICAMENTE', 'METOPROLOL', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('AMLODIPINO+GHIDROCLORORIZIDA Y METFORMINA', 'AMLODIPINO+HIDROCLOROTIAZIDA+METFORMINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('VILDAGLITINA METFORMINA', 'VILDAGLIPTINA+METFORMINA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('PROPONOOL', 'PROPRANOLOL', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN+CARVEDILLOL+ASA+HTZA+CLOPIDROGEL', 'LOSARTAN+CARVEDILOL+ASA+HTZA+CLOPIDOGREL', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('LOSARTAN+ESPIRINOLACTONA', 'LOSARTAN+ESPIRONOLACTONA', regex=True)
        self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('HOLMENTAN H', 'HOLMENTAN', regex=True)

        #self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].str.replace('\s+', ' ')
        #fix plus signs
        self.df = self.fix_plus_sign(self.df)

    def fixed_data2(self):
        print("Unique values in column:")
        print("**{}**".format(self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].unique()))

        self.df = self.trim_all_columns(self.df)

        patterns_and_replacements = [
            (r'\bLOSARTAN\s*\+\s*HCT\s*ASA\b', 'LOSARTAN+HCT+ASA'),
            (r'\bLOSARTAN\s*\+\s*HCTA\s*ASA\b', 'LOSARTAN+HCT+ASA'),
            (r'\bLOSARTAN\s*AMLODIPINO\b', 'LOSARTAN+AMLODIPINO'),
            (r'\bENALAPRIL\s*AMLODIPINO\s*HCT\b', 'ENALAPRIL+AMLODIPINO+HCT'),
            (r'\bVALSARTAN\s*Y\s*AMLODIPINO\b', 'VALSARTAN+AMLODIPINO'),
            (r'\bVALSARTAN\s+6\b', 'VALSARTAN'),
            (r'\bHIDROCLOROTIAZIDA\b', 'HCTZ'),
            (r'\bHCTZ\s*+\s*ENALAPRIL\s*+\s*NIFEDIPINO\b', 'HCTZ+ENALAPRIL+NIFEDIPINO'),
            (r'\bLOSARTAN\s*\.\s*FUROSEMIODA\s*\+\s*AMLODIPINO\b', 'LOSARTAN+FUROSEMIDA+AMLODIPINO'),
            (r'\bINSULINA DEGLUDEC\s*\+\s*DEPAGLIFOZINA\b', 'INSULINA DEGLUDEC+DAPAGLIFLOZINA'),
            (r'\s{2,}', ' '),  # Replace multiple spaces with single space
            (r'\s*-\s*', '-'),  # Remove spaces around hyphens
            (r'\s*\/\s*', '/'),  # Remove spaces around slashes
            (r'\s*\+\s*', '+'),  # Remove spaces around plus signs
            (r'(\b[a-zA-Z])\.([a-zA-Z])\.', r'\1\2'),  # Remove periods from abbreviations
            (r'\s*\.+\s*', '.'),  # Remove multiple periods and spaces around periods
            (r'\s*\,\s*', ','),  # Remove spaces around commas
             (r'(\b\w+)\.(\w+\b)', r'\1\2'),  # Remove periods between words
            (r'\bTELMISARTAN\b', 'TELMISARTAN'),
            (r'\bHOLMENTAN\b', 'OLMESARTAN'),
            (r'\bLOSRTAN\b', 'LOSARTAN'),
            (r'\bLOSAR\b', 'LOSARTAN'),
            (r'\bLOSARTSN\b', 'LOSARTAN'),
            (r'\bLIBERSARTAN\b', 'IRBESARTAN'),
            (r'\bIBERSERTAN\b', 'IRBESARTAN'),
            (r'\bIBERSARTAN\b', 'IRBESARTAN'),
            (r'\bANALAPRIL\b', 'ENALAPRIL'),
            (r'\bCARVEDILLOL\b', 'CARVEDILOL'),
            (r'\bENLAPRIL\b', 'ENALAPRIL'),
            (r'\bENELAPRIL\b', 'ENALAPRIL'),
            (r'\bENALAPRIL\s+INSULINA\b', 'ENALAPRIL+INSULINA'),
            (r'\bSACUBITRILO\+VVALSARTAN\b', 'SACUBITRILO+VALSARTAN'),
            (r'\bESPIRINOLACTINA\b', 'ESPIRONOLACTONA'),
            (r'\bESPIRINOLATONA\b', 'ESPIRONOLACTONA'),
            (r'\bMETFORMINA\s*+\s*ROSUVASTATIBNA\b', 'METFORMINA+ROSUVASTATINA'),
            (r'\bATORVASRTATINA\b', 'ATORVASTATINA'),
            (r'\bGENGIBROZILO\b', 'GEMFIBROZILO'),
            (r'\bGENFIBROZILO\b', 'GEMFIBROZILO'),
            (r'\bGENFIBROXILO\b', 'GEMFIBROZILO'),
            (r'\bLIRAGLUTINA\b', 'LIRAGLUTIDE'),
            (r'\bCARVERIDOL\b', 'CARVEDILOL'),
            (r'\bCARDERIDOL\b', 'CARVEDILOL'),
            (r'\bNIMODIFPINO\b', 'NIMODIPINO'),
            (r'\bIECA\b', 'IECA'),
            (r'\bARA\b', 'ARA'),
            (r'\bHCTZ\b', 'HCTZ'),
            (r'\bHCT\b', 'HCTZ'),
            (r'\bMEFT\b', 'METFORMINA'),
            (r'\bMALEATO\b', 'MALEATO'),
            (r'\bDEGLUDEC\b', 'DEGLUDEC'),
            (r'\bEUTIROX\b', 'LEVO'),
            (r'\bLEVOT\b', 'LEVO'),
            (r'\bLEVO\b', 'LEVOTIROXINA'),
            (r'\bINSULINA DEGLUDEC+DAPAGLIFLOZIN\b', 'INSULINA DEGLUDEC+DAPAGLIFLOZIN'),
            (r'\bAMLODIPINO\s+GHIDROCLORORIZIDA\b', 'AMLODIPINO+HIDROCLOROTIAZIDA'),
            (r'\bGEMFIBROZILO\b', 'GEMFIBROZIL'),
            (r'\bGENFIBROZILO\b', 'GEMFIBROZIL'),
            (r'\bGLIMEPERIDINA\b', 'GLIMEPERIDINA'),
            (r'\bGLIMEPRIDINA\b', 'GLIMEPERIDINA'),
            (r'\bNO APLICA\b', 'NO APLICA'),
            (r'\bTRATAMIENTO NO APLICA FARMACOLOGICO\b', 'NO APLICA'),
            (r'\bRETIRADO\b', 'NO APLICA'),
            (r'\bFUROSEMIDA\b', 'FUROSEMIDA'),
            (r'\bFLOXETINA\b', 'FLOXETINA'),
            (r'\bWARFARINA\b', 'WARFARINA'),
            (r'\bBLOQUEADOR BETA\b', 'BETA BLOQUEADOR'),
            (r'\bPROPRANOLOL\b', 'PROPRANOLOL'),
            (r'\bNEBIVOLOL\b', 'NEBIVOLOL'),
            (r'\bNIDAGLIPTINA\b', 'NIDAGLIPTINA'),
            (r'\bINSULINA GLARGINA\b', 'INSULINA GLARGINA'),
            (r'\bSITAGLIPTINA\b', 'SITAGLIPTINA'),
            (r'\bVIDALGLIPTINA\b', 'VIDALGLIPTINA'),
            (r'\bVILDAGLIPTINA\b', 'VILDAGLIPTINA'),
            (r'\bDAPAGLIFLOZINA\b', 'DAPAGLIFLOZINA'),
            (r'\bEMPAGLIFLOZINA\b', 'EMPAGLIFLOZINA'),
            (r'\bLANSOPRAZOL\b', 'LANSOPRAZOL'),
            (r'\bEZOMEPRAZOL\b', 'ESOMEPRAZOL'),
            (r'\bCLOPIDOGREL\b', 'CLOPIDOGREL'),
            (r'\bGLIMEPERIDINA\b', 'GLIMEPIRIDA'),
            (r'\bGLIMEPIRIDE\b', 'GLIMEPIRIDA'),
            (r'\bACIDO ACETILSALICILICO\b', 'ASA'),
            (r'\bAMLOD\b', 'AMLODIPINO'),
            (r'\bMETOCARBAMOL\b', 'METHOCARBAMOL'),
            (r'\bMETFORMINA\s*+\s*ROSUVASTATIBNA\b', 'METFORMINA+ROSUVASTATINA'),
            (r'\bOTORVASTATINA\b', 'ATORVASTATINA'),
            (r'\bVALSARTAN\s*X\b', 'VALSARTAN'),
            (r'\bVALSARTAN\s*\*\s*6\b', 'VALSARTAN'),
            (r'\bARA\s*II\b', 'ARA'),
            (r'\bARA\s*\+\s*AMLODIPINO\b', 'ARA+AMLODIPINO'),
            (r'\bARA\s*\+\s*NIFEDIPINO\b', 'ARA+NIFEDIPINO'),
            (r'\bLOSARTAN HCTZ\b', 'LOSARTAN+HCTZ'),
            (r'\bARA\s*\+\s*HIDROCLOROTIAZIDA\b', 'ARA+HCTZ'),
            (r'\bARA\s*\+\s*METOPROLOL\b', 'ARA+METOPROLOL'),
            (r'\bVALSARTAN\+HCTZTABLETA\b', 'VALSARTAN+HCTZ'),
            (r'\bAMLODIPINO TABLETA\b', 'AMLODIPINO'),
            (r'\bVALSARTAN\+HCTZTABLETA\b', 'VALSARTAN+HCTZ'),
            (r'\bLOSARTAN ASA ROSUV\b', 'LOSARTAN+ASA+ROSUVASTATINA'),
            (r'\bAMLODIPINO\+LOSARAN\.\+HCTZ\+CLONIDINA\b', 'AMLODIPINO+LOSARANTAN+HCTZ+CLONIDINA'),
            (r'\bAMLODIPINO\+GHIDROCLORORIZIDA Y METFORMINA\b', 'AMLODIPINO+HCTZ+METFORMINA'),
            (r'\bIRBESARTAN\+HCTZ\.\b', 'IRBESARTAN+HCTZ'),
            (r'\bMIOCARDIX\+TELMISARTAN\b', 'MICARDIS+TELMISARTAN'),
            (r'\bLOSARTAN AMLODIPINI ASA\b', 'LOSARTAN+AMLODIPINO+ASA'),
            (r'\bENALAPRIL ASA\b', 'ENALAPRIL+ASA'),
            (r'\bACETIL SALICILICO\b', 'ASA'),
            (r'\bMELOXICAN\b', 'MELOXICAM'),
            (r'\bROSUVASTATIBNA\b', 'ROSUVASTATINA'),
            (r'\bDAPAGLIFOXINA\b', 'DAPAGLIFLOXINA'),
            (r'\bRIVAROXON\b', ' RIVAROXABAN'),
            (r'\bFLOXETINA\b', 'FLUOXETINA'),
            (r'\bACETILSALICILICO\b', 'ASA'),
            (r'\bMTOPROLOL\b', 'METOPROLOL'),
            (r'\bESPIRONOLCTONA\b', 'ESPIRONOLACTONA'),
            (r'\bVILDAGLIPTIMA\b', 'VIDALGLIPTINA'),
            (r'\bMETFORMINA\+VILDAGLIPTIMA\+METFOTMINA\b', 'METFORMINA+VIDALGLIPTINA'),
            (r'\bGLIMEPIRIDA\+ATORVASTATINA\+VIDALGLIPTINA R\+METFORMINA R\b', 'GLIMEPIRIDA+ATORVASTATINA+VIDALGLIPTINA+METFORMINA'),
            (r'\bCARBAMEZAPINA\b', 'CARBAMAZEPINA'),
            (r'\bEMPAGLIFOZINA\b', 'EMPAGLIFLOZIN'),
            (r'\bHCTZ\.\b', 'HCTZ'),
            (r'\s*TABLETA\b', '')

        ]

        for pattern, replacement in patterns_and_replacements:
            compiled_pattern = re.compile(pattern, flags=re.IGNORECASE)
            self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'] = self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].apply(
                lambda x: re.sub(compiled_pattern, replacement, x) if isinstance(x, str) else x
            )

        print("Fixed unique values in column:")
        print("**{}**".format(self.df['OTROS FARMACOS ANTIHIPERTENSIVOS'].unique()))
        numerical_cols = self.df.select_dtypes(include=['int64','float64']).columns

        for col in numerical_cols:
            self.df[col] = self.df[col].apply(lambda x: np.nan if x == 999 else x)
            self.df[col] = self.df[col].apply(lambda x: np.nan if x == 9999 else x)
            self.df[col] = self.df[col].apply(lambda x: np.nan if x >= 9999 else x)

        for i, value in enumerate(self.df['ALBUMINURIA/CREATINURIA']):
            try:
                if int(value) == 999 or int(value) == 9999 or float(value) >= 9999:
                    self.df.at[i, 'ALBUMINURIA/CREATINURIA'] = np.nan
            except ValueError:
                pass

        return self.df

    @staticmethod
    def get_nan_per_col(df):
        nan_percentage = ((df.isna().sum() * 100) / df.shape[0]).sort_values(ascending=True)
        return nan_percentage

    @staticmethod
    def remove_by_nan(accepted_nan_percentage, nan_percentage_series, df):
        columns_dropped = []
        for items in nan_percentage_series.items():
            if items[1] > accepted_nan_percentage:
                df = df.drop([items[0]], axis=1)
                columns_dropped.append(items)

        return df, columns_dropped

    def drop_nan(self):
        nan_percentages = self.get_nan_per_col(self.df)
        self.df_clean, columns_dropped = self.remove_by_nan(5, nan_percentages, self.df)
        self.df_clean = self.df_clean.dropna()

    def save_df(self):
        self.df_clean = self.df
        self.df_clean = self.df_clean.reset_index(drop=True)
        self.df_clean.to_csv(self.saving_path)
        print("Clean data successfully saved in: {}".format(self.saving_path))

    def run(self):
        print("------------------------------------------------")
        print("Cleaning...")
        self.read_csv()
        self.drop_columns()
        self.concat_dfs()
        self.replace_blanks()
        self.fixed_data()
        self.fixed_data2()
        self.data_types()
        self.drop_nan()
        print("Data successfully cleaned!")
        self.save_df()
        print("------------------------------------------------")

    #gets

    def get_caqueta_df(self):
        return self.caqueta_df

    def get_narino_df(self):
        return self.narino_df

    def get_putumayo_df(self):
        return self.putumayo_df

    def get_first_caqueta_clean(self):
        return self.first_caqueta_clean

    def get_first_narino_clean(self):
        return self.first_narino_clean

    def get_first_putumayo_clean(self):
        return self.first_putumayo_clean

    def get_df(self):
        return self.df

    def get_unified_df(self):
        return self.unified_df

    def get_df_clean(self):
        return self.df_clean

"""

# Plot missingness of unified data
print("********* UNIFIED DF ********")
print(df.info())
msno.matrix(df);
plt.title("Data after main drops and blanks to no aplica")
plt.show()

# Plot correlation heatmap of missingness
msno.heatmap(df, cmap='rainbow');
plt.title("Missingness - Data after main drops")
#plt.rc('font', size=1)
plt.show()

print("************************* Imputing with MICE ********************")

df_mice = df.filter(['CALCULO DE RIESGO DE Framingham (% a 10 años)','GLICEMIA 100 MG/DL_DIC','COLESTEROL TOTAL > 200 MG/DL_DIC', 'PERIMETRO ABDOMINAL'], axis=1).copy()

# Define MICE Imputer and fill missing values
mice_imputer = IterativeImputer(estimator=linear_model.BayesianRidge(), n_nearest_features=None, imputation_order='ascending')

df_mice_imputed = pd.DataFrame(mice_imputer.fit_transform(df_mice), columns=df_mice.columns)

print("************************* imputed ********************")
print(df_mice_imputed.info())
df['CALCULO DE RIESGO DE Framingham (% a 10 años)'] = df_mice_imputed['CALCULO DE RIESGO DE Framingham (% a 10 años)']
df['GLICEMIA 100 MG/DL_DIC'] = df_mice_imputed['GLICEMIA 100 MG/DL_DIC']
df['COLESTEROL TOTAL > 200 MG/DL_DIC'] = df_mice_imputed['COLESTEROL TOTAL > 200 MG/DL_DIC']
df['PERIMETRO ABDOMINAL'] = df_mice_imputed['PERIMETRO ABDOMINAL']

msno.matrix(df);
plt.title("Missingness Data after imputation")
plt.show()

def get_nan_per_col(df):

      Iterates trough every column and caculates the percentage of nan values present in each column.

      :param df: {pandas data frame}.
      :type df: DataFrame.
      :return: a series of all the nan percentages asociated to the column name.
      :rtype: series.

    NAN_percentage = ((df.isna().sum() * 100) / df.shape[0]).sort_values(ascending=True)
    return NAN_percentage

def removeByNan(accepted_nan_percentage, nan_percentage_series, df):

      Takes a series that contains all the columns name with the respective nan percentage,
      then drops the columns that had nan content above the diven max nan limit.

      :param accepted_nan_percentage: {positive number that represent the limit of nan % acceptance}.
      :param nan_percentage_series: {series that contains all the columns name with the respective nan percentage}.
      :param df: {pandas data frame}.
      :type accepted_nan_percentage: float.
      :type nan_percentage_series: series.
      :type df: DataFrame.
      :return df: A dataframe without the columns that didint comply with the limit.
      :rtype df: DataFrame.
      :return columns_dropped: list of the name of the dropped columns and its nan percentage.
      :rtype columns_dropped: list[str,float].

    columns_dropped = []
    for items in nan_percentage_series.iteritems():
        if items[1] > accepted_nan_percentage:
            df = df.drop([items[0]], axis=1)
            columns_dropped.append(items)

    return df, columns_dropped

NAN_percentages = get_nan_per_col(df)
df_clean, columns_dropped = removeByNan(5, NAN_percentages, df)

print("****************************** nans drops ******************************")
print(NAN_percentages)
print(columns_dropped)

df_clean = df_clean.dropna()
df_clean = df_clean.reset_index(drop=True)
df_clean.to_csv(paths["clean_data"])

print(df_clean.info())

print("****************************** raw graphs ******************************")

#boxplot de RIESGO DE Framingham

df_clean['CALCULO DE RIESGO DE Framingham (% a 10 años)'] = df_clean['CALCULO DE RIESGO DE Framingham (% a 10 años)'].astype(int)
plt.boxplot(df_clean['CALCULO DE RIESGO DE Framingham (% a 10 años)'])
plt.title('Distribucion de riesgo de Framingham')
plt.show()

#Histograma de edad

df_clean['Edad'].hist(bins=5)
plt.title('Distribucion de la edad')
plt.xlabel('Edad')
plt.ylabel('Cuenta')
plt.show()

#bar chart género
gender_counts = df['Cod_Género'].value_counts()
ax = gender_counts.plot(kind='bar')
plt.title('Distribucion de género')
plt.xlabel('Cod_Género')
plt.ylabel('Número de personas')
ax.set_xticklabels(['H', 'M'])
plt.show()

#bar chart de fumador
smoker_counts = df['Fumador Activo'].value_counts()
ax = gender_counts.plot(kind='bar')
plt.title('Distribucion de fumadores activos')
plt.xlabel('Fumador Activo')
plt.ylabel('Número de personas')
ax.set_xticklabels(['No fuma', 'Fuma'])
plt.show()

cycle_counts = df_clean['CiclosV'].value_counts()
cycle_percentages = cycle_counts / cycle_counts.sum() * 100
fig, ax = plt.subplots()
ax.pie(cycle_counts)
ax.axis('equal')
plt.title('Distribucion ciclos de vida')
labels = [f'{i}: {j:.1f}%' for i, j in zip(cycle_percentages.index, cycle_percentages)]
plt.legend(bbox_to_anchor=(0.85, 1), loc='upper left',labels=labels)
plt.show()

"""


import qiskit
import sys
import os

from qiskit.dagcircuit import DAGCircuit
from Scheduler.DagHandler import dag_to_list, hash_adj_list, chop_subdag, divide_into_subdags
from qiskit import QuantumCircuit
from qiskit.converters import circuit_to_dag
from Utils import put_subdags_into_csv, hash_training_data

'''
This module parses the given qasm file into QC, converts it into DAG,
returns a temporary csv file containing hashed circuit data to be analyzed
by Learner class
'''

qasm_file = ""

if(len(sys.argv) < 2):
    print("Qasm file not provided. Exiting")
    sys.exit(1)
else:
    if(os.path.splitext(sys.argv[1])[1] != ".qasm"):
        print("Incorrect file provided, expected .qasm file")
        sys.exit(1)
    qasm_file = sys.argv[1]

circuit = qiskit.QuantumCircuit.from_qasm_file(qasm_file)
dag = circuit_to_dag(circuit)

adj_list = dag_to_list(dag)
subdags, cx_direction_exists = divide_into_subdags(adj_list)

if(cx_direction_exists):
    pred_file = open("temp_pred.csv", 'a')
    pred_file.write("3,")
    pred_file.close()

for subdag in subdags:
    chopped_dag = list(chop_subdag(subdag))
    put_subdags_into_csv("temp_eval.csv", chopped_dag)

hash_training_data("temp_eval.csv", "temp_eval_hashed.csv", 3)

sys.exit(0)


from colorama import Fore, Style
import glob
import pickle
import time
import os

from app.environment.params import PIPELINE_DIRECTORY_HEART_ATTACK
from sklearn.pipeline import Pipeline

def save_pipeline(pipeline: Pipeline) -> None:
    timestamp = time.strftime("%Y%m%d-%H%M%S")  # e.g. 20210824-154952

    # Save pipeline locally
    pipeline_path = os.path.join(PIPELINE_DIRECTORY_HEART_ATTACK ,f"{timestamp}.pkl")
    pickle.dump(pipeline, open(pipeline_path, 'wb'))

    print("✅ Pipeline saved locally")

    return None

def load_pipeline() -> Pipeline:
    #load pipeline locally
    local_pipeline_paths = glob.glob(f"{PIPELINE_DIRECTORY_HEART_ATTACK}/*")
    if not local_pipeline_paths:
        print(Fore.YELLOW +
                f"⚠ No pipeline found in {PIPELINE_DIRECTORY_HEART_ATTACK}"
                + Style.RESET_ALL)
        raise FileNotFoundError

    most_recent_pipeline_path_on_disk = sorted(
        local_pipeline_paths)[-1]

    print(f"✅ Pipeline found at {most_recent_pipeline_path_on_disk}")
    print(Fore.BLUE + f"\nLoad latest pipeline from disk..." + Style.RESET_ALL)

    latest_pipeline = pickle.load(
        open(most_recent_pipeline_path_on_disk, "rb"))

    print("✅ Pipeline loaded from local disk")

    return latest_pipeline


def kill_monsters(health, monsters, damage):
#     1 punch kills a monster
# after 3 punches, OPM gets hit with damage once
# health = # of hits
# if health is <= zero, return 'hero died'
# for each multiple of 3, hits++
    hits = 0
    for i in range(1, monsters):
        print('i: ',i)
        if i % 3 == 0:
            hits += 1
            print('hits: ',hits)
    damage_done = hits*damage
    health -= damage_done
    if health <= 0:
        return 'hero died'

    return f'hits: {hits}, damage: {damage_done}, health: {health}'


from __future__ import division

MEAN_LIST=[]

def bayes_filter(idx, y, true_x, prior_mean, p, eps_mean, q, n):
    i=idx
    return cal_mean(i, y, true_x, prior_mean, p, eps_mean, q, n)

def cal_mean(i, y, true_x, prior_mean, p, eps_mean, q, n):
    MEAN_LIST.append(prior_mean)
    cur_i=i
    y_i=y[cur_i]
    precision=p+q
    mean=(p*prior_mean+q*y_i)/precision

    if cur_i==n-1:
        return MEAN_LIST, mean

    print("bayes step {}:".format(i), "mean = {}".format(mean))
    cur_i+=1
    mean_list, mean_new=cal_mean(cur_i, y, true_x, mean, precision, eps_mean, q, n)

    return mean_list, mean_new


import math

def paint_calculator(height, width, coverage):
    cans = math.ceil((height * width) / coverage)
    print(f"You'll need {cans} cans")

height = int(input("What is the height of the wall: "))
width = int(input("What is the width of the wall: "))
coverage = 5

paint_calculator(height=height, width=width, coverage=coverage)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Updates the ExoPlayer version in platform/external/exoplayer."""
import argparse
import os
import sys
import subprocess
import atexit
import shutil
import re
import datetime

TEMPORARY_TREE_CHECKOUT_DIR = ".temp_tree/"
TREE_LOCATION = "tree/"
METADATA_FILE = "METADATA"

def run(command, check=True):
  print(f"Running: {command}")
  return (subprocess.run(
      command, shell=True, check=check, capture_output=True, text=True)
          .stdout.strip())

# Argument parsing.
parser = argparse.ArgumentParser(
    description=f"Update the ExoPlayer version in the {TREE_LOCATION}"
        " directory and stage changes for commit.")
parser.add_argument(
    "--tag",
    help="The tag to update the ExoPlayer version to.")
parser.add_argument(
    "--commit",
    help="The commit SHA to update the ExoPlayer version to.")
parser.add_argument(
    "--branch",
    help="The branch to create for the change.",
    default="update-exoplayer")
args = parser.parse_args()

script_directory = os.path.dirname(os.path.abspath(sys.argv[0]))
os.chdir(script_directory)

if (args.tag is None) == (args.commit is None):
  parser.print_help()
  sys.exit("\nError: Please provide the tag or the commit. But not both.")

# Check whether the branch exists, and abort if it does.
if run(f"git rev-parse --verify --quiet {args.branch}", check=False):
  parser.print_help()
  sys.exit(f"\nBranch {args.branch} already exists. Please delete, or change "
      "branch.")

run(f"repo start {args.branch}")

# Cleanup function.
def cleanup():
  print(f"Restoring branch {args.branch}")
  run(f"git checkout {args.branch}")
  shutil.rmtree(TEMPORARY_TREE_CHECKOUT_DIR, ignore_errors=True)
atexit.register(cleanup)

# Update remote branches.
run("git fetch --all --tags")
if args.tag:
  # Get the commit SHA associated to the tag.
  commit = run(f"git rev-list -n 1 {args.tag}")
else: # a commit SHA was provided.
  commit = args.commit

# Checkout the version we want to update to.
run(f"git checkout {commit}")
# Checkout all files into a temporary dir.
run(f"git checkout-index -a --prefix={TEMPORARY_TREE_CHECKOUT_DIR}")
run(f"git checkout {args.branch}")
shutil.rmtree(TREE_LOCATION)
run(f"mv {TEMPORARY_TREE_CHECKOUT_DIR} {TREE_LOCATION}")
run(f"git add {TREE_LOCATION} {METADATA_FILE}")

with open(METADATA_FILE) as metadata_file:
  metadata_lines = metadata_file.readlines()

# Update the metadata file.
today = datetime.date.today()
with open(METADATA_FILE, "w") as metadata_file:
  for line in metadata_lines:
    line = re.sub(
        r"version: \".+\"", f"version: \"{args.tag or commit}\"", line)
    line = re.sub(r"last_upgrade_date {.+}", f"last_upgrade_date "
        f"{{ year: {today.year} month: {today.month} day: {today.day} }}",
        line)
    metadata_file.write(line)

run(f"git add {METADATA_FILE}")
print("All done. Ready to commit.")


from typing import List

class Solution:
    def hasValidPath(self, grid: List[List[int]]) -> bool:

        visited = [[False] * len(grid[0]) for i in range(len(grid))]

        def path(i, j, p, visited):

            if i >= 0 and i < len(grid) and j >= 0 and j < len(grid[0]) and visited[i][j] == False:

                if grid[i][j] not in p:
                    return False

                visited[i][j] = True

                if i == len(grid) - 1 and j == len(grid[0]) - 1:
                    return True

                if grid[i][j] == 1:
                    if path(i, j-1, visited, set([3, 5])) or path(i, j+1, visited, set([2, 4])):
                        return True

                if grid[i][j] == 2:
                    if path(i-1, j, visited, set([3, 6])) or path(i + 1, j, visited, set([5, 6])):
                        return True

                if grid[i][j] == 3:
                    if path(i, j-1, visited, set([3, 5])) or path(i + 1, j, visited, set([5, 6])):
                        return True

                if grid[i][j] == 4:
                    if path(i, j+1, visited, set(2, 4)) or path(i + 1, j, visited, set([5, 6])):
                        return True

                if grid[i][j] == 5:
                    if path(i, j-1, visited, set([3, 5])) or path(i - 1, j, visited, set([3, 6])):
                        return True
                if grid[i][j] == 6:
                    if path(i, j+1, visited, set([2, 4])) or path(i - 1, j, visited, set([3, 6])):
                        return True

            return False

        return path(0, 0, set([1, 2, 3, 4, 5, 6]), visited)

g = [[2, 4, 3], [6, 5, 2]]
s = Solution()
print(s.hasValidPath(g))


from time import sleep
from copier import Clipboard, AutoCopier
from ui import Ui_Dialog
from translator import Translator
from sys import argv, exit
from PySide2.QtWidgets import QApplication, QWidget
from PySide2.QtCore import QThread, Signal, Qt
from PySide2.QtGui import QIcon, QPixmap
from icon import icon
import xml.etree.ElementTree as ET

class MainWindow(QWidget):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowFlags(
            Qt.WindowMinimizeButtonHint |      # Enable minimize button
            Qt.WindowCloseButtonHint |         # Enable close button
            Qt.WindowStaysOnTopHint)           # Window stays on top
        pixmap = QPixmap()
        pixmap.loadFromData(icon)
        self.setWindowIcon(QIcon(pixmap))

        self.ui = Ui_Dialog()   # Create UI
        self.ui.setupUi(self)   # Import UI

        self.checker = Checker(self.ui)

    def run(self):
        self.checker.start()

    def closeEvent(self, event):
        self.checker.close()
        event.accept()
        tran.close()

class Checker(QThread):
    singal = Signal(str, str)
    def __init__(self, ui):
        super().__init__()
        self.clipboard = Clipboard()
        self.isworking = True
        self.singal.connect(ui.changeText)

    def close(self):
        self.isworking = False
        self.wait()
        print("checker is free")

    def run(self):
        while self.isworking:
            flag, text = self.clipboard.getText() # Get clipboard content
            if flag:                              # If new copied content
                tran_text = tran.translate(text)  # Get translated text
                self.singal.emit(text, tran_text) # Send text to UI thread
                sleep(1)
            else:
                sleep(0.5)

        self.singal.disconnect()

if __name__ == "__main__":
    tree = ET.parse("config.xml")
    root = tree.getroot()

    headless = bool(int(root.find("headless").text))
    autocopy = bool(int(root.find("autocopy").text))
    refresh_rate = int(root.find("refresh_rate").text)
    refresh_time = float(root.find("refresh_time").text)

    app = QApplication(argv)   # Create application
    tran = Translator(headless=headless, refresh_time=refresh_time, refresh_rate=refresh_rate) # Create translator
    myWindow = MainWindow()    # Create window

    if autocopy:
        AutoCopier()

    myWindow.show()            # Show window
    myWindow.run()             # Run window
    exit(app.exec_())          # Run program until closed


incomes = {
    'apple': 5600.20,
    'orange': 3500.45,
    'banana': 5000.00,
    'bergamot': 3700.56,
    'durian': 5987.23,
    'grapefruit': 300.40,
    'peach': 10000.50,
    'pear': 1020.00,
    'persimmon': 310.00,
}

print('Общий доход за год составил:', sum(incomes.values()))

arr = list(incomes)[list(incomes.values()).index(min(incomes.values()))]

print('Самый маленький доход у', arr, '. Он составляет ', min(incomes.values()))

incomes.pop(arr)

print('Итоговый словарь:\n', incomes)

Методы словарей/Кризис фруктов/Кризис фруктов_1.py

Кризис фруктов_1.py


"""
cry_gen_docs
~~~~~~~~~~~~

This module provides utilities for generating Sphinx documentation for the project.
It sets up the necessary directories, creates a configuration file for Sphinx,
generates a table of contents, and runs the Sphinx build process.

"""

import os
import subprocess
import logging
from globals import LOG_LEVEL

logging.basicConfig(level=LOG_LEVEL)

def setup_directories(script_dir):
    """Sets up the necessary directories for documentation generation.

    Args:
        script_dir (str): The directory where this script is located.

    Returns:
        tuple: A tuple containing paths to the main, modules, routes, and docs directories.
    """
    main_dir = os.path.join(script_dir, '..', 'main')
    modules_dir = os.path.join(script_dir, '..', 'modules')
    routes_dir = os.path.join(script_dir, '..', 'routes')
    docs_dir = os.path.abspath(os.path.join(script_dir, '..', 'docs'))
    if not os.path.exists(docs_dir):
        os.makedirs(docs_dir)

    return main_dir, modules_dir, routes_dir, docs_dir

def create_conf_py(main_dir, modules_dir, routes_dir, docs_dir):
    """Creates a `conf.py` file for Sphinx in the docs directory.

    Args:
        main_dir (str): Path to the main directory.
        modules_dir (str): Path to the modules directory.
        routes_dir (str): Path to the routes directory.
        docs_dir (str): Path to the docs directory.
    """
    conf_py_path = os.path.join(docs_dir, 'conf.py')
    conf_py_content = (
        "# Configuration file for the Sphinx documentation builder.\n"
        "import os\n"
        "import sys\n"
        "\n"
        f"project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))\n"
        "sys.path.insert(0, project_root)\n"
        "\n"
        "extensions = ['autoapi.extension']\n"
        "\n"
        "# AutoAPI settings\n"
        f"autoapi_dirs = ['{main_dir}','{modules_dir}','{routes_dir}']\n"
        "autoapi_exclude = ['venv', 'pip', 'django', '__init__.py']\n"
        "autoapi_file_patterns = ['*.py']\n"
        "\n"
        "html_theme = 'pydata_sphinx_theme'\n"
    )

    with open(conf_py_path, 'w') as conf_py:
        conf_py.write(conf_py_content)
    logging.info(f"Configuration file created at {conf_py_path}")

def generate_autoapi_toc_entries(docs_dir):
    """Generates table of contents entries for AutoAPI.

    Args:
        docs_dir (str): Path to the docs directory.
    """
    with open(os.path.join(docs_dir, 'index.rst'), 'w') as index_rst:
        index_rst.write(
            "Welcome to AmethystKey - CRYSTAL Secret Management's documentation!\n"
            "=========================================================================\n\n"
            ".. toctree::\n"
            "   :maxdepth: 2\n"
            "   :caption: Contents:\n\n"
            "   autoapi/index\n"
            ".. toctree::\n"
            "   :maxdepth: 1\n"
            "   :caption: AutoAPI Documentation\n\n"
        )

        for root, _, files in os.walk(os.path.join(docs_dir, '_build', 'autoapi')):
            for file in files:
                if file.endswith(".rst"):
                    rst_path = os.path.relpath(os.path.join(root, file),
                                               os.path.join(docs_dir, '_build', 'autoapi'))
                    index_rst.write(f"   autoapi/{rst_path}\n")
    logging.info("TOC entries for AutoAPI generated.")

def run_sphinx_build(docs_dir):
    """Runs the Sphinx build process.

    Args:
        docs_dir (str): Path to the docs directory.
    """
    build_dir = os.path.join(docs_dir, '_build')
    if not os.path.exists(build_dir):
        os.makedirs(build_dir)
    subprocess.run(['sphinx-build', '-b', 'html', '.', '_build'], cwd=docs_dir)
    logging.info("Sphinx build completed.")

def generate_documentation():
    """Main function to generate the project documentation."""
    try:
        script_dir = os.path.dirname(os.path.abspath(__file__))
        main_dir, modules_dir, routes_dir, docs_dir = setup_directories(script_dir)
        create_conf_py(main_dir, modules_dir, routes_dir, docs_dir)
        generate_autoapi_toc_entries(docs_dir)
        run_sphinx_build(docs_dir)
        logging.info("Documentation generation completed successfully.")
    except Exception as e:
        logging.error(f"Error generating documentation: {str(e)}")


import random
import string
import sys
import os

# =============================================================================
# PARENT MODULES

if __name__ == "__main__":
    import nicePrints
else:
    from . import nicePrints

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

# Restituisce un array casuale strutturato nel seguente modo
# [YYYYYEEEEESSSS]
# Il numero di Y, E, e S, è casuale
def randomYESarray():
    outputArray = []

    numOfY = extractRandomNumber(1,20)
    numOfE = extractRandomNumber(1,20)
    numOfS = extractRandomNumber(1,20)

    for i in range(0,numOfY):
        outputArray.append("Y")

    for i in range(0,numOfE):
        outputArray.append("E")

    for i in range(0,numOfS):
        outputArray.append("S")

    return outputArray

# Restituisce una stringa casuale
def randomString(stringLength: int,upperLetters=True,lowerLetters=True,numbers=True,specialChars=True):

    if((upperLetters is False) and (lowerLetters is False) and (numbers is False)):
        nicePrints.errorPrint("Bisogna specificare almeno uno dei due campi da randomizzare.")
        raise Exception()

    choices = ""

    #aggiunge lettere maiuscole
    if(upperLetters):
        choices += string.ascii_uppercase

    #aggiunge lettere minuscole
    if(lowerLetters):
        choices += string.ascii_lowercase

    #aggiunge numeri
    if(numbers):
        choices += string.digits

    #aggiunge caratteri speciali
    if(specialChars):
        choices += str('#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~')

    return ''.join(random.choices(choices, k=stringLength))

# Restituisce un array di stringhe casuali
def randomStringArray(arrayLen: int, minStrLen: int, maxStrLen: int, lowerLetters=True,upperLetters=True,numbers=True):
    outputArray = []

    if((upperLetters is False) and (lowerLetters is False) and (numbers is False)):
        nicePrints.errorPrint("Bisogna specificare almeno uno dei due campi da randomizzare.")
        raise Exception()

    for i in range(0,arrayLen):
        strToAppend = randomString(stringLength=extractRandomNumber(minStrLen,maxStrLen),lowerLetters=lowerLetters,upperLetters=upperLetters,numbers=numbers)
        outputArray.append(strToAppend)

    return outputArray

# Restituisce un array di numeri casuali
def randomIntegerArray(lb, ub, d):
    a = []
    for i in range(lb, ub):
        a.append(i)

    r = []
    for index in range(d):
        r.append(random.choice(a))
    return r

# Restituisce un numero casuale
def extractRandomNumber(lb, ub):
    a = []
    for i in range(lb, ub):
        a.append(i)
    return random.choice(a)


from collections import OrderedDict

import numpy as np
import pytorch_lightning as pl
import torch
import torchvision
from nowcasting_utils.models.loss import get_loss
from pl_bolts.optimizers.lr_scheduler import LinearWarmupCosineAnnealingLR
from torch.optim import lr_scheduler

from satflow.models import ConvLSTM, R2U_Net
from satflow.models.gan import GANLoss, define_discriminator, define_generator
from satflow.models.layers import ConditionTime

class CloudGAN(pl.LightningModule):
    def __init__(
        self,
        forecast_steps: int = 48,
        input_channels: int = 12,
        lr: float = 0.0002,
        beta1: float = 0.5,
        beta2: float = 0.999,
        num_filters: int = 64,
        generator_model: str = "runet",
        norm: str = "batch",
        use_dropout: bool = False,
        discriminator_model: str = "enhanced",
        discriminator_layers: int = 0,
        loss: str = "vanilla",
        scheduler: str = "plateau",
        lr_epochs: int = 10,
        lambda_l1: float = 100.0,
        l1_loss: str = "l1",
        channels_per_timestep: int = 12,
        condition_time: bool = False,
        pretrained: bool = False,
    ):
        """
        Creates CloudGAN, based off of https://www.climatechange.ai/papers/icml2021/54
        Changes include allowing outputs for all timesteps, optionally conditioning on time
        for single timestep output

        Args:
            forecast_steps: Number of timesteps to forecast
            input_channels: Number of input channels
            lr: Learning Rate
            beta1: optimizer beta1
            beta2: optimizer beta2 value
            num_filters: Number of filters in generator
            generator_model: Generator name
            norm: Norm type
            use_dropout: Whether to use dropout
            discriminator_model: model for discriminator, one of options in define_discriminator
            discriminator_layers: Number of layers in discriminator, only for NLayerDiscriminator
            loss: Loss function, described in GANLoss
            scheduler: LR scheduler name
            lr_epochs: Epochs for LR scheduler
            lambda_l1: Lambda for L1 loss, from slides recommended between 5-200
            l1_loss: Loss to use for the L1 in the slides, default is L1, also SSIM is available
            channels_per_timestep: Channels per input timestep
            condition_time: Whether to condition on a future timestep, similar to MetNet
        """
        super().__init__()
        self.lr = lr
        self.b1 = beta1
        self.b2 = beta2
        self.loss = loss
        self.lambda_l1 = lambda_l1
        self.lr_epochs = lr_epochs
        self.lr_method = scheduler
        self.forecast_steps = forecast_steps
        self.input_channels = input_channels
        self.output_channels = forecast_steps * channels_per_timestep
        self.channels_per_timestep = channels_per_timestep
        self.condition_time = condition_time
        if condition_time:
            self.ct = ConditionTime(forecast_steps)
        # define networks (both generator and discriminator)
        gen_input_channels = (
            input_channels  # + forecast_steps if condition_time else input_channels
        )
        self.recurrent = (
            False  # Does the generator generate all timesteps at once, or a single one at a time?
        )
        if generator_model == "runet":
            generator_model = R2U_Net(gen_input_channels, self.output_channels, t=3)
        elif generator_model == "convlstm":
            self.recurrent = True  # ConvLSTM makes a list of output timesteps
            generator_model = ConvLSTM(
                gen_input_channels, hidden_dim=num_filters, out_channels=self.channels_per_timestep
            )
        self.generator = define_generator(
            gen_input_channels,
            self.output_channels,
            num_filters,
            generator_model,
            norm,
            use_dropout,
        )
        if generator_model == "convlstm":
            # Timestep x C x H x W inputs/outputs, need to flatten for discriminator
            # TODO Add Discriminator that can use timesteps
            self.flatten_generator = True
        else:
            self.flatten_generator = False

        self.discriminator = define_discriminator(
            self.channels_per_timestep if condition_time else self.output_channels,
            num_filters,
            discriminator_model,
            discriminator_layers,
            norm,
        )

        # define loss functions
        self.criterionGAN = GANLoss(loss)
        self.criterionL1 = get_loss(l1_loss, channels=self.channels_per_timestep)
        self.save_hyperparameters()

    def train_per_timestep(
        self, images: torch.Tensor, future_images: torch.Tensor, optimizer_idx: int, batch_idx: int
    ):
        """
        For training with conditioning on time, so when the model is giving a single output

        This goes through every timestep in forecast_steps and runs the training
        Args:
            images: (Batch, Timestep, Channels, Width, Height)
            future_images: (Batch, Timestep, Channels, Width, Height)
            optimizer_idx: int, the optiimizer to use

        Returns:

        """

        if optimizer_idx == 0:
            # generate images
            total_loss = 0
            vis_step = True if np.random.random() < 0.01 else False
            generated_images = self(
                images, forecast_steps=self.forecast_steps
            )  # (Batch, Channel, Width, Height)
            for i in range(self.forecast_steps):
                # x = self.ct.forward(images, i)  # Condition on future timestep
                # fake = self(x, forecast_steps=i + 1)  # (Batch, Channel, Width, Height)
                fake = generated_images[:, :, i, :, :]  # Only take the one at the end
                if vis_step:
                    self.visualize_step(
                        images, future_images[:, i, :, :], fake, batch_idx, step=f"train_frame_{i}"
                    )
                # adversarial loss is binary cross-entropy
                gan_loss = self.criterionGAN(self.discriminator(fake), True)
                # Only L1 loss on the given timestep
                l1_loss = self.criterionL1(fake, future_images[:, i, :, :]) * self.lambda_l1
                self.log(f"train/frame_{i}_l1_loss", l1_loss)
                g_loss = gan_loss + l1_loss
                total_loss += g_loss
            g_loss = total_loss / self.forecast_steps  # Get the mean loss over all timesteps
            tqdm_dict = {"g_loss": g_loss}
            output = OrderedDict({"loss": g_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/g_loss": g_loss})
            return output

        # train discriminator
        if optimizer_idx == 1:
            # Measure discriminator's ability to classify real from generated samples
            # generate images
            total_loss = 0
            generated_images = self(
                images, forecast_steps=self.forecast_steps
            )  # (Batch, Channel, Width, Height)
            for i in range(self.forecast_steps):
                # x = self.ct.forward(images, i)  # Condition on future timestep
                # fake = self(x, forecast_steps=i + 1)  # (Batch, Channel, Width, Height)
                fake = generated_images[:, :, i, :, :]  # Only take the one at the end
                real_loss = self.criterionGAN(self.discriminator(future_images[:, i, :, :]), True)
                # adversarial loss is binary cross-entropy
                fake_loss = self.criterionGAN(self.discriminator(fake), False)
                # Only L1 loss on the given timestep
                # discriminator loss is the average of these
                d_loss = (real_loss + fake_loss) / 2
                self.log(f"train/frame_{i}_d_loss", d_loss)
                total_loss += d_loss
            d_loss = total_loss / self.forecast_steps  # Average of the per-timestep loss
            tqdm_dict = {"d_loss": d_loss}
            output = OrderedDict({"loss": d_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/d_loss": d_loss})
            return output

    def train_all_timestep(
        self, images: torch.Tensor, future_images: torch.Tensor, optimizer_idx: int, batch_idx: int
    ):
        """
        Train on all timesteps, instead of single timestep at a time. No conditioning on future timestep
        Args:
            images:
            future_images:
            optimizer_idx:
            batch_idx:

        Returns:

        """
        if optimizer_idx == 0:
            # generate images
            generated_images = self(images)
            fake = torch.cat((images, generated_images), 1)
            # log sampled images
            if np.random.random() < 0.01:
                self.visualize_step(
                    images, future_images, generated_images, batch_idx, step="train"
                )

            # adversarial loss is binary cross-entropy
            gan_loss = self.criterionGAN(self.discriminator(fake), True)
            l1_loss = self.criterionL1(generated_images, future_images) * self.lambda_l1
            g_loss = gan_loss + l1_loss
            tqdm_dict = {"g_loss": g_loss}
            output = OrderedDict({"loss": g_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/g_loss": g_loss})
            return output

        # train discriminator
        if optimizer_idx == 1:
            # Measure discriminator's ability to classify real from generated samples

            # how well can it label as real?
            real = torch.cat((images, future_images), 1)
            real_loss = self.criterionGAN(self.discriminator(real), True)

            # how well can it label as fake?
            gen_output = self(images)
            fake = torch.cat((images, gen_output), 1)
            fake_loss = self.criterionGAN(self.discriminator(fake), False)

            # discriminator loss is the average of these
            d_loss = (real_loss + fake_loss) / 2
            tqdm_dict = {"d_loss": d_loss}
            output = OrderedDict({"loss": d_loss, "progress_bar": tqdm_dict, "log": tqdm_dict})
            self.log_dict({"train/d_loss": d_loss})
            return output

    def training_step(self, batch, batch_idx, optimizer_idx):
        images, future_images = batch
        if self.condition_time:
            return self.train_per_timestep(images, future_images, optimizer_idx, batch_idx)
        return self.train_all_timestep(images, future_images, optimizer_idx, batch_idx)

    def val_all_timestep(self, images, future_images, batch_idx):
        # generate images
        generated_images = self(images)
        fake = torch.cat((images, generated_images), 1)
        # log sampled images
        if np.random.random() < 0.01:
            self.visualize_step(images, future_images, generated_images, batch_idx, step="val")
        # adversarial loss is binary cross-entropy
        gan_loss = self.criterionGAN(self.discriminator(fake), True)
        l1_loss = self.criterionL1(generated_images, future_images) * self.lambda_l1
        g_loss = gan_loss + l1_loss
        # how well can it label as real?
        real = torch.cat((images, future_images), 1)
        real_loss = self.criterionGAN(self.discriminator(real), True)

        # how well can it label as fake?
        fake_loss = self.criterionGAN(self.discriminator(fake), True)

        # discriminator loss is the average of these
        d_loss = (real_loss + fake_loss) / 2
        tqdm_dict = {"d_loss": d_loss}
        output = OrderedDict(
            {
                "val/discriminator_loss": d_loss,
                "val/generator_loss": g_loss,
                "progress_bar": tqdm_dict,
                "log": tqdm_dict,
            }
        )
        self.log_dict({"val/d_loss": d_loss, "val/g_loss": g_loss, "val/loss": d_loss + g_loss})
        return output

    def val_per_timestep(self, images, future_images, batch_idx):
        total_g_loss = 0
        total_d_loss = 0
        vis_step = True if np.random.random() < 0.01 else False
        generated_images = self(
            images, forecast_steps=self.forecast_steps
        )  # (Batch, Channel, Width, Height)
        for i in range(self.forecast_steps):
            # x = self.ct.forward(images, i)  # Condition on future timestep
            fake = generated_images[:, :, i, :, :]  # Only take the one at the end
            if vis_step:
                self.visualize_step(
                    images, future_images[:, i, :, :], fake, batch_idx, step=f"val_frame_{i}"
                )
            # adversarial loss is binary cross-entropy
            gan_loss = self.criterionGAN(self.discriminator(fake), True)
            # Only L1 loss on the given timestep
            l1_loss = self.criterionL1(fake, future_images[:, i, :, :]) * self.lambda_l1
            real_loss = self.criterionGAN(self.discriminator(future_images[:, i, :, :]), True)
            # adversarial loss is binary cross-entropy
            fake_loss = self.criterionGAN(self.discriminator(fake), False)
            # Only L1 loss on the given timestep
            # discriminator loss is the average of these
            d_loss = (real_loss + fake_loss) / 2
            self.log(f"val/frame_{i}_d_loss", d_loss)
            total_d_loss += d_loss
            self.log(f"val/frame_{i}_l1_loss", l1_loss)
            g_loss = gan_loss + l1_loss
            total_g_loss += g_loss
        g_loss = total_g_loss / self.forecast_steps
        d_loss = total_d_loss / self.forecast_steps
        loss = g_loss + d_loss
        tqdm_dict = {"loss": loss}
        output = OrderedDict(
            {
                "val/discriminator_loss": d_loss,
                "val/generator_loss": g_loss,
                "progress_bar": tqdm_dict,
                "log": tqdm_dict,
            }
        )
        self.log_dict({"val/d_loss": d_loss, "val/g_loss": g_loss, "val/loss": d_loss + g_loss})
        return output

    def validation_step(self, batch, batch_idx):
        images, future_images = batch
        if self.condition_time:
            return self.val_per_timestep(images, future_images, batch_idx)
        return self.val_all_timestep(images, future_images, batch_idx)

    def forward(self, x, **kwargs):
        return self.generator.forward(x, **kwargs)

    def configure_optimizers(self):
        lr = self.lr
        b1 = self.b1
        b2 = self.b2

        opt_g = torch.optim.Adam(self.generator.parameters(), lr=lr, betas=(b1, b2))
        opt_d = torch.optim.Adam(self.discriminator.parameters(), lr=lr, betas=(b1, b2))
        if self.lr_method == "plateau":
            g_scheduler = lr_scheduler.ReduceLROnPlateau(
                opt_g, mode="min", factor=0.2, threshold=0.01, patience=10
            )
            d_scheduler = lr_scheduler.ReduceLROnPlateau(
                opt_d, mode="min", factor=0.2, threshold=0.01, patience=10
            )
        elif self.lr_method == "cosine":
            g_scheduler = lr_scheduler.CosineAnnealingLR(opt_g, T_max=self.lr_epochs, eta_min=0)
            d_scheduler = lr_scheduler.CosineAnnealingLR(opt_d, T_max=self.lr_epochs, eta_min=0)
        elif self.lr_method == "warmup":
            g_scheduler = LinearWarmupCosineAnnealingLR(
                opt_g, warmup_epochs=self.lr_epochs, max_epochs=100
            )
            d_scheduler = LinearWarmupCosineAnnealingLR(
                opt_d, warmup_epochs=self.lr_epochs, max_epochs=100
            )
        else:
            return NotImplementedError("learning rate policy is not implemented")

        return [opt_g, opt_d], [g_scheduler, d_scheduler]

    def visualize_step(
        self, x: torch.Tensor, y: torch.Tensor, y_hat: torch.Tensor, batch_idx: int, step: str
    ):
        # the logger you used (in this case tensorboard)
        tensorboard = self.logger.experiment[0]
        # Image input is either (B, C, H, W) or (B, T, C, H, W)
        if len(x.shape) == 5:
            # Timesteps per channel
            images = x[0].cpu().detach()
            for i, t in enumerate(images):  # Now would be (C, H, W)
                t = [torch.unsqueeze(img, dim=0) for img in t]
                image_grid = torchvision.utils.make_grid(t, nrow=self.channels_per_timestep)
                tensorboard.add_image(
                    f"{step}/Input_Image_Stack_Frame_{i}", image_grid, global_step=batch_idx
                )
        else:
            images = x[0].cpu().detach()
            images = [torch.unsqueeze(img, dim=0) for img in images]
            image_grid = torchvision.utils.make_grid(images, nrow=self.channels_per_timestep)
            tensorboard.add_image(f"{step}/Input_Image_Stack", image_grid, global_step=batch_idx)
        # In all cases, the output target and image are in (B, C, H, W) format
        images = y[0].cpu().detach()
        images = [torch.unsqueeze(img, dim=0) for img in images]
        image_grid = torchvision.utils.make_grid(images, nrow=12)
        tensorboard.add_image(f"{step}/Target_Image_Stack", image_grid, global_step=batch_idx)
        images = y_hat[0].cpu().detach()
        images = [torch.unsqueeze(img, dim=0) for img in images]
        image_grid = torchvision.utils.make_grid(images, nrow=12)
        tensorboard.add_image(f"{step}/Generated_Image_Stack", image_grid, global_step=batch_idx)


import numpy
import scipy.io
import math
import csv
import sys

maxDepth = 124

class Node:
	def __init__(self, split_rule, left = None, right = None):
		self.split_rule = split_rule
		self.left = left
		self.right = right

	def getRule(self):
		return self.split_rule

	def getLeft(self):
		return self.left

	def getRight(self):
		return self.right

	def isLeaf(self):
		return False

class LeafNode(Node):
	def __init__(self, label):
		self.split_rule = None
		self.left = None
		self.right = None
		self.label = label

	def getLabel(self):
		return self.label

	def isLeaf(self):
		return True

def allSpam(data, labels):
	for i in range(len(data)):
		if labels[i] != 1:
			return False
	return True

def allHam(data, labels):
	for i in range(len(data)):
		if labels[i] != 0:
			return False
	return True

def findMajority(data, labels):
	spam_count = 0
	ham_count = 0
	for i in range(len(data)):
		if labels[i] == 1:
			spam_count += 1
		else:
			ham_count += 1
	if spam_count >= ham_count:
		return 1
	else:
		return 0

def impurity(left, right):

	left_total = left["spam"] + left["ham"]
	if left_total == 0:
		left_pct = 0
	else:
		left_pct = left["spam"]/float(left_total)
	right_total = right["spam"] + right["ham"]
	if right_total == 0:
		right_pct = 0
	else:
		right_pct = right["spam"]/float(right_total)

	if left_pct == 1.0:
		left_impurity = 0
	elif left_pct == 0.0:
		left_impurity = 1
	else:
		left_impurity = 0.0
		left_impurity += left_pct*math.log(left_pct, 2)
		left_impurity += (1 - left_pct)*math.log((1 - left_pct), 2)
		left_impurity = -left_impurity
	if right_pct == 1.0:
		right_impurity = 0
	elif right_pct == 0.0:
		right_impurity = 1
	else:
		right_impurity = 0.0
		right_impurity += right_pct*math.log(right_pct, 2)
		right_impurity += (1 - right_pct)*math.log((1 - right_pct), 2)
		right_impurity = -right_impurity

	return left_impurity + right_impurity

def segmentor(data_points, data_labels, impurity):
	best_feature = None
	best_threshold = 0
	best_impurity = float("inf")

	for i in range(len(data_points[0])):
		spam_count = 0
		ham_count = 0
		spam_total = 0
		ham_total = 0
		for j in range(len(data_points)):
			if data_labels[j] == 1:
				spam_count += 1
				spam_total += data_points[j][i]
			else:
				ham_count += 1
				ham_total += data_points[j][i]
		spam_average = spam_total/float(spam_count)
		ham_average = ham_total/float(ham_count)
		average = (spam_average + ham_average)/2
		left = {"spam" : 0, "ham" : 0}
		right = {"spam" : 0, "ham" : 0}
		for j in range(len(data_points)):
			if data_points[j][i] >= average:
				if data_labels[j] == 1:
					left["spam"] += 1
				else:
					left["ham"] += 1
			else:
				if data_labels[j] == 1:
					right["spam"] += 1
				else:
					right["ham"] += 1
		current_impurity = impurity(left, right)
		if current_impurity < best_impurity:
			best_feature = i
			best_threshold = average
			best_impurity = current_impurity

	return (best_feature, best_threshold)

class DecisionTree:

	def __init__(self, depth, impurity, segmentor):
		self.depth = depth
		self.impurity = impurity
		self.segmentor = segmentor
		self.root = None

		self.inOrder = []
		self.postOrder = []

	def train(self, data, labels):
		self.root = self.train_helper(data, labels, self.depth)

	def train_helper(self, data, labels, depth):
		if allSpam(data, labels):
			return LeafNode(1)
		elif allHam(data, labels):
			return LeafNode(0)
		elif depth == 0:
			majority = findMajority(data, labels)
			if majority == 1:
				return LeafNode(1)
			else:
				return LeafNode(0)
		split_decision = self.segmentor(data, labels, self.impurity)
		feature = split_decision[0]
		threshold = split_decision[1]

		set0 = []
		set0_labels = []
		set1 = []
		set1_labels = []
		for i in range(len(data)):
			if data[i][feature] >= threshold:
				set0.append(data[i])
				set0_labels.append(labels[i])
			else:
				set1.append(data[i])
				set1_labels.append(labels[i])

		return Node(split_decision, self.train_helper(set0, set0_labels, depth - 1), self.train_helper(set1, set1_labels, depth - 1))

	def predict(self, data):
		predictions = []
		for point in data:
			prediction= self.predict_helper(self.root, point)
			predictions.append(prediction)
		return predictions

	def predict_helper(self, current_node, data):
		if current_node.isLeaf():
			return current_node.getLabel()

		splitRule = current_node.getRule()

		feature = splitRule[0]
		threshold = splitRule[1]

		if data[feature] == 1:
			return self.predict_helper(current_node.getLeft(), data)
		else:
			return self.predict_helper(current_node.getRight(), data)

def main():
	mat = scipy.io.loadmat(sys.argv[1])
	trainingData = mat['trainingData']
	trainingLabels = mat['trainingLabels'][0]

	classifier = DecisionTree(maxDepth, impurity, segmentor)
	classifier.train(trainingData, trainingLabels)

	testData = scipy.io.loadmat(sys.argv[2])
	testData = testData['testData']
	predictions = classifier.predict(testData)

	f = open('DecisionTreePredictions.csv', 'wb')
	writer = csv.writer(f)
	writer.writerow(['Team Comp Number', 'Result'])
	for i in range(len(predictions)):
		if predictions[i] == 1:
			writer.writerow([i+1, "Win"])
		else:
			writer.writerow([i+1, "Loss"])

	f.close()

if __name__ == "__main__":
	main()


class Node:
    def __init__(self, value=None):
        self.value = value
        self.next = None

class Queue:
    def __init__(self):
        self.front = None
        self.rear = None

    def enqueue(self, value):
        """ Add an item to the rear fo the queue """
        node = Node(value)

        if not self.front:

            self.front = node
            self.rear = node
        else:
            self.rear.next = node
            self.rear = self.rear.next

    def dequeue(self):
        """ removed an item to the front fo the queue """
        try:
            temp = self.front
            self.front = self.front.next
            temp.next = None
            return temp.value
        except Exception:
            return "the queue is empty"

    def is_empty(self):
        """ Returns True if Empty and false otherwise """
        if self.front:
            return False
        return True

class KNode:
  def __init__(self, value=None):
    self.value = value
    self.children = []

class K_ary_tree:
    def __init__(self, root=None):
        self.root = root

    def breadth_first(self):
        self.breadth_first_list = []
        queque = Queue()
        queque.enqueue(self.root)
        while not queque.is_empty():
            ele = queque.dequeue()
            self.breadth_first_list.append(ele.value)
            if ele.children :
                for i in ele.children:
                    queque.enqueue(i)
        return self.breadth_first_list

def FizzBuzzTree(tree):
    fizzbuzzlist = []
    queque = Queue()
    queque.enqueue(tree.root)
    while not queque.is_empty():
        item = queque.dequeue()
        try:
            if item.value % 15 == 0:
                item.value = "FizzBuzz"
            elif item.value % 3 ==0 :
                item.value = 'Fizz'
            elif item.value % 5 ==0:
                item.value = 'Buzz'
            else :
                item.value = str(item.value)
            fizzbuzzlist.append(item.value)
            if len(item.children) > 0 :
                for i in item.children:
                    queque.enqueue(i)
        except TypeError:
            return 'the value should be an integer'
    return fizzbuzzlist

if __name__ == '__main__':
    node = KNode(10)
    node.children.append(KNode(5))
    node.children.append(KNode(2))
    node.children.append(KNode(3))
    node.children[0].children.append(KNode(7))
    node.children[1].children.append(KNode(2))
    node.children[1].children.append(KNode(4))
    node.children[2].children.append(KNode(30))
    node.children[2].children.append(KNode(6))
    node.children[2].children.append(KNode(1))

    k_tree = K_ary_tree(node)
    print(k_tree.breadth_first())
    print(FizzBuzzTree(k_tree))


"""Tests for google3.third_party.java_src.tsunami.plugin_server.py.plugin.payload.payload_secret_generator."""

import base64

from testing.pybase import googletest
from tsunami.plugin_server.py.plugin.payload.payload_secret_generator import PayloadSecretGenerator

_SECRET_BYTE_SIZE = 8

class PayloadSecretGeneratorTest(googletest.TestCase):

  def test_generate_returns_secret_with_specified_size(self):
    secret = PayloadSecretGenerator().generate(_SECRET_BYTE_SIZE)
    self.assertLen(base64.b16decode(secret), _SECRET_BYTE_SIZE)

if __name__ == "__main__":
  googletest.main()


import unittest

from .solution import Node, solution

class NodeTestCase(unittest.TestCase):
    def test_initialize(self):
        test_value = 5
        test_node = Node(test_value)
        self.assertEqual(test_node.value, test_value)

    def test_left_right_assert(self):
        base_node = Node(5)
        small_node = Node(3)
        big_node = Node(7)
        base_node.set_left(big_node)
        base_node.set_right(small_node)
        self.assertEqual(base_node.get_left(), big_node)
        self.assertEqual(base_node.get_right(), small_node)

class P135TestCase(unittest.TestCase):
    def test_case1(self):
        root_node = Node(10)
        min_path_sum = solution(root_node)
        self.assertEqual(min_path_sum, 10)

    def test_case2(self):
        root_node = Node(10)
        root_node.set_left(Node(5))
        root_node.set_right(Node(5))
        min_path_sum = solution(root_node)
        self.assertEqual(min_path_sum, 15)

    def test_case3(self):
        root_node = Node(10)
        root_node.set_left(Node(5))
        root_node.get_left().set_right(Node(2))
        root_node.set_right(Node(5))
        min_path_sum = solution(root_node)
        self.assertEqual(min_path_sum, 15)

    def test_case4(self):
        root_node = Node(10)
        root_node.set_left(Node(5))
        root_node.get_left().set_right(Node(2))
        root_node.set_right(Node(5))
        root_node.get_right().set_right(Node(1))
        min_path_sum = solution(root_node)
        self.assertEqual(min_path_sum, 16)

    def test_case5(self):
        root_node = Node(10)
        root_node.set_left(Node(5))
        root_node.get_left().set_right(Node(2))
        root_node.set_right(Node(5))
        root_node.get_right().set_right(Node(1))
        root_node.get_right().get_right().set_left(Node(-1))
        min_path_sum = solution(root_node)
        self.assertEqual(min_path_sum, 15)