import configuracion
import vdb_generado
import utiles
import criticidades
import logging
import requests
import re
import time
from threading import Thread

class Iterador_Nessus_Vdb(Thread):

    conf = configuracion.Configuracion()

    componente = None
    columnas_fijas = None
    environmental_impact_subscore_modifiers = None
    ruta_nessus = None

    progressbar = None
    textbox = None

    vulnerabilidades_rejected = []

    def __init__(self, componente, columnas_fijas, environmental_impact_subscore_modifiers, ruta_nessus, progressbar, textbox):
        super().__init__()
        self.componente = componente
        self.columnas_fijas = columnas_fijas
        self.environmental_impact_subscore_modifiers = environmental_impact_subscore_modifiers
        self.ruta_nessus = ruta_nessus
        self.progressbar = progressbar
        self.textbox = textbox

    def run(self):
        logging.log(self.conf.vdbauto_nivel_log, "######## COMIENZA ITERACION DE "+utiles.get_nombre_fichero(self.conf.ruta_nessus)+" ########")
        self.insertar_texto_textbox("####### COMIENZA ITERACION DE "+utiles.get_nombre_fichero(self.conf.ruta_nessus)+" #######\n")

        self.iterar_nessus_csv(self.componente, self.columnas_fijas, self.environmental_impact_subscore_modifiers, self.ruta_nessus)

    def insertar_texto_textbox(self, texto):
        self.textbox.insert("end", texto)
        self.textbox.see("end")

    def iterar_nessus_csv(self, componente, columnas_fijas, environmental_impact_subscore_modifiers, ruta_nessus):

        vulnerabilidades_vdb = self.extraer_vulnerabilidades_vdb(self.conf.vdb_hoja, componente)
        vulnerabilidades_duplicadas = [] #almacena las vulnerabilidades que no se han insertado porque estaban en el vdb introducido
        vulnerabilidades_error = [] #almacena los cves que no se han añadido por un error en la peticion API

        lista_cabeceras_nessus_conf = list(self.conf.configuracion["vdb_generado"]["nessus"])
        cabeceras_nessus_csv = next(self.conf.nessus_reader) #sacamos las cabeceras del fichero csv Nessus para luego poder coger las columnas que se vayan a insertar en el metodo "anadir_columnas_nessus_fila"

        vdb_final = vdb_generado.Vdb_generado()

        plugin_id_anterior = "" #para evitar duplicidades ya que todos los cves asociados a un mismo plugin id tienen una entrada cada uno. (La aplicacion coge el plugin id y saca los CVEs que hay que meter, por tanto, las entradas que contienen esos CVEs en el excel no nos importan porque meten tambien las de Reference Information)

        numero_filas_total = sum(1 for row in self.conf.nessus_reader)

        self.conf.fichero_nessus.seek(0)
        next(self.conf.nessus_reader) #quitamos la cabecera

        indice_fila_plugin_id = utiles.get_indice_columna_nessus(cabeceras_nessus_csv, lista_cabeceras_nessus_conf[0])
        indice_fila_risk = utiles.get_indice_columna_nessus(cabeceras_nessus_csv, lista_cabeceras_nessus_conf[2])

        numero_fila_actual = 1
        for fila in self.conf.nessus_reader:
            vul_id = fila[indice_fila_plugin_id] #guardamos el plugin ID

            if fila[indice_fila_risk] != "None": #si tiene None en la columna de "Risk" es porque es INFO, y, por tanto, pasamos de largo
                if (vul_id != plugin_id_anterior): #para evitar la duplicidad
                    plugin_id_anterior = vul_id

                    fila_insertar = [""] * 26

                    multiples_cves = [] #por si tiene varios CVEs asociados un plugin ID

                    indice_columna_cve = utiles.get_indice_columna_nessus(cabeceras_nessus_csv, lista_cabeceras_nessus_conf[1])
                    if fila[indice_columna_cve] != "": #si tiene CVE, entonces tenemos que hacer una petición http para extraer los CVEs asociados a ese plugin ID, ya que si usamos los del csv nos metería también los CVEs de Reference Information, los cuales no metemos

                        conexion_tenable = True
                        contador_reintentos = 0
                        while(conexion_tenable):
                            try:
                                multiples_cves = self.extraer_cves_de_plugin_id(vul_id)
                                self.progressbar.configure(progress_color="#2CC985")
                                conexion_tenable = False
                                contador_reintentos = 0

                                for cve in multiples_cves:
                                    if cve in vulnerabilidades_vdb:
                                        self.insertar_texto_textbox("VULNERABILIDAD QUE CONTINÚA "+cve+"\n")
                                        logging.warning("vulnerabilidad que continúa: "+cve)
                                        vulnerabilidades_duplicadas.append(cve)
                                    else:
                                        self.anadir_columnas_fijas_fila(fila_insertar, columnas_fijas)
                                        self.anadir_columnas_nessus_fila(fila_insertar, cve, fila, cabeceras_nessus_csv)

                                        conexion_nist = True
                                        contador_reintentos = 0
                                        while (conexion_nist):
                                            try:
                                                time.sleep(2)

                                                respuesta = criticidades.peticion_cve_api_nist(cve, self.conf.configuracion["nist_api_key"])
                                                self.progressbar.configure(progress_color="#2CC985")
                                                cvss2_base_score, cvss3_base_score = criticidades.get_cvss2_cvss3_base_score_api_nist(respuesta) #devuelve una lista con cvss2.0 y cvss3.0 base scores
                                                version_cvss3 = criticidades.get_version_cvss3_api_nist(respuesta) #contiene 3.0, 3.1 o N/A
                                                vector_cvss3 = criticidades.get_vector_ataque_cvss3_api_nist(respuesta)
                                                fecha_publicacion_cve = criticidades.get_fecha_publicacion_cve_api_nist(respuesta)
                                                all_scores = self.get_criticidades_cve(cve, cvss2_base_score, cvss3_base_score, version_cvss3, vector_cvss3, environmental_impact_subscore_modifiers) #cvss2.0, cvss3.0 base, cvss3.1 base, cvss3.0 enviromental, cvss3.1 enviromental, fecha publicacion
                                                all_scores.append(utiles.formatear_fecha(fecha_publicacion_cve)) #añadimos la fecha de publicacion
                                                self.anadir_criticidades_fila(fila_insertar, all_scores)
                                                vdb_final.insertar_fila_vdb_generado(fila_insertar)

                                                conexion_nist = False
                                                contador_reintentos = 0
                                                logging.info("insertado el cve: "+cve+" que viene del plugin id: "+vul_id)
                                                self.insertar_texto_textbox("INSERTADO "+cve+"\n")
                                            except requests.exceptions.ConnectionError:
                                                if contador_reintentos == 0:
                                                    logging.critical("El ordenador ha sido desconectado de internet durante la ejecucion de: "+cve+" asociado al plugin id: "+vul_id)
                                                    self.progressbar.configure(progress_color="red")
                                                    self.insertar_texto_textbox("CONEXIÓN A INTERNET PÉRDIDA\n")
                                                contador_reintentos += 1
                                                self.insertar_texto_textbox("REINTENTO DE CONEXIÓN "+str(contador_reintentos)+"\n")
                                                time.sleep(5)
                                            except Exception:
                                                logging.error("en la peticion API para la vulnerabilidad: "+cve+" que viene del plugin id: "+vul_id)
                                                self.insertar_texto_textbox("ERROR PETICION API PARA "+cve+"\n")
                                                vulnerabilidades_error.append(cve)
                                                conexion_nist = False

                            except requests.exceptions.ConnectionError:
                                if contador_reintentos == 0:
                                    logging.critical("El ordenador ha sido desconectado de internet durante la ejecucion del plugin id: "+vul_id+" el cual contiene CVEs asociados")
                                    self.progressbar.configure(progress_color="red")
                                    self.insertar_texto_textbox("CONEXIÓN A INTERNET PÉRDIDA\n")
                                contador_reintentos += 1
                                self.insertar_texto_textbox("REINTENTO DE CONEXIÓN "+str(contador_reintentos)+"\n")
                                time.sleep(5)
                            except Exception:
                                logging.error("en la peticion HTTP para extraer los CVEs del plugin id: "+vul_id)
                                self.insertar_texto_textbox("ERROR PETICIÓN HTTP PARA PLUGIN ID "+vul_id+"\n")
                                vulnerabilidades_error.append(cve)
                                conexion_tenable = False

                    else: #Si no hay CVE, entonces se pone solo el plugin id
                        if vul_id in vulnerabilidades_vdb:
                                logging.info("vulnerabilidad que continúa: "+vul_id)
                                self.insertar_texto_textbox("VULNERABILIDAD QUE CONTINÚA PLUGIN ID"+vul_id+"\n")
                                vulnerabilidades_duplicadas.append(vul_id)
                        else:
                            self.anadir_columnas_fijas_fila(fila_insertar, columnas_fijas)
                            self.anadir_columnas_nessus_fila(fila_insertar, vul_id, fila, cabeceras_nessus_csv)

                            claves = list(self.conf.configuracion["vdb_generado"]["nessus"].keys())

                            conexion_tenable = True
                            contador_reintentos = 0
                            while conexion_tenable:
                                try:
                                    time.sleep(2)

                                    all_scores = self.get_criticidades_plugin_id(fila, cabeceras_nessus_csv, claves, vul_id, environmental_impact_subscore_modifiers) #cvss2.0, cvss3.0 base, cvss3.1 base, cvss3.0 enviromental, cvss3.1 enviromental
                                    self.progressbar.configure(progress_color="#2CC985")

                                    all_scores.append("") #el metodo anadir_criticalidades_fila añade tambien la fecha de publicacion. Añadimos esto para que no de error. Va a sobreescribir la fecha con "" pero da igual porque justo debajo vamos a añadir la fecha formateada sobreescribiendo este ""
                                    self.anadir_criticidades_fila(fila_insertar, all_scores)

                                    fila_insertar[utiles.get_indice_a_partir_columna(self.conf.configuracion["vdb_generado"]["nessus"][claves[5]])] = utiles.voltear_fecha(fila[utiles.get_indice_columna_nessus(cabeceras_nessus_csv, claves[5])]) #añadimos la fecha del plugin formateada

                                    vdb_final.insertar_fila_vdb_generado(fila_insertar)

                                    conexion_tenable = False
                                    contador_reintentos = 0
                                    logging.info("insertado el plugin id: "+vul_id)
                                    self.insertar_texto_textbox("INSERTADO PLUGIN ID "+vul_id+"\n")

                                except requests.exceptions.ConnectionError:
                                    if contador_reintentos == 0:
                                        logging.critical("El ordenador ha sido desconectado de internet durante la ejecucion del plugin id: "+vul_id)
                                        self.progressbar.configure(progress_color="red")
                                        self.insertar_texto_textbox("CONEXIÓN A INTERNET PÉRDIDA\n")
                                    contador_reintentos += 1
                                    self.insertar_texto_textbox("REINTENTO DE CONEXIÓN "+str(contador_reintentos)+"\n")
                                    time.sleep(5)
                                except Exception:
                                    logging.error("en la peticion HTTP para extraer el vector cvss3.X del plugin id: "+vul_id)
                                    self.insertar_texto_textbox("ERROR PETICION HTTP VECTOR CVSS3.X PLUGIN ID "+vul_id+"\n")
                                    vulnerabilidades_error.append(vul_id)
                                    conexion_tenable = False
            else:
                logging.debug("El plugin id: "+vul_id+" es una entrada de INFO, por tanto, no se inserta")
                self.insertar_texto_textbox("INFO: "+vul_id+"\n")
                self.textbox.see("end")

            self.progressbar.set(numero_fila_actual/(numero_filas_total+2))
            numero_fila_actual += 1

        self.insertar_duplicados_y_errores(vdb_final, vulnerabilidades_error, self.vulnerabilidades_rejected, vulnerabilidades_duplicadas, vulnerabilidades_vdb)

        self.progressbar.set(numero_fila_actual/(numero_filas_total))
        logging.log(self.conf.vdbauto_nivel_log, "######## FINALIZADA ITERACION DE "+utiles.get_nombre_fichero(ruta_nessus)+" ########")
        self.insertar_texto_textbox("###### FINALIZADA ITERACION DE "+utiles.get_nombre_fichero(ruta_nessus)+" ######")

    def insertar_duplicados_y_errores(self, vdb_final, vulnerabilidades_error, vulnerabilidades_rejected, vulnerabilidades_duplicadas, vulnerabilidades_antiguas):
        for i in range(2, len(vulnerabilidades_error)+2):
            vdb_final.insertar_celda(vdb_final.hoja2, i, 1, vulnerabilidades_error[i-2])

        for i in range(2, len(vulnerabilidades_rejected)+2):
            vdb_final.insertar_celda(vdb_final.hoja2, i, 3, vulnerabilidades_rejected[i-2])

        vulnerabilidades_pueden_cerrar = [item for item in vulnerabilidades_antiguas if item not in vulnerabilidades_duplicadas]
        for i in range(2, len(vulnerabilidades_pueden_cerrar)+2):
            vdb_final.insertar_celda(vdb_final.hoja2, i, 5, vulnerabilidades_pueden_cerrar[i-2])

        for i in range(2, len(vulnerabilidades_duplicadas)+2):
            vdb_final.insertar_celda(vdb_final.hoja2, i, 7, vulnerabilidades_duplicadas[i-2])

        vdb_final.workbook.save(vdb_final.ruta)

    def get_criticidades_cve(self, cve, cvss2_base_score, cvss3_base_score, version_cvss3, vector_cvss3, environmental_impact_subscore_modifiers):
        """Devuelve una lista con cvss2.0, cvss3.0 base, cvss3.1 base, cvss3.0 enviromental y cvss3.1 enviromental"""
        all_criticidades = []

        if version_cvss3 == "N/A": #solo hay cvss2.0
            if cvss2_base_score == "N/A": #ES UN CVE REJECTED (no tiene ni cvss3.x ni cvss2.0)
                all_criticidades.append("N/A")
                logging.warning(cve+" es un posible CVE rejected")
                self.insertar_texto_textbox("POSIBLE CVE REJECTED "+cve+"\n")
                self.vulnerabilidades_rejected.append(cve)
            else: all_criticidades.append(str(float(cvss2_base_score))) #lo pasamos a float para convertir lo 5 en 5.0 y luego a string porque sino si tienes el excel en español al insertarlo te convierte los . en , porque detecta que son numeros. Al ser un String no cambia nada
            for i in range(4):
                all_criticidades.append("N/A")

        elif version_cvss3 == "3.0":
            if cvss2_base_score == "N/A": all_criticidades.append("N/A") #Puede que tenga cvss3.x pero no tenga cvss2.0
            else: all_criticidades.append(str(float(cvss2_base_score))) #queremos que queden con 5.2. Reemplazamos las comas por puntos y convertimos a float para que pase los 5 a 5.0
            all_criticidades.append(str(float(cvss3_base_score))) #cvss3.0
            all_criticidades.append("N/A") #ponemos N/A al cvss3.1 base
            all_criticidades.append(str(float(criticidades.get_recalculo_cvssv3(vector_cvss3, environmental_impact_subscore_modifiers[0], environmental_impact_subscore_modifiers[1], environmental_impact_subscore_modifiers[2]))))
            all_criticidades.append("N/A") #ponemos N/A al cvss3.1 enviromental

        else: #es cvss3.1
            if cvss2_base_score == "N/A": all_criticidades.append("N/A") #Puede que tenga cvss3.x pero no tenga cvss2.0
            else: all_criticidades.append(str(float(cvss2_base_score)))
            all_criticidades.append("N/A") #ponemos N/A al cvss3.0 base
            all_criticidades.append(str(float(cvss3_base_score)))
            all_criticidades.append("N/A") #ponemos N/A al cvss3.0 enviromental
            all_criticidades.append(str(float(criticidades.get_recalculo_cvssv3(vector_cvss3, environmental_impact_subscore_modifiers[0], environmental_impact_subscore_modifiers[1], environmental_impact_subscore_modifiers[2]))))

        return all_criticidades

    def get_criticidades_plugin_id(self, fila, cabeceras_nessus_csv, claves, vul_id, environmental_impact_subscore_modifiers):
        """Devuelve una lista con cvss2.0, cvss3.0 base, cvss3.1 base, cvss3.0 enviromental y cvss3.1 enviromental"""
        all_scores = [] #cvss2.0, cvss3.0 base, cvss3.1 base, cvss3.0 enviromental, cvss3.1 enviromental, fecha_publicacion

        cvss2 = fila[utiles.get_indice_columna_nessus(cabeceras_nessus_csv, claves[3])]
        if (cvss2 == ""): all_scores.append("N/A") #Puede pasar que tenga un Risk Factor de High pero no tenga ni criticidad 2.0 ni 3.0.
        else: all_scores.append(str(float(cvss2))) #añadimos el CVSS2.0 basescore

        cvss3 = fila[utiles.get_indice_columna_nessus(cabeceras_nessus_csv, claves[4])] #CVSS3.0 basescore.

        if (cvss3 == ""): #no tiene cvss3.x
                all_scores.append("N/A") #3.0 base
                all_scores.append("N/A") #3.1 base
                all_scores.append("N/A") #3.0 env
                all_scores.append("N/A") #3.1 env
        else:
            vector_cvss3 = self.extraer_vector_cvss3_plugin_id(vul_id)
            cvss3_recalculo = cvss3_recalculo = criticidades.get_recalculo_cvssv3(vector_cvss3, environmental_impact_subscore_modifiers[0], environmental_impact_subscore_modifiers[1], environmental_impact_subscore_modifiers[2])

            if vector_cvss3[:8] == "CVSS:3.0":
                all_scores.append(str(float(cvss3))) #3.0 base
                all_scores.append("N/A") #3.1 base
                all_scores.append(str(float(cvss3_recalculo))) #3.0 env
                all_scores.append("N/A") #3.1 env
            elif vector_cvss3[:8] == "CVSS:3.1":
                all_scores.append("N/A") #3.0 base
                all_scores.append(str(float(cvss3))) #3.1 base
                all_scores.append("N/A") #3.0 env
                all_scores.append(str(float(cvss3_recalculo))) #3.1 env
            else:
                logging.error("Error en la iteracion del plugin: "+vul_id)

        return all_scores

    def extraer_vulnerabilidades_vdb(self, vdb_hoja, componente):
        """Devuelve las vulnerabilidades abiertas de un componente dado, iterando un VDB"""
        vulnerabilidades_vdb_componente = []

        indice_columna_componentes = utiles.get_indice_columna_vdb(vdb_hoja, "columna_componente")
        indice_columna_id_vulnerabilidad = utiles.get_indice_columna_vdb(vdb_hoja, "columna_vulnerabilidad")
        indice_columna_status = utiles.get_indice_columna_vdb(vdb_hoja, "estado")

        for fila in range(3, vdb_hoja.max_row+1):
            if vdb_hoja.cell(row=fila, column=indice_columna_componentes).value == componente and vdb_hoja.cell(row=fila, column=indice_columna_status).value == "Open":
                vulnerabilidades_vdb_componente.append(vdb_hoja.cell(row=fila, column=indice_columna_id_vulnerabilidad).value)

        return vulnerabilidades_vdb_componente

    def extraer_cves_de_plugin_id(self, plugin_id):
        """Extrae los CVEs asociados a un plugin id en la parte de CVSS2.0 y CVSS3.0"""
        url = 'https://www.tenable.com/plugins/nessus/'+plugin_id
        r = requests.get(url)

        htmlRespuesta = r.content.decode("utf-8")

        indiceInicial = htmlRespuesta.index("Risk Information")
        indiceFinal = htmlRespuesta.index("Vulnerability Information")

        htmlFormateado = htmlRespuesta[indiceInicial:indiceFinal]

        cveRegex = re.compile(r'CVE-\d*-\d*<')
        cvesBuenos = cveRegex.findall(htmlFormateado)

        for i in range(len(cvesBuenos)): #Elimina el < en los strings con cves extraidos del html
            cvesBuenos[i] = cvesBuenos[i][:len(cvesBuenos[i])-1]

        return cvesBuenos

    def extraer_vector_cvss3_plugin_id(self, plugin_id):
        """Extrae el vector de ataque cvss3 de un plugin id"""
        url = 'https://www.tenable.com/plugins/nessus/'+plugin_id
        r = requests.get(url)

        htmlRespuesta = r.content.decode("utf-8")

        indiceInicial = htmlRespuesta.index("CVSS v3")
        indiceFinal = htmlRespuesta.index("Vulnerability Information")

        htmlFormateado = htmlRespuesta[indiceInicial:indiceFinal]

        indice_inicio_vector = htmlFormateado.rfind("CVSS:3.")
        vector = htmlFormateado[indice_inicio_vector:]
        indice_fin_vector = vector.find("<")
        vector = vector[:indice_fin_vector]

        return vector

    def anadir_columnas_fijas_fila(self, fila_insertar, columnas_fijas):
        """Añade las columnas fijas a la fila que se va a insertar en el VBD generado"""
        for clave, valor in columnas_fijas.items():
            fila_insertar[utiles.get_indice_a_partir_columna(clave)] = valor

    def anadir_columnas_nessus_fila(self, fila_insertar, id_vulnerabilidad, fila_nessus, cabeceras_nessus_csv):
        """Añade las columnas del reporte de Nessus a la fila que se va a insertar en el VBD generado (excepto el CVSS2.0 basescore ya que para los CVE se hace con la petición a la API y para los plugin en "iterar_nessus_csv"). Recibe la fila y el Plugin ID/CVE"""
        i=0
        for clave, valor in self.conf.configuracion["vdb_generado"]["nessus"].items():
            if valor != "n/a":
                if i==1: #se mete el plugin/cve
                    fila_insertar[utiles.get_indice_a_partir_columna(valor)] = id_vulnerabilidad
                else:
                    fila_insertar[utiles.get_indice_a_partir_columna(valor)] = fila_nessus[utiles.get_indice_columna_nessus(cabeceras_nessus_csv, clave)]
            i+=1

    def anadir_criticidades_fila(self, fila_insertar, cvs):
        """Recibe la fila donde se insertan las criticidades y una lista con las mismas en el siguiente orden: cvss2.0, cvss3.0 base, cvss3.1 base, cvss3.0 enviromental, cvss3.1 enviromental y fecha de publicacion"""
        i = 0
        for clave, valor in self.conf.configuracion["vdb_generado"]["columnas_criticidades"].items():
            fila_insertar[utiles.get_indice_a_partir_columna(valor)] = cvs[i]
            i+=1


