# -*- coding: utf-8 -*-
"""
-------------------------------------------------------------------------------
GUFY - Copyright (c) 2019, Fabian Balzer

Distributed under the terms of the GNU General Public License v3.0.

The full license is in the file LICENSE.txt, distributed with this software.
-------------------------------------------------------------------------------

@author: Fabian Balzer (fabian.balzer@studium.uni-hamburg.de)

A module for the progress bar updates and threading, including the heads of the
evaluation functions.

The module is structured as follows:
    - The ProgressDialog class for creating a progress window when plotting
    - The Worker classes for carrying out the plotting process
    - Function for evaluating single file data
    - Function for evaluating time series data
"""
import numpy as np
import traceback
import PyQt5.QtWidgets as QW
import PyQt5.QtCore as QC
import PyQt5.QtGui as QG
import simgui_modules.plots as splot  # used in eval-commands
import simgui_modules.utils as sut
from simgui_modules.additionalWidgets import GUILogger

# %% The ProgressDialog class for creating a progress window when plotting
class ProgressDialog(QW.QDialog):
    """A dialog that pops up when a plot is being made. Shows a progressbar
    and a status concerning plotting, and contains a button to stop the plot
    process. Automatically closes upon finishing.
    Parameters:
        Param_Dict: For the plot parameters.
        parent: QObject: preferably the main window.
        all_: bool: unimplemented function to plot everything
    """
    finished = QC.pyqtSignal(bool)  # signal to indicate success
    def __init__(self, Param_Dict, parent, mode, Request_Dict=None, slider=None):
        super().__init__(parent=parent)
        self.setModal(True)
        self.setWindowFlags(  # This will stop the close button from appearing
                            QC.Qt.Window |
                            QC.Qt.CustomizeWindowHint |
                            QC.Qt.WindowTitleHint |
                            QC.Qt.WindowMinimizeButtonHint |
                            QC.Qt.WindowStaysOnTopHint
                            )
        self.Request_Dict = Request_Dict
        self.mode = mode
        self.initUi()
        self.determineProgressLength(Param_Dict)
        self.setWindowTitle("Plotting in progress...")
        if mode == "Test":
            self.parent().progressWorker = TestPlotWorker()
        elif mode == "PlotSingle":
            self.parent().progressWorker = PlotSingleWorker(Param_Dict)  # Create worker for plotting
        elif mode == "PlotAll":
            self.parent().progressWorker = PlotMultipleWorker(Param_Dict,
                                                              Request_Dict,
                                                              slider)
        else:
            self.parent().progressWorker = TestPlotWorker()
        self.parent().thread = QC.QThread()  # Create thread for worker. For safety reasons leave a reference on the main window.
        self.signalsConnection()
        self.parent().progressWorker.moveToThread(self.parent().thread)
        self.parent().thread.start()  # Start the thread
        self.resize(400, self.height())
        self.setWindowIcon(QG.QIcon('simgui_registry/CoverIcon.png'))
        self.show()

    def initUi(self):
        """Initiates the visual elements, including a progress bar and a cancel
        button"""
        self.progressBar = QW.QProgressBar()
        self.infoLabel = QW.QLabel()
        self.cancelButton = QW.QPushButton("Cancel")
        buttonBox = QW.QWidget()
        buttonBoxLayout = QW.QHBoxLayout(buttonBox)
        buttonBoxLayout.addStretch(1)
        buttonBoxLayout.addWidget(self.cancelButton)
        buttonBoxLayout.setContentsMargins(0, 0, 0, 0)
        layout = QW.QVBoxLayout(self)
        layout.addWidget(self.progressBar)
        layout.addWidget(self.infoLabel)
        if self.mode == "PlotAll":
            self.multipleProgressBar = QW.QProgressBar()
            self.multipleProgressBar.setRange(0, self.Request_Dict["PlotNumber"])
            self.multipleProgressBar.setValue(0)
            layout.addWidget(self.multipleProgressBar)
            self.multiInfoLabel = QW.QLabel(f"Currently working on plot 1/{self.Request_Dict['PlotNumber']}...")
            layout.addWidget(self.multiInfoLabel)
        layout.addStretch(1)
        layout.addWidget(buttonBox)

    def determineProgressLength(self, Param_Dict):
        """Calculate the number of checkpoint steps for the current plot
        settings and format the slider accordingly."""
        self.progressBar.setRange(0, 0)
        # startplot, modifications, annotations, startplot,
        length = 6  # _setupPlots, finish
        if Param_Dict["PlotMode"] == "Profile":
            length += sut.calculateProfileAdditions(Param_Dict)
        self.progressBar.setRange(0, length)
        self.progressBar.setValue(0)

    def updateProgress(self, message):
        """Updates the progressbar by one step and sets the text of the
        infoLabel to message."""
        value = self.progressBar.value()
        self.progressBar.setValue(value + 1)
        self.infoLabel.setText(f"{message}...")

    def updateMultiProgress(self):
        oldValue = self.multipleProgressBar.value()
        self.multipleProgressBar.setValue(oldValue + 1)
        self.progressBar.setValue(0)
        text = f"{oldValue+2}/{self.Request_Dict['PlotNumber']}"
        self.multiInfoLabel.setText(f"Currently working on plot {text}...")
        GUILogger.debug(text)

    def signalsConnection(self):
        """Connect the cancelButton"""
        self.parent().progressWorker.progressUpdate.connect(self.updateProgress)
        self.parent().progressWorker.finished.connect(lambda: self.close())
        if self.mode == "PlotAll":
            self.parent().progressWorker.multiProgress.connect(self.updateMultiProgress)
        self.cancelButton.clicked.connect(self.stopProcess)
        self.parent().thread.started.connect(self.parent().progressWorker.plot)

    def keyPressEvent(self, event):
        if event.key() == QC.Qt.Key_Escape:
            self.stopProcess()

    def closeEvent(self, event):
        self.parent().thread.quit()
        super().closeEvent(event)

    def stopProcess(self):
        self.infoLabel.setText(f"Plotting interrupted. Please wait until the current step is finished...")
        self.cancelButton.setDisabled(True)
        plotWindow = self.parent().Param_Dict["CurrentPlotWindow"]
        plotWindow.restoreSettings.setDisabled(True)
        plotWindow.writeFileButton.setDisabled(True)
        plotWindow.externalWindowButton.setDisabled(True)
        self.parent().progressWorker._isRunning = False

# %% The Worker classes for carrying out the plotting process
class WorkerBase(QC.QObject):
    """A base class for objects to be used during threading"""
    finished = QC.pyqtSignal(bool)
    progressUpdate = QC.pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self._isRunning = True
        self.oldMessage = "Starting up"

class PlotSingleWorker(WorkerBase):
    """A worker to carry out a single plot"""

    def __init__(self, Param_Dict):
        super().__init__()
        self.Param_Dict = Param_Dict

    @QC.pyqtSlot()  # This is necessary to make the threading work.
    def plot(self):
        try:
            evaluateSingle(self.Param_Dict, self)
        except sut.WorkingException as e:
            GUILogger.error(str(e.args[0]))
        except Exception as e:
            traceback.print_exc()  # This will print the complete traceback including links to the lines
            GUILogger.exception("A yt-internal exception occured:
{type(e).__name__}:
'
                                f"{e}")
            GUILogger.log(29, "I've printed the traceback for you.")
            self._isRunning = False
        self.finished.emit(self._isRunning)

class PlotMultipleWorker(WorkerBase):
    """A worker to carry out multiple consecutive plots"""
    finished = QC.pyqtSignal(bool, str)
    multiProgress = QC.pyqtSignal()

    def __init__(self, Param_Dict, Request_Dict, slider):
        super().__init__()
        self.Param_Dict = Param_Dict
        self.Request_Dict = Request_Dict
        self.slider = slider

    @QC.pyqtSlot()  # This is necessary to make the threading work.
    def plot(self):
        try:
            evaluateMultiple(self.Param_Dict, self.Request_Dict, self.slider, self)
        except sut.WorkingException as e:
            GUILogger.error(str(e.args[0]))
        except Exception as e:
            traceback.print_exc()  # This will print the complete traceback including links to the lines
            GUILogger.exception("A yt-internal exception occured:
{type(e).__name__}:
'
                                f"{e}")
            GUILogger.log(29, "I've printed the traceback for you.")
            self._isRunning = False
        self.finished.emit(self._isRunning, self.Request_Dict["Directory"])

class TestPlotWorker(WorkerBase):

    @QC.pyqtSlot()  # Override this
    def plot(self):
        import time
        for i in range(100):
            if self._isRunning:
                time.sleep(0.02)
                self.progressUpdate.emit(str(i))
        if self._isRunning:
            self.success = True
        self.finished.emit()

# %% Function for evaluating single file data
def evaluateSingle(Param_Dict, worker):
    """Handles the different cases needed for evaluation of a Data or
    DataSetSeries object.
    Parameters:
        Param_Dict: For the information to be plotted
        worker: Worker object the evaluation is initiated from
    """
    mode = Param_Dict["PlotMode"]
    sut.emitStatus(worker, f"Creating the initial {mode.lower()} plot")
    GUILogger.log(29, f"Producing the requested {mode.lower()} plot...")
    # For lineplotting we need to remember the grid unit
    Param_Dict["oldGridUnit"] = Param_Dict["GridUnit"]
    # Convenient way to choose the right function:
    eval(f"splot.{mode}Plot(Param_Dict, worker)")
    sut.emitStatus(worker, "Finishing")

# %% Function for evaluating time series data
def evaluateMultiple(Param_Dict, Request_Dict, slider, worker):
    """Evaluate the series according to the settings given from the
    plotDialog. If the makeMovie-attribute from the dialog is True, ask for a
    directory, create a folder and save the figures there."""
    mode = Param_Dict["PlotMode"]
    directory = Request_Dict["Directory"]
    onlyEvery = Request_Dict["OnlyEvery"]
    plotnum = Request_Dict["PlotNumber"]
    GUILogger.log(29, f"Producing the requested {mode.lower()} plots...")
    sut.emitStatus(worker, f"Creating the initial {mode.lower()} plot")
    # For lineplotting we need to remember the grid unit
    Param_Dict["oldGridUnit"] = Param_Dict["GridUnit"]
    i = 0
    for j in range(Request_Dict["Length"]):
        if i % onlyEvery == 0:
            # The following will set the plotWindow and dataset to the one we want
            Param_Dict["SignalHandler"].getSliderInput(value=j, seriesEval=True)
            # Convenient way to choose the right plot function
            eval(f"splot.{mode}Plot(Param_Dict, worker)")
            GUILogger.info(f"Progress: {int(i/onlyEvery+1)}/{plotnum} {mode.lower()} plots done.")
            if Request_Dict["MakeMovie"]:
                saveName = f"{directory}/{mode}plot_{i+1}"
                Param_Dict["CurrentPlotWindow"].saveFigure(saveName)
            sut.emitMultiStatus(worker, i, plotnum)
        i += 1
    slider.setValue(j)


class Solution:
    def sumOfUnique(self, nums: List[int]) -> int:
        li = []
        re = []
        for i in nums:
            if i not in li:
                li.append(i)
            else:
                if i not in re:
                    re.append(i)

        nums = [j for j in nums if j not in re]
        return sum(nums)


