import cv2, re, os, math, pytesseract
import numpy as np
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract'

try:
    from PIL import Image
except ImportError:
    import Image

height = 602
width = 295

def crop_and_rotates_image(image, image_mono, file_name):
     (alt, lar) = image_mono.shape[:2]
     _ , imgT = cv2.threshold(image_mono, 200, 255, cv2.THRESH_BINARY)

     p1=0
     xi=lar

     # variavel que valida se a inclinação é positiva ou negativa
     inc=0

     for y in range (0,alt,1):
          for x in range (0,lar,1):
               cor = imgT[y,x]
               if cor!=255 and(p1==0):
                    ponto1=(x,y)
                    xi=x
                    p1=1
                    if x>(lar/2):
                         inc=1

               if (p1==1) and (inc==1) and (cor!=255) and (x<xi):
                    ponto2=(x,y)
                    xi=x

               if (p1==1) and (inc==0) and (cor!=255) and (x>xi):
                    ponto2=(x,y)
                    xi=x

     angulo = math.atan2 (ponto1[1]-ponto2[1],ponto1[0]-ponto2[0])

     if inc==1:
          angulo = math.degrees(angulo)

     if inc==0:
          angulo = math.degrees(angulo)+180
          aux=ponto1
          ponto1=ponto2
          ponto2=aux

     # gira img mono
     M = cv2.getRotationMatrix2D(ponto1, angulo, 1.0)

     # gira imagem original
     original_rotacionada = cv2.warpAffine(image, M, (lar, alt))

     pontoinicial=ponto1

     xi=pontoinicial[0]-height
     xf=pontoinicial[0]
     yi=pontoinicial[1]
     yf=pontoinicial[1]+width

     recorte = original_rotacionada[yi:yf,xi:xf]

     cv2.imwrite(file_name.replace(".", "_cropped."), recorte)

     return recorte

def read_plate_title(cropped_image):
     imagem = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2GRAY)
     thresh = cv2.threshold(imagem, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
     custom_config = r'-c tessedit_char_whitelist=ABCDEFGHIJKLMNPQRSTUVWXYZ-1234567890 --psm 1'
     texto=(pytesseract.image_to_string(thresh, config=custom_config))
     texto=re.sub(r'[-|\s“"—— \n_]', '', texto)
     return texto

def list_files_from_dir(test_path):
     return os.listdir(test_path), os.listdir('gabarito')

def validate_plates(path):
     test_files, template_files = list_files_from_dir(path)

     dict_template_images = {}

     for template_path in template_files:
          template_code = template_path.split(' ')[1].replace('.jpg','')
          dict_template_images[template_code] = cv2.imread(f'gabarito/{template_path}')

     for file in test_files:
          print("-" * 100)

          complete_path_file = f'{path}/{file}'
          image = cv2.imread(complete_path_file)
          image_mono = cv2.imread(complete_path_file, 0)

          print(f'Arquivo => {complete_path_file}')

          cropped_image = crop_and_rotates_image(image, image_mono, file)
          plate_code = read_plate_title(cropped_image)

          print(f'Gabarito => {plate_code}')

          template = dict_template_images[plate_code]

          try:
               difference = cv2.absdiff(template, cropped_image)
               subtract = cv2.subtract(template, cropped_image)
               ret, difference = cv2.threshold(difference, 127, 255, cv2.THRESH_BINARY)
          except:
               print("Placa => Recusada - As placas tem tamanhos diferentes")
               continue

          b, g, r = cv2.split(difference)

          for y in range (0,template.shape[0],1):
               for x in range (0,template.shape[1],1):
                    if b[y,x] != 0 or g[y,x] != 0 or r[y,x] != 0:
                         cropped_image [y,x]=(0,0,255)

          if cv2.countNonZero(b) < 5000 and cv2.countNonZero(g) < 5000 and cv2.countNonZero(r) < 5000:
               print("Placa => Aceita")

          elif cv2.countNonZero(b) >= 5000 or cv2.countNonZero(g) >= 5000 or cv2.countNonZero(r) >= 5000:
               print("Placa => Recusada - As imagens são diferentes")

if __name__ == '__main__':

     # placas_com_erros
     validate_plates('FotosPlacas')

     # placas_sem_erros
     validate_plates('FotosPlacasComErro')


T = int(input())
for tc in range(1, T+1):
    N = int(input())
    data = [list(map(int, input().split())) for _ in range(N)]
    visited = [False] * N   #첫행부터 밑으로 내려가는데 열이 겹치지 않아야 하므로, 이미 선택한 열인지 확인
    min_sum = 9999999   #최소값
    each_sum = 0    #백트래킹을 위한 부분합

    def sum_sub(i):
        global each_sum, min_sum
        #만약 부분합이 이미 최소값을 넘어선다면 중단하고 돌아감
        if each_sum > min_sum:
            return

        #마지막 행까지 선택을 완료했다면
        if i == N:
            #선택한 숫자들의 합과 현재의 최소값을 비교하고, 기존 최소값보다 작다면 교체
            if each_sum < min_sum:
                min_sum = each_sum
            return

        for j in range(N):
            #아직 사용하지 않은 열에 대해서 다음 단계 진행
            if not visited[j]:
                visited[j] = True   #이번에 선택한 열을 방문처리하고
                each_sum += data[i][j]  #해당 인덱스의 값을 더해주고
                sum_sub(i+1)    #다음 행 진행

                visited[j] = False  #그 후에 같은 행의 다음 열을 선택하는 경우를 위해 방문처리와 숫자합 복구
                each_sum -= data[i][j]

    sum_sub(0)
    print(f'#{tc} {min_sum}')


DB_EMAILS=[]
while True:
    print('1)) Input Email 2) Show db_emails 3) Delete email in DB 4)stop while 5)rename email')
    choices = int(input('enter choice'))
    if choices==1:
        email=input('enter email:')
        if email.endswith('@gmail.com'):
            if email in DB_EMAILS:
                print('email exists')
                continue
            DB_EMAILS.append(email)
            continue
        print('Invalid email, only GMAIL')

    elif choices==2:
        print(DB_EMAILS)
    elif choices==3:
        email=input('enter email:')
        if email in DB_EMAILS:
            # index=DB_EMAILS.index(emai)
            # DB_EMAILS.pop(index)
            DB_EMAILS.remove(email)
        else:
            print(f'{email} doesnt exist')
    elif choices==4:
        break
    elif choices==5:
        old_email=input('enter email:')
        if email in DB_EMAILS:
            new_email=input('enter new email:')
            if email.endswith('@gmail.com'):
                DB_EMAILS.remove(old_email)
                DB_EMAILS.append(new_email)

    else:
        print('Error')

# 1)отменя выбора
# 2)оптимизация
# 3)


'''

Given inorder and postorder traversal of a tree, construct the binary tree.

Note:
You may assume that duplicates do not exist in the tree.

'''

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """
        d = {}
        for index, val in enumerate(inorder):
            d[val] = index

        return self._helper(inorder, 0, len(inorder) - 1, postorder, 0, len(postorder) - 1, d)

    def _helper(self, inorder, in_left, in_right, postorder, post_left, post_right, d):
        if in_right < in_left or post_right < post_left:
            return None
        curr = postorder[post_right]
        index = d[curr]
        node = TreeNode(curr)
        node.left = self._helper(inorder, in_left, index - 1, postorder, post_left, post_left + index - in_left - 1, d)
        node.right = self._helper(inorder, index + 1, in_right, postorder, post_right - in_right + index, post_right - 1, d)
        return node


from datetime import datetime
from datetime import date
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
from collections import OrderedDict
import os
import random
import seaborn as sns
import matplotlib as mpl
import re
import csv
import pickle
import sys
#sys.path.insert(0,'D:/PhD/Behavior/Behavior_Analysis/batch_clean_selected')
from parse_data_v2_1 import Mouse_data
from parse_data_v2_1 import pickle_dict
from parse_data_v2_1 import load_pickleddata
from datamatrix import io
from statsmodels.stats.anova import AnovaRM

#path = '/Volumes/GoogleDrive/My Drive/behavior data/experiment_data_2022_3_odor go_no-go_no_delay/parsed_dataframe_pickle'
#path = 'F:/My Drive/behavior data/valence_task_2023_odor go_no-go_no_delay/parsed_dataframe_pickle'
path='F:/My Drive/behavior data/valence_task_2023_go_no_66/parsed_dataframe_pickle'
path_excels = 'F:/My Drive/behavior data/valence_task_2023_go_no_66/parsed_dataframe_spreadsheet'

#females = os.path.join(path,'combined_all_clean_G_females.pickle')
#males = os.path.join(path,'combined_all_clean_G_males.pickle')

#f = load_pickleddata(females)
#m = load_pickleddata(males)

# event plot with trials and iscorerct data

# assign two df

# filepath1 = os.path.join(path,'combined_all_clean_iso_pre.pickle')
# pre_data = load_pickleddata(filepath1)
#
# filepath2=os.path.join(path,'combined_all_clean_iso_post.pickle')
# post_data = load_pickleddata(filepath2)
#
# filepath3 = os.path.join(path,'combined_all_clean_social_pre.pickle')
# social_pre=load_pickleddata(filepath3)
#
# filepath4 = os.path.join(path,'combined_all_clean_social_post.pickle')
# social_post=load_pickleddata(filepath4)

# pre_data.index.duplicated()
# post_data.index.duplicated()
# social_post.index.duplicated()
# social_pre.index.duplicated()
#f.index.duplicated()
#m.index.duplicated()

#values= ['','baseline','after_2_weeks']
#values = ['5*10^-2','5*10^-3','5*10^-4','5*10^-5','5*10^-6']
#values_water = ['pipet vs non-pipet','pipet vs non-pipet','pipet vs pipet','pipet vs non-pipet']
#values = ['5*10^-1','5*10^-2','5*10^-4','5*10^-5']

# data_pre = pre_data[(pre_data['trialtype'].isin(['go','no_go']))]
# data_post = post_data[(post_data['trialtype'].isin(['go','no_go']))]
# social_pre = social_pre[(social_pre['trialtype'].isin(['go','no_go']))]
# social_post = social_post[(social_post['trialtype'].isin(['go','no_go']))]
#f = f[(f['trialtype'].isin(['go','no_go']))]
#m = m[(m['trialtype'].isin(['go','no_go']))]
# cold_test = post_data[(post_data['dilution'].isin(['6']))]

#data_deg_cond =  data_deg_cond.groupby(['mouse_name','session','phase'])[y].mean().reset_index()
# data_pre =  data_pre.groupby(['mouse_name','dilution'])[y].mean().reset_index()
# data_post =  data_post.groupby(['mouse_name','dilution'])[y].mean().reset_index()
# social_pre=social_pre.groupby(['mouse_name','dilution'])[y].mean().reset_index()
# social_post=social_post.groupby(['mouse_name','dilution'])[y].mean().reset_index()
#f = f.groupby(['mouse_name','dilution'])[y].mean().reset_index()

#g = g.groupby(['mouse_name'])[y].mean().reset_index()

#g[g[:, 2].argsort()]

#print(np.shape(g))

#for_prism = pd.DataFrame(g['mouse_name'],g['session'],g['odormix'])
#for_prism.sort_values(by=['mouse_name'])
#for_prism.sort_values(by=['mouse_name','session','odormix'])

#print(for_prism)
#g = g.groupby(['mouse_name','session'])[y].mean().reset_index()

# cold_test= cold_test.groupby(['mouse_name'])[y].mean().reset_index()

#plot = sns.lineplot(data = data_pre,x = x,y = y, marker="o",ci='sd', err_style='bars')
#plot = sns.lineplot(data = data_post,x = x,y = y, marker="o",ci='sd', err_style='bars')
#plot = sns.lineplot(data = social_post,x = x,y = y, marker="o",ci='sd', err_style='bars')
#plot= sns.lineplot(data = social_pre,x = x,y = y, marker="o",ci='sd', err_style='bars')

#plot = sns.lineplot(data = data_pre,x = x,y = y, marker="o",hue="mouse_name",palette=['g','r','b'])
#plot = sns.lineplot(data = data_post,x = x,y = y, marker="o",hue="mouse_name",palette=['g','r'],linestyle='dashed')

#plot = sns.boxplot(data = data_pre, column= '5', x = 5,y = y)
#plt.scatter(x=5, y=y)

#leg = plot.legend()
#leg_lines = leg.get_lines()
#leg_lines[4].set_linestyle(":")

#plot = sns.lineplot(data = g,x = x,y = y, marker = "o", hue="odormix", ci=68, err_style='bars')
#plot = sns.barplot(data = g,x = x,y = y, marker = "o", hue="odormix#", ci=68, err_style='bars', hue_order = ('8:0 Go' ,'1:8 Go', '2:7 Go', '3:6 Go', '6:3 No', '7:2 No', '8:1 No', '8:0 No'))
#
# g['odormix'] = pd.Categorical(g['odormix'],
#                                     categories=['8:0 Go','1:8 Go', '2:7 Go', '3:6 Go', '4:5 Go', '5:4 No', '6:3 No', '7:2 No', '8:1 No', '8:0 No'],
#                                     ordered=True)

# g['odormix'] = pd.Categorical(g['odormix'],
#                                     categories=['1:8 Go', '2:7 Go', '3:6 Go', '6:6', '6:3 No', '7:2 No','8:1 No'],
#                                     ordered=True)

#plot = sns.lineplot(data = g,x = x,y = y, marker = "o", hue="odormix", ci=68, err_style='bars', legend = True)

#color = (sns.set_palette("bright")

#plot = sns.lineplot(data = g,x = x,y = y, marker = "o", hue="session",  legend = True, palette = sns.color_palette('bright', as_cmap=True))
#plot = sns.lineplot(data = g,x = x,y = y, marker = "o", hue="session",  legend = True, palette = ['g', 'g', 'purple','purple','r','r'])
#plot = sns.lineplot(data = g,x = x,y = y, marker = "o", hue="mouse_name", err_style='bars', errorbar="se", legend = True, palette = ['magenta','orange','purple','b','r'])
#plot = sns.scatterplot(data = g,x = x,y = y, hue="mouse_name")

#dm=io.readpickle(os.path.join(path,'combined_all_clean_learning.pickle'))

# females = os.path.join(path_excels,'females_pre_dark_lick_stat.xlsx')
# dataframe_fe=pd.read_excel(females)

# males = os.path.join(path_excels,'males_pre_dark_lick_stat.xlsx')
# dataframe_male = pd.read_excel(males)

#plot.set_ylabel(">1 Lick (%)", fontsize = 15)
#plot.set_ylabel("lick/s", fontsize = 15)
#plot.set_ylabel("latency (s)", fontsize = 15)

#plot.yaxis.set_ticks(np.arange(0, 1.05, 0.1))
#plot.yaxis.set_ticks(np.arange(0, 7, 1))
#plot.yaxis.set_ticks(np.arange(0, 3, 0.5))

def prism_output(path):
    #y = 'rate_window'
    #y= 'licked'
    y = 'latency_to_odor'
    pickled_data = os.path.join(path,'combined_all_clean_learning.pickle')
    pickled_data = load_pickleddata(pickled_data)
    pickled_data.index.duplicated()
    pickled_data = pickled_data[(pickled_data['trialtype'].isin(['no_go','go']))]
    pickled_data = pickled_data.groupby(['mouse_name','session','odormix'])[y].mean().reset_index()
    for_prism=pd.DataFrame(pickled_data)
    for_prism.sort_values('odormix')
    saving = path + 'latency.csv'
    for_prism.to_csv(saving)
    print(for_prism)

def correct_graph(path):
    y='is_Correct'
    pickled_data = os.path.join(path,'combined_all_clean_learning.pickle')
    pickled_data = load_pickleddata(pickled_data)
    pickled_data.index.duplicated()
    pickled_data = pickled_data[(pickled_data['trialtype'].isin(['no_go','go']))]
    pickled_data = pickled_data.groupby(['mouse_name','session','odormix'])[y].mean().reset_index()
    pickled_data['odormix'] = pd.Categorical(pickled_data['odormix'],
                                    categories=['0:8 Go','1:8 Go', '1:5 Go', '2:7 Go', '3:6 Go', '4:5 Go', '6:6', '5:4 No', '6:3 No', '7:2 No','5:1 No', '8:1 No', '8:0 No'],
                                    ordered=True)
    x = 'odormix'
    plot = sns.lineplot(data = pickled_data,x = x,y = y, marker = "o", hue="session",  legend = True, palette = ['g', 'r', 'g','b'])
    plt.show()

def lick_graph (pickled_data):
    y = 'licked'
    x = 'odormix'
    pickled_data = os.path.join(path,'combined_all_clean_learning.pickle')
    pickled_data = load_pickleddata(pickled_data)
    pickled_data.index.duplicated()
    pickled_data = pickled_data[(pickled_data['trialtype'].isin(['no_go','go']))]
    pickled_data = pickled_data.groupby(['mouse_name','session','odormix'])[y].mean().reset_index()
    pickled_data['odormix'] = pd.Categorical(pickled_data['odormix'],
                                    categories=['0:8 Go','1:8 Go', '1:5 Go', '2:7 Go', '3:6 Go', '4:5 Go', '6:6', '5:4 No', '6:3 No', '7:2 No','5:1 No', '8:1 No', '8:0 No'],
                                    ordered=True)
    plot = sns.lineplot(data = pickled_data,x = x,y = y, marker = "o", hue="mouse_name",  legend = True, palette = ['g', 'r', 'b'])
    plot.set_ylabel(">1 Lick (%)", fontsize = 15)
    plot.yaxis.set_ticks(np.arange(0, 1.05, 0.1))
    mpl.rcParams['font.size']=30
    plt.show()

def rate_graph(pickled_data):
    #y = 'rate_odor'
    y='rate_window'
    x = 'odormix'
    pickled_data = os.path.join(path,'combined_all_clean_learning.pickle')
    pickled_data = load_pickleddata(pickled_data)
    pickled_data.index.duplicated()
    pickled_data = pickled_data[(pickled_data['trialtype'].isin(['no_go','go']))]
    pickled_data = pickled_data.groupby(['mouse_name','session','odormix'])[y].mean().reset_index()
    pickled_data['odormix'] = pd.Categorical(pickled_data['odormix'],
                                    categories=['0:8 Go','1:8 Go', '1:5 Go', '2:7 Go', '3:6 Go', '4:5 Go', '6:6', '5:4 No', '6:3 No', '7:2 No','5:1 No', '8:1 No', '8:0 No'],
                                    ordered=True)
    plot = sns.lineplot(data = pickled_data,x = x,y = y, marker = "o", hue="mouse_name",  legend = True, palette = ['g', 'r', 'g'])
    plot.set_ylabel("lick/s", fontsize = 15)
    plot.yaxis.set_ticks(np.arange(0, 1.05, 0.1))
    mpl.rcParams['font.size']=30
    plt.show()

def latency_graph(pickled_data):
    y = 'latency_to_odor'
    x = 'odormix'
    pickled_data = os.path.join(path,'combined_all_clean_learning.pickle')
    pickled_data = load_pickleddata(pickled_data)
    pickled_data.index.duplicated()
    pickled_data = pickled_data[(pickled_data['trialtype'].isin(['no_go','go']))]
    pickled_data = pickled_data.groupby(['mouse_name','session','odormix'])[y].mean().reset_index()
    #pickled_data['odormix'] = pd.Categorical(pickled_data['odormix'],
                                    #categories=['0:8 Go','1:8 Go', '1:5 Go', '2:7 Go', '3:6 Go', '4:5 Go', '6:6', '5:4 No', '6:3 No', '7:2 No','5:1 No', '8:1 No', '8:0 No'],
                                    #ordered=True)
    plot = sns.lineplot(data = pickled_data,x = x,y = y, marker = "o", hue="mouse_name",  legend = True, palette = sns.color_palette('bright', as_cmap=True))
    plot.set_ylabel("latency (s)", fontsize = 15)
    plot.yaxis.set_ticks(np.arange(0, 3, 0.5))
    mpl.rcParams['font.size']=30
    plt.show()

#lick_graph(path)
#correct_graph(path)
#rate_graph(path)
#latency_graph(path)
prism_output(path)

#plot.yaxis.set_ticks(np.arange(0.25, 1.05, 0.05))

#plot = sns.lineplot(data = g,x = x,y = y, marker = "o")

#plot = sns.lineplot(data = g,x = x,y = y, marker = "o")
#plot = sns.lineplot(data = g,x = x,y = y, palette=['r'])

#plot=sns.boxplot(data = g,x = x,y = y)

#plot = sns.lineplot(data = m,x = x,y = y, palette=['g'])
#plot = sns.lineplot(data = g,x = x,y = y, palette=['r'])
#plot = sns.lineplot(data = g,x = x,y = y, hue="mouse_name")

# my_dict = dict(x=x, y=y)
# data = pd.DataFrame(my_dict)
# fig, ax= plt.subplots()
# ax =  sns.lineplot(data = m,x = x,y = y, palette=['g'])
# ax1 = sns.lineplot(data = m,x = x,y = y, palette=['r'])

#plot=sns.boxplot(data = cold,x = x,y = y, position=(0,1))

#plot = sns.lineplot(data = social_post,x = x,y = y, marker="o",hue="mouse_name", palette=['g'])
#plot = sns.lineplot(data = social_pre,x = x,y = y, marker="o",hue="mouse_name")

#plot = sns.lineplot(data = data_deg_cond,x = x,y = y, marker="o",hue="phase")
#plot.set_xlabel("Concentration", fontsize = 10)
#plot.set_xlabel("Training Day", fontsize = 15)

#plot.set_ylabel("anticipatory lick rate", fontsize = 15)

#plot.set_ylabel("Refrain from licking No_Go", fontsize = 10)
#plot.set_ylabel("Latency to Rewarded Odor (s)", fontsize = 10)

#plot.axes.set_xticklabels(labels = "odormix", fontsize = 15)
#plot.axes.set_yticklabels(fontsize = 15)

#for i, j in zip(x, y):
 #   plot.annotate(str(j),xy=(i,j))

#plot.set_ylim (0.4,1)

# label = "{:.2f}".format(y)
# plt.annotate(label, # this is the text
#                  (x,y), # these are the coordinates to position the label
#                  textcoords="offset points", # how to position the text
#                  xytext=(0,10), # distance from text to points (x,y)
#                  ha='center') # horizontal alignment can be left, right or center

#sns.lineplot(data = data_deg_cond,x = x,y = y, marker="o")

#plt.locator_params(axis="x", integer=True, tight=True)

#sns.move_legend(plot, "upper left", bbox_to_anchor=(1, 1))

#plt.locator_params(axis="both", tight=True)

#plt.xticks(ticks = x ,labels = values, rotation = 'vertical')

#plt.gca().invert_xaxis()

#plt.show()


# conda create -n chatvenv python=3.6.2
# pip install nltk
# numpy 1.14.0
# pip install tensorflow==1.4
# pip install tflearn==0.3.2
# pip install scipy==1.4.1
# pip install h5py==2.8.0rc1
# pip install windows-curses==1.0 (if you're on windows)

import nltk
from nltk import word_tokenize
from nltk.stem.lancaster import LancasterStemmer
import numpy as np
import tflearn
import tensorflow
import random
import json
import string
import pickle
from gtts import gTTS
import playsound
import pyttsx3
import os
from datetime import datetime
import time

def speak(text):
    engine = pyttsx3.init()
    engine.setProperty('rate', 125)
    volume = engine.getProperty('volume')
    engine.setProperty('volume',1.0)
    engine.say(text)
    engine.runAndWait()
    engine.stop()

def speak2(text):
    tts = gTTS(text=text, lang='ro')
    date_string = datetime.now().strftime("%d%m%Y%H%M%S")
    filename = "voice"+date_string+".mp3"
    #filename = "voice.mp3"
    tts.save(filename)
    playsound.playsound(filename)
    time.sleep(2)
    os.remove(filename)

stemmer = LancasterStemmer()

with open("intents.json") as d:
    data = json.load(d)

#print(data['intents']) # list of dictionaries
try:
    with open("data.pickle", "rb") as f:
        words, labels, training, output = pickle.load(f)
except:
    words = []
    labels = []
    docs_x = []
    docs_y = []
    ########################### testing word_tokenize ###########
    # asd = "how are you?"
    # w = word_tokenize(asd) #['how', 'are', 'you', '?']
    # print(w)
    ##############################################################

    for intent in data['intents']:
        for pattern in intent["patterns"]:
            wrds = nltk.word_tokenize(pattern)
            words.extend(wrds)
            docs_x.append(wrds)
            docs_y.append(intent["tag"])

        if intent["tag"] not in labels:
            labels.append(intent["tag"])

    #### stemmer the words (punctuation) ########
    words = [stemmer.stem(w.lower()) for w in words if w != "?"]
    #words = [char.lower() for char in words if char not in string.punctuation]

    #  and duplicates
    words = sorted(list(set(words)))
    # #################################################
    labels = sorted(labels)

    # ########### Training ################
    # # Bag of words
    training = []
    output = []

    out_empty = [0 for _ in range(len(labels))]

    for x, doc in enumerate(docs_x):
        bag = []
        # no punctuation from docs_x
        #wrds = [char.lower() for char in doc if char not in string.punctuation]
        wrds = [stemmer.stem(w) for w in doc]

        for w in words:
            if w in wrds:
                bag.append(1)
            else:
                bag.append(0)
        output_row = out_empty[:]
        output_row[labels.index(docs_y[x])] = 1

        training.append(bag)
        output.append(output_row)

    #print(training)
    training = np.array(training)
    output = np.array(output)

    with open("data.pickle", "wb") as f:
        pickle.dump((words, labels, training, output), f)

# Learning Model A.I
tensorflow.reset_default_graph() # doesnt matter
net = tflearn.input_data(shape=[None, len(training[0])])
net = tflearn.fully_connected(net, 8) # hidden layer 8 neurons
net = tflearn.fully_connected(net, 8) # hidden layer 8 neurons
net = tflearn.fully_connected(net, len(output[0]), activation="softmax")
net = tflearn.regression(net)

model = tflearn.DNN(net)
try:
    model.load("model.tflearn")
except:
    model.fit(training, output, n_epoch=1000, batch_size=8, show_metric=True)
    model.save("model.tflearn")

def bag_of_words(s, words):
    bag = [0 for _ in range(len(words))]
    s_words = nltk.word_tokenize(s)
    s_words = [stemmer.stem(word.lower()) for word in s_words]

    for se in s_words:
        for i,w in enumerate(words):
            if w == se:
                bag[i] = 1
    return np.array(bag)

def chat():
    print("Start talking with the bot")
    speak("Start talking with the bot")
    while True:
        inp = input("You: ")
        if inp.lower() == "quit":
            break

        results = model.predict([bag_of_words(inp,words)])[0]
        #print(results)
        results_index = np.argmax(results) # pick the biggest procent
        #print(results[results_index]) # 0.934
        tag = labels[results_index]
        #print(tag)
        if results[results_index] > 0.7:
            for tg in data["intents"]:
                if tg["tag"] == tag:
                    responses = tg["responses"]
            v = random.choice(responses)
            print(v)
            speak(v)
        else:
            print("I didn't understand. Please try again!")
            speak("I didn't understand. Please try again!")

chat()


from django.urls import path
from . import views

urlpatterns = [

     path('confirmado', views.CreateConfirmadoView.create, name='confirmado'),
     path('f_confirmados/', views.ListToFreelancerConfirmadoView.listToFreelancer,
          name='f_confirmados'),
     path('e_confirmados/', views.ListToEstabelecimentoConfirmadoView.listToEstabelecimento,
          name='e_confirmados'),
]


import sys,os
from random import randint
plat = sys.platform
try:
    import PySimpleGUI as sg
except:
    if plat == 'win32':
        os.system('pip install pysimplegui')
        import PySimpleGUI as sg
    elif plat == 'linux':
        os.system('pip3 install pysimplegui')
        import PySimpleGUI as sg
aleatorio = randint(0,100)

class telapython:
  def __init__(self):
    sg.change_look_and_feel('DarkTeal7')
    layout = [
      [sg.Text('Chute um numero de 0 a 100',size=(12,0)),sg.Input(size=(10,10),key='numero')],
      [sg.Button('enviar')],
      [sg.Output(size=(30,10))]

    ]
    self.janela = sg.Window('Dados do usuario').layout(layout)

  def iniciar(self):
    while True:
      self.Button, self.values = self.janela.Read()
      num = int(self.values['numero'])
      if aleatorio == num:
          print('Parabens, voce acertou!!')
      else:
          if num > aleatorio:
              print()
              print('Chute um numero menor')
          else:
              print()
              print('Chute um valor maior')

tela = telapython()
tela.iniciar()


from pybricks.hubs import EV3Brick
from pybricks.ev3devices import (Motor, TouchSensor, ColorSensor,
                                 InfraredSensor, UltrasonicSensor, GyroSensor)
from pybricks.parameters import Port, Stop, Direction, Button, Color
from pybricks.tools import wait, StopWatch, DataLog
from pybricks.robotics import DriveBase
from pybricks.media.ev3dev import SoundFile, ImageFile

from constants import *

DRIVE_SPEED = 120

# Initialize the motors.
left_motor = Motor(Port.A)
right_motor = Motor(Port.D)

# Initialize the color sensor.
left_line_sensor = ColorSensor(Port.S3)
right_line_sensor = ColorSensor(Port.S4)

# Initialize the drive base.
robot = DriveBase(left_motor, right_motor, wheel_diameter=55.5, axle_track=104)

current_direction = 0 #0 = front, 1 = right, 2 = back, 3 = left
x_position = 0
y_position = 8

def follow_line(speed, direction=FORWARD, turn_speed=30):
    """
    Follow black line betwen the two sensors until black in both sensors is detected
    (follow line until sensors on corner)
    """
    while True:
        left_is_white = left_line_sensor.reflection() > 15
        right_is_white = right_line_sensor.reflection() > 15

        if left_is_white and right_is_white:
            robot.drive(speed*direction, 0)
        if left_is_white and not right_is_white:
            robot.drive(speed, turn_speed)
            if direction == -1:
                wait(50)
        if not left_is_white and right_is_white:
            robot.drive(speed, -turn_speed)
            if direction == -1:
                wait(50)
        if not left_is_white and not right_is_white:
            robot.stop(Stop.BRAKE)
            break

        wait(10)

    robot.stop(Stop.BRAKE)

def update_position(direction):
    global y_position
    global current_direction
    global x_position

    if current_direction == FRONT:
        y_position -= direction
    if current_direction == RIGHT:
        x_position += direction
    if current_direction == BACK:
        y_position += direction
    if current_direction == LEFT:
        x_position -= direction

def move_back():
    """
    Robot moves to the corner in the back
    """
    robot.drive(-100, 0)
    wait(1000)
    follow_line(speed = 50, direction = FORWARD, turn_speed = 30)

    robot.drive(-50, 0)
    wait(500)
    follow_line(speed = 50, direction = BACKWARD, turn_speed = 10)

    update_position(BACKWARD)

def move_front(speed=DRIVE_SPEED):
    """
    Robot moves to the corner in the front
    """
    robot.drive(50, 0)
    wait(500)

    follow_line(speed)
    update_position(FORWARD)

def move_right():
    """
    Robot moves to the corner in the right
    """
    robot.drive(50, 50)
    wait(1000)

    left_is_white = left_line_sensor.reflection() > 15
    while left_is_white:
        left_is_white = left_line_sensor.reflection() > 15
        robot.drive(50, 50)

    follow_line(DRIVE_SPEED)

    global current_direction
    current_direction += 1
    if current_direction == 4:
        current_direction = 0
    update_position(FORWARD)

def move_left():
    """
    Robot moves to the corner in the left
    """
    robot.drive(50, -50)
    wait(1000)

    right_is_white = right_line_sensor.reflection() > 15
    while right_is_white:
        right_is_white = right_line_sensor.reflection() > 15
        robot.drive(50, -50)

    follow_line(DRIVE_SPEED)

    global current_direction
    current_direction -= 1
    if current_direction == -1:
        current_direction = 3
    update_position(FORWARD)

# Create your objects here.
ev3 = EV3Brick()

# Write your program here.
ev3.speaker.beep()


from datetime import datetime
from random import choice

from musicleague.errors import UserDoesNotExistError
from musicleague.errors import UserExistsError
from musicleague.persistence.models import User
from musicleague.persistence.select import select_user
from musicleague.persistence.select import select_user_by_email
from musicleague.persistence.update import upsert_user

DEFAULT_AVATARS = [
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/bowie_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/daft_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/dmc_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/elvis_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/hendrix_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/madonna_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/marley_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/morrison_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/prince_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/punk_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/siouxie_avatar.svg',
    'https://s3.amazonaws.com/musicleague-static-assets/avatars/supremes_avatar.svg'
]

PROFILE_BACKGROUNDS = [
    'ml_header01.jpg', 'ml_header02.jpg', 'ml_header03.jpg', 'ml_header04.jpg',
    'ml_header05.jpg', 'ml_header06.jpg', 'ml_header07.jpg', 'ml_header08.jpg',
    'ml_header09.jpg', 'ml_header10.jpg', 'ml_header11.jpg', 'ml_header12.jpg',
    'ml_header13.jpg', 'ml_header14.jpg', 'ml_header15.jpg', 'ml_header16.jpg',
    'ml_header17.jpg', 'ml_header18.jpg', 'ml_header19.jpg', 'ml_header20.jpg',
    'ml_header21.jpg', 'ml_header22.jpg', 'ml_header23.jpg', 'ml_header24.jpg'
]

def create_user_from_spotify_user(spotify_user):
    user_id = spotify_user.get('id')
    user_email = spotify_user.get('email')
    user_display_name = spotify_user.get('display_name')
    if user_display_name is None:
        user_display_name = user_id

    user_images = spotify_user.get('images')
    user_image_url = ''
    if user_images:
        user_image_url = user_images[0].get('url', user_image_url)

    return create_user(
        id=user_id, email=user_email, name=user_display_name, image_url=user_image_url)

def update_user_from_spotify_user(user, spotify_user):
    user_images = spotify_user.get('images')
    user_image_url = ''
    if user_images:
        user.image_url = user_images[0].get('url', user_image_url)
        upsert_user(user)

    return user

def create_user(id, name, email, image_url):
    if get_user(id):
        raise UserExistsError('User with id %s already exists' % id)

    if not image_url:
        image_url = choice(DEFAULT_AVATARS)

    profile_background = choice(PROFILE_BACKGROUNDS)

    new_user = User(
        id=id, email=email, image_url=image_url, is_admin=False,
        joined=datetime.utcnow(), name=name, profile_bg=profile_background)

    upsert_user(new_user)

    return new_user

def update_user(id, name, email, image_url):
    user = get_user(id)

    if not user:
        raise UserDoesNotExistError('User with id %s does not exist' % id)

    user.id = id if id else user.id
    user.name = name if name else user.name
    user.email = email if email else user.email
    user.image_url = image_url if image_url else user.image_url

    upsert_user(user)

    return user

def create_or_update_user(id, name, email, image_url):
    user = get_user(id)

    if not user:
        user = create_user(id, name, email, image_url)
    else:
        user = update_user(id, name, email, image_url)

    return user

def get_user(id):
    user = select_user(id)
    if user is None:
        return None

    updated = False

    if not user.image_url:
        user.image_url = choice(DEFAULT_AVATARS)
        updated = True

    if not user.profile_background:
        user.profile_background = choice(PROFILE_BACKGROUNDS)
        updated = True

    if updated:
        upsert_user(user)

    return user

def get_user_by_email(email):
    # TODO No need for this function
    return select_user_by_email(email)


import cv2
import geometry
import numpy as np
import match

def keypointsMatching(left, right):
    """Returns the list of the matched keypoints from input images left and right."""
    akaze = cv2.AKAZE_create()
    kpts1, desc1 = akaze.detectAndCompute(left, None)
    kpts2, desc2 = akaze.detectAndCompute(right, None)
    matcher = cv2.BFMatcher(cv2.NORM_L2, True)
    matches = matcher.match(desc1, desc2)
    sortedmatches = sorted(matches, key=lambda x: x.distance)
    nb_matches = 300
    good_matches = sortedmatches[:nb_matches]
    obj = []
    scene = []

    for i in range(len(good_matches)):
        # -- Get the keypoints from the good matches
        obj.append(kpts1[good_matches[i].queryIdx].pt)
        scene.append(kpts2[good_matches[i].trainIdx].pt)

    F, mask = cv2.findFundamentalMat(np.array(obj), np.array(scene), cv2.FM_RANSAC)
    correct_kpts1 = []
    correct_kpts2 = []
    correct_matches = []

    for i in range(len(mask)):
        if mask[i, 0] > 0:
            correct_kpts1.append(obj[i])
            correct_kpts2.append(scene[i])
            correct_matches.append(good_matches[i])

    print(len(correct_kpts1))
    matches_image = np.empty((max(left.shape[0], right.shape[0]), left.shape[1] + right.shape[1], 3), dtype=np.uint8)
    cv2.drawMatches(left, kpts1, right, kpts2, correct_matches, matches_image)
    cv2.waitKey(0)

    return correct_kpts1, correct_kpts2, matches_image, F

def rectification(left, right, correct_kpts1, correct_kpts2, F):
    """Returns rectified images using OpenCV rectification algorithm"""
    """
    We have implemented our rectification method based on papar
    Physically-valid view synthesis by image interpolation, Charles R. Dyer Steven M. Seitz.

    In this case, we can obtain the matrix of rotation, translation and scale to de-rectify, but
    this algorithm has a very poor performance compared to OpenCV built-in rectification method.
    Furthermore, this method is not good enough to calculate the disparity, so we comment the few following lines
    and we use OpenCV.
    """
    correct_kpts1 = np.array(correct_kpts1)
    correct_kpts1 = correct_kpts1.reshape((correct_kpts1.shape[0] * 2, 1))
    correct_kpts2 = np.array(correct_kpts2)
    correct_kpts2 = correct_kpts2.reshape((correct_kpts2.shape[0] * 2, 1))
    shape = (left.shape[1], left.shape[0])

    rectBool, H1, H2 = cv2.stereoRectifyUncalibrated(correct_kpts1, correct_kpts2, F, shape, threshold=1)
    R1 = cv2.warpPerspective(left, H1, shape)
    R2 = cv2.warpPerspective(right, H2, shape)

    return R1, R2

def stereoMatching(R1, R2, mi, ma):
    """Compute disparity mapping between two rectified images, using Kolmogorov and Zabih's graph cuts stereo matching algorithm."""
    """
    We found that the quality of disparity map obtained by OpenCV StereoSGBM is depend
    strongly the choice of parameters. So we implement the method based on paper:
    Kolmogorov and zabih’sgraph cuts stereo matching algorithm, Pauline Tan Vladimir Kolmogorov, Pascal Monasse.
    It suffice to set a good disparity range [Min, Max].
    Attention: with this python version implementation, this method is very slow, so to quickly have a result,
    we force here the images used can't be larger than 200*200
    """
    K = -1
    lambda_ = -1
    lambda1 = -1
    lambda2 = -1
    params = match.Parameters(is_L2=True,
                              denominator=1,
                              edgeThresh=8,
                              lambda1=lambda1,
                              lambda2=lambda2,
                              K=K,
                              maxIter=4)
    # create match instance
    is_color = True if R1.shape[-1] == 3 else False
    m = match.Match(R1, R2, is_color)
    m.SetDispRange(mi, ma)
    m = match.fix_parameters(m, params, K, lambda_, lambda1, lambda2)
    disparity = m.kolmogorov_zabih()

    return disparity

def displayDispImg(disparity, OCCLUDED=int(2 ** 31 - 1)):
    """Display the disparity result of the graph cut as an image. Blue color corresponds to OCCLUDED"""
    dispImg = np.zeros((disparity.shape[0], disparity.shape[1],3), dtype=np.uint8)
    dispImg[:, :, 0] = 255
    dispImg[:, :, 1] = 255
    dispImg[:, :, 2] = 0

    flat = disparity.flatten()
    print(flat)
    flat = np.unique(flat)
    print(flat)
    flat.sort()

    minVal = flat[0]
    maxVal = flat[-2]
    print(minVal, maxVal)

    for i in range(disparity.shape[0]):
        for j in range(disparity.shape[1]):
            if disparity[i,j] != OCCLUDED:
                c = 255 - 255 * (disparity[i,j] - minVal) / (maxVal - minVal)
                print(c)
                dispImg[i, j, 0] = c
                dispImg[i, j, 1] = c
                dispImg[i, j, 2] = c
    return dispImg

def viewInterpolation(imgLpath, imgRpath, mi, ma, resize, scale):
    """Using precedent methods to compute view interpolation"""
    print("Reading images...")
    left = cv2.imread(imgLpath)
    right = cv2.imread(imgRpath)

    print("Compute keypoints matching...")
    correct_kpts1, correct_kpts2, matches_image, F = keypointsMatching(left, right)
    cv2.imshow("key pointsmatching", matches_image)
    cv2.waitKey(0)

    print("Computing rectification...")
    R1, R2 = rectification(left, right, correct_kpts1, correct_kpts2, F)
    cv2.imshow("rectified_left", R1)
    cv2.imshow("rectified_right", R2)
    cv2.imwrite("./results/rectifiedL.png", R1)
    cv2.imwrite("./results/rectifiedR.png", R2)
    cv2.waitKey(0)

    if resize:
        print("Resizing pictures...")
        R1 = cv2.resize(R1, dsize=None, fx=scale, fy=scale)
        R2 = cv2.resize(R2, dsize=None, fx=scale, fy=scale)
        cv2.imwrite("./results/rectifiedL.png", R1)
        cv2.imwrite("./results/rectifiedR.png", R2)

    print("Computing disparity...")
    disparity = stereoMatching(R1, R2, mi, ma)
    np.save("./results/dispArray.npy", disparity)
    dispImg = displayDispImg(disparity)
    cv2.imshow("Disparity", dispImg)
    cv2.imwrite("./results/dispImage.png", dispImg)
    cv2.waitKey(0)

    print("Computing interpolation...")
    ir = geometry.interpolate(1.5, R1, R2, disparity)
    cv2.imshow("interpolated view", ir)
    cv2.waitKey(0)
    cv2.imwrite("./results/interpolated.png", ir)
    print("Save successfully interpolated view !")

if __name__ == '__main__':
    # Choosing images to rectify
    imgLpath = './images/perra_7.jpg'
    imgRpath = './images/perra_8.jpg'
    # Setting disparity range
    dispMin = -11
    dispMax = 7
    # If rescale is True, the size of rectified images will be multiplied by scale
    rescale = True
    scale = 0.25
    # Compute view interpolation.
    """Running time with these parameters shouldn't exceed 5 minutes."""
    viewInterpolation(imgLpath, imgRpath, dispMin, dispMax, rescale, scale)


from authorization import authorize, bot, youtube
from telegram_bot import start
import asyncio
from aiogram import Dispatcher
import pandas as pd
import models
from sklearn.metrics.pairwise import cosine_similarity
from database import get_ratings, get_top_rated_tracks
import yt_dlp
import os

sp = authorize()
def get_track_info(track_id):
  track_info = sp.track(track_id)
  track_name = track_info.name
  artists = track_info.artists
  artist_names = [artist.name for artist in artists]

  # Создание словаря с информацией о треке
  track_data = {
    'track_name': track_name,
    'artist_names': artist_names,
    'track_id': track_id
  }
  return track_data

def search_track(query):

  res = sp.search(query, limit=1)

  if res[0].items:
    track = res[0].items[0]
    return {
      'id': track.id,
      'name': track.name,
      'artist': track.artists[0].name
    }

def get_similar(track_id):
  results = sp.recommendations(track_ids=track_id, limit=1)
  recommended_tracks = []

  for track in results.tracks:
    recommended_tracks.append({
      'id': track.id
    })

  return recommended_tracks

def youtube_search(query):

  request = youtube.search().list(part="snippet", q=query, maxResults=1)

  response = request.execute()

  return response['items'][0]

def download_audio(url):

  ydl_opts = {
    'format': 'bestaudio/best',
    'outtmpl': 'temp.%(ext)s',
    'postprocessors': [{
        'key': 'FFmpegExtractAudio',
        'preferredcodec': 'mp3',
        'preferredquality': '192',
    }],
    'keepvideo': True
  }

  with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    info = ydl.extract_info(url, download=True)

  title = info['title']
  os.rename('temp.mp3', f'{title}.mp3')

  return f'{title}.mp3'

# Функция для получения похожих пользователей
def find_similar_users(df, user_id, user_sim, k=5):
  user_index = df[df['user_id'] == user_id].index[0]
  user_sim_scores = user_sim[user_index]
  top_similar_users = user_sim_scores.argsort()[::-1][:k]
  similar_user_ids = df.iloc[top_similar_users]['user_id'].tolist()
  return similar_user_ids

# Функция для генерации рекомендаций
def generate_recs(user_id, k=5):
  # Загружаем данные из БД
  df = get_ratings()
  top_tracks = get_top_rated_tracks(user_id=user_id)

  similar_tracks = []
  for track in top_tracks:
    similar_tracks += get_similar(track)
  user_id = int(user_id)

  # Преобразуем в матрицу пользователей-треков
  pivot_table = df.pivot(index='user_id', columns='track_id', values='rating').fillna(0)
  # Вычисляем матрицу сходства
  user_sim = cosine_similarity(pivot_table)
  # Находим похожих пользователей
  similar_users = find_similar_users(df, user_id, user_sim, k)
  # Среднее рейтингов похожих пользователей
  scores = pivot_table.loc[similar_users].mean(axis=0)
  # Сортируем и возвращаем топ-5
  scores = scores.sort_values(ascending=False)[:5].index.values
  # Сортируем и сохраняем рекомендации в словарь с популярностью
  recommendations = {}
  for track in scores:
    popularity = sp.track(track).popularity
    recommendations[track] = popularity

  # Добавляем похожие треки в словарь рекомендаций
  for track in similar_tracks:
    track = track['id']
    popularity = sp.track(track_id=track).popularity
    recommendations[track] = popularity

  # Сортируем словарь по популярности и выбираем топовые рекомендации
  top_recommendations = sorted(recommendations.items(), key=lambda x: x[1], reverse=True)[:5]
  recs = []

  for track_id, popularity in top_recommendations:
      track_data = get_track_info(track_id)
      # Добавление информации в список top_recommendations
      recs.append(track_data)
  return recs
dp = Dispatcher(bot)
start(bot, dp)
if __name__ == '__main__':
  asyncio.run(dp.start_polling())


# https://github.com/AmusementClub/vs-mlrt/blob/7856cd3aab7111345475f7d526fe75ef81e198c8/scripts/vsmlrt.py

import vapoursynth as vs
from vapoursynth import core
from .vfi_inference import vfi_frame_merger

def cain_trt(
    clip: vs.VideoNode,
    device_id: int = 0,
    num_streams: int = 1,
    engine_path: str = "",
):
    initial = core.std.Interleave([clip] * (2 - 1))

    terminal = clip.std.DuplicateFrames(frames=clip.num_frames - 1).std.Trim(first=1)
    terminal = core.std.Interleave([terminal] * (2 - 1))

    clips = [initial, terminal]

    output = core.trt.Model(
        clips, engine_path, device_id=device_id, num_streams=num_streams
    )

    # using FrameEval is much faster than calling core.akarin.Expr
    output = vfi_frame_merger(initial, output)

    return core.std.Interleave([clip, output])


# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def zigzagLevelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """

        if not root:
            return None
        level=[root]
        res=[[root.val]]
        # i to keep track of alternative levels
        i=1

        while level:
            queue=[]
            for node in level:
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            # next level is the queue of children
            level=queue
            # break if there are no further levels
            if not level:
                break

            # next level is to be reversed if i is odd
            if i%2!=0:
                res.append(nodes.val for nodes in reversed(level))
            else:
                res.append(nodes.val for nodes in level)
            i+=1

        return res


import object.map.terrain as terrain
from object.map.hexagon import Hexagon

from view.transform import hex_to_cartesian

class Tile:

    def __init__(self, _position, _colors):

        self.position = _position
        self.colors = _colors

        self.entity = None
        self.terrain = terrain.Void()
        self.layout = Hexagon(_position, _colors)

        self.hover = False
        self.selected = False

    def draw(self, projection_view_matrix, shaders):
        states = {
            "ON": self.selected,
            "HOVER": self.hover
        }

        self.layout.draw(projection_view_matrix, shaders["color"], states)

        if self.entity:
            self.entity.draw(projection_view_matrix, shaders["texture"])

        # print(self.terrain.type)
        if self.terrain.type != terrain.types["VOID"]:
            # print("hey")
            self.terrain.draw(projection_view_matrix, shaders["texture"])

    def set_entity(self, entity):
        self.entity = entity
        if not self.entity.is_moving:
            self.entity.position = self.position

    def set_terrain(self, terrain):
        self.terrain = terrain
        self.terrain.position = self.position

    def remove_entity(self):
        self.entity = None

    def update(self, _dt, keys):
        if self.entity:
            # try:
            return self.entity.update(_dt, keys)
            # except Exception as e:
            #     print("Can't update the entity : " + str(e))


from flask import Flask, request
import json
import urllib2
import os
from argparse import ArgumentParser
from flask import make_response
app = Flask(__name__)

def import_class(name):
    mod = __import__(".".join(name.split(".")[:-1]))
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod

@app.route('/')
def index():
    """

    NLPY Server
    ---

    To invoke methods, access "http://[host]/[class]?[parameters]"

    # Example

        http://[host]/nlpy.basic.DefaultTokenizer?input=I'll+buy.

        http://[host]/nlpy.ex.keyword.DefaultKeywordExtractor?input=I%27ll+buy+a+cup.

    # Development

    A method named "serve" must exist in the class, which accepts one parameter.

    Like this one:

    ---------------------------------------------
        class SampleTokenizer(object):

            def tokenize(self, input):
                return input.split()

            @staticmethod
            def serve(param):
                return SampleTokenizer().tokenize(param['input'])
    ---------------------------------------------

    Good luck.

    """
    return index.__doc__.replace("[host]", request.host)

@app.route('/<path:class_name>', methods=['GET', 'POST'])
def runner(class_name):
    try:
        mod = import_class(class_name)
    except AttributeError:
        return "Error:
 %s can not be found" % class_name
    except ImportError:
        return "Error:
 %s can not be found" % class_name

    param = {}
    for k in request.form:
        param[k] = request.form[k].encode('utf-8', "i")
    for k in request.args:
        param[k] = urllib2.unquote(request.args[k].encode('utf-8', "i"))

    result = json.dumps(mod.serve(param))
    resp = make_response(result)
    resp.headers['Access-Control-Allow-Origin'] = "*"
    return resp

def preload():
    if os.path.exists(".nlpyserverload"):
        with open(".nlpyserverload") as fpreload:
            for l in fpreload.xreadlines():
                class_name = l.strip()
                if not class_name: continue

                try:
                    mod = import_class(class_name)
                except AttributeError:
                    raise Exception("Error: %s can not be found" % class_name)
                except ImportError:
                    raise Exception("Error: %s can not be found" % class_name)

                if "load" not in dir(mod):
                    raise Exception("Error: %s do not have static method 'load'" % class_name)

                mod.load()

if __name__ == '__main__':

    ap = ArgumentParser(__name__)
    ap.add_argument("--port", type=int, default=8077)
    ap.add_argument("--debug", type=bool, default=True)
    ap.add_argument("--reload", type=bool, default=False)
    args = ap.parse_args()

    # Pre-load
    preload()

    # Run server instance
    app.run(port=args.port, debug=args.debug, host="0.0.0.0", use_reloader=args.reload)


from flask import Flask, request, render_template
import json
from sklearn.feature_extraction.text import TfidfVectorizer
import pickle
import pandas as pd

app = Flask(__name__)

def analyse_text(text) :
	df = pd.read_csv('df_sentiment_analysis.csv')
	tfidf_vect = TfidfVectorizer(max_features=5000)
	tfidf_vect.fit(df['text_final'])
	loaded_model = pickle.load(open('sentiment_analysis_model.sav', 'rb'))
	return loaded_model.predict(tfidf_vect.transform([text]))[0]

def analyse(text):

	sentiment = analyse_text(text)
	if sentiment == 1:
		return render_template('index.html', result = '😀')
	elif sentiment == 0:
		return render_template('index.html', result = '😐')
	else:
		return render_template('index.html', result = '☹')

@app.route('/', methods=['GET', 'POST'])
def index():
	if request.method == 'POST':
		details = request.form
		return analyse(details['text-to-analyse'])

	return render_template('index.html', result = '')

if __name__ == '__main__':
	app.run(host='0.0.0.0')


PRIORITIES = {
    'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14,
    'o': 15, 'p': 16, 'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21, 'v': 22, 'w': 23, 'x': 24, 'y': 25, 'z': 26,

    'A': 27, 'B': 28, 'C': 29, 'D': 30, 'E': 31, 'F': 32, 'G': 33, 'H': 34, 'I': 35, 'J': 36, 'K': 37, 'L': 38, 'M': 39,
    'N': 40, 'O': 41, 'P': 42, 'Q': 43, 'R': 44, 'S': 45, 'T': 46, 'U': 47, 'V': 48, 'W': 49, 'X': 50, 'Y': 51, 'Z': 52,
}

total_priority = 0

def in_half(line): # Split each line into 2 halves
    half1, half2 = line[:(len(line)//2)], line[(len(line)//2):]
    return half1, half2

def common_items(pocket1, pocket2):
    common = ''
    for item in pocket1:
        if item in pocket2 and not item in common:
            common += item
    return common

with open("/Users/nicholas/AoC2022/day3/input.txt") as f:
    for line in f:
        line = line.strip()
        pocket1, pocket2 = in_half(line)[0], in_half(line)[1]
        item = common_items(pocket1, pocket2)

        total_priority += PRIORITIES[item]

print(total_priority)


from config.db_config import pg_config
import psycopg2
class RequestCompletedData:
    def __init__(self):
        connection_url = "dbname=%s user=%s host=%s password=%s" % (pg_config['dbname'],
                                                            pg_config['user'],
                                                            pg_config['host'],
                                                            pg_config['passwd'])

        self.conn = psycopg2._connect(connection_url)

    def getAllRequestsCompleted(self):
        cursor = self.conn.cursor()
        query = "select * from request_completed;"
        cursor.execute(query)
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getRequestCompletedByID(self, request_completed_id):
        cursor = self.conn.cursor()
        query = "select * from request_completed where request_completed_id = %s;"
        cursor.execute(query, (request_completed_id,))
        result = cursor.fetchone()
        return result

    def getRequestCompletedByDateResolved(self, date_resolved):
        cursor = self.conn.cursor()
        query = "select * from request_completed where date_resolved = %s;"
        cursor.execute(query, (date_resolved,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getRequestCompletedByOrderType(self, order_type):
        cursor = self.conn.cursor()
        query = "select * from order_type where order_type = %s;"
        cursor.execute(query, (order_type,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getRequestCompletedBySupplierId(self, supplier_id):
        cursor = self.conn.cursor()
        query = "select request_completed_id, resource_name, date_resolved, price, order_type from request_completed natural inner join supplier natural inner join resource where supplier_id = %s;"
        cursor.execute(query, (supplier_id,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getRequestCompletedByVictimId(self, victim_id):
        cursor = self.conn.cursor()
        query = "select victim_id, company_name, resource_name, date_resolved, order_type, price, total from request_completed natural inner join disaster_victim natural inner join supplier natural inner join resource natural inner join supplies natural inner join account where victim_id = %s;"
        cursor.execute(query, (victim_id,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getRequestCompletedByResourceId(self, resource_id):
        cursor = self.conn.cursor()
        query = "select request_completed_id, resource_name, price, quantity, total, date_resolved, order_type from request_completed natural inner join resource where resource_id = %s;"
        cursor.execute(query, (resource_id,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getRequestCompletedByCategoryId(self, category_id):
        cursor = self.conn.cursor()
        query = "select * from request_completed natural inner join category where category_id = %s;"
        cursor.execute(query, (category_id,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getRequestCompletedByPrice(self, price):
        cursor = self.conn.cursor()
        query = "select * from request_completed where price = %s;"
        cursor.execute(query, (price,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getResourceNameFromRequestCompleted(self):
        cursor = self.conn.cursor()
        query = "select resource_name from request_completed natural inner join resource;"
        cursor.execute(query)
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getSaleRequestCompletedByResourceId(self, resource_id):
        cursor = self.conn.cursor()
        query = "select resource_name, date_resolved, price, company_name, victim_id from request_completed natural inner join resource natural inner join supplier where resource_id = %s and order_type = 'Sale';"
        cursor.execute(query, (resource_id,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def getTransactionSaleRequestCompletedByResourceId(self, resource_id):
        cursor = self.conn.cursor()
        query = "select request_completed_id, resource_name, date_resolved, price, company_name, victim_id from request_completed natural inner join resource natural inner join supplier where resource_id = %s and order_type = 'Sale';"
        cursor.execute(query, (resource_id,))
        result = []
        for row in cursor:
            result.append(row)
        return result

    def insertSale(self, date_resolved, supplier_id, victim_id, resource_id, price, quantity):
        cursor = self.conn.cursor()
        query_1 = "insert into request(date_submited, resource_id) values (%s, %s) returning request_id;"
        cursor.execute(query_1, (date_resolved, resource_id))
        request_id = cursor.fetchone()[0]
        query_2 = "insert into requestby( victim_id, request_id) values (%s, %s);"
        cursor.execute(query_2, (victim_id, request_id))
        query_get_quantity = "Select quantity from resource where resource_id = %s;"
        cursor.execute(query_get_quantity, (resource_id))
        actual_quantity = cursor.fetchone()[0]
        actual_quantity = int(actual_quantity)
        query_3 = "update resource set quantity = %s;"
        sub = actual_quantity - quantity
        sub = str(sub)
        cursor.execute(query_3, (sub,))
        query_4 = "insert into request_completed(request_id, date_resolved, order_type, supplier_id, victim_id, resource_id, price) values (%s, %s, 'Sale', %s, %s, %s, %s) returning request_completed_id;"
        cursor.execute(query_4, (request_id, date_resolved, supplier_id, victim_id, resource_id, (price * quantity)))
        request_completed_id = cursor.fetchone()[0]
        self.conn.commit()
        return request_completed_id

    def getAllSales(self):
        cursor = self.conn.cursor()
        query = "select * from request_completed where order_type = 'Sale';"
        cursor.execute(query)
        result = []
        for row in cursor:
            result.append(row)
        return result

    def insertDonation(self, date_resolved, supplier_id, victim_id, resource_id, quantity,isavailable):
        cursor = self.conn.cursor()
        query_1 = "insert into request(date_submited, resource_id) values (%s, %s) returning request_id;"
        cursor.execute(query_1, (date_resolved, resource_id))
        print("done1")
        request_id = cursor.fetchone()[0]
        query_2 = "insert into requestby( victim_id, request_id) values (%s, %s);"
        cursor.execute(query_2, (victim_id, request_id))
        print("done2")
        query_3 = "update resource set quantity = %s, isavailable=%s where resource_id=%s;"
        cursor.execute(query_3, (quantity,isavailable,resource_id))
        print("done3")
        query_4 = "insert into request_completed(request_id, date_resolved, order_type, supplier_id, victim_id, resource_id, price) values (%s, %s, 'Donation', %s, %s, %s, %s) " \
                  "returning request_completed_id;"
        price = 0.00*float(quantity)
        cursor.execute(query_4, (request_id, date_resolved, supplier_id, victim_id, resource_id, price))
        request_completed_id = cursor.fetchone()[0]
        self.conn.commit()
        return request_completed_id

    def getAllDonation(self):
        cursor = self.conn.cursor()
        query = "select * from request_completed where order_type = 'Donation';"
        cursor.execute(query)
        result = []
        for row in cursor:
            result.append(row)
        return result


import argparse
import shutil
import sys
import os

from faults.flp import FLP
from faults.jbe import JBE
from faults.jmp import JMP
from faults.nop import NOP
from faults.z1b import Z1B
from faults.z1w import Z1W
from utils import check_or_fail

class ExecConfig:
    """Keeps the configuration variables."""

    def __init__(self, infile, outfile, arch, word_length):
        super().__init__()
        self.infile = infile
        self.outfile = outfile
        self.arch = arch
        self.word_length = word_length

def main(argv):
    fault_models = {'FLP': FLP, 'Z1B': Z1B, 'Z1W': Z1W, 'NOP': NOP, 'JMP': JMP, 'JBE': JBE}

    # Collect parameters
    parser = argparse.ArgumentParser(description='Software implemented fault injection tool',
                                     formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument('-i', '--infile', type=str, metavar='INFILE', required=True, help='path to the source file')
    parser.add_argument('-o', '--outfile', type=str, metavar='OUTFILE', required=True,
                        help='path to the destination file')
    parser.add_argument('-w', '--wordsize', type=int, metavar='WORDSIZE', required=False,
                        help='number of bytes in a word')
    parser.add_argument('-a', '--arch', type=str, metavar='ARCHITECTURE', required=False, choices=['x86', 'arm'],
                        help='architecture of the executable (x86 or arm)')
    parser.add_argument('-g', '--graphical', action='store_true', required=False,
                        help='open a window comparing the input and the output')
    parser.add_argument('-f', '--fromfile', type=str, metavar='FILE_MODELS', required=False,
                        help='read the faults models from a file instead of command line')
    parser.add_argument('fault_models', nargs='*', metavar='FAULT_MODEL',
                        help='one fault model followed by its parameters\n' +
                             'The possible models are :\n' + "\n".join([s.docs for s in fault_models.values()]) +
                             '\naddr can be a number or a range (number-number)')
    args = parser.parse_args(argv[1:])
    check_or_fail(args.wordsize is None or args.wordsize > 0, "Word size must be positive")

    # General configuration
    config = ExecConfig(os.path.expanduser(args.infile), os.path.expanduser(args.outfile), args.arch, args.wordsize)

    # Fault models asked
    if args.fromfile is not None:
        with open(args.fromfile, 'r') as ff:
            args.fault_models.extend(ff.read().split())
    check_or_fail(len(args.fault_models) >= 1, "No fault models provided")
    fm_list = []
    indices = [i for i, x in enumerate(args.fault_models) if fault_models.get(x) is not None]
    indices.append(len(args.fault_models))

    for i in range(len(indices) - 1):
        n = indices[i]
        fm_name = args.fault_models[n]
        fm_type = fault_models.get(fm_name)
        if fm_type is not None:
            check_or_fail(indices[i + 1] - n - 1 == fm_type.nb_args, "Wrong number of parameters for " + fm_name)
            ar = []
            for j in range(fm_type.nb_args):
                ar.append(args.fault_models[n + 1 + j])
            fm_list.append(fm_type(config, ar))

    # Check that the faults do not overlap and do not write outside the end of the file
    mem = {}
    max_bits = os.stat(config.infile).st_size * 8
    for f in fm_list:
        for m in f.edited_memory_locations():
            check_or_fail(0 <= m < max_bits, "Address outside file content : byte " + hex(m // 8))
            check_or_fail(mem.get(m) is None, "Applying two fault models at the same place : byte " + hex(m // 8))
            mem[m] = f.name

    # Duplicate the input and then apply the faults
    shutil.copy(config.infile, config.outfile)
    with open(config.outfile, "r+b") as file:
        for f in fm_list:
            f.apply(file)

    # Open a window for comparing the Input/Output with the faults highlighted
    if args.graphical:
        colors = {'FLP': 'turquoise', 'Z1B': 'green', 'Z1W': 'green2', 'NOP': 'red', 'JMP': 'orange', 'JBE': 'tomato'}
        import diff_ui
        diff_ui.diff_ui(config.infile, config.outfile, fm_list, colors)

if __name__ == '__main__':
    main(sys.argv)


import numpy as np
import pandas as pd
from bamboo_lib.connectors.models import Connector
from bamboo_lib.models import AdvancedPipelineExecutor
from bamboo_lib.models import BasePipeline
from bamboo_lib.models import Parameter
from bamboo_lib.models import PipelineStep
from bamboo_lib.steps import LoadStep

class DownloadStep(PipelineStep):
    def run_step(self, prev, params):
        return self.connector.download(params=params)

class ExtractStep(PipelineStep):
    def run_step(self, prev, params):
        df = pd.read_csv(prev)

        # Belgium-Luxembourg pair does not have `iso2`
        df['iso2'] = df['iso2'].fillna('').astype(str)

        # Belgium and Luxembourg don't have individual entries, so we'll add
        # those manually
        manual_entries = [
            {
                'id': 'eubel', 'id_num': 56,
                'iso3': 'bel', 'iso2': 'be',
                'continent': 'eu',
                'color': '#752277',
                'name': 'Belgium',
                'export_val': np.nan,
            },
            {
                'id': 'eulux', 'id_num': 442,
                'iso3': 'lux', 'iso2': 'lu',
                'continent': 'eu',
                'color': '#752277',
                'name': 'Luxembourg',
                'export_val': np.nan,
            }
        ]

        df = df.append(pd.DataFrame(manual_entries), ignore_index=True)
        df.drop('export_val', axis=1, inplace=True)

        return df

class DimCountriesPipeline(BasePipeline):
    @staticmethod
    def pipeline_id():
        return 'countries-dimension-pipeline'

    @staticmethod
    def name():
        return 'Countries Dimension Pipeline'

    @staticmethod
    def description():
        return 'Processes countries data'

    @staticmethod
    def website():
        return 'http://datawheel.us'

    @staticmethod
    def parameter_list():
        return [
            Parameter(label="Source connector", name="source_connector", dtype=str, source=Connector),
            Parameter(label="DB connector", name="db_connector", dtype=str, source=Connector),
        ]

    @staticmethod
    def run(params, **kwargs):
        source_connector = Connector.fetch(params.get("source_connector"), open("etl/conns.yaml"))
        db_connector = Connector.fetch(params.get("db_connector"), open("etl/conns.yaml"))

        dtype = {
            'id':            'String',
            'id_num':        'UInt32',
            'iso3':          'String',
            'iso2':          'String',
            'continent':     'String',
            'color':         'String',
            'name':          'String',
        }

        download_data = DownloadStep(connector=source_connector)
        extract_step = ExtractStep()
        load_step = LoadStep(
            "dim_shared_countries", db_connector, if_exists="append",
            dtype=dtype, pk=['id_num']
        )

        pp = AdvancedPipelineExecutor(params)
        pp = pp.next(download_data).next(extract_step).next(load_step)

        return pp.run_pipeline()

if __name__ == '__main__':
    pipeline = DimCountriesPipeline()
    pipeline.run({
        'source_connector': 'dim-countries',
        'db_connector': 'clickhouse-remote',
    })


print("Welcome to pizza deliveries!")
size = input("What size pizza do you want? S, M or L \n")
pepperoni = input("Do you want pepperoni? Y or N \n")
cheese = input("Do you want cheese? Y or N \n")
bill = 0

if size == "S":
    bill += 15
    #print(f"Small size is for {bill}")
    if pepperoni == "Y":
        bill += 2
        #print(f"Small size + pepperoni is for {bill}")
elif size == "M":
    bill += 20
    #print(f"Medium size is for {bill}")
    if pepperoni == "Y":
        bill += 3
        #print(f"Medium size + pepperoni is for {bill}")
else:
    bill += 25
    #print(f"Large size is for {bill}")
    if pepperoni == "Y":
        bill += 3
        #print(f"Large size + pepperoni is for {bill}")

if cheese == "Y":
    bill += 1
    #print(f"Your total bill is {bill}")
#else:
    #print(f"Your total bill is {bill}")
print(f"Your final bill is {bill}")


from keras import backend as K
import keras
from keras.layers import Layer

import tensorflow as tf

from keras import backend as K

class Attention(Layer):
    def __init__(self, units):

        self.units = units

        super(Attention, self).__init__()

    def build(self, input_shape):

        # Need to know input shapes in order to register
        # number of parameters (weights)

        # These undergo transformations in call(), so can't
        # just build these here and request input_shape
        # and output_shape

        self.W1 = keras.layers.Dense(self.units, activation = 'relu', use_bias = True)
        self.W2 = keras.layers.Dense(self.units, activation = 'relu', use_bias = True)
        self.V = keras.layers.Dense(1, activation = None, use_bias = True)

        super(Attention, self).build(input_shape)

    def compute_output_shape(self, input_shape):

        #Neccessary - automatic inference, gets the shape wrong!

        shape_hidden, shape_values = input_shape
        batch_size = shape_hidden[0]

        shape_context = (batch_size, shape_hidden[1])
        shape_attention_weights = (batch_size, shape_values[1], 1)

        return [shape_context, shape_attention_weights]

    def compute_mask(self, inputs, mask = None):
        return mask

    def count_params(self):
        # A placeholder
        return 42

    def call(self, input):
        query, values = input

        # query = K.print_tensor(query, 'query')
        # values = K.print_tensor(values, 'values')

        # hidden shape == (batch_size, hidden size)
        # hidden_with_time_axis shape == (batch_size, 1, hidden size)
        # we are doing this to perform addition to calculate the score
        hidden_with_time_axis = K.expand_dims(query, 1)

        # score shape == (batch_size, max_length, hidden_size)
        score = self.V(K.tanh(
            self.W1(values) + self.W2(hidden_with_time_axis)))

        # score = K.print_tensor(score, 'score')

        # attention_weights shape == (batch_size, max_length, 1) # 32?
        # we get 1 at the last axis because we are applying score to self.V
        attention_weights = K.softmax(score, axis=1)

        # context_vector shape after sum == (batch_size, hidden_size)
        context_vector = attention_weights * values
        context_vector = K.sum(context_vector, axis=1)

        attention_weights = K.print_tensor(attention_weights, 'attention_weights')

        return [context_vector, attention_weights]

class SoftRecoLayer(Layer):

    def __init__(self, units, **kwargs):
        self.units = units

        self.I = tf.eye(4)
        self.epsilon = 1E-9

        u = tf.constant([[-1, 0, 0, 0]] + 3 * [[0, -1, -1, -1]], tf.float32)

        # Reshape to add batch dimension
        self.u = tf.reshape(u, (1, 4, 4))

        self.attention = Attention(self.units)

        super(SoftRecoLayer, self).__init__(**kwargs)

    def build(self, input_shape):

        super(SoftRecoLayer, self).build(input_shape)

    def call(self, inputs):

        query, values, fourVectorInputs = inputs

        attention_outputs, attention_weights = self.attention([query, values])

        # attention_outputs = K.print_tensor(attention_outputs, 'attention_outputs')
        # attention_weights = K.print_tensor(attention_weights, 'attention_weights')

        restFrame4Vectors = self.calculateWeightedRestFrames(attention_weights, fourVectorInputs)

        # restFrame4Vectors = K.print_tensor(restFrame4Vectors, 'restFrame4Vectors')

        boosts = self.calculateBoosts(restFrame4Vectors)

        fourVectorOutputs = self.applyBoost(boosts, fourVectorInputs)

        return [attention_outputs, attention_weights, fourVectorOutputs]

    def calculateWeightedRestFrames(self, weights, fourVectors):

        # Broadcast weights so that the weight is 'tiled' across each 4v component

        weights = tf.broadcast_to(weights, tf.shape(fourVectors))

        return tf.einsum('Bij, Bij -> Bj', weights, fourVectors)

    def applyBoost(self, boost, fourVectors):

        # (batch, 4, 4) . (batch, n4v, 4) -> (batch, n4v, 4)
        return tf.einsum('Bij, Bki -> Bkj', boost, fourVectors)

    def calculateBoosts(self, restFrame4Vector):

        # Uses the same formulation as arXiv:1812.09722

        # First element (slice to maintain last axis)
        E = restFrame4Vector[:,:1]

        # Last three elements
        p = restFrame4Vector[:,1:]

        beta = p / E

        absBeta = tf.expand_dims(tf.sqrt(tf.reduce_sum(beta ** 2, axis = -1)), -1)

        n = beta / absBeta

        gamma = 1. / tf.sqrt(1. - absBeta ** 2 + self.epsilon)

        e = tf.concat([tf.ones_like(E, tf.float32), -n], -1)

        l1 = self.u + tf.reshape(gamma, (-1, 1, 1))
        l2 = (self.u + 1) * tf.reshape(absBeta, (-1, 1, 1)) - self.u
        l3 = tf.einsum('Bi, Bj -> Bij', e, e)

        lamb = self.I + l1 * l2 * l3

        return lamb

    def compute_output_shape(self, input_shape):

        attentionOutputShape = self.attention.compute_output_shape(input_shape[:-1])

        return attentionOutputShape + [input_shape[-1]]

    def compute_mask(self, inputs, mask = None):

        # Attention outputs has no mask
        # Attention weights has same mask as input to attention
        # Four vectors have same mask as input to attention

        return [None, mask[-1], mask[-1]]

def testAttention():

    inputs = keras.layers.Input((10, 18))
    tracks = keras.layers.Masking(mask_value = -999, name = 'mask')(inputs)

    tracks, hidden = keras.layers.GRU(3, activation = 'relu', return_state = True, return_sequences = True)(tracks)
    context_vector, attention_weights = Attention(128)([hidden, tracks])

    out = keras.layers.Dense(1)(context_vector)

    model = keras.models.Model(inputs = inputs, outputs = out)

    model.summary(line_length = 150)

    testInput = np.random.normal(0, 1, size = (123, 10, 18))
    testTargets = np.random.randint(0, 2, size = 123)

    model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
    model.fit(testInput, testTargets, epochs = 1)

def testReco():

    inputFeatures = keras.layers.Input((10, 18))
    inputFourVectors = keras.layers.Input((10, 4))

    inputFeaturesMasked = keras.layers.Masking(mask_value = -999, name = 'maskFeatures')(inputFeatures)
    inputFourVectorsMasked = keras.layers.Masking(mask_value = -999, name = 'maskFourVectors')(inputFourVectors)

    tracks, hidden = keras.layers.GRU(1, activation = 'relu', return_state = True, return_sequences = True)(inputFeaturesMasked)
    attention_outputs, attention_weights, fourVectorOutputs = SoftRecoLayer(32)([hidden, tracks, inputFourVectorsMasked])

    tracks = keras.layers.Concatenate()([tracks, fourVectorOutputs])

    tracks = keras.layers.GRU(1, activation = 'relu', return_state = False, return_sequences = False)(tracks)

    out = keras.layers.Dense(1)(tracks)

    model = keras.models.Model(inputs = [inputFeatures, inputFourVectors], outputs = out)

    model.summary(line_length = 200)

    testTargets = np.random.randint(0, 2, size = 123)
    testInputFeatures = np.random.normal(0, 1, size = (123, 10, 18))

    import uproot
    import pandas as pd

    dataFile = '/Users/MBP/GoogleDrive/Bu2pipipi_sqDalitz_withVars.h5'
    data = pd.read_hdf(dataFile)

    fourVectorsInput = data[['pip_0_E', 'pip_0_PX', 'pip_0_PY', 'pip_0_PZ']][:123 * 10].values
    fourVectorsInput = fourVectorsInput.reshape((123, 10, 4))

    model.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])
    model.fit([testInputFeatures, fourVectorsInput], testTargets, epochs = 100)

if __name__ == '__main__':

    import numpy as np

    from pprint import pprint

    testReco()
    # testAttention()