from PyQt5 import QtWidgets, uic
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication
from glview import *
from glview3D import *
from plot import *
from input import *
from subprocess import PIPE, Popen
import math
import os
import numpy as np
import sys
import pyautogui
import traceback
import secrets

np.seterr(divide='ignore', invalid='ignore')
resolution = pyautogui.size()
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
app = QtWidgets.QApplication(sys.argv)

default_input = input_file_setup("config/benzene.out", "config/attributes.txt", "config/benzene.wf")

class MainWindow(QtWidgets.QMainWindow):

    def __init__(self, screen_width, atoms=None):
        super(MainWindow, self).__init__()

        # Load the UI Page
        uic.loadUi("config/mainwindow5.ui", self)
        self.setWindowTitle("Platomic")
        self.setWindowIcon(QIcon("config/platomic.png"))
        self.multiplier = int(screen_width / 1920)

        # Initialise state
        self.atoms = atoms
        self.csvFilename = None
        self.inputFilename = None
        self.transInputFilename = None
        self.currInputFilename = None
        self.transSelected = {"1": [], "2": [], "3": [], "4": [], "5": []}
        self.currentSelectedA = []
        self.currentSelectedB = []
        self.mode = 0
        self.id = secrets.token_hex(3)

        # Initialise propertiesWindow
        # setupSettingsTab
        # executeButton
        self.executeButton.clicked.connect(self.onExecuteButtonClicked)

        # executeTransButton
        self.executeTransButton.clicked.connect(self.onTransExecuteButtonClicked)

        # executeCurrButton and executeCurrGraphButton
        self.executeCurrButton.clicked.connect(self.onExecuteCurrButtonClicked)
        self.executeCurrGraphButton.clicked.connect(self.onExecuteCurrGraphButtonClicked)
        self.execute3DGraphButton.clicked.connect(self.onExecute3DGraphButtonClicked)

        # executeLoadedButton and transExecuteLoadedButton
        self.executeLoadedButton.clicked.connect(self.onExecuteLoadedButtonClicked)
        self.transExecuteLoadedButton.clicked.connect(self.onTransExecuteLoadedButtonClicked)
        self.currExecuteLoadedButton.clicked.connect(self.onCurrExecuteLoadedButtonClicked)
        self.gammaExecuteLoadedButton.clicked.connect(self.onGammaExecuteLoadedButtonClicked)

        # generateInputFileButton
        self.generateInputFileButton.clicked.connect(self.onGenerateInputFileButtonClicked)

        # generateTransInputFileButton
        self.generateTransInputFileButton.clicked.connect(self.onGenerateTransInputFileButtonClicked)

        # generateCurrInputFileButton
        self.generateCurrInputFileButton.clicked.connect(self.onGenerateCurrInputFileButtonClicked)

        # openFileLineEdit
        # openFileButton
        self.openFileButton.clicked.connect(self.onOpenFileButtonClicked)
        self.openOutFileButton.clicked.connect(self.onOpenOutFileButtonClicked)
        self.openOutFileButton2.clicked.connect(self.onOpenOutFileButtonClicked2)
        self.openWfFileButton.clicked.connect(self.onOpenWfFileButtonClicked)
        self.openCsvFileButton.clicked.connect(self.onOpenCsvFileButtonClicked)
        self.openDirButton.clicked.connect(self.onOpenDirButtonClicked)
        self.openDirGammaButton.clicked.connect(self.onOpenDirGammaButtonClicked)
        self.gammaLineEdit.editingFinished.connect(self.onGammaLineEditChanged)
        self.gammaLineEdit2.editingFinished.connect(self.onGammaLineEditChanged2)
        self.gammaStartLineEdit.editingFinished.connect(self.onGammaStartLineEditChanged)
        self.gammaEndLineEdit.editingFinished.connect(self.onGammaEndLineEditChanged)
        self.gammaStepsLineEdit.editingFinished.connect(self.onGammaStepsLineEditChanged)
        self.excessLineEdit.editingFinished.connect(self.onExcessLineEditChanged)
        self.referenceLineEdit.editingFinished.connect(self.onReferenceLineEditChanged)
        self.biasLineEdit.editingFinished.connect(self.onBiasLineEditChanged)
        self.stepsLineEdit.editingFinished.connect(self.onStepsLineEditChanged)
        self.offsetLineEdit.editingFinished.connect(self.onOffsetLineEditChanged)

        # switchToInputFileTabButton
        self.switchToInputFileTabButton.clicked.connect(self.onSwitchToInputFileTabButtonClicked)

        # checkBoxIndex
        # checkBoxSymbol
        # checkBoxPosition
        # checkBoxRadius
        # fontComboBox
        # sizeComboBox
        # offsetComboBox
        # colourComboBox
        self.checkBoxIndex.stateChanged.connect(self.setCheckBoxIndex)
        self.checkBoxSymbol.stateChanged.connect(self.setCheckBoxSymbol)
        self.checkBoxPosition.stateChanged.connect(self.setCheckBoxPosition)
        self.checkBoxRadius.stateChanged.connect(self.setCheckBoxRadius)

        self.fontComboBox.currentIndexChanged.connect(self.setFontComboBox)
        self.sizeComboBox.currentIndexChanged.connect(self.setSizeComboBox)
        self.offsetComboBox.currentIndexChanged.connect(self.setOffsetComboBox)
        self.colourComboBox.currentIndexChanged.connect(self.setColourComboBox)

        self.fontComboBox.addItems(["Arial", "Cambria", "Helvetica", "Times New Roman"])
        self.sizeComboBox.addItems(["14", "16", "18", "20", "24", "30", "42"])
        self.offsetComboBox.addItems(["X", "Y", "Z"])
        self.colourComboBox.addItems(["Orange", "Red", "Lime", "Blue", "Purple", "Yellow"])
        self.openGLWidget.font = "Arial"
        self.openGLWidget.size = 14
        self.openGLWidget.offset = 0
        self.openGLWidget.colour = "Orange"

        # graphSettingsTab and terminalComboBox
        self.graphComboBox.currentIndexChanged.connect(self.setGraphComboBox)
        self.terminalComboBox.currentIndexChanged.connect(self.setTerminalComboBox)
        self.terminalComboBox.addItems(["Terminal 1", "Terminal 2", "Terminal 3", "Terminal 4", "Terminal 5"])
        self.graphKeys = None
        self.offset = 0

        # atomSettingsTab
        # atomColSlider
        self.atomCol = self.atomColSlider.value()
        # atomColSliderLabel
        self.atomColSlider.sliderReleased.connect(self.setAtomColSliderLabel)
        self.atomColSlider.valueChanged.connect(self.updateAtomColSliderLabel)

        # atomRowSlider
        self.atomRow = self.atomRowSlider.value()
        # atomRowSliderLabel
        self.atomRowSlider.sliderReleased.connect(self.setAtomRowSliderLabel)
        self.atomRowSlider.valueChanged.connect(self.updateAtomRowSliderLabel)

        # bondColSlider
        self.bondCol = self.bondColSlider.value()
        # bondColSliderLabel
        self.bondColSlider.sliderReleased.connect(self.setBondColSliderLabel)
        self.bondColSlider.valueChanged.connect(self.updateBondColSliderLabel)

        # bondRowSlider
        self.bondRow = self.bondRowSlider.value()
        # bondRowSliderLabel
        self.bondRowSlider.sliderReleased.connect(self.setBondRowSliderLabel)
        self.bondRowSlider.valueChanged.connect(self.updateBondRowSliderLabel)

        # brightnessSlider
        # brightnessSliderLabel
        self.brightnessSlider.sliderReleased.connect(self.setBrightnessSliderLabel)
        self.brightnessSlider.valueChanged.connect(self.updateBrightnessSliderLabel)

        # bondRadiusSlider
        self.bondRadius = 0.15
        # bondRadiusSliderLabel
        self.bondRadiusSlider.sliderReleased.connect(self.setBondRadiusSliderLabel)
        self.bondRadiusSlider.valueChanged.connect(self.updateBondRadiusSliderLabel)

        # bondThresholdSlider
        self.bondThreshold = 5.0
        # bondThersholdSliderlabel
        self.bondThresholdSlider.sliderReleased.connect(self.setBondThresholdSliderLabel)
        self.bondThresholdSlider.valueChanged.connect(self.updateBondThresholdSliderLabel)

        # switchToAttrFileTabButton
        self.switchToAttrFileTabButton.clicked.connect(self.onSwitchToAttrFileTabButtonClicked)

        # orbitalSettingsTab
        # advOrbWfCheckBox
        self.advOrbWfCheckBox.stateChanged.connect(self.draw)

        # advOrbHorzCheckBox
        self.advOrbHorzCheckBox.stateChanged.connect(self.draw)

        # advOrbVertCheckBox
        self.advOrbVertCheckBox.stateChanged.connect(self.draw)

        # sphOrbWfCheckBox
        self.sphOrbWfCheckBox.stateChanged.connect(self.draw)

        # sphOrbFacesCheckBox
        self.sphOrbFacesCheckBox.stateChanged.connect(self.draw)

        # advOrbFacesCheckBox
        self.advOrbFacesCheckBox.stateChanged.connect(self.draw)

        # orbColSlider
        self.orbCol = self.orbColSlider.value()
        # orbColSliderLabel
        self.orbColSlider.sliderReleased.connect(self.setOrbColSliderLabel)
        self.orbColSlider.valueChanged.connect(self.updateOrbColSliderLabel)

        # orbRowSlider
        self.orbRow = self.orbRowSlider.value()
        # orbRowSliderLabel
        self.orbRowSlider.sliderReleased.connect(self.setOrbRowSliderLabel)
        self.orbRowSlider.valueChanged.connect(self.updateOrbRowSliderLabel)

        # orbScalerSlider
        self.orbScaler = self.orbScalerSlider.value()
        # orbScalerSliderLabel
        self.orbScalerSlider.sliderReleased.connect(self.setScalerSliderLabel)
        self.orbScalerSlider.valueChanged.connect(self.updateScalerSliderLabel)

        # thetaSlider
        self.theta = self.thetaSlider.value()
        # thetaSliderLabel
        self.thetaSlider.sliderReleased.connect(self.setThetaSliderLabel)
        self.thetaSlider.valueChanged.connect(self.updateThetaSliderLabel)

        # phiSlider
        self.phi = math.radians(self.phiSlider.value())
        # phiSliderLabel
        self.phiSlider.sliderReleased.connect(self.setPhiSliderLabel)
        self.phiSlider.valueChanged.connect(self.updatePhiSliderLabel)

        # colourXSlider
        self.R = 1.00
        self.G = 0.00
        self.B = 1.00
        self.A = 0.50
        self.colourRSlider.sliderReleased.connect(self.setColourRSliderLabel)
        self.colourGSlider.sliderReleased.connect(self.setColourGSliderLabel)
        self.colourBSlider.sliderReleased.connect(self.setColourBSliderLabel)
        self.colourASlider.sliderReleased.connect(self.setColourASliderLabel)
        self.colourRSlider.valueChanged.connect(self.updateColourRSliderLabel)
        self.colourGSlider.valueChanged.connect(self.updateColourGSliderLabel)
        self.colourBSlider.valueChanged.connect(self.updateColourBSliderLabel)
        self.colourASlider.valueChanged.connect(self.updateColourASliderLabel)

        # Initialise mainWindow

        # mainDisplayTab
        # horizontalSlider
        # horizontalSliderLabel
        self.horizontalSlider.sliderReleased.connect(self.setHorizontalSliderLabel)
        self.horizontalSlider.valueChanged.connect(self.updateHorizontalSliderLabel)

        # openGLWidget and gammaGLWidget
        self.openGLWidget.opts['distance'] = 15
        self.gammaGLWidget.opts['distance'] = 5
        self.openGLWidget.multiplier = self.multiplier
        self.gammaGLWidget.multiplier = self.multiplier
        if self.atoms is None:
            self.openGLWidget.atoms = [
                Atom("0", 0, -9, 0,
                     "Welcome to Platomic. To get started, import an .xyz file in the 'Setup' tab."),
                Atom("0", 0, -9.25, -1, "Hover over any (?) icons for help and / or additional information."),
                Atom("0", 0, -9.5, -2, "For a full in-depth tutorial check out the User Guide in the doc/ directory.")]
        else:
            self.openGLWidget.atoms = self.atoms
            self.draw()
        self.backgroundColor = (40, 40, 40)
        self.openGLWidget.setBackgroundColor(self.backgroundColor)
        self.openGLWidget.left_clicked.connect(self.onTransSelection)
        self.openGLWidget.right_clicked.connect(self.onCurrentSelectionA)
        self.openGLWidget.middle_clicked.connect(self.onCurrentSelectionB)

        # resetViewButton
        self.resetViewButton.clicked.connect(self.onResetViewButtonClicked)

        # saveImageButton, save3DImageButton
        self.saveImageButton.clicked.connect(self.onSaveImageButtonClicked)
        self.save3DImageButton.clicked.connect(self.onSave3DImageButtonClicked)

        # toggleAtomsButton
        self.toggleAtomsButton.clicked.connect(self.onToggleAtomsButtonClicked)

        # inputFileTab
        # inputTextEdit
        # saveInputFileButton
        self.saveInputFileButton.clicked.connect(self.onSaveInputFileButtonClicked)

        # AttributeFileTab
        # attributeTextEdit
        with open("config/attributes.txt", "r") as f:
            contents = f.readlines()
        for line, content in enumerate(contents):
            self.attributeTextEdit.insertPlainText(content)

        # saveAttributeFileButton
        self.saveAttributeFileButton.clicked.connect(self.onSaveAttributeFileButtonClicked)

        # fullConsoleTab
        # fullConsoleTextEdit

    # Initialise functions
    # Initialise propertiesWindow
    # setupSettingsTab
    # executeButton

    def execute(self, verbose=True):
        if os.name == 'nt':
            self.writeErrorToLogs("Plato back-end execution is not supported on Windows systems.")
            return False
        try:
            command = "(cd ./Plato/bin && ./tb1 ../../" + self.inputFilename + ")"
        except TypeError:
            self.writeErrorToLogs("No Plato input file found, click generate before clicking execute.")
            return False
        result = Popen(command, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True, encoding='utf-8',
                       errors='replace')
        while True:
            output = result.stdout.readline()
            if output == "" and result.poll() is not None:
                break
            if output:
                self.writeToLogs(output.strip(), "black")
                QApplication.processEvents()
        if result.returncode and verbose:
            return False
        return True

    def onExecuteButtonClicked(self):
        self.writeToLogs("Starting execution.", "green")
        QApplication.processEvents()
        if not self.execute():
            return
        self.atoms = input_file_setup(self.inputFilename + ".out", "config/attributes.txt", self.inputFilename + ".wf")
        self.horizontalSlider.setMinimum(0)
        self.horizontalSlider.setMaximum(self.atoms[0].get_total_orbitals() - 1)
        self.openGLWidget.atoms = self.atoms
        self.draw()
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.mainDisplayTab))
        self.propertiesWindow.setCurrentIndex(self.propertiesWindow.indexOf(self.displaySettingsTab))
        self.writeToLogs("Execution carried out successfully.\n", "green")
        self.transSelected = {"1": [], "2": [], "3": [], "4": [], "5": []}
        self.currentSelectedA = []
        self.currentSelectedB = []
        self.executeButton.setEnabled(False)
        self.id = secrets.token_hex(3)

    def onTransExecuteButtonClicked(self):
        self.writeToLogs("Starting transmission execution.", "green")
        QApplication.processEvents()
        if not self.execute():
            return
        self.writeToLogs("Execution carried out successfully.", "green")
        self.csvFilename = self.inputFilename + "_trans.csv"
        headers_mapped, headers = transmission_headers(self.csvFilename, self.transSelected)
        self.offset = find_chemical_potential(self.inputFilename + ".out")
        self.offsetLineEdit.setText(str(self.offset))
        self.graphKeys = headers
        self.graphComboBox.clear()
        self.graphComboBox.addItems(headers_mapped)
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.graphTab))
        self.propertiesWindow.setCurrentIndex(self.propertiesWindow.indexOf(self.graphSettingsTab))
        self.writeToLogs("Graph offset set to " + str(self.offset) + ".", "green")
        self.writeToLogs("Graphs plotted successfully.\n", "green")
        self.executeTransButton.setEnabled(False)
        self.id = secrets.token_hex(3)

    def onExecuteCurrButtonClicked(self, boolean, verbose=True):
        self.writeToLogs("Starting current execution.", "green")
        QApplication.processEvents()
        if not self.execute(verbose):
            return
        current = find_current_in_file(self.inputFilename + ".out")
        if verbose:
            self.writeToLogs("Execution carried out successfully.", "green")
            self.writeToLogs("Current: " + current + " mA.\n", "green")
        self.executeCurrButton.setEnabled(False)
        self.id = secrets.token_hex(3)
        return float(current)

    def onExecuteCurrGraphButtonClicked(self):
        self.writeToLogs("Starting current graph execution.", "green")
        try:
            steps = int(self.stepsLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for steps.")
            return
        try:
            bias = float(self.biasLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for maximum bias.")
            return

        occupied_keys = return_occupied_keys(self.transSelected)
        if not occupied_keys == 2:
            self.writeErrorToLogs(
                "Error: Incorrect number of terminals selected, may only calculate current between two terminals.")
            return
        if len(self.currentSelectedA) <= 0:
            self.writeErrorToLogs(
                "Error: Insufficient atoms for region A (min. one required). Select atoms for A by right clicking.")
            return
        if len(self.currentSelectedB) <= 0:
            self.writeErrorToLogs(
                "Error: Insufficient atoms for region B (min. one required). Select atoms for B by middle clicking.")
            return
        currents = []
        biases = np.linspace(0, bias, steps)
        self.writeToLogs("Starting " + str(steps) + " current calculations.", "green")
        self.id = secrets.token_hex(3)
        ind = 1
        for i in biases:
            bias_i = round(i, 4)
            if not self.onGenerateCurrInputFileButtonClicked(False, False, bias=bias_i):
                return
            currents.append(self.onExecuteCurrButtonClicked(False, False))
            self.writeToLogs(str(ind) + "/" + str(steps) + " transmission calculation completed.", "green")
            QApplication.processEvents()
            ind += 1
        self.writeToLogs("All current calculations completed successfully.", "green")
        current_graph(self.graphWidget2, biases, np.array(currents) / 1000)
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.graphTab2))
        # self.propertiesWindow.setCurrentIndex(self.propertiesWindow.indexOf(self.graphSettingsTab))
        self.writeToLogs("Current vs. bias graph plotted successfully.\n", "green")

    def onExecute3DGraphButtonClicked(self):
        self.writeToLogs("Starting 3D transmission graph execution.", "green")
        self.gammaGLWidget.clear()
        occupied_keys = return_occupied_keys(self.transSelected)
        if not occupied_keys == 2:
            self.writeErrorToLogs(
                "Error: Incorrect number of terminals selected, may only plot 3D graph between two terminals.")
            return
        try:
            gamma_start = float(self.gammaStartLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for gamma minimum.")
            return
        try:
            gamma_end = float(self.gammaEndLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for gamma maximum.")
            return
        try:
            gamma_steps = int(self.gammaStepsLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for gamma steps.")
            return
        interval = (gamma_end - gamma_start) / gamma_steps
        if interval <= 0:
            self.writeErrorToLogs(
                "Error: Difference between Gamma minimum and maximum must be positive and greater than 0.")
            return
        self.writeToLogs("Starting " + str(gamma_steps) + " transmission calculations.", "green")
        self.id = secrets.token_hex(3)
        i = 1
        for gamma in np.linspace(gamma_start, gamma_end, gamma_steps):
            if not self.onGenerateTransInputFileButtonClicked(verbose=False, gamma=round(gamma, 5), step_size=interval):
                return
            self.execute(verbose=False)
            self.writeToLogs(str(i) + "/" + str(gamma_steps) + " transmission calculation completed.", "green")
            QApplication.processEvents()
            i += 1
        self.writeToLogs("All transmission calculations completed successfully.", "green")
        energy, gamma, transmission = process_energy_gamma_trans_csv(".", self.id)
        energy_gamma_trans_graph(self.gammaGLWidget, energy, gamma, transmission)
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.gammaGraphTab))
        self.propertiesWindow.setCurrentIndex(self.propertiesWindow.indexOf(self.graphSettingsTab))
        self.writeToLogs("Energy vs. gamma vs. transmission graph plotted successfully.\n", "green")

    # generateInputFileButton

    def replaceTextEdit(self, filename):
        self.inputTextEdit.clear()
        with open(filename + ".in", "r") as f:
            contents = f.readlines()
        for line, content in enumerate(contents):
            self.inputTextEdit.insertPlainText(content)

    def onGenerateInputFileButtonClicked(self):
        try:
            excess = float(self.excessLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for excess electrons.")
            return
        try:
            filename = xyz_to_plato_input(self.openFileLineEdit.text(), excess, self.id)
            self.inputFilename = filename
            self.replaceTextEdit(filename)
        except FileNotFoundError:
            self.writeErrorToLogs("Error: No default input file found, check that config/default.in exists.")
            return
        except IOError:
            self.writeErrorToLogs("Error: No .xyz file selected to generate Plato input file.")
            return
        self.writeToLogs("Input file " + self.inputFilename + ".in generated successfully.\n", "green")
        self.executeButton.setEnabled(True)

    def onGenerateTransInputFileButtonClicked(self, boolean=False, verbose=True, gamma=None, step_size=0.003):
        if gamma is None:
            try:
                gamma = float(self.gammaLineEdit.text())
            except ValueError:
                self.writeErrorToLogs("Error: Missing input for gamma.")
                return False
        try:
            excess = float(self.excessLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for excess electrons.")
            return
        try:
            filename = trans_plato_input(self.openFileLineEdit.text(), self.transSelected, excess, gamma, step_size,
                                         self.id)
            self.inputFilename = filename
            if verbose:
                self.replaceTextEdit(filename)
        except FileNotFoundError:
            self.writeErrorToLogs(
                "Error: No default input file found, check that config/default_trans.in exists.")
            return False
        except IOError:
            self.writeErrorToLogs("Error: No .xyz file selected to generate Plato input file.")
            return False
        except AssertionError:
            self.writeErrorToLogs(
                "Error: Insufficient terminals selected (min. two required). Select terminals by left clicking atoms.")
            return False
        if verbose:
            self.writeToLogs("Transmission input file " + self.inputFilename + ".in generated successfully.\n", "green")
            self.executeTransButton.setEnabled(True)
        return True

    def onGenerateCurrInputFileButtonClicked(self, boolean, verbose=True, bias=None, step_size=0.003):
        try:
            gamma = float(self.gammaLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for gamma.")
            return False
        if bias is None:
            try:
                bias = float(self.biasLineEdit.text())
            except ValueError:
                self.writeErrorToLogs("Error: Missing input for bias.")
                return False
        try:
            reference_pot = float(self.referenceLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for reference potential.")
            return
        try:
            excess = float(self.excessLineEdit.text())
        except ValueError:
            self.writeErrorToLogs("Error: Missing input for excess electrons.")
            return
        try:
            filename = curr_plato_input(self.openFileLineEdit.text(), self.transSelected, self.currentSelectedA,
                                        self.currentSelectedB, excess, reference_pot, bias, gamma,
                                        step_size, self.id)
            self.inputFilename = filename
            self.replaceTextEdit(filename)
        except FileNotFoundError:
            self.writeErrorToLogs(
                "Error: No default current input file found, check that config/default_curr.in exists.")
            return False
        except IOError:
            self.writeErrorToLogs("Error: No .xyz file selected to generate Plato input file.")
            return False
        except AssertionError:
            self.writeErrorToLogs(
                "Error: Insufficient terminals selected (two required). Select terminals by left clicking atoms.")
            return False
        except NotImplementedError:
            self.writeErrorToLogs(
                "Error: Incorrect number of terminals selected, may only calculate current between two terminals.")
            return False
        except ValueError:
            self.writeErrorToLogs(
                "Error: Insufficient atoms for region A (min. one required). Select atoms for A by right clicking.")
            return False
        except ZeroDivisionError:
            self.writeErrorToLogs(
                "Error: Insufficient atoms for region B (min. one required). Select atoms for B by middle clicking.")
            return False
        if verbose:
            self.writeToLogs("Current input file " + self.inputFilename + ".in generated successfully.\n", "green")
            self.executeCurrButton.setEnabled(True)
        return True

        # openFileButton
        # openFileLineEdit

    def onOpenFileButtonClicked(self):
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(parent=self, caption='Open xyz file',
                                                            filter="XYZ File (*.xyz);;All Files (*.*)")

        if filename:
            self.openFileLineEdit.setText(filename)

    def onOpenOutFileButtonClicked(self):
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(parent=self, caption='Open output file',
                                                            filter="Output File (*.out);;All Files (*.*)")

        if filename:
            self.openOutFileLineEdit.setText(filename)

    def onOpenOutFileButtonClicked2(self):
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(parent=self, caption='Open output file',
                                                            filter="Output File (*.out);;All Files (*.*)")

        if filename:
            self.openOutFileLineEdit2.setText(filename)

    def onOpenWfFileButtonClicked(self):
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(parent=self, caption='Open wavefunction file',
                                                            filter="Wavefunction File (*.wf);;All Files (*.*)")
        if filename:
            self.openWfFileLineEdit.setText(filename)

    def onOpenCsvFileButtonClicked(self):
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(parent=self, caption='Open transmission csv file',
                                                            filter="CSV File (*.csv);;All Files (*.*)")

        if filename:
            self.openCsvFileLineEdit.setText(filename)

    def onOpenDirButtonClicked(self):
        dirname = QtWidgets.QFileDialog.getExistingDirectory(parent=self, caption='Select directory')

        if dirname:
            self.openDirLineEdit.setText(dirname)

    def onOpenDirGammaButtonClicked(self):
        dirname = QtWidgets.QFileDialog.getExistingDirectory(parent=self, caption='Select directory')

        if dirname:
            self.gammaOpenDirLineEdit.setText(dirname)

    def onExecuteLoadedButtonClicked(self):
        if self.openOutFileLineEdit.text() == "":
            self.writeErrorToLogs("Error: no Plato output file (.out) selected.")
            return
        if self.openWfFileLineEdit.text() == "":
            self.writeErrorToLogs("Error: no Plato wavefunction file (.wf) selected.")
            return
        self.atoms = input_file_setup(self.openOutFileLineEdit.text(), "config/attributes.txt",
                                      self.openWfFileLineEdit.text())
        self.horizontalSlider.setMinimum(0)
        self.horizontalSlider.setMaximum(self.atoms[0].get_total_orbitals() - 1)
        self.openGLWidget.atoms = self.atoms
        self.draw()
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.mainDisplayTab))
        self.propertiesWindow.setCurrentIndex(self.propertiesWindow.indexOf(self.displaySettingsTab))
        self.writeToLogs("Execution carried out successfully.\n", "green")
        self.transSelected = {"1": [], "2": [], "3": [], "4": [], "5": []}
        self.currentSelectedA = []
        self.currentSelectedB = []

    def onTransExecuteLoadedButtonClicked(self):
        if self.openCsvFileLineEdit.text() == "":
            self.writeErrorToLogs("Error: no Plato generated csv file (.csv) selected.")
            return
        self.csvFilename = self.openCsvFileLineEdit.text()
        headers_mapped, headers = transmission_headers(self.csvFilename, self.transSelected)
        if self.openOutFileLineEdit2.text() == "":
            self.offset = 0
        else:
            self.offset = find_chemical_potential(self.openOutFileLineEdit2.text())
        self.offsetLineEdit.setText(str(self.offset))
        self.writeToLogs("Graph offset set to " + str(self.offset) + ".", "green")
        self.graphKeys = headers
        self.graphComboBox.clear()
        self.graphComboBox.addItems(headers_mapped)
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.graphTab))
        self.propertiesWindow.setCurrentIndex(self.propertiesWindow.indexOf(self.graphSettingsTab))
        self.writeToLogs("Graphs plotted successfully.\n", "green")

    def onCurrExecuteLoadedButtonClicked(self):
        if self.openDirLineEdit.text() == "":
            self.writeErrorToLogs("Error: no directory selected.")
            return
        bias_v, bias, currents = process_current_out(self.openDirLineEdit.text())
        self.writeToLogs("Bias from directory determined to be " + bias_v + ".", "green")
        current_graph(self.graphWidget2, bias, np.array(currents) / 1000)
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.graphTab2))
        self.writeToLogs("Current vs. bias graph plotted successfully.\n", "green")

    def onGammaExecuteLoadedButtonClicked(self):
        self.gammaGLWidget.clear()
        if self.gammaOpenDirLineEdit.text() == "":
            self.writeErrorToLogs("Error: no directory selected.")
            return
        energy, gamma, transmission = process_energy_gamma_trans_csv(self.gammaOpenDirLineEdit.text(), None)
        energy_gamma_trans_graph(self.gammaGLWidget, energy, gamma, transmission)
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.gammaGraphTab))
        self.propertiesWindow.setCurrentIndex(self.propertiesWindow.indexOf(self.graphSettingsTab))
        self.writeToLogs("Energy vs. gamma vs. transmission graph plotted successfully.\n", "green")

    # SwitchToInputFileTabButton

    def onSwitchToInputFileTabButtonClicked(self):
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.inputFileTab))

        # graphSettingsTab

    def setGraphComboBox(self):
        transmission_graph(self.graphWidget, self.csvFilename, self.graphKeys[self.graphComboBox.currentIndex()],
                           self.offset)

    def setTerminalComboBox(self):
        self.openGLWidget.terminal = self.terminalComboBox.currentIndex() + 1

        # atomSettingsTab
        # atomColSlider
        # atomColSliderLabel

    def setAtomColSliderLabel(self):
        value = self.atomColSlider.value()
        self.atomCol = value
        self.draw()

    def updateAtomColSliderLabel(self):
        value = self.atomColSlider.value()
        self.atomColSliderLabel.setText("Columns: " + str(value))

        # atomRowSlider
        # atomRowSliderLabel

    def setAtomRowSliderLabel(self):
        value = self.atomRowSlider.value()
        self.atomRow = value
        self.draw()

    def updateAtomRowSliderLabel(self):
        value = self.atomRowSlider.value()
        self.atomRowSliderLabel.setText("Rows: " + str(value))

        # bondColSlider
        # bondColSliderLabel

    def setBondColSliderLabel(self):
        value = self.bondColSlider.value()
        self.bondCol = value
        self.draw()

    def updateBondColSliderLabel(self):
        value = self.bondColSlider.value()
        self.bondColSliderLabel.setText("Columns: " + str(value))

        # bondRowSlider
        # bondRowSliderLabel

    def setBondRowSliderLabel(self):
        value = self.bondRowSlider.value()
        self.bondRow = value
        self.draw()

    def updateBondRowSliderLabel(self):
        value = self.bondRowSlider.value()
        self.bondRowSliderLabel.setText("Rows: " + str(value))

        # brightnessSlider
        # brightnessSliderLabel

    def setBrightnessSliderLabel(self):
        value = self.brightnessSlider.value()
        self.backgroundColor = (value, value, value)
        self.openGLWidget.setBackgroundColor(self.backgroundColor)

    def updateBrightnessSliderLabel(self):
        value = self.brightnessSlider.value()
        self.brightnessSliderLabel.setText("Brightness: " + str(value))

        # checkBoxIndex
        # checkBoxSymbol
        # checkBoxPosition
        # checkBoxRadius
        # fontComboBox
        # sizeComboBox
        # offsetComboBox
        # colourComboBox

    def setCheckBoxIndex(self, state):
        self.openGLWidget.index = state
        self.openGLWidget.update()

    def setCheckBoxSymbol(self, state):
        self.openGLWidget.symbol = state
        self.openGLWidget.update()

    def setCheckBoxPosition(self, state):
        self.openGLWidget.position = state
        self.openGLWidget.update()

    def setCheckBoxRadius(self, state):
        self.openGLWidget.radius = state
        self.openGLWidget.update()

    def setFontComboBox(self):
        self.openGLWidget.font = self.fontComboBox.currentText()
        self.openGLWidget.update()

    def setSizeComboBox(self):
        self.openGLWidget.size = int(self.sizeComboBox.currentText())
        self.openGLWidget.update()

    def setOffsetComboBox(self, state):
        self.openGLWidget.offset = state
        self.openGLWidget.update()

    def setColourComboBox(self):
        self.openGLWidget.colour = self.colourComboBox.currentText()
        self.openGLWidget.update()

        # bondRadiusSlider
        # bondRadiusSliderLabel

    def setBondRadiusSliderLabel(self):
        value = self.bondRadiusSlider.value()
        self.bondRadius = value / 100
        self.draw()

    def updateBondRadiusSliderLabel(self):
        value = self.bondRadiusSlider.value()
        self.bondRadiusSliderLabel.setText("Radius: " + str(value / 100))

        # bondThresholdSlider
        # bondThresholdSliderlabel

    def setBondThresholdSliderLabel(self):
        value = self.bondThresholdSlider.value()
        self.bondThreshold = value / 10
        self.draw()

    def updateBondThresholdSliderLabel(self):
        value = self.bondThresholdSlider.value()
        self.bondThresholdSliderLabel.setText("Length: " + str(value / 10))

        # switchToAttrFileTabButton

    def onSwitchToAttrFileTabButtonClicked(self):
        self.mainWindow.setCurrentIndex(self.mainWindow.indexOf(self.attributeFileTab))

        # orbitalSettingsTab
        # advOrbWfCheckBox
        # advOrbHorzCheckBox
        # advOrbVertCheckBox
        # sphOrbWfCheckBox

    def draw(self):
        atoms_off = self.toggleAtomsButton.isChecked()
        self.openGLWidget.clear()

        # Plot atoms and bonds
        if not atoms_off:
            draw_atoms(self.atoms, self.openGLWidget, self.atomRow, self.atomCol)
            draw_bonds(self.atoms, self.openGLWidget, self.bondRow, self.bondCol, self.bondRadius, self.bondThreshold)
            draw_selection(self.atoms, self.openGLWidget, self.atomRow, self.atomCol)

        # Plot orbitals
        if self.advOrbWfCheckBox.isChecked():
            draw_advOrbWf(self.atoms, self.openGLWidget, self.mode, self.orbRow, self.orbCol, self.orbScaler,
                          self.theta, self.phi, self.R, self.G, self.B, self.A)

        if self.advOrbHorzCheckBox.isChecked():
            draw_advOrbHorz(self.atoms, self.openGLWidget, self.mode, self.orbScaler, self.theta, self.phi, self.R,
                            self.G, self.B, self.A)

        if self.advOrbVertCheckBox.isChecked():
            draw_advOrbVert(self.atoms, self.openGLWidget, self.mode, self.orbScaler, self.theta, self.phi, self.R,
                            self.G, self.B, self.A)

        if self.sphOrbWfCheckBox.isChecked():
            draw_sphOrbWf(self.atoms, self.openGLWidget, self.mode, self.orbRow, self.orbCol, self.orbScaler, self.R,
                          self.G, self.B, self.A)

        if self.sphOrbFacesCheckBox.isChecked():
            draw_sphOrbFaces(self.atoms, self.openGLWidget, self.mode, self.orbRow, self.orbCol, self.orbScaler, self.R,
                             self.G, self.B, self.A)

        if self.advOrbFacesCheckBox.isChecked():
            draw_advOrbFaces(self.atoms, self.openGLWidget, self.mode, self.orbRow, self.orbCol, self.orbScaler,
                             self.theta, self.phi, self.R, self.G, self.B, self.A)

        # orbColSlider
        # orbColSliderLabel

    def setOrbColSliderLabel(self):
        value = self.orbColSlider.value()
        self.orbCol = value
        self.draw()

    def updateOrbColSliderLabel(self):
        value = self.orbColSlider.value()
        self.orbColSliderLabel.setText("Columns: " + str(value))

        # orbRowSlider
        # orbRowSliderLabel

    def setOrbRowSliderLabel(self):
        value = self.orbRowSlider.value()
        self.orbRow = value
        self.draw()

    def updateOrbRowSliderLabel(self):
        value = self.orbRowSlider.value()
        self.orbRowSliderLabel.setText("Rows: " + str(value))

        # orbScalerSlider
        # orbScalerSliderLabel

    def setScalerSliderLabel(self):
        value = self.orbScalerSlider.value()
        self.orbScaler = value
        self.draw()

    def updateScalerSliderLabel(self):
        value = self.orbScalerSlider.value()
        self.orbScalerSliderLabel.setText("Scaler: " + str(value))

    def setThetaSliderLabel(self):
        value = self.thetaSlider.value()
        self.theta = math.radians(value)
        self.draw()

    def updateThetaSliderLabel(self):
        value = self.thetaSlider.value()
        self.thetaSliderLabel.setText("Theta: " + str(value))

    def setPhiSliderLabel(self):
        value = self.phiSlider.value()
        self.phi = math.radians(value)
        self.draw()

    def updatePhiSliderLabel(self):
        value = self.phiSlider.value()
        self.phiSliderLabel.setText("Phi: " + str(value))

    def setColourRSliderLabel(self):
        value = self.colourRSlider.value()
        self.R = value / 100
        self.draw()

    def setColourGSliderLabel(self):
        value = self.colourGSlider.value()
        self.G = value / 100
        self.draw()

    def setColourBSliderLabel(self):
        value = self.colourBSlider.value()
        self.B = value / 100
        self.draw()

    def setColourASliderLabel(self):
        value = self.colourASlider.value()
        self.A = value / 100
        self.draw()

    def updateColourRSliderLabel(self):
        value = self.colourRSlider.value()
        self.colourRSliderLabel.setText("R: " + str(value / 100))

    def updateColourGSliderLabel(self):
        value = self.colourGSlider.value()
        self.colourGSliderLabel.setText("G: " + str(value / 100))

    def updateColourBSliderLabel(self):
        value = self.colourBSlider.value()
        self.colourBSliderLabel.setText("B: " + str(value / 100))

    def updateColourASliderLabel(self):
        value = self.colourASlider.value()
        self.colourASliderLabel.setText("A: " + str(value / 100))

    # mainDisplayTab
    # horizontalSlider
    # horizontalSliderLabel
    def setHorizontalSliderLabel(self):
        value = self.horizontalSlider.value()
        self.mode = value
        self.draw()

    def updateHorizontalSliderLabel(self):
        value = self.horizontalSlider.value()
        self.horizontalSliderLabel.setText("Molecular Orbital: " + str(value + 1))
        self.horizontalSliderEnergyLabel.setText("Energy (eV): " + self.atoms[0].get_eigenenergy(value))

    # openGLWidget
    def onTransSelection(self):
        self.transSelected = {"1": [], "2": [], "3": [], "4": [], "5": []}
        self.draw()
        for i in range(len(self.atoms)):
            terminal = self.atoms[i].get_isSelectedTrans()
            if terminal:
                self.transSelected[str(terminal)].append(str(i + 1))
        self.writeToLogs("Selected atom indices for terminals 1-5:", "black")
        for key in self.transSelected:
            self.writeToLogs("Terminal " + key + ": " + ", ".join(self.transSelected[key]), colours(key))
        self.writeToLogs("\n", "black")

    def onCurrentSelectionA(self):
        self.currentSelectedA = []
        self.draw()
        for i in range(len(self.atoms)):
            if self.atoms[i].get_isSelectedCurrA():
                self.currentSelectedA.append(i + 1)
        if len(self.currentSelectedA) == 0:
            self.writeToLogs("No regions selected.", "purple")
            return
        selection = "Region A by atom index: "
        for j in range(len(self.currentSelectedA)):
            selection = selection + str(self.currentSelectedA[j]) + ", "
        self.writeToLogs(selection[:-2], "purple")

    def onCurrentSelectionB(self):
        self.currentSelectedB = []
        self.draw()
        for i in range(len(self.atoms)):
            if self.atoms[i].get_isSelectedCurrB():
                self.currentSelectedB.append(i + 1)
        if len(self.currentSelectedB) == 0:
            self.writeToLogs("No regions selected.", "lime")
            return
        selection = "Region B by atom index: "
        for j in range(len(self.currentSelectedB)):
            selection = selection + str(self.currentSelectedB[j]) + ", "
        self.writeToLogs(selection[:-2], "lime")

    # resetViewButton
    def onResetViewButtonClicked(self):
        self.openGLWidget.reset()
        self.openGLWidget.setBackgroundColor(self.backgroundColor)
        self.writeToLogs("View reset to default.", "grey")

    # saveImageButton
    def onSaveImageButtonClicked(self):
        filename, _ = QtWidgets.QFileDialog.getSaveFileName(parent=self, caption='Save image',
                                                            filter="PNG Image (*.png);;JPEG Image (*.jpg);;All Files (*.*)")

        self.openGLWidget.readQImage().save(filename)

    def onSave3DImageButtonClicked(self):
        filename, _ = QtWidgets.QFileDialog.getSaveFileName(parent=self, caption='Save image',
                                                            filter="PNG Image (*.png);;JPEG Image (*.jpg);;All Files (*.*)")

        self.gammaGLWidget.readQImage().save(filename)

    # toggleAtomsButton
    def onToggleAtomsButtonClicked(self):
        if self.toggleAtomsButton.isChecked():
            self.draw()
            self.writeToLogs("Atoms toggled off.", "grey")
            return
        self.draw()
        self.writeToLogs("Atoms toggled on.", "grey")

    # inputFileTab
    # inputTextEdit
    # saveInputFileButton
    def onSaveInputFileButtonClicked(self):
        with open(self.inputFilename + ".in", 'w') as f:
            f.write(str(self.inputTextEdit.toPlainText()))
        self.writeToLogs("Input file " + self.inputFilename + ".in saved successfully.", "green")

    def onGammaLineEditChanged(self):
        string = self.gammaLineEdit.text()
        if not isposfloat(string):
            self.writeErrorToLogs("Error: non-positive float '" + string + "' entered for gamma.")
            self.gammaLineEdit.setText("")
            self.gammaLineEdit2.setText("")
        else:
            self.gammaLineEdit.setText(string)
            self.gammaLineEdit2.setText(string)

    def onGammaLineEditChanged2(self):
        string = self.gammaLineEdit2.text()
        if not isposfloat(string):
            self.writeErrorToLogs("Error: non-positive float '" + string + "' entered for gamma.")
            self.gammaLineEdit.setText("")
            self.gammaLineEdit2.setText("")
        else:
            self.gammaLineEdit.setText(string)
            self.gammaLineEdit2.setText(string)

    def onExcessLineEditChanged(self):
        string = self.excessLineEdit.text()
        if not isfloat(string):
            self.writeErrorToLogs("Error: non-float '" + string + "' entered for excess electrons.")
            self.excessLineEdit.setText("")

    def onReferenceLineEditChanged(self):
        string = self.referenceLineEdit.text()
        if not isfloat(string):
            self.writeErrorToLogs("Error: non-float '" + string + "' entered for reference potential.")
            self.referenceLineEdit.setText("")

    def onBiasLineEditChanged(self):
        string = self.biasLineEdit.text()
        if not isfloat(string):
            self.writeErrorToLogs("Error: non-float '" + string + "' entered for bias.")
            self.biasLineEdit.setText("")

    def onStepsLineEditChanged(self):
        string = self.stepsLineEdit.text()
        if not isnatnumber(string):
            self.writeErrorToLogs("Error: non-natural number '" + string + "' entered for steps.")
            self.stepsLineEdit.setText("")

    def onGammaStartLineEditChanged(self):
        string = self.gammaStartLineEdit.text()
        if not isposfloat(string):
            self.writeErrorToLogs("Error: non-pos float '" + string + "' entered for gamma start value.")
            self.gammaStartLineEdit.setText("")

    def onGammaEndLineEditChanged(self):
        string = self.gammaEndLineEdit.text()
        if not isposfloat(string):
            self.writeErrorToLogs("Error: non-pos float '" + string + "' entered for gamma end value.")
            self.gammaEndLineEdit.setText("")

    def onGammaStepsLineEditChanged(self):
        string = self.gammaStepsLineEdit.text()
        if not isnatnumber(string):
            self.writeErrorToLogs("Error: non-natural number '" + string + "' entered for gamma steps value.")
            self.gammaStepsLineEdit.setText("")

    def onOffsetLineEditChanged(self):
        string = self.offsetLineEdit.text()
        if not isfloat(string):
            self.writeErrorToLogs("Error: non-float '" + string + "' entered for offset value.")
            self.offsetLineEdit.setText("0")
        else:
            self.offset = float(self.offsetLineEdit.text())
            self.setGraphComboBox()

    # AttributeFileTab
    # attributeTextEdit
    # saveAttributeFileButton
    def onSaveAttributeFileButtonClicked(self):
        with open("config/attributes.txt", 'w') as f:
            f.write(str(self.attributeTextEdit.toPlainText()))
        self.writeToLogs("Attribute file attributes.txt modified successfully. Settings will be applied on next "
                         "execution.", "green")

    # fullConsoleTab
    # fullConsoleTextEdit
    def writeToLogs(self, text, color):
        self.consoleLog.setTextColor(QColor(color))
        self.fullConsoleLog.setTextColor(QColor(color))
        self.consoleLog.append(text)
        self.fullConsoleLog.append(text)

    def writeErrorToLogs(self, text):
        self.consoleLog.setTextColor(QColor("red"))
        self.fullConsoleLog.setTextColor(QColor("red"))
        self.consoleLog.append(text)
        self.fullConsoleLog.append(text)
        self.fullConsoleLog.append(traceback.format_exc())