#%%
import numpy as np
import math
from scipy.signal import convolve2d
from numpy.core.fromnumeric import transpose

#%%
def to255(X):
    return (((X-X.min())/(X.max()-X.min()))*255).astype(np.int16)

def RMSE(x, y):
    """
    RMSE on single spectral
    """
    return np.sqrt(np.mean(np.power(x - y, 2)))

def SAM(x, y):
    """
    SAM all spectral
    x : CWH
    y : CWH
    """
    def sam1(src, dst):
        """
        numpy array : src, dst
        """
        val = np.dot(src, dst)/(np.linalg.norm(src)*np.linalg.norm(dst))
        sam = np.arccos(val)
        return sam

    def sam2(src, dst):
        """
        numpy array : src, dst
        """
        val = np.dot(src, dst)/(np.mean(np.power(src, 2))*np.mean(np.power(dst, 2)))
        sam = np.arccos(val)
        return sam

    Sam = 0
    for i in range(x.shape[1]):
        for j in range(x.shape[2]):
            Sam = Sam + sam1(x[:,i,j], y[:,i,j])

    return Sam/(x.shape[1] * x.shape[2])

def SRE(x, y):
    """
    SRE on single spectral
    """
    sre = 10*np.log10(
        np.sum(np.power(x, 2))/          \
        np.sum(np.power(y-x,2))
    )
    return sre

def UIQA(x, y):
    """
    UIQA on single spectral
    https://github.com/tgandor/urban_oculus/blob/4fb32138641a276e77b61acafaf0de77caa0cf22/metrics/image_quality_index.py
    """
    def universal_image_quality_index_conv(x, y, kernelsize=8):
        """Compute the Universal Image Quality Index (UIQI) of x and y.
        Not normalized with epsilon, and using scipy.signal.convolve2d."""

        N = kernelsize ** 2
        kernel = np.ones((kernelsize, kernelsize))

        x = x.astype(np.float)
        y = y.astype(np.float)

        # sums and auxiliary expressions based on sums
        S_x = convolve2d(x, kernel, mode='valid')
        S_y = convolve2d(y, kernel, mode='valid')
        PS_xy = S_x * S_y
        SSS_xy = S_x*S_x + S_y*S_y

        # sums of squares and product
        S_xx = convolve2d(x*x, kernel, mode='valid')
        S_yy = convolve2d(y*y, kernel, mode='valid')
        S_xy = convolve2d(x*y, kernel, mode='valid')

        Q_s = 4 * PS_xy * (N * S_xy - PS_xy) / (N*(S_xx + S_yy) - SSS_xy) / SSS_xy

        return np.mean(Q_s)

    return universal_image_quality_index_conv(x, y)


import time
import machine
import neopixel
import dht
import uos as os
import network
import ntptime

#
# 0  -  1  -  2
# |     |     |
# 3  -  4  -  5
# |     |     |
# 6  -  7  -  8
# |     |     |
# 9  -  10 -  11
# |     |     |
# 12 -  13 -  14
#

font = {
    "A": [1, 3, 5, 6, 7, 8, 9, 11, 12, 14],
    "B": [0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
    "C": [1, 2, 3, 6, 9, 13, 14],
    "D": [0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 13, 14],
    "E": [0, 1, 2, 3, 6, 7, 8, 9, 12, 13, 14],
    "F": [0, 1, 2, 3, 6, 7, 8, 9, 12],
    "G": [0, 1, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14],
    "H": [0, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14],
    "I": [0, 1, 2, 4, 7, 10, 12, 13, 14],
    "J": [2, 5, 8, 9, 11, 12, 13, 14],
    "K": [0, 2, 3, 4, 6, 9, 10, 12, 14],
    "L": [0, 3, 6, 9, 12, 13, 14],
    "M": [0, 2, 3, 4, 5, 6, 8, 9, 11, 12, 14],
    "N": [0, 1, 2, 3, 5, 6, 8, 9, 10, 11, 12, 13, 14],
    "O": [0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 13, 14],
    "P": [0, 1, 2, 3, 5, 6, 7, 8, 9, 12],
    "Q": [0, 1, 2, 3, 5, 6, 8, 9, 10, 11, 12, 13, 14],
    "R": [0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14],
    "S": [1, 2, 3, 7, 11, 12, 13],
    "T": [0, 1, 2, 4, 7, 10, 13],
    "U": [0, 2, 3, 5, 6, 8, 9, 11, 12, 13, 14],
    "V": [0, 2, 3, 5, 6, 8, 9, 11, 13],
    "W": [0, 2, 3, 5, 6, 8, 9, 10, 11, 12, 14],
    "X": [0, 2, 3, 5, 7, 9, 11, 12, 14],
    "Y": [0, 2, 3, 5, 7, 10, 13],
    "Z": [0, 1, 2, 5, 7, 8, 9, 12, 13, 14],
    "1": [2, 5, 8, 11, 14],
    "2": [0, 1, 2, 5, 6, 7, 8, 9, 12, 13, 14],
    "3": [0, 1, 2, 5, 6, 7, 8, 11, 12, 13, 14],
    "4": [0, 3, 5, 6, 7, 8, 11, 14],
    "5": [0, 1, 2, 3, 6, 7, 8, 11, 12, 13, 14],
    "6": [0, 1, 2, 3, 6, 7, 8, 9, 11, 12, 13, 14],
    "7": [0, 1, 2, 5, 8, 11, 14],
    "8": [0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14],
    "9": [0, 1, 2, 3, 5, 6, 7, 8, 11, 12, 13, 14],
    "0": [0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 13, 14],
    "°": [1, 3, 5, 7],
    ".": [13],
    "%": [0, 5, 7, 9, 14]
}

class LEDUtils:
    def __init__(self, np: neopixel.NeoPixel, fontDir: {}):
        self.np = np
        self.fontDir = fontDir

    def writeStringToMatrix(self, s: str):
        self.np.fill((0, 0, 0))
        for pos, char in enumerate(s):
            seperator = True
            if s == '.':
                seperator = False

            position = self.fontDir.get(char)
            self.writeCharToMatrix(position, pos, seperator)

    def writeCharToMatrix(self, char: [], char_position, seperator: bool):
        # width of matrix : 32, char width : 3 + 1 padding
        # -> max. 8 fonts on matrix
        matrixWidth = 32
        startIndex = char_position * 3

        if seperator:
            # 1 led padding between letters
            startIndex += char_position

        for pos in char:
            if pos < 3:
                self.np[startIndex + pos] = (10, 5, 6)
            elif pos < 6:
                self.np[-startIndex + (matrixWidth * 2 - 1) + (3 - pos)] = (10, 5, 6)
            elif pos < 9:
                self.np[startIndex + (matrixWidth * 2) + (6 - pos) * -1] = (10, 5, 6)
            elif pos < 12:
                self.np[-startIndex + (matrixWidth * 4 - 1) + (9 - pos)] = (10, 5, 6)
            else:
                self.np[startIndex + (matrixWidth * 4) + (12 - pos) * -1] = (10, 5, 6)

    def runningText(self, s, padding):
        self.np.fill((0, 0, 0))
        matrixWidth = 32
        for char_position, letter in enumerate(s):
            char = self.fontDir.get(letter)

            startIndex = char_position * 3 + char_position + padding
            for pos in char:
                if pos < 3:
                    index = startIndex + pos
                    if index >= matrixWidth:
                        index = index - matrixWidth

                elif pos < 6:
                    index = -startIndex + (matrixWidth * 2 - 1) + (3 - pos)
                    if index < matrixWidth:
                        index = (matrixWidth * 2 - 1) - (matrixWidth - 1 - index)

                elif pos < 9:
                    index = startIndex + (matrixWidth * 2) + (6 - pos) * -1
                    if index >= matrixWidth * 3:
                        index = matrixWidth * 2 + (index - matrixWidth * 3)

                elif pos < 12:
                    index = -startIndex + (matrixWidth * 4 - 1) + (9 - pos)
                    if index < matrixWidth * 3:
                        index = (matrixWidth * 4 - 1) - (matrixWidth * 3 - 1 - index)

                else:
                    index = startIndex + (matrixWidth * 4) + (12 - pos) * -1
                    if index >= matrixWidth * 5:
                        index = matrixWidth * 4 + (index - matrixWidth * 5)

                self.np[index] = (10, 10, 10)

class Persistence:
    def sensorDataToBytes(self, temp: float, humidity: float) -> str:
        # Byte-Composition for Sensor-Data:
        # AAAABBBB | BCCCCCCC
        # A -> 4Bit for Temp. comma -> 0-9 / 16
        # B -> 5Bit for Temp. dezimal -> 0-32
        # C -> 7Bit for Humidity -> 0-100 / 128

        tDec = int(str(temp).split('.')[0])
        tComma = int(str(temp).split('.')[1][:1])
        humByte = int(humidity)

        decLSB = tDec & 1
        decRest = (tDec >> 1) & 0b00001111
        humCut = humByte & 0b01111111

        firstByte = (tComma << 4) | decRest
        secondByte = (decLSB << 7) | humCut

        return str(firstByte) + str(secondByte)

    def persistedBytesToValues(self, persistedBytes: str) -> [float, int]:
        firstByte = int(persistedBytes[0])
        secondByte = int(persistedBytes[1])

        tempComma = (firstByte >> 4) / 10
        tempDec = (secondByte >> 7) | ((firstByte & 0b00001111) << 1)
        humidity = secondByte & 0b01111111

        return [tempDec + tempComma, humidity]

    def persistValue(self, temperature: float, humidity: float):
        currDateMEZ = time.localtime(self.getCurrentMEZ())
        print(currDateMEZ)

        dataFile = open('sensorData.txt', 'ab+')

        entryYear = 0
        while entryYear != currDateMEZ[0]:
            yearLine = str(dataFile.readline().decode())
            if yearLine.startswith('year'):
                entryYear = int(yearLine.split(":")[1])
            elif yearLine == '':
                dataFile.write(('year:' + str(currDateMEZ[0]) + '\n').encode())
                entryYear = currDateMEZ[0]
                print('Created Year entry')

        entryMonth = 0
        while entryMonth != currDateMEZ[1]:
            monthLine = str(dataFile.readline().decode())
            if monthLine.startswith('month'):
                entryMonth = int(monthLine.split(":")[1])
            elif monthLine == '':
                dataFile.write(('month:' + str(currDateMEZ[1]) + '\n').encode())
                entryMonth = currDateMEZ[1]
                print('Created Month entry')

        entryDay = 0
        while entryDay != currDateMEZ[2]:
            dayLine = str(dataFile.readline().decode())
            if dayLine.startswith('day'):
                entryDay = int(dayLine.split(":")[1])
            elif dayLine == '':
                dataFile.write(('day:' + str(currDateMEZ[2]) + '\n').encode())
                entryDay = currDateMEZ[2]
                print('Created Day entry')

        entryHour = 0
        while entryHour != currDateMEZ[3]:
            hourLine = str(dataFile.readline().decode())
            if hourLine.startswith('hour'):
                entryHour = int(hourLine.split(":")[1])
            elif hourLine == '':
                dataFile.write(('hour:' + str(currDateMEZ[3]) + '\n').encode())
                entryHour = currDateMEZ[3]
                print('Created Hour entry')

        # Now we know that the Data-Structure exists -> save Data

        data = self.sensorDataToBytes(temperature, humidity)
        dataFile.write((data + '\n').encode())
        dataFile.close()

    def readPersistedValue(self):
        pass

    def getCurrentMEZ(self):
        # Austria-MESZ between:
        # Last March-Sunday at 02:00 MEZ
        # Last Oktober-Sunday at 03:00 MESZ
        mez = (time.time() + 3600)
        t = time.localtime(mez)
        month = t[1]
        day = t[2]

        if 3 <= month <= 10:
            if month == 3:
                lastSunday = self.getLastSundayOfMonth(mez)
                if day > lastSunday or (day == lastSunday and t[3] >= 2):
                    mez = mez + 3600
            elif month == 10:
                mest = mez + 3600
                tmest = time.localtime(mest)
                lastSunday = self.getLastSundayOfMonth(mest)
                if tmest[2] < lastSunday or (tmest[2] == lastSunday and tmest[3] < 3):
                    mez = mez + 3600
            else:
                mez = mez + 3600

        return mez

    def getLastSundayOfMonth(self, mez) -> int:
        t = time.localtime(mez)
        currDay = t[2]
        diff = 0

        if t[1] == 3 or t[1] == 10:
            diff = 31 - currDay

        eomTime = mez + (3600 * 24 * diff)
        eom = time.localtime(eomTime)
        weekDay = eom[6]
        diffToPrevSunday = 7 - (6 - weekDay)

        lastSunday = eom[2]
        if diffToPrevSunday < 7:
            lastSunday = time.localtime(eomTime - (3600 * 24 * diffToPrevSunday))[2]

        return lastSunday

# Get time from Timeserver to handle persistence
wlan = network.WLAN(network.STA_IF)
wlan.active(True)  # activate the interface
wlan.connect('PBS-3ADBF1', 'BbK1dBhg2wwgKb3dmh7S4')  # connect to an AP

for i in range(10):
    if wlan.isconnected():
        print("System time set")
        ntptime.settime()
        wlan.disconnect()
        wlan.active(False)
        break
    time.sleep(1)

# LED Inits
n = 256
np = neopixel.NeoPixel(machine.Pin(2), n)
dhtSensor = dht.DHT22(machine.Pin(26))
ledUtil = LEDUtils(np, font)

dataFileName = "sensorData.txt"

if dataFileName not in os.listdir():
    x = open(dataFileName, 'x')
    x.close()

persistence = Persistence()

padding = 0

avgTmpBuff = [0, 0, 0, 0, 0]
avgHumBuff = [0, 0, 0, 0, 0]

count = 0
while True:
    dhtSensor.measure()
    tmp = str(dhtSensor.temperature()) + '°C'
    hum = str(int(dhtSensor.humidity())) + '%'

    if padding >= 32:
        padding = 0

    # ledUtil.runningText(s.upper(), padding)
    ledUtil.writeStringToMatrix(tmp)

    np.write()
    padding += 1

    avgTmpBuff[count] = dhtSensor.temperature()
    avgHumBuff[count] = dhtSensor.humidity()

    if count == 4:
        avgTmp = sum(avgTmpBuff) / 5
        avgHum = sum(avgHumBuff) / 5

        persistence.persistValue(avgTmp, avgHum)

        print("Temperature Buffer: " + ', '.join(map(str, avgTmpBuff)))
        print("Humidity Buffer: " + ', '.join(map(str, avgHumBuff)))
        print("Persisted Data!")  # Todo Timestamp?

        avgTmpBuff = [0] * 5
        avgHumBuff = [0] * 5
        count = 0
    else:
        count += 1

    time.sleep(60)


class Solution:
    def dfs(self, grid, i, j, rows, cols):
        grid[i][j] = 0

        directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]

        for dr, dc in directions:
            nr, nc = dr + i, dc + j

            if nr < 0 or nr > rows - 1 or nc < 0 or nc > cols - 1 or grid[nr][nc] != 1:
                continue

            self.dfs(grid, nr, nc, rows, cols)

    def numEnclaves(self, grid: List[List[int]]) -> int:
        count = 0

        rows, cols = len(grid), len(grid[0])

        for i in range(rows):
            for j in range(cols):
                if (i == 0 or i == rows - 1 or j == 0 or j == cols - 1) and grid[i][j] == 1:
                    self.dfs(grid, i, j, rows, cols)

        for i in range(rows):
            for j in range(cols):
                if grid[i][j] == 1:
                    count += 1

        return count


import argparse
import json
import os
# from pprint import pprint
from typing import Tuple

import ebooklib
from ebooklib import epub
from gensim import summarization

from pkgs.utils import config_system

# load paths, books dictionaries
paths_dict = config_system.load_paths_config()
books_dict = config_system.load_books_config(paths_dict['books_config_file'])
# pprint(books_dict)

def store_title_author(book: ebooklib.epub.EpubBook, book_id: str) -> None:
    """Stores the book's title, author in books.yml

    Args:
        book (ebooklib.epub.EpubBook): book object for the current book-id
        book_id (str): id of the book currently under processsing
    """
    # Get the book title
    if not books_dict[book_id]['meta']['bookName']:
        book_title = book.get_metadata('DC', 'title')[0][0]
        books_dict[book_id]['meta']['bookName'] = book_title
    else:
        print('Title already filled')

    # Get the book author
    if not books_dict[book_id]['meta']['authorName']:
        book_author = book.get_metadata('DC', 'creator')[0][0]
        books_dict[book_id]['meta']['authorName'] = book_author
    else:
        print('Author name already filled')

    # Add the meta data to the books.yml file
    config_system.update_books(books_dict)

def get_titles(book: ebooklib.epub.EpubBook) -> list:
    """Gets the titles in the book for generating the summary outline

    Args:
        book (ebooklib.epub.EpubBook): book object for the current book-id

    Returns:
        titles (list): all the titles that will section the summary
    """
    titles = list()

    # Get all the titles
    for item in book.toc:
        if isinstance(item, ebooklib.epub.Link):
            titles.append(item.title)
        elif isinstance(item, Tuple):
            for link in item[1]:
                if isinstance(link, Tuple):
                    for smaller_link in link[1]:
                        if isinstance(smaller_link, ebooklib.epub.Link):
                            titles.append(smaller_link.title)
                elif isinstance(link, ebooklib.epub.Link):
                    # print('here', link)
                    titles.append(link.title)

    # Remove the clutter
    clean_titles = list()
    # print('titles', titles)
    for title in titles:
        if 'chapter' in title.lower():
            clean_titles.append(title)

    # content_index = 0
    # for i, title in enumerate(titles):
    #     if 'content' in title.lower():
    #         content_index=i
    # titles = titles[content_index+1:]

    print('Titles: stored: ', clean_titles)
    return clean_titles

def get_original_text(raw_book_path: str, titles: list) -> dict:
    """Get the raw text and store it in a dictionary keyed by sections

    Args:
        raw_book_path (str): path to the raw format of the book
        titles (list): titles used to section the book

    Returns:
        dict: raw text in a dict keyed by sections
    """
    text_dict = dict()
    with open(raw_book_path) as file:
        lines = file.readlines()
        for line in lines:
            if line.strip() in titles:
                curr_title = line.strip()
                text_dict.setdefault(curr_title, '')
            elif len(text_dict) > 0:
                text_dict[curr_title] += line.replace('\n', ' ')

    return text_dict

def get_summary(text_dict: dict, ratio: float):
    """Summarizes the text in the dictionary

    Args:
        text_dict (dict): dictionary of original text keyed by chapters
        ratio (float): ratio of length of summary to length of original text

    Returns:
        dict: dictionary of summary text keyed by sections
    """
    # print('Length before summarization')
    # for k, v in text_dict.items():
    #     print(k, len(v))

    for k, v in text_dict.items():
        print(k, len(v))
        text_dict[k] = summarization.summarize(v, ratio=ratio)

    # print('Length after summarization')
    # for k, v in text_dict.items():
    #     print(k, len(v))

    return text_dict

def summarize(book_id: str, ratio: float):
    epub_file_path = books_dict[book_id]['files']['epub']
    book = epub.read_epub(epub_file_path)

    store_title_author(book, book_id)

    titles = get_titles(book)

    raw_book_path = books_dict[book_id]['files']['raw']

    original_text_dict = get_original_text(raw_book_path, titles)
    print('length of original text dict', len(original_text_dict))

    data_dir = config_system.load_paths_config()['data_dir']
    summary_text = os.path.join(data_dir, book_id, 'summary_' +
                                str(ratio) + '.txt')
    summary_json = os.path.join(data_dir, book_id, 'summary_' +
                                str(ratio) + '.json')
    kshitij_json = os.path.join(data_dir, book_id, 'kshitij_' +
                                str(ratio) + '.json')

    if os.path.exists(summary_text) and len(original_text_dict) == 0:
        print("Text summary already exists or raw text \
               wasn't parsed correctly")
    else:
        # Get the summary
        print('-----Getting the summary-----')
        summary_text_dict = get_summary(original_text_dict, ratio)

        # Store the summary in .txt file
        print('Storing in .txt file')
        if not os.path.exists(summary_text):
            with open(summary_text, 'w') as file:
                for k, v in summary_text_dict.items():
                    file.write(k)
                    file.write('\n\n')
                    file.write(v)
                    file.write('\n\n')
        else:
            print('Summary text file already exists')

        # Store the summary in .json file
        print('Storing in json file')
        if not os.path.exists(summary_json):
            json_file = open(summary_json, 'w')
            json.dump(summary_text_dict, json_file, indent=4)
            json_file.close()
        else:
            print('Summary json file already exists')

        # store a file for kshitij
        all_text = ''
        with open(summary_text) as f:
            all_text = ''.join(f.readlines())[:5000]

        kshitij_dict = {
            "summary": all_text
        }

        print('Storing kshitij file')
        json_file = open(kshitij_json, 'w')
        json.dump(kshitij_dict, json_file, indent=4)
        json_file.close()

        # Add the summary paths to books.yml
        books_dict[book_id]['files']['summary_text'] = summary_text
        books_dict[book_id]['files']['summary_json'] = summary_json
        books_dict[book_id]['files']['kshitij_json'] = kshitij_json

        config_system.update_books(books_dict)

def main():
    parser = argparse.ArgumentParser(
        description='Summarize the book with bookid provided')
    parser.add_argument(
        '--bookid',
        dest='book_id',
        type=str,
        help='Enter the book id which you want to summarize.',
        required=True
    )
    parser.add_argument(
        '--ratio',
        dest='ratio',
        type=float,
        help='Enter the ratio of lengths of summary text to the original text',
        required=False
    )
    args = parser.parse_args()
    summarize(args.book_id, args.ratio)

if __name__ == '__main__':
    main()


# Verdi GUI is a small tool to interface with a Coherent Verdi laser through Serial
# Verdi GUI is distributed under the MIT Expat License

from PyQt4 import QtCore, QtGui
import os, serial, sys, time
from win32event import CreateMutex
from win32api import CloseHandle, GetLastError
from winerror import ERROR_ALREADY_EXISTS

sys.path.insert(0, './forms')   # tell Python where to look for the next imports

# import the code for all the ui window classes
import icon_qrc
from verdiconnect import Ui_Form as Connect_Form
from verdidoc import Ui_Form as Doc_Form
from verdifault import Ui_Form as Fault_Form
from verdiname import Ui_Form as Name_Form
from verdiwindow import Ui_Form as Window_Form

# Verdi class contains information and methods that directly deal with the laser:
#    1. All methods that handle serial communication (establishing connection, sending, receiving)
#    2. It also contains information obtained from laser status updates
#    3. Finally, it contains static dictionaries of fault error codes and serial commands

class Verdi(object):

    # Fault dictionary, contains laser fault codes as keys and the corresponding fault message as values
    FAULTS = {
        "1": "Laser Head Interlock Fault",
        "2": "External Interlock Fault",
        "3": "PS Cover Interlock Fault",
        "4": "LBO Temperature Fault",
        "5": "LBO Not Locked at Set Temp",
        "6": "Vanadate Temp. Fault",
        "7": "Etalon Temp. Fault",
        "8": "Diode 1 Temp. Fault",
        "10": "Baseplate Temp. Fault",
        "11": "Heatsink 1 Temp. Fault",
        "16": "Diode 1 Over Current Fault",
        "18": "Over Current Fault",
        "19": "Diode 1 Under Volt Fault",
        "21": "Diode 1 Over Volt Fault",
        "25": "Diode 1 EEPROM Fault",
        "27": "Laser Head EEPROM Fault",
        "28": "Power Supply EEPROM Fault",
        "29": "PS-Head Mismatch Fault",
        "31": "Shutter State Mismatch",
        "40": "Head-Diode Mismatch Fault",
        "47": "Vanadate2 Temp Fault"
    }

    # STATS and HOURS tuples contain queries to get the desire laser health stats and operating hours

    STATS = ("C", "D1C", "D2C", "D1HST", "D2HST", "ET", "VT", "BT", "LBOT")

    HOURS = ("HH", "D1H", "D2H")

    def __init__(self):
        object.__init__(self)

        # populate laser initial values. These will change later.
        self.connected = False
        self.baud = 19200
        self.powerValue = 0
        self.fault = 0
        self.shutter = "0"

        # iterate over STATS and HOURS and populate initial values for each laser health stat
        for i in self.STATS:
            exec("".join(["self.", i, "= 'N/A'"]))

        for h in self.HOURS:
            exec("".join(["self.", h, "= 'N/A'"]))

        self.serSetup()        # call method to set up the serial port

    def serSetup(self):
        # Windows ports are named "COM1," "COM2", "COM3, ...
        # iterate through all of these to find a connection
        # once a connection is found, test to see if it is the laser
        # if it is the laser, flag the connection and escape the loop
        for i in range(0, 10):
            self.port = "COM" + str(i)
            try:
                self.ser = serial.Serial(self.port, baudrate=self.baud, timeout=0.2)
            except serial.serialutil.SerialException:
                continue
            else:
                try:
                    # make sure that this is the laser's port
                    replies = ("PRINT SHUTTER", "Verdi>", "1", "0")
                    self.send("PRINT SHUTTER")
                    reply = self.read()
                    if reply in replies:
                        self.connected = True
                        break
                    else:
                        continue
                except:
                    continue

        if self.connected:
            # make sure Verdi is not in ECHO or PROMPT mode as that will mess with reading results
            self.send("ECHO = 0")
            self.send("PROMPT = 0")
            self.ser.flush()            # flush the buffer so there is no old data left

            # now that connection is ready, do the first round of updates
            self.updateStats()
            self.checkPower()
            self.checkShutter()

    def read(self):
        # read 1 line from serial buffer, stripping off termination characters automatically
        try:
            line = self.ser.readline().strip()
            return line
        except serial.serialutil.SerialTimeoutException:
            window.pred("ERROR: Readline timed out.")
        except:
            return "No response received."

    def send(self, cmd):
        # automatically add termination chars if none are given, then send command
        if cmd[-2:] != "\r\n":
            cmd += "\r\n"
        try:
            self.ser.write(cmd)
        except serial.serialutil.SerialTimeoutException:
            window.pred("ERROR: Send command timed out.")
        except serial.serialutil.SerialException as e:
            if "WriteFile failed" in str(e):
                self.connected = False
                window.serialDisconnected()
            else:
                raise

    def updateSer(self):
        # this function is called when serial port, baudrate or timeout are updated
        # program will close pre-existing serial port and open a new one
        try:
            self.ser.close()
        except:
            pass
        finally:
            try:
                self.ser = serial.Serial(self.port, baudrate=self.baud, timeout=0.2)
            except serial.serialutil.SerialException:
                window.pred("Could not connect with these values.")
                self.connected = False
            else:
                self.connected = True

    def updateStats(self):
        # get updated values from the laser for currents and temperatures
        # but first make sure there are no faults
        if self.connected:
            self.checkFault()
            if not self.fault:
                for stat in self.STATS:
                    query = "?" + stat
                    self.send(query)
                    reply = self.read()
                    if reply == "No response received.":
                        reply = "N/A"
                    exec ("".join(["self.", stat, " = reply"]))
                self.checkPower()
            else:
                fault = self.fault
                window.triggerFault(fault, self.FAULTS[fault])

    def getHours(self):
        # get values for laser head, diode 1, diode 2 operating hours
        if self.connected:
            for item in self.HOURS:
                query = "?" + item
                self.send(query)
                reply = self.read()
                if reply == "No response received.":
                    reply = "???"
                exec("".join(["self.", item, " = reply + ' h'"]))

    def checkShutter(self):
        # check if shutter is open or closed
        if self.connected:
            self.send("?S")
            self.shutter = self.read()

    def checkFault(self):
        # query the laser for active faults
        # laser will return "SYSTEM OK" if no faults are active
        self.send("?F")
        fault = self.read()
        if fault == "SYSTEM OK":
            self.fault = 0
        elif fault == "No response received.":
            pass
        else:
            self.fault = fault

    def setPower(self, power):
        # set the laser's power to a new value
        if self.connected:
            power = round(power, 2)
            pstring = str(power)
            cmd = "P = " + pstring
            self.send(cmd)
            self.read()     # discard a line
            verdi.powerValue = power
            # window.pblue(" ".join(["Power has been set to", str(power), "watts."]))

    def checkPower(self):
        # get the laser's set power value
        if self.connected:
            self.send("?SP")
            power = self.read()
            if power != '':
                self.powerValue = float(power)

    def toggleShutter(self, open):
        # open or close the shutter
        if self.connected:
            if open:
                self.send("S = 1")
            else:
                self.send("S = 0")
        self.checkShutter()

# Create the connection pop-up window class

class ConnectWindow(QtGui.QWidget, Connect_Form):
    def __init__(self, parent=None):
        super(ConnectWindow, self).__init__(parent)
        # try to tell Windows to keep this window on top
        QtGui.QMainWindow.__init__(self, None, QtCore.Qt.WindowStaysOnTopHint)
        self.setupUi(self)
        self.resize(200,100)
        self.setWindowIcon(QtGui.QIcon(':/icon/verdigui.ico'))
        # assign handlers for the two buttons
        self.retry_btn.clicked.connect(self.retryConnection)
        self.cancel_btn.clicked.connect(self.cancelConnection)

    def retryConnection(self):
        verdi.serSetup()
        if verdi.connected:
            window.pblue("Verdi GUI is now connected to port: " + verdi.port)
            window.updateAll()
            window.checkShutter()
            window.formatPower()
            window.timer.start()
            self.hide()
        else:
            self.label.setText("Still no connection.")
            self.label_2.setText("Are you sure your computer is connected to the laser?")

    def cancelConnection(self):
        window.warned = True
        window.pblue("Verdi GUI did not establish a connection to a laser.\n"
                 "To try again at a later time, use the command \"-scan\".")
        self.hide()

# Create the documentation pop-up window

class DocWindow(QtGui.QWidget, Doc_Form):
    def __init__(self, parent=None):
        super(DocWindow, self).__init__(parent)
        self.setupUi(self)
        self.setWindowIcon(QtGui.QIcon(':/icon/verdigui.ico'))

# Create the fault pop-up window

class FaultWindow(QtGui.QWidget, Fault_Form):
    def __init__(self, parent=None):
        super(FaultWindow, self).__init__(parent)
        QtGui.QMainWindow.__init__(self, None, QtCore.Qt.WindowStaysOnTopHint)
        self.setupUi(self)
        self.setWindowIcon(QtGui.QIcon(':/icon/verdigui.ico'))
        self.pushButton.clicked.connect(self.clearFault)

    def clearFault(self):
        # tell the laser that the fault is (probably) cleared
        verdi.send("L = 1")
        verdi.read()
        window.timer.start(window.interval)
        window.checkShutter()
        self.hide()

# Create the export pop-up window
# This window request the user's name, then logs laser stats

class NameWindow(QtGui.QWidget, Name_Form):

    def __init__(self, parent=None):
        super(NameWindow, self).__init__(parent)
        self.setupUi(self)
        self.setWindowIcon(QtGui.QIcon(':/icon/verdigui.ico'))

        # assign handlers to the buttons
        self.okay_btn.clicked.connect(self.exportData)
        self.name_box.returnPressed.connect(self.exportData)
        self.cancel_btn.clicked.connect(self.hide)

    def exportData(self):
        # record user's name and log that name, along with all the stats, to the log file
        name = str(self.name_box.text()).strip()
        comment = str(self.comment_box.text()).strip()
        if len(name) == 0:      # catch empty strings
            return
        else:
            verdi.getHours()
            try:
                # try to open the file
                log = open("verdilog.csv")
            except IOError:
                # an IOError would mean that the file does not exist, so create one
                log = open("verdilog.csv", "a")
                cols = ("TIME,LOGGER,POWER,AVG CURRENT,D1 CURRENT,D2 CURRENT,D1 HEATSINK,D2 HEATSINK,"
                        "BASEPLATE,ETALON,VANADATE,LBO,HEAD HRS,D1 HRS, D2 HRS, COMMENT\n")
                log.write(cols)
            else:
                # if file already exists, switch to "append" mode
                log = open("verdilog.csv", "a")
            finally:
                # now that the file definitely exists and is open, format stats and append them to the file
                datetime = time.strftime("%Y-%m-%d %H:%M:%S")
                stats = (datetime, name, str(verdi.powerValue), verdi.C, verdi.D1C, verdi.D2C, verdi.D1HST, verdi.D2HST,
                         verdi.BT, verdi.ET, verdi.VT, verdi.LBOT, verdi.HH, verdi.D1H, verdi.D2H, comment)
                writestring = ",".join(stats)
                try:
                    log.write(writestring + "\n")
                except IOError as e:
                    # an IOError here would mean the program doesn't have permission to write
                    if str(e) == "File not open for writing":
                        window.pred("Verdi GUI doesn't have permission to write to the log.\n"
                                 "Make sure the log file is closed and you have permission to write "
                                 "in the folder in which Verdi GUI is installed.")
                        self.hide()
                        return
                log.close()
                # get Verdi GUI's folder's path
                path = os.path.realpath(sys.argv[0])
                if not os.path.isdir(path):
                    path = os.path.dirname(path)
                window.pblue("".join(["Current stats have been logged to ", path, "\\verdilog.csv"]))
            self.hide()

# Main UI window

class MainWindow(QtGui.QWidget, Window_Form):

    COLORS = {
        "black": "#000000",
        "green": "#508a59",
        "red": "#a83636",
        "blue": "#586890",
    }

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)

        # resize the window based on user's screen resolution
        screen = app.desktop().screenGeometry()
        width = int(screen.width()*0.22)
        height = int(screen.height()*0.52)
        self.resize(width, height)

        # set a custom icon
        self.setWindowIcon(QtGui.QIcon(':/icon/verdigui.ico'))

        # initialize the Verdi class when the GUI is created
        global verdi
        verdi = Verdi()

        self.assignHandlers()
        self.createTooltips()
        self.updateAll()
        self.checkShutter()
        self.formatPower()

        # initialize the auto-update timer with default interval of 5 seconds
        self.interval = 2000
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updateAll)
        self.timer.start(self.interval)

        # flag to remember whether the user has already been warned about connection issues
        # so that they are not repeatedly spammed with pop-up warning windows
        self.warned = False

        # say hello to the user
        self.pgreen("Welcome to Verdi GUI! ")
        if verdi.connected:
            self.pgreen("Verdi GUI is connected via Windows port: " + verdi.port)
        else:
            self.serialDisconnected()

    def assignHandlers(self):
        # connect buttons to handler functions so that they actually do something when clicked
        self.shutter_btn.clicked.connect(self.toggleShutter)
        self.export_btn.clicked.connect(self.exportButtonPushed)
        self.openLog_btn.clicked.connect(self.openLog)
        self.doc_btn.clicked.connect(self.openDocumentation)
        self.input_box.returnPressed.connect(self.inputEntered)
        self.power_up.clicked.connect(self.powerUp)
        self.power_down.clicked.connect(self.powerDown)

    def createTooltips(self):
        # add helpful tooltips to everything
        self.power_up.setToolTip("Increase the laser power by 10 mW.")
        self.power_down.setToolTip("Decrease the laser power by 10 mW.")
        self.shutter_btn.setToolTip("Shutter is currently CLOSED. Click to OPEN.")
        self.export_btn.setToolTip("Record currents, temperatures, and operating hours to log file.")
        self.C_box.setToolTip("Average measured current, in amperes, of the two diodes.")
        self.D1C_box.setToolTip("Measured current, in amperes, of Diode 1.")
        self.D2C_box.setToolTip("Measured current, in amperes, of Diode 2.")
        self.D1HST_box.setToolTip("Measured temperature, in degrees Celsius, of Diode 1's heatsink.")
        self.D2HST_box.setToolTip("Measured temperature, in degrees Celsius, of Diode 2's heatsink.")
        self.BT_box.setToolTip("Measured temperature, in degrees Celsius, of laser head baseplate.")
        self.ET_box.setToolTip("Measured temperature, in degrees Celsius, of the etalon.")
        self.VT_box.setToolTip("Measured temperature, in degrees Celsius, of the vanadate element.")
        self.LBOT_box.setToolTip("Measured temperature, in degrees Celsius, of the LBO crystal.")
        self.doc_btn.setToolTip("Open the full list of commands.")

    # print colorful messages for better visibility
    def pblack(self, msg):
        msg = "".join(["", msg, ""])
        self.output_box.append(msg)
        self.output_box.moveCursor(QtGui.QTextCursor.End)

    def pgreen(self, msg):
        msg = "".join(["", msg, ""])
        self.output_box.append(msg)
        self.output_box.moveCursor(QtGui.QTextCursor.End)

    def pblue(self, msg):
        msg = "".join(["", msg, ""])
        self.output_box.append(msg)
        self.output_box.moveCursor(QtGui.QTextCursor.End)

    def pred(self, msg):
        msg = "".join(["", msg, ""])
        self.output_box.append(msg)
        self.output_box.moveCursor(QtGui.QTextCursor.End)

    def updateAll(self):
        # query the laser for the latest health stats, then update the display boxes to match
        verdi.updateStats()
        self.checkShutter()
        self.C_box.setText(verdi.C)
        self.D1C_box.setText(verdi.D1C)
        self.D2C_box.setText(verdi.D2C)
        self.D1HST_box.setText(verdi.D1HST)
        self.D2HST_box.setText(verdi.D2HST)
        self.ET_box.setText(verdi.ET)
        self.VT_box.setText(verdi.VT)
        self.BT_box.setText(verdi.BT)
        self.LBOT_box.setText(verdi.LBOT)

    def formatPower(self):
        power = "{:.2f}".format(verdi.powerValue)
        self.power_lcd.display(power)

    def powerUp(self):
        # increase the laser's power by 0.01 W, unless power is already too high
        power = verdi.powerValue + 0.01
        if power <= 5.001:
            verdi.setPower(power)
            self.formatPower()
        else:
            window.pred("Power cannot be higher than 5.00 W.")

    def powerDown(self):
        # decrease the laser's power by 0.01 W, unless power is already too low
        power = verdi.powerValue - 0.01
        if power >= 0.009:
            verdi.setPower(power)
            self.formatPower()
        else:
            window.pred("Power cannot be lower than 0.01 W.")

    def toggleShutter(self):
        # toggle laser's shutter when button is pushed
        if verdi.shutter == "1":
            verdi.toggleShutter(False)
        else:
            verdi.toggleShutter(True)
        self.checkShutter()

    def checkShutter(self):
        # make sure GUI's shutter status button matches laser's real shutter status
        btn = self.shutter_btn
        verdi.checkShutter()
        if verdi.shutter == "1":
            btn.setChecked(True)
            btn.setText("OPEN")
            btn.setToolTip("Shutter is currently OPEN. Click to CLOSE.")
        else:
            btn.setChecked(False)
            btn.setText("CLOSED")
            btn.setToolTip("Shutter is currently CLOSED. Click to OPEN.")

    def exportButtonPushed(self):
        # when the export button is pushed, launch the export window
        self.name_popup = NameWindow()
        self.name_popup.show()

    def openLog(self):
        try:
            os.startfile("verdilog.csv")
        except WindowsError:
            self.pred("No log found. Click \"Export\" to start a log.")

    def openDocumentation(self):
        # when documentation button is pushed, launch the documentation window
        self.doc_popup = DocWindow()
        self.doc_popup.show()

    def inputEntered(self):
        # take whatever the user has typed and parse it for commands
        # give the user help, if they are asking for help
        # if user types a GUI command that beings with a dash (-), figure out what it is
        # all other commands are sent straight to the laser

        cmd = str(self.input_box.text()).strip()
        HELP = ["help", "-", "-help", "HELP", "-HELP", "-h"]

        if len(cmd) == 0:       # catch accidental enter press
            return

        self.pblack("» " + cmd)
        if cmd in HELP:
            self.showHelp()
        elif cmd[0] == "-":
            self.dashCommand(cmd[1:].split(" ", 1))
        else:
            if verdi.connected:
                verdi.send(cmd)
                reply = verdi.read()
                self.pblue("Reply received: " + reply)
        self.input_box.setText("")

    def dashCommand(self, cmd):
        # find out what the user wants and do it
        CMDS = ("port", "baud", "update")
        if cmd[0] == "scan":
            verdi.serSetup()
            if verdi.connected:
                self.pblue("Verdi GUI is now connected to port: " + verdi.port)
                self.warned = False
            else:
                self.pblue("Verdi GUI could not establish a connection to a laser.")
        elif cmd[0] not in CMDS:
            self.pred("Not a value command.")
        else:
            try:
                val = int(cmd[1])       # try to convert the second word to an integer
            except ValueError:
                # if we get a ValueError, that means there is a second word, but it's not a number
                # so we can't do anything with it. In this case, give the user an appropriate error message.
                self.pred("ERROR: New setting must be an integer")
            except IndexError:
                # if we get an IndexError, that means there was only one word in the command, which is fine
                # we can execute that one word command
                if cmd[0] == "port":
                    self.pblue("Current port is: " + str(verdi.port))
                elif cmd[0] == "baud":
                    self.pblue("Current baudrate is: " + str(verdi.baud))
                elif cmd[0] == "update":
                    if verdi.connected:
                        self.updateAll()
                        self.pblue("Updated laser stats.")
                    else:
                        self.pred("Verdi GUI cannot update stats while laser is disconnected.")
            else:
                # if no errors are raised, that means the user issued a valid 2-word command
                # figure out what it is and execute it
                if cmd[0] == "port":
                    verdi.port = "COM" + cmd[1]
                    self.pblue("Switching to port: " + cmd[1])
                elif cmd[0] == "baud":
                    verdi.baud = val
                    self.pblue("Baudrate changed to: " + cmd[1])
                elif cmd[0] == "update":
                    if val > 0:
                        self.interval = val*1000
                        self.timer.setInterval(self.interval)
                        self.pblue(" ".join(["Update frequency changed to", cmd[1], "seconds"]))
                    else:
                        self.pred("Update interval must be an integer greater than 0.")

                if verdi.connected:
                    verdi.updateSer()

        # clear the input box
        self.input_box.setText("")

    def showHelp(self):
        # mini-documentation, accessed in the program by typing 'help', '-h', etc
        helpstring = ("VERDI GUI QUICK COMMANDS REFERENCE:
"
                      "    -scan: sweep COM ports to try and find the laser
"
                      "    -port n: switch to using COM port n
"
                      "    -baud n: switch to baudrate n
"
                      "    -update: force Verdi GUI to fetch new stat values
"
                      "    -update n: change update frequency to every n seconds
"
                      "Commands that do not being with a dash (-) are sent directly to the laser.")
        self.pblue(helpstring)

    def serialDisconnected(self):
        # check warning flag to see if user has already been warned about the lack of connection
        # if not, make the warning pop up window
        self.timer.stop()
        if not self.warned:
            self.connection_popup = ConnectWindow()
            self.connection_popup.show()

    def triggerFault(self, fault, faultstring):
        # oh no there is a fault
        # better make a new window
        self.fault_popup = FaultWindow()
        self.fault_popup.errorCode_label.setText("Error code: " + fault)
        self.fault_popup.errorMsg_label.setText(faultstring)
        self.fault_popup.show()
        self.timer.stop()   # temporarily suspend the update timer

# prevent duplicate windows by registered a mutex
class Singleton:

    def __init__(self):
        self.mutex = CreateMutex(None, False, "Verdi_GUI_Windows_Registered_Mutex")
        self.lasterror = GetLastError()

    def isRunning(self):
        return (self.lasterror == ERROR_ALREADY_EXISTS)

    def __del__(self):
        if self.mutex:
            CloseHandle(self.mutex)

if __name__ == "__main__":
    # check for pre-existing window immediately; kill new window if it is a duplicate
    instance = Singleton()
    if instance.isRunning():
        sys.exit(0)
    else:
        app = QtGui.QApplication(sys.argv)
        window = MainWindow()
        window.show()
        sys.exit(app.exec_())


import os
import platform
import sys
import lit.formats
from lit.llvm import llvm_config
from lit.llvm.subst import ToolSubst

# Lint for undefined variables is disabled as config is not defined inside this
# file, instead config is injected by way of evaluating runlit.cfg.py from
# runlit.site.cfg.py which in turn is evaluated by lit.py. The structure is
# common for lit tests and intended to only persist temporarily (b/136126535).
# pylint: disable=undefined-variable
# Configuration file for the 'lit' test runner.

# name: The name of this test suite.
config.name = 'MLIR ' + os.path.basename(config.mlir_test_dir)

config.test_format = lit.formats.ShTest(not llvm_config.use_lit_shell)

# suffixes: A list of file extensions to treat as test files.
config.suffixes = ['.cc', '.hlo', '.hlotxt', '.json', '.mlir', '.pbtxt', '.py']

# test_source_root: The root path where tests are located.
config.test_source_root = config.mlir_test_dir

# test_exec_root: The root path where tests should be run.
config.test_exec_root = os.environ['RUNFILES_DIR']

if platform.system() == 'Windows':
  tool_patterns = [
      ToolSubst('FileCheck.exe', unresolved='fatal'),
      #  Handle these specially as they are strings searched for during testing.
      ToolSubst('count.exe', unresolved='fatal'),
      ToolSubst('not.exe', unresolved='fatal')
  ]

  llvm_config.config.substitutions.append(
      ('%python', '"%s"' % (sys.executable)))

  llvm_config.add_tool_substitutions(tool_patterns,
                                     [llvm_config.config.llvm_tools_dir])
else:
  llvm_config.use_default_substitutions()

llvm_config.config.substitutions.append(
    ('%tfrt_bindir', 'tensorflow/compiler/aot'))

# Tweak the PATH to include the tools dir.
llvm_config.with_environment('PATH', config.llvm_tools_dir, append_path=True)

tool_dirs = config.mlir_tf_tools_dirs + [
    config.mlir_tools_dir, config.llvm_tools_dir
]
tool_names = [
    'dtensor-opt',
    'flatbuffer_to_string',
    'flatbuffer_translate',
    'hlo_to_kernel',
    'hlo_to_llvm_ir',
    'ifrt-opt',
    'json_to_flatbuffer',
    'kernel-gen-opt',
    'lhlo-tfrt-opt',
    'mlir-bisect',
    'mlir-hlo-opt',
    'mlir-interpreter-runner',
    'mlir-opt',
    'mlir-tflite-runner',
    'mlir-translate',
    'odml-to-stablehlo-opt',
    'odml_to_stablehlo',
    'stable-quant-opt',
    'tac-opt-all-backends',
    'tac-translate',
    'tf-mlir-translate',
    'tf-opt',
    'tf-quant-opt',
    'tf-reduce',
    'tf-tfrt-opt',
    'tf_tfjs_translate',
    'tf_tfl_translate',
    'tfcompile',
    'tfg-opt-no-passes',
    'tfg-transforms-opt',
    'tfg-translate',
    'tfjs-opt',
    'xla-cpu-opt',
    'xla-gpu-opt',
    'xla-mlir-gpu-opt',
    'xla-runtime-opt',
    'xla-translate',
    'xla-translate-gpu-opt',
    'xla-translate-opt',
]
tools = [ToolSubst(s, unresolved='ignore') for s in tool_names]
llvm_config.add_tool_substitutions(tools, tool_dirs)
# pylint: enable=undefined-variable


from board import Board
import random
import numpy as np

walk_punishment = -1/100
flag_reward = 1/7
target_reward = None
back_punishment = 0
been_punishment = -2/100
alpha = 0.2
gamma = 0.3

step_treshhold = 20
iters = 0

def play(iters,name):
    """
        this function is the main function of the game and it is responsible for the game loop and training the agent
    Args:
        iters (int): number of iterations that agent has been trained
        name (str): name of the Qtable file
    """

    # game is the object of the class Board which is responsible for the game logic and visualise the board
    game = Board(walk_punishment,flag_reward,target_reward,back_punishment,been_punishment,alpha,gamma)

    # game reset function is responsible for reseting the game's attr
    game.reset(iters)

    # this function is responsible for creating the zeros Qtable. if we want to train agent from first place
    # game.resetQtable()

    # this function is responsible for loading the Qtable from the file. if we want to train agent from the last place
    # game.loadQtable()

    # agent is the object of the class Agent which is responsible for the agent's actions and choices
    agent = Agent(game)

    # steps is variable that is responsible for the number of steps that agent has been moved in this iteration
    step = 0
    # main loop of code that is responsible for the training agent for infinite times
    while True:
        step += 1

        state = agent.get_state()
        choices = agent.get_choices(state)
        action = agent.make_action(state,choices,iters)
        flag,target = game.run(action,step,iters)

        # if agent has been moved to the flag then it could move 15 more step in this iteration
        if flag :
            step -= 15

        # if agent has been moved to the target then it game should be reset
        if target:
            step = -np.NINF
        # agent's step never should be more than 20. for avoiding infinite loop
        if step > step_treshhold:
            iters += 1
            game.saveQtable(name)
            game.reset(iters)
            step = 0

'''
    this function is responsible for printing the Qtable
'''
def printQtable(qTable):
    Qtable = np.loadtxt('{}.txt'.format(qTable))
    Qtable = Qtable.reshape(10,10,4)

    for i in range(10):
        for j in range(10):
            print('({},{})'.format(i,j),end=' ')
            print(Qtable[i][j])
        print()

# Class for agent. responsible for agent's actions and choices
class Agent:
    def __init__(self,game):
        self.game = game
        self.qTable = None
        self.choices = ['left','up','right','down']

    # this function is responsible for getting the state that agent is in. state is the board at the moment
    def get_state(self):
        cords = self.game.player.rect.center
        i = (cords[0] - 30) // 60
        j = (cords[1] - 30) // 60
        return self.game.nodes[j][i]

    # return the possible choices that agent can move
    def get_choices(self,state):
        i = state.j
        j = state.i
        choices = ['left','up','right','down']
        if j == 0:
            choices.remove('left')
        elif j == 9:
            choices.remove('right')
        if i == 0:
            choices.remove('up')
        elif i == 9:
            choices.remove('down')

        if i > 0 and self.game.nodes[i-1][j].type == 'block':
            try:
                choices.remove('up')
            except:
                pass
        if i < 9 and self.game.nodes[i+1][j].type == 'block':
            try:
                choices.remove('down')
            except:
                pass
        if j > 0 and self.game.nodes[i][j-1].type == 'block':
            try:
                choices.remove('left')
            except:
                pass
        if j < 9 and self.game.nodes[i][j+1].type == 'block':
            try:
                choices.remove('right')
            except:
                pass

        return choices

    # this function is responsible for making the action for agent. it is responsible for choosing the best action recording to the Qtable
    # or move randomly for exploring the environment
    def make_action(self,state,choices,iters):
        if random.randint(0,500) < 200 - iters:
            action = random.choice(choices)
        else:
            actionChoices = [0 for i in range(4)]
            if 'left' in choices:
                actionChoices[0] = 1
            if 'up' in choices:
                actionChoices[1] = 1
            if 'right' in choices:
                actionChoices[2] = 1
            if 'down' in choices:
                actionChoices[3] = 1

            q = state.q

            for i in range(4):
                if actionChoices[i] == 0:
                    q[i] = np.NINF

            action = self.choices[np.argmax(q)]

        return action

# this function is responsible for generating the random environment and board for task assignment
def generate_random_env():
    env = np.ones((10,10),dtype=int)
    for i in range(10):
        for j in range(10):
            if i == j == 0 or i == j == 9:
                continue

            if random.randint(0,100) < 20:
                env[i][j] = 3
            elif random.randint(0,100) < 7:
                env[i][j] = 2
    env[9][9] = 4

    return env

# this function is responsible for training the agent for random environment for task assignment
def train_random(iters,board,randomName):
    game = Board(walk_punishment,flag_reward,target_reward,back_punishment,been_punishment,alpha,gamma,board)
    game.reset(iters,board)

    game.resetQtable(randomName)

    # game.loadQtable()
    agent = Agent(game)

    step = 0
    while True:
        step += 1

        state = agent.get_state()
        choices = agent.get_choices(state)
        action = agent.make_action(state,choices,iters)
        flag,target = game.run(action,step,iters)

        if flag :
            step -= 15

        if target:
            step = -np.NINF
        if step > step_treshhold:
            iters += 1
            game.saveQtable(randomName)
            game.reset(iters,board)
            step = 0

if __name__ == "__main__":

    # board = generate_random_env()
    # train_random(iters,board,'random')
    play(iters,'Qtable')
    printQtable('Qtable')


def get_number():
	user_input = None
	total = 0.0
	while True:
		try:
			user_input = input("Enter a number:  ")
			if user_input.upper() != "DONE":
				number = float(user_input)
				total += number
			else:
				break
		except:
			print("Invalid number")
	return total

print("Total ",get_number())


from ape import project
from ape.cli import get_user_selected_account

def simpleStorage_interact():
    # The CLI will ask which account to use
    dev_account = get_user_selected_account()
    print(f'The account balance is: {dev_account.balance / 1e18} Goerli ETH')

    # Initialize latest deployed contract
    simple_storage= project.SimpleStorage.deployments[-1]
    print(f'The latest SimpleStorage contract is deployed at: {simple_storage.address}')

    # Prompt the user to input a string to save
    string_to_save = input("Type the string to save: ")

    print("Saving the string, please sign the transaction when prompted.")
    simple_storage.setWord(string_to_save, sender=dev_account)

    # Retrive and display the saved string
    print(f'The saved string is: {simple_storage.getWord()}')

def main():
    simpleStorage_interact()


import os
import math
from urllib.parse import urlencode
import pandas as pd
import numpy as np
from ulttb import downsample
from bottle import abort

import config

class App():
    def __init__(self, *args, **kwargs):
        # set up output dir
        if not os.path.exists(config.APP_CONFIG["OUTPUT_DIR"]):
            os.makedirs(config.APP_CONFIG["OUTPUT_DIR"])

    def get_data(self, request):
        # check for params
        if (request.query.get("keys") is None or
            request.query.get("project") is None or
            request.query.get("source_id") is None):
            abort(400, "missing query parameter")

        # pull out parameters from the request
        bounds = [float(x) for x in request.query.get("bounds").split(",")] if request.query.get("bounds") else []
        in_keys = request.query.get("keys").split(",") if request.query.get("keys") else config.APP_CONFIG["DEFAULT_KEYS"]
        threshold = int(request.query.get("target")) if request.query.get("target") else config.APP_CONFIG["DEFAULT_TARGET"]
        project = request.query.get("project") if request.query.get("project") else "NONE"
        source_id = request.query.get("source_id") if request.query.get("source_id") else "NONE"
        output_format = request.query.get("format") if request.query.get("format") else config.APP_CONFIG["DEFAULT_FORMAT"]

        # deal with idioms
        keys = self._fix_keys(in_keys)

        # download the file if it's not cached
        filename = self._build_filename(project, source_id, keys)
        if not os.path.isfile(filename):
            url = self._build_url(project, source_id, keys)
            self._download_file(url, filename, keys)

        # decimate the data into the desired format
        data = None
        if "csv" in output_format:
            output_format = config.APP_CONFIG["AVAILABLE_FORMATS"]["csv"]
            data = self._decimate_data(filename, bounds, keys, in_keys, threshold, output_format).to_csv(index=False)
        elif "json" in output_format:
            output_format = config.APP_CONFIG["AVAILABLE_FORMATS"]["json"]
            data = self._decimate_data(filename, bounds, keys, in_keys, threshold, output_format)

        if data is None:
            abort(500, "Failed to decimate data")

        # clear cached data
        if not config.APP_CONFIG["CACHE_FILES"] and os.path.isfile(filename):
            os.remove(filename)

        return (output_format, data)

    def _fix_keys(self, in_keys):
        keys = ["_".join(key.split(" ")).lower() for key in in_keys]

        if len(keys) < 2:
            keys = [key for key in config.APP_CONFIG["DEFAULT_KEYS"]]

        time_found = False
        for i, key in enumerate(keys):
            if "time" in key:
                keys[i] = "measurement_date_time"
                time_found = True
            elif not key in config.APP_CONFIG["NO_MOD_KEYS"] and not key.endswith("_d"):
                keys[i] = key + "_d"

        # ensure there is always time in the data
        if not time_found:
            keys.append("measurement_date_time")

        return keys

    def _build_url(self, project, source_id, keys):
        query = {
            "facet": "on",
            "wt": "csv",
            "rows": "10000000",
            "sort": "%s asc" % (keys[0]),
            "fl": ",".join(keys),
            "q": "datatype:data AND project:%s AND source_id: %s" % (project, source_id)
        }

        return config.APP_CONFIG["SOLR_URL"] + "?" + urlencode(query)

    def _build_filename(self, project, source_id, keys):
        filename = "%s/%s__%s__%s.h5" % (config.APP_CONFIG["OUTPUT_DIR"], project, source_id, "_".join(keys))

        return filename

    def _download_file(self, url, filename, keys):
        # download file
        data = pd.read_csv(url)

        # remove empty lines
        data.dropna(inplace=True)

        # edit time entries
        for key in keys:
            if "time" in key.lower():
                to_sec = 10**9
                data[key] = (pd.to_datetime(data[key]).astype(np.int64)/to_sec).astype(np.int64)

        # mask out no data values
        masks = [data[key] != -9999 for key in keys]
        datamask = masks[0]
        for mask in masks:
            datamask = datamask & mask
        data = data[datamask]

        # save to hdf file
        data.to_hdf(filename, "table", append=True, data_columns=True)

        return filename

    def _decimate_data(self, filename, bounds=[], keys=[], header_keys=[], threshold=config.APP_CONFIG["DEFAULT_TARGET"], output_format=config.APP_CONFIG["DEFAULT_FORMAT"]):
        if len(keys) < 2:
            return None

        # edit time bounds
        if "time" in keys[0].lower():
            bounds = [ x / 1000 if self._magnitude(x) > 9 else x for x in bounds ]

        # read data from hdf file
        if(len(bounds) == 2):
            filter_str = "%s >= %f & %s <= %f" % (keys[0], bounds[0], keys[0], bounds[1])
            data = pd.read_hdf(filename, "table", where = filter_str)
        else:
            data = pd.read_hdf(filename, "table")

        # reset index bounds
        sub_data = data.reset_index(drop=True)

        # rearrange columns
        sub_data = sub_data[keys]

        # sort the data
        sub_data = sub_data.sort_values(by=[keys[0]])

        # decimate the data
        dec_data = sub_data.values.tolist()
        if(threshold != config.APP_CONFIG["NO_DECIMATION_TARGET"] and len(dec_data) > threshold):
            dec_data = downsample(dec_data, threshold)

        # format the data
        col_names = list(sub_data)
        if len(header_keys) > 0:
            col_names = [x for x in header_keys] # duplicate the list
            col_names.extend(list(sub_data)[len(header_keys):]) # add any additional columns (hidden time column)
        if output_format == config.APP_CONFIG["AVAILABLE_FORMATS"]["csv"]:
            dec_data = pd.DataFrame(dec_data, columns=col_names)
        elif output_format == config.APP_CONFIG["AVAILABLE_FORMATS"]["json"]:
            dec_data = {
                "data": dec_data,
                "meta": {
                    "sub_size": len(sub_data),
                    "dec_size": len(dec_data),
                    "columns": col_names
                }
            }

        return dec_data

    def _magnitude(self, x):
        return int(math.floor(math.log10(x)))


from flask import Flask, render_template, request

app = Flask(__name__)

@app.route("/")
def index():
    title = "Signos LTDA"
    return render_template("index.html", title=title)

@app.route("/signo_chines")
def chines():
    title = "Signo Chinês"
    return render_template("signo_chines.html",title=title)

if __name__ == "__main__":
    app.run(debug=True)


from Mqtt_Parser import Mqtt_Parser

class Mqtt_Publish_Parser(Mqtt_Parser):
    def __init__(self,mqtt):
        self.packet = mqtt
        self.messageType = mqtt[0]
        self.messageTypeCode,self.messageTypeWord = self.typeMap(mqtt[0])
        self.messageLength = mqtt[1]
        self.messageLengthNum = self.messageLength
        self.topicLength = mqtt[2:4]
        self.topicLengthNum = int.from_bytes(self.topicLength,"big")
        self.topicName = mqtt[4:self.topicLengthNum+4]
        self.message = mqtt[self.topicLengthNum+4:][:self.messageLengthNum]

    def changeTopic(self,newTopic):
        topic = []
        for letter in newTopic:
            topic.append(ord(letter))
        self.topicName = topic
        self.topicLengthNum = len(newTopic)
        self.topicLength = self.topicLengthNum.to_bytes(2,"big")

    def changeMessage(self,newMessage):
        self.messageWords = newMessage
        self.messageLengthNum = self.topicLengthNum + 2 + len(newMessage)
        self.messageLength = self.messageLengthNum
        message = []
        for letter in newMessage:
            message.append(ord(letter))
        self.message = message

    def getHex(self):
        full = []
        full.append(self.messageType)
        full.append(self.messageLength)
        [full.append(x) for x in self.topicLength]
        [full.append(x) for x in self.topicName]
        [full.append(x) for x in self.message]
        return(full)

if __name__=="__main__":#Tesztelés
    asd = Mqtt_Publish_Parser(b'0\x1f\x00\x0bTemperature6. tempeture: 50.0')
    print(b'0\x1f\x00\x0bTemperature6. tempeture: 50.0')
    print(len(asd.getHex()))
    print(bytes(asd.getHex()))
    asd.changeTopic("Temperatur")
    asd.changeMessage("6. tempeture: 50.0")
    print(bytes(asd.getHex()))
    print(len(asd.getHex()))


# -*- coding: utf-8 -*-
"""
Created on Thu Jan 23 17:28:54 2014

@author: cesar zamora
"""

import numpy as np
from scipy.spatial import Delaunay

n = 100

points = np.zeros((n,2))

for i in range(0,n): #genera n puntos aleatorios dentro del cir de radio 1
    point = np.array([1.,1.])
    while np.linalg.norm(point) >= 1:
        g_point = 2 * np.random.random_sample((1, 2)) - 1
        point = g_point
    points[i] = point

#print points

#points = np.random.rand(n, 2) # 30 points in 2-d
tri = Delaunay(points)

# Make a list of line segments:
# edge_points = [ ((x1_1, y1_1), (x2_1, y2_1)),
#                 ((x1_2, y1_2), (x2_2, y2_2)),5
#                 ... ]
edge_points = []
edges = set()

def add_edge(i, j):
    """Add a line between the i-th and j-th points, if not in the list already"""
    if (i, j) in edges or (j, i) in edges:
        # already added
        return
    edges.add( (i, j) )
    edge_points.append(points[ [i, j] ])

# loop over triangles:
# ia, ib, ic = indices of corner points of the triangle
for ia, ib, ic in tri.vertices:
    add_edge(ia, ib)
    add_edge(ib, ic)
    add_edge(ic, ia)

# plot it: the LineCollection is just a (maybe) faster way to plot lots of
# lines at once
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection

lines = LineCollection(edge_points)
plt.figure()
plt.title('Delaunay triangulation')
plt.gca().add_collection(lines)
plt.plot(points[:,0], points[:,1], 'o', hold=1)
#plt.xlim(-2, 2)
#plt.ylim(-2, 2)

# -- the same stuff for the convex hull

plt.show()


# @author: Gautam Patel
# Problem Description URL: https://www.hackerrank.com/challenges/super-reduced-string/problem
#!/bin/python3

import math
import os
import random
import re
import sys
from itertools import groupby

# Complete the superReducedString function below.
def superReducedString(s):
    old_s = s
    new_s = ''
    while True:
        new_s = ''
        for k, g in groupby(old_s):
            if len(list(g))%2 == 1:
                new_s += k
        if new_s == old_s:
            break
        old_s = new_s
    return (new_s if len(new_s) > 0 else 'Empty String')

if __name__ == '__main__':
    fptr = open(os.environ['OUTPUT_PATH'], 'w')

    s = input()

    result = superReducedString(s)

    fptr.write(result + '\n')

    fptr.close()


import os
import argparse
import json
import numpy as np
import torch

from utils.util import get_mask_mnr, get_mask_bm, get_mask_rm
from utils.util import find_max_epoch, print_size, sampling, calc_diffusion_hyperparams

from imputers.DiffWaveImputer import DiffWaveImputer
from imputers.SSSDSAImputer import SSSDSAImputer
from imputers.SSSDS4Imputer import SSSDS4Imputer

from sklearn.metrics import mean_squared_error
from statistics import mean

def generate(output_directory,
             num_samples,
             ckpt_path,
             data_path,
             ckpt_iter,
             use_model,
             masking,
             missing_k,
             only_generate_missing):

    """
    Generate data based on ground truth

    Parameters:
    output_directory (str):           save generated speeches to this path
    num_samples (int):                number of samples to generate, default is 4
    ckpt_path (str):                  checkpoint path
    ckpt_iter (int or 'max'):         the pretrained checkpoint to be loaded;
                                      automitically selects the maximum iteration if 'max' is selected
    data_path (str):                  path to dataset, numpy array.
    use_model (int):                  0:DiffWave. 1:SSSDSA. 2:SSSDS4.
    masking (str):                    'mnr': missing not at random, 'bm': black-out, 'rm': random missing
    only_generate_missing (int):      0:all sample diffusion.  1:only apply diffusion to missing portions of the signal
    missing_k (int)                   k missing time points for each channel across the length.
    """

    # generate experiment (local) path
    local_path = "T{}_beta0{}_betaT{}".format(diffusion_config["T"],
                                              diffusion_config["beta_0"],
                                              diffusion_config["beta_T"])

    # Get shared output_directory ready
    output_directory = os.path.join(output_directory, local_path)
    if not os.path.isdir(output_directory):
        os.makedirs(output_directory)
        os.chmod(output_directory, 0o775)
    print("output directory", output_directory, flush=True)

    # map diffusion hyperparameters to gpu
    for key in diffusion_hyperparams:
        if key != "T":
            diffusion_hyperparams[key] = diffusion_hyperparams[key].cuda()

    # predefine model
    if use_model == 0:
        net = DiffWaveImputer(**model_config).cuda()
    elif use_model == 1:
        net = SSSDSAImputer(**model_config).cuda()
    elif use_model == 2:
        net = SSSDS4Imputer(**model_config).cuda()
    else:
        print('Model chosen not available.')
    print_size(net)

    # load checkpoint
    ckpt_path = os.path.join(ckpt_path, local_path)
    if ckpt_iter == 'max':
        ckpt_iter = find_max_epoch(ckpt_path)
    model_path = os.path.join(ckpt_path, '{}.pkl'.format(ckpt_iter))
    try:
        checkpoint = torch.load(model_path, map_location='cpu')
        net.load_state_dict(checkpoint['model_state_dict'])
        print('Successfully loaded model at iteration {}'.format(ckpt_iter))
    except:
        raise Exception('No valid model found')

    ### Custom data loading and reshaping ###

    testing_data = np.load(trainset_config['test_data_path'])
    testing_data = np.split(testing_data, 4, 0)
    testing_data = np.array(testing_data)
    testing_data = torch.from_numpy(testing_data).float().cuda()
    print('Data loaded')

    all_mse = []

    for i, batch in enumerate(testing_data):

        if masking == 'mnr':
            mask_T = get_mask_mnr(batch[0], missing_k)
            mask = mask_T.permute(1, 0)
            mask = mask.repeat(batch.size()[0], 1, 1)
            mask = mask.type(torch.float).cuda()

        elif masking == 'bm':
            mask_T = get_mask_bm(batch[0], missing_k)
            mask = mask_T.permute(1, 0)
            mask = mask.repeat(batch.size()[0], 1, 1)
            mask = mask.type(torch.float).cuda()

        elif masking == 'rm':
            mask_T = get_mask_rm(batch[0], missing_k)
            mask = mask_T.permute(1, 0)
            mask = mask.repeat(batch.size()[0], 1, 1).float().cuda()

        batch = batch.permute(0,2,1)

        start = torch.cuda.Event(enable_timing=True)
        end = torch.cuda.Event(enable_timing=True)
        start.record()

        sample_length = batch.size(2)
        sample_channels = batch.size(1)
        generated_audio = sampling(net, (num_samples, sample_channels, sample_length),
                                   diffusion_hyperparams,
                                   cond=batch,
                                   mask=mask,
                                   only_generate_missing=only_generate_missing)

        end.record()
        torch.cuda.synchronize()

        print('generated {} utterances of random_digit at iteration {} in {} seconds'.format(num_samples,
                                                                                ckpt_iter,
                                                                                int(start.elapsed_time(
                                                                                end) / 1000)))

        generated_audio = generated_audio.detach().cpu().numpy()
        batch = batch.detach().cpu().numpy()
        mask = mask.detach().cpu().numpy()

        outfile = f'imputation{i}.npy'
        new_out = os.path.join(ckpt_path, outfile)
        np.save(new_out, generated_audio)

        outfile = f'original{i}.npy'
        new_out = os.path.join(ckpt_path, outfile)
        np.save(new_out, batch)

        outfile = f'mask{i}.npy'
        new_out = os.path.join(ckpt_path, outfile)
        np.save(new_out, mask)

        print('saved generated samples at iteration %s' % ckpt_iter)

        mse = mean_squared_error(generated_audio[~mask.astype(bool)], batch[~mask.astype(bool)])
        all_mse.append(mse)

    print('Total MSE:', mean(all_mse))

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--config', type=str, default='config.json',
                        help='JSON file for configuration')
    parser.add_argument('-ckpt_iter', '--ckpt_iter', default='max',
                        help='Which checkpoint to use; assign a number or "max"')
    parser.add_argument('-n', '--num_samples', type=int, default=500,
                        help='Number of utterances to be generated')
    args = parser.parse_args()

    # Parse configs. Globals nicer in this case
    with open(args.config) as f:
        data = f.read()
    config = json.loads(data)
    print(config)

    gen_config = config['gen_config']

    train_config = config["train_config"]  # training parameters

    global trainset_config
    trainset_config = config["trainset_config"]  # to load trainset

    global diffusion_config
    diffusion_config = config["diffusion_config"]  # basic hyperparameters

    global diffusion_hyperparams
    diffusion_hyperparams = calc_diffusion_hyperparams(
        **diffusion_config)  # dictionary of all diffusion hyperparameters

    global model_config
    if train_config['use_model'] == 0:
        model_config = config['wavenet_config']
    elif train_config['use_model'] == 1:
        model_config = config['sashimi_config']
    elif train_config['use_model'] == 2:
        model_config = config['wavenet_config']

    generate(**gen_config,
             ckpt_iter=args.ckpt_iter,
             num_samples=args.num_samples,
             use_model=train_config["use_model"],
             data_path=trainset_config["test_data_path"],
             masking=train_config["masking"],
             missing_k=train_config["missing_k"],
             only_generate_missing=train_config["only_generate_missing"])