'''
85. Maximal Rectangle

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.

Example:
Input:
[
  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]
]
Output: 6
'''

class Solution:
    # based on problem 84
    def largestRectangleArea(self, heights):
        """
        :type heights: List[int]
        :rtype: int
        """
        if not heights: return 0
        stack, index, maxArea = [], 0, 0
        while index < len(heights):
            if not stack: stack.append((heights[index],index))
            else:
                if heights[index] > stack[-1][0]: stack.append((heights[index],index))
                elif heights[index] < stack[-1][0]:
                    while True:
                        if stack:
                            tailNum = stack[-1][0]
                            if tailNum > heights[index]:
                                tailIndex = stack[-1][1]
                                area = tailNum * (index-tailIndex)
                                maxArea = area if area > maxArea else maxArea
                                stack.pop()
                            elif tailNum < heights[index]:
                                stack.append((heights[index],tailIndex))
                                break
                            else: break
                        else:
                            stack.append((heights[index],tailIndex))
                            break
                else: pass
            index += 1

        if stack:
            for s in stack:
                area = s[0] * (index-s[1])
                maxArea = area if area > maxArea else maxArea
        return maxArea

    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        if not matrix: return 0
        row, col, heights, maxArea = len(matrix), len(matrix[0]), [], 0
        for i in range(row):
            for j in range(col):
                n = int(matrix[i][j])
                if i == 0:
                    heights.append(n)
                else:
                    if not n: heights[j] = 0
                    else: heights[j] += 1
            area = self.largestRectangleArea(heights)
            maxArea = area if area > maxArea else maxArea
        return maxArea

class Solution2:
    # using dynamic programming
    # heights[j]: height of j-th col to the i-th row
    # left[j]: leftmost index of height[j]
    # right[j]: rightmost index of height[j]
    def init(self, col):
        res = []
        for i in range(col):
            res.append(0)
        return res

    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        if not matrix: return 0
        row, col, maxArea = len(matrix), len(matrix[0]), 0
        heights, left, right = self.init(col), self.init(col), self.init(col)
        for i in range(row):
            for j in range(col):
                n = int(matrix[i][j])
                if i == 0:
                    heights[j] = n
                else:
                    if not n: heights[j] = 0
                    else: heights[j] += 1

            currLeft = 0
            for j in range(col):
                n = int(matrix[i][j])
                if i == 0:
                    if not n:
                        left[j] = -1
                        currLeft = j+1
                    else: left[j] = currLeft
                else:
                    if not n:
                        left[j] = -1
                        currLeft = j+1
                    else: left[j] = max(currLeft,left[j])

            currRight = col-1
            for j in range(col-1,-1,-1):
                n = int(matrix[i][j])
                if i == 0:
                    if not n:
                        right[j] = col
                        currRight = j-1
                    else: right[j] = currRight
                else:
                    if not n:
                        right[j] = col
                        currRight = j-1
                    else: right[j] = min(currRight,right[j])
            '''
            print('heights:',heights)
            print('left:',left)
            print('right:',right)
            print('-'*10)
            '''
            for j in range(col):
                maxArea = max(maxArea, heights[j] * (right[j] - left[j] + 1))
        return maxArea


# Authors: Clyde Sumagang and Roy Morla
# Date: 9/22/ 2019
# Course: CST 205
# Abstract: This program will count the rgb values in a matrix and store them into a dictionary
#           with 4 bins based on color and intensity
import pickle

file = open('image_matrix', 'rb')
data = pickle.load(file)

def task1(data):

    #dictionary to hold levels respective rgb intensity values
    histo = {
            'red': [0,0,0,0],
            'green': [0,0,0,0],
            'blue': [0,0,0,0]
    }

    #for loop to iterate through length of the outer list
    for x in range(len(data)):
        #changes what values list comprehension statements use
        real_data = data[x]

        #list comprehension to split up rgb data from tuples
        red_data = [x[0] for x in real_data]
        green_data = [x[1] for x in real_data]
        blue_data = [x[2] for x in real_data]

        #logic for red data to increment dictionary list values
        for i in red_data:
            if (i <= 63):
                histo['red'][0] += 1
            elif (i <= 127 and i >= 64):
                histo['red'][1] += 1
            elif (i <= 191 and i >= 128):
                histo['red'][2] += 1
            elif (i <= 255 and i >= 192):
                histo['red'][3] += 1

        #logic for green data to increment dictionary list values
        for i in green_data:
            if (i <= 63):
                histo['green'][0] += 1
            elif (i <= 127 and i >= 64):
                histo['green'][1] += 1
            elif (i <= 191 and i >= 128):
                histo['green'][2] += 1
            elif (i <= 255 and i >= 192):
                histo['green'][3] += 1

        #logic for blue data to increment dictionary list values
        for i in blue_data:
            if (i <= 63):
                histo['blue'][0] += 1
            elif (i <= 127 and i > 64):
                histo['blue'][1] += 1
            elif (i <= 191 and i >= 128):
                histo['blue'][2] += 1
            elif (i <= 255 and i >= 192):
                histo['blue'][3] += 1

    return histo

print (task1(data))


def linear_search(data, item):
    index = 0
    found = False

    while index < len(data):
        if data[index] == item:
            found = True
        else:
            index += 1

    return found, index

def binary_search(data, item):
    first = 0
    last = len(data) - 1
    found = False

    while first <= last and not found:
        midpoint = (first + last) // 2

        if data[midpoint] == item:
            found = True
        else:
            if item < data[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1

    return found

def interpolation_serach(list,x ):
    idx0 = 0
    idxn = (len(list) - 1)
    found = False
    while idx0 <= idxn and x >= list[idx0] and x <= list[idxn]:
        # Find the mid point
        mid = idx0 +int(((float(idxn - idx0)/( list[idxn] - list[idx0])) *( x - list[idx0])))
        # Compare the value at mid point with search value
        if list[mid] == x:
            found = True
            return found
        if list[mid] < x:
            idx0 = mid + 1

    return found

data = [12,13, 11, 99, 22, 55, 90]
print(binary_search(sorted(data), 99))


import os

from default.liststc import splitter_to_array, add_list, length, convert_arr_to_type, el_in_array

delimiter = ';'

def write(header, arr, folder_name, file_name, url_file=None):
    url = url_file
    url_none = True if not url else False
    folder_found = False
    url = ''

    if url is None:
        for (root, dirs, files) in os.walk('..', topdown=True):

            # memvalidasi apakah directory sesuai dengan nama folder
            if el_in_array(folder_name, dirs):
                url = os.path.join(root, folder_name)
                folder_found = True

                # menurut spek, jika file dengan nama yang sama ditemukan, maka harus menghapus existing file
                # terlebih dahulu
                if el_in_array(file_name, files):
                    os.remove(file_name)
    else:
        folder_found=True

    # membuat folder baru jika tidak ada folder bername file_name
    if not folder_found:
        os.mkdir(path=folder_name)
        url = folder_name

    # proses writing
    with open(os.path.join(url, file_name), 'w') as file:

        # write csv header
        for i in range(length(header)):
            file.write(header[i] + delimiter)

        file.write('\n')

        # write the data
        for i in range(0, length(arr)):

            for j in range(length(arr[i])):
                file.write(str(arr[i][j]) + delimiter)

            file.write('\n')

    # untuk optimisasi, kita melakukan pencarian url hanya sekali untuk beberapa file dengan cara melempar
    # url jika url awalnya tidak dimiliki (ditandai dengan variabel url_none)
    return url if url_none else None

def read(folder_name, file_name, type_arr=None, function_validator=None, function_search=None,
         validator_param=None, search_param=None, url_file=None):
    # penjelasan parameter fungsi
    #
    # folder_name: nama folder
    #
    # file_name: nama_file
    #
    # function_validator: jika tidak semua lines dari file ingin disimpan sebagai array, maka akan dilakukan validasi
    #                     di tiap line data menggunakan fungsi ini
    #
    # function_search: jika ingin menggunakan data di tiap line file untuk melakukan proses search, maka fungsi
    #                  function_search akan dijalankan. karena berada di fungsi load, artinya tidak akan ada field
    #                  yang merupakan input user selain folder_name, maka diasumsikan fungsi ini akan selalu memberikan
    #                  array yang memiliki isi
    #
    # validator_param: parameter yang akan digunakan sebagai validator di function_validator
    #
    # search_param: parameter yang akan digunakan function_search

    url = url_file
    url_none = True if not url_file else False
    folder_found = False

    # proses pencarian file. proses ini bisa mencakup folder dimanapun jika masih merupakan child dari folder
    # utama program
    if url is None:
        for (root, dirs, files) in os.walk('..', topdown=True):
            if el_in_array(folder_name, dirs):
                url = os.path.join(root, folder_name)
                folder_found = True
    else:
        folder_found = True

    if not folder_found:
        raise FileNotFoundError

    with open(os.path.join(url, file_name)) as file:

        raw = file.readlines()

        data_arr = []

        for i in range(length(raw)):

            # karena line 0 dari data merupakan header, maka kita bisa melewati i = 0
            if i == 0:
                pass

            else:
                # memecah string tiap line menjadi array berdasarkan delimiter
                data = splitter_to_array(raw[i], delimiter)

                # pemasukan data ke array. menggunakan parameter yang sudah dijelaskan di atas
                if type_arr is not None:
                    data = convert_arr_to_type(data, type_arr)

                if function_validator is None:

                    if function_search is None:
                        data_arr = add_list(data_arr, data)
                    else:
                        search = function_search(search_param, data)
                        data_arr = add_list(data_arr, search)

                else:

                    if function_validator(data, validator_param):
                        if function_search is None:
                            data_arr = add_list(data_arr, data)

                        else:
                            search = function_search(search_param, data)
                            data_arr = add_list(data_arr, search)

    # untuk optimisasi, kita hanya akan melakukan pencarian url sekali, lalu melempar url yang sudah ditemukan
    if url_none:
        return data_arr, url
    else:
        return data_arr


__title__ = 'pyfcm'
__summary__ = 'Python client for FCM - Firebase Cloud Messaging (Android, iOS and Web)'
__url__ = 'https://github.com/olucurious/pyfcm'

__version__ = '1.5.2'

__author__ = 'Emmanuel Adegbite'
__email__ = 'olucurious@gmail.com'

__license__ = 'MIT License'


import os.path
import pandas
import numpy as np

def opt_report(reportPath, snrTh=0.9, debug=False, plotError=True):
    df = pandas.read_csv(reportPath)

    totalNbLoop = list(df["nbLoop"])[-1]
    # print(totalNbLoop)
    loopList = []
    rmseList = []
    avgErrorList = []
    for loop_ in range(totalNbLoop + 1):
        if debug:
            print("------ Loop:{} -------".format(loop_))
        itemList = []
        dxPixList = []
        dyPixList = []
        snrList = []
        dxList = []
        dyList = []
        for item, dxPix_, dyPix_, snr_ in zip(list(df["nbLoop"]), list(df["dxPix"]), list(df["dyPix"]),
                                              list(df["SNR"])):
            if item == loop_:
                itemList.append(item)
                dxPixList.append(dxPix_)
                dyPixList.append(dyPix_)
                snrList.append(snr_)

        nanList = [item_ for item_ in snrList if item_ == 0]
        snrThList = [item_ for item_ in snrList if item_ > snrTh]
        dxPixAvg = np.nanmean(np.asarray(dxPixList))
        dyPixAvg = np.nanmean(np.asarray(dyPixList))

        dxPixRMSE = np.nanstd(np.asarray(dxPixList))
        dyPixRMSE = np.nanstd(np.asarray(dyPixList))

        xyErrorAvg = np.sqrt(dxPixAvg ** 2 + dyPixAvg ** 2)
        xyRMSE = np.sqrt(dxPixRMSE ** 2 + dyPixRMSE ** 2)
        if debug:
            print("#GCPs:{} --> #NaNs:{} ; #snrTh >{}:{}".format(len(itemList), len(nanList), snrTh, len(snrThList)))
            print("dxPixAvg:{}  , xRMSE:{}".format("{0:.4f}".format(dxPixAvg),

                                                   "{0:.2f}".format(dxPixRMSE)))
            print("dyPixAvg:{}  , yRMSE:{}".format("{0:.4f}".format(dyPixAvg),

                                                   "{0:.2f}".format(dyPixRMSE)))
            print("xyErrorAvg:{}  , xyRMSE:{}".format("{0:.4f}".format(xyErrorAvg),

                                                      "{0:.2f}".format(xyRMSE)))
        loopList.append(loop_)
        rmseList.append(xyRMSE)
        avgErrorList.append(xyErrorAvg)
    indexMin = np.argmin(avgErrorList)
    # if debug:
    print("Loop of Min Error:{} --> RMSE:{:.3f} , avgErr:{:.3f}".format(loopList[indexMin], np.min(rmseList),
                                                                        np.min(avgErrorList)))
    if plotError:
        import matplotlib.pyplot as plt
        from matplotlib.ticker import (AutoMinorLocator)
        fig, ax = plt.subplots()

        ax.plot(loopList, rmseList, c="r", linestyle="--", marker="o", label="RMSE [pix]")

        ax.plot(loopList, avgErrorList, c="g", linestyle="-", marker="o", label="meanErr [pix]")

        ax.grid()
        ax.legend()
        ax.xaxis.set_minor_locator(AutoMinorLocator())
        ax.yaxis.set_minor_locator(AutoMinorLocator())

        ax.tick_params(which='both', width=2, direction="in")
        ax.set_xlabel('#iterations')
        ax.set_ylabel("Error [pix]")
        # plt.show()
        fig.savefig(os.path.join(os.path.dirname(reportPath), "CoregistrationError.png"), dpi=400)

    return loopList[indexMin], totalNbLoop, np.min(avgErrorList)

def parse_opt_report(opt_report_path):
    df = pandas.read_csv(opt_report_path)

    nb_loops = list(df["nbLoop"])[-1]
    loopList = []
    rmse = []
    avg_error = []
    for loop_ in range(nb_loops + 1):

        itemList = []
        dxPixList = []
        dyPixList = []
        snrList = []
        for item, dxPix_, dyPix_, snr_ in zip(list(df["nbLoop"]), list(df["dxPix"]), list(df["dyPix"]),
                                              list(df["SNR"])):
            if item == loop_:
                itemList.append(item)
                dxPixList.append(dxPix_)
                dyPixList.append(dyPix_)
                snrList.append(snr_)
        dxPixAvg = np.nanmean(np.asarray(dxPixList))
        dyPixAvg = np.nanmean(np.asarray(dyPixList))

        dxPixRMSE = np.nanstd(np.asarray(dxPixList))
        dyPixRMSE = np.nanstd(np.asarray(dyPixList))

        xyErrorAvg = np.sqrt(dxPixAvg ** 2 + dyPixAvg ** 2)
        xyRMSE = np.sqrt(dxPixRMSE ** 2 + dyPixRMSE ** 2)
        loopList.append(loop_)
        rmse.append(xyRMSE)
        avg_error.append(xyErrorAvg)
    idx_min = np.argmin(avg_error)
    loop_min_err = loopList[idx_min]
    # print("Loop of Min Error:{} --> RMSE:{:.3f} , avgErr:{:.3f}".format(loopList[indexMin], np.min(rmse),
    #                                                                     np.min(avg_error)))

    return rmse, avg_error, loop_min_err


"""You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps.

In how many distinct ways can you climb to the top?"""

"""Example 1:

Input: n = 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps"""

# goal is two climb to two
# can take one step = 1
# two steps at once = 2
# can either take one step twice or one two step to get to 2

"""Example 2:

Input: n = 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step"""

# time complexity = O(n)
# Solution
class Solution:
# create a function called climbStairs
    def climbStairs(self, n):
# have two variables that are both initialized as one
        one_step = 1
        two_step = 1
# loop through n minus 1 time
# continuously update the two variables one & two
        for i in range(n - 1):
# temp variable before other variables are updated
            temp_num = one_step
# update one. one is set to one plus two | adding two previous values = new result
            one_step = one_step + two_step
# update two to whatever the previous value of what one was | updating one
# before we update two
# set two to the temporary variable | avoid setting it to one plus two
            two_step = temp_num
# return what one happens to land on
        return one_step

if __name__ == "__main__":
    test = Solution()
    input = test.climbStairs(3)
    print(input)

"""The time complexity of this function is O(n) since it iterates through the
loop n-1 times, and each iteration takes constant time. The time complexity is linear in terms of the input size n."""


from sklearn.linear_model import LogisticRegression
import pandas as pd
import numpy as np
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sn
import matplotlib.pyplot as plt

data = pd.read_csv('./Data/wine.csv')
data = data.sample(frac=1, random_state=42).reset_index(drop=True)

ndata = data.shape[0]
ncolumn = data.shape[1]

train_rate = 0.7
ntrain = int(ndata * train_rate)
train_index = range(ntrain)
test_index = range(ntrain, ndata)

train, test = data.iloc[train_index,], data.iloc[test_index,]
train_x, train_y = train.iloc[:,:-1], train.iloc[:,-1]
test_x, test_y = test.iloc[:,:-1], test.iloc[:,-1]

log = LogisticRegression()
log.fit(train_x,train_y)
estimates = log.predict(train_x)

C=confusion_matrix(train_y,estimates)
TN, FP, FN, TP = C.ravel()

Accuracy= accuracy_score(train_y,estimates)
Precision=float(TP/(TP+FP))
Recall=float(TP/(TP+FN))
Specificity=float(TN/(TN+FP))
F1measure=float(2*Precision*Recall/(Precision+Recall))
Gmean=float(np.sqrt(Precision*Recall))

print("This solution is computed using train data")
print(C)
print("Accuracy using train data is: %.3f"%(Accuracy))
print("Precision : %.3f, Recall : %.3f, Specificity : %.3f, F1measure : %.3f, G-mean : %.3f" %(Precision, Recall, Specificity, F1measure, Gmean))
print("Type 1 error : %.3f, Type 2 error : %.3f\n"%(1-Specificity, 1-Recall))

estimates2 = log.predict(test_x)

C2=confusion_matrix(test_y,estimates2)
TN2, FP2, FN2, TP2 = C2.ravel()

Accuracy2 = accuracy_score(test_y, estimates2)
Precision2 = float(TP2 / (TP2 + FP2))
Recall2 = float(TP2 / (TP2 + FN2))
Specificity2 = float(TN2 / (TN2 + FP2))
F1measure2 = float(2 * Precision2 * Recall2 / (Precision2 + Recall2))
Gmean2 = float(np.sqrt(Precision2 * Recall2))

print("This solution is computed using test data")
print(C2)
print("Accuracy using test data is: %.3f" % (Accuracy2))
print("Precision : %.3f, Recall : %.3f, Specificity : %.3f, F1measure : %.3f, G-mean : %.3f" % (
Precision2, Recall2, Specificity2, F1measure2, Gmean2))
print("Type 1 error : %.3f, Type 2 error : %.3f\n" % (1 - Specificity2, 1 - Recall2))

df_cm = pd.DataFrame(C, ['Actual N','Actual P'],['Predicted N','Predicted P'])
df_cm2 = pd.DataFrame(C2, ['Actual N','Actual P'],['Predicted N','Predicted P'])

fig = plt.figure()
ax1 = fig.add_subplot(211)
ax1.set(title='Confusion Matrix of Train Data')

ax2 = fig.add_subplot(212)
ax2.set(title='Confusion Matrix of Test Data')

sn.heatmap(df_cm, annot=True, fmt='d', ax=ax1, annot_kws={"size": 16})
sn.heatmap(df_cm2, annot=True, fmt='d', ax=ax2, annot_kws={"size": 16})
plt.tight_layout()
plt.show()


from distutils.core import setup, Extension

from Cython.Build import cythonize

include_dirs_list = [
                    "../include",
                    "../Thirdparty/libccd/src", #libccd
                    "../Thirdparty/libccd/build/src", #libccd
                    "../Thirdparty/yaml-cpp/include", # yaml-cpp
                    "../Thirdparty/boost_1_7_0", # boost
                    "../Thirdparty/eigen", # eigen
                    "../Thirdparty/googletest/googletest/include", # gtest
                    "../Thirdparty/octomap/octomap/include", # octomap ?
                    "../Thirdparty/fcl/build/include", # fcl
                    "../Thirdparty/fcl/include", # fcl

]

setup(ext_modules = cythonize(Extension(
           "pympl",                                # the extension name
           sources=["pympl.pyx"],       # the Cython source and additional C++ source files
           language="c++",                        # generate and compile C++ code
           include_dirs=include_dirs_list,
           library_dirs=["../build"],
           libraries=["mpl"],
           extra_compile_args=["-std=c++11"]
      )))


from pathlib import Path
import pandas as pd
from . import integration
from model import advanced_controls as ac
from model import aez
from model import dd
from model import vma
from model import world_land
from solution import factory

standard_land_allocation_types = list(world_land.AEZ_ALLOCATION_MAP.keys()) + ["Add-On Solutions"]

standard_land_solution_priorities = {
    'Non-Degraded Forest':
        ['peatland', 'mangroverestoration', 'indigenouspeoplesland', 'forestprotection', 'multistrataagroforestry'],
    'Degraded Forest':
        ['tropicalforests', 'temporateforests', 'BOREAL FOREST', 'peatland', 'mangroverestoration', 'bamboo', 'afforestation'],
    'Non-Degraded Grassland':
        ['peatland', 'grasslandprotection', 'multistrataagroforestry', 'tropicaltreestaples', 'silvopasture', 'managedgrazing'],
    'Degraded Grassland':
        ['afforestation', 'farmlandrestoration', 'perennialbioenergy'],
    'Non-Degraded Cropland':
        ['tropicalforests', 'peatland', 'riceintensification', 'improvedrice', 'conservationagriculture', 'treeintercropping'],
    'Degraded Cropland':
        ['treeintercropping'],
    'Add-On Solutions':
        ['improvedcattlefeed', 'regenerativeagriculture', 'irregationefficiency', 'nutrientmanagement', 'SUSTAINABLE INTENSIFICATION']
}
"""The prioritization amongst Land solutions for access to land in each land allocation type.
Any land solution not on this list will be assumed to be lower priority than these."""
# UPPER CASE are items in the integration workbook that don't correspond to any solution known to me:
# BOREAL FOREST, SUSTAINABLE INTENSIFICATION

# VMAS={
#     'Current Adoption': vma.VMA(
#         filename=THISDIR.joinpath("vma_data", "Current_Adoption.csv"),
#         use_weight=False)
# }

class AEZ_Land_Integration:
    """The AEZ / Land Integration looks at competition between LAND solutions for land of different types,
    and adjusts land availability accordingly.
    """

    def assemble_current_status(self, scenario_list=None):
        """Perform the first step of the integration, which is to collate the current adoptions of all
        the scenarios across all allocation regions and TMRs.  By default, the drawdown PDS2 scenario is
        used for all Land solutions.  An alternative list (with differing solutions and/or scenario choices)
        may be provided instead.
        """
        if scenario_list:
            self.scenario_list = scenario_list
            self.solution_list = [ _map_scenario_to_module(scenario) for scenario in self.scenario_list ]
        else:
            self.solution_list = factory.all_solutions_category(ac.SOLUTION_CATEGORY.LAND)
            self.scenario_list = [ factory.solution_pds_type(x, "PDS2") for x in self.solution_list ]

        self.world_land_availability = world_land.World_TMR_AEZ_Map(series_name="2020")
        #).reduce_columns(world_land.AEZ_ALLOCATION_MAP)

        per_solution_allocations  = {}
        for scenario in self.scenario_list:
            sc_dict = scenario.ae.world_land_alloc_dict
            per_solution_allocations[scenario.name] = pd.concat( sc_dict.values(), keys=sc_dict.keys() )

        self.all_solution_allocations = pd.concat( per_solution_allocations.values(), keys=per_solution_allocations.keys() )

        # What we want is triple-index (allocation_zone, solution, tmr) and these columns
        #    "Total Area", "Area Available for Solution", "Solution Current Adoption", "Solution Current Adoption %",
        #    ... more stuff.  Let's start there.

        # Then we have to sort the data by priority within the different Landtypes

def _map_scenario_to_module(scenario):
    """Given a scenario, return the common module name (e.g. 'afforestation') of the solution"""
    fullmodule = scenario.__module__
    period = fullmodule.rfind('.')
    return fullmodule[period+1:]


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

class DoublyLinkedList:
    def __init__(self):
        self.head = None
        self.tail = None

    def prepend(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.tail = new_node
            return
        new_node.next = self.head
        self.head.previous = new_node
        self.head = new_node

    def append(self, data):
        new_node = Node(data)
        if self.head is None:
            self.head = new_node
            self.tail = new_node
            return
        self.tail.next = new_node
        new_node.previous = self.tail
        self.tail = new_node

    def add_to_middle(self, data, prev_data):
        new_node = Node(data)
        current_node = self.head
        while current_node:
            if current_node.data == prev_data:
                new_node.next = current_node.next
                current_node.next = new_node
                new_node.previous = current_node
                new_node.next.previous = new_node

            current_node = current_node.next

    def pop(self):
        if self.tail is None:
            return 'List is empty'
        data = self.tail.data
        if self.tail.previous:
            self.tail = self.tail.previous
            self.tail.next = None
        else:
            self.tail = None
            self.head = None
        return data

    def print_first_to_last(self):
        current_node = self.head
        full_list = []
        while current_node:
            full_list.append(current_node.data)
            current_node = current_node.next
        print(full_list)

    def print_last_to_first(self):
        current_node = self.tail
        full_list = []
        while current_node:
            full_list.append(current_node.data)
            current_node = current_node.previous
        print(full_list)

if __name__ == '__main__':
    doubly_linked_list = DoublyLinkedList()
    doubly_linked_list.prepend('Third')
    doubly_linked_list.prepend('Second')
    doubly_linked_list.prepend('First')
    doubly_linked_list.append('Fourth')
    doubly_linked_list.add_to_middle('Test', 'Two')
    doubly_linked_list.print_first_to_last()
    doubly_linked_list.print_last_to_first()

    print('-------Print head and tail --------')
    print(doubly_linked_list.head.data)
    print(doubly_linked_list.tail.data)

    print('-------Pop data--------')
    print(doubly_linked_list.pop())
    doubly_linked_list.print_first_to_last()
    print(doubly_linked_list.pop())
    print(doubly_linked_list.pop())
    print(doubly_linked_list.pop())
    print(doubly_linked_list.pop())


import csv
import pandas as pd
import numpy as np

data = pd.read_csv("insurance.csv")

ages = []
sexes = []
bmis = []
num_children = []
smoker_statuses = []
regions = []
insurance_charges = []

def load_list_to_data(lst, csv_file, column_name):
    # open csv file
    with open(csv_file) as csv_info:
        # read the data from the csv file
        csv_dict = csv.DictReader(csv_info)
        # loop through the data in each row of the csv
        for row in csv_dict:
            # add the data from each row to a list
            lst.append(row[column_name])
        # return the list
        return lst

load_list_to_data(ages, "insurance.csv", 'age')
load_list_to_data(sexes , "insurance.csv", 'sex')
load_list_to_data(bmis , "insurance.csv", 'bmi')
load_list_to_data(num_children , "insurance.csv", 'children')
load_list_to_data(smoker_statuses , "insurance.csv", 'smoker')
load_list_to_data(regions , "insurance.csv", 'region')
load_list_to_data(insurance_charges , "insurance.csv", 'charges')

# list_of_patients = [ages,sexes,bmis, num_children, smoker_statuses, regions, insurance_charges]

# patient_dict = {z[0]: list(z[1:]) for z in zip(*list_of_patients)}
# print(patient_dict)

class PatientInfo:

    def __init__(self, patient_age, patient_sex, patient_bmi,
                 patient_children, patient_smoke, patient_region, patient_charge):
        self.patient_age = patient_age
        self.patient_sex = patient_sex
        self.patient_bmi = patient_bmi
        self.patient_children = patient_children
        self.patient_smoke = patient_smoke
        self.patient_region = patient_region
        self.patient_charge = patient_charge

    def average_smoker(self):

    	total_smokers = []
    	total_na_smoker = []

    	for element in self.patient_smoke:
    		if element == 'yes':
    			total_smokers.append(element)
    		elif element == 'no':
    			total_na_smoker.append(element)

    	total_smoker_mean = round(len(total_smokers) / len(self.patient_smoke), 3)
    	total_na_smoker_mean = round(len(total_na_smoker) / len(self.patient_smoke), 3)

    	print("Average patients that smokes:", total_smoker_mean)
    	print("Average patients that do not smoke:", total_na_smoker_mean)

    	if total_smoker_mean > total_na_smoker_mean:
    		print("According currenlty our database indicates average person that smokes greater than people does not smoke.")
    	elif total_na_smoker_mean > total_smoker_mean:
    		print('According currently our database indicates average person does not smoke greater than people who actually smoke.')

    def update_age(self, new_age):
    	""" Updates Patient's age """
    	self.patient_ages = new_age
    	new_age = int(new_age)
    	print("Patient's new age is {}".format(self.patient_age))

    def update_children(self, num_of_child):
    	self.patient_children = num_of_child

    	if num_of_child <= 0:
    		print(f'You have no child.')

    	elif num_of_child == 1:
    		print(f"You have got a single child.")
    	else:
    		print(f"You have {self.patient_children} children's.")

    def update_smoke(self, update_smoke=None):

    	if update_smoke == 0:
      		print("Well done, smoking it is not good for you plus makes your insurance cheaper.")

    	elif update_smoke >= 1:
      		print("Consider quit smoking to have a healty life and make your insurance cheaper.")

    def change_of_sex(self, gender=None):

    	if type(gender) is int:
    		print('Number is invalid entry. Enter a your gender')
    	else:
    		print(f"Succed. Information has been changed to {gender}.")

    def analyze_ages(self): # Copied.
        # initialize total age at zero
        total_age = 0
        # iterate through all ages in the ages list
        for age in self.patient_age:
            # sum of the total age
            total_age += int(age)
        # return total age divided by the length of the patient list
        return ("Average Patient Age: " + str(round(total_age/len(self.patient_age), 2)) + " years")

    def create_dictionary(self):
        self.patients_dictionary = {}
        self.patients_dictionary["Age"] = [int(age) for age in self.patient_age]
        self.patients_dictionary["Sex"] = self.patient_sex
        self.patients_dictionary["BMI"] = self.patient_bmi
        self.patients_dictionary["Num Of Child"] = self.patient_children
        self.patients_dictionary["Smoke Status"] = self.patient_smoke
        self.patients_dictionary["Regions"] = self.patient_region
        self.patients_dictionary["charges"] = self.patient_charge
        return self.patients_dictionary

    def gender_count(self):
    	male_count = 0
    	female_count = 0

    	for gender in self.patient_sex:

    		if gender == 'male':
    			male_count += 1

    		elif gender == 'female':
    			female_count += 1
    	print("Male Count:", str(male_count))
    	print("Female Count:", str(female_count))

    def unique_region(self):
    	unique = []

    	for element in self.patient_region:
    		if element not in unique:
    			unique.append(element)
    	return unique

    def average_charges(self):

    	total_charges = 0

    	for element in self.patient_charge:

    		total_charges += float(element)
    	return ("Average Yearly Medical Insurance Charges: " +
                str(round(total_charges/len(self.patient_charge), 2)) + " dollars.")

    def average_gender(self):

    	total_male = []
    	total_female = []

    	for element in self.patient_sex:

    		if element == 'male':
    			total_male.append(element)

    		elif element == 'female':
    			total_female.append(element)

    	print("According in our data set we have got total of males", len(total_male),
    	 "average Male", round(len(total_male) / len(self.patient_sex), 2))

    	print("According in our data set we have got total of Females", len(total_female),
    	 "average Female", round(len(total_female) / len(self.patient_sex), 2))

    def gender_vs_charge(self):

        # Change everything into float
        self.patient_charge = [float(i) for i in self.patient_charge]

        comprasion = list(zip(self.patient_sex, self.patient_charge))

        male_cost = 0
        female_cost = 0
        total_male = 0
        total_female = 0

        for k, v in comprasion:
            if k == 'male':
                male_cost += v
                total_male += 1

            elif k == 'female':
                female_cost += v
                total_female += 1

        average_male_insurance_chargers = round(male_cost / total_male, 2)
        average_female_insurance_chargers = round(female_cost / total_female, 2)

        return "Average Male Insurance cost is: " + str(average_male_insurance_chargers) + "\nAverage Female Insurance cost is: " + str(average_female_insurance_chargers)

    def average_bmi_gender(self):

        # Turn every single bmi into float

        bmi = [float(i) for i in self.patient_bmi]
        sex = [str(i) for i in self.patient_sex]

        sex_bmi = list(zip(sex, bmi))

        male_bmi = 0.0
        female_bmi = 0.0
        male_total = 0
        female_total = 0

        for gender, bmi in sex_bmi:

            if gender == 'male':
                male_bmi += bmi
                male_total += 1
            else:
                female_bmi += bmi
                female_total += 1

        male_bmi = round(male_bmi / male_total, 2)
        female_bmi = round(female_bmi / female_total, 2)
        return "Average Male Bmi is: " + str(male_bmi) + "\nAverage Female Bmi is: " + str(female_bmi)

patients = PatientInfo(ages, sexes, bmis, num_children, smoker_statuses, regions, insurance_charges)

patients.update_children(1)
patients.update_smoke(0)
patients.change_of_sex('Male')
patients.analyze_ages()
patients.gender_count()
print(patients.unique_region())
print(patients.average_charges())
patients.average_gender()
patients.average_smoker()
patients.create_dictionary()
print(patients.gender_vs_charge())
print(patients.average_bmi_gender())


cookbook = {'sandwich' :  {'ingredients' : ['ham', 'bread', 'cheese', \
'tomatoes'] , 'meal' : 'lunch', 'prep_time' : 10}, \
'cake' : {'ingredients' : ['flour', 'sugar', 'eggs'] , 'meal' : 'dessert', \
'prep_time' : 60}, \
'salad' : {'ingredients' : ['avocado', 'arugula', 'tomatoes', 'spinach'] , \
'meal' : 'lunch', 'prep_time' : 15}}

def print_my_cookbook(cookbook):
    print('Let\'s see what we have here...\n')
    for recipe in cookbook:
        print_recipe(recipe)
        print('\n')

def print_recipe(recipe=''):
    if recipe == '':
        print("\nNo recipe, grandma can't guess\n")
        return
    if not recipe in cookbook:
        print("\nGrandma never wrote this recipe\n")
        return
    print('Recipe for grandma\'s {}'.format(recipe))
    for value in cookbook[recipe]:
            if value == 'ingredients':
                print('Ingredients :')
                for ingr in cookbook[recipe][value]:
                    print('  -', ingr)
            if value == 'meal':
                print('For {} time !'.format(cookbook[recipe][value]))
            if value == 'prep_time':
                print('Take only {} minutes !'.format(cookbook[recipe][value]))

def delete_recipe(recipe):
    if recipe == '':
        print("\nNo recipe, grandma can't guess\n")
        return
    if not recipe in cookbook:
        print("\nGrandma never wrote this recipe\n")
        return
    del cookbook[recipe]

def new_recipe(name='', ingr='', meal_t='', time=''):
    if name == '' or ingr == '' or meal_t == '' or time == '':
        print("\nSomething is missing, grandma can't guess\n")
        return
    if name in cookbook:
        print("\nGrandma already wrote it\n")
        return
    cookbook[name] = dict(ingredients = ingr, meal = meal_t, prep_time = time)

print('You have found the old grandma\'s cookbook\n what are you going to do ?\
')

choice = 0
ing_list = []
while choice != 5:
    choice = int(input('1.Add a recipe\n2.Delete a recipe\n3.Print a recipe\
    \n4.Print the cookbook\
    \n5.Leave the book the alone\n\n'))
    if choice == 1:
        print('\nBe careful with the pages my dear...\n\
        They are older than you\n\n')
        print('\nFirst choose a name for the recipe:\n')
        name = input()
        print('\nWrite the ingredient if you\'re done tap enter with nothing')
        ingre = input()
        while ingre != '':
            ing_list.append(ingre)
            ingre = input()
        print('\nWhat kind of meal is it?')
        meal_t = input()
        print('\nHow many time it takes for a mortal to cook it?')
        prep_time = int(input())
        new_recipe(name, ing_list, meal_t, prep_time)
        ingre = ''
        ing_list = []
        name = ''
        meal_t = ''
        prep_time = 0
    if choice == 2:
        print('\nYou can burn one of this page:\n')
        for recipe in cookbook:
            print('{}'.format(recipe))
        print('choose:')
        name = input()
        delete_recipe(name)
        name = ''
    if choice == 3:
        print('\nYou can look at one of this page:\n')
        for recipe in cookbook:
            print('{}'.format(recipe))
        print('choose:')
        name = input()
        print_recipe(name)
        name = ''
    if choice == 4:
        print_my_cookbook(cookbook)
print('The book is closed')


s = input()
n = len(s)
k ="keyence"

flg = 1

# はじめて違う文字を見つけたら、マイナスインデックスで後ろからも同時に見ていく（かしこい…）
for i in range(7):
  if s[i] != k[i]:
    if s[-7+i] != k[-7+i]:
      flg = 0
      break

print('YES' if flg else 'NO')

# -- ダメだったコード --
# OKなパターンは3つ
# 1.keyencexxx
# 2.xxxkeyence
# 3.keyxxxence など、頭とお尻に分かれてるパターン
# 分かれてるパターンに関して、はじめて違った部分をiとし
# その後スライスでお尻の部分判定をしようとした

# if k in s:
#   print('YES')
#   exit()

# x = 0
# for i in range(n):
#   if s[i] != k[i]:
#     x = i
#     break

# ここの条件がうまくできなかった…最後の1文字だけの時など。どうやったらうまくできるんだろう
# if k[x:] == s[n-1-x:n]:
#   print('YES')
# else:
#   print('NO')


#!/usr/bin/env python

import glob, os, sys, subprocess, shutil, string, argparse

parser = argparse.ArgumentParser(description="Wrapper script for MakePlots_HTopMultilep.py. This gets called on the PBS worker node via the PBS script generated by submit-PBS-ARRAY-MakePlots_HTopMultilep.py. The variable to be plotted gets retrieved via the PBS_ARRAYID index.")

parser.add_argument("--optstr", dest="optstr", action="store", type=str)
parser.add_argument("--varlist", dest="varlist", action="store", type=str, nargs="+")
parser.add_argument("--outputpath", dest="outputpath", action="store", type=str)

args = parser.parse_args()

if __name__ == '__main__':

    # Read varlist from argparse.
    # It will automagically re-create a python list from the multiple arguments of the input --varlist option.

    varlist = args.varlist

    # Get the var from the PBS_ARRAYID

    pbs_array_idx = int(os.getenv('PBS_ARRAYID'))

    var = varlist[pbs_array_idx]

    print("Current job index PBS_ARRAYID={0}, var={1}".format(pbs_array_idx,var))

    # OK, execute plotting script for this var!

    # NB: it's crucial to make this call when running on the worker node, otherwise
    # python will not be able to find modules in Plotter/

    os.chdir(os.path.abspath(os.path.curdir)+"/HTopMultilepAnalysis/PlotUtils")

    plotscript = os.path.abspath(os.path.curdir) + "/Plotter/MakePlots_HTopMultilep.py"
    optlist = args.optstr.split(' ')

    cmdlist = ['python',plotscript] + optlist + ['--submitPBSVar',var]
    cmd = " ".join( "{0}".format(c) for c in cmdlist )

    print("Executng command:\n{0}".format(cmd))

    subprocess.call( cmd, shell = True )

    # Now move the output to the target directory

    outputpath  = args.outputpath
    if not outputpath[-1] == '/':
        outputpath += '/'

    # Get all subdirs in current location whose name starts with "OutputPlots_", rsync them to output directory, and remove the local copy

    job_outdirs = [ dir for dir in os.listdir(".") if "OutputPlots_" in dir and os.path.isdir(dir)  ]

    for dir in job_outdirs:
        thisdir = dir
        if thisdir[-1] == '/':
            thisdir = thisdir[:-1]
        subprocess.call( ['rsync','-azP',thisdir,outputpath] )
        shutil.rmtree(thisdir)


import functools
import inspect
import types
from typing import Dict, List, Optional, Type, Union

import pytest

import servo.utilities.inspect

class OneClass:
    def one(self) -> None:
        ...

    def two(self) -> None:
        ...

    def three(self) -> None:
        ...

class TwoClass(OneClass):
    def four(self) -> None:
        ...

    def five(self) -> None:
        ...

class ThreeClass(TwoClass):
    def six(self) -> None:
        ...

@pytest.mark.parametrize(
    "cls, stop_at_parent, method_names",
    [
        (OneClass, None, ["one", "two", "three"]),
        (TwoClass, None, ["four", "five"]),
        (TwoClass, OneClass, ["one", "two", "three", "four", "five"]),
        (ThreeClass, OneClass, ["one", "two", "three", "four", "five", "six"]),
        (ThreeClass, TwoClass, ["four", "five", "six"]),
    ],
)
def test_get_instance_methods(cls, stop_at_parent, method_names) -> None:
    methods = servo.utilities.inspect.get_instance_methods(
        cls, stop_at_parent=stop_at_parent
    )
    assert list(methods.keys()) == method_names

def test_get_instance_methods_invalid_parent() -> None:
    with pytest.raises(TypeError) as e:
        servo.utilities.inspect.get_instance_methods(OneClass, stop_at_parent=int)
    assert (
        str(e.value)
        == "invalid parent type \"<class 'int'>\": not found in inheritance hierarchy"
    )

def test_get_instance_methods_returns_bound_methods_if_possible() -> None:
    methods = servo.utilities.inspect.get_instance_methods(
        ThreeClass(), stop_at_parent=OneClass
    )
    assert list(methods.keys()) == ["one", "two", "three", "four", "five", "six"]
    assert functools.reduce(
        lambda bound, m: bound & inspect.ismethod(m), methods.values(), True
    )

def test_get_instance_methods_returns_finds_dynamic_instance_methods() -> None:
    def seven() -> None:
        ...

    instance = ThreeClass()
    instance.seven = types.MethodType(seven, instance)
    methods = servo.utilities.inspect.get_instance_methods(
        instance, stop_at_parent=OneClass
    )
    assert list(methods.keys()) == [
        "one",
        "two",
        "three",
        "four",
        "five",
        "six",
        "seven",
    ]
    assert functools.reduce(
        lambda bound, m: bound & inspect.ismethod(m), methods.values(), True
    )

def test_get_instance_methods_returns_ignores_attributes() -> None:
    class FourClass(ThreeClass):
        ignore_me: str = "ignore_me"

    instance = FourClass()
    methods = servo.utilities.inspect.get_instance_methods(
        instance, stop_at_parent=OneClass
    )
    assert list(methods.keys()) == ["one", "two", "three", "four", "five", "six"]
    assert functools.reduce(
        lambda bound, m: bound & inspect.ismethod(m), methods.values(), True
    )

def test_resolution_none() -> None:
    def test_type() -> None:
        ...

    def test_str() -> "None":
        ...

    res_type, res_str = servo.utilities.inspect.resolve_type_annotations(
        inspect.Signature.from_callable(test_type).return_annotation,
        inspect.Signature.from_callable(test_str).return_annotation,
    )
    assert res_type == res_str

def test_resolution_none() -> None:
    def test_type() -> None:
        ...

    def test_str() -> "None":
        ...

    res_type, res_str = servo.utilities.inspect.resolve_type_annotations(
        inspect.Signature.from_callable(test_type).return_annotation,
        inspect.Signature.from_callable(test_str).return_annotation,
    )
    assert res_type == res_str

def test_aliased_types() -> None:
    import servo
    import servo.types
    from servo import types
    from servo.types import Duration

    def test_type_path() -> servo.types.Duration:
        ...

    def test_type_abbr() -> types.Duration:
        ...

    def test_type() -> Duration:
        ...

    def test_str_path() -> "servo.types.Duration":
        ...

    def test_str_abbr() -> "types.Duration":
        ...

    def test_str() -> "Duration":
        ...

    resolved = servo.utilities.inspect.resolve_type_annotations(
        inspect.Signature.from_callable(test_type_path).return_annotation,
        inspect.Signature.from_callable(test_type_abbr).return_annotation,
        inspect.Signature.from_callable(test_type).return_annotation,
        inspect.Signature.from_callable(test_str_path).return_annotation,
        inspect.Signature.from_callable(test_str_abbr).return_annotation,
        inspect.Signature.from_callable(test_str).return_annotation,
        globalns=globals(),
        localns=locals(),
    )

    assert set(resolved) == {Duration}

# TODO: Compare compound return types, generic, skipping arguments...
# None, None.__class__, 'None'
# Optional[str], Dict[str, int], Dict[str, List[float]]
# omit argument, extra argument, argument with wrong type

# @pytest.mark.parametrize(
#     "reference_callable"
# )
import typing
from typing import Any

def test_equal_callable_descriptors() -> None:
    import servo
    import servo.types

    def test_one() -> typing.Dict:
        ...

    def test_two() -> typing.Dict[str, Any]:
        ...

    def test_three() -> typing.Dict[str, int]:
        ...

    def test_four() -> typing.Dict[float, str]:
        ...

    sig1 = inspect.Signature.from_callable(test_one)
    sig2 = inspect.Signature.from_callable(test_two)

    with pytest.raises(TypeError) as e:
        servo.utilities.inspect.assert_equal_callable_descriptors(
            servo.utilities.inspect.CallableDescriptor(
                signature=sig1, globalns=globals(), localns=locals()
            ),
            servo.utilities.inspect.CallableDescriptor(
                signature=sig2, globalns=globals(), localns=locals()
            ),
        )
    assert (
        str(e.value)
        == 'invalid callable "() -> Dict": incompatible return type annotation "typing.Dict[str, typing.Any]" in callable signature "() -> Dict[str, Any]", expected "typing.Dict"'
    )

    servo.utilities.inspect.assert_equal_callable_descriptors(
        servo.utilities.inspect.CallableDescriptor(
            signature=inspect.Signature.from_callable(test_two),
            globalns=globals(),
            localns=locals(),
        ),
        servo.utilities.inspect.CallableDescriptor(
            signature=inspect.Signature.from_callable(test_three),
            globalns=globals(),
            localns=locals(),
        ),
    )

    # before_handler_signature = inspect.Signature.from_callable(__before_handler)
    # servo.utilities.inspect.assert_equal_callable_descriptors(
    #     servo.utilities.inspect.CallableDescriptor(signature=before_handler_signature, module=event.module, globalns=event_globalns, localns=None),
    #     servo.utilities.inspect.CallableDescriptor(signature=handler_signature, module=handler_module, globalns=handler_globalns, localns=handler_localns),
    #     name=name,
    # )
    # servo.utilities.inspect.assert_equal_callable_descriptors()
    # ...

MaybeNumeric = Optional[Union[float, int]]

@pytest.mark.parametrize(
    "types_, error_message",
    [
        # Success cases
        ([dict, dict], None),
        ([str, str], None),
        ([None, None], None),
        ([List[str], List[str]], None),
        ([Dict[str, int], Dict[str, int]], None),
        ([dict[str, int], Dict[str, int]], None),
        ([Any, str], None),
        ([Any, List[str]], None),
        ([List[Any], List[str]], None),
        ([Dict[str, Any], Dict[str, int]], None),
        # Subclassing
        ([OneClass, TwoClass], None),
        ([List[OneClass], List[TwoClass]], None),
        ([Dict[str, OneClass], Dict[str, TwoClass]], None),
        # Special forms
        ([MaybeNumeric, MaybeNumeric], None),
        ([MaybeNumeric, Optional[Union[int, float]]], None),
        # ---
        # Failure cases
        (
            [dict, int],
            "Incompatible type annotations: expected <class 'dict'>, but found <class 'int'>",
        ),
        (
            [Dict[str, int], dict],
            "Incompatible type annotations: expected typing.Dict[str, int], but found <class 'dict'>",
        ),
        (
            [List[str], List[int]],
            "Incompatible type annotations: expected typing.List[str], but found <class 'str'>",
        ),
        (
            [MaybeNumeric, float],
            "Incompatible type annotations: expected typing.Union[float, int, NoneType], but found <class 'float'>",
        ),
        (
            [dict, Dict[str, Any]],
            "Incompatible type annotations: expected <class 'dict'>, but found typing.Dict[str, typing.Any]",
        ),
        (
            [TwoClass, MaybeNumeric],
            "Incompatible type annotations: expected <class 'inspect_test.TwoClass'>, but found typing.Union[float, int, NoneType]",
        ),
        (
            [TwoClass, OneClass],
            "Incompatible type annotations: expected <class 'inspect_test.TwoClass'>, but found <class 'inspect_test.OneClass'>",
        ),
    ],
)
def test_assert_equal_types(types_: List[Type], error_message: Optional[str]) -> None:
    if error_message:
        with pytest.raises(TypeError) as e:
            servo.utilities.inspect.assert_equal_types(*types_)
        assert str(e.value) == error_message

    else:
        servo.utilities.inspect.assert_equal_types(*types_)


class Solution:
    def findMin(self, nums: List[int]) -> int:
        start , end = 0 ,len(nums) - 1
        curr_min = float("inf")

        while start  <  end :
            mid = (start + end ) // 2
            curr_min = min(curr_min,nums[mid])

            # right has the min
            if nums[mid] > nums[end]:
                start = mid + 1

            # left has the  min
            else:
                end = mid - 1

        return min(curr_min,nums[start])


from dateutil import rrule
import datetime
# 算两个时间的月数
def months_calculte(begin,end):
    begin += '-01'
    end += '-01'
    d1 = datetime.datetime.strptime(begin,'%Y-%m-%d')
    d2 = datetime.datetime.strptime(end,'%Y-%m-%d')
    # d2 = datetime.date(2017, 4)
    months = rrule.rrule(rrule.MONTHLY, dtstart=d1, until=d2).count()
    return months

# 算两个时间的天数
def days_calculte(begin, end):
    begin = begin.split('-')
    end = end.split('-')
    d = int(begin[2])
    m = int(begin[1])
    y = int(begin[0])
    # difference in day
    dd = int(end[2])
    # difference in month
    dm = int(end[1])
    # difference in year
    dy = int(end[0])
    begind = datetime.date(y, m, d)
    endd = datetime.date(dy, dm, dd)
    return (endd - begind).days+1

#算年数
def years_calculte(begin,end):
    begin = int(begin)
    end = int(end)
    return  end-begin+1

#生成连续的日期
def dateRange(begin, end):
    ymd = "%Y-%m-%d"
    if len(begin) == 7:
        ymd = "%Y-%m"
    if len(begin) == 4:
        c = int(end) - int(begin)+1
        year = []
        for i in range(c):
            year.append(str(int(begin)+i))
        return sorted(year)
    dates = []
    dt = datetime.datetime.strptime(begin, ymd)
    date = begin[:]
    while date <= end:
        dates.append(date)
        dt = dt + datetime.timedelta(1)
        date = dt.strftime(ymd)
    return sorted(set(dates))

def date_parmas_check(params):
    if not params.get('time_kind'):
        return False,'请表明要查寻的时间格式!'
    if not params.get('start_time') or not params.get('end_time'):
        return False,'缺少时间范围!'
    if params.get('time_kind') == 'month' and (
        not len(params.get('start_time')) == 7 or not len(params.get('end_time')) == 7):
        return False,'按月统计时间范围有误!'
    if params.get('time_kind') == 'year' and (
        not len(params.get('start_time')) == 4 or not len(params.get('end_time')) == 4):
        return False,'按年统计时间范围有误!'
    if params.get('time_kind') == 'day' and (
        not len(params.get('start_time')) == 10 or not len(params.get('end_time')) == 10):
        return False,'按日统计时间范围有误!'
    return True,'success'

#时间增长 几天几个月几年
def date_up(begin,several):
    if several == 0:
        return begin
    several = several - 1
    if len(begin) == 4:
        return int(begin) + several
    elif len(begin) == 7:
        b = begin.split('-')
        m = int(b[1]) + several
        y = int(b[0])
        if m > 12:
            y = int(b[0]) + int(m / 12)
            m = m % 12
        result_date = str(y) + '-' + str(m)
        return result_date
    else:
        b = begin.split('-')
        s = datetime.date(int(b[0]), int(b[1]), int(b[2]))
        result_date = s + datetime.timedelta(days=several)
        return result_date.strftime('%Y-%m-%d')

#判断哪个时间大
def mix_min_check(start,end):
    a = int(start.replace('-',''))
    b = int(end.replace('-', ''))
    if a>b:
        return False
    return True


import sys
n, m = [int(x) for x in input().split()]
one, two = 0, 0
for i in range(1, m + 1):
    a, b, c, d = [int(x) for x in input().split()]
    one += a * b
    two += c * d
    if one >= n and two >= n:
        print(f"It's a tie at round {i}!")
        sys.exit()
    if one >= n:
        print(f"Team 1 wins at round {i}!")
        sys.exit()
    if two >= n:
        print(f"Team 2 wins at round {i}!")
        sys.exit()
print("Oh no!")


import os

def getVariantspath(modelPath,reportfname):
    """list dirs in modelPath in order to get variant names and its folder path"""
    vnames = [name for name in os.listdir(modelPath) if os.path.isdir(os.path.join(modelPath,name))]
    vpath = [os.path.join(modelPath,name) for name in os.listdir(modelPath) if os.path.isdir(os.path.join(modelPath,name))]
    return vnames,vpath

def getoutputID(line,searchsignal):
    """get index/location of searchsignal in line"""
    line0 = line.split()
    SID = None
    for sid,item in enumerate(line0):
        if searchsignal in item:
            SID = sid
            break
    return SID #this is not a very safe way of doing it

def getSUMresults(filepath,searchsignal):
    """return sum/total value of searchsignal"""
    OutputFile = open(filepath,"r")
    OutputLines = OutputFile.readlines()
    OutputFile.close()
    SID = getoutputID(OutputLines[1],searchsignal)
    results = []
    for line in OutputLines:
        line = line.split()
        try:
            hour = float(line[0]) #should be between 1-8760
            results.append(float(line[SID]))
        except:
            pass
    return sum(results)

def illCAL(illline,thres):
    """Calculating sDA, %of DF based on ill line"""
    illline = illline.split()
    nrpts = len(illline)-1
    count = 0
    for value in illline[1:]:
        if float(value) >= thres:
            count +=1
    percentage = round(count/nrpts * 100,0)
    return percentage

def getsDA(illfile):
    """read Ill file and return sDA300,50
    In this case also returning % of floor area has DF larger than 3%"""
    illf = open(illfile,"r")
    illlines = illf.readlines()
    illf.close()
    for line in illlines:
        if "DA_300" in line and "CDA" not in line:
            DAline = line
        elif "DF" in line:
            DFline = line
    #sDA calculation
    sDA = illCAL(DAline,50)
    sDF = illCAL(DFline,3)
    return sDA,sDF

def ReadAndWriteReport(modelPath,reportfname):
    #extract all file paths
    vnames, vpath = getVariantspath(modelPath,reportfname)
    addOutput = [os.path.join(fpath,"Results\\AddOutput_1h.prn") for fpath in vpath]
    illfile = [os.path.join(fpath,"Daylight\\001_Z1.ill") for fpath in vpath]
    #read files and write reports
    reportf = open(os.path.join(modelPath,reportfname),"w")
    #first line
    reportf.write("Varname\tOrientation\tWWR\tSHDActive\tTotalRadOnWindow\tTotalRadThroughWindow\tTotalInternalGain\tTotalHeating\tTotalCooling\tTotalEnergy\tDaylightFactor\tSpatialDaylightAutonomy\n")
    for vid,va in enumerate(vnames):
        reportf.write("%s\t"%(va))
        reportf.write("%s\t"%(va.split("_")[0]))
        reportf.write("%s\t"%(va.split("_")[1]))
        reportf.write("%s\t"%(round(getSUMresults(addOutput[vid],"SHD_active"),0))) #hours
        reportf.write("%s\t"%(round(getSUMresults(addOutput[vid],"IT_")/3600,0))) #kW
        reportf.write("%s\t"%(round(getSUMresults(addOutput[vid],"QSOLTR_")/3600,0))) #kW
        reportf.write("%s\t"%(round(getSUMresults(addOutput[vid],"Q_intgain_")/1000,0))) #kW
        reportf.write("%s\t"%(round(getSUMresults(addOutput[vid],"Q_tot_ht_")/1000,0))) #kW
        reportf.write("%s\t"%(round(getSUMresults(addOutput[vid],"Q_tot_cl_")/1000,0))) #kW
        reportf.write("%s\t"%(round(getSUMresults(addOutput[vid],"Q_tot_ht_")/1000 + getSUMresults(addOutput[vid],"Q_tot_cl_")/1000,0))) #kW
        sda,sdf = getsDA(illfile[vid])
        reportf.write("%s\t"%(sdf))
        reportf.write("%s\n"%(sda))
    reportf.close()


import requests
import random
from dotenv import load_dotenv
from PIL import ImageTk, Image
from io import BytesIO
import tkinter as tk
import os
class FetchAPI():
        query: str
        quantity: int
        img_width: int
        img_height: int
        load_dotenv()
        api_key = os.getenv('PEXELS_API_KEY')
        def __init__(self, query: str, quantity: int) -> None:
                self.img_width = 1280
                self.img_height = 720
                self.query = query
                self.quantity = quantity

        # Getter Method
        def get_query(self):
                return self.query
        def get_quantity(self):
                return self.quantity
        # Setter Method
        def set_query(self, newQuery):
                self.query = newQuery
        def set_quantity(self, newQuantity):
                self.query = newQuantity

        @staticmethod
        def randomNumber() -> int:
                return random.randint(1, 100)

        def fetchAPI(self):
                url = f'https://api.pexels.com/v1/search?query={self.get_query()}&per_page={self.get_quantity()}&page={self.randomNumber()}&orientation=landscape'
                headers = {'Authorization': self.api_key}
                response = requests.get(url, headers=headers)
                return response.json()

        def DisplayPhotos(self)-> None:
                data = self.fetchAPI()
                for photo in data['photos']:
                        photo_link = photo['src']['medium']
                        response = requests.get(photo_link)
                        image = Image.open(BytesIO(response.content))
                        root = tk.Tk()
                        root.wm_attributes("-topmost", 1)
                        tk_image = ImageTk.PhotoImage(image)
                        label = tk.Label(root, image=tk_image, text= self.get_query())
                        label.pack()
                        root.mainloop()
                return None


from django.db.models import Model, Q, OuterRef, Max, Count
from django.conf import settings
from django.core import mail
from django.http import HttpResponse
from django.template import Context, Template, loader
from django.utils.translation import gettext_lazy as _
from django.contrib import admin
import os, glob
from pathlib import Path
import pyexcel
import markdown
from markdown_link_attr_modifier import LinkAttrModifierExtension
from urllib.parse import quote

def get_current_site(request):
    from .models import Site
    try: return Site.objects.get_current(request)
    except Site.DoesNotExist: pass

    return None

def user_programs(queryset, path, request, or_cond=None):
    if request.user.is_superuser:
        if not request.user.site: return queryset
        cond = Q(**{path+'sites': request.user.site})
        return queryset.filter(cond | or_cond if or_cond else cond)
    cond = Q(**{path+'user': request.user})
    return queryset.filter(cond | or_cond if or_cond else cond)

def create_model(name, fields, app_label='formative', module='',
                 program=None, meta=None, base_class=Model):
    class Meta:
        pass

    setattr(Meta, 'app_label', app_label)
    if meta is not None:
        for key, value in meta.__dict__.items():
            if key[:2] == '__' or key == 'abstract': continue
            setattr(Meta, key, value)
    setattr(Meta, 'db_table', name)

    if not module: module = app_label
    attrs = {'__module__': module, 'Meta': Meta}
    attrs.update(dict(fields))

    # Create the class, which automatically triggers ModelBase processing
    model = type(name, (base_class,), attrs)

    if program: model._meta.program_slug = program
    return model

def remove_p(text):
    s = text.strip()
    if s[-3-1:] == '</p>':
        i = s.rindex('<p>')
        return s[i+3:-3-1]

    return text

def send_email(template, to, subject, context={}, connection=None):
    new_context = { 'settings': settings }
    new_context.update(context)
    context = Context(new_context)

    if type(template) != Template: context = new_context # wtf, Django

    sub = ' '.join(subject.render(context).splitlines()).rstrip()
    message = template.render(context)

    email = mail.EmailMessage(sub, message, settings.CONTACT_EMAIL, [to],
                              connection=connection)
    return email.send()

class TabularExport:
    def __init__(self, form, queryset, **kwargs):
        self.args, self.fields, self.collections = kwargs, [], {}

        names = []
        for name in self.args:
            if not self.args[name]: continue
            if name.startswith('block_'): names.append(name[len('block_'):])
            elif name.startswith('collection_') and self.args[name] != 'no':
                cname = name[len('collection_'):]
                self.collections[cname] = [0, []]
                if self.args[name] == 'combine':
                    self.collections[cname][0] = -1
        blocks = { 'block_'+b.name: b
                   for b in form.submission_blocks().filter(name__in=names) }

        self.items = {}
        if self.collections:
            item_model = form.item_model
            # item_model's _submission rel doesn't recognize original queryset
            qs = form.model.objects.filter(pk__in=queryset) # but this works

            sub_items = item_model.objects.filter(_submission__in=qs)
            items_qs = sub_items.filter(_collection__in=self.collections)
            # TODO order should be by block_rank, cf Submission._collections()
            for item in items_qs.order_by('_collection', '_block', '_rank'):
                app = self.items.setdefault(item._submission_id, {})
                app_col = app.setdefault(item._collection, [])
                app_col.append(item)

            for c in self.collections:
                if self.collections[c][0] < 0: continue
                lengths = [ len(app[c])
                            for app in self.items.values() if c in app ]
                self.collections[c][0] = lengths and max(lengths) or 0

        for name in self.args:
            if name.startswith('block_'):
                if blocks[name].block_type() == 'stock':
                    for n in blocks[name].stock.widget_names():
                        self.fields.append(blocks[name].stock.field_name(n))
                else: self.fields.append(blocks[name].name)

            elif name.startswith('cfield_'):
                cname, field = name[len('cfield_'):].split('.')
                if cname not in self.collections: continue
                self.collections[cname][1].append(field)

    def header_row(self):
        ret = ['email']
        for name in self.fields:
            if name.startswith('_'): ret.append(name[1:])
            else: ret.append(name)

        for collection, (n, fields) in self.collections.items():
            if not n: continue
            cfields = []
            for field in fields:
                if field == '_file': cfields.append(collection + '_file')
                else: cfields.append(collection + '_' + field)
            if n < 0: ret += cfields
            else: ret += cfields * n

        return ret

    def data_row(self, submission, sub_items):
        row = [submission._email]

        for name in self.fields:
            val = getattr(submission, name)
            if val is None: out = ''
            else: out = str(val)
            row.append(out)

        def item_val(item, field):
            if field == '_file' and item._file:
                return 'https://' + settings.DJANGO_SERVER + item._file.url
            val = getattr(item, field)
            if val is None: return ''
            return str(val)

        for collection, (n, fields) in self.collections.items():
            col_items = sub_items.setdefault(collection, [])
            if n < 0:
                for field in fields:
                    vals = [ item_val(item, field) for item in col_items ]
                    sep = ' ' if field == '_file' else ', '
                    out = sep.join(vals)
                    row.append(out)
            else:
                for item in col_items:
                    for field in fields: row.append(item_val(item, field))
                row.extend([''] * (n-len(col_items)) * len(fields))

        return row

    def data_rows(self, queryset):
        ret = []
        for submission in queryset:
            sub_items = self.items.setdefault(submission._id, {})
            row = self.data_row(submission, sub_items)
            ret.append(row)
        return ret

    def data(self, queryset):
        ret = [self.header_row()]
        ret += self.data_rows(queryset)
        return ret

    def csv_response(self, filename, queryset):
        data = self.data(queryset)

        stream = pyexcel.save_as(array=data, dest_file_type='csv')
        response = HttpResponse(stream, content_type='text/csv')

        disp = f"attachment; filename*=UTF-8''" + quote(filename)
        response['Content-Disposition'] = disp
        return response

def submission_link(s, form, rest=''):
    server = settings.DJANGO_SERVER
    if ':' in server or server.endswith('.local'): proto = 'http'
    else: proto = 'https'

    if s._valid > 1 and not rest:
        if s._valid == form.num_pages(): rest = f'page-{form.num_pages()}'
        else: rest = f'page-{s._valid + 1}'

    return f'{proto}://{server}/{form.program.slug}/{form.slug}/{s._id}/{rest}'

def get_file_extension(name):
    return Path(name).suffix[1:].lower()

def thumbnail_path(path, ext=None):
    idx = path.rindex('.')
    return path[:idx] + '_tn' + (ext and '.'+ext or path[idx:])

def subtitle_path(path, lang):
    idx = path.rindex('.')
    return path[:idx] + '_s_' + lang + '.vtt'

def delete_submission_files(files_recs):
    for rec in files_recs:
        submission_dir = os.path.join(settings.MEDIA_ROOT, str(rec.submission))
        if not os.path.isdir(submission_dir): continue

        for filename in os.listdir(submission_dir):
            os.remove(os.path.join(submission_dir, filename))
        os.rmdir(submission_dir)

def delete_file(file):
    if os.path.isfile(file.path): os.remove(file.path)

    thumb = thumbnail_path(file.path)
    if os.path.isfile(thumb): os.remove(thumb)

    for path in glob.glob(subtitle_path(file.path, '*')):
        os.remove(path)

def human_readable_filesize(size, decimal_places=2):
    for unit in ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB']:
        if size < 1024 or unit == 'PB': break
        size /= 1024
    return f"{size:.{decimal_places}f} {unit}"

def any_name_field(**kwargs):
    Qs = [ Q(**{ namen + (k != '_' and k or ''): v for k, v in kwargs.items() })
           for namen in ('name1', 'name2', 'name3') ]
    return Qs[0] | Qs[1] | Qs[2]

def get_tooltips():
    return {
        'previoustip': _('Previous Page'),
#        'sortabletip': _('Drag to reorder'),
#        'uploadtip': _('Replace File'),
    }

class MarkdownFormatter(markdown.Markdown):
    def __init__(self):
        super().__init__(extensions=[
            LinkAttrModifierExtension(new_tab='external_only')
        ])

    def convert(self, text):
        self.reset() # in our context this seems to be always needed
        return super().convert(text)


import random

def randomquote(quotes):
  last = len(quotes) -1
  rnd = random.randint(0,last)
  print("Random Quote: ",quotes[rnd])

f = open("quotes.txt")
quotes = f.readlines()
f.close()

selection = "A"

while True:
  selection = input("(D)isplay a quote\n(A)dd a quote\nChoose your selection by typing in the letter: ")
  if selection.upper() == "D":
    randomquote(quotes)
  elif selection.upper() == "A":
    quotetoadd = input("What saying would you like to add to the file?: ")
    filewritingto = open("quotes.txt",'a')
    filewritingto.write(quotetoadd + "\n")
    filewritingto.close()
    print("'",quotetoadd,"' has been written to the file!")
  else:
    print("Exiting...")
    break


import sys
import librosa
import numpy as np
#import soundfile as sf
import functools
import torch
#from torch.nn.functional import cosine_similarity
#import essentia.standard as es

def logme(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        print('\n-----------------\n')
        print('   MODEL: {}'.format(f.__name__.upper()))
        print('\n-----------------\n')
        return f(*args, **kwargs)
    return wrapped

class ProgressBar:
    """Progress bar

    """
    def __init__ (self, valmax, maxbar, title):
        if valmax == 0:  valmax = 1
        if maxbar > 200: maxbar = 200
        self.valmax = valmax
        self.maxbar = maxbar
        self.title  = title
        print ('')

    def update(self, val, avg_loss=0):
        # format
        if val > self.valmax: val = self.valmax

        # process
        perc  = round((float(val) / float(self.valmax)) * 100)
        scale = 100.0 / float(self.maxbar)
        bar   = int(perc / scale)

        # render
        if avg_loss:
            # out = '\r %20s [%s%s] %3d / %3d  cost: %.2f  r_loss: %.0f  l_loss: %.4f  clf_loss: %.4f' % (
            out = '\r %20s [%s%s] %3d / %3d  loss: %.5f' % (
                self.title,
                '=' * bar, ' ' * (self.maxbar - bar),
                val,
                self.valmax,
                avg_loss,
                )
        else:
            out = '\r %20s [%s%s] %3d / %3d ' % (self.title, '=' * bar, ' ' * (self.maxbar - bar), val, self.valmax)

        sys.stdout.write(out)
        sys.stdout.flush()

def pad(l, sr):
    # 0-Pad 10 sec at fs hz and add little noise
    z = np.zeros(10*sr, dtype='float32')
    z[:l.size] = l
    z = z + 5*1e-4*np.random.rand(z.size).astype('float32')
    return z

def compute_spectrogram(filename, sr=22000, n_mels=96):
    # zero pad and compute log mel spec
    try:
        audio, sr = librosa.load(filename, sr=sr, res_type='kaiser_fast')
    except:
        audio, o_sr = sf.read(filename)
        audio = librosa.core.resample(audio, o_sr, sr)
    try:
        x = pad(audio, sr)
    except ValueError:
        x = audio
    audio_rep = librosa.feature.melspectrogram(y=x, sr=sr, hop_length=512, n_fft=1024, n_mels=n_mels, power=1.)
    audio_rep = np.log(audio_rep + np.finfo(np.float32).eps)
    return audio_rep

def return_spectrogram_max_nrg_frame(spectrogram):
    frames = librosa.util.frame(np.asfortranarray(spectrogram), frame_length=96, hop_length=12)
    idx_max_nrg = np.argmax(np.sum(np.sum(frames, axis=0), axis=0))
    return frames[:,:,idx_max_nrg]

def return_spectrogram_3_max_nrg_frames(spectrogram):
    frames = librosa.util.frame(np.asfortranarray(spectrogram), frame_length=96, hop_length=12)
    idxes_max_nrg = (-np.sum(np.sum(frames, axis=0), axis=0)).argsort()[:3]
    return frames[:,:,idxes_max_nrg]

def spectrogram_to_audio(filename, y, sr=22000):
    y = np.exp(y)
    x = librosa.feature.inverse.mel_to_audio(y, sr=sr, n_fft=1024, hop_length=512, power=1.)
    librosa.output.write_wav(filename, x, sr)

def extract_spectrogram(filename, sr=16000, n_mels=48):
    audio = cut_audio(filename, sampleRate=sr, segment_duration=29.1)
    frames = melspectrogram(audio, sampleRate=sr, frameSize=512, hopSize=256, numberBands=[48],
                            warpingFormula='slaneyMel', window='hann', normalize='unit_tri')

    return frames['mel_48_db'].T

def melspectrogram(audio, sampleRate=44100, frameSize=2048, hopSize=1024,
                   window='blackmanharris62', zeroPadding=0, center=True,
                   numberBands=[128, 96, 48, 32, 24, 16, 8],
                   lowFrequencyBound=0, highFrequencyBound=None,
                   weighting='linear', warpingFormula='slaneyMel', normalize='unit_tri'):

    if highFrequencyBound is None:
        highFrequencyBound = sampleRate/2

    windowing = es.Windowing(type=window, normalized=False, zeroPadding=zeroPadding)
    spectrum = es.Spectrum()
    melbands = {}
    for nBands in numberBands:
        melbands[nBands] = es.MelBands(numberBands=nBands,
                                       sampleRate=sampleRate,
                                       lowFrequencyBound=lowFrequencyBound,
                                       highFrequencyBound=highFrequencyBound,
                                       inputSize=(frameSize+zeroPadding)//2+1,
                                       weighting=weighting,
                                       normalize=normalize,
                                       warpingFormula=warpingFormula,
                                       type='power')
    norm10k = es.UnaryOperator(type='identity', shift=1, scale=10000)
    log10 = es.UnaryOperator(type='log10')
    amp2db = es.UnaryOperator(type='lin2db', scale=2)

    results = essentia.Pool()

    for frame in es.FrameGenerator(audio, frameSize=frameSize, hopSize=hopSize,
                                   startFromZero=not center):
        spectrumFrame = spectrum(windowing(frame))

        for nBands in numberBands:
            melFrame = melbands[nBands](spectrumFrame)
            results.add('mel_' + str(nBands)+'_db', amp2db(melFrame))
            results.add('mel_' + str(nBands)+'_log1+10kx', log10(norm10k(melFrame)))
            results.add('mel_' + str(nBands), melFrame)

    return results

def cut_audio(filename, sampleRate=44100, segment_duration=None):

    audio = es.MonoLoader(filename=filename, sampleRate=sampleRate)()

    if segment_duration:
        segment_duration = round(segment_duration*sampleRate)
        segment_start = (len(audio) - segment_duration) // 2
        segment_end = segment_start + segment_duration
    else:
        segment_start = 0
        segment_end = len(audio)

    if segment_start < 0 or segment_end > len(audio):
      raise ValueError('Segment duration is larger than the input audio duration')

    return audio[segment_start:segment_end]

def kullback_leibler(y_hat, y):
    """Generalized Kullback Leibler divergence.
    :param y_hat: The predicted distribution.
    :type y_hat: torch.Tensor
    :param y: The true distribution.
    :type y: torch.Tensor
    :return: The generalized Kullback Leibler divergence\
             between predicted and true distributions.
    :rtype: torch.Tensor
    """
    return (y * (y.add(1e-5).log() - y_hat.add(1e-5).log()) + (y_hat - y)).sum(dim=-1).mean()

def embeddings_to_cosine_similarity_matrix(z):
    """Converts a a tensor of n embeddings to an (n, n) tensor of similarities.
    """
    cosine_similarity = torch.matmul(z, z.t())
    embedding_norms = torch.norm(z, p=2, dim=1)
    embedding_norms_mat = embedding_norms.unsqueeze(0)*embedding_norms.unsqueeze(1)
    cosine_similarity = cosine_similarity / (embedding_norms_mat)
    return cosine_similarity

def contrastive_loss(z_audio, z_tag, t=1):
    """Computes contrastive loss following the paper:
        A Simple Framework for Contrastive Learning of Visual Representations
        https://arxiv.org/pdf/2002.05709v1.pdf
        TODO: make it robust to NaN (with low values of t it happens).
        e.g Cast to double float for exp calculation.
    """
    z = torch.cat((z_audio, z_tag), dim=0)
    s = embeddings_to_cosine_similarity_matrix(z)
    N = int(s.shape[0]/2)
    s = torch.exp(s/t)
    try:
        s = s * (1 - torch.eye(len(s), len(s)).cuda())
        # s[range(len(s)), range(len(s))] = torch.zeros((len(s),)).cuda()
    except AssertionError:
        s = s * (1 - torch.eye(len(s), len(s)))
    denom = s.sum(dim=-1)
    num = torch.cat((s[:N,N:].diag(), s[N:,:N].diag()), dim=0)
    return torch.log((num / denom) + 1e-5).neg().mean()


def read():
    with open("input/02.txt") as f:
        return [x.split() for x in f.read().split('\n')[:-1]]

def part1(m):
    r = [0, 0]
    for x, y in m:
        if x == 'forward':
            r[0] += int(y)
        elif x == 'up':
            r[1] -= int(y)
        elif x == 'down':
            r[1] += int(y)
    return r[0] * r[1]

def part2(m):
    r = [0, 0, 0]
    for x, y in m:
        if x == 'forward':
            r[0] += int(y)
            r[1] += r[2] * int(y)
        elif x == 'up':
            r[2] -= int(y)
        elif x == 'down':
            r[2] += int(y)
    return r[0] * r[1]

print(part1(read()))
print(part2(read()))


# -*- coding: utf-8 -*-
# @Time    : 2020/8/19 11:17
# @Author  : WuatAnt
# @File    : ext_gcd.py
# @Project : Python数据结构与算法分析
def ext_gcd(x,y):
    if y == 0:
        return (x,1,0)
    else:
        (d,a,b) = ext_gcd(y,x%y)
        return (d,b,a-(x//y)*b)

print(ext_gcd(25,9))


import argparse

from algorithms.utils import timedcall

@timedcall
def count_inversions(array):
    _, inversions = _count_inversions(array)
    return inversions

def _count_inversions(array):
    if len(array) < 2:
        return array, 0
    mid = len(array) // 2
    left, left_inversions = _count_inversions(array[:mid])
    right, right_inversions = _count_inversions(array[mid:])
    array, cross_inversions = merge(left, right)
    return array, left_inversions + right_inversions + cross_inversions

def merge(left, right):
    array, inversions = [], 0
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] > right[j]:
            inversions += len(left) - i
            array.append(right[j])
            j += 1
        else:
            array.append(left[i])
            i += 1
    while i < len(left):
        array.append(left[i])
        i += 1
    while j < len(right):
        array.append(right[j])
        j += 1
    return array, inversions

@timedcall
def count_inversions_naive(array):
    inversions = 0
    for j in range(len(array)):
        for i in range(j):
            inversions += array[i] > array[j]
    return inversions

def read_data(filepath):
    with open(filepath, 'r') as fp:
        data = map(int, fp.read().splitlines())
    return list(data)

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument('--in_file', default='data/data.txt')
    return parser.parse_args()

def main():
    args = parse_args()
    data = read_data(args.in_file)
    inversions = count_inversions(data)
    print(inversions)

if __name__ == '__main__':
    main()