# -*- coding: utf-8 -*-

"""
Module for the Model abstract base class

"""

__author__ = 'Samir Adrik'
__email__ = 'samir.adrik@gmail.com'

from abc import ABC, abstractmethod

from PyQt5.QtCore import pyqtSlot, QObject, QDateTime

from source.util import Assertor

class Model(ABC):
    """
    Implementation of the Model Abstract Base Class

    """

    @abstractmethod
    def __init__(self, parent: QObject):
        """
        Constructor / Instantiation

        Parameters
        ----------
        parent  : QObject
                  parent view for which the model resides

        """
        Assertor.assert_data_types([parent], [QObject])
        super().__init__()
        self._parent = parent
        self._data = {}

    @property
    def parent(self):
        """
        parent getter

        Returns
        -------
        out     : OQbject
                  active parent in model

        """
        return self._parent

    @property
    def data(self):
        """
        data getter

        Returns
        -------
        out     : dict
                  active data that has been inputted in model

        """
        return self._data

    @data.setter
    def data(self, new_data: dict):
        """
        data setter

        Parameters
        ----------
        new_data    : dict
                      new data dict to be set in model

        """
        Assertor.assert_data_types([new_data], [dict])
        self._data = new_data

    @pyqtSlot()
    def set_date_edit(self, date_edit_name: str):
        """
        method for setting value in a date_edit

        Parameters
        ----------
        date_edit_name  : str
                          name of date_edit

        """
        try:
            Assertor.assert_data_types([date_edit_name], [str])
            date_edit_text = getattr(self.parent.ui_form, "date_edit_" + date_edit_name).dateTime()
            self.data.update({date_edit_name: date_edit_text.toString("dd.MM.yyyy")})
        except Exception as set_date_edit_error:
            self.parent.error_view.show_error(set_date_edit_error, self.data)

    @pyqtSlot()
    def clear_date_edits(self, date_edits: list):
        """
        method for clearing content in date_edits

        Parameters
        ----------
        date_edits  : list
                      list of date_edits

        """
        Assertor.assert_data_types([date_edits], [list])
        date = QDateTime.fromString("01.01.2000", "dd.MM.yyyy")
        for date_edit in date_edits:
            getattr(self.parent.ui_form, "date_edit_" + date_edit).setDateTime(date)
            if date_edit in self.data.keys():
                self.data.pop(date_edit)

    @pyqtSlot()
    def set_combo_box(self, combo_box_name: str, common_key: str = None, key_name: str = None):
        """
        method for setting value in single combo_box

        Parameters
        ----------
        combo_box_name  : str
                          name of combo_box
        common_key      : str, None
                          common data key to append all values
        key_name        : str, None
                          customized name of key

        """
        try:
            Assertor.assert_data_types([combo_box_name, common_key, key_name],
                                       [str, (type(None), str), (type(None), str)])
            combo_box_text = str(
                getattr(self.parent.ui_form, "combo_box_" + combo_box_name).currentText())
            values = {key_name if key_name else combo_box_name: combo_box_text}

            if common_key and common_key not in self.data.keys():
                self.data.update({common_key: values})
            elif common_key and common_key in self.data.keys():
                self.data[common_key].update(values)
            else:
                self.data.update(values)

            for keys, vals in self.data.copy().items():
                if isinstance(vals, dict):
                    self.data.update({keys: dict(sorted(vals.items()))})

            for keys, vals in self.data.copy().items():
                if isinstance(vals, dict):
                    for key, val in vals.copy().items():
                        if not val:
                            self.data[keys].pop(key)
                if not vals:
                    self.data.pop(keys)
        except Exception as set_combo_box_error:
            self.parent.error_view.show_error(set_combo_box_error)

    @pyqtSlot()
    def clear_combo_boxes(self, combo_boxes: list):
        """
        method for clearing content of combo_boxes

        Parameters
        ----------
        combo_boxes     : list
                          list of comboboxes

        """
        Assertor.assert_data_types([combo_boxes], [list])
        for combo_box in combo_boxes:
            getattr(self.parent.ui_form, "combo_box_" + combo_box).setCurrentIndex(0)
            self.set_combo_box(combo_box)

    @pyqtSlot()
    def update_line_edits(self, line_edit_name: str, line_edits: list, obj: object = None,
                          method: str = None, postfix: str = None, data: dict = None):
        """
        method for updating the value of multiple line_edits

        Parameters
        ----------
        line_edit_name  : str
                          name of line_edit to get values from
        line_edits      : list
                          all line_edits to update values for based on input, see line_edit_name
        obj             : object
                          name of object to get values to update line_edits
        method          : str
                          name of method in obj to use to get values to update line_edits
        postfix         : str
                          index if used in naming of line_edits
        data            : dict
                          dictionary with data to set if no object or method used

        """
        postfix = postfix if postfix else ""
        line_edit = getattr(self.parent.ui_form, "line_edit_" + line_edit_name + postfix)
        try:
            Assertor.assert_data_types([line_edit_name, line_edits, obj, method, postfix],
                                       [str, list, (type(None), object), (type(None), str),
                                        (type(None), str), (type(None), str)])
            line_edit_text = line_edit.text().strip()
            if line_edit_text and line_edit_text not in self.data.values():
                self.set_line_edits(line_edit_text, line_edits, obj, method, postfix, data)
            elif line_edit_text and line_edit_text in self.data.values():
                self.get_line_edits(line_edits, postfix)
            else:
                self.clear_line_edits(line_edits, postfix)
        except Exception as update_error:
            self.clear_line_edits(line_edits, postfix)
            self.parent.error_view.show_error(update_error, self.data)
            line_edit.setFocus()

    @pyqtSlot()
    def clear_empty_line_edits(self, line_edit_name: str, line_edits: list, postfix: str):
        """
        method for clearing empty line edits

        Parameters
        ----------
        line_edit_name  : str
                          name of line_edit to get values from
        line_edits      : list
                          all line_edits to update values for based on input, see line_edit_name
        postfix         : str
                          index if used in naming of line_edits

        """
        line_edit = getattr(self.parent.ui_form, "line_edit_" + line_edit_name + postfix).text()
        if not line_edit:
            self.clear_line_edits(line_edits, postfix)

    @pyqtSlot()
    def set_line_edits(self, line_edit_text: str, line_edits: list, obj: object = None,
                       method: str = None, postfix: str = None, data: dict = None):
        """
        method for setting values of multiple line_edits

        Parameters
        ----------
        line_edit_text  : str
                          value of inputted line_edit
        line_edits      : list
                          list of line_edit names to update values
        obj             : object
                          object with method for extracting values to update line_edits
        method          : str
                          name of method to call in object
        postfix         : str
                          index if used in naming of line_edits
        data            : dict
                          dictionary with data to set if no object or method used

        """
        model_info = getattr(obj(line_edit_text), method)() if obj and method else data
        try:
            Assertor.assert_data_types([line_edit_text, line_edits, obj, method, postfix, data],
                                       [str, list, (object, type(None)), (str, type(None)),
                                        (str, type(None)), (dict, type(None))])
            if not model_info:
                return
            for line_edit in line_edits:
                if line_edit in model_info.keys():
                    info = model_info[line_edit]
                    line_edit_name = line_edit + postfix if postfix else line_edit
                    self.set_line_edit(line_edit_name, data=info)
        except Exception as set_line_edits_error:
            self.clear_line_edits(line_edits, postfix)
            self.parent.error_view.show_error(set_line_edits_error, self.data)

    @pyqtSlot()
    def set_line_edit(self, line_edit_name: str, obj: object = None, method: str = None,
                      data: str = None, clearing: object = None):
        """
        method for setting value of a single line_edit

        Parameters
        ----------
        line_edit_name  : str
                          name of line_edit to set value
        obj             : object
                          object to get values from
        method          : str
                          name of method to call in obj to get values
        data            : str
                          value to set if no obj or method used
        clearing        : object
                          object for clearing data after exceptions

        """
        line_edit = getattr(self.parent.ui_form, "line_edit_" + line_edit_name)
        try:
            Assertor.assert_data_types([line_edit_name, obj, method, data, clearing],
                                       [str, (object, type(None)), (str, type(None)),
                                        (str, type(None)), (object, type(None))])
            line_edit_text = line_edit.text().strip() if not data else data
            new_value = line_edit_text not in self.data.values()
            if line_edit_text and new_value and obj:
                output = getattr(obj(line_edit_text), method)() if not data else data
                self.data.update({line_edit_name: output})
            elif line_edit_text and new_value and not obj:
                output = line_edit_text
                self.data.update({line_edit_name: output})
            elif line_edit_text and not new_value:
                output = line_edit_text
                self.data.update({line_edit_name: output})
            else:
                output = ""
                self.clear_line_edit(line_edit_name)
            getattr(self.parent.ui_form, "line_edit_" + line_edit_name).setText(output)
        except Exception as set_line_edit_error:
            self.parent.error_view.show_error(set_line_edit_error, self.data)
            self.clear_line_edit(line_edit_name)
            getattr(self.parent.ui_form, "line_edit_" + line_edit_name).setFocus()
            if clearing:
                clearing()

    @pyqtSlot()
    def get_line_edits(self, line_edits: list, postfix: str = None):
        """
        method for getting values of multiple line_edits

        Parameters
        ----------
        line_edits  : list
                      names of line_edits to get
        postfix     : str
                      index if used in naming of line_edits

        """
        Assertor.assert_data_types([line_edits, postfix], [list, (str, type(None))])
        for line_edit in line_edits:
            line_edit = "line_edit_" + (line_edit + postfix if postfix else line_edit)
            if line_edit in self.data.keys():
                getattr(self.parent.ui_form, line_edit).setText(
                    self.data[line_edit])

    @pyqtSlot()
    def clear_line_edits(self, line_edits: list, postfix: str = None):
        """
        method for clearing the content of multiple line_edits

        Parameters
        ----------
        line_edits  : list
                      list of names of line_edits to clear content
        postfix     : str
                      index if used in naming of line_edits

        """
        Assertor.assert_data_types([line_edits, postfix], [list, (str, type(None))])
        for line_edit in line_edits:
            line_edit_name = line_edit + postfix if postfix else line_edit
            self.clear_line_edit(line_edit_name)

    @pyqtSlot()
    def clear_line_edit(self, line_edit_name: str):
        """
        method for clearing content of a single line_edit

        Parameters
        ----------
        line_edit_name  : str
                          name of line_edit to clear the content

        """
        Assertor.assert_data_types([line_edit_name], [str])
        line_edit = "line_edit_" + line_edit_name
        if line_edit_name in self.data.keys():
            getattr(self.parent.ui_form, line_edit).clear()
            self.data.pop(line_edit_name)
        else:
            getattr(self.parent.ui_form, line_edit).clear()

    @pyqtSlot()
    def clear_data(self, key_name: str, common_key: str):
        """
        method for clearing single data point from self.data

        Parameters
        ----------
        key_name    : str
                      key_name to pop
        common_key  : str
                      common_key if data point stored as common_key

        """
        for key, val in self.data.items():
            if common_key == key and isinstance(val, dict):
                if key_name in val.keys():
                    val.pop(key_name)


from data_generator import Data
from predictor import Predictor
from corrector import Corrector
from data_generator import DWDTemperatureLoader

from predictor import SmoothingAndPredict
class Executor() :
    def __init__(self, data: Data, predictor: Predictor, corrector: Corrector = None):
        pass
        self.data = data
        self.predictor = predictor
        self.corrector = corrector

    def validate_single_station(self, station) :
        self.predictor.predict(self.data, station)

    def validate_whole_stations(self) :
        sum_edit_distances = self.predictor.predict(self.data)
        print(sum_edit_distances)

if __name__ == '__main__' :
    n_stations = 450
    n_timesteps= 50 #maybe 288 is the maximum to propoerly manage them
    k = 5
    p_noise_stations = 0.05
    p_noise_timesteps= 0.05
    print(n_stations * n_timesteps * p_noise_stations * p_noise_timesteps, n_stations * n_timesteps)

    data = DWDTemperatureLoader(n_stations, n_timesteps, k, p_noise_stations=p_noise_stations, p_noise_timesteps= p_noise_timesteps)
    predictor = SmoothingAndPredict()
    executor = Executor(data, predictor)

    executor.validate_whole_stations()


import socket
import os
import sys
from _thread import *
import pickle
import random
import platform
import time

def upload(clienthost,clientport):
    host = '0.0.0.0'
    sock = socket.socket()
    sock.bind((host,clientport))
    sock.listen(5)
    while True:
        downloadSocket,downloadAddr = sock.accept()
        msgTransfer = downloadSocket.recv(1048576)
        msg = pickle.loads(msgTransfer)
        print("Print the msg now....")
        # ['GET 1 P2P-CI/1.0\nHost: 127.0.0.1\nOS: macOS-10.15.7-x86_64-i386-64bit', 'rfc0001']
        reply = ""
        message = msg[0]
        #print("Entering transfer mode ....inside upload******")
        print(msg)
        t = time.localtime()
        current_time = time.strftime("%H:%M:%S", t)
        #print("Testing 1")
        print(message)
        if "GET" in message and "Host" in message and "OS" in message:
            if "P2P-CI/1.0" not in message:
                reply += "ERROR 505: P2P-CI/1.0 version not supported"
            else:
                breakContent = message.split("\n")
                if "GET" in breakContent[0]:
                    rfcNo = breakContent[0][4]
                    filePath = os.getcwd() + '/RFC/' + str(rfcNo) + "-" + str(msg[1]) + ".txt"
                    fileContent = ""
                    with open(filePath,'r') as f:
                        fileContent = f.read()
                    reply += "P2P-CI/1.0 200OK\n"\
                             "Date: " + str(current_time) + "\n"\
                             "OS: " + str(platform.platform()) + "\n"\
                             "Last-Modified: " + str(time.ctime(os.path.getmtime(filePath))) + "\n"\
                             "Content-Length: " + str(len(fileContent)) + "\n"\
                             "Content-Type: text/plain\n"
                    reply += str(fileContent)
                    print("File Transfer Completed...")
                else:
                    print("Incorrect request to accomodate")
        downloadSocket.sendall(bytes(reply,'utf-8'))
        downloadSocket.close

def performAdd(clienthost,clientport,number,title):
    return "ADD RFC " + str(number) + " P2P-CI/1.0\n"\
           "HOST: " + str(clienthost) + "\n"\
           "Port: " + str(clientport) + "\n"\
           "Title: " + str(title)

def transferFile(number, title, request):
    print(request)
    hname = request[0]
    port = int(request[1])
    s = socket.socket()
    s.connect((hname,port))
    msg = "GET " + str(number) + " P2P-CI/1.0\n"\
          "Host: " + str(hname) + "\n"\
          "OS: " + str(platform.platform())
    msgList = [msg,title]
    m = pickle.dumps(msgList,-1)
    s.send(m)
    print("********Getting data from the peer*********")
    res = s.recv(1048576)
    if len(str(res)) > 0:
        directory = os.getcwd() + "/RFC/Downloaded" + str(number) + ".txt"
        with open(directory,'w') as f:
            f.write(str(res.decode('utf-8')))
        print("File is stored in the new directory now")
    print(res)

def performLookup(host,port,number,title):
    return "LOOKUP " + str(number) + " P2P-CI/1.0" + "\n"\
            "Host: " + str(host) + "\n"\
            "Port: " + str(port) + "\n"\
            "Title: " + str(title)

def getUserInput(s,clienthost,clientport):
    print("Enter the function you want to perform: GET, LOOKUP, ADD, LIST, EXIT")
    inp = input()
    if inp == "ADD":
        print("Enter RFC number: ")
        rfcNum = input()
        print("Enter RFC Title: ")
        rfcTitle = input()
        directory = os.getcwd() + '/RFC/'
        fileSelect = directory + str(rfcNum) + "-" + str(rfcTitle) + ".txt"
        if os.path.isfile(fileSelect):
            data = performAdd(clienthost,clientport,rfcNum,rfcTitle)
            s.send(bytes(data,'utf-8'))
            #print("ADD response from server is: ")
            receive = s.recv(1024).decode('utf-8')
            print(receive)
        else:
            print("404 Error: File not found")
        getUserInput(s,clienthost,clientport)

    elif inp == "LIST":
        res = "LIST ALL P2P-CI/1.0\n"\
              "Host: " + str(clienthost) + "\n"\
              "Port: " + str(clientport) + "\n"
        print(res)
        s.send(bytes(res,'utf-8'))
        getList = s.recv(4096)
        print(getList.decode('utf-8'))
        getUserInput(s,clienthost,clientport)

    elif inp == "GET":
        print("Enter RFC number: ")
        rfcNum = input()
        print("Enter RFC Title: ")
        rfcTitle = input()
        request = "GET\n" + str(rfcNum) + "\n" + str(clienthost) + "\n" + str(clientport) + "\n" + str(rfcTitle)
        s.send(bytes(request,'utf-8'))
        #s.send(pickle.dumps(request,-1))
        res = s.recv(1024)
        resList = pickle.loads(res)
        if len(resList) > 1:
            transferFile(rfcNum,rfcTitle,resList)
            sendInfoToServer = "GET Request Completed\n" + "RFC: " + str(rfcNum) + "\n" + "Title: " + str(rfcTitle) + "\n" + "Host: " + str(clienthost)
            s.send(bytes(sendInfoToServer,'utf-8'))
        else:
            print(resList)
        getUserInput(s,clienthost,clientport)

    elif inp == "LOOKUP":
        print("Enter RFC number: ")
        rfcNum = input()
        print("Enter RFC Title: ")
        rfcTitle = input()
        getRfcDetails = performLookup(clienthost,clientport,rfcNum,rfcTitle)
        print(getRfcDetails)
        s.send(bytes(getRfcDetails,'utf-8'))
        response = s.recv(1024)
        print(response.decode('utf-8'))
        getUserInput(s,clienthost,clientport)

    elif inp == "EXIT":
        s.send(bytes("EXIT",'utf-8'))
        s.close

def main():
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect((socket.gethostname(),7734))
    print(f"Connected to {socket.gethostname()} with port 7734")
    peerRfc = {} # The peers who have the RFC. Key: RFC number, Value: the list of peers having the RFC
    clienthost = sys.argv[1]
    clientport = 60000 + random.randint(1,1000)
    print(f"Client port {clientport}")
    currdir = os.listdir(os.getcwd()+'/RFC')
    details = ""
    sendToServer = [clienthost,clientport,peerRfc,details]
    sendData = pickle.dumps(sendToServer,-1)
    s.send(sendData)
    s.close
    # Threads for managing download requests from peers which remains active until connection is open
    start_new_thread(upload,(clienthost,clientport))
    getUserInput(s,clienthost,clientport)

if __name__ == "__main__":
    main()


class Dfa:
    """Representation of deterministic finite automata (DFA)."""

    def __init__(self, delta, accept_states, init_state):
        """
        delta: a transition table. Should be an array of hashes, where
            delta[s][x] gives next state for state s on input x.
        accept_states: list of accept states
        """
        self._delta = delta
        self._accept_states = accept_states
        self._init_state = init_state

    def states(self):
        return list(range(len(self._delta)))

    @property
    def accept_states(self):
        return self._accept_states

    @property
    def init_state(self):
        return self._init_state

    def transition(self, s, x):
        """Next state upon receiving input x in state s."""
        return self._delta[s][x]

    def next_states(self, s):
        """Map from next states to inputs triggering the transition.

        Only possible next states appear as keys, and values give a list of
        inputs.
        """
        next_states = {}
        for x, next_s in self._delta[s].items():
            if next_s not in next_states:
                next_states[next_s] = []
            next_states[next_s].append(x)
        return next_states

    def run(self, s):
        """Run DFA on string s, producing a terminating state."""
        state = self.init_state
        for c in s:
            state = self.transition(state, c)
        return state

    def accepts(self, s):
        """Return True if the automaton accepts s."""
        return self.run(s) in self.accept_states

    def _minimal_partition(self):
        """Partition the DFA states according to equivalence."""
        non_accept_states = [s for s in self.states()
                             if s not in self.accept_states]
        p = Partition([list(self.accept_states), non_accept_states])
        # will break when we stop making progress
        while True:
            # two states are equivalent if they have map each input to the same
            # partition (assuming q and other_q started in the same partition)
            def same_partition(q, other_q):
                for x in self._delta[q].keys():
                    delta_q = self.transition(q, x)
                    delta_other_q = self.transition(other_q, x)
                    if p.index(delta_q) != p.index(delta_other_q):
                        return False
                return True
            new_p = p.refine(same_partition)
            # reached a fixed point, no more refinement is posible
            if len(new_p.sets) == len(p.sets):
                break
            p = new_p
        return p

    def minimal(self):
        """Compute an equivalent DFA with the minimal number of states.

        Follows the partition-based algorithm in these lecture notes (from
        UIUC's CS 373 from Spring 2010):
        https://courses.engr.illinois.edu/cs373/sp2010/lectures/lect_11.pdf.

        Does not modify self.
        """
        # The heavy lifting of computing which DFA states to merge is handled
        # by _minimal_partition.
        p = self._minimal_partition()

        # Now we must re-organize the DFA to use the partition indices in p as
        # states, copying over the transition function with the new state names.
        state_renaming = p.indices()

        # Each new state transitions according to an arbitrary state from that
        # partition.
        delta = []
        for q in range(len(p.sets)):
            # arbitrary row from old delta corresponding to this partition
            old_q_delta = self._delta[p.sets[q][0]]
            new_q_delta = {}
            for x, old_next_q in old_q_delta.items():
                new_q_delta[x] = state_renaming[old_next_q]
            delta.append(new_q_delta)

        # The initial state may have been renamed.
        init = state_renaming[self._init_state]

        # The accept states are those partitions that contain original accept
        # states. Minimization begins by keeping accept states together, so if
        # a partition has any accept state, all are accept states.
        accept_states = set([])
        for accept_q in self.accept_states:
            accept_states.add(state_renaming[accept_q])

        # Assemble the new DFA
        return Dfa(delta, accept_states, init)

class Partition:
    def __init__(self, sets):
        self.sets = [s for s in sets if s]

    def refine(self, same_partition):
        sets = []
        for p in self.sets:
            # We take each each element of p and remove it plus anything
            # equivalent to it, moving them into a new top-level partition set.
            # Continue until all the elements of p end up in the same
            # partition.
            while len(p) > 0:
                q = p[0]
                # partition into things similar to q...
                q_p = [q]
                # ...and others
                other_p = []
                for other_q in p[1:]:
                    if same_partition(q, other_q):
                        q_p.append(other_q)
                    else:
                        other_p.append(other_q)
                # this partition was determined by p
                sets.append(q_p)
                # continue into all other elements
                p = other_p
        return Partition(sets)

    def indices(self):
        new_names = {}
        for i, s in enumerate(self.sets):
            for q in s:
                new_names[q] = i
        return new_names

    def index(self, q):
        for i, s in enumerate(self.sets):
            if q in s:
                return i
        raise ValueError("unknown state {} for partition".format(q))


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time     : 2022/12/21 15:39
# @Author   : wqh
# @Email    : 867075698@qq.com
# @File     : login_test.py
# @Software : PyCharm

class TestUser(object):
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.get("http://192.166.66.22:8080/user/login")

    @pytest.mark.parametrize("username,pwd,expected", get_data())  # 调用json数据
    def test_user_login_Error(self, username, pwd, expected):
        user = username
        pwd = pwd
        expected = expected

        # 清空输入框后输入用户名
        self.driver.find_element(By.NAME, "user").clear()
        self.driver.find_element(By.NAME, "user").send_keys(user)
        # 清空输入框后输入密码
        self.driver.find_element(By.NAME, "pwd").clear()
        self.driver.find_element(By.NAME, "pwd").send_keys(pwd)
        # 点击【登录】
        self.driver.find_element(By.CLASS_NAME, "btn").click()
        # 等待页面加载
        WebDriverWait(self.driver, 3).until(EC.alert_is_present())
        alert = self.driver.switch_to.alert
        # 验证报错信息是否正确
        assert alert.text == expected
        alert.accept()


"""
Project Euler Version:
"""
# def calc_name(name):
#     return sum([ord(l) - 64 for l in name])
#
# names = []
# with open('./data/p022_names.txt', 'r') as fin:
#     for entry in fin.readlines():
#         names.extend(map(lambda x: x.strip('"'), entry.strip().split(',')))
#
# names.sort()
# score = 0
# for i, name in enumerate(names):
#     score += (i+1) * calc_name(name)
#
# print(score)
"""
HackerRank Version
"""

def calc_name(name):
    return sum([ord(l) - 64 for l in name])

names = []
# get 1st integer and the corresponding set of names
for _ in range(int(input().strip())):
    names.append(input().strip())
names.sort()
for _ in range(int(input().strip())):
    name = input().strip()
    i = names.index(name)
    print((i+1) * calc_name(name))


import os
import sys
import json
from collections import Counter, defaultdict

import numpy as np
import pandas as pd
import dill

"""
baseline is computed by looking at the segregation level of each district by comparing it to it's appriximity.

in this setting t_m would be the total number of people in the section m and it's neighbors

"""

def get_sector_population(aid):
    import random
    return random.choice(range(10,500))
if __name__ == '__main__':
    antenna_count = pd.read_pickle("data/count_by_antenna.pkl")
    ordinal_seg = dill.load(open('sectorDiversity_ordinal.dill','rb'))
    antennas_ne = dill.load(open(os.path.join('output','files','antennas_ne_polygon.dill'),'rb'))
    antenna_imd = pd.read_pickle("Antenna_IMD.pkl")
    antenna_imd['IMD_DEC'] = antenna_imd['decile'].round().astype(int)
    k = sorted(antenna_imd.IMD_DEC.unique().tolist())
    K = len(k)-1

    mk_raw = defaultdict(lambda:defaultdict(float))
    Cmk = defaultdict(list)
    m_var = {}
    miss = 0
    for aid, ne_aids in antennas_ne.items():
        ne_aids.append(aid) #we should also count number of people in section m as well
        td = 0
        for i in ne_aids:
            try:
                imd = int(antenna_imd[antenna_imd.SectorID==i]['IMD_DEC'])
                ucount = antenna_count.loc[i]['user_id']
                mk_raw[aid][imd]+=ucount
                td+=ucount
            except Exception as err:
#                print(antenna_imd[antenna_imd.SectorID==i]['IMD_DEC'])
#                print(err)
                pass
        try:
            mk = np.round(np.cumsum([mk_raw[aid][j]/td for j in k]),1)
            m_var[aid] = (1/(K)) * sum([2*np.sqrt(mk[c]*(1-mk[c])) for c in range(K)])
        except  Exception as err:
#            print(err)
            miss+=1
            continue

    print(miss,len(antennas_ne))
            #*get_sector_population(i)
#        mk_raw[aid] = Counter([int(antenna_imd[antenna_imd.SectorID==i]['IMD_DEC'])*get_sector_population(i) for i in ne_aids])
#        td = sum(mk_raw[aid].values())
#        mk = np.round(np.cumsum([mk_raw[aid][i]/td for i in k]),1)
#        m_var[aid] = (1/(K)) * sum([2*np.sqrt(mk[c]*(1-mk[c])) for c in range(K)])
#
    with  open('sectorDiversity_ordinal_baseline.dill','wb') as handle:
        dill.dump(m_var,handle)
#


import json
import os
import re
import stat
import math
from functools import reduce as functools_reduce

import te.lang.cce
from te import tvm
from te.platform.fusion_manager import fusion_manager
from topi import generic
from topi.cce import util
from te.lang.cce.te_compute import common
from te import platform as tbe_platform
from te.platform import cce_params
from te.platform import insn_cmd
from te.platform.cce_build import build_config

SHAPE_SIZE_LIMIT = 2**30
BLOCK_SIZE = 16
BLOCK_SIZE_ALIGN = 16
BLOCK_SIZE_FP16 = 16
BLOCK_SIZE_INT8 = 32

DOUBLE_BUFFER = 2
FP16_SIZE = 2
INT8_SIZE = 1
NEED_UB_SPACE_NUM = 2
L1_SIZE = tbe_platform.cce_conf.get_soc_spec(tbe_platform.cce_conf.L1_SIZE)
UB_SIZE = tbe_platform.cce_conf.get_soc_spec(tbe_platform.cce_conf.UB_SIZE)
LOAD3D_REPEAT_TIME_LIMIT = 255

def ub_split_c1(ub_split_c1_shape, A, ksize):
    def _ub_split_c1_indices(indices, A):
        n, howo, co1, khw, howo0, co0 = indices
        n_index = n
        hw_index = howo
        hw0_index = howo0
        c1_index = co1 * ksize + khw
        c0_index = co0
        return A(n_index, hw_index, c1_index, hw0_index, c0_index)

    return tvm.compute(ub_split_c1_shape,
                       lambda *indices: _ub_split_c1_indices(indices, A),
                       name='ub_split_c1')

def ub_transpose(ub_transpose_shape, A):
    def _ub_transpose_indices(indices, A):
        n, howo, howo0, khw, co1, co0 = indices
        n_index = n
        hw_index = howo
        c1_index = co1
        khw_index = khw
        hw0_index = howo0
        c0_index = co0

        return A(n_index, hw_index, c1_index, khw_index, hw0_index, c0_index)

    return tvm.compute(ub_transpose_shape,
                       lambda *indices: _ub_transpose_indices(indices, A),
                       name='ub_transpose')

def ub_merge_hw(ub_merge_shape, A):
    def _ub_merge_hw_indices(indices, A):
        in_n, in_hw, in_hw0, in_khw, in_c1, in_c0 = A.shape
        n, howo, khw, co1, co0 = indices
        n_index = n
        hw_index = howo // in_hw0
        hw0_index = howo % in_hw0
        c1_index = co1
        khw_index = khw
        c0_index = co0
        return A(n_index, hw_index, hw0_index, khw_index, c1_index, c0_index)

    return tvm.compute(ub_merge_shape,
                       lambda *indices: _ub_merge_hw_indices(indices, A),
                       name='ub_merge_hw')

def ub_merge_co(ub_merge_co_shape, A):
    def _ub_merge_co_indices(indices, A):
        in_n, in_hw, in_khw, in_c1, in_c0 = A.shape
        n, howo, khw, co = indices
        n_index = n
        hw_index = howo
        khw_index = khw
        c1_index = co // in_c0
        c0_index = co % in_c0
        return A(n_index, hw_index, khw_index, c1_index, c0_index)

    return tvm.compute(ub_merge_co_shape,
                       lambda *indices: _ub_merge_co_indices(indices, A),
                       name='ub_merge_co')

def im2col_row_major_v2(A, A_im2col_VM_shape, kernel_h, kernel_w, padding,
                        stride, dilate, compute_dtype):
    """
    calculate im2col_row_major tensor
    Parameters
    ----------
    A : feature map

    A_im2col_VM_shape : shape of A_im2col_row_major

    kernel_h: the kernel value in  h

    kernel_w: the kernel value in  w

    padding: the padding shape

    stride: the stride value

    dilate: the dilation value

    compute_dtype: dtype of compute result
    -------
    Returns : A_im2col_row_major tensor
    """
    def _im2col_row_major_indices(indices, A, kernel_h, kernel_w, padding,
                                  stride, dilate):
        """
        calculate im2col_row_major tvm lambda function
        Parameters
        ----------
        indices : indices in lambda function

        A : feature map

        kernel_h: the kernel value in  h

        kernel_w: the kernel value in  w

        padding: the padding shape

        stride: the stride value

        -------
        Returns  im2col_row_major tvm lambda function
        """
        in_n, in_c1, inH, in_w, in_c0 = A.shape

        n, hw, c1, kh, kw, c0 = indices
        stride_h, stride_w = stride
        dilate_h, dilate_w = dilate
        padding_top, padding_bottom, padding_left, padding_right = padding

        kernel_dilate_w = (kernel_w - 1) * dilate[1] + 1

        width_out = (in_w.value + padding_left + padding_right -
                     kernel_dilate_w) // (stride_w) + 1

        n_index = n
        c1_index = c1
        h_index = (hw // width_out) * stride_h + (kh * dilate_h)
        w_index = (hw % width_out) * stride_w + (kw * dilate_w)
        c0_index = c0
        return tvm.select(
            tvm.any(h_index < padding_top,
                    h_index > inH.value + padding_top - 1,
                    w_index < padding_left,
                    w_index > in_w.value + padding_left - 1),
            tvm.const(0.0, compute_dtype),
            A(n_index, c1_index, h_index - padding_top, w_index - padding_left,
              c0_index))

    return tvm.compute(
        A_im2col_VM_shape,
        lambda *indices: _im2col_row_major_indices(
            indices, A, kernel_h, kernel_w, padding, stride, dilate),
        name='im2col_row_major',
        tag='im2col_row_major')

def im2col_fractal_v2(A_im2col_shape, A, config, compute_dtype):
    """
    calculate im2col_fractal tensor
    Parameters
    ----------
    A_im2col_shape : shape of A_im2col

    A : feature map

    config: the config of cube

    compute_dtype: dtype of compute result
    -------
    Returns : A_im2col_fractal tensor
    """
    def _im2col_fractal_indices(indices, A):
        """
        calculate im2col_fractal tvm lambda function
        Parameters
        ----------
        indices : indices in lambda function

        A : feature map

        -------
        Returns : im2col_fractal tvm lambda function
        """
        block_size = config['mac'][1]
        block_size_M = config['mac'][0]
        n, hw, c1, kernel_h, kernel_w, c0 = A.shape
        batch_size, i1, j1, i0, j0 = indices
        n_index = batch_size

        hw_index = i1 * block_size_M + i0

        c1_index = (((j1 * block_size + j0) // c0.value) //
                    kernel_w.value) // kernel_h.value

        kh_index = (((j1 * block_size + j0) // c0.value) //
                    kernel_w.value) % kernel_h.value

        kw_index = ((j1 * block_size + j0) // c0.value) % kernel_w.value

        c0_index = (j1 * block_size + j0) % c0.value

        dtype = compute_dtype
        return tvm.select(
            tvm.any(hw_index < 0, hw_index > hw.value - 1),
            tvm.const(0.0, dtype),
            A(n_index, hw_index, c1_index, kh_index, kw_index, c0_index))

    return tvm.compute(A_im2col_shape,
                       lambda *indices: _im2col_fractal_indices(indices, A),
                       name='im2col_fractal',
                       tag='im2col_fractal')

@fusion_manager.register("extract_image_patches")
def extract_image_patches_compute(fmap,
                                  c_in_real,
                                  ksizes,
                                  strides,
                                  dilates,
                                  padding,
                                  kernel_name="extract_image_patches"):
    """
    ops compute

    Parameters
    ----------
    fmap : TVM tensor
        the placeholder of input_x
    c_in_real : real c size of input
    ksizes: input attr
    strides: input attr
    dilates: input attr
    padding: input attr
    kernel_name : str
        kernel name, default value is "extract_image_patches"

    Returns
    -------
    compute results
    """
    # fmap's format is NC1HWC0
    fmap_shape = fmap.shape
    dtype_input = fmap.dtype
    if dtype_input == "int8" or dtype_input == "uint8":
        BLOCK_SIZE_ALIGN = BLOCK_SIZE_INT8
        type_size = INT8_SIZE
    else:
        BLOCK_SIZE_ALIGN = BLOCK_SIZE
        type_size = FP16_SIZE
    fmap_batch, fmap_c1, fmap_h, fmap_w, fmap_c0 = fmap_shape
    # out to L1
    fmap_in_l1 = tvm.compute(fmap_shape, lambda *i: fmap[i], name="fmap_in_l1")

    _, filter_h, filter_w, _ = ksizes
    _, stride_h, stride_w, _ = strides
    _, dilate_h, dilate_w, _ = dilates

    out_h, padding_h_before, padding_h_after = \
        common.tf_get_windowed_output_size_verbose_v2(fmap_h.value,
                                                      filter_h, dilate_h,
                                                      stride_h, padding)
    out_w, padding_w_before, padding_w_after = \
        common.tf_get_windowed_output_size_verbose_v2(fmap_w.value, filter_w,
                                                      dilate_w, stride_w,
                                                      padding)

    pad = (padding_h_before, padding_h_after, padding_w_before,
           padding_w_after)
    stride = (stride_h, stride_w)
    dilate = (dilate_h, dilate_w)

    fmap_vm_shape = (fmap_batch, out_h * out_w, fmap_c1, filter_h, filter_w,
                     fmap_c0)

    fmap_im2col = im2col_row_major_v2(fmap_in_l1, fmap_vm_shape, filter_h,
                                      filter_w, pad, stride, dilate,
                                      dtype_input)

    howo = ((out_h * out_w + BLOCK_SIZE - 1) // BLOCK_SIZE) * BLOCK_SIZE
    fractal_shape = (fmap_batch, howo // BLOCK_SIZE,
                     fmap_c1 * filter_h * filter_w, BLOCK_SIZE,
                     BLOCK_SIZE_ALIGN)

    config = {"mac": [16, BLOCK_SIZE_ALIGN, 16]}

    fmap_fractal = im2col_fractal_v2(fractal_shape, fmap_im2col, config,
                                     dtype_input)

    extract_params = {}
    extract_params["padding_mode"] = padding
    extract_params["out_h"] = out_h
    extract_params["out_w"] = out_w
    extract_params["fmap_shape"] = fmap_shape
    extract_params["ksizes"] = ksizes
    extract_params["strides"] = strides
    extract_params["pad"] = pad
    extract_params["fmap_vm_shape"] = fmap_vm_shape
    extract_params["fractal_shape"] = fractal_shape
    extract_params["howo"] = howo
    extract_params["c_in_real"] = c_in_real
    setfmatrix_dict = {
        "conv_kernel_h": filter_h,
        "conv_kernel_w": filter_w,
        "conv_padding_top": padding_h_before,
        "conv_padding_bottom": padding_h_after,
        "conv_padding_left": padding_w_before,
        "conv_padding_right": padding_w_after,
        "conv_stride_h": stride_h,
        "conv_stride_w": stride_w,
        "conv_dilation_h": dilate_h,
        "conv_dilation_w": dilate_w,
        "conv_fm_c": fmap_c1 * fmap_c0,
        "conv_fm_h": fmap_h,
        "conv_fm_w": fmap_w,
    }

    ub_split_c1_shape = (fmap_batch, howo // BLOCK_SIZE, fmap_c1,
                         filter_h * filter_w, BLOCK_SIZE, BLOCK_SIZE_ALIGN)
    ub_split_c1_res = ub_split_c1(ub_split_c1_shape, fmap_fractal,
                                  filter_h * filter_w)
    ub_transpose_shape = (fmap_batch, howo // BLOCK_SIZE, BLOCK_SIZE,
                          filter_h * filter_w, fmap_c1, BLOCK_SIZE_ALIGN)
    ub_transpose_res = ub_transpose(ub_transpose_shape, ub_split_c1_res)

    ub_merge_hw_shape = (fmap_batch, howo, filter_h * filter_w, fmap_c1,
                         BLOCK_SIZE_ALIGN)
    ub_merge_hw_res = ub_merge_hw(ub_merge_hw_shape, ub_transpose_res)
    ub_merge_co_shape = (fmap_batch, howo, filter_h * filter_w,
                         fmap_c1 * BLOCK_SIZE_ALIGN)
    ub_merge_co_res = ub_merge_co(ub_merge_co_shape, ub_merge_hw_res)
    workspace_shape = (fmap_batch, out_h * out_w, filter_h * filter_w,
                       fmap_c1 * BLOCK_SIZE_ALIGN)
    workspace_res = tvm.compute(workspace_shape,
                                lambda *i: ub_merge_co_res[i],
                                name="workspace_res")

    ub_res_shape = (fmap_batch, out_h * out_w, filter_h * filter_w,
                    fmap_c1 * BLOCK_SIZE_ALIGN)
    ub_res = tvm.compute(ub_res_shape,
                         lambda *i: workspace_res[i],
                         name="ub_res")

    out_shape = (fmap_batch, out_h * out_w, filter_h * filter_w, c_in_real)
    output_res = tvm.compute(out_shape,
                             lambda *i: ub_res[i],
                             name="res",
                             attrs={
                                 'extract_params': extract_params,
                                 'setfmatrix_dict': setfmatrix_dict
                             })

    return output_res, workspace_res, workspace_shape

def extract_image_patches_schedule(res, sch_list):
    """
    :param res: the multi-results in the operator
    :param sch: schedule list
    """
    sch = sch_list[0]
    setfmatrix_map = res.op.attrs['setfmatrix_dict']
    setfmatrix_dict = {}
    for key, value in setfmatrix_map.items():
        if hasattr(value, "value"):
            setfmatrix_dict[key] = value.value
        else:
            setfmatrix_dict[key] = value

    extract_map = res.op.attrs['extract_params']
    extract_params = {}
    for key, value in extract_map.items():
        if hasattr(value, "value"):
            extract_params[key] = value.value
        else:
            extract_params[key] = value

    padding = extract_params['padding_mode']
    out_h = extract_params['out_h']
    out_w = extract_params['out_w']
    filter_h = setfmatrix_map['conv_kernel_h'].value
    filter_w = setfmatrix_map['conv_kernel_w'].value
    fmap_shape = extract_params['fmap_shape']
    c_in_real = extract_params["c_in_real"]
    batch = fmap_shape[0].value
    dilate_h = setfmatrix_dict['conv_dilation_h']
    stride_h = setfmatrix_dict['conv_stride_h']

    ub_res = res.op.input_tensors[0]
    workspace_res = ub_res.op.input_tensors[0]
    ub_merge_co = workspace_res.op.input_tensors[0]
    ub_merge_hw = ub_merge_co.op.input_tensors[0]
    ub_transpose = ub_merge_hw.op.input_tensors[0]
    ub_split_c1 = ub_transpose.op.input_tensors[0]
    fmap_fractal = ub_split_c1.op.input_tensors[0]
    fmap_im2col = fmap_fractal.op.input_tensors[0]
    fmap_in_l1 = fmap_im2col.op.input_tensors[0]
    fmap = fmap_in_l1.op.input_tensors[0]

    sch[fmap_in_l1].set_scope(tbe_platform.scope_cbuf)
    sch[fmap_im2col].set_scope(tbe_platform.scope_cbuf)
    sch[fmap_fractal].set_scope(tbe_platform.scope_ubuf)
    sch[ub_split_c1].set_scope(tbe_platform.scope_ubuf)
    sch[ub_transpose].set_scope(tbe_platform.scope_ubuf)
    sch[ub_merge_hw].set_scope(tbe_platform.scope_ubuf)
    sch[ub_merge_co].set_scope(tbe_platform.scope_ubuf)
    sch[workspace_res].set_scope(tbe_platform.scope_gm)
    sch[ub_res].set_scope(tbe_platform.scope_ubuf)

    dtype_input = ub_res.dtype
    if dtype_input == "int8" or dtype_input == "uint8":
        BLOCK_SIZE_ALIGN = BLOCK_SIZE_INT8
        type_size = INT8_SIZE
    else:
        BLOCK_SIZE_ALIGN = BLOCK_SIZE
        type_size = FP16_SIZE
    lcm_out_w = BLOCK_SIZE // math.gcd(out_w, BLOCK_SIZE) * out_w
    out_hw_up16 = ((out_h * out_w - 1) // BLOCK_SIZE + 1) * BLOCK_SIZE
    if lcm_out_w > out_hw_up16:
        lcm_out_w = out_hw_up16
    sch[ub_res].buffer_align((1, 1), (1, 1), (1, 1),
                             (1, BLOCK_SIZE_ALIGN))
    sch[fmap_im2col].buffer_align((1, 1), (lcm_out_w, lcm_out_w), (1, 1), (1, 1),
                                  (1, 1), (1, BLOCK_SIZE_ALIGN))

    sch[fmap_fractal].buffer_align((1, 1), (1, 1), (1, 1), (1, BLOCK_SIZE),
                                   (1, BLOCK_SIZE_ALIGN))

    fractal_shape = extract_params["fractal_shape"]
    C = fractal_shape[2] * fractal_shape[4] // filter_h // filter_w

    split_ub_size = int(UB_SIZE / NEED_UB_SPACE_NUM / type_size /
                        DOUBLE_BUFFER)

    if c_in_real % BLOCK_SIZE_ALIGN == 0:
        n_factor = batch
        howo_factor = out_h * out_w
        khkw_factor = filter_h * filter_w
        c_factor = c_in_real
        if (((lcm_out_w - 1) // BLOCK_SIZE + 1) * fractal_shape[2] * \
            fractal_shape[3] * fractal_shape[4] // filter_h // \
            filter_w >= split_ub_size).value \
            or (fractal_shape[2] > LOAD3D_REPEAT_TIME_LIMIT).value:
            n_factor = 1
            howo_factor = lcm_out_w
            khkw_factor = 1
            c_factor = BLOCK_SIZE_ALIGN
        elif (fractal_shape[1] * fractal_shape[2] * fractal_shape[3] *
              fractal_shape[4] <= split_ub_size).value:
            n_factor = 1
        elif (lcm_out_w * fractal_shape[2] * fractal_shape[3] *
              fractal_shape[4] <= split_ub_size).value:
            n_factor = 1
            howo_factor = lcm_out_w
        else:
            n_factor = 1
            howo_factor = lcm_out_w
            khkw_factor = 1

        res_n_outer, res_n_inner = sch[res].split(res.op.axis[0],
                                                  factor=n_factor)
        res_howo_outer, res_howo_inner = sch[res].split(res.op.axis[1],
                                                        factor=howo_factor)
        res_khkw_outer, res_khkw_inner = sch[res].split(res.op.axis[2],
                                                        factor=khkw_factor)
        res_c_outer, res_c_inner = sch[res].split(res.op.axis[3],
                                                  factor=c_factor)
        sch[res].reorder(res_n_outer, res_howo_outer, res_khkw_outer,
                         res_c_outer, res_n_inner, res_howo_inner,
                         res_khkw_inner, res_c_inner)

        sch[fmap_im2col].compute_at(sch[res], res_howo_outer)
        sch[fmap_in_l1].compute_at(sch[res], res_howo_outer)

        sch[workspace_res].compute_at(sch[res], res_c_outer)
        sch[ub_res].compute_at(sch[res], res_c_outer)
        sch[ub_merge_co].compute_at(sch[res], res_c_outer)
        sch[ub_merge_hw].compute_at(sch[res], res_c_outer)
        sch[ub_transpose].compute_at(sch[res], res_c_outer)
        sch[ub_split_c1].compute_at(sch[res], res_c_outer)
        sch[fmap_fractal].compute_at(sch[res], res_c_outer)

        sch[workspace_res].compute_inline()
        sch[ub_res].compute_inline()
        sch[ub_merge_co].compute_inline()
        sch[ub_merge_hw].compute_inline()
        sch[ub_split_c1].compute_inline()

        block = tvm.thread_axis("blockIdx.x")
        sch[res].bind(res_n_outer, block)

        sch[fmap_in_l1].emit_insn(fmap_in_l1.op.axis[0], insn_cmd.DMA_COPY)
        sch[fmap_im2col].emit_insn(fmap_im2col.op.axis[0],
                                   insn_cmd.SET_FMATRIX,
                                   setfmatrix_dict)
        sch[fmap_fractal].emit_insn(fmap_fractal.op.axis[0], insn_cmd.IM2COL)

        sch[ub_split_c1].emit_insn(ub_split_c1.op.axis[0], insn_cmd.DMA_COPY)
        sch[ub_transpose].emit_insn(ub_transpose.op.axis[0], insn_cmd.DMA_COPY)
        sch[ub_merge_hw].emit_insn(ub_merge_hw.op.axis[0], insn_cmd.DMA_COPY)
        sch[ub_merge_co].emit_insn(ub_merge_co.op.axis[0], insn_cmd.DMA_COPY)
        sch[ub_res].emit_insn(ub_res.op.axis[2], insn_cmd.DMA_COPY)
        sch[workspace_res].emit_insn(workspace_res.op.axis[2],
                                     insn_cmd.DMA_COPY)
        sch[res].emit_insn(res_n_inner, insn_cmd.DMA_COPY)

    else:
        c1_factor = BLOCK_SIZE_ALIGN
        res_n_outer, res_n_inner = sch[res].split(res.op.axis[0], factor=1)
        res_c1_outer, res_c1_inner = sch[res].split(res.op.axis[3],
                                                    factor=c_in_real)
        sch[ub_res].compute_at(sch[res], res_c1_outer)

        workspace_res_n_outer, workspace_res_n_inner = sch[
            workspace_res].split(workspace_res.op.axis[0], factor=1)
        workspace_res_howo_outer, workspace_res_howo_inner = sch[
            workspace_res].split(workspace_res.op.axis[1], factor=lcm_out_w)
        workspace_res_khkw_outer, workspace_res_khkw_inner = sch[
            workspace_res].split(workspace_res.op.axis[2], factor=1)

        workspace_res_c1_outer, workspace_res_c1_inner = sch[
            workspace_res].split(workspace_res.op.axis[3],
                                 factor=c1_factor)
        sch[workspace_res].reorder(
            workspace_res_n_outer, workspace_res_howo_outer,
            workspace_res_khkw_outer, workspace_res_c1_outer,
            workspace_res_n_inner, workspace_res_howo_inner,
            workspace_res_khkw_inner, workspace_res_c1_inner)

        sch[ub_merge_co].compute_at(sch[workspace_res],
                                    workspace_res_c1_outer)
        sch[ub_merge_hw].compute_at(sch[workspace_res],
                                    workspace_res_c1_outer)
        sch[ub_transpose].compute_at(sch[workspace_res],
                                     workspace_res_c1_outer)
        sch[ub_split_c1].compute_at(sch[workspace_res],
                                    workspace_res_c1_outer)

        sch[fmap_fractal].compute_at(sch[workspace_res],
                                     workspace_res_c1_outer)
        sch[fmap_im2col].compute_at(sch[workspace_res],
                                    workspace_res_howo_outer)
        sch[fmap_in_l1].compute_at(sch[workspace_res],
                                   workspace_res_howo_outer)

        if c_in_real > BLOCK_SIZE_ALIGN:
            sch[workspace_res].compute_at(sch[res], res_n_outer)
            block = tvm.thread_axis("blockIdx.x")
            sch[res].bind(res_n_outer, block)

        sch[ub_split_c1].compute_inline()
        sch[ub_transpose].compute_inline()
        sch[ub_merge_co].compute_inline()

        sch[fmap_in_l1].emit_insn(fmap_in_l1.op.axis[0], insn_cmd.DMA_COPY)
        sch[fmap_im2col].emit_insn(fmap_im2col.op.axis[0],
                                   insn_cmd.SET_FMATRIX,
                                   setfmatrix_dict)
        sch[fmap_fractal].emit_insn(fmap_fractal.op.axis[0], insn_cmd.IM2COL)

        sch[ub_split_c1].emit_insn(ub_split_c1.op.axis[0], insn_cmd.DMA_COPY)
        sch[ub_transpose].emit_insn(ub_transpose.op.axis[0], insn_cmd.DMA_COPY)
        sch[ub_merge_hw].emit_insn(ub_merge_hw.op.axis[0], insn_cmd.DMA_COPY)
        sch[ub_merge_co].emit_insn(ub_merge_co.op.axis[0], insn_cmd.DMA_COPY)
        sch[workspace_res].emit_insn(workspace_res_c1_inner, insn_cmd.DMA_COPY)
        sch[ub_res].emit_insn(ub_res.op.axis[3], insn_cmd.DMA_COPY)
        sch[res].emit_insn(res_c1_inner, insn_cmd.DMA_COPY, {"no_overlap": 1})

    sch[fmap_in_l1].double_buffer()
    sch[fmap_im2col].double_buffer()
    sch[fmap_fractal].double_buffer()
    sch[ub_split_c1].double_buffer()
    sch[ub_transpose].double_buffer()
    sch[ub_merge_hw].double_buffer()
    sch[ub_merge_co].double_buffer()
    sch[ub_res].double_buffer()

@util.check_input_type(dict, dict, (list, tuple), (list, tuple), (list, tuple),
                       str, str)
def extract_image_patches(images,
                          y,
                          ksizes,
                          strides,
                          dilates,
                          padding,
                          kernel_name="extract_image_patches"):
    """
    calculating data

    Parameters
    ----------
    images : dict
        shape and dtype of input, only support float16
    y : dict
        shape and dtype of output, should be same shape and type as input
    ksizes: input attr
    strides: input attr
    dilates: input attr
    padding: input attr
    kernel_name : str
        kernel name, default value is "extract_image_patches"

    Returns
    -------
    None
    """
    shape_input_4d = images.get("ori_shape")
    dtype_input = images.get("dtype")
    util.check_kernel_name(kernel_name)
    util.check_shape_rule(shape_input_4d)
    util.check_shape_size(shape_input_4d, SHAPE_SIZE_LIMIT)

    check_list = ("float16", "int8", "uint8")
    util.check_dtype_rule(dtype_input, check_list)
    dtype_input = dtype_input.lower()

    if dtype_input == "int8" or dtype_input == "uint8":
        BLOCK_SIZE_ALIGN = BLOCK_SIZE_INT8
        type_size = INT8_SIZE
    else:
        BLOCK_SIZE_ALIGN = BLOCK_SIZE
        type_size = FP16_SIZE
    fmap_batch, fmap_h, fmap_w, fmap_c = shape_input_4d
    fmap_c1 = (fmap_c + BLOCK_SIZE_ALIGN - 1) // BLOCK_SIZE_ALIGN
    fmap_c0 = BLOCK_SIZE_ALIGN
    shape_input = (fmap_batch, fmap_c1, fmap_h, fmap_w, fmap_c0)

    _, filter_h, filter_w, _ = ksizes
    _, stride_h, stride_w, _ = strides
    _, dilate_h, dilate_w, _ = dilates
    out_h, padding_h_before, padding_h_after = \
        common.tf_get_windowed_output_size_verbose_v2(
            fmap_h, filter_h, dilate_h, stride_h, padding)
    out_w, padding_w_before, padding_w_after = \
        common.tf_get_windowed_output_size_verbose_v2(
            fmap_w, filter_w, dilate_w, stride_w, padding)

    if (out_h <= 0) or (out_w <= 0):
        raise RuntimeError(
            "out_h and out_w can not <= 0, out_h:%d, out_w:%d"
            % (out_h, out_w))

    if (fmap_w + padding_w_before + padding_w_after) <= \
            (filter_w - 1)*dilate_w + 1 + stride_w:
        raise RuntimeError(
            "the size of fmap_w(after pad) <= filter_w(after dilation) + "
            "stride_w is forbidden")

    if (filter_h % 2 == 0 and dilate_h > fmap_h):
        raise RuntimeError(
            "get all data from padding is forbidden")

    # min cut_h
    cut_h = BLOCK_SIZE // math.gcd(out_w, BLOCK_SIZE) * stride_h + filter_h
    if cut_h > fmap_h:
        cut_h = fmap_h
    if (cut_h * fmap_w * fmap_c1 * fmap_c0 * type_size * DOUBLE_BUFFER >
            L1_SIZE):
        raise RuntimeError(
            "Input size is too large load to L1, while cut h, need size: %d" %
            (cut_h * fmap_w * fmap_c1 * fmap_c0 * type_size * DOUBLE_BUFFER))

    data_input = tvm.placeholder(shape_input, name="data", dtype=dtype_input)
    output_res, workspace_res, workspace_shape = extract_image_patches_compute(
        data_input, fmap_c, ksizes, strides, dilates, padding, kernel_name)
    sch = tvm.create_schedule(output_res.op)
    extract_image_patches_schedule(output_res, [sch])

    def _write_workspace_info(workspace_list, kernel_name):
        def write_code(wkspace_dict, fname):
            fname = os.path.realpath(fname)
            if fname.startswith(os.getcwd()):
                if os.path.exists(fname):
                    with open(fname, "r") as f:
                        load_dict = json.load(f)
                    load_dict.update(wkspace_dict)
                    with open(fname, "w") as f:
                        json.dump(load_dict,
                                  f,
                                  sort_keys=True,
                                  indent=4,
                                  separators=(',', ':'))

        def shape_to_list(shape):
            """
            translate tvm.shape to list type in python
            """
            tmp = []
            for i in shape:
                tmp.append(i.value)
            return tmp

        def get_data_width(dtype):
            m = re.search(r'\d+', dtype)
            if m:
                return int(m.group(0)) // 8
            return 0

        num = len(workspace_list)
        if num:
            shape_list = [shape_to_list(i.shape) for i in workspace_list]
            total_size = [
                functools_reduce(lambda x, y: x * y, list_i)
                for list_i in shape_list
            ]

            total_size = [i * get_data_width(j.dtype)
                          for i, j in zip(total_size, workspace_list)]
            if not os.path.exists("kernel_meta"):
                os.mkdir("kernel_meta")
                os.chmod("kernel_meta",
                         stat.S_IRWXU + stat.S_IRGRP + stat.S_IXGRP)
            wkspace_dict = {"workspace": {"num": num, "size": total_size}}
            write_code(wkspace_dict, "kernel_meta/" + kernel_name + ".json")

    with build_config:
        tvm.build(sch, [data_input, output_res, workspace_res],
                  "cce",
                  name=kernel_name)
        _write_workspace_info([workspace_res], kernel_name)


import tkinter as tk
import tkinter.font as tkFont
from tkinter import Label, ttk, messagebox
from tkinter.constants import CENTER, END, GROOVE, NONE, RIDGE, WORD

import json, random, time

FONT_NAME = "Helvetica"
sample_text = "Cras varius ex ut cursus gravida. Curabitur vitae leo egestas, imperdiet nibh eget, finibus justo. Nulla et libero tortor. Integer gravida, nisl ac maximus ullamcorper, libero nisi maximus felis, eget feugiat leo orci vitae urna. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nullam enim ante, pellentesque in pharetra nec, maximus nec orci. Aliquam erat volutpat."

class Window(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        self.init_parent()

        # Add Style-Theme
        self.style = ttk.Style(self.parent)
        ## Import the tcl file
        self.parent.tk.call("source", "project\\themes\\forest-light.tcl")
        ## Set the theme with the theme_use method
        self.style.theme_use("forest-light")

        # Define fonts
        self.font_practice_box = tkFont.Font(family=FONT_NAME, size=16)
        self.font_title = tkFont.Font(family=FONT_NAME, size=20, weight="bold")
        self.font_info_box = tkFont.Font(family=FONT_NAME, size=12)

        # Initial Phrase
        self.word_list = self.load_words()
        self.rand_phrase, self.word_num = self.get_random_phrase(self.word_list)
        self.char_num = len(self.rand_phrase)
        self.tindex = 0
        self.welcome_str = "Welcome to the typing speed test, it will start automatically when you begin typing, glhf!"

        # Timing vars
        self.test_started = False
        self.test_ended = False
        self.t_start = time.perf_counter()
        self.t_end = 1000

        # Title
        self.title_label = ttk.Label(
            self, text="Typing Speed Test", foreground="#008a25"
        )
        self.title_label.grid(row=0, column=0, padx=10, pady=5, sticky="nsew")
        self.title_label.configure(font=self.font_title, anchor=CENTER)

        # Create a frame for information to the user
        self.info_frame = ttk.LabelFrame(self, text="Info", padding=(10, 10))
        self.info_frame.grid(row=1, column=0, padx=10, pady=(20, 10), sticky="nsew")

        # Info Text
        self.info_text = tk.Text(
            self.info_frame,
            width=70,
            height=2,
            padx=5,
            pady=5,
            wrap=WORD,
            highlightthickness=0,
            borderwidth=0,
        )
        self.info_text.configure(font=self.font_info_box)
        self.info_text.config(spacing3=10)
        self.info_to_display = self.gen_info_text(self.welcome_str)
        self.info_text.insert("1.0", self.info_to_display)
        self.info_text.grid(row=0, column=0)

        # Create a frame for practice text
        self.practice_frame = ttk.LabelFrame(
            self, text="Practice text", padding=(10, 10)
        )
        self.practice_frame.grid(row=2, column=0, padx=10, pady=(10, 10), sticky="nsew")
        # Practice Text
        self.practice_text = tk.Text(
            self.practice_frame,
            width=50,
            height=4,
            padx=20,
            pady=15,
            wrap=WORD,
            highlightthickness=0,
            borderwidth=0,
        )
        self.practice_text.configure(font=self.font_practice_box)
        self.practice_text.config(spacing1=10, spacing2=10)
        self.practice_text.tag_configure("center", justify="center")

        self.practice_text.insert("1.0", self.rand_phrase)
        self.practice_text.tag_add("center", "1.0", "end")
        self.practice_text.grid(row=0, column=0)

        self.entry_label = ttk.Label(self, text="Type your text below:")
        self.entry_label.grid(row=3, column=0, padx=10, pady=2, sticky="nsew")
        self.entry_label.configure(font=self.font_info_box)

        # Entry
        self.entry = ttk.Entry(self, justify="center")
        self.entry.insert(0, "")
        self.entry.bind("<Key>", self.keystroke)
        self.entry.grid(row=4, column=0, padx=10, pady=(8, 10), sticky="ew")

        # Accentbutton
        self.restart_btn = ttk.Button(
            self,
            text="Reset Typing Test",
            style="Accent.TButton",
            command=self.restart_test,
        )
        self.restart_btn.grid(row=5, column=0, padx=10, pady=10, sticky="nsew")

    def init_parent(self):
        self.parent.title("Typing speed test")
        self.parent.config(padx=20, pady=20)
        self.parent.option_add("*tearOff", False)  # This is always a good idea
        # Make the app responsive
        self.parent.columnconfigure(index=0, weight=1)
        self.parent.columnconfigure(index=1, weight=1)
        self.parent.columnconfigure(index=2, weight=1)
        self.parent.rowconfigure(index=0, weight=1)
        self.parent.rowconfigure(index=1, weight=1)
        self.parent.rowconfigure(index=2, weight=1)

    def load_words(self):
        # Thank this guy for the wordlist : https://github.com/dwyl/english-words
        with open("project\\assets\\simple_dictionary.json") as word_file:
            valid_words = json.load(word_file)
        mlist = list(valid_words.keys())
        return mlist

    def get_random_phrase(self, word_list, min_words=10, max_words=25):
        word_num = random.randint(min_words, max_words)
        words = random.choices(word_list, k=word_num)
        phrase = " ".join(words)
        # print(word_num)
        # print(phrase)
        return (phrase, word_num)

    def gen_info_text(self, line1="hello", cpm=0, wpm=0):
        return f"{line1}\nCharacters per minute: {cpm}         ||        Words per minute: {wpm}"

    def keystroke(self, key):
        # If its the first character of the test start timing
        if not self.test_started:
            self.test_started = True
            self.t_start = time.perf_counter()

        # Compare if the character is the correct one
        current_char = self.practice_text.get(self.idx_to_position(self.tindex))
        key_pressed = key.char
        self.limit_entry_chars()
        msg = f"Try the following:{current_char}"
        if current_char == key_pressed:
            self.tindex += 1
            self.highlight_char(text=self.practice_text, index=self.tindex)
            msg = "You're doing great!"

        if not self.test_ended:
            cpm = 0
            if self.tindex > 1:
                dt = time.perf_counter() - self.t_start
                cps = float(self.tindex) / dt
                cpm = int(cps * 60)

            wpm = 0
            # When the last character is typed correctly end the test
            if self.tindex == self.char_num:
                self.test_ended = True
                self.practice_text.tag_delete("hl")
                self.t_end = time.perf_counter()
                dt = self.t_end - self.t_start
                wps = float(self.word_num) / dt
                wpm = int(wps * 60)
                msg = "Well done! Your results:"

            self.info_text.delete("1.0", END)
            self.info_to_display = self.gen_info_text(msg, cpm, wpm)
            if self.test_ended:
                messagebox.showinfo("You made it!", self.info_to_display)
            self.info_text.insert("1.0", self.info_to_display)

    def idx_to_position(self, index):
        return f"1.0+{index}c"

    def highlight_char(self, text, index):
        # turn previous character to normal color
        text.tag_delete("hl")
        # highlight next char
        idx = self.idx_to_position(index)
        text.tag_add("hl", idx)
        text.tag_config("hl", background="black", foreground="white")

    def restart_test(self):
        # reset info text
        self.info_text.delete("1.0", END)
        self.info_to_display = self.gen_info_text(self.welcome_str)
        self.info_text.insert("1.0", self.info_to_display)

        # reset practice text
        self.rand_phrase, self.word_num = self.get_random_phrase(self.word_list)
        self.char_num = len(self.rand_phrase)
        self.tindex = 0
        self.practice_text.delete("1.0", END)
        self.practice_text.insert("1.0", self.rand_phrase)
        self.practice_text.tag_add("center", "1.0", "end")

        self.test_started = False
        self.test_ended = False

    def limit_entry_chars(self):
        entry_txt = self.entry.get()
        if len(entry_txt) > 14:
            self.entry.delete(0, END)
            self.entry.insert(0, entry_txt[len(entry_txt) - 14 :])

if __name__ == "__main__":
    root = tk.Tk()
    Window(root).pack(fill="both", expand=True)
    root.mainloop()
# Notes:
# How to highlight text in a tkinter Text widget?  https://www.tutorialspoint.com/how-to-highlight-text-in-a-tkinter-text-widget

# @ViníciusGabriel: so, just the border? I don't have a system I can try it out on (linux ttk widgets don't look much different
#  from tkinter widgets), but you can probably get the look you want just by packing a borderless text widget inside a ttk.Entry
# widget so that you see the entry widget border.

# https://www.youtube.com/watch?v=qM3sXJhFGiA


# https://www.codewars.com/kata/521c2db8ddc89b9b7a0000c1

# Given an n x n array, return the array elements arranged
# from outermost elements to the middle element, traveling clockwise.

array = [[1,2,3],
         [4,5,6],
         [7,8,9]]

result = []
indent = 0
n = len(array)

while indent < n / 2:

    # top line
    for i in range(indent, n - indent):
        result.append(array[indent][i])

    # right line
    for j in range(indent + 1, n - indent):
        result.append(array[j][n-indent-1])

    # bottom line
    for i in range(n - indent - 2, indent - 1, -1):
        result.append(array[n - indent - 1][i])

    # Left line
    for j in range(n - indent - 2, indent, -1):
        result.append(array[j][indent])
    indent += 1

print(result)


# "bd {sn sn, ho ho hc}"
# for a tree, you

# "{{bd bd, ho ho hc} {~ sn:2 ~, ~ cp}, {bin bin ~, ~ bottle ~ cp}} "

import itertools
import copy
import random

#other node types needed: SquareBracket,

#render for () and <> (random and "next") - must wrap a single {} or []
#	render children totally
#	select appropriate rendered child
#for {} expressions, this could lead to the length of the full expression
#	to be greater than "1". Alternatively, children which are "too long"
#	could be stateful and are cut off and remember their position to start
#	next, just like with a normal {} (the state would be stored in the
#	() or <> node)

#every top level expression can (should?) be considered as wrapped in a []
#	for ease of computation

#for expression containing a x-node, the grandparent of the expression with
#	the x-term must handle it. eg {ax5, b d c}, the {}  is the grandparent
#	of the ax5

def flattenChildren(childList):
	timeToTuple = {} #combine the children
	for timePitchTuple in itertools.chain.from_iterable(childList):
		if timePitchTuple[0] not in timeToTuple:
			timeToTuple[timePitchTuple[0]] = timePitchTuple
		else:
			timeToTuple[timePitchTuple[0]] = (timePitchTuple[0], timeToTuple[timePitchTuple[0]][1] | timePitchTuple[1])

	return sorted(timeToTuple.values(), None, lambda tup: tup[0])

def mergeRenderedChildren(childFrac, children):
	merge = []
	for i in range(len(children)):
		timeShift = lambda timePitchTuple: (timePitchTuple[0]+(i*childFrac), timePitchTuple[1])
		merge += map(timeShift, children[i])
	#print "merge", childFrac, merge
	return merge

#node.frac is the length (in beats) of a chunk returned from a call to "render"
#TODO: make sure the above is actually true in all the code

class PydalNode:
	def __init__(self, children):
		self.children = children
		self.frac = sum(map(lambda c : c.frac, children))
		self.type = "" #the type of thing that this pydal pattern is sequencing

	def __add__(self, b):
		return PydalNode([self, b]) #TODO: make sure we REALLY want this to be dependent on child instances

	def render(self, frac=None):

		scale = 1.0 if frac is None else frac/self.frac
		self.frac = self.frac if frac is None else frac

		childFracs = map(lambda node: node.frac*scale, self.children)
		childFracs.insert(0, 0)
		accumulatedShift = 0
		merge = []

		for i in range(len(childFracs)-1):
			accumulatedShift += childFracs[i]
			#print i, accumulatedShift
			timeShift = lambda timePitchTuple: (timePitchTuple[0]+accumulatedShift, timePitchTuple[1])

			#because 0 is inserted into child fracs, use childFracs[i+1]
			#so each child is rendered with its actual frac value
			merge += map(timeShift, self.children[i].render(childFracs[i+1]))

		return merge

#render for SquareBracket:
#	render children
#	scale children to same total length
#	combine children
class SquareBracketNode(PydalNode):

	def __init__(self, children, frac = 1):
		self.children = children
		self.leaf = False
		self.frac = frac
		self.type = "SquareBracket"

	def render(self, frac=None):
		self.frac = frac = self.frac if frac is None else frac
		renderedChildren = [c.render(frac) for c in self.children]
		return flattenChildren(renderedChildren)

	def __str__(self):
		return "["+".".join([str(c) for c in self.children])+"]"

class SymbolNode(PydalNode):

	def __init__(self, children, frac = 1):
		self.children = children
		self.frac = frac
		self.leaf = True
		self.type = "Symbol"

	def render(self, frac=None):
		self.frac = frac = self.frac if frac is None else frac
		return [(0, set(self.children))]

	def __str__(self):
		return self.children[0]

class ExpressionNode(PydalNode):

	def __init__(self, children, frac = 1):
		self.frac = frac
		self.children = children
		self.type = "Expression"
		self.leaf = False

	def render(self, frac=None):
		self.frac = frac = self.frac if frac is None else frac
		#todo: why is the 1.0 needed? frac should always be a decimal
		#because 1.0 is passed in at the root of the AST
		childFrac = 1.0 * frac / len(self.children)
		renderedChildren = [c.render(childFrac) for c in self.children]
		return mergeRenderedChildren(childFrac, renderedChildren)

	def __str__(self):
		return ".".join([str(c) for c in self.children])

class MultNode(PydalNode):

	#child is either a SymbolNode or a *BracketNode
	#multNum is an integer
	#TODO: "x" multiplier and fractional multiples
	def __init__(self, child, multNum, frac = 1):
		self.child = child
		self.multNum = int(multNum)
		self.frac = frac
		self.type = "Mult"
		self.leaf = False
		self.children = [self.child]

	def render(self, frac=None):
		self.frac = frac = self.frac if frac is None else frac
		childFrac = frac / self.multNum
		childCopies = [self.child.render(childFrac) for i in range(self.multNum)]
		return mergeRenderedChildren(childFrac, childCopies)

	def __str__(self):
		return str(self.child) + "*" + str(self.multNum)

class CurlyBracketNode(PydalNode):

	def __init__(self, children, frac = 1):
		self.children = children
		self.leaf = False
		#must be set in initial construction wrt self.children
		self.alignmentInds = None

		#should this be saved state? how do we want to hanlde <> and () - "choice" operators?
		#we could make it st choice operators must contain a single "overlay" operator ({} or [])
		self.frac = frac
		self.type = "CurlyBracket"

	#because each expression is potentially nested inside a larger one,
	#the durations of each term may only be a fraction of their original
	#length wrt the number of terms in the "parent" expression.
	#frac thus represents the fraction of the total loop time that
	#this sub expression takes [0, 1]
	def render(self, frac=None):
		self.frac = frac = self.frac if frac is None else frac

		if self.alignmentInds is None:
			self.alignmentInds = [0] * len(self.children)

		#allign and select grandchildren
		#use grandchild expressions to figure out this node's expressions
		#	ex, {bd lt, bd sn sn} would have children with expressions "bd lt" and "bd sn sn",
		# 	and grandchildren "bd", "lt" from child 1, and "bd", "sn", "sn" from child 2.
		#	while its own expressions would be "bd/bd lt/sn", "bd/sn lt/bd", and "bd/sn lt/sn"
		# 	in that order, with "a/b" meaning samples a and b are played at the same time
		#this is a stateful computation and will return the "next" alignment on every call
		alignment = [[] for i in range(len(self.children))]
		for i in range(len(self.children[0].children)):
			for j in range(len(self.children)):
				alignment[j].append( self.children[j].children[ self.alignmentInds[j] ] )
				self.alignmentInds[j] = (self.alignmentInds[j]+1) % len(self.children[j].children)

		#render grandchildren
		#however, grandchild expressions could be nested expressions (and thus stateful) themselves.
		#	therefore, we need to "re-render" the grandchildren each time after alligning them
		#	a "rendered" expression is a [(float, set)], where float is in [0, 1] and
		#	represents a point of time in a loop, and set is the set of samples played
		#	at that point in time
		grandChildFrac = frac / len(self.children[0].children)
		for i in range(len(alignment)):
			for j in range(len(alignment[i])):
				alignment[i][j] = alignment[i][j].render(grandChildFrac)

		#render expression ("assemble" grandchildren)
		#	once the children have been aligned/selected and rendered, use their rendered
		#	forms to render the final version of this "state" of the expression
		renderedChildren = []#combine the "grandchildren" into "children"
		for i in range(len(alignment)):
			renderedChildren.append(mergeRenderedChildren(grandChildFrac, alignment[i]))

		return flattenChildren(renderedChildren)

	def __str__(self):
		return "{"+".".join([str(c) for c in self.children])+"}"

	#how many times this node must be evaluated before it returns the same expression
	#def getPeriod():

class AngleBracketNode(PydalNode):

	def __init__(self, children, frac=1):
		self.children = children
		self.leaf = False
		self.frac = frac
		self.type = "AngleBracket"

	def render(self, frac=None):
		self.frac = frac = self.frac if frac is None else frac
		randInd = random.randint(0, len(self.children)-1)
		child = self.children[randInd].render(frac)
		return child

	def __str__(self):
		return "<"+".".join([str(c) for c in self.children])+">"

class ParenBracketNode(PydalNode):

	def __init__(self, children, frac=1):
		self.children = children
		self.leaf = False
		self.frac = frac
		self.type = "ParenBracket"

		self.seqInd = 0

	def render(self, frac=None):
		self.frac = frac = self.frac if frac is None else frac
		child = self.children[self.seqInd].render(frac)
		self.seqInd = (self.seqInd+1) % len(self.children)
		return child

	def __str__(self):
		return "("+".".join([str(c) for c in self.children])+")"


"""
Read file into texts and calls.
It's ok if you don't understand how to read files
"""
import csv
with open('texts.csv', 'r') as f:
    reader = csv.reader(f)
    texts = list(reader)

with open('calls.csv', 'r') as f:
    reader = csv.reader(f)
    calls = list(reader)

"""
TASK 2: Which telephone number spent the longest time on the phone
during the period? Don't forget that time spent answering a call is
also time spent on the phone.
Print a message:
"<telephone number> spent the longest time, <total time> seconds, on the phone during
September 2016.".
"""

#look at calls duration only
N=len(calls)
time_spent={}
for ii in range(N):
    time_spent[calls[ii][0]]=time_spent.get(calls[ii][0],0)+int(calls[ii][-1])
    time_spent[calls[ii][1]]=time_spent.get(calls[ii][1],0)+int(calls[ii][-1])

#
phone = max(time_spent, key=time_spent.get)
time=time_spent[phone]
print("{:} spent the longest time, {:} seconds, on the phone during September 2016.".format(phone,time))


import numpy as np

def split_blob(blob):
    x = np.array([1,0,0])
    y = np.array([0,1,0])
    z = np.array([0,0,1])
    return np.array([ blob + x , blob + y , blob + z ])

state1 = np.array([[0,0,1],[0,1,0],[1,0,0]])
states = [state1]
n = 1

while True:
    # print(n)
    print("\n")
    for i in states:print(i.shape)
    # print(states)
    input("[Enter to continue]")

    new_states = []
    for state in states:
        # for each possible state of ameobas, branch
        for idx,blob in enumerate(state):
            # split each ameoba / blob into three
            new_blobs = split_blob(blob)
            acceptable = True
            for a in new_blobs:
                for b in state:
                    # verify to see if this split is allowed
                    if (a==b).all():
                        acceptable = False
                        break
                if acceptable==False:
                    break
                # since it can split, verify to see if this state has already beendone
                new_state = np.concatenate([ new_blobs , state[:idx] , state[idx+1:] ])
                new_state = new_state[np.argsort(new_state[:,2])]
                new_state = new_state[np.argsort(new_state[:,1])]
                new_state = new_state[np.argsort(new_state[:,0])]
                for nstate in new_states:
                    if (new_state == nstate).all():
                        acceptable = False
                    break
            if acceptable:
                new_states.append( new_state )

    states = new_states
    n += 1
    print(n,len(states))


import sys
sys.stdin = open("test_input.txt")
# from itertools import permutations
# from itertools import combinations
# from itertools import product
# from itertools import combinations_with_replacement

from collections import deque
input = sys.stdin.readline

N, M = map(int, input().split())
info = [list(map(int, input().split())) for _ in range(N)]

di = [0,0,-1,1,1,1,-1,-1]
dj = [-1,1,0,0,-1,1,-1,1]

def bfs(q):
    while q:
        i, j, cnt = q.popleft()
        for _ in range(8):
            ni = di[_] + i
            nj = dj[_] + j

            if 0 <= ni < N and 0 <= nj < M:
                if info[ni][nj] == 0:
                    info[ni][nj] = cnt + 1
                    q.append((ni,nj,cnt+1))

    return cnt

q = deque()
for i in range(N):
    for j in range(M):
        if info[i][j] == 1:
            q.append((i,j,0))

print(bfs(q))

# for i in range(N):
#     for j in range(M):
#         print(info[i][j], end=" ")
#     print()


from __future__ import absolute_import

import base64
import codecs
import json
import logging
import os
from binascii import hexlify, unhexlify

from pony.orm import db_session

from six import viewitems
from six.moves.urllib.parse import unquote

from twisted.internet.defer import Deferred
from twisted.web import http, resource
from twisted.web.error import SchemeNotSupported
from twisted.web.server import NOT_DONE_YET

import Tribler.Core.Utilities.json_util as json
from Tribler.Core.Modules.restapi.metadata_endpoint import SpecificChannelTorrentsEndpoint
from Tribler.Core.TorrentDef import TorrentDef
from Tribler.Core.Utilities.utilities import http_get, is_infohash, parse_magnetlink
from Tribler.Core.exceptions import DuplicateTorrentFileError
from Tribler.pyipv8.ipv8.database import database_blob

class BaseMyChannelEndpoint(resource.Resource):

    def __init__(self, session):
        resource.Resource.__init__(self)
        self.session = session
        self._logger = logging.getLogger(self.__class__.__name__)

    def return_500(self, request, exception):
        self._logger.exception(exception)
        request.setResponseCode(http.INTERNAL_SERVER_ERROR)
        return json.twisted_dumps({
            u"error": {
                u"handled": True,
                u"code": exception.__class__.__name__,
                u"message": exception.message
            }
        })

class MyChannelEndpoint(BaseMyChannelEndpoint):

    def __init__(self, session):
        BaseMyChannelEndpoint.__init__(self, session)
        self.putChild(b"torrents", MyChannelTorrentsEndpoint(session))
        self.putChild(b"commit", MyChannelCommitEndpoint(session))

    def render_GET(self, request):
        with db_session:
            my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
            if not my_channel:
                request.setResponseCode(http.NOT_FOUND)
                return json.twisted_dumps({"error": "your channel has not been created"})

            return json.twisted_dumps({
                'mychannel': {
                    'public_key': hexlify(my_channel.public_key),
                    'name': my_channel.title,
                    'description': my_channel.tags,
                    'dirty': my_channel.dirty
                }
            })

    def render_POST(self, request):
        parameters = http.parse_qs(request.content.read(), 1)
        if 'name' not in parameters and 'description' not in parameters:
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "name or description parameter missing"})

        with db_session:
            my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
            if not my_channel:
                request.setResponseCode(http.NOT_FOUND)
                return json.twisted_dumps({"error": "your channel has not been created"})

            my_channel.update_metadata(update_dict={
                "tags": unquote(parameters['description'][0]).decode('utf-8'),
                "title": unquote(parameters['name'][0]).decode('utf-8')
            })

        return json.twisted_dumps({"edited": True})

    def render_PUT(self, request):
        parameters = http.parse_qs(request.content.read(), 1)

        if 'name' not in parameters or not parameters['name'] or not parameters['name'][0]:
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "channel name cannot be empty"})

        if 'description' not in parameters or not parameters['description']:
            description = u''
        else:
            description = unquote(parameters['description'][0]).decode('utf-8')

        my_key = self.session.trustchain_keypair
        my_channel_pk = my_key.pub().key_to_bin()

        # Do not allow to add a channel twice
        if self.session.lm.mds.get_my_channel():
            request.setResponseCode(http.CONFLICT)
            return json.twisted_dumps({"error": "channel already exists"})

        title = unquote(parameters['name'][0]).decode('utf-8')
        self.session.lm.mds.ChannelMetadata.create_channel(title, description)
        return json.twisted_dumps({
            "added": hexlify(str(my_channel_pk)),
        })

class MyChannelTorrentsEndpoint(BaseMyChannelEndpoint):

    def getChild(self, path, request):
        return MyChannelSpecificTorrentEndpoint(self.session, path)

    def render_GET(self, request):
        with db_session:
            my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
            if not my_channel:
                request.setResponseCode(http.NOT_FOUND)
                return json.twisted_dumps({"error": "your channel has not been created"})

            sanitized = SpecificChannelTorrentsEndpoint.sanitize_parameters(request.args)
            if b'exclude_deleted' in request.args:
                sanitized['exclude_deleted'] = request.args[b'exclude_deleted']

            torrents, total = self.session.lm.mds.TorrentMetadata.get_entries(
                channel_pk=database_blob(my_channel.public_key), **sanitized)
            torrents = [torrent.to_simple_dict() for torrent in torrents]

            return json.twisted_dumps({
                "results": torrents,
                "first": sanitized['first'],
                "last": sanitized['last'],
                "sort_by": sanitized['sort_by'],
                "sort_asc": int(sanitized['sort_asc']),
                "total": total,
                "dirty": my_channel.dirty
            })

    def render_POST(self, request):
        parameters = http.parse_qs(request.content.read(), 1)
        if 'status' not in parameters or 'infohashes' not in parameters:
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "status or infohashes parameter missing"})

        new_status = int(parameters['status'][0])
        infohashes = parameters['infohashes']

        with db_session:
            my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
            if not my_channel:
                request.setResponseCode(http.NOT_FOUND)
                return json.twisted_dumps({"error": "your channel has not been created"})

            for infohash in infohashes:
                torrent = my_channel.get_torrent(unhexlify(infohash))
                if not torrent:
                    continue
                torrent.status = new_status

        return json.twisted_dumps({"success": True})

    def render_DELETE(self, request):
        with db_session:
            my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
            if not my_channel:
                request.setResponseCode(http.NOT_FOUND)
                return json.twisted_dumps({"error": "your channel has not been created"})

            my_channel.drop_channel_contents()

        return json.twisted_dumps({"success": True})

    @db_session
    def render_PUT(self, request):
        """
        .. http:put:: /mychannel/torrents

        Add a torrent file to your own channel. Returns error 500 if something is wrong with the torrent file
        and DuplicateTorrentFileError if already added to your channel. The torrent data is passed as base-64 encoded
        string. The description is optional.

        Option torrents_dir adds all .torrent files from a chosen directory
        Option recursive enables recursive scanning of the chosen directory for .torrent files

            **Example request**:

            .. sourcecode:: none

                curl -X PUT http://localhost:8085/mychannel/torrents
                --data "torrent=...&description=funny video"

            **Example response**:

            .. sourcecode:: javascript

                {
                    "added": True
                }

            **Example request**:

            .. sourcecode:: none

                curl -X PUT http://localhost:8085/mychannel/torrents? --data "torrents_dir=some_dir&recursive=1"

            **Example response**:

            .. sourcecode:: javascript

                {
                    "added": 13
                }

            :statuscode 404: if your channel does not exist.
            :statuscode 500: if the passed torrent data is corrupt.
        """
        my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
        if not my_channel:
            request.setResponseCode(http.NOT_FOUND)
            return json.twisted_dumps({"error": "your channel has not been created yet"})

        parameters = http.parse_qs(request.content.read(), 1)

        if 'description' not in parameters or not parameters['description']:
            extra_info = {}
        else:
            extra_info = {'description': parameters['description'][0]}

        def _on_url_fetched(data):
            return TorrentDef.load_from_memory(data)

        def _on_magnet_fetched(meta_info):
            return TorrentDef.load_from_dict(meta_info)

        def _on_torrent_def_loaded(torrent_def):
            with db_session:
                channel = self.session.lm.mds.get_my_channel()
                channel.add_torrent_to_channel(torrent_def, extra_info)
            return 1

        def _on_added(added):
            request.write(json.twisted_dumps({"added": added}))
            request.finish()

        def _on_add_failed(failure):
            failure.trap(ValueError, DuplicateTorrentFileError, SchemeNotSupported)
            self._logger.exception(failure.value)
            request.write(self.return_500(request, failure.value))
            request.finish()

        def _on_timeout(_):
            request.write(self.return_500(request, RuntimeError("Metainfo timeout")))
            request.finish()

        # First, check whether we did upload a magnet link or URL
        if 'uri' in parameters and parameters['uri']:
            deferred = Deferred()
            uri = parameters['uri'][0]
            if uri.startswith("http:") or uri.startswith("https:"):
                deferred = http_get(uri)
                deferred.addCallback(_on_url_fetched)
            elif uri.startswith("magnet:"):
                _, xt, _ = parse_magnetlink(uri)
                if xt and is_infohash(codecs.encode(xt, 'hex')) \
                        and (my_channel.torrent_exists(xt) or my_channel.copy_to_channel(xt)):
                    return json.dumps({"added": 1})
                try:
                    self.session.lm.ltmgr.get_metainfo(uri, callback=deferred.callback,
                                                       timeout=30, timeout_callback=_on_timeout, notify=True)
                except Exception as ex:
                    deferred.errback(ex)

                deferred.addCallback(_on_magnet_fetched)
            else:
                request.setResponseCode(http.BAD_REQUEST)
                return json.twisted_dumps({"error": "unknown uri type"})

            deferred.addCallback(_on_torrent_def_loaded)
            deferred.addCallback(_on_added)
            deferred.addErrback(_on_add_failed)
            return NOT_DONE_YET

        torrents_dir = None
        if 'torrents_dir' in parameters and parameters['torrents_dir'] > 0:
            torrents_dir = parameters['torrents_dir'][0]
            if not os.path.isabs(torrents_dir):
                request.setResponseCode(http.BAD_REQUEST)
                return json.twisted_dumps({"error": "the torrents_dir should point to a directory"})

        recursive = False
        if 'recursive' in parameters and parameters['recursive'] > 0:
            recursive = parameters['recursive'][0]
            if not torrents_dir:
                request.setResponseCode(http.BAD_REQUEST)
                return json.twisted_dumps({"error": "the torrents_dir parameter should be provided when the recursive "
                                            "parameter is set"})

        if torrents_dir:
            torrents_list, errors_list = my_channel.add_torrents_from_dir(torrents_dir, recursive)
            return json.twisted_dumps({"added": len(torrents_list), "errors": errors_list})

        if 'torrent' not in parameters or not parameters['torrent']:
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "torrent parameter missing"})

        # Try to parse the torrent data
        try:
            torrent = base64.b64decode(parameters['torrent'][0])
            torrent_def = TorrentDef.load_from_memory(torrent)
        except (TypeError, ValueError):
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            return json.twisted_dumps({"error": "invalid torrent file"})

        try:
            my_channel.add_torrent_to_channel(torrent_def, extra_info)
        except DuplicateTorrentFileError:
            request.setResponseCode(http.INTERNAL_SERVER_ERROR)
            return json.twisted_dumps({"error": "this torrent already exists in your channel"})

        return json.twisted_dumps({"added": 1})

class MyChannelSpecificTorrentEndpoint(BaseMyChannelEndpoint):

    def __init__(self, session, infohash):
        BaseMyChannelEndpoint.__init__(self, session)
        self.infohash = unhexlify(infohash)

    @db_session
    def render_PATCH(self, request):
        """
        .. http:put:: /mychannel/torrents/(string: torrent infohash)

        Edit tags, status or title of a torrent entry in a your channel.
        The properties to edit should be provided in the PATCH data as a URL-encoded dict.
        On success, it returns a new status for the torrent.

            **Example request**:

            .. sourcecode:: none

                curl -X PUT http://localhost:8085/mychannel/torrents/97d2..151
                --data "tags=Video"

            **Example response**:

            .. sourcecode:: javascript

                {
                    "success": 1,
                    "new_status": 6,
                    "dirty": 1
                }

            :statuscode 404: if your channel or the infohash does not exist.
            :statuscode 500: if the passed arguments data is wrong.
        """
        parameters_raw = http.parse_qs(request.content.read(), 1)
        parameters = {}
        # FIXME: make all endpoints Unicode-compatible in a unified way
        for param, val in viewitems(parameters_raw):
            parameters.update({param: [item.decode('utf-8') for item in val]})

        if 'status' not in parameters and 'tags' not in parameters and 'title' not in parameters:
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "attribute to change is missing"})

        if 'status' in parameters and ('tags' in parameters or 'title' in parameters):
            request.setResponseCode(http.BAD_REQUEST)
            return json.twisted_dumps({"error": "cannot set status manually when changing other parameters"})

        my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
        if not my_channel:
            request.setResponseCode(http.NOT_FOUND)
            return json.twisted_dumps({"error": "your channel has not been created"})

        torrent = my_channel.get_torrent(self.infohash)
        if not torrent:
            request.setResponseCode(http.NOT_FOUND)
            return json.twisted_dumps({"error": "torrent with the specified infohash could not be found"})

        torrent.update_properties(dict([(attribute, parameters[attribute][0]) for attribute in
                                        ['status', 'tags', 'title'] if attribute in parameters]))

        return json.twisted_dumps({"success": True, "new_status": torrent.status, "dirty": my_channel.dirty})

class MyChannelCommitEndpoint(BaseMyChannelEndpoint):

    def render_POST(self, request):
        with db_session:
            my_channel = self.session.lm.mds.ChannelMetadata.get_my_channel()
            if not my_channel:
                request.setResponseCode(http.NOT_FOUND)
                return json.twisted_dumps({"error": "your channel has not been created"})

            torrent_dict = my_channel.commit_channel_torrent()
            if torrent_dict:
                self.session.lm.gigachannel_manager.updated_my_channel(TorrentDef.load_from_dict(torrent_dict))

        return json.twisted_dumps({"success": True})