if __name__ == '__main__':
    main = MainWindow(resolution.width)
    # main = MainWindow(resolution.width, default_input)
    main.show()
    sys.exit(app.exec_())


#Cris Chou
#Homework 4

import pickle
from nltk.util import ngrams
from nltk import word_tokenize
import math

#get pickled dicts
bidictEn = pickle.load(open('bidictEn.p', 'rb'))
unidictEn = pickle.load(open('unidictEn.p', 'rb'))
bidictFr = pickle.load(open('bidictFr.p', 'rb'))
unidictFr = pickle.load(open('unidictFr.p', 'rb'))
bidictIt = pickle.load(open('bidictIt.p', 'rb'))
unidictIt = pickle.load(open('unidictIt.p', 'rb'))

#probablility of each language
def compute_prob(text_in, unigram_dict, bigram_dict, V):
    #V = vocab size
    unigrams_test = word_tokenize(text_in)
    bigrams_test = list(ngrams(unigrams_test,2))

    #print(bigrams_test)
    #print(unigram_dict)
    #p of using a variation of laplace smoothing
    p_laplace = 1

    for bigram in bigrams_test:

        n = bigram_dict[bigram] if bigram in bigram_dict else 0
        d = unigram_dict[bigram[0]] if bigram[0] in unigram_dict else 0

        p_laplace *= ((n+1)/(d+V))

    #print(p_laplace)
    #print("Probability with laplace smoothing is %.5f" % p_laplace)

    return p_laplace

if __name__ == '__main__':

    #get vocab size
    vocab_size = len(unidictEn) + len(unidictFr) + len(unidictIt)
    print(vocab_size)

    check = []
    count = 0
    with open('data\LangId.test') as f:
            lines = f.readlines()

            for line in lines:
                en_prob = compute_prob(line, unidictEn, bidictEn, vocab_size)
                fr_prob = compute_prob(line, unidictFr, bidictFr, vocab_size)
                it_prob = compute_prob(line, unidictIt, bidictIt, vocab_size)

                #find out which one is most likely
                if en_prob > fr_prob and en_prob > it_prob:
                    check.append("English")
                elif fr_prob > en_prob and fr_prob > it_prob:
                    check.append("French")
                elif it_prob > en_prob and it_prob > fr_prob:
                    check.append("Italian")
                else:
                    check.append("Unknown")

    total = len(check)
    correct = 0
    #check for accuracy
    with open('data\LangId.sol') as f:

        lines = f.readlines()
        for i in range(len(lines)):
            #remove the numbers to compare
            if lines[i].strip()[2:] == check[i]:
                correct += 1
            print(lines[i].strip()[2:], " ", check[i])
            if i == 50:
                break
    print("Accuracy is %.2f" % (correct/total))


from django.urls import path
from . import views

app_name = 'burgerkingapp'

urlpatterns = [
	#path('',views.home,name='home'),
	path('',views.products,name='products'),  #viewing all products
	path('<int:id>', views.product_detail, name='product_detail'),
]


###########################
###### STREAMING API ######
# Developed by: @Matt0550 #
###########################

import flask
import os
import sys

app = flask.Flask(__name__)
app.config["DEBUG"] = True

HOST = 'localhost'
PORT = 5000

# Import modules
modules = []
for module in os.listdir('modules'):
    if module.endswith('.py') and module.startswith("OFF_"):
        print("Module " + module + " is disabled")
        continue
    if module.endswith('.py') and module != '__init__.py':
        modules.append(module[:-3])

for module in modules:
    try:
        __import__('modules.' + module)
        # Register the blueprint
        app.register_blueprint(getattr(sys.modules['modules.' + module], 'app_' + module))

    except Exception as e:
        print('Error importing module: ' + module)
        print(e)

# Create a URL route in our application for "/"
@app.route('/', methods=['GET'])
def home():
    return "
How to use this API

Visit my GitHub page at: @Matt0550
"

# List of available modules
@app.route('/modules', methods=['GET'])
def modules():
    # List of registered blueprints
    blueprints = []
    for blueprint in app.blueprints.keys():
        # Remove the "app_" prefix
        blueprints.append(blueprint[4:])
    return {"modules": blueprints, "status": "success"}

# Run the application
app.run(host=HOST, port=PORT)


from __future__ import print_function, division
import serial
from enum import Enum
import time
import struct
import numpy as np

try:     # Python 2
    from Tkinter import *
    bytes_fromhex = lambda h: h.decode("hex")
except:
    from tkinter import *
    bytes_fromhex = bytes.fromhex

class Axis(Enum):
    z = 0
    x = 1
    y = 2

class Command(Enum):
    ROR = 1     # extend
    ROL = 2     # contract
    MST = 3     # motor STOP
    MVP = 4     # move to position
                #  type is 0 for absolute (w.r.t. origin, which is reset on startup)
                #          1 for relative (w.r.t. current position)
                #          2 for stored coordinate
    SAP = 5     # set axis parameter
    GAP = 6     # get axis parameter

    GCO = 31    # get coordinate

class AxisParameter(Enum):
    target_pos  = 0
    actual_pos  = 1
    max_accel   = 5
    pos_reached = 8

class Point(object):
    def __init__(self, x, y, z):
        self.x = int(x)
        self.y = int(y)
        self.z = int(z)

    def as_array(self):
        return np.array((self.x, self.y, self.z))

    def __add__(self, p):
        assert type(p) is Point
        return Point(self.x+p.x, self.y+p.y, self.z+p.z)

    def __neg__(self):
        return Point(-self.x, -self.y, -self.z)

    def __sub__(self, p):
        assert type(p) is Point
        return self + (-p)

    def __mul__(self, p):
        if type(p) is Point:
            return Point(self.x*p.x, self.y*p.y, self.z*p.z)
        else:
            return Point(p*self.x, p*self.y, p*self.z)

    __rmul__ = __mul__
    __radd__ = __add__

    def __str__(self):
        #return "(x={},y={},z={})".format(self.x, self.y, self.z)
        return "({}, {}, {})".format(self.x, self.y, self.z)

class Reply(object):
    def __init__(self, reply):
        reply = bytearray(reply)
        self.reply_addr  = reply[0]
        self.module_addr = reply[1]
        self.status      = reply[2]
        self.cmd_number  = reply[3]
        self.value       = struct.unpack(">i", reply[4:8])[0]
        self.checksum    = reply[8]

        if self.checksum != sum(reply[:-1]) % 256:
            raise Exception("Invalid reply checksum!")

        self.ok = self.status == 100

class Request(object):
    def __init__(self, cmd, type, axis, value):

        self.cmdHex = hex(cmd.value)[2:].zfill(2)
        self.typeHex = hex(type)[2:].zfill(2)
        self.axisHex = hex(axis.value)[2:].zfill(2)
        value = (2**32+value)&0xFFFFFFFF
        self.valueHex = "%08x"%value

    def as_hex(self):
        command = '01' + self.cmdHex + self.typeHex + self.axisHex + self.valueHex
        checksum = "%02x" % (sum(bytearray(bytes_fromhex(command))) % 256)
        return command + checksum

    def as_bytes(self):
        return bytes_fromhex(self.as_hex())

class XYTable:

    ser = serial.Serial()

    # for the chip-coordinates,
    #  y-axis points down,
    #  x-axis points right
    origin = None    # NW corner
    xpoint = None    # NE corner
    ypoint = None    # SE corner
    # These must be set

    directions = {
        "left"     : (Axis.x, Command.ROR),   # -x
        "right"    : (Axis.x, Command.ROL),   # +x
        "forward"  : (Axis.y, Command.ROL),   # +y
        "backward" : (Axis.y, Command.ROR),   # -y
        "up"       : (Axis.z, Command.ROR),   # +z
        "down"     : (Axis.z, Command.ROL)    # -z
    }

    def __init__(self, points_file=None):
        if points_file:
            self.read_from_file(points_file)

    def set_origin(self):
        self.origin = self.get_position()

    def set_xpoint(self):
        self.xpoint = self.get_position()

    def set_ypoint(self):
        self.ypoint = self.get_position()

    def gen2coord(self, x, y):
        # We do a simple affine transformation:
        #     P = x*E1 + y*E2
        # where E1=(xpoint-origin) and E2=(ypoint-xpoint)
        #  are the basis vectors of the new vector space
        assert self.origin and self.xpoint and self.ypoint
        assert 0<=x<=1 and 0<=y<=1
        return x*(self.xpoint-self.origin) + y*(self.ypoint-self.xpoint) + self.origin

    def coord2gen(self, coord):
        # The transformation from point p0 (in tablespace) to p1 (in 01space)
        #  can be represented as:
        #     A*p0 + b = p1
        #  where the columns of A are the new basis vectors.
        #
        # This function does the inverse transformation:
        #     p0 = inv(A) * (p1 - b)
        #
        #  (That is, we first align the origins
        #   and then we invert the linear part)
        assert self.origin and self.xpoint and self.ypoint
        p1 = (coord - self.origin).as_array()

        xv = (self.xpoint - self.origin).as_array()
        yv = (self.ypoint - self.xpoint).as_array()
        #zv = np.cross(xv, yv); zv = zv/np.linalg.norm(zv)
        zv = np.array([0.0, 0.0, 1.0])        # any plane normal is OK

        A = np.vstack((xv, yv, zv)).T
        Ainv = np.linalg.inv(A)
        p0 = Ainv.dot(p1)
        assert p0[2] < 0.01, "Point not in plane!"
        return (p0[0], p0[1])

    def connect(self):
        self.ser.baudrate = 9600 #115200
        self.ser.port = 'COM1'
        self.ser.open()
        print('Connection is open: ' + str(self.ser.is_open))
        if self.ser.is_open:
            return True
        else:
            return False

    def disconnect(self):
        self.ser.close()
        if self.ser.is_open:
            print('Disconnect failed!')
            return False
        else:
            print('Connection closed!')
            return True

    def action(self, cmd, type, axis, value):

        req = Request(cmd, type, axis, value)
        self.ser.write(req.as_bytes())
        reply = Reply(self.ser.read(9))

        assert reply.ok, "Action failed!"
        return reply.value

    def move(self, direction, speed=10000, stop=False, sleeptime=1):
        if direction not in self.directions:
            raise Exception("{} is not a valid direction".format(direction))
        axis, cmd = self.directions[direction]

        self.action(cmd, 0, axis, value=speed)
        if stop:
            time.sleep(sleeptime)
            self.action(Command.MST, 0, axis, 0)

    def move_to_position(self, position):
        self.action(Command.MVP, 0, Axis.x, position.x)
        self.action(Command.MVP, 0, Axis.y, position.y)
        self.action(Command.MVP, 0, Axis.z, position.z)

    def get_position(self):
        """Returns the current position, in absolute coordinates"""
        xpos = self.action(Command.GAP, AxisParameter.actual_pos.value, Axis.x, 0)
        ypos = self.action(Command.GAP, AxisParameter.actual_pos.value, Axis.y, 0)
        zpos = self.action(Command.GAP, AxisParameter.actual_pos.value, Axis.z, 0)
        return Point(xpos, ypos, zpos)

    def wait(self):
        """Returns when target position has been reached"""
        while True:
            xstop = self.action(Command.GAP, AxisParameter.pos_reached.value, Axis.x, 0)
            ystop = self.action(Command.GAP, AxisParameter.pos_reached.value, Axis.y, 0)
            zstop = self.action(Command.GAP, AxisParameter.pos_reached.value, Axis.z, 0)
            if xstop and ystop and zstop:
                return

    def stop(self, direction=None):
        if direction:
            axis = self.directions[direction][0]
            self.action(Command.MST, 0, axis, 0)
        else:
            for axis in Axis:
                self.action(Command.MST, 0, axis, 0)
            #print("Stopped.")

    def read_from_file(self, points_file):
        p1, p2, p3 = [l.strip() for l in points_file.readlines()]
        self.origin = Point(*(p1.split(",")))
        self.xpoint = Point(*(p2.split(",")))
        self.ypoint = Point(*(p3.split(",")))

    def write_to_file(self, points_file):
        if self.origin and self.xpoint and self.ypoint:
            print("{},{},{}".format(self.origin.x, self.origin.y, self.origin.z), file=points_file)
            print("{},{},{}".format(self.xpoint.x, self.xpoint.y, self.xpoint.z), file=points_file)
            print("{},{},{}".format(self.ypoint.x, self.ypoint.y, self.ypoint.z), file=points_file)

if __name__ == "__main__":

    try:
        tab = XYTable()
        tab.connect()

        from gui import GUI
        interface = GUI(tab)
        interface.start()

        print(tab.get_position())

    except KeyboardInterrupt:
        print("Killed by KeyboardInterrupt")
    finally:
        tab.stop()
        tab.disconnect()


from typing import Union

from aiogram import types
from aiogram.dispatcher import FSMContext
from aiogram.types import CallbackQuery, InlineKeyboardMarkup, InlineKeyboardButton, MediaGroup, InputMedia, \
    InputMediaPhoto, InputMediaDocument

from data.callback_data import vpns_cd, qr_code_cd
from loader import dp, db, bot

@dp.message_handler(commands=["my_key"])
@dp.callback_query_handler(text="my_keys")
async def my_key(message: Union[types.Message, CallbackQuery], state: FSMContext):
    callback = False
    if isinstance(message, CallbackQuery):
        callback = True

    if callback:
        user_id = message.message.chat.id
    else:
        user_id = message.chat.id

    user = await db.get_user(user_id)

    vpn_id1 = user[0]["vpn_id"]
    vpn_id2 = user[0]["vpn_id2"]

    if callback:
        message = message.message

    if not vpn_id1:
        markup = InlineKeyboardMarkup(inline_keyboard=[
            [InlineKeyboardButton("⬅ Назад", callback_data="main_menu")],
        ])
        text = "🥺 У вас пока нет ключей" \
               "\n\nВыберите нужный вам тариф в разделе 'Тарифы'"
        await message.answer(text, reply_markup=markup)

        try:
            await message.delete()
        except:
            pass
    else:

        if vpn_id2:
            markup = InlineKeyboardMarkup(inline_keyboard=[
                [InlineKeyboardButton("🔑 Ключ 1", callback_data=vpns_cd.new(vpn_id=vpn_id1)),
                 InlineKeyboardButton("🔑 Ключ 2", callback_data=vpns_cd.new(vpn_id=vpn_id2))],
                [InlineKeyboardButton("⬅ Назад", callback_data="main_menu")],
            ])
        else:
            markup = InlineKeyboardMarkup(inline_keyboard=[
                [InlineKeyboardButton("🏞 QR код", callback_data=qr_code_cd.new(vpn_id=vpn_id1))],
                [InlineKeyboardButton("⬅ Назад", callback_data="main_menu")],
            ])

        text = """🔑 Ваши ключи"""

        vpn = await db.get_vpn(vpn_id1)
        file = vpn[0]["file_id"]

        media = InputMediaDocument(file, caption=text)

        if not vpn_id2:
            try:
                await message.edit_media(media, reply_markup=markup)
            except:
                await message.answer_document(file, caption=text, reply_markup=markup)

                try:
                    await message.delete()
                except:
                    pass
        else:
            try:
                await message.edit_text(text, reply_markup=markup)
            except:
                await message.answer(text, reply_markup=markup)

                try:
                    await message.delete()
                except:
                    pass

    async with state.proxy() as data:
        if "msg" in data.keys():
            msg = data["msg"]
            for m in msg:
                try:
                    await bot.delete_message(user_id, m)
                except:
                    pass
            data.pop("msg")

@dp.callback_query_handler(vpns_cd.filter())
async def get_vpn(call: CallbackQuery, state: FSMContext, callback_data: dict = None):
    if callback_data:
        vpn_id = int(callback_data["vpn_id"])
        async with state.proxy() as data:
            data["vpn_id"] = vpn_id
    else:
        async with state.proxy() as data:
            vpn_id = data["vpn_id"]

    vpn = await db.get_vpn(vpn_id)
    file = vpn[0]["file_id"]

    markup = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton("🏞 QR код", callback_data=qr_code_cd.new(vpn_id=vpn_id))],
        [InlineKeyboardButton("⬅ Назад", callback_data="my_keys")],
        [InlineKeyboardButton("🏠 Главное меню", callback_data="main_menu")],
    ])

    text = """🔑 Ваш ключ"""

    media = InputMediaDocument(file, caption=text)

    try:
        await call.message.edit_media(media, reply_markup=markup)
    except:
        await call.message.answer_document(file, caption=text, reply_markup=markup)
        try:
            await call.message.delete()
        except:
            pass

@dp.callback_query_handler(qr_code_cd.filter())
async def get_qr_code(call: CallbackQuery, callback_data: dict):
    vpn_id = int(callback_data["vpn_id"])

    vpn = await db.get_vpn(vpn_id)
    qr_code = vpn[0]["photo_id"]

    text = """🏞 Ваш QR код"""

    markup = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton("⬅ Назад", callback_data="my_keys")],
        [InlineKeyboardButton("🏠 Главное меню", callback_data="main_menu")],
    ])

    media = InputMediaPhoto(qr_code, caption=text)

    await call.message.edit_media(media, reply_markup=markup)


from models.sql import *
import json
import random

class Prediction:
    table_name = 'predictions'
    query = \
        '''
            id INTEGER PRIMARY KEY,
            prediction varchar(350) NOT NULL
        '''
    create_table(table_name, query, remove_previous=False)

    @classmethod
    def get_random_prediction(cls):
        count = select_one(cls.table_name, columns=['COUNT(*)'])[0]
        random_num = random.randint(1, count)
        return select_one(table_name=cls.table_name, columns=['prediction'], condition=f'id = {random_num}')

    @classmethod
    def add_prediction(cls, prediction: str) -> None:
        insert_into(table_name=cls.table_name, columns=['prediction'], values=[prediction])

    @classmethod
    def add_all_predictions_from_json(cls) -> None:
        with open(f'{cls.table_name}.json', 'r', encoding='utf-8') as file:
            parsed_predictions = json.load(file)
            for prediction in parsed_predictions:
                cls.add_prediction(prediction=prediction['prediction'])


import datetime
from datetime import datetime as d
import time
import os
from os import listdir, stat

from django.shortcuts import render

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
FILES_PATH = os.path.join(BASE_DIR, 'files')

def file_list(request, dat=None):
    template_name = 'index.html'
    file_l = []
    if dat:
        ddate = d.strptime(dat, '%Y-%m-%d')
        for file in listdir(FILES_PATH):
            statinfo = stat(FILES_PATH + '\\' + file)
            mt = time.gmtime(statinfo.st_mtime)
            mtime = datetime.date(mt.tm_year, mt.tm_mon, mt.tm_mday)
            ct = time.gmtime(statinfo.st_ctime)
            ctime = datetime.date(ct.tm_year, ct.tm_mon, ct.tm_mday)
            if ctime == ddate:
                file_info = {'name': file,
                             'ctime': ctime,
                             'mtine': mtime
                         }
                file_l.append(file_info)
        context = {'files': file_l,
                   'date': ddate
                   }
        return render(request, template_name, context)
    for file in listdir(FILES_PATH):
        statinfo = stat(FILES_PATH + '\\' + file)
        mt = time.gmtime(statinfo.st_mtime)
        mtime = datetime.date(mt.tm_year, mt.tm_mon, mt.tm_mday)
        ct = time.gmtime(statinfo.st_ctime)
        ctime = datetime.date(ct.tm_year, ct.tm_mon, ct.tm_mday)
        file_info = {'name': file,
                     'ctime': ctime,
                     'mtine': mtime
        }
        file_l.append(file_info)
    context = {'files': file_l
               }
    return render(request, template_name, context)

def file_content(request, name):
    with open(FILES_PATH + '\\' + name) as file:
        my_text = file.read()
    return render(
        request,
        'file_content.html',
        context={'file_name': name, 'file_content': my_text}
    )


import numpy as np
import matplotlib.pyplot as plt
import cmath as cm
wh=[]
m=31
for n in range(0,m):
	p=0.5*np.cos((2*np.pi*n)/(m-1))
	y=0.5-p
	wh=np.append(wh,y)
plt.stem(wh)
plt.show()


import os
from itertools import chain
from pathlib import Path
from typing import Optional, Tuple, List

import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from torch.utils.data import DataLoader
from torch.utils.data.dataloader import default_collate
from tqdm import tqdm

from synop.consts import SYNOP_PERIODIC_FEATURES, LOWER_CLOUDS, CLOUD_COVER
from util.coords import Coords
from wind_forecast.config.register import Config
from wind_forecast.consts import BatchKeys
from wind_forecast.consts import SYNOP_DATASETS_DIRECTORY
from wind_forecast.datamodules.SplittableDataModule import SplittableDataModule
from wind_forecast.datasets.ConcatDatasets import ConcatDatasets
from wind_forecast.datasets.Sequence2SequenceGFSDataset import Sequence2SequenceGFSDataset
from wind_forecast.datasets.Sequence2SequenceSynopDataset import Sequence2SequenceSynopDataset
from wind_forecast.preprocess.synop.synop_preprocess import prepare_synop_dataset, \
    modify_feature_names_after_periodic_reduction
from wind_forecast.util.common_util import NormalizationType
from wind_forecast.util.config import process_config
from wind_forecast.util.df_util import normalize_data_for_training, decompose_data, resolve_indices
from wind_forecast.util.gfs_util import add_param_to_train_params, \
    GFSUtil, extend_wind_components, decompose_gfs_data, get_gfs_target_param
from wind_forecast.util.logging import log
from wind_forecast.util.synop_util import get_correct_dates_for_sequence

class Sequence2SequenceDataModule(SplittableDataModule):

    def __init__(
            self,
            config: Config
    ):
        super().__init__(config)
        self.config = config
        self.batch_size = config.experiment.batch_size
        self.shuffle = config.experiment.shuffle

        self.synop_train_params = config.experiment.synop_train_features
        self.target_param = config.experiment.target_parameter
        all_params = add_param_to_train_params(self.synop_train_params, self.target_param)
        self.synop_feature_names = list(list(zip(*all_params))[1])

        self.synop_file = config.experiment.synop_file
        self.synop_from_year = config.experiment.synop_from_year
        self.synop_to_year = config.experiment.synop_to_year
        self.sequence_length = config.experiment.sequence_length
        self.future_sequence_length = config.experiment.future_sequence_length
        self.normalization_type = config.experiment.normalization_type
        self.prediction_offset = config.experiment.prediction_offset
        coords = config.experiment.target_coords
        self.target_coords = Coords(coords[0], coords[0], coords[1], coords[1])

        self.gfs_features_params = process_config(config.experiment.train_parameters_config_file).params
        self.gfs_features_names = [f"{f['name']}_{f['level']}" for f in self.gfs_features_params]
        self.gfs_wind_parameters = ["V GRD_HTGL_10", "U GRD_HTGL_10"]

        self.gfs_target_param = get_gfs_target_param(self.target_param)

        self.gfs_util = GFSUtil(self.target_coords, self.sequence_length, self.future_sequence_length,
                                self.prediction_offset, self.gfs_features_params)

        self.periodic_features = config.experiment.synop_periodic_features
        self.uses_future_sequences = True

        self.synop_data = ...
        self.gfs_data = ...
        self.data_indices = ...
        self.synop_mean = ...
        self.synop_std = ...
        self.synop_min = ...
        self.synop_max = ...
        self.gfs_mean = ...
        self.gfs_std = ...
        self.gfs_min = ...
        self.gfs_max = ...
        self.synop_dates = ...

    def prepare_data(self, *args, **kwargs):
        self.load_from_disk(self.config)

        if self.initialized:
            if self.config.experiment._tags_[0] == 'GFS':
                self.eliminate_gfs_bias()
            return

        self.synop_data = prepare_synop_dataset(self.synop_file,
                                                list(list(zip(*self.synop_train_params))[1]),
                                                dataset_dir=SYNOP_DATASETS_DIRECTORY,
                                                from_year=self.synop_from_year,
                                                to_year=self.synop_to_year,
                                                norm=False)

        if self.config.debug_mode:
            self.synop_data = self.synop_data.head(self.sequence_length * 20)

        self.after_synop_loaded()

        self.synop_feature_names = modify_feature_names_after_periodic_reduction(self.synop_feature_names)

        # Get indices which correspond to 'dates' - 'dates' are the ones, which start a proper sequence without breaks
        self.data_indices = self.synop_data[self.synop_data["date"].isin(self.synop_dates)].index

        if self.config.experiment.stl_decompose:
            self.synop_decompose()
            features_to_normalize = self.synop_feature_names
        else:
            # do not normalize periodic features
            features_to_normalize = [name for name in self.synop_feature_names if name not in
                                     modify_feature_names_after_periodic_reduction(
                                         [f['column'][1] for f in SYNOP_PERIODIC_FEATURES])]

        # data was not normalized, so take all frames which will be used, compute std and mean and normalize data
        self.synop_data, self.synop_mean, self.synop_std, self.synop_min, self.synop_max = normalize_data_for_training(
            self.synop_data, self.data_indices, features_to_normalize,
            self.sequence_length + self.prediction_offset + self.future_sequence_length,
            self.normalization_type)

        log.info(f"Synop target mean: {self.synop_mean[self.target_param]}")
        log.info(f"Synop target std: {self.synop_std[self.target_param]}")

    def after_synop_loaded(self):
        self.synop_dates = get_correct_dates_for_sequence(self.synop_data, self.sequence_length,
                                                          self.future_sequence_length,
                                                          self.prediction_offset)

        self.synop_data = self.synop_data.reset_index()

    def setup(self, stage: Optional[str] = None):
        if self.initialized:
            self.log_dataset_info()
            return
        if self.get_from_cache(stage):
            self.log_dataset_info()
            return

        if self.config.experiment.load_gfs_data:
            self.prepare_dataset_for_gfs()
            synop_dataset = Sequence2SequenceSynopDataset(self.config, self.synop_data, self.data_indices,
                                                          self.synop_feature_names)
            synop_dataset.set_mean(self.synop_mean)
            synop_dataset.set_std(self.synop_std)
            synop_dataset.set_min(self.synop_min)
            synop_dataset.set_max(self.synop_max)

            gfs_dataset = Sequence2SequenceGFSDataset(self.config, self.gfs_data, self.data_indices, self.gfs_features_names)
            gfs_dataset.set_mean(self.gfs_mean)
            gfs_dataset.set_std(self.gfs_std)
            gfs_dataset.set_min(self.gfs_min)
            gfs_dataset.set_max(self.gfs_max)
            dataset = ConcatDatasets(synop_dataset, gfs_dataset)
        else:
            synop_dataset = Sequence2SequenceSynopDataset(self.config, self.synop_data, self.data_indices,
                                                          self.synop_feature_names)
            synop_dataset.set_mean(self.synop_mean)
            synop_dataset.set_std(self.synop_std)
            synop_dataset.set_min(self.synop_min)
            synop_dataset.set_max(self.synop_max)
            dataset = synop_dataset

        if len(dataset) == 0:
            raise RuntimeError("There are no valid samples in the dataset! Please check your run configuration")

        self.split_dataset(self.config, dataset, self.sequence_length)
        self.log_dataset_info()

        if self.config.experiment._tags_[0] == 'GFS':
            self.eliminate_gfs_bias()

    def prepare_dataset_for_gfs(self):
        log.info("Preparing the GFS dataset")
        # match GFS and synop sequences
        self.data_indices, self.gfs_data = self.gfs_util.match_gfs_with_synop_sequence2sequence(
            self.synop_data,
            self.data_indices)

        if all([f in self.gfs_features_names for f in self.gfs_wind_parameters]):
            self.gfs_data = self.prepare_gfs_data_with_wind_components(self.gfs_data)

        if self.config.experiment.stl_decompose:
            self.gfs_features_names = self.gfs_decompose()
            features_to_normalize = [*self.gfs_features_names]
        else:
            # do not normalize periodic features
            features_to_normalize = [name for name in self.gfs_features_names if name not in ["wind-sin", "wind-cos"]]

        features_to_normalize.remove(self.gfs_target_param)

        # normalize GFS parameters data
        self.gfs_data, self.gfs_mean, self.gfs_std, self.gfs_min, self.gfs_max = normalize_data_for_training(
            self.gfs_data, self.data_indices, features_to_normalize,
            self.sequence_length + self.prediction_offset + self.future_sequence_length,
            self.normalization_type)

        target_data = self.gfs_data[self.gfs_target_param]

        if self.target_param in [LOWER_CLOUDS[1], CLOUD_COVER[1]]:
            self.gfs_data[self.gfs_target_param] = target_data / 100
        else:
            if self.normalization_type == NormalizationType.STANDARD:
                self.gfs_mean[self.gfs_target_param] = target_data.mean(axis=0)
                self.gfs_std[self.gfs_target_param] = target_data.std(axis=0)
                self.gfs_data[self.gfs_target_param] = (target_data - target_data.mean(axis=0)) / target_data.std(axis=0)
            else:
                self.gfs_min[self.gfs_target_param] = target_data.min(axis=0)
                self.gfs_max[self.gfs_target_param] = target_data.max(axis=0)
                self.gfs_data[self.gfs_target_param] = (target_data - target_data.min(axis=0)) / (target_data.max(axis=0) - target_data.min(axis=0))

        if self.target_param == "wind_direction":
            # TODO handle this case - as for now we do not use this parameter as target
            pass

    def resolve_all_synop_data(self):
        synop_inputs = []
        all_synop_targets = []
        synop_data_dates = self.synop_data['date']
        train_params = list(list(zip(*self.synop_train_params))[1])
        # all_targets and dates - dates are needed for matching the labels against GFS dates
        all_targets_and_labels = pd.concat([synop_data_dates, self.synop_data[train_params]], axis=1)

        for index in tqdm(self.data_indices):
            synop_inputs.append(
                self.synop_data.iloc[index:index + self.sequence_length][[*train_params, 'date']])
            all_synop_targets.append(all_targets_and_labels.iloc[
                                     index + self.sequence_length + self.prediction_offset:index + self.sequence_length + self.prediction_offset + self.future_sequence_length])

        return synop_inputs, all_synop_targets

    def prepare_gfs_data_with_wind_components(self, gfs_data: pd.DataFrame):
        gfs_wind_data = gfs_data[self.gfs_wind_parameters]
        gfs_data.drop(columns=self.gfs_wind_parameters, inplace=True)
        velocity, sin, cos = extend_wind_components(gfs_wind_data.values)
        gfs_data["wind-velocity"] = velocity
        gfs_data["wind-sin"] = sin
        gfs_data["wind-cos"] = cos
        self.gfs_features_names.remove(self.gfs_wind_parameters[0])
        self.gfs_features_names.remove(self.gfs_wind_parameters[1])
        self.gfs_features_names.extend(["wind-velocity", "wind-sin", "wind-cos"])
        return gfs_data

    def train_dataloader(self):
        return DataLoader(self.dataset_train,
                          batch_size=len(self.dataset_train) if self.batch_size == 0 else self.batch_size,
                          shuffle=self.shuffle,
                          collate_fn=self.collate_fn, num_workers=self.config.experiment.num_workers)

    def val_dataloader(self):
        return DataLoader(self.dataset_val,
                          batch_size=len(self.dataset_val) if self.batch_size == 0 else self.batch_size,
                          collate_fn=self.collate_fn,
                          num_workers=self.config.experiment.num_workers)

    def test_dataloader(self):
        return DataLoader(self.dataset_test,
                          batch_size=len(self.dataset_test) if self.batch_size == 0 else self.batch_size,
                          collate_fn=self.collate_fn,
                          num_workers=self.config.experiment.num_workers)

    def collate_fn(self, x: List[Tuple]):
        if self.config.experiment.load_gfs_data:
            synop_data, gfs_data = [item[0] for item in x], [item[1] for item in x]
            synop_data, dates = [[*item[:4], *item[6:]] for item in synop_data], [item[4:6] for item in synop_data]
            all_data = [*default_collate(synop_data), *default_collate(gfs_data),  *list(zip(*dates))]
        else:
            synop_data, dates = [[*item[:4], *item[6:]] for item in x], [item[4:6] for item in x]
            all_data = [*default_collate(synop_data),  *list(zip(*dates))]

        dict_data = {
            BatchKeys.SYNOP_PAST_Y.value: all_data[0],
            BatchKeys.SYNOP_PAST_X.value: all_data[1],
            BatchKeys.SYNOP_FUTURE_Y.value: all_data[2],
            BatchKeys.SYNOP_FUTURE_X.value: all_data[3],
            BatchKeys.DATES_PAST.value: all_data[-2],
            BatchKeys.DATES_FUTURE.value: all_data[-1]
        }

        if self.config.experiment.load_gfs_data:
            dict_data[BatchKeys.GFS_PAST_X.value] = all_data[4]
            dict_data[BatchKeys.GFS_PAST_Y.value] = all_data[5]
            dict_data[BatchKeys.GFS_FUTURE_X.value] = all_data[6]
            dict_data[BatchKeys.GFS_FUTURE_Y.value] = all_data[7]

            if self.config.experiment.differential_forecast:
                target_mean = self.dataset_train.dataset.get_dataset("Sequence2SequenceSynopDataset").mean[self.target_param]
                target_std = self.dataset_train.dataset.get_dataset("Sequence2SequenceSynopDataset").std[self.target_param]
                gfs_past_y = dict_data[BatchKeys.GFS_PAST_Y.value] * target_std + target_mean
                gfs_future_y = dict_data[BatchKeys.GFS_FUTURE_Y.value] * target_std + target_mean
                synop_past_y = dict_data[BatchKeys.SYNOP_PAST_Y.value].unsqueeze(-1) * target_std + target_mean
                synop_future_y = dict_data[BatchKeys.SYNOP_FUTURE_Y.value].unsqueeze(-1) * target_std + target_mean
                diff_past = gfs_past_y - synop_past_y
                diff_future = gfs_future_y - synop_future_y
                dict_data[BatchKeys.GFS_SYNOP_PAST_DIFF.value] = diff_past / target_std
                dict_data[BatchKeys.GFS_SYNOP_FUTURE_DIFF.value] = diff_future / target_std

        return dict_data

    def synop_decompose(self):
        target_param_series = self.synop_data[self.target_param]
        self.synop_data = decompose_data(self.synop_data, self.synop_feature_names)
        self.synop_data[self.target_param] = target_param_series
        self.synop_feature_names = list(
            chain.from_iterable(
                (f"{feature}_T", f"{feature}_S", f"{feature}_R") for feature in self.synop_feature_names))
        self.synop_feature_names.append(self.target_param)

    def gfs_decompose(self):
        target_param_series = self.gfs_data[self.gfs_target_param]
        self.gfs_data = decompose_gfs_data(self.gfs_data, self.gfs_features_names)
        self.gfs_data[self.gfs_target_param] = target_param_series
        new_gfs_features_names = list(
            chain.from_iterable(
                (f"{feature}_T", f"{feature}_S", f"{feature}_R") for feature in self.gfs_features_names))
        new_gfs_features_names.append(self.gfs_target_param)
        return new_gfs_features_names

    def eliminate_gfs_bias(self):
        target_mean = self.dataset_train.dataset.get_dataset("Sequence2SequenceSynopDataset").mean[self.target_param]
        target_std = self.dataset_train.dataset.get_dataset("Sequence2SequenceSynopDataset").std[self.target_param]

        # we can check what is the mean GFS error and just add it to target values to improve performance. We assume we know only train data
        train_indices = [self.dataset_train.dataset.data[index] for index in self.dataset_train.indices]
        all_gfs_data = resolve_indices(self.dataset_train.dataset.gfs_data, train_indices,
                                       self.sequence_length + self.prediction_offset + self.future_sequence_length)
        targets = all_gfs_data[self.gfs_target_param].values
        all_synop_data = resolve_indices(self.dataset_train.dataset.synop_data, train_indices,
                                         self.sequence_length + self.prediction_offset + self.future_sequence_length)
        synop_targets = all_synop_data[self.target_param].values

        real_gfs_train_targets = targets * target_std + target_mean

        real_diff = (synop_targets * target_std + target_mean - real_gfs_train_targets)

        plt.figure(figsize=(20, 10))
        plt.tight_layout()
        sns.displot(real_diff, bins=100, kde=True)
        plt.ylabel('Liczebność')
        plt.xlabel('Różnica')

        os.makedirs(os.path.join(Path(__file__).parent, "plots"), exist_ok=True)
        plt.savefig(
            os.path.join(Path(__file__).parent, "plots", f"gfs_diff_{self.config.experiment.target_parameter}.png"),
            dpi=200, bbox_inches='tight')

        bias = real_diff.mean(axis=0)
        real_gfs_targets = self.dataset_test.dataset.gfs_data[self.gfs_target_param] * target_std + target_mean
        self.dataset_test.dataset.gfs_data[self.gfs_target_param] = (real_gfs_targets + bias - target_mean) / target_std

    def log_dataset_info(self):
        log.info('Dataset train len: ' + str(len(self.dataset_train)))
        log.info('Dataset val len: ' + ('0' if self.dataset_val is None else str(len(self.dataset_val))))
        log.info('Dataset test len: ' + str(len(self.dataset_test)))

        log.info('Dataset train first date: ' +
                 str(self.dataset_train.dataset.get_dataset("Sequence2SequenceSynopDataset")[self.dataset_train.indices[0]][4][0]))
        log.info('Dataset train last date: ' +
                 str(self.dataset_train.dataset.get_dataset("Sequence2SequenceSynopDataset")[self.dataset_train.indices[-1]][5][-1]))
        log.info('Dataset val first date: ' +
                 str(self.dataset_val.dataset.get_dataset("Sequence2SequenceSynopDataset")[self.dataset_val.indices[0]][4][0]))
        log.info('Dataset val last date: ' +
                 str(self.dataset_val.dataset.get_dataset("Sequence2SequenceSynopDataset")[self.dataset_val.indices[-1]][5][-1]))
        log.info('Dataset test first date: ' +
                 str(self.dataset_test.dataset.get_dataset("Sequence2SequenceSynopDataset")[self.dataset_test.indices[0]][4][0]))
        log.info('Dataset test last date: ' +
                 str(self.dataset_test.dataset.get_dataset("Sequence2SequenceSynopDataset")[self.dataset_test.indices[-1]][5][-1]))


import math

import torch
from torch.optim.lr_scheduler import _LRScheduler

class CosineWarmupLR(_LRScheduler):

    def __init__(self, optimizer, epochs, lr_min=0, warmup_epochs=0, last_epoch=-1):
        self.lr_min = lr_min
        self.warmup_epochs = warmup_epochs
        self.cosine_epochs = epochs - warmup_epochs
        super(CosineWarmupLR, self).__init__(optimizer, last_epoch)

    def get_lr(self):
        if self.last_epoch < self.warmup_epochs:
            return [(self.lr_min + (base_lr - self.lr_min) * self.last_epoch / self.warmup_epochs) for base_lr in self.base_lrs]
        else:
            return [(self.lr_min + (base_lr - self.lr_min) * \
                (1 + cos(pi * (self.last_epoch - self.warmup_epochs) / self.cosine_epochs)) / 2) \
                    for base_lr in self.base_lrs]

class PolynomialLRDecay(_LRScheduler):

    def __init__(self, optimizer, max_decay_steps, end_learning_rate=1e-5, power=0.9):
        if max_decay_steps <= 1.:
            raise ValueError('max_decay_steps should be greater than 1.')
        self.max_decay_steps = max_decay_steps
        self.end_learning_rate = end_learning_rate
        self.power = power
        self.last_step = 0
        super().__init__(optimizer)

    def get_lr(self):
        if self.last_step > self.max_decay_steps:
            return [self.end_learning_rate for _ in self.base_lrs]

        return [(base_lr - self.end_learning_rate) *
                ((1 - self.last_step / self.max_decay_steps) ** (self.power)) +
                self.end_learning_rate for base_lr in self.base_lrs]

    def step(self, step=None):
        if step is None:
            step = self.last_step + 1
        self.last_step = step if step != 0 else 1
        if self.last_step <= self.max_decay_steps:
            decay_lrs = [(base_lr - self.end_learning_rate) *
                         ((1 - self.last_step / self.max_decay_steps) ** (self.power)) +
                         self.end_learning_rate for base_lr in self.base_lrs]
            for param_group, lr in zip(self.optimizer.param_groups, decay_lrs):
                param_group['lr'] = lr


import matplotlib.pyplot as plt
import numpy as np

def step_function_1input(x):
    if x > 0:
        return 1
    else:
        return 0

# activate function for intermediate layer
def step_function(x):
    y = x > 0
    return y.astype(np.int)

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def relu(x):
    return np.maximum(0, x)

# activate function for output layer
def identity_function(x):
    return x

# text初出のときはこっち。
# def softmax(a):
#     c = np.max(a)
#     exp_a = np.exp(a - c)  # overflow対策
#     sum_exp_a = np.sum(exp_a)
#     y = exp_a / sum_exp_a
#     return y

def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T

    x = x - np.max(x)  # オーバーフロー対策
    return np.exp(x) / np.sum(np.exp(x))

# test graph show
def test():
    x = np.arange(-5, 5, 0.1)
    y = step_function(x)
    plt.plot(x, y, 'o-')
    plt.show()

    x = np.arange(-5, 5, 0.1)
    y = sigmoid(x)
    plt.plot(x, y, 'o-')
    plt.show()

    x = np.arange(-5, 5, 0.1)
    y = relu(x)
    plt.plot(x, y, 'o-')
    plt.show()

    x = np.arange(-5, 5, 0.1)
    y = identity_function(x)
    plt.plot(x, y, 'o-')
    plt.show()

    x = np.arange(-5, 5, 0.1)
    y = softmax(x)
    plt.plot(x, y, 'o-')
    plt.show()


import json

def loadFile(file):
    """
    load data from .json file into variable
    """
    f = open(file)
    # returns JSON object as
    # a dictionary
    data = json.load(f)
    f.close()
    return data

def updateFile(file, data):
    """
    save json data into file
    """
    with open(file, "w") as outfile:
        json.dump(data, outfile)
    return False


# coding=UTF-8

import socket, select, json, gc
from binascii import a2b_base64, hexlify
from micropython import const
from hashlib import sha1

class server:

	def __init__(self, port = 80):

		try: self.users = json.load(open('/etc/users.json', 'r'))
		except: self.users = dict()

		self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

		self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
		self.sock.bind(('', port))
		self.sock.listen(25)

		self.poll = select.poll()
		self.poll.register(self.sock, select.POLLIN)

		self.slites = dict()

	def accept(self):

		if self.poll.poll(1000):

			try: s = self.sock.accept()[0]
			except: return None
			else: s.settimeout(3)

			try: self.recv(s)
			except: pass
			else: gc.collect()
			finally: s.close()

	def recv(self, sock):

		try:

			try: buff = sock.recv(1024)
			except: return None

			while buff.find(b'\r\n\r\n') == -1:
				if not buff: raise BufferError
				else: buff += sock.recv(1024)

			if not self.auth(buff):
				code = b'401 Unauthorized'
				slite = None

			elif buff.startswith(b'GET /'):
				slite, par = self.get(buff, sock)

			elif buff.startswith(b'POST /'):
				slite, par = self.post(buff, sock)

			else:
				code = b'405 Method Not Allowed'
				slite = None

			del buff; gc.collect()

			if slite: code = self.resp(slite, par, sock)

			if code: sock.sendall(\
				b'HTTP/1.1 %s\r\n' \
				b'Allow: GET, POST\r\n' \
				b'Connection: close\r\n' \
				b'Content-Length: %s\r\n' \
				b'WWW-Authenticate: Basic\r\n' \
				b'Accept: application/json\r\n' \
				b'text/plain; charset=utf-8\r\n' \
				b'\r\n%s' % (code, len(code), code))

		except: raise
		finally: gc.collect()

	def resp(self, slite, par, sock):

		con = mim = siz = res = None

		if slite in self.slites:
			try: con = self.slites[slite](par)
			except: return b'406 Not Acceptable'

		else:
			try: res, mim, siz = self.slite(slite)
			except: return b'404 Not Found'

		if res == None: res = str(con).encode()
		if mim == None: mim = self.mime(slite)
		if siz == None: siz = len(res)

		try:

			sock.sendall(\
				b'HTTP/1.1 200 OK\r\n' \
				b'Content-Type: %s\r\n' \
				b'Connection: close\r\n' \
				b'Content-Length: %s\r\n' \
				b'\r\n' % (mim, siz))
			sock.sendall(res)

		except: raise
		finally: del res

	def get(self, req, sock):

		e = req.find(b'\r\n')
		a = req.find(b'GET /', 0, e) + 5
		b = req.find(b' HTTP', a, e)

		if a == 4 or b == -1 or a > b:
			return str(), dict()
		else: req = req[a:b]

		par = req.find(b'?')
		d = self.unquote

		if par != -1:

			slite = d(req[0:par])
			req = req[par+1:]
			vlist = self.split(req)

		else:

			slite = d(req)
			vlist = dict()

		if slite == b'': slite = 'index.html'
		else: slite = slite.decode()

		return slite, vlist

	def post(self, req, sock):

		e = req.find(b'\r\n')
		a = req.find(b'POST /', 0, e) + 6
		b = req.find(b' HTTP', a, e)
		e = req.find(b'\r\n\r\n', e)
		d = self.unquote

		if a == 5 or b == -1 or a > b:
			return str(), dict()

		elif a == b: slite = 'index.html'
		else: slite = d(req[a:b]).decode()

		j = req.find(b'Content-Type: application/json', b, e)
		p = req.find(b'Content-Type: text/plain', b, e)
		a = req.find(b'Content-Length: ', b, e) + 15
		b = req.find(b'\r\n', a, e)

		if a == 14 or b == -1 or a > b:
			return slite, dict()

		else:
			try: leng = int(req[a:b])
			except: return slite, dict()
			else: req = req[e+4:]

		while len(req) != leng:
			try: req += sock.recv(leng - len(req))
			except: return slite, dict()
			if not req: raise BufferError

		if j != -1: vlist = json.loads(req)
		elif p != -1: vlist = req.decode()
		else: vlist = self.split(req)

		return slite, vlist

	def auth(self, req):

		if not len(self.users): return True

		s = req.find(b'\r\n') + 2
		e = req.find(b'\r\n\r\n')
		a = req.find(b'Authorization: Basic ', s, e) + 21
		b = req.find(b'\r\n', a)

		if a == 20 or b == -1 or a > b: return False
		else: auth = a2b_base64(req[a:b]).split(b':')

		if len(auth) != 2: return False
		else:
			u = auth[0].decode()
			p = auth[1].decode()

		if not u in self.users: return False
		else:
			h = hexlify(sha1(p).digest())
			ok = self.users[u] == h.decode()

		return ok

	def unquote(self, string):

		if not string: return bytes()
		if not b'%' in string: return string

		bits = string.split(b'%')
		res = [ bits[0] ]

		for s in bits[1:]:

			try:

				char = bytes([int(s[:2], 16)])
				res.append(char)
				res.append(s[2:])

			except:

				res.append(b'%')
				res.append(s)

		return b''.join(res)

	def split(self, string):

		d = self.unquote
		vlist = dict()

		for p in string.split(b'&'):

			if p.find(b'=') != -1:

				i = p.split(b'=')
				k = d(i[0]).decode()
				v = d(i[1]).decode()

				vlist[k] = v

			else:

				k = d(p).decode()
				vlist[k] = None

		return vlist

	def slite(self, path):

		if path == 'favicon.ico': path = '/obj/favicon.ico'

		elif path.endswith('.html'): path = '/http/%s' % path
		elif path.endswith('.json'): path = '/etc/%s' % path
		elif path.endswith('.css'): path = '/css/%s' % path
		elif path.endswith('.js'): path = '/src/%s' % path

		else: path = '/var/%s' % path

		try:

			with open(path, 'rb') as f:

				mime = self.mime(path)
				cont = f.read()
				size = len(cont)

		except: raise
		else: return cont, mime, size

	def mime(self, path):

		if path.startswith('/var/'): mime = b'application/json'
		elif path.endswith('.html'): mime = b'text/html'
		elif path.endswith('.json'): mime = b'application/json'
		elif path.endswith('.css'): mime = b'text/css'
		elif path.endswith('.ico'): mime = b'image/png'
		elif path.endswith('.js'): mime = b'text/javascript'

		else: mime = b'text/plain'

		return b'%s; charset=utf-8' % mime

	def defslite(self, slite, callback):

		self.slites[slite] = callback

	def rmslite(self, slite):

		del self.slites[slite]


from driving.car import *
import numpy as np
import pytest

def test_car():
  car = DubinsCarModel(speed=1.0, max_turn_rate=pi)
  s = (0.0, 0.5, -0.1)
  sp = car.dynamics(s, 0.0, 1.0)
  assert sp[0] == pytest.approx(cos(-0.1))
  assert sp[1] == pytest.approx(0.5+sin(-0.1))
  assert sp[2] == pytest.approx(2*pi-0.1)
  s = (0.0, 0.5, 0.0)
  dt = pi/4
  sp = car.dynamics(s, -(pi/2)/dt, dt)
  assert sp[0] == pytest.approx(0.5)
  assert sp[1] == pytest.approx(0.0)
  assert sp[2] == pytest.approx(2*pi-pi/2)