# -*- coding: gb18030 -*-
#
# $Id: PBItem.py,v 1.7 2008-07-21 02:57:41 huangyongwei Exp $

"""
implement quick item class
2006/07/15: writen by huangyongwei
"""

import csdefine
from guis import *
from guis.controls.Item import Item
from guis.controls.StaticText import StaticText
from guis.controls.CircleCDCover import CircleCDCover as CDCover
from guis.otheruis.AnimatedGUI import AnimatedGUI
from Helper import courseHelper
from LabelGather import labelGather
from SKItemDetector import SKIDetector
import csconst
import csstatus

class PBItem( Item ):
	__cc_colors = {}
	__cc_colors[ "default" ] = ( 255, 255, 255, 255 )
	__cc_colors[ "unableUse" ] = ( 150, 70, 70, 255 )

	def __init__( self, item, index ):
		Item.__init__( self, item )
		self.dragMark = DragMark.PET_QUICK_BAR
		self.__initialize( item )
		self.__index = index

		self.__triggers = {}
		self.__registerTriggers()

		rds.shortcutMgr.setHandler( "PET_QB_GRID_%i" % ( index + 1 ), self.spellItem )

	def __initialize( self, item ) :
		self.focus = True
		self.crossFocus = True
		self.dragFocus = True
		self.dropFocus = True

		self.__pyLbAmount = StaticText( item.lbAmount )
		self.__pyLbAmount.font = "system_small.font"
		self.__pyLbAmount.color = 255, 228, 193, 255
		self.__pyLbAmount.text = ""
		self.autoUseSign = False

		self.__pyCDCover = CDCover( item.circleCover, self )
		self.__pyCDCover.crossFocus = False

		self.__pyOverCover = AnimatedGUI( item.overCover )
		self.__pyOverCover.initAnimation( 1, 8, ( 2, 4 ) )					# 动画播放一次，共8帧
		self.__pyOverCover.cycle = 0.4										# 循环播放一次的持续时间，单位：秒
		self.__pyCDCover.onUnfreezed.bind( self.__pyOverCover.playAnimation )

		self.clear()

	def dispose( self ) :
		Item.dispose( self )
		self.__pyCDCover.dispose()
		self.__deregisterTriggers()

	# ----------------------------------------------------------------
	# private
	# ----------------------------------------------------------------
	def __registerTriggers( self ) :
		self.__triggers["EVT_ON_PET_BEGIN_COOLDOWN"] = self.__beginCooldown
		self.__triggers["EVT_ON_PET_MP_CHANGE"]  	 = self.__onMPChanded
		for trigger in self.__triggers.iterkeys() :
			ECenter.registerEvent( trigger, self )

	def __deregisterTriggers( self ) :
		for trigger in self.__triggers.iterkeys() :
			ECenter.unregisterEvent( self, trigger )
		self.__triggers = {}

	# -------------------------------------------------
	def __cancelCooldown( self ) :
		if self.itemInfo is not None :
			self.itemInfo.unlock()
		self.__pyCDCover.reset( 0 )

	def __beginCooldown( self, cooldownType, lastTime ) :
		if self.itemInfo is None : return
		if self.itemInfo.isCooldownType( cooldownType ) :
			cdInfo = self.itemInfo.getCooldownInfo()
			self.__pyCDCover.unfreeze( *cdInfo )

	def __onMPChanded( self, mp, mpMax ) :
		"""
		宠物MP 改变会通知此处
		"""
		self.updateIconState()

	# ----------------------------------------------------------------
	# protected
	# ----------------------------------------------------------------
	def checkForDetection_( self, newItemInfo ) :
		"""
		检查是否需要从探测器中更新
		"""
		SKIDetector.unbindPyItem( self )						# 格子清空则从探测器中清除

		if newItemInfo is None : return

		spell = newItemInfo.getSpell()
		if spell is None : return

		rangeMax = csconst.PET_FORCE_FOLLOW_RANGE
		SKIDetector.bindPyItem( self, ( "PET", rangeMax ) )		# 添加到探测器

	# -------------------------------------------------
	def onDragStart_( self, pyDragged ) :
		if pyDragged.itemInfo is None:
			toolbox.itemCover.normalizeItem()
			return False
		Item.onDragStart_( self, pyDragged )
		return True

	def onDrop_( self, pyTarget, pyDropped ):
		Item.onDrop_( self, pyTarget, pyDropped )
		if pyDropped.itemInfo is None : return True
		player = BigWorld.player()
		if pyDropped.dragMark == DragMark.PETSKILL_BAR: 					# 从宠物技能面板
			player.pcg_updatePetQBItem( self.index, pyDropped.itemInfo.baseItem )
		elif pyDropped.dragMark == DragMark.PET_QUICK_BAR:					# 宠物快捷栏之间拖放
			player.pcg_exchangePetQBItem( pyDropped.index, self.index )
		return True

	def onDragStop_( self, pyDragged ):
		Item.onDragStop_( self, pyDragged )
		if ruisMgr.isMouseHitScreen():
			BigWorld.player().pcg_updatePetQBItem( self.index, None )

	def onLClick_( self, mods ) :
		Item.onLClick_( self, mods )
		self.spellItem()
		return

	def onRClick_( self, mods ) :
		Item.onRClick_( self, mods )
		BigWorld.player().pcg_toggleAutoUsePetQBItem( self.index )

	# ----------------------------------------------------------------
	# public
	# ----------------------------------------------------------------
	def onEvent( self, macroName, *args ) :
		"""
		triggered by base
		"""
		self.__triggers[macroName]( *args )

	# -------------------------------------------------
	def update( self, itemInfo ) :
		self.checkForDetection_( itemInfo )
		Item.update( self, itemInfo )
		if itemInfo is not None :
			self.__pyLbAmount.text = ""
			if itemInfo.countable and itemInfo.amount > 1 :
				self.__pyLbAmount.text = str( itemInfo.amount )
			cdInfo = itemInfo.getCooldownInfo()
			self.__pyCDCover.unfreeze( *cdInfo )
			self.updateIconState()
		else :
			# 如果没有物品了当然就是正常显示
			self.__pyCDCover.reset( 0 )
			self.color = PBItem.__cc_colors[ "default" ]

	# -------------------------------------------------
	def spellItem( self ):
		if self.itemInfo is not None :
			self.itemInfo.spell()
		return True

	def clear( self ) :
		"""
		clean the item
		"""
		Item.clear( self )
		self.__pyLbAmount.text = ""
		self.__cancelCooldown()
		self.autoUseSign = False
		SKIDetector.unbindPyItem( self )						# 从探测器移除

	def updateIconState( self ):
		"""
		更新快捷栏图标的状态  红色，绿色
		"""
		player = BigWorld.player()
		itemInfo = self.itemInfo

		if itemInfo is not None :
			#显示/取消自动释放的标志
			if itemInfo.autoUse:
				# 自动攻击状态
				self.autoUseSign = True
			else:
				self.autoUseSign = False
			#设置图标的颜色
			state = itemInfo.validTarget()
			if state in [ csstatus.SKILL_INTONATING, csstatus.SKILL_NOT_READY, csstatus.SKILL_GO_ON ]:
				self.color = PBItem.__cc_colors[ "default" ]
				return
			elif state == csstatus.SKILL_TOO_FAR :
				# 距离远要单列出来
				if player.pcg_getActPet() and player.position.distTo( player.pcg_getActPet().position ) > csconst.PET_FORCE_FOLLOW_RANGE:
					# 距离太远红色
					self.color = PBItem.__cc_colors[ "unableUse" ]
					return
			else:
				# 不能使用，显示红色
				self.color = PBItem.__cc_colors[ "unableUse" ]
				return

		# 正常显示
		self.color = PBItem.__cc_colors[ "default" ]

	def onDetectorTrigger( self ) :
		"""目标距离侦测回调"""
		self.updateIconState()

	# ----------------------------------------------------------------
	# protected
	# ----------------------------------------------------------------
	def onDescriptionShow_( self ) :
		if self.itemInfo is None :
			self.description = labelGather.getText( "quickbar:pbItem", "tipsSkillItem" )
		Item.onDescriptionShow_( self )

	def onDescriptionHide_( self ) :
		"""
		当鼠标离开时被调用，这里因此描述
		"""
		Item.onDescriptionHide_( self )

	# ----------------------------------------------------------------
	# property methods
	# ----------------------------------------------------------------
	def _getIndex( self ) :
		return self.__index

	def _setAutoUseSign( self , show ):
		"""
		设置自动释放的标志（边框出现旋转的线条）
		"""
		self.gui.y_light.visible = show

	def _getAutoUseSign( self ):
		"""
		查看是否显示自动释放的标志
		"""
		return self.gui.y_light.visible

	# ----------------------------------------------------------------
	# property methods
	# ----------------------------------------------------------------
	index = property( _getIndex )
	autoUseSign = property( _getAutoUseSign, _setAutoUseSign)			#　获取和设置自动使用标志的显示


import math
import bisect

def get_all_prime_numbers(n):
    # return all prime numbers less than n
    prime_nums = []
    for i in range(2, n+1):
        is_prime = True
        sqrt_i = int(math.sqrt(i))
        for j in range(2, sqrt_i + 1):
            if i % j == 0:
                is_prime = False
                break
        if is_prime:
            prime_nums.append(i)
    return prime_nums

def factorise(keys, keys_product):
    for prime in keys:
        quotient, remainder = divmod(keys_product, prime)
        if remainder == 0:
            return prime, quotient

def get_keys(possible_primes, encrypted_text):
    keys = []
    for index, crypt in enumerate(encrypted_text):
        keys += factorise(possible_primes, crypt)
    keys = list(set(keys))
    keys.sort()
    return keys

def get_key_sequence(encrypted_text, cipher_keys):
    text_len = len(encrypted_text)
    key_sequence = []
    for i in range(text_len - 1):
        current_keys = factorise(cipher_keys, encrypted_text[i])
        next_keys = factorise(cipher_keys, encrypted_text[i+1])
        if (current_keys[0] == next_keys[0]) or (current_keys[0] == next_keys[1]):
            key_sequence.append(current_keys[0])
        else:
            key_sequence.append(current_keys[1])
    first_key = [encrypted_text[0]//key_sequence[0]]
    last_key = [encrypted_text[-1]//key_sequence[-1]]
    key_sequence = first_key + key_sequence + last_key
    print(key_sequence)
    return key_sequence

def get_decrypt_dict(cipher_keys):
    decrypt_dict = {}
    ascii_start = 65
    for i, key in enumerate(cipher_keys):
        decrypt_dict[key] = chr(ascii_start + i)
    return decrypt_dict

def get_decrypted_text(key_sequence, decrypt_dict):
    decrypted_text = ''
    for key in key_sequence:
        decrypted_text += decrypt_dict[key]
    return decrypted_text

n_cases = int(input())
limits = []
lengths = []
encrypted_texts = []
for i in range(n_cases):
    lim_and_length = input().split()
    limits.append(int(lim_and_length[0]))
    lengths.append(int(lim_and_length[1]))
    encrypted_text = input()
    encrypted_text = list(map(int, encrypted_text.split()))
    encrypted_texts.append(encrypted_text)

all_primes = get_all_prime_numbers(max(limits))
# print(all_primes)

for i in range(n_cases):
    n = limits[i]
    index_of_n = bisect.bisect_right(all_primes, n)
    possible_primes = all_primes[:index_of_n]
    cipher_keys = get_keys(possible_primes, encrypted_texts[i])
    decrypt_dict = get_decrypt_dict(cipher_keys)
    print(decrypt_dict)
    print(cipher_keys)
    key_sequence = get_key_sequence(encrypted_texts[i], cipher_keys)
    decrypted_text = get_decrypted_text(key_sequence, decrypt_dict)
    print("DECRYPTED\n", decrypted_text)


import pytest
import os
import time
import json
import uuid
import pandas as pd
import logging

from unify import dbmgr
from unify.db_wrapper import DBManager, TableHandle, DBSignals

logging.basicConfig(level=logging.DEBUG)
#logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)

@pytest.fixture
def db():
    with dbmgr() as _db:
        yield _db

def test_schemas(db: DBManager):
    db.create_schema("myscheme1")
    scs = db.list_schemas()
    assert 'myscheme1' in scs['schema_name'].tolist()

    db.drop_schema("myscheme1")
    time.sleep(0.5)
    assert 'myscheme1' not in db.list_schemas()['schema_name'].tolist()

def test_tables(db: DBManager):
    schema = "myscheme2"
    db.create_schema(schema)

    db.create_table(TableHandle("users", schema), {"*id":"VARCHAR", "name":"VARCHAR"})

    df = db.list_tables(schema)

    if not df.empty:
        alltables = df['table_name'].tolist()
        assert alltables == ['users']

    db.drop_table(TableHandle("users", schema))
    time.sleep(1)
    df = db.list_tables(schema)
    assert df.empty
    db.drop_schema(schema, cascade=True)

@pytest.mark.skip(reason="")
def test_df_tables(db: DBManager):
    schema = "myscheme3"
    db.drop_schema(schema, cascade=True)

    db.create_schema(schema)

    df = pd.DataFrame({"names": ["scott","alex","joe","mary"], "ages": [25, 35, 45, 55]})

    desc = 'the greatest people ever'
    db.write_dataframe_as_table(df, TableHandle("people", schema, {'description':desc}))

    info_tables = db.list_tables(schema)
    assert 'people' in info_tables['table_name'].tolist()

    meta_rec = info_tables.loc[info_tables['table_name'] == 'people'].to_dict('records')
    assert 'description' in meta_rec[0]
    assert meta_rec[0]['description'] == desc

    dbpeeps = db.execute(f"select * from {schema}.people")
    print(dbpeeps)
    assert sorted(dbpeeps['names'].tolist()) == sorted(df['names'].tolist())
    assert sorted(dbpeeps['ages'].tolist()) == sorted(df['ages'].tolist())

    db.drop_schema(schema, cascade=True)

from sqlalchemy.orm.session import Session
@pytest.mark.skip(reason="")
def test_connection_scans(db):
    from unify.db_wrapper import ConnectionScan

    values = {"key": "abc123", "opts": 5}

    session = Session(bind=db.engine)
    c = ConnectionScan(table_name="pulls",table_schema="github",connection="github")
    c.values = values
    session.add(c)

    scan = session.query(ConnectionScan).order_by('created').first()
    print("found scan: ", scan)
    assert c.values == values

@pytest.mark.skip(reason="")
def test_create_table_signal(db: DBManager):
    signals = []
    def on_table_create(dbmgr, table):
        signals.append((table, table.table_opts()))

    db.register_for_signal(DBSignals.TABLE_CREATE, on_table_create)

    db.create_schema("sch1")
    db.execute("drop table if exists sch1.users")
    db.execute("create table sch1.users (id VARCHAR, PRIMARY KEY(id))")

    assert len(signals) == 1

    db.execute("drop view if exists sch1.all_users")
    db.execute("create view sch1.all_users as select * from sch1.users")
    print(signals)
    assert len(signals) == 2


n = int(input())
odd = set()
even = set()

for row in range(1, n+1):
    name = input()
    name_sum = sum(list([ord(x) for x in name]))

    result = name_sum//row

    if result % 2 == 0:
        even.add(result)
    else:
        odd.add(result)

odd_sum = sum(odd)
even_sum = sum(even)

if odd_sum == even_sum:
    union = odd.union(even)
    print(*union, sep=', ')
elif odd_sum > even_sum:
    difference = odd.difference(even)
    print(*difference, sep=', ')
elif even_sum > odd_sum:
    difference = odd.symmetric_difference(even)
    print(*difference, sep=', ')


import socket

host = '10.2.3.3'
port = 13337

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port))

s.listen(1)
conn, addr = s.accept()
print(f"Un client vient de se co et son IP c'est {addr}.")

while True:

    try:
        data = conn.recv(1024).decode('utf-8')

        if not data: break

        print(f"Données reçues du client : {data}")

        message = "Will be send to the client"
        if "meo" in data :
            message = "Meo à toi confrère."
        elif "waf" in data :
            message = "ptdr t ki"
        else :
            message = "Mes respects humble humain."
        conn.sendall(message.encode('utf-8'))
    except socket.error:
        print("Error Occured.")
        break

conn.close()


#!/usr/bin/python

import tornado.web
import tornado.websocket
import tornado.ioloop
import MFRC522
import signal
import sys
import RPi.GPIO as GPIO
import time
import threading
import requests
import json
import logging

logging.basicConfig(filename='timekeeper.log', level=logging.INFO, format='%(asctime)s %(message)s');

class WorkerThread(threading.Thread):
	def __init__(self, threadID, name, counter, race_id):
		threading.Thread.__init__(self)
		self.threadID = threadID
		self.name = name
		self.counter = counter
		self.is_running = False;
		self.race_id = race_id;

	def exit_thread(self):
		self.is_running = False;
	def run(self):
		self.is_running = True;
		logging.info('start race whith id: '+ str(self.race_id));
		tagUId_old = "";
		MIFAREReader = MFRC522.MFRC522();
		while(self.is_running):
				(status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
				if status == MIFAREReader.MI_OK:
					(status,backData) = MIFAREReader.MFRC522_Anticoll()
					if(len(backData) < 5):
						logging.debug("cannot read card id correct");
					else:
						tagUId = str(backData[0])+"@"+str(backData[1])+"@"+str(backData[2])+"@"+str(backData[3])+"@"+str(backData[4])
					logging.debug('tagUid:' + str(tagUId));
					if tagUId != tagUId_old:
						logging.info("tagUid:" + str(tagUId));
						data = json.dumps({'command':'scan','raceId':str(self.race_id),'tagId':tagUId});
						tornado.ioloop.IOLoop.instance().add_callback(WebSocketHandler.push_client,data)
						try:
							data = json.dumps({'raceId':str(self.race_id),'tagId':tagUId})
							logging.info('insert lap: '+str(data));
							response = requests.post("http://10.0.0.80:8080/lap/insert?raceId="+str(self.race_id)+"&tagId="+tagUId);
							tagUId_old = tagUId;
						except:
							logging.info("Exception occured during insert ");
		logging.info("do cleanup")
		MFRC522.MFRC522_cleanUp
class WebSocketHandler(tornado.websocket.WebSocketHandler):

	connections = set()
	global thread

	def check_origin(self, origin):
		return True
	def open(self):
		logging.info("New client connected");
		self.connections.add(self)

	def on_message(self, message):
		global thread
		jsonMessage = json.loads(message);
		if(jsonMessage['command']=="start"):
			logging.info("start game");
			if(thread.isAlive()):
				logging.info("Thread allready started");
			else:
				raceId = jsonMessage['raceId']
				thread = WorkerThread(1,"Thread1",1,raceId);
				thread.start();

		if(jsonMessage['command']=="stop"):
			logging.info("stop game");
			thread.exit_thread();
			logging.info("thread beended:" + str(thread.isAlive()));
		if(jsonMessage['command']=="readSingle"):
			logging.info("read singleId");
			self.readSingle();
	def readSingle(self):
		tagUId_old = "";
		MIFAREReader = MFRC522.MFRC522();
		tagUId = "";
		tagUId_old = "";
		while tagUId_old == tagUId:
			(status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
			if status == MIFAREReader.MI_OK:
				(status,backData) = MIFAREReader.MFRC522_Anticoll()
				if status == MIFAREReader.MI_OK:
					tagUId = str(backData[0])+"@"+str(backData[1])+"@"+str(backData[2])+"@"+str(backData[3])+"@"+str(backData[4])
					logging.info (tagUId)
					data = json.dumps({'command':'singleScan','tagId':tagUId});
					self.push_client(data);
		MFRC522.MFRC522_cleanUp
	def on_close(self):
		self.connections.remove(self)
		logging.info("Client disconnected");

	@classmethod
	def push_client(cls, msg):
		logging.debug('writing to clients' + msg);
		for client in cls.connections:
			client.write_message(msg)

thread = WorkerThread(1,"Thread1",1,0)
application = tornado.web.Application([
    (r"/", WebSocketHandler),
])

if __name__ == "__main__":
    application.listen(4711)
    tornado.ioloop.IOLoop.instance().start()


import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import serial
import pathlib

def import_data():
    try:
        arduino = serial.Serial("COM3", timeout=1)
    except:
        print('please check port')

    rawdata = []
    count = 0

    while count<50:
        rawdata.append(arduino.readline())
        count+=1

    return rawdata

def clean_data(raw):
    cleaned = []
    for i in range(len(raw)):
        test = str(raw[i]).split(';')
        L = len(test)-1
        test[0] = test[0][2:]
        test[L] = test[L][:len(test[L])-5]
        if len(test) == 8:
            cleaned.append(test)
    return cleaned

def to_pandas(data):
    df = pd.DataFrame(data).rename(
        index=str,
        columns={
            0:'raw',
            1:'R',
            2:'SHID',
            3:'SIG',
            4:'E',
            5:'M',
            6:'t_b',
            7:'t_a'
            }
        )
    return df

def create_dict(freq,amp,title):
    dict_ = {'Freq_'+title:freq.tolist(),'Amp_'+title:amp.tolist()}
    return dict_

def norm(x):
    max = np.max(x)
    min = np.min(x)
    z = [(i-min)/(max-min) for i in x]
    return z

def get_fourier(df):

    _list = ['raw','R','SIG','SHID','E','M']

    norm_dict = {}

    for item in _list:
        norm_dict.update({item:norm([float(i) for i in df[item].tolist()])})

    fs = 100

    x = np.linspace(0,0.5,500)
    S = np.sin(40 * 2 * np.pi * x) + 0.5 * np.sin(90 * 2 * np.pi * x)

    four_dict = {}

    for item in _list:
        x, freq, amp = fourier(norm_dict[item])
        four_dict.update({item:{'x':x,'freq':freq,'amp':amp}})

    i = 0
    for item in _list:
        i=i+1
        graph(norm_dict[item],
              four_dict[item]['x'],
              four_dict[item]['freq'],
              four_dict[item]['amp'],
              item,
              i
            )

    _dict = {}

    for item in _list:
        _dict.update({'freq_'+item:freq.tolist(),'amp_'+item:amp.tolist()})

    df_fourier = pd.DataFrame(_dict)

    #plt.show()

    return df_fourier

def graph(y,x,freq,amp,title,count):
    """
    Graphs the fourier transformed data

    Parameters
    ----------
    x

    Returns
    -------
    N/A

    """

    plt.figure(count)
    fig,ax = plt.subplots(2,1)
    ax[0].plot(x,y,color='blue',linewidth = 0.5, marker='.', markersize = 0.5)
    ax[0].set_title('pre_fft_'+title)
    ax[1].plot(freq,amp,color='blue',linewidth = 0.5, marker='.', markersize = 0.5)
    ax[1].set_title('fft_'+title)

def fourier(s):

    x = np.linspace(0,0.5,len(s))
    s = np.array(s)
    fft = np.fft.fft(s)
    #print(fft.real())
    T = x[1] - x[0]
    N = s.size
    f = np.linspace(0,1/T,N)
    freq = f[:N // 2]
    amp = np.abs(fft)[:N // 2] * 1 / N

    return x,freq,amp

def save_data(df):

    try:
        pd.read_csv("C://Users//ianvi//OneDrive//Documents//Projects//Hand//unlabelled_test.csv")
        new = False
    except:
        print("no existing data, creating new")
        new = True

    path = pathlib.Path(__file__).parent.resolve()
    df.to_csv(path_or_buf=path)

def data_processing():
    rawdata = import_data()
    cleaned = clean_data(rawdata)
    df = to_pandas(cleaned)
    df_fourier = get_fourier(df)
    save_data(df_fourier)

data_processing()


#########################################################
#
# author: Yoni Leibner
# description: Analyzer class allows to easy visualization
#              and exploration of neurons from multiple
#              point of view, and also to create neuron
#              movies.
# date of modification:  11.05.2023
#
#########################################################

# from neuron import h, gui
# from Neuron_analysis_tool.distance import Distance
# from Neuron_analysis_tool.morph_ploter import get_norm
# import matplotlib.pyplot as plt
# from mpl_toolkits.axes_grid1 import make_axes_locatable
# import os
# import matplotlib as mpl
# import numbers

from Neuron_analysis_tool.protocols import *
from Neuron_analysis_tool.color_func import color_func, color_func_norm #, color_func_by_func
from Neuron_analysis_tool.morph_ploter import plot_morph
from Neuron_analysis_tool.dendogram import plot_dendogram
from Neuron_analysis_tool.cable import get_cable, plot_cable
from Neuron_analysis_tool.attenuation import plot_attenuation, record_to_value
from Neuron_analysis_tool.record import multi_record_all #record, record_all,
from Neuron_analysis_tool.utils import seg_Rin_func #, get_segment_length_lamda, get_segment_length_um, LAMDA, MICRO, sec_name, seg_name
from Neuron_analysis_tool.loaders import open_swc, open_L5PC, open_ASC, open_rall_tree, get_parts_and_colors, insert_g_total #, open_morph
from Neuron_analysis_tool.Video import *
from Neuron_analysis_tool.more_conductances import more_conductances, more_conductances_fake, callback
from tqdm import tqdm
from neuron import h
import neuron
# import os

import sys
if hasattr(sys, 'getwindowsversion'):
    print('error in loading mechanism')
    # h.load_dll(os.path.dirname(os.path.realpath(__file__))+'/')
else:
    neuron.load_mechanisms(os.path.dirname(os.path.realpath(__file__)))

class Analyzer:
    """
    class that help to analyze neuron model from every location on the morphology and a givin protocol
    main components:
    distance - compute both the phisical distance in (um) and in electrical units (lamda(0)) from a givin segment
    records - record the activity of the neuron (record a givin values (such as voltage/conductances .etc)

    you can generate the folowing plots:
        morphology - in 2d
        dendogram from a givin starting point
        cable from a givin starting point
        attenuation of a recorded value along distance
        plot a record
        plot all the records

    you can also create a vidio from this protocol and see the resolting record as a color map on the plots
    (morphology, dendorgam, attenuation)
    note that the morphology, dendorgam and cable plots are dancing!!
    """
    def __init__(self, cell=None, parts_dict=None, colors_dict=None, type='input_cell',
                 morph_path = None, Rm=10000.0, Ra=100, Cm=1, e_pas=-70,
                 more_conductances_protocol =resting_protocol, seg_every=20):
        """
        you can initiate this tool with
            type='input_cell' -> with a givin cell model
            type == 'Rall_tree' -> will give you a Rall tree model
            type == 'ASC'/ type == 'swc' -> will load a morphology file from morph_path
            type == 'L5PC' -> will load Itay Hay L5PC model

        the init function can take a short time since its runing a resting membrane protocol in order to get the
        resting conductances of the model (you can change it by passing a diffrent more_conductances_protocol

        :param cell: a neuron cell model.
        :param parts_dict: split os the model into parts, this is a dictionary of {part_name: [seg list]}. default is soma, basal, apical, axon.
        :param colors_dict: a dictinary of colors to use for each part {part_name: color}. default is soma, basal, apical, axon.
        :param type: the type of open from: input_cell, Rall_tree, ASC, swc, L5PC
        :param morph_path: the path to the ASC/swc file used only if type == 'ASC'/ type == 'swc'
        :param Rm: the Rm value type == 'ASC'/ type == 'swc'
        :param Ra: the Ra value type == 'ASC'/ type == 'swc'
        :param Cm: the Cm value type == 'ASC'/ type == 'swc'
        :param e_pas: the e_pas value type == 'ASC'/ type == 'swc'
        :param more_conductances_protocol: protocol to run to get the conductances for the distance. default is 500 ms of nothing
        :param seg_every: how many segments there are in every um
        """
        if cell is None:
            if type == 'Rall_tree':
                cell, parts_dict, colors_dict = open_rall_tree(seg_every=seg_every)
            elif type == 'ASC' and morph_path:
                cell, parts_dict, colors_dict = open_ASC(morph_path, Rm=Rm, Ra=Ra,
                                                         Cm=Cm, e_pas=e_pas, seg_every=seg_every)
            elif type == 'swc' and morph_path:
                cell, parts_dict, colors_dict = open_swc(morph_path, Rm=Rm, Ra=Ra,
                                                         Cm=Cm, e_pas=e_pas, seg_every=seg_every)
            elif type == 'L5PC':
                cell, parts_dict, colors_dict = open_L5PC(seg_every=seg_every)

        if parts_dict is None:
            cell, parts_dict, colors_dict = get_parts_and_colors(cell)
        insert_g_total(cell)
        self.type=type
        self.cell=cell
        self.parts_dict = parts_dict
        #
        soma_segs = list(cell.soma[0])
        self.bscallback = h.beforestep_callback(soma_segs[len(soma_segs)//2])  # starting the ref of g_tatals (this asume you have less then 19 mechanisms and les then 39 synapses per segment!!!!!!!
        self.bscallback.set_callback((callback, self))
        self.more_conductances = more_conductances(cell, is_resting=True,
                                                   protocol=more_conductances_protocol)
        self.colors_dict = colors_dict
        self.colors = color_func(parts_dict=parts_dict, color_dict=colors_dict)

    def get_mechanism_names(self):
        """

        :return: the names of the mechanism in the model
        """
        mechanisms_names = set()
        for sec in self.cell.all:
            for seg in sec:
                for mechanisms in seg:
                    if not mechanisms.is_ion():
                        mechanisms_names.add(str(mechanisms))
        return list(mechanisms_names)

    def get_synapses(self):
        """

        :return: the names of the mechanism in the model
        """
        synapses = dict()
        for sec in self.cell.all:
            for seg in sec:
                for syn in seg.point_processes():
                    if sec_name(sec) not in synapses:
                        synapses[sec_name(sec)] = []
                    syn_type = syn.hname().split('[')[0]
                    if hasattr(syn, 'g'):
                            synapses[sec_name(sec)].append(dict(syn=syn, g_name='g'))
                    else:
                        synapses[sec_name(sec)].append(dict(syn=syn, g_name='g_'+syn_type))
        return synapses

    def change_color_dict(self, colors_dict):
        """
        change the colors in the plots
        :param colors_dict: a dictinary of colors to use for each part {part_name: color}.
        :return:
        """
        for part in self.parts_dict:
            assert part in colors_dict

        self.colors_dict = colors_dict
        self.colors = color_func(parts_dict=self.parts_dict, color_dict=colors_dict)

    def change_parts_dict(self, parts_dict, colors_dict):
        """
        change the naming parts and color in the model
        :param parts_dict: split os the model into parts, this is a dictionary of {part_name: [seg list]}.
        :param colors_dict:  a dictinary of colors to use for each part {part_name: color}.
        :return:
        """
        for part in parts_dict:
            assert part in colors_dict
        self.parts_dict = parts_dict
        self.colors_dict = colors_dict
        self.colors = color_func(parts_dict=self.parts_dict, color_dict=colors_dict)

    def plot_morph(self, ax=None, seg_to_indicate_dict = {}, diam_factor=None, diam_const=1, diam_min=0, sec_to_change=None,
                   ignore_sections=[], theta=0, scale=0, scale_text=True, ignore_soma=True, distance=None,
                   electrical=False, time=None, dt=1, more_conductances_=None, colors=None,
                   dt_func= lambda x: np.mean(x)):
        """
        plot the morphology
        :param ax: the ax to plot on
        :param seg_to_indicate_dict: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param sec_to_change:sec to change there location. the cange is only to the plot and not the model
        :param ignore_sections: section to not plot
        :param theta:the rotation to the plot in degrees [0-360]
        :param scale:if this is>0 it will add a scale bar with length scale
        :param scale_text: the text to put next to the scalebar
        :param ignore_soma: True in order to plot the soma as a circle and not a line
        :param distance: a distance for the segments
                        (if not givin its uses the default distance from the init more_conductances function)
        :param electrical: if True it will change the distance units from um to lamda
        :param time: time to plot from the more_conductances
        :param dt: the dt to use in the more_conductances
        :param more_conductances_: recorded more_conductances that are diffrent from the init
        :param colors: a givin color class that gives color to each segment via get_seg_color func
        :param dt_func: function for dt in the more_conductances
        :return:
        """
        if colors is None:
            colors = self.colors
        if self.type.startswith('Rall_tree'):
            ignore_soma=False
        if ax is None:
            ax = plt.gca()
        ax.set_aspect('equal', adjustable='box')
        fig = plt.gca().figure
        ax, lines, segs = plot_morph(self.cell, color_func=colors.get_seg_color, add_nums=False,
                                                     seg_to_indicate=seg_to_indicate_dict,
                                                     ax=ax, diam_factor=diam_factor, diam_const=diam_const,
                                                     diam_min=diam_min, sec_to_change=sec_to_change,
                                                     theta=theta, ignore_sections=ignore_sections,
                                                     ignore_soma=ignore_soma,
                                                     distance=distance,
                                                     more_conductances=self.more_conductances if more_conductances_ is None else more_conductances_,
                                                     electrical=electrical,
                                                     time=time, dt=dt, dt_func=dt_func
                                                     )
        ax.grid(False)
        ax.set_xticks([])
        ax.set_yticks([])
        ax.set_axis_off()
        if not scale == 0:
            x_lim = ax.get_xlim()
            y_lim = ax.get_ylim()
            ax.plot([x_lim[0], x_lim[0]], [y_lim[0], y_lim[0] + scale], color='k')
            if scale_text:
                text_scale = str(scale)+' '+MICRO+'m'
                y_pos = y_lim[0]+scale/3.0
                text_scale_size=10
                ax.annotate(text_scale,
                            xy=(x_lim[0], y_pos), xycoords='data', size=text_scale_size,
                            xytext=(-text_scale_size - 2, -text_scale_size / 2), textcoords='offset points', rotation=90)

        return ax, lines, segs

    def plot_morph_with_values(self, seg_val_dict, ax=None, seg_to_indicate_dict={}, diam_factor=None,
                               sec_to_change=None, ignore_sections=[], theta=0, scale=500, scale_text=True,
                               cmap=plt.cm.turbo,
                               plot_color_bar=True, bounds=None, ignore_soma=True,
                               color_bar_kwarts=dict(shrink=0.6),
                               colors=None, distance=None, electrical=False, time=None, dt=1,
                               more_conductances_=None, dt_func=lambda x: np.mean(x), bar_name=''):
        """
        plot the morph with colorcode
        :param seg_val_dict: dictionary of {section name: {segment name: numarical value}}
        :param ax: the ax to plot on
        :param seg_to_indicate_dict: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param sec_to_change:sec to change there location. the cange is only to the plot and not the model
        :param ignore_sections: section to not plot
        :param theta:the rotation to the plot in degrees [0-360]
        :param scale:if this is>0 it will add a scale bar with length scale
        :param scale_text: the text to put next to the scalebar
        :param ignore_soma: True in order to plot the soma as a circle and not a line
        :param distance: a distance for the segments (if not givin its uses the default distance from the init more_conductances function)
        :param electrical: if True it will change the distance units from um to lamda
        :param time: time to plot from the more_conductances
        :param dt: the dt to use in the more_conductances
        :param more_conductances_: recorded more_conductances that are diffrent from the init
        :param colors: a givin color class that gives color to each segment via get_seg_color func
        :param dt_func: function for dt in the more_conductances
        :param plot_color_bar: if to plot a color bar
        :param bounds: the bounds for the color map. default is the max and min value givin in seg_val_dict.
        :param cmap: cmap to use, a matplotlib cmap.
        :param color_bar_kwarts: kwarts for the color bar
        :param bar_name: text for the color-bar
        :return:
        """
        if self.type.startswith('Rall_tree'):
            ignore_soma=False
        if ax is None:
            ax = plt.gca()
        fig = plt.gca().figure
        ax.set_aspect('equal', adjustable='box')
        if colors is None:
            colors = color_func_norm(value_dict=seg_val_dict, bounds=bounds, cmap=cmap)

        ax, lines, segs =  self.plot_morph(ax=ax, seg_to_indicate_dict = seg_to_indicate_dict, diam_factor=diam_factor,
                                           sec_to_change=sec_to_change, ignore_sections=ignore_sections, theta=theta,
                                           scale=scale, scale_text=scale_text, ignore_soma=ignore_soma, distance=distance,
                                           electrical=electrical, time=time, dt=dt,
                                           more_conductances_=more_conductances_, colors=colors, dt_func=dt_func)

        if plot_color_bar:
            im = plt.cm.ScalarMappable(norm=colors.norm, cmap=colors.cmap)
            color_bar = plt.colorbar(im, ax=ax, **color_bar_kwarts )
            if bounds is not None:
                ticks = color_bar.get_ticks()
                ticks[0]=bounds[0]
                ticks[-1]=bounds[-1]
                color_bar.set_ticks(ticks)
            cax = color_bar.ax
            fig.add_axes(cax)
            cax.set_title(bar_name)
        else:
            color_bar = None
            cax = None
        return ax, cax, colors, lines, segs

    def plot_morph_with_value_func(self, func=seg_Rin_func, run_time=0, ax=None, seg_to_indicate_dict={},
                                   diam_factor=None,
                                   sec_to_change=None, ignore_sections=[], theta=0, scale=0, scale_text=True,
                                   cmap=plt.cm.turbo,
                                   plot_color_bar=True, bounds=None, ignore_soma=True,
                                   color_bar_kwarts=dict(shrink=0.6),
                                   colors=None, distance=None, electrical=False, time=None, dt=1,
                                   more_conductances_=None, dt_func=lambda x: np.mean(x), bar_name=''):
        """
        plot the morph with color code resulting of a function for each segment
        :param func: the function to run on each segment
        :param run_time: time to run befor the function
        :param seg_val_dict: dictionary of {section name: {segment name: numarical value}}
        :param ax: the ax to plot on
        :param seg_to_indicate_dict: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param sec_to_change:sec to change there location. the cange is only to the plot and not the model
        :param ignore_sections: section to not plot
        :param theta:the rotation to the plot in degrees [0-360]
        :param scale:if this is>0 it will add a scale bar with length scale
        :param scale_text: the text to put next to the scalebar
        :param ignore_soma: True in order to plot the soma as a circle and not a line
        :param distance: a distance for the segments (if not givin its uses the default distance from the init more_conductances function)
        :param electrical: if True it will change the distance units from um to lamda
        :param time: time to plot from the more_conductances
        :param dt: the dt to use in the more_conductances
        :param more_conductances_: recorded more_conductances that are diffrent from the init
        :param colors: a givin color class that gives color to each segment via get_seg_color func
        :param dt_func: function for dt in the more_conductances
        :param plot_color_bar: if to plot a color bar
        :param bounds: the bounds for the color map. default is the max and min value givin in seg_val_dict.
        :param cmap: cmap to use, a matplotlib cmap.
        :param color_bar_kwarts: kwarts for the color bar
        :param bar_name: text for the color-bar
        :return:
        """
        if colors is None:
            if run_time>0:
                h.tstop=run_time
                h.run()
            value_dict = dict()
            for part in self.parts_dict:
                for seg in tqdm(self.parts_dict[part], desc=part):
                    if sec_name(seg.sec) not in value_dict:
                        value_dict[sec_name(seg.sec)]=dict()
                    value_dict[sec_name(seg.sec)][seg_name(seg)] = func(seg)
            colors = color_func_norm(value_dict=value_dict, bounds=bounds, cmap=cmap)

        return self.plot_morph_with_values({}, ax=ax, seg_to_indicate_dict=seg_to_indicate_dict,
                                           diam_factor=diam_factor,
                                           sec_to_change=sec_to_change, ignore_sections=ignore_sections, theta=theta,
                                           scale=scale, scale_text=scale_text, cmap=cmap,
                                           plot_color_bar=plot_color_bar, bounds=bounds, ignore_soma=ignore_soma,
                                           color_bar_kwarts=color_bar_kwarts,
                                           colors=colors, distance=distance, electrical=electrical, time=time, dt=dt,
                                           more_conductances_=more_conductances_, dt_func=dt_func, bar_name=bar_name)

    def plot_dendogram(self, start_seg = None ,ax=None, segs_to_indecate = dict(), plot_legend=True, ignore_sections=[],
                       electrical=False, diam_factor=None, distance=None, colors=None,
                       BRANCH_SPACE_=None, dt_func= lambda x: np.mean(x)):
        """
        plot dendorgam of the tree from a givin start point
        :param start_seg: the seg to start the dendogram from
        :param ax: the axes to plot on
        :param segs_to_indecate: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param plot_legend: if True it will dd a legend with the parts_dict keys and colors
        :param ignore_sections: section not to plot
        :param electrical: if True it will plot the dendogram in electrical units
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param distance: a distance for the segments (if not givin its uses the default distance from the init more_conductances function)
        :param colors: a givin color class that gives color to each segment via get_seg_color func
        :param BRANCH_SPACE_: spacing between the terminal branches (makes the dendogram more/less sparse)
        :param dt_func: function for dt in the more_conductances
        :return:
        """
        if colors is None:
            colors = self.colors
        if start_seg is None:
            start_sec = list(self.cell.soma[0])
            start_seg = start_sec[len(start_sec)//2]
        max_y, x_pos, lines, segs = plot_dendogram(self.cell, start_seg, self.more_conductances, colors, ax=ax,
                                                   plot_legend=plot_legend, ignore_sections=ignore_sections,
                                                   segs_to_indecate=segs_to_indecate, electrical=electrical,
                                                   diam_factor=diam_factor, distance=distance,
                                                   BRANCH_SPACE_=BRANCH_SPACE_, dt_func=dt_func)
        return ax, x_pos, lines, segs

    def plot_dendogram_with_values(self, seg_val_dict, start_seg = None ,ax=None, segs_to_indecate = dict(),
                                   ignore_sections=[], electrical=False, diam_factor=None, distance=None, bounds=None,
                                   cmap=plt.cm.turbo, plot_color_bar=True, color_bar_kwarts=dict(shrink=0.6),
                                   colors=None, BRANCH_SPACE_=None, dt_func= lambda x: np.mean(x), bar_name=''):
        """
        plot the dendorgam with colorcode
        :param seg_val_dict:
        :param start_seg: the seg to start the dendogram from
        :param ax: the axes to plot on
        :param segs_to_indecate: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param ignore_sections: section not to plot
        :param electrical: if True it will plot the dendogram in electrical units
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param distance: a distance for the segments (if not givin its uses the default distance from the init more_conductances function)
        :param colors: a givin color class that gives color to each segment via get_seg_color func
        :param BRANCH_SPACE_: spacing between the terminal branches (makes the dendogram more/less sparse)
        :param dt_func: function for dt in the more_conductances
        :param bounds: the bounds for the color map. default is the max and min value givin in seg_val_dict.
        :param cmap: cmap to use, a matplotlib cmap.
        :param plot_color_bar: if to plot a color bar
        :param color_bar_kwarts: kwarts for the color bar
        :return:
        """
        if start_seg is None:
            start_sec = list(self.cell.soma[0])
            start_seg = start_sec[len(start_sec)//2]
        if ax is None:
            ax = plt.gca()
        if colors is None:
            colors = color_func_norm(value_dict=seg_val_dict, bounds=bounds, cmap=cmap)

        max_y, x_pos, lines, segs = self.plot_dendogram(start_seg = start_seg ,ax=ax, plot_legend=False,
                                                        segs_to_indecate = segs_to_indecate,
                                                        ignore_sections=ignore_sections, electrical=electrical,
                                                        diam_factor=diam_factor, distance=distance, colors=colors,
                                                        BRANCH_SPACE_=BRANCH_SPACE_, dt_func=dt_func)
        if plot_color_bar:
            fig = ax.figure
            im = plt.cm.ScalarMappable(norm=colors.norm, cmap=colors.cmap)
            color_bar = plt.colorbar(im, ax=ax, **color_bar_kwarts)
            if bounds is not None:
                ticks = color_bar.get_ticks()
                ticks[0]=bounds[0]
                ticks[-1]=bounds[-1]
                color_bar.set_ticks(ticks)
            cax = color_bar.ax
            fig.add_axes(cax)
            cax.set_title(bar_name)
        else:
            color_bar = None
            cax = None

        return ax, x_pos, cax, colors, lines, segs

    def plot_dendogram_with_value_func(self, func, start_seg=None, ax=None, segs_to_indecate=dict(),
                                       ignore_sections=[], electrical=False, diam_factor=None, distance=None,
                                       bounds=None,
                                       cmap=plt.cm.turbo, plot_color_bar=True, color_bar_kwarts=dict(shrink=0.6),
                                       colors=None,
                                       BRANCH_SPACE_=None, dt_func=lambda x: np.mean(x)):
        """
        plot the morph with color code resulting of a function for each segment
        :param func: the function to run on each segment
        :param start_seg: the seg to start the dendogram from
        :param ax: the axes to plot on
        :param segs_to_indecate: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param ignore_sections: section not to plot
        :param electrical: if True it will plot the dendogram in electrical units
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param distance: a distance for the segments (if not givin its uses the default distance from the init more_conductances function)
        :param colors: a givin color class that gives color to each segment via get_seg_color func
        :param BRANCH_SPACE_: spacing between the terminal branches (makes the dendogram more/less sparse)
        :param dt_func: function for dt in the more_conductances
        :param bounds: the bounds for the color map. default is the max and min value givin in seg_val_dict.
        :param cmap: cmap to use, a matplotlib cmap.
        :param plot_color_bar: if to plot a color bar
        :param color_bar_kwarts: kwarts for the color bar
        :return:
        """
        if colors is None:
            value_dict = dict()
            for part in self.parts_dict:
                for seg in tqdm(self.parts_dict[part], desc=part):
                    if seg.sec not in value_dict:
                        value_dict[sec_name(seg.sec)]=dict()
                    value_dict[sec_name(seg.sec)][seg_name(seg)] = func(seg)
            colors = color_func_norm(value_dict=value_dict, bounds=bounds, cmap=cmap)
        return self.plot_dendogram_with_values({}, start_seg=start_seg, ax=ax, segs_to_indecate=segs_to_indecate,
                                               ignore_sections=ignore_sections, electrical=electrical,
                                               diam_factor=diam_factor, distance=distance, bounds=bounds, cmap=cmap,
                                               plot_color_bar=plot_color_bar, color_bar_kwarts=color_bar_kwarts,
                                               colors=colors, BRANCH_SPACE_=BRANCH_SPACE_, dt_func=dt_func)

    def get_cable(self, start_seg=None, type='d3_2', factor_e_space=100, factor_m_space=10,
                  ignore_sections = [], distance=None, dt_func=lambda x: np.mean(x)):
        """
        calculate the cables.
        for each direction in 'sons', 'parent' and for each part from part dict and all
        :param cell: the cell neuron model
        :param start_seg: the seg to start the cable from
        :param factor_e_space: the bin size in electrical units, every 1/factor_e_space lamda is a bin
        :param factor_m_space: the bin size in physical units, every factor_m_space um is a bin
        :param more_conductances: more_conductances to initiate the distance if distance is None
        :param seg_dist_dict: dict in the form {direction: {seg: []}} (segs to get there electric distane
        :param part_dict: the part dict as {part name: list of segments}
        :param ignore_sections: section to skip in the cable
        :param distance: a distance for the segments
                        (if not givin its uses the default distance from the init more_conductances function)
        :param dt_func: function for dt in the more_conductances
        :return:
        """
        if start_seg is None:
            start_sec = list(self.cell.soma[0])
            start_seg = start_sec[len(start_sec)//2]
        cable = get_cable(self.cell,
                         start_seg=start_seg,
                         factor_e_space=factor_e_space,
                         factor_m_space=factor_m_space,
                         more_conductances=self.more_conductances,
                         seg_dist_dict={},
                         part_dict=self.parts_dict,
                         ignore_sections = ignore_sections,
                         distance=distance,
                         dt_func=dt_func)[0]
        return cable['parent']['all'][type], cable['sons']['all'][type]

    def plot_cable(self, start_seg = None ,ax=None, factor_e_space=25, factor_m_space=10, segs_to_indecate= dict(),
                   ignore_sections=[], cable_type='d3_2', start_loc=0, shift=None, vertical=True, dots_size=10,
                   start_color='k', plot_legend=True, distance=None, cable_factor=1, labal_start=None,
                   return_shift=False, dt_func= lambda x: np.mean(x)):
        """
        plot a cable from a givin start point
        :param start_seg: the seg to start the cable from
        :param ax: the axes to plot on
        :param factor_e_space: the bin size in electrical units, every 1/factor_e_space lamda is a bin
        :param factor_m_space: the bin size in physical units, every factor_m_space um is a bin
        :param segs_to_indecate: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param ignore_sections: section to skip in the cable
        :param cable_type: type of the cable from: d3_2/electric/dist, d3_2 is the equivelent cable, electricis dA/dX and dist is dA/dx
        :param start_loc: the start location on the axes
        :param shift: a constant shift or a chosen from the data
        :param vertical: if to plot the cable vertically or horizontally
        :param dots_size: the size of the dot at the origin
        :param start_color: the color of the dot in the origin
        :param plot_legend: if True it will dd a legend with the parts_dict keys and colors
        :param distance: a distance for the segments (if not givin its uses the default distance from the init more_conductances function)
        :param cable_factor: factor to add to the cable to match the axes
        :param labal_start: the label for the legend of the start seg
        :param return_shift: if return the result shift
        :param dt_func: function for dt in the more_conductances
        :return:
        """
        if ax is None:
            ax = plt.gca()
        if start_seg is None:
            start_sec_list = list(self.cell.soma[0])
            start_seg = start_sec_list[len(start_sec_list)//2]

        return plot_cable(self.cell, start_seg=start_seg, ax=ax, cable_type=cable_type, factor_e_space=factor_e_space,
                          factor_m_space=factor_m_space, more_conductances=self.more_conductances,
                          part_dict=self.parts_dict, colors_dict=self.colors_dict, ignore_sections=ignore_sections,
                          distance=distance, segs_to_indecate=segs_to_indecate, start_loc=start_loc, vertical=vertical,
                          dots_size=dots_size, start_color=start_color, shift=shift, plot_legend=plot_legend,
                          cable_factor=cable_factor, labal_start=labal_start, return_shift=return_shift, dt_func=dt_func)

    def plot_attenuation(self, protocol=long_pulse_protocol, ax=None, seg_to_indicate_dict=dict(), start_seg =None,
                         record_to_value_func=record_to_value, norm=True, record_name='v', norm_by=None,
                         electrical=True, distance=None, records=None, ignore_sections=[],
                         dt_func= lambda x: np.mean(x), direction_dist_factors=dict(sons=1, parent=1), **kwargs):
        """
        plot the record along the distance
        :param protocol: protocol to run (if records are not givin)
        :param ax: the axes to plot on
        :param seg_to_indicate_dict: dictinary {seg: dict(size=100, color='b', alpha=0.75)}
        :param start_seg: the seg to start the plot from
        :param record_to_value_func: function to do after protocol is done
        :param norm: if to norm the records
        :param record_name: the name of the record
        :param norm_by: value for norm, if not givin its will be the value of the record at start_seg
        :param electrical: if True it will plot the dendogram in electrical units
        :param distance: a distance for the segments (if not givin its uses the default distance from the init more_conductances function)
        :param records: the records from a pre-calc protocol
        :param ignore_sections: section not to plot
        :param dt_func: function for dt in the more_conductances
        :param kwargs: extra kwargs for the each line in matplotlib such lw, ls etc.
        :return:
        """
        ax, norm_by, lines, segs, records = plot_attenuation(cell=self.cell, start_seg=start_seg, protocol=protocol,
                                                             more_conductances=self.more_conductances,
                                                             color_func=self.colors, ax=ax, record_name=record_name,
                                                             cut_start_ms=None,  record_to_value=record_to_value_func,
                                                             norm_by=norm_by,  norm=norm, electrical=electrical,
                                                             seg_to_indicate=seg_to_indicate_dict, distance=distance,
                                                             records=records, ignore_sections=ignore_sections,
                                                             dt_func=dt_func, direction_dist_factors=direction_dist_factors,
                                                             **kwargs)

        ax.set_yscale('log')
        ax.set_xlabel('distance from origin (x / '+LAMDA+')')
        if norm or (not norm_by==1.0):
            ax.set_ylabel('V(x)/V(0)')
        else:
            ax.set_ylabel('attanuation (mV)')

        return ax, norm_by, lines, segs, records

    def create_card(self, start_seg=None, theta=0, scale=500, factor_e_space=50, cable_type='d3_2', diam_factor=None,
                    plot_legend=False, start_color='green', start_dots_size=50, cable_factor=1, dt_func= lambda x: np.mean(x),
                    protocol1=long_pulse_protocol, protocol2=short_pulse_protocol, **kwargs):
        """
        create a viewing card from a segment prospective
        the card incluses:
            morphology with the segment indication
            dendogram from the segment prespective
            attenuation in response to 2 protocols
        :param start_seg: the seg to start the plot from
        :param theta: the rotation to the plot in degrees [0-360]
        :param scale: if this is>0 it will add a scale bar with length scale
        :param factor_e_space: the bin size in electrical units, every 1/factor_e_space lamda is a bin
        :param cable_type: type of the cable from: d3_2/electric/dist, d3_2 is the equivelent cable, electricis dA/dX and dist is dA/dx
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param plot_legend: if True it will dd a legend with the parts_dict keys and colors
        :param start_dots_size: the size of the dot at the origin
        :param start_color: the color of the dot in the origin
        :param cable_factor: factor for the cable width
        :param dt_func: function for dt in the more_conductances
        :param protocol1: the first protocol for attenuation plot. default=long_pulse_protocol (1 sec current ingection of 0.1 pA)
        :param protocol2: the second protocol for attenuation plot. default=short_pulse_protocol (2 ms current ingection of 0.1 pA)
        :param kwargs: extra kwargs for attenuation (for the each line in matplotlib such lw, ls etc.)
        :return:
        """
        fig, ax = plt.subplots(1, 4, figsize=(12, 3), gridspec_kw={'width_ratios': [0.5, 1.5, 1, 1]})
        plt.subplots_adjust(wspace=0.5)
        if start_seg is None:
            start_seg = self.cell.soma[0]
            start_seg = list(start_seg)
            start_seg = start_seg[len(start_seg)//2]
        seg_to_indicate_dict = {start_seg: dict(size=start_dots_size, color=start_color, alpha=1)}
        distance = Distance(self.cell, self.more_conductances, dt_func=dt_func)
        distance.compute(start_seg=start_seg)

        _,_,_ = self.plot_morph(ax=ax[0], theta=theta, seg_to_indicate_dict=seg_to_indicate_dict, scale=scale,
                                diam_factor=diam_factor, ignore_soma=not self.type.startswith('Rall_tree'),
                                distance=distance)
        _, x_pos, _, _ = self.plot_dendogram(start_seg=start_seg, ax=ax[1], electrical=True, plot_legend=False,
                                             segs_to_indecate=seg_to_indicate_dict, distance=distance)
        _, shift = self.plot_cable(start_seg=start_seg, ax=ax[1], factor_e_space=factor_e_space, cable_type=cable_type,
                        plot_legend=plot_legend, start_loc=x_pos+15, start_color=start_color, dots_size=start_dots_size,
                        distance=distance, cable_factor=cable_factor, return_shift=True)
        for a in ax[1:]:
            a.spines['top'].set_visible(False)
            a.spines['right'].set_visible(False)
        ax[1].set_axis_off()
        x_lim = ax[1].get_xlim()
        y_lim = ax[1].get_ylim()

        ax[1].plot([shift-5*cable_factor, shift+5*cable_factor], [y_lim[0], y_lim[0]], color='k')
        text_size = 10
        if cable_type=='d3_2':
            x_scale_text = '10 '+MICRO+'m'
            ax[1].annotate(x_scale_text,
                        xy=(shift, y_lim[0]), xycoords='data', size=text_size,
                        xytext=(-text_size, -text_size - 2), textcoords='offset points')
        else:
            x_scale_text = '10 '+MICRO+'$m^{2}$'
            ax[1].annotate(x_scale_text,
                        xy=(shift, y_lim[0]), xycoords='data', size=text_size,
                        xytext=(-text_size, -text_size - 2), textcoords='offset points')

        ax[1].plot([x_lim[0], x_lim[0]], [y_lim[0], y_lim[0] + 0.5], color='k')
        y_scale_text = '0.5 '+LAMDA+''
        y_pos = y_lim[0] + 0.5 / 3.0
        ax[1].annotate(y_scale_text,
                    xy=(x_lim[0], y_pos), xycoords='data', size=text_size,
                    xytext=(-text_size - 2, -text_size / 2), textcoords='offset points', rotation=90)
        ax[1].set_ylabel('')

        self.plot_attenuation(start_seg=start_seg, ax=ax[2], protocol=protocol1,
                              seg_to_indicate_dict=seg_to_indicate_dict, distance=distance, ** kwargs)
        self.plot_attenuation(start_seg=start_seg, ax=ax[3], protocol=protocol2,
                              seg_to_indicate_dict=seg_to_indicate_dict, distance=distance, ** kwargs)
        ylim = ax[2].get_ylim()
        ax[3].set_xlim(ax[2].get_xlim())
        ax[3].set_ylim(ylim)
        y_ticks = np.array([10**-i for i in range(5)])
        y_ticks = y_ticks[np.logical_and(y_ticks > ylim[0], y_ticks < ylim[-1])]
        if len(y_ticks)==1:
            y_ticks = np.array([10**0, 10**-1])
        ax[2].set_yticks(y_ticks)
        # ax[2].set_yticklabels(y_ticks)
        ax[3].set_ylabel('')
        ax[3].set_yticks(y_ticks)
        ax[3].set_yticklabels(['' for i in range(len(y_ticks))])
        ax[0].set_title('morphology')
        ax[1].set_title(cable_type +' dendogram')
        ax[2].set_title('long pulse attanuation')
        ax[3].set_title('short pulse attanuation')
        return fig, ax

    def create_morpho_card(self, start_seg=None, theta=0, scale=500, factor_e_space=50, cable_type='d3_2',
                           diam_factor=None, plot_legend=False, start_color='green', start_dots_size=50,
                           dt_func= lambda x: np.mean(x)):
        """
        create a morphology viewing card from a segment prospective
        the card incluses:
            morphology with the segment indication
            dendogram from the segment prespective
        :param start_seg: the seg to start the plot from
        :param theta: the rotation to the plot in degrees [0-360]
        :param scale: if this is>0 it will add a scale bar with length scale
        :param factor_e_space: the bin size in electrical units, every 1/factor_e_space lamda is a bin
        :param cable_type: type of the cable from: d3_2/electric/dist, d3_2 is the equivelent cable, electricis dA/dX and dist is dA/dx
        :param diam_factor: factor to change the ploting diam so the line width will be seg.diam*diam_factor. default=1
        :param plot_legend: if True it will dd a legend with the parts_dict keys and colors
        :param start_dots_size: the size of the dot at the origin
        :param start_color: the color of the dot in the origin
        :param dt_func: function for dt in the more_conductances
        :return:
        """
        fig, ax = plt.subplots(1, 3, figsize=(12, 3), gridspec_kw={'width_ratios': [0.5, 1.5, 1]})
        plt.subplots_adjust(wspace=0.5)
        if start_seg is None:
            start_seg = self.cell.soma[0]
            start_seg = list(start_seg)
            start_seg = start_seg[len(start_seg)//2]
        seg_to_indicate_dict = {start_seg: dict(size=start_dots_size, color=start_color, alpha=1)}
        distance = Distance(self.cell, self.more_conductances, dt_func=dt_func)
        distance.compute(start_seg=start_seg)

        self.plot_morph(ax=ax[0], theta=theta, seg_to_indicate_dict=seg_to_indicate_dict, scale=scale,
                        diam_factor=diam_factor, ignore_soma=not self.type.startswith('Rall_tree'), distance=distance)
        _, x_pos, _, _ = self.plot_dendogram(start_seg=start_seg, ax=ax[1], electrical=True, plot_legend=False,
                                             segs_to_indecate=seg_to_indicate_dict, distance=distance)
        self.plot_cable(start_seg=start_seg, ax=ax[2], factor_e_space=factor_e_space, cable_type=cable_type,
                        plot_legend=plot_legend, start_loc=0, start_color=start_color, dots_size=start_dots_size,
                        distance=distance)
        for a in ax[1:]:
            a.spines['top'].set_visible(False)
            a.spines['right'].set_visible(False)
        ax[1].set_axis_off()
        x_lim = ax[1].get_xlim()
        y_lim = ax[1].get_ylim()

        text_size=10
        ax[1].plot([x_lim[0], x_lim[0]], [y_lim[0], y_lim[0] + 0.5], color='k')
        y_scale_text = '0.5 '+LAMDA+''
        y_pos = y_lim[0] + 0.5 / 3.0
        ax[1].annotate(y_scale_text,
                    xy=(x_lim[0], y_pos), xycoords='data', size=text_size,
                    xytext=(-text_size - 2, -text_size / 2), textcoords='offset points', rotation=90)
        ax[1].set_ylabel('')

        ax[2].set_ylabel('distance ('+LAMDA+')')
        ax[1].set_ylabel('')

        ax[0].set_title('morphology')
        ax[1].set_title('dendogram')
        ax[2].set_title('$d^{3/2}$ equivalent cable' if cable_type=='d3_2' else cable_type)
        return fig, ax

    def record_protocol(self, protocol=spike_protocol, cut_start_ms=None, record_names=['v'], start_seg=None,
                        compute_more_condunctances = False):
        """
        run and record a protocol
        :param protocol: protocol to run
        :param cut_start_ms: the start time to cut from the starting of the protocol (in ms)
        :param record_names: the names of variable to record
        :param start_seg: the start_seg to pass for the rotocol
        :param compute_more_condunctances: if to record all the conductances in the neuron model whie runing
        the protocol(needed for dancing video plots)
        :return: the multi records, and extra stuff (more_conductances/draw_funcs etc.)
        """
        if start_seg is None:
            start_seg = self.cell.soma[0](0.5)
        records = multi_record_all(self.cell, record_names=record_names)
        if compute_more_condunctances:
            more_conductances_ = more_conductances(self.cell, is_resting=False, protocol=None)
        delay, extra = protocol(self.cell, start_seg)
        if cut_start_ms is None:
            cut_start_ms = max(delay, 0)
        extraction_func = lambda x: np.array(x)[int(cut_start_ms / h.dt):]
        records.extract(extraction_func )
        if compute_more_condunctances:
            more_conductances_.extract(extraction_func)
            extra['more_conductances']=more_conductances_
        if 'draw_funcs' not in extra:
            extra['draw_funcs'] = []
        return records, extra#['draw_funcs'] if 'draw_funcs' in extra else []

    def save_movie_from_rec(self, fig,slow_down_factor=1, plot_kwargs=[], func_before_run=[lambda: plt.tight_layout()],
                            func_during_run=[], save_to='', clip_name='clip', fps=None, threads=16, preset='medium', auto_del=False): # ultrafast
        """
        create and save a video
        :param fig: the figure of the movie
        :param slow_down_factor: slowdown factor in the movie
        :param plot_kwargs: list of dictionarys each is used to create a single axes in the movie (see Video script)
        :param func_before_run: function to run after axes are initialized before the video creation start
        :param func_during_run: function to run on every dt in the video
        :param save_to: the folder to save the video to
        :param clip_name: the clip name for saving
        :param fps: the fsp of the video, note that fps*slow_down_factor is the number of frames that will be generated
        :param threads: number of threds to use in saving
        :param preset: the preset to use in frame generation
        :return:
        """
        if not clip_name.endswith('.mp4'):
            clip_name += '.mp4'
        if os.path.isfile(os.path.join(save_to, clip_name)):
            if auto_del:
                os.remove(os.path.join(save_to, clip_name))
            else:
                to_del=input('the file name is taken, delete the file? (Y, N)')
                if to_del=='Y':
                    os.remove(os.path.join(save_to, clip_name))
                else:
                    print('exit without running')
                    return
        save_movie_from_rec(analyzer=self, fig=fig, slow_down_factor=slow_down_factor, plot_kwargs=plot_kwargs,
                            func_before_run=func_before_run, func_during_run=func_during_run, save_to=save_to,
                            clip_name=clip_name, fps=fps, threads=threads, preset=preset)

    def create_movie_from_rec(self, fig, slow_down_factor=1, plot_kwargs=[],
                              func_before_run=[lambda: plt.tight_layout()], func_during_run=[]):
        """

        :param fig: the figure of the movie
        :param slow_down_factor: slowdown factor in the movie
        :param plot_kwargs: list of dictionarys each is used to create a single axes in the movie (see Video script)
        :param func_before_run: function to run after axes are initialized before the video creation start
        :param func_during_run: function to run on every dt in the video
        :return:
        """
        return create_movie_from_rec(analyzer=self, fig=fig, slow_down_factor=slow_down_factor, plot_kwargs=plot_kwargs,
                                     func_before_run=func_before_run, func_during_run=func_during_run)


import sys
sys.stdin = open("input.txt", "r")

for tc in range(1, 11):
    N, s = map(int, input().split()) #입력받는 길이, 시작점
    num = list(map(int, input().split()))
    V = max(num) #노드 개수

    # 간선정보
    arr = [[] for _ in range(V+1)]
    for i in range(N):
        if i%2:
            arr[num[i-1]].append(num[i])

    visited = [0] * (V + 1)
    q = [s]
    visited[s] = 1
    path = []
    while q:
        t = q.pop(0)
        path.append(t)
        for v in arr[t]:
            if visited[v] == 0: #거쳐지지 않았다면
                q.append(v)
                visited[v] = visited[t] + 1

    #마지막에 연락이 됨
    final = 0
    for p in path:
        if visited[p] == max(visited) and final < p:
            final = p
    print(f'#{tc}', final)


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

# Learn more: https://github.com/kennethreitz/setup.py

from setuptools import setup, find_packages

with open('README.rst') as f:
    readme = f.read()

with open('LICENSE') as f:
    license = f.read()

setup(
        name='project_name',
        version='0.1.0',
        description='give short descritpion of package here',
        long_description=readme,
        author='Jonathan A. Webb',
        author_email='webbja123@gmail.com',
        license=license,
        packages=find_packages()
)


import asyncio
import kcet
import os
import main
import updates
from eprint import eprint

async def handle_client(reader, writer):
    response = "HTTP/1.1 200 OK\n\nJEEhadistBOT running"
    request = (await reader.read(255)).decode("utf8")
    if len(request.strip()) != 0 and "User-Agent: Render/1.0" not in request:
        if not request.startswith("GET / HTTP/1.1") and "Cf-Ray" not in request:
            eprint(request)
    if request.startswith("GET /sendJeeReminder?auth=@droppers95tards1"):
        eprint("sending jee reminder 1")
        await main.send_jee_reminder(main.remind_jee_users[0])
    elif request.startswith("GET /sendJeeReminder?auth=@droppers95tards2"):
        eprint("sending jee reminder 2")
        await main.send_jee_reminder(main.remind_jee_users[1])

    elif request.startswith("GET /sendDailyDpp"):
        await main.send_dpp()

    elif request.startswith("GET /getNewsUpdates"):
        eprint("polling for news updates")
        # kcet_message = ""
        # kcet_updates = await kcet.get_kcet_updates()
        # eprint("kcet ", kcet_updates)
        # if len(kcet_updates) > 0:
        #    kcet_message += "KCET Updates📢\n"
        #    for link in kcet_updates:
        #        kcet_message += "▪ " + str(link) + "\n"
        # if len(kcet_message) > 0:
        #    await main.bot.send_message(
        #        chat_id=main.ADMIN_ID, text=kcet_message, parse_mode="html"
        #    )

        message = ""
        # csab = await updates.get_csab_updates()
        # eprint("csab ", csab)
        # if len(csab) > 0:
        #    message += "CSAB Updates📢\n"
        #    for link in csab:
        #        message += "▪ " + str(link) + "\n"
        #    message += "\n"

        josaa = await updates.get_josaa_updates()
        eprint("josaa ", josaa)
        if len(josaa) > 0:
            message += "JoSAA Updates📢\n"
            for link in josaa:
                message += "▪ " + str(link) + "\n"
            message += "\n"

        jeemain = await updates.get_jeemain_updates()
        eprint("jeemain ", jeemain)
        if len(jeemain) > 0:
            message += "JEE Main Updates📢\n"
            for link in jeemain:
                message += "▪ " + str(link) + "\n"
            message += "\n"

        if len(message) > 0:
            await main.bot.send_chat_action(
                action="typing", chat_id=main.CHAT_ID, message_thread_id=main.NTC_ID
            )
            eprint("sending update")
            await main.bot.send_message(
                chat_id=main.CHAT_ID,
                message_thread_id=main.NTC_ID,
                text=message,
                parse_mode="html",
            )
    writer.write(response.encode("utf8"))
    await writer.drain()
    writer.close()

async def run():
    server = await asyncio.start_server(
        handle_client, "0.0.0.0", int(os.environ["PORT"])
    )
    async with server:
        await server.serve_forever()

if __name__ == "__main__":
    asyncio.run(run())


# DFS: Depth First Search

class Graph:
	'''
	Basic Graph Class
	'''

	def __init__(self, num_vertex):
		self.graph = [[False for j in range(num_vertex)]
							 for i in range(num_vertex)]
		self.num_vertex = num_vertex

	def addEdge(self, u, v, mode='undirected'):
		self.graph[u][v] = True

		if mode=='undirected':
			self.graph[v][u] = True

	def dfs_util(self, v, visited):
		# Set the current node as visited
		visited[v] = True

		# Print the current vertex
		print(v, end = ' ')

		# Recur for all its adjacent neighbours
		for neighbour_idx in range(self.num_vertex):
			# If a neighbour is not visited then visit it.
			if self.graph[v][neighbour_idx] and not visited[neighbour_idx]:
				self.dfs_util(neighbour_idx, visited)

	def dfs(self, v):
		visited = [False for _ in range(self.num_vertex)]

		self.dfs_util(v, visited)
		print()

g = Graph(4)
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)
g.dfs(2)

# Output: 2, 0, 1, 3


import requests, pem
#TO-DO:
#
#https request with verify function!
#
#
#
ROOT_PATH = 'http://127.0.0.1:8000/users/'

#download key from WKD

def get_key(user):
    try:
        path = ROOT_PATH + user
        response = requests.get(path)
        # response = requests.get(path, cert=(cert_filename,key_filename))
        response.raise_for_status()
    except requests.exceptions.HTTPError as http_err:
        raise ValueError(f'HTTP error occurred: {http_err}')  # Python 3.6
    except Exception as err:
        raise ValueError(f'Other error occurred: {err}')  # Python 3.6
    else:
        jtext = response.json()
        return jtext['encrypt_key']


from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from BusinessLayer.BusinessLayerMatch import BusinessLayerMatch
from BusinessLayer.BusinessLayerTeam import BusinessLayerTeam
from UserLayer.form import Form

blm = BusinessLayerMatch()
blt = BusinessLayerTeam()

class FormMatch(Form):
    def __init__(self, tournament, range_matches):

        self.window = Tk()

        self.tree = ttk.Treeview(self.window)

        self.tree["columns"] = ("team_1", "score_team_1", "score_team_2", "team_2")
        self.tree.heading('#0', text="Id")
        self.tree.column("team_1", width=150)
        self.tree.heading("team_1", text="Equipo 1")
        self.tree.column("score_team_1", width=150)
        self.tree.heading("score_team_1", text="Puntaje Equipo 1")
        self.tree.column("score_team_2", width=150)
        self.tree.heading("score_team_2", text="Puntaje Equipo 2")
        self.tree.column("team_2", width=150)
        self.tree.heading("team_2", text="Equipo 2")

        self.tree.grid(row=0, column=0)

        self.frame = Frame(self.window, width=150, height=100)
        self.frame.grid(row=1, column=0)

        self.button_save = Button(self.frame, text='Resultado', command=self.result)
        self.button_save.pack(side="left")

        self.tournament = tournament

        self.range_matches = range_matches

        self.updateView()

        self.window.mainloop()

    def result(self):
        def abort():
            form.destroy()

        def save():
            noti = Toplevel()
            noti.title('Resultado')
            try:
                blm.result(match, score_team_1.get(), score_team_2.get())
                print(match.id_phase)
                if match.id_phase == 1:
                    if score_team_1.get() > score_team_2.get():
                        team_w = team_1.team_name
                    else:
                        team_w = team_2.team_name
                    print(team_w)
                    messagebox.showinfo("Resultado", "El ganador es " + team_w, parent=self.window)
                self.updateView()
                noti.destroy()
                form.destroy()

            except Exception as e:
                Label(master=noti, text=e).grid(row=0, column=0)

        form = Toplevel()
        form.title('top level 1')

        selectedMatch = super(FormMatch, self).get_select(self.tree)
        match = blm.get_match(selectedMatch)

        team_1 = blt.get_team(match.team_1)
        team_2 = blt.get_team(match.team_2)

        label_score_1 = Label(form, text="Puntaje equipo " + team_1.team_name)
        label_score_1.grid(row=0, column=0)

        label_score_2 = Label(form, text="Puntaje equipo " + team_2.team_name)
        label_score_2.grid(row=1, column=0)

        frame = Frame(form, width=125, height=50)
        frame.grid(row=4, column=1, columnspan=1)

        accept = Button(frame, text="Guardar", command=save)
        accept.pack(side="left")
        abort = Button(frame, text="Cancelar", command=abort)
        abort.pack(side="left")

        # Variables

        score_team_1 = IntVar(value=0)
        score_team_2 = IntVar(value=0)

        e_score_1 = Entry(form, textvariable=score_team_1)
        e_score_1.grid(row=0, column=1)
        e_score_2 = Entry(form, textvariable=score_team_2)
        e_score_2.grid(row=1, column=1)

    def updateView(self):
        self.tree.delete(*self.tree.get_children())
        for i in blm.get_matches(self.tournament, self.range_matches):
            team_1 = blt.get_team(i.team_1)
            team_2 = blt.get_team(i.team_2)
            self.tree.insert("", 'end', text=i.id, values=(team_1.team_name, i.score_team_1, i.score_team_2, team_2.team_name))


# Convert string to zigzag and compute new string
# Input: "PAYPALISHIRING", 4 =>
# P     I    N
# A   L S  I G
# Y A   H R
# P     I
# Output: "PINALSIGYAHRPI"
def convert(s, numRows):
    if (numRows == 1): return s
    r = (numRows * 2) - 2
    res = ""
    for x in range(numRows):
        tmp1 = 2*x
        tmp2 = r - tmp1
        nxt = x
        for y in range(len(s)):
            if (x == 0 or x == numRows - 1):
                nxt = x + y*r
            elif (y != 0):
                if (y % 2 == 0): nxt += tmp1
                else: nxt += tmp2
            if (nxt > len(s) - 1): break
            if (nxt < 0): continue
            res += s[nxt]
    return res

z1 = convert("PAYPALISHIRING", 3)
print(z1)
z2 = convert("PAYPALISHIRING", 4)
print(z2)
z3 = convert("A", 1)
print(z3)


# from Bradfeld Practical Algorithms and Data Structures
# chapter on Trees
# imports go here

class Node:

    def __init__(self, value):
        self.value = value
        self.left = None
        self.right = None

    def insert_left(self, value):
        if self.left is None:
            self.left = Node(value)
        else:
            sub_tree = self.left
            self.left = Node(value)
            self.left.left = sub_tree

    def insert_right(self, value):
        if self.right is None:
            self.right = Node(value)
        else:
            sub_tree = self.right
            self.right = Node(value)
            self.right.right = sub_tree

class ListTree:

    def __init__(self, value):
        # value, left child, right child
        self.tree_list = [
            value,
            [],
            []
        ]

    @staticmethod
    def insert_left(root, value):
        if len(root[1]) == 0:
            root[1] = [
                value,
                [],
                []
            ]
        else:
            sub_tree = root.pop(1)
            root.insert(1, [
                value,
                sub_tree,
                []
            ])

    @staticmethod
    def insert_right(root, value):
        if len(root[2]) == 0:
            root[2] = [
                value,
                [],
                []
            ]
        else:
            sub_tree = root.pop(2)
            root.insert(2, [
                value,
                [],
                sub_tree
            ])

    @staticmethod
    def get_value(root):
        return root[0]

    @staticmethod
    def set_value(root, value):
        root[0] = value
        return value

    @staticmethod
    def get_left(root):
        return root[1]

    @staticmethod
    def get_right(root):
        return root[2]


"""Tests for ord_schema.process_dataset."""

import os
import tempfile

from absl import flags
from absl.testing import absltest
from absl.testing import flagsaver

from ord_schema import process_dataset
from ord_schema.proto import dataset_pb2
from ord_schema.proto import reaction_pb2

class ProcessDatasetTest(absltest.TestCase):

    def setUp(self):
        super().setUp()
        self.test_subdirectory = tempfile.mkdtemp(dir=flags.FLAGS.test_tmpdir)
        reaction1 = reaction_pb2.Reaction()
        dummy_input = reaction1.inputs['dummy_input']
        dummy_component = dummy_input.components.add()
        dummy_component.identifiers.add(type='CUSTOM')
        dummy_component.identifiers[0].details = 'custom_identifier'
        dummy_component.identifiers[0].value = 'custom_value'
        dummy_component.is_limiting = True
        dummy_component.mass.value = 1
        dummy_component.mass.units = reaction_pb2.Mass.GRAM
        reaction1.outcomes.add().conversion.value = 75
        dataset1 = dataset_pb2.Dataset(reactions=[reaction1])
        self.dataset1_filename = os.path.join(self.test_subdirectory,
                                              'dataset1.pb')
        with open(self.dataset1_filename, 'wb') as f:
            f.write(dataset1.SerializeToString())
        # reaction2 is empty.
        reaction2 = reaction_pb2.Reaction()
        dataset2 = dataset_pb2.Dataset(reactions=[reaction1, reaction2])
        self.dataset2_filename = os.path.join(self.test_subdirectory,
                                              'dataset2.pb')
        with open(self.dataset2_filename, 'wb') as f:
            f.write(dataset2.SerializeToString())

    def test_main_with_input_pattern(self):
        with flagsaver.flagsaver(input_pattern=self.dataset1_filename):
            process_dataset.main(())

    def test_main_with_input_file(self):
        input_file = os.path.join(self.test_subdirectory, 'input_file.txt')
        with open(input_file, 'w') as f:
            f.write(f'{self.dataset1_filename}\n')
        with flagsaver.flagsaver(input_file=input_file):
            process_dataset.main(())

    def test_main_with_validation_errors(self):
        with flagsaver.flagsaver(input_pattern=self.dataset2_filename,
                                 write_errors=True):
            with self.assertRaisesRegex(ValueError,
                                        'validation encountered errors'):
                process_dataset.main(())
        error_filename = f'{self.dataset2_filename}.error'
        self.assertTrue(os.path.exists(error_filename))
        expected_output = [
            'Reactions should have at least 1 reaction input\n',
            'Reactions should have at least 1 reaction outcome\n',
        ]
        with open(error_filename) as f:
            self.assertEqual(f.readlines(), expected_output)

    def test_main_with_updates(self):
        output = os.path.join(self.test_subdirectory, 'output.pb')
        with flagsaver.flagsaver(input_pattern=self.dataset1_filename,
                                 update=True,
                                 output=output):
            process_dataset.main(())
        self.assertTrue(os.path.exists(output))
        with open(output, 'rb') as f:
            dataset = dataset_pb2.Dataset.FromString(f.read())
        self.assertLen(dataset.reactions, 1)
        self.assertStartsWith(dataset.reactions[0].provenance.record_id, 'ord-')

    def test_main_with_too_many_flags(self):
        with flagsaver.flagsaver(input_pattern=self.dataset1_filename,
                                 input_file=self.dataset2_filename):
            with self.assertRaisesRegex(ValueError, 'not both'):
                process_dataset.main(())

    def test_preserve_existing_dataset_id(self):
        dataset = dataset_pb2.Dataset(
            reactions=[reaction_pb2.Reaction()],
            dataset_id='64b14868c5cd46dd8e75560fd3589a6b')
        filename = os.path.join(self.test_subdirectory, 'test.pb')
        with open(filename, 'wb') as f:
            f.write(dataset.SerializeToString())
        expected_filename = os.path.join(self.test_subdirectory, 'data', '64',
                                         '64b14868c5cd46dd8e75560fd3589a6b.pb')
        self.assertFalse(os.path.exists(expected_filename))
        with flagsaver.flagsaver(root=self.test_subdirectory,
                                 input_pattern=filename,
                                 validate=False,
                                 update=True):
            process_dataset.main(())
        self.assertTrue(os.path.exists(expected_filename))

    def test_bad_dataset_id(self):
        dataset = dataset_pb2.Dataset(
            reactions=[reaction_pb2.Reaction()],
            dataset_id='not-a-real-dataset-id')
        filename = os.path.join(self.test_subdirectory, 'test.pb')
        with open(filename, 'wb') as f:
            f.write(dataset.SerializeToString())
        with flagsaver.flagsaver(root=self.test_subdirectory,
                                 input_pattern=filename,
                                 validate=False,
                                 update=True):
            with self.assertRaisesRegex(ValueError, 'malformed dataset ID'):
                process_dataset.main(())

if __name__ == '__main__':
    absltest.main()