#!/usr/bin/python python
# -*- coding: utf-8 -*-

class WaBot():
    # Libraries importieren
    import urllib.request
    import urllib.parse
    import time
    from selenium import webdriver
    from selenium.webdriver.support import expected_conditions as EC
    from selenium.webdriver.support.ui import WebDriverWait
    from selenium.webdriver.common.by import By

    from bs4 import BeautifulSoup

    def __init__(self, data_dir):
        # Webdriver Einstellungen
        options = self.webdriver.ChromeOptions()
        options.add_argument("--user-data-dir=" + data_dir)
        #options.add_argument("--window-size=1920,5000")
        #options.binary_location = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"
        #options.binary_location = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
        self.driver = self.webdriver.Chrome(options=options)
        #self.driver.set_window_size(512, 100000)

        # Seite aufrufen und QR-Code Scannen
        self.driver.get('https://web.whatsapp.com/')
        #self.driver.execute_script("document.body.style.zoom='15%'")# Seite verkleinern, damit alle Chats angezeigt werden und so auch alle ausgelesen werden können
        self.time.sleep(3)

        self.wait = self.WebDriverWait(self.driver, 100)

    def stop(self):
        self.driver.close()

    # Funktionen
    def select_user(self, name=False, index=False):
        # User auswählen
        element  = self.wait.until(self.EC.element_to_be_clickable((self.By.XPATH, '//span[@title = "{}"]'.format(name))))
        element.click()
        #self.driver.find_element_by_xpath('//span[@title = "{}"]'.format(name)).click()
        #self.driver.execute_script('document.querySelector(\'span[title="' + name + '"]\').click()')
        #self.time.sleep(0.5)

    def select_user_by_index(self, index = 0):
        pass

    def read_messages(self, group = False):
        print('#################################################')
        messages = self.driver.find_elements_by_css_selector('div._1_keJ div._1ays2 div.FTBzM')
        messages_dicts = []
        if messages:
            for message in messages[::-1]:
                message = self.BeautifulSoup(message.get_attribute("outerHTML"), "html.parser")#.prettify()
                classes = []
                for element in message.find_all(class_=True):
                    classes.extend(element["class"])
                    break

                #class_detector = message.text.split('FTBzM ')#[1].split('"')[0]
                #print(class_detector)
                # message in
                if "message-in" in classes:
                    #print("\n", message.select_one(".-N6Gq .woe4f"))
                     # Erste Nachricht
                    if message.select_one(".-N6Gq .woe4f"):
                        # Emojis richtig formatieren
                        print('+++++++++++++++++++')
                        text = str(message.select_one("._F7Vk span")).replace('<span>', '').replace('</span>', '')
                        if not '<img' in text:
                            text_message = message.select_one("._F7Vk span").text
                        else:
                            text_message = ''
                            for i in text.split('<img'):
                                part = i.split('alt="')
                                if len(part) > 1:
                                    text_message += part[1][0] + part[1].split('>')[-1]
                                elif len(part) == 1:
                                    text_message += part[0]

                        messages_dicts.append({"class": "message-in",
                                               "name" : message.select_one("._F7Vk").text,
                                               "message": text_message,
                                               "date": str(message.select_one(".-N6Gq .woe4f")).split('[')[1].split(']')[0]
                                })
                    # nächste Nachrichen
                    elif message.select_one(".-N6Gq .copyable-text"):
                        # Emojis richtig formatieren
                        text = str(message.select_one("._F7Vk span"))
                        text_message = ''
                        for i in text.split('<img'):
                            part = i.split('alt="')
                            if len(part) > 1:
                                text_message += part[1][0] + part[1].split('>')[-1]
                            elif len(part) == 1:
                                text_message += part[0]

                        messages_dicts.append({"class": "message-in",
                                               "name" : message.select_one(".-N6Gq .copyable-text")['data-pre-plain-text'].split("] ", 1)[1][:-2],
                                               "message": text_message,
                                               "date": str(message.select_one(".-N6Gq .copyable-text")).split('[')[1].split(']')[0]
                                })
                    elif message.select_one("._12pGw"):
                        messages_dicts.append({"class": "message-in",
                                               "name" : message.select_one("._F7Vk").text,
                                               "message": message.select_one('._12pGw').text,
                                               "date": message.select_one('._3MYI2 ._3fnHB').text,
                                               "info": "deleted message"
                            })
                    elif message.select_one("._1zGQT.a81-s"): # Sticker
                        messages_dicts.append({"class": "message-in",
                                               "name" : message.select_one("._1uQFN._18GNg._F7Vk").text,
                                               "message": False,
                                               "date": message.select_one('._3MYI2 ._3fnHB').text,
                                               "info": "sticker"
                            })

                # message out
                elif "message-out" in classes:
                    # Erste Nachricht
                    if str(message.select_one(".-N6Gq .copyable-text")) and message.select_one("._F7Vk span"):
                        # Emojis richtig formatieren
                        text = str(message.select_one("._F7Vk span"))
                        text_message = ''
                        for i in text.split('<img'):
                            part = i.split('alt="')
                            if len(part) > 1:
                                text_message += part[1][0] + part[1].split('>')[-1]
                            elif len(part) == 1:
                                text_message += part[0]

                        messages_dicts.append({"class": "message-out",
                                               "name" : False,
                                               "message" : text_message,
                                               "date" : str(message.select_one(".-N6Gq .copyable-text")).split('[')[1].split(']')[0],
                                               "status" : message.select_one('._370iZ span')['data-icon']
                                })
                    # nächste Nachrichen
                    elif message.select_one('._F7Vk span'):
                        # Emojis richtig formatieren
                        text = str(message.select_one("._F7Vk span"))
                        text_message = ''
                        for i in text.split('<img'):
                            part = i.split('alt="')
                            if len(part) > 1:
                                text_message += part[1][0] + part[1].split('>')[-1]
                            elif len(part) == 1:
                                text_message += part[0]

                        messages_dicts.append({"class": "message-out",
                                               "name" : False,
                                               "message": text_message,
                                               "date" : str(message.select_one(".-N6Gq .woe4f")).split('[')[1].split(']')[0],
                                               "status" : message.select_one('._370iZ span')['data-icon']
                                })
                    elif message.select_one("._1zGQT.a81-s"): # Sticker
                        messages_dicts.append({"class": "message-out",
                                               "name" : False,
                                               "message": False,
                                               "date": message.select_one('._3MYI2 ._3fnHB').text,
                                               "status": message.select_one('._370iZ span')['data-icon'],
                                               "info": "sticker"
                            })
                    elif message.select_one("._12pGw"):
                        messages_dicts.append({"class": "message-out",
                                               "name": False,
                                               "message": message.select_one('._12pGw').text,
                                               "date": message.select_one('._3MYI2 ._3fnHB').text,
                                               "info": "deleted message"
                            })
                # events
                else:
                    messages_dicts.append({"class": "event",
                                           "message": message.select_one('._F7Vk').text
                            })

                #print(message)
            print(messages_dicts)
                #break

    def send_img(self):
        pass

    def download_pp(self):
        pass

    def send_msg(self, msg):
        # Nachricht ins Textfeld eingeben
        self.driver.find_element_by_xpath('//div[@class = "_3u328 copyable-text selectable-text"]').send_keys(msg)
        # Nachricht absenden
        self.driver.execute_script('document.querySelector(\'span[data-icon="send"]\').click()')
        self.time.sleep(0.5)

    def read_data(self):
        self.time.sleep(2)
        chatfields = self.driver.find_elements_by_class_name('X7YrQ')
        if chatfields == []:
            print('Es wurde kein Chat gefunden...')
        else:
            self.main_data = {
                    'time': self.time.strftime('%d.%m.%Y-%H:%M:%S'),
                    }
            chats = []
            for field in chatfields:
                field = self.BeautifulSoup(field.get_attribute("outerHTML"), "html.parser")
                name = field.select_one("div._3H4MS span._19RFN").text
                if field.select_one("span._3NWy8 span._19RFN"):
                    is_group = False
                elif field.select_one('div._3H4MS span._19RFN'):
                    is_group = True

                zeit = field.select_one('div._0LqQ').text# Zeit
                dictionary = {
                    'name': name,
                    'is_group': is_group,
                    'time': zeit
                    }

                if is_group:
                    status = field.select_one('div._3VIru span[data-icon^="status"]')
                    if status:# letzte Nachricht von einem selber
                        dictionary['sender'] = False
                        dictionary['status'] = status.attrs['data-icon']# Status der eigenen letzten Nachricht
                        if field.select_one('span._1Wn_k'):
                            dictionary['last_message'] = field.select_one('span._1Wn_k').attrs['title'][1:-1] # Letzte Nachricht

                    else:
                        if field.select_one('div._2Bw3Q span._1Wn_k ._1ovWX._F7Vk'):
                            dictionary['sender'] = field.select_one('div._2Bw3Q span._1Wn_k span._1ovWX._F7Vk').text# Sender
                            if field.select_one('span._1Wn_k'):
                                dictionary['last_message'] = field.select_one('span._1Wn_k').attrs['title'][1:-1] # Letzte Nachricht

                        else:
                            if field.select_one('span._1_8_q > span'):
                                dictionary['event'] = field.select_one('span._1_8_q > span').text# Event

                    if field.select_one('div._0LqQ span div._1ZMSM span.P6z4j'):
                        dictionary['new_messages'] = int(field.select_one('div._0LqQ span div._1ZMSM span.P6z4j').text)# Anzahl neuer Nachrichten

                    if field.select_one('div._0LqQ span div._1ZMSM span[data-icon="muted"]'):
                        dictionary['is_muted'] = True# Stummgeschaltet

                    if field.select_one('div._0LqQ span div._1ZMSM span[data-icon="pinned"]'):
                        dictionary['is_pinned'] = True# Fixiert

                    if field.select_one('div._2Ol0p div._2UVJ5 div.yKiIK svg > path'):
                        dictionary['label'] = field.select_one('div._2Ol0p div._2UVJ5 div.yKiIK svg > path').attrs['fill']# Mit label versehen
                    elif field.select_one('div._2Ol0p div._2UVJ5 path:nth-of-type(2)'):
                        dictionary['label'] = field.select_one('div._2Ol0p div._2UVJ5 path:nth-of-type(2)').attrs['fill']# Mit mehreren Labeln versehen

                    if field.select_one('div._3RWII div.B9BIa span[data-icon="default-group"]') and not field.select_one('div._3RWII img.jZhyM._13Xdg'):
                        dictionary['pp'] = False# Kein Profilbild
                    elif field.select_one('div._3RWII img.jZhyM._13Xdg'):
                        dictionary['pp'] = self.urllib.parse.unquote(field.select_one('div._3RWII img.jZhyM._13Xdg').attrs['src'].split('?e=')[1].split('&')[0])# Profilbild vorhanden

                else: # Einzelne Chats
                    status = field.select_one('div._3VIru span[data-icon^="status"]')
                    if status:# letzte Nachricht von einem selber
                        dictionary['sender'] = False
                        dictionary['status'] = status.attrs['data-icon']# Status der eigenen letzten Nachricht
                    else:
                        dictionary['sender'] = True# Sender
                        if field.select_one('div._0LqQ span div._1ZMSM span.P6z4j'):
                            dictionary['new_messages'] = int(field.select_one('div._0LqQ span div._1ZMSM span.P6z4j').text)# Anzahl neuer Nachrichten

                    if field.select_one('span._1Wn_k > span'):
                        dictionary['last_message_or_event'] = field.select_one('div._2Bw3Q span._1Wn_k').attrs['title'][1:-1] # Letzte Nachricht

                    if field.select_one('div._0LqQ span div._1ZMSM span[data-icon="muted"]'):
                        dictionary['is_muted'] = True  # Stummgeschaltet

                    if field.select_one('div._0LqQ span div._1ZMSM span[data-icon="pinned"]'):
                        dictionary['is_pinned'] = True  # Fixiert

                    if field.select_one('div._2Ol0p div._2UVJ5 div.yKiIK svg > path'):
                        dictionary['label'] = field.select_one('div._2Ol0p div._2UVJ5 div.yKiIK svg > path').attrs['fill']  # Mit label versehen
                    elif field.select_one('div._2Ol0p div._2UVJ5 path:nth-of-type(2)'):
                        dictionary['label'] = field.select_one('div._2Ol0p div._2UVJ5 path:nth-of-type(2)').attrs['fill']  # Mit mehreren Labeln versehen

                    if field.select_one('div._3RWII img.jZhyM._13Xdg'):
                        dictionary['pp'] = self.urllib.parse.unquote(field.select_one('div._3RWII img.jZhyM._13Xdg').attrs['src'].split('?e=')[1].split('&')[0])  # Profilbild vorhanden
                    else:
                        dictionary['pp'] = False  # Kein Profilbild

                if field.select_one('div._2WP9Q div.xD91K div._2Bw3Q span._2ZAIy._19RFN'): # Screibt / Audio
                    dictionary['activity'] = field.select_one('div._2WP9Q div.xD91K div._2Bw3Q span._2ZAIy._19RFN').text

                dictionary['index'] = int(int(field.select_one('div.X7YrQ').attrs['style'][:-33].split('(')[-1].split(': ')[1].split(';')[0]) / 72) # Nummer des Chats
                chats.append(dictionary)

            chats = sorted(chats, key = lambda i: i['index'])
            for dictionary in chats:
                del dictionary['index']

            self.main_data['chats'] = chats
            if self.driver.find_elements_by_css_selector('header._3Jvyf div._2rZZg div._3RWII img.jZhyM._13Xdg'):# eigenes Profielbild
                self.main_data['own_pp'] = self.urllib.parse.unquote(self.driver.find_element_by_css_selector('header._3Jvyf div._2rZZg div._3RWII img.jZhyM._13Xdg').get_attribute('src').split('?e=')[1].split('&')[0])
            else:
                self.main_data['own_pp'] = False

            if self.driver.find_elements_by_css_selector('div._3lq69 span div._3j8Pd:first-child div[role="button"] span[data-icon^=status]'): # Überprüfen ob neuer Status vorhanden ist
                if self.driver.find_element_by_css_selector('div._3lq69 span div._3j8Pd:first-child div[role="button"] span[data-icon^=status]').get_attribute('outerHTML').split('-')[3].split('"')[0] == 'unread':
                    self.main_data['new_status'] = True
                else:
                    self.main_data['new_status'] = False
            else:
                self.main_data['new_status'] = False

            if self.driver.find_elements_by_css_selector('span._3O0po div._25f0v div._2-Q3h._1puWZ div._3K1Z_ div._28Bny'): # Zeigt an ob der Computer oder das Telefon Verbindungsprobleme hat
                self.main_data['connection_error'] = self.driver.find_element_by_css_selector('span._3O0po div._25f0v div._2-Q3h._1puWZ div._3K1Z_ div._28Bny').get_attribute('innerHTML')

    def login(self):
        # 2 Sekunden Pause, damit die Seite geladen werden kann
        self.time.sleep(2)

        src_test = ""
        src= ""
        logout_or_tryagin = ""
        img = False

        # Anmeldevorgang
        if not self.driver.find_elements_by_css_selector('div._3WtUH div div._2UaNq div._3vpWv'):
            if self.driver.find_elements_by_css_selector('img[alt="Scan me!"]'):
                img = self.driver.find_element_by_xpath('//img[@alt = "Scan me!"]')
                src = img.get_attribute('src')
                self.urllib.request.urlretrieve(src, "qrcode.png")
                print('Scanne den QR-Code um dich Anzumelden')

            while not self.driver.find_elements_by_css_selector('div._3WtUH div div._2UaNq div._3vpWv'):
                if logout_or_tryagin == 'logout':
                    if self.driver.find_elements_by_css_selector('img[alt="Scan me!"]'):
                        img = self.driver.find_element_by_xpath('//img[@alt = "Scan me!"]')
                        src = img.get_attribute('src')
                        self.urllib.request.urlretrieve(src, "qrcode.png")
                        print('Scanne den QR-Code um dich Anzumelden')
                        logout_or_tryagin = False

                if self.driver.find_elements_by_css_selector('img[alt="Scan me!"]'):
                    if img == False:
                        img = self.driver.find_element_by_xpath('//img[@alt = "Scan me!"]')
                    else:
                        src_test = img.get_attribute('src')

                if src_test != src:
                    # QR-Code herunterladen
                    self.urllib.request.urlretrieve(src_test, "qrcode.png")
                    print('Es gibt einen neuen QR-Code')
                    src = src_test
                    self.time.sleep(2)

                # Wenn der QR-Code abgelaufen ist, kann man ENTER drücken um einen neuen anzufordern
                if self.driver.find_elements_by_css_selector('span div._1MOym'):
                    input("Der QR-Code ist abgelaufen. Drücke ENTER um einen neuen QR-Code anzufordern")
                    self.driver.find_element_by_class_name('_1MOym').click()
                    self.time.sleep(1)
                    img = self.driver.find_element_by_xpath('//img[@alt = "Scan me!"]')

                if self.driver.find_elements_by_css_selector('div._3RiLE div.aymnx div._13HPh'):
                    logout_or_tryagin = input('Versuche dein Telefon zu erreichen... gebe "logout" ein, um dich dauerhaft abzumelden oder drücke eine beliebige Taste, um es erneut zu probieren: ')
                    if logout_or_tryagin != 'logout':
                        print('Laden...')

                    if self.driver.find_elements_by_css_selector('div._2eK7W'):
                        if logout_or_tryagin == 'logout':
                            self.driver.find_element_by_xpath('//div[@class="_2eK7W _23_1v"]').click()
                            self.time.sleep(1)
                        else:
                            self.driver.find_element_by_xpath('//div[@class="_2eK7W _3PQ7V"]').click()
                    self.time.sleep(1)

        print('Du wurdest eingeloggt!')

bot = WaBot('./selenium')
bot.login()
bot.read_data()
#bot.driver.find_element_by_xpath('//span[@title = "{}"]'.format("Test1")).click()
print(bot.main_data)
#bot.select_user("Test1")
#bot.send_msg("test")
bot.select_user("Test")
#bot.send_msg("test")
bot.read_messages()

"""
    • Umfragefunktion
    • Bild senden
    • zu Gruppe hinzufügen
    • Gruppe erstellen
    • read_messages_from()
    • Profilbild hinzufügen

"""


import time
import matplotlib.pyplot as plt

import sys

import numpy as np

from datetime import datetime
import os
import pickle

class Plotloss():
    def __init__(self,loss_names,pretrain=False):
        self.loss_names=loss_names
        if pretrain==True:
            historyFile='/home/yong/hdd/HPE/output/2019_10_26_20_54/history.pickle'
            self.losses={}
            with open(historyFile, 'rb') as f:
                for nm in loss_names:
                    self.losses[nm]=pickle.load(f)

            self.loss_best=999999
        else:
            self.losses={}
            for nm in loss_names:
                self.losses[nm]=[]
            self.loss_best=999999

        self.initialize_local()

    def initialize_local(self):
        #initialize local
        self.loss_local={}
        for nm in self.loss_names:
            self.loss_local[nm]=[]

    def append_local(self,loss_,nm):
        self.loss_local[nm].append(loss_)
        '''
        for i,nm in enumerate(self.loss_names):
            self.loss_local[nm].append(loss_[i])
        '''

    def append_loss(self):
        for nm in self.loss_names:
            self.losses[nm].append(np.mean(self.loss_local[nm]))
        self.initialize_local()

    def get_average(self,nm):
        return np.mean(self.loss_local[nm])

    def get_std(self,nm):
        return np.std(self.loss_local[nm])

    def save_plot(self,plt_title,fname):
        plt.figure()

        min_list=[]
        max_list=[]
        for nm in self.loss_names:
            vals=[x for x in self.losses[nm]]
            plt.plot(vals,label=nm)

            min_list.append(min(vals))
            max_list.append(max(vals))

        plt.autoscale(enable=True,axis='y')
        #plt.ylim(0,min(min_list)*100)
        #plt.ylim(0,0.01)

        plt.xlabel('epoch')
        plt.ylabel('loss')
        plt.title(plt_title)
        plt.legend()
        plt.grid()
        fig=plt.gcf()
        #plt.show()
        fig.savefig(fname)
        plt.close()
        '''
        plt.figure()

        for nm in self.loss_names:
            plt.plot([x for x in self.losses[nm]],label=nm)

        plt.autoscale(enable=True,axis='y')

        plt.xlabel('epoch')
        plt.ylabel('loss')
        plt.title(plt_title)
        plt.legend()
        plt.grid()
        fig=plt.gcf()
        #plt.show()
        fig.savefig(fname)
        plt.close()
        '''


from domain.dataset import Dataset
from domain.model_repository import get_prophet, get_arima, get_auto_arima, get_LSTM, get_multivariante_prophet
from domain.model_repository import EnsembleModel, EnsembleWeights

class ModelService:
    def train(self,
              n_splits,
              look_back,
              data_series: Dataset):

        prophet = get_prophet()
        data_series.train(prophet)

        arima = get_arima()
        data_series.train(arima)

        auto_arima = get_auto_arima()
        data_series.train(auto_arima)

        lstm = get_LSTM(look_back)
        data_series.train(lstm)

        multivar_prophet = get_multivariante_prophet()
        data_series.train(multivar_prophet)

        ensemble = EnsembleWeights(
            models_to_ensemble=[prophet, arima, auto_arima, lstm, multivar_prophet], n_splits=n_splits)
        weights, preds, rmses, mapes = ensemble.get_weights_and_estimators()
        return weights, preds, rmses, mapes

    def train_ensemble(self, data_series: Dataset, preds, weights, look_back):
        ensemble = EnsembleModel(
            preds, weights, look_back)
        y_vals = data_series.get_validation_data(ensemble)
        ensemble_preds, rmses, mapes = ensemble.calculate_rmses_mapes(y_vals)
        return ensemble_preds, rmses, mapes


import sys
sys.setrecursionlimit(10 ** 5)

def dfs(node, length):
    global ans, start_node
    if len(graph[node]) == 1 and length != 0:
        if ans < length:
            ans = length
            start_node = node
        return
    for next_node in graph[node]:
        if not visited[next_node]:
            visited[next_node] = True
            dfs(next_node, length + graph[node][next_node])
            visited[next_node] = False

n = int(sys.stdin.readline().rstrip())
graph = {node: {} for node in range(n)}
for i in range(n - 1):
    arr = list(map(int, sys.stdin.readline().split()))
    a = arr[0]
    a -= 1
    idx = 1
    while arr[idx] != -1:
        b = arr[idx] - 1
        c = arr[idx + 1]
        graph[a][b] = c
        graph[b][a] = c
        idx += 2
ans = 0
start_node = 0
for i in range(2):
    visited = [False] * n
    visited[start_node] = True
    dfs(start_node, 0)
print(ans)

백준/Gold/1167. 트리의 지름/트리의 지름.py


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
def calc(numbers):
    sum = 0
    for n in numbers:
        sum += n * n
    return sum
print('calc([]):', calc([]))
print('calc([1, 2, 3]):', calc([1, 2, 3]))

def calc2(*numbers):
    sum = 0
    for n in numbers:
        sum += n * n
    return sum
print('calc2():', calc2())
print('calc2(1, 2, 3):', calc2(1, 2, 3))
nums = [1, 2, 3]
print('calc2(1, 2, 3):', calc2(*nums))

def joinStr(*args):
    if (len(args) == 0):
        pass
    else:
        print('%s!' % ('+'.join(args)))
joinStr('1', '2', '3')
def joinNum(*args):
    if (len(args) == 0):
        pass
    else:
        #print('%s!' % ('+'.join(str(args))))
        print('%s!' % ('+'.join(str(args))))
joinNum(1, 2, 3)
print(str(nums), len(str(nums)))
for i in str(nums):
    print("=" + i + "=", end ='')
print()
############################################
def person(name, age, **kw):
    print("name:%s, age:%s, other:%s"%(name, age, kw))
    #print('name:', name, 'age:', age, 'other:', kw)
    for n in kw:
        kw[n] = ''
person('Lily', 19)
#person('Dick', 21, 'Hangzhou', 'Teacher')	error, postional arguments only 2
person('Dick', 21, city = 'Hangzhou', job = 'Teacher')
extra = {'city': 'Hangzhou', 'job': 'Teacher'}
person("Mack", 14, **extra)
print("extra:", extra)
for n in extra:
    extra[n] = ''
print("extra:", extra)
############################################
def person(name, age, *, city = 'Hangzhou', job):
    print(name, age, city, job)
person("Lily", 24, city = "shaoxin", job = 'Teacher')
person("Lily", 24, job = 'Teacher')

def f1(a, b, c = 0, *args, **kw):
	print("a:%s, b:%s, c:%s, args:%s, kw:%s" %(a, b, c, args, kw))
    #print("a:", a, ",b:", b, ',c:', c, ',args:', args, ',kw:', kw)
def f2(a, b, c = 0, *, d, **kw):
    print("a:%s, b:%s, c:%s, d:%s, kw:%s" %(a, b, c, d, kw))
f1(1, 0)
f1(1, 2, 3)
f1(1, 2, 3, 4, 5)
f1(1, 2, 3, 4, 5, 6, x = 7)
f2(1, 2, d = 99, ext = None)

args = (1, 2, 3, 4)
kw = {'d':99, 'x':'#'}
f1(*args, **kw)
args = (1, 2, 3)
f2(*args, **kw)


from tkinter import *

def calcular_promedio():

    nota = float(nota_text.get())
    promedio = (nota + float(E_promedio["text"]))/ 4
    E_promedio["text"]= promedio

def limpiar():
    # Limpiar los campos de nota y promedio
    nota_text.delete(0, "end")

def eliminar_todo():
    # Limpiar los campos de nota y promedio
    E_promedio["text"] =0
    nota_text.delete(0, "end")

def agregar_nota():
    nota = float(nota_text.get())
    promedio = float(E_promedio["text"]) + nota
    E_promedio["text"]= promedio

# Crear la ventana
ventana = Tk()
ventana.title("☻♣◙")

# Crear las etiquetas
notas=Label(ventana, text="Nota")
notas.grid(row=0, column=0)
nota_text =Entry(ventana)
nota_text.grid(row=0, column=1)

promedio_text =Label(ventana, text="Promedio: ")
promedio_text.grid(row=1, column=0)

E_promedio = Label(ventana, text="0")
E_promedio.grid(row=1, column=1)

# Crear los botones
boton_calcular =Button(ventana, text="Calcular", command=calcular_promedio)
boton_calcular.grid(row=2, column=0)
boton_calcular.config(bg="yellow")

boton_limpiar = Button(ventana, text="Limpiar", command=limpiar)
boton_limpiar.grid(row=2, column=1)

boton_agregar = Button(ventana, text="+", command=agregar_nota)
boton_agregar.grid(row=0, column=2)

eliminar = Button(ventana, text="Eliminar", command=eliminar_todo)
eliminar.grid(row=4, column=1)
eliminar.config(bg="red")

ventana.mainloop()


def prod():
	print('Ingrese precio')
	pre=float(input())
	print('Ingrese codigo')
	cod=input()
	print('Ingrese tipo')
	tipo=input()
	p=dict(pr=pre,co=cod,ti=tipo)
	return(p)
def imprimir(cc,cb):
	print('El codigo del producto pantalon mas caro es:',cc)
	print('El codigo del producto mas barato es:',cb)
	return()
maximo=-100
minimo=100000
codbar=''
codcar=''
for i in range(100):
	p=prod()
	if(p['pr']>maximo)and(p['ti']=='pantalon'):
		maximo=p['pr']
		codcar=p['co']
	if(p['pr']<minimo):
		codbar=p['co']
		minimo=p['pr']
imprimir(codcar,codbar)


from collections import defaultdict

from strategies.filter_strategy import FilterStrategy, get_filter
from feedback.feedback import LetterFeedback, feedback_to_int, get_feedback
from constants import GUESSES
from progressbar import progressbar

def _zero():
    return 0

class LookAheadStrategy(FilterStrategy):

    """
    There are 5 letters, each letter has 3 possible colours B, Y, G...

    Therefore there are 3*3*3*3*3 = 243 possible sets of feedback from any
    given guess.

    A "perfect guess" would have equal probability of all sets of
    feedback.  That guess would then split the solution space down by
    1/243.  Clearly this is not actually possible since there's only
    one GGGGG possibility, which is the solution.

    But pretending it is, if there's 2300-ish solutions, you'd go down
    to 10 possibilites.  So, the good guesses have a wide distribution
    of possible feedback.  How do I quantify this width?
    """
    # num_sets_of_possible_feedback = pow(3, 5) # 243

    def next_guess(self, candidate_words, previous_guesses):
        # It always chose the first thing each time
        if len(previous_guesses) == 0:
            return "reast" #"slate"

        best = None
        best_score = None
        for g_i, next_guess in enumerate(GUESSES):
            if next_guess in previous_guesses:
                continue

            values = [0 for _ in range(243)]
            for candidate_word in candidate_words:
                i = get_feedback(next_guess, candidate_word, as_int=True)
                values[i] = values[i] + 1
            values = sorted(values, reverse=True)

            score=0
            for _i, v in enumerate(values):
                if v == 0:
                    break
                score = score + (_i+1)*v

            # Add bonus points for guess being in candidate words list
            if next_guess in candidate_words:
                score = score + 10

            if best_score is None or score > best_score:
                best_score = score
                best = next_guess
                # print(f"{next_guess}: {score}... {best}: {best_score}")

        # print(f"{best}: {best_score}")
        return best


from itertools import repeat
import time

arraySet1 = list(repeat(4, 1000000))
arraySet2 = list(repeat(8, 1000000))
arraySet3 = list(repeat(16, 1000000))

def NoThread(arraySet):
    t0 = time.time()
    print(sum(arraySet))
    t = time.time()
    print('Time Rate : %2.9f' % (t-t0))
    time.sleep(2)

NoThread(arraySet1)
NoThread(arraySet2)
NoThread(arraySet3)

print(" ")
input('press any key to exit...')


"""
Set the path to import from parent directory.
"""
import os
import sys
import inspect
import json
import pytest

currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)

import numpy as np

# Try imports for good work of the LSP
try:
    from ..parsers import (
        Decoder,
        CoordNumber,
        CoordNumbers,
        Information,
        CnrFileItemType,
    )
except ImportError:
    from parsers import (
        Decoder,
        CoordNumber,
        CoordNumbers,
        Information,
        CnrFileItemType,
    )

@pytest.fixture
def json_path(tmp_path) -> str:
    path = tmp_path / "test.json"
    with open(os.path.join(currentdir, "./info_test.json")) as f:
        fcontent = f.read()
    fcontent = fcontent.replace("{path}", os.path.join(parentdir, "data"))
    path.write_text(fcontent)
    return path

@pytest.fixture
def coord_number() -> CoordNumber:
    """
    Fixture for CoordNumber class.
    """
    distances = np.linspace(0, 2, 100)
    return CoordNumber(
        fpath=os.path.join(parentdir, "data/EAN/cnr_BA_anion_cation_metal.txt"),
        column=1,
        header="Test header",
        charge=2,
        distances=distances,
        cnr=np.concatenate((np.zeros(10), np.arange(90))),
    )

@pytest.fixture
def coord_numbers_info(json_path: str) -> CnrFileItemType:
    """
    Fixture for CoordNumbers class.
    """
    data = json.load(
        open(json_path, "r"),
        cls=Decoder,
    )
    return data["Li"]["cnrs"]

@pytest.fixture
def information(json_path: str) -> Information:
    """
    Fixture for Information class.
    """
    return Information(json_path)

def test_decoder():
    """
    Test the Decoder class.
    """
    test_json = """
    {
        "coord_numbers": [
            {
                "test": "1.5",
                "information": {
                    "name": "test",
                    "value": "1"
                }
            }
        ]
    }
    """
    parsed_json = json.loads(test_json, cls=Decoder)
    assert parsed_json["coord_numbers"][0]["test"] == 1.5
    assert parsed_json["coord_numbers"][0]["information"]["name"] == "test"
    assert parsed_json["coord_numbers"][0]["information"]["value"] == 1

def test_coord_number(coord_number):
    """
    Test the CoordNumber class.
    """
    chr_dis = coord_number.charge_distribution
    assert len(coord_number.distances) == 100
    assert np.isclose(chr_dis[0], 0.0, rtol=1e-2)
    assert np.isclose(chr_dis[-1], 89 * 2, rtol=1e-2)
    distance = np.linspace(0, 2, 100)[11]
    assert coord_number.exclusion_radius == distance
    assert coord_number.charge == 2

def test_coord_numbers(coord_numbers_info):
    """
    Test the CoordNumbers class.
    """
    coord_numbers = CoordNumbers("Li", coord_numbers_info)
    assert set(coord_numbers.keys()) == {"anion", "cation", "metal"}
    assert isinstance(coord_numbers["anion"], CoordNumber)
    assert coord_numbers["anion"].charge == -1
    assert coord_numbers["cation"].charge == 1
    assert coord_numbers["metal"].charge == 1
    tot_char = coord_numbers.total_charge_distribution
    assert isinstance(tot_char, np.ndarray)
    assert tot_char.shape == (len(coord_numbers.distances),)
    assert len(coord_numbers.distances) == 1250

def test_information(information: Information):
    """
    Test the Information class.
    """
    assert set(information.keys()) == {"Li", "Na"}
    assert isinstance(information["Li"], dict)
    assert set(information["Li"].keys()) == {
        "cnrs",
        "Q",
        "enthalpy",
        "enthalpy_md",
        "ionic_radius",
        "cnrs",
    }
    assert isinstance(information["Li"]["cnrs"], CoordNumbers)
    assert information["Li"]["Q"] == 1
    assert information["Li"]["enthalpy"] == -505.71
    assert information["Li"]["enthalpy_md"] == -695.7
    assert information["Li"]["ionic_radius"] == 0.59
    assert isinstance(information.distances, np.ndarray)
    assert len(information.distances) == len(information["Li"]["cnrs"]["anion"].cnr)
    excl_rad = {k: v for k, v in zip(information.keys(), information.exclusion_radii)}
    compare = {"Li": 0.222, "Na": 0.266}
    assert excl_rad == compare
    assert round(information["Li"]["cnrs"].electrostatic_work) == -818
    assert round(information["Na"]["cnrs"].electrostatic_work) == -674


import os
from pathlib import Path
from typing import Callable, List

import numpy as np
import pytest

from gaddlemaps import rotation_matrix
from gaddlemaps.components import (Atom, AtomGro, AtomTop, Molecule,
                                   MoleculeTop, Residue, System)
from gaddlemaps.components._components import _molecule_top_and_residues_match
from gaddlemaps.parsers import GroFile

AtomGenerator = Callable[..., AtomGro]

ACTUAL_PATH = os.path.split(os.path.join(os.path.abspath(__file__)))[0]

@pytest.fixture
def atom_gro_generator() -> AtomGenerator:
    """
    A function to create AtomGro instances.
    """
    def generate_atom_gro(resid: int = 1, resname: str = 'BMIM',
                          name: str = 'N1', atomid: int = 1) -> AtomGro:
        info = (resid, resname, name, atomid, 4.668,
                3.571, 8.232, -0.2489, 0.2514, 0.1046)
        return AtomGro(info)
    return generate_atom_gro

@pytest.fixture
def atom_n1_bmim(atom_gro_generator: AtomGenerator) -> Atom:
    """
    N1 Atom instance of BMIM.
    """
    atom_gro = atom_gro_generator()
    atom_top = AtomTop('N1', 'BMIM', 1, 0)
    return Atom(atom_top, atom_gro)

@pytest.fixture
def molecule_top_bmim() -> MoleculeTop:
    """
    BMIM MoleculeTop instance.
    """
    fname = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/BMIM_AA.itp')
    return MoleculeTop(fname)

@pytest.fixture
def residue_bmim() -> Residue:
    """
    BMIM Residue instance.
    """
    fname = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/BMIM_AA.gro')
    with GroFile(fname) as fgro:
        atoms = [AtomGro(line) for line in fgro]
    return Residue(atoms)

@pytest.fixture
def molecule_bmim(molecule_top_bmim: MoleculeTop,
                  residue_bmim: Residue) -> Molecule:
    """
    BMIM Molecule instance.
    """
    return Molecule(molecule_top_bmim, [residue_bmim])

@pytest.fixture
def molecule_top_protein() -> MoleculeTop:
    """
    MoleculeTop instance for molecule with multiple residues.
    """
    fname = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/Protein_CG.itp')
    return MoleculeTop(fname)

@pytest.fixture
def residue_protein() -> List[Residue]:
    """
    List of Residue instances for a molecule with multiple residues.
    """
    fname = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/Protein_CG.gro')
    residues: List[Residue] = []
    with GroFile(fname) as fgro:
        atom = AtomGro(next(fgro))
        residname = atom.residname
        atoms = [atom]
        for line in fgro:
            atom = AtomGro(line)
            if residname != atom.residname:
                residues.append(Residue(atoms))
                residname = atom.residname
                atoms = [atom]
            else:
                atoms.append(atom)
    residues.append(Residue(atoms))
    return residues

@pytest.fixture
def molecule_protein(molecule_top_protein: MoleculeTop,
                     residue_protein: List[Residue]) -> Molecule:
    """
    Molecule instance with multiple residues.
    """
    return Molecule(molecule_top_protein, residue_protein)

def test_molecule_top_and_residues_match(molecule_top_bmim: MoleculeTop,
                                         residue_bmim: Residue,
                                         molecule_top_protein: MoleculeTop,
                                         residue_protein: List[Residue]):
    """
    Test the match between residues and MoleculeTop.
    """
    # different length
    assert not _molecule_top_and_residues_match(molecule_top_bmim,
                                                residue_protein)

    assert _molecule_top_and_residues_match(molecule_top_bmim,
                                            [residue_bmim])
    assert _molecule_top_and_residues_match(molecule_top_protein,
                                            residue_protein)
    residue_bmim.resname = 'test'
    assert not _molecule_top_and_residues_match(molecule_top_bmim,
                                                [residue_bmim])

class TestAtom:
    """
    Wraps all the tests for the Atom class.
    """
    def test_initialization(self, atom_gro_generator: AtomGenerator):
        """
        Tests Atom initialization and own attributes
        """
        atom_gro = atom_gro_generator()
        atom_top = AtomTop('N1', 'BMIM', 1, 0)
        atom = Atom(atom_top, atom_gro)
        assert atom.atom_gro is atom_gro
        assert atom.atom_top is atom_top
        new_atom = atom.copy()
        assert new_atom == atom
        assert atom != 0
        assert new_atom is not atom
        assert hash(atom) == 0

        with pytest.raises(TypeError, match="atom_gro.*"):
            atom = Atom(atom_top, atom_top)  # type:ignore
        with pytest.raises(TypeError, match="atom_top.*"):
            atom = Atom(atom_gro, atom_gro)  # type:ignore

        atom_gro.name = 'B5'
        with pytest.raises(IOError):
            atom = Atom(atom_top, atom_gro)

        atom_gro.name = 'N1'
        atom_gro.resname = 'BF4'
        with pytest.raises(IOError):
            atom = Atom(atom_top, atom_gro)

        # Print and str
        atom_str = str(atom)
        assert atom_str == repr(atom)
        line = 'Atom N1 of molecule BF4 with gro residue number 1.'
        assert atom_str == line

        # Some check to the dir()
        atom_dir = dir(atom)
        for key in dir(atom.atom_gro):
            assert key in atom_dir
        for key in dir(atom.atom_top):
            assert key in atom_dir

    def test_get_set_attributes(self, atom_n1_bmim: Atom):
        """
        Tests get and set attributes for Atom.
        """
        # Name
        assert atom_n1_bmim.name == 'N1'
        atom_n1_bmim.name = 'B5'
        assert atom_n1_bmim.name == 'B5'
        assert atom_n1_bmim._atom_gro.name == 'B5'
        assert atom_n1_bmim._atom_top.name == 'B5'

        # Resname
        assert atom_n1_bmim.resname == 'BMIM'
        atom_n1_bmim.resname = 'BF4'
        assert atom_n1_bmim.resname == 'BF4'
        assert atom_n1_bmim._atom_gro.resname == 'BF4'
        assert atom_n1_bmim._atom_top.resname == 'BF4'

        # resids
        with pytest.raises(AttributeError):
            resid = atom_n1_bmim.resid
        with pytest.raises(AttributeError):
            atom_n1_bmim.resid = 1

        # Other access and set
        with pytest.raises(AttributeError):
            resid = atom_n1_bmim.test
        atom_n1_bmim.test = 1
        assert atom_n1_bmim.test == 1

        assert atom_n1_bmim.gro_resid == 1
        atom_n1_bmim.gro_resid = 3
        assert atom_n1_bmim.gro_resid == 3
        assert atom_n1_bmim._atom_gro.resid == 3

        assert atom_n1_bmim.top_resid == 1
        atom_n1_bmim.top_resid = 5
        assert atom_n1_bmim.top_resid == 5
        assert atom_n1_bmim._atom_top.resid == 5

        # Residname is the gro one
        assert atom_n1_bmim.residname == '3BF4'

        # index
        assert atom_n1_bmim.index == 0
        assert hash(atom_n1_bmim) == 0
        atom_n1_bmim.index = 5
        assert atom_n1_bmim.index == 5
        assert hash(atom_n1_bmim) == 5
        assert atom_n1_bmim._atom_top.index == 5

        # bonds
        assert atom_n1_bmim.bonds == atom_n1_bmim._atom_top.bonds
        atom_n1_bmim.bonds = {1, 32, 4}
        assert atom_n1_bmim.bonds == {1, 32, 4}
        assert atom_n1_bmim.closest_atoms() == [1, 4]
        atom_n1_bmim.connect(atom_n1_bmim)
        assert atom_n1_bmim.bonds == {1, 32, 4, 5}

        # atomid
        assert atom_n1_bmim.atomid == 1
        atom_n1_bmim.atomid = 6
        assert atom_n1_bmim.atomid == 6
        assert atom_n1_bmim._atom_gro.atomid == 6

        # element
        assert atom_n1_bmim.element == 'B'

        # position
        assert np.isclose(atom_n1_bmim.position, [4.668, 3.571, 8.232]).all()
        atom_n1_bmim.position = np.array([0, 0, 0])
        assert np.isclose(atom_n1_bmim.position, [0, 0, 0]).all()
        assert np.isclose(atom_n1_bmim._atom_gro.position, [0, 0, 0]).all()

        # velocity
        assert np.isclose(atom_n1_bmim.velocity, [-0.2489, 0.2514, 0.1046]).all()
        atom_n1_bmim.velocity = None
        assert atom_n1_bmim.velocity is None
        assert atom_n1_bmim._atom_gro.velocity is None

        # gro_line
        assert atom_n1_bmim.gro_line() == atom_n1_bmim._atom_gro.gro_line()

class TestMolecule:
    """
    Wraps all the tests for the Molecule class.
    """
    def test_init_one_residue(self, molecule_top_bmim: MoleculeTop,
                              residue_bmim: Residue):
        """
        Tests Molecule initialization with only one residue and some basic
        attributes and methods.
        """
        # Wrong residue
        new_residue = Residue(residue_bmim[:-1])
        with pytest.raises(IOError):
            molec = Molecule(molecule_top_bmim, [new_residue])

        molec = Molecule(molecule_top_bmim, [residue_bmim])

        assert len(molec) == len(molecule_top_bmim)
        assert len(molec) == len(residue_bmim)
        assert len(molec) == 25

        assert str(molec) == 'Molecule of BMIM.'

        for atom, atom_top, atom_gro in zip(molec, molecule_top_bmim,
                                            residue_bmim):
            atom_compare = Atom(atom_top, atom_gro)
            assert atom == atom_compare

        for atom, atom_comp in zip(molec, molec.atoms):
            assert atom == atom_comp

        assert molec[-1] == atom_compare
        assert atom_compare != molec[0]
        assert molec.index(atom_compare) == 24
        with pytest.raises(ValueError):
            _ = molec.index(34)  # type: ignore

        assert molec._each_atom_resid == [0] * len(molec)

        assert molec == molec
        assert molec is molec

        copy_mol = molec.copy()
        assert copy_mol == molec
        assert copy_mol is not molec

        assert len(molec.residues) == 1
        assert molec.residues[0] == residue_bmim
        assert molec.residues[0] is not residue_bmim
        assert molec.molecule_top is molecule_top_bmim

        with pytest.raises(NotImplementedError):
            _ = molec + 1  # type: ignore
        with pytest.raises(NotImplementedError):
            _ = 1 + molec  # type: ignore

        for key in dir(molec.molecule_top):
            assert key in dir(molec)

    def test_init_multi_residue(self, molecule_top_protein: MoleculeTop,
                                residue_protein: List[Residue]):
        """
        Tests Molecule initialization with multiple residues and some basic
        attributes and methods.
        """
        molec = Molecule(molecule_top_protein, residue_protein)

        assert len(molec) == len(molecule_top_protein)
        assert len(molec) == sum(len(res) for res in residue_protein)
        assert len(molec) == 34

        index = 0
        for res in residue_protein:
            for atom_gro in res:
                atom_compare = Atom(molecule_top_protein[index], atom_gro)
                assert molec[index] == atom_compare
                index += 1

        for atom, atom_comp in zip(molec, molec.atoms):
            assert atom == atom_comp

        assert molec[-1] == atom_compare
        assert molec.index(atom_compare) == 33

        assert molec._each_atom_resid == [0, 0, 0, 1, 1, 1, 2, 2, 3,
                                          3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 7,
                                          8, 8, 8, 9, 9, 10, 10, 10, 11, 11,
                                          11, 12, 12]

        assert molec != 0
        assert molec == molec
        assert molec is molec

        assert molec.copy() == molec
        assert molec.copy() is not molec

        assert len(molec.residues) == 13
        for res1, res2 in zip(molec.residues, residue_protein):
            assert res1 == res2
            assert res1 is not res2
        assert molec.molecule_top is molecule_top_protein

    def test_init_from_files(self):
        """
        Test the molecule initialization from files.
        """
        fitp = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/BMIM_AA.itp')
        fgro = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/BMIM_AA.gro')
        bmim = Molecule.from_files(fgro, fitp)
        assert len(bmim) == 25
        fitp = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/BMIM_CG.itp')
        fgro = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/system_bmimbf4_cg.gro')
        with pytest.raises(IOError):
            bmim = Molecule.from_files(fgro, fitp)

    def test_resnames(self, molecule_protein: Molecule,
                      molecule_bmim: Molecule):
        """
        Tests for the resnames property
        """
        assert molecule_bmim.resnames == ['BMIM']
        molecule_bmim.resnames = ['BF4']
        assert molecule_bmim.resnames == ['BF4']
        assert molecule_bmim.molecule_top.resnames == ['BF4']
        assert molecule_bmim.residues[0].resname == 'BF4'
        for atom in molecule_bmim:
            assert atom.resname == 'BF4'

        with pytest.raises(ValueError):
            molecule_bmim.resnames = ['BF4', 'Test']
        with pytest.raises(TypeError):
            molecule_bmim.resnames = 4  # type: ignore

        molecule_bmim.resnames = 'test'  # type: ignore
        assert molecule_bmim.resnames == ['test']
        assert molecule_bmim.molecule_top.resnames == ['test']
        assert molecule_bmim.residues[0].resname == 'test'
        for atom in molecule_bmim:
            assert atom.resname == 'test'

        resnames_compare = ['ARG', 'ARG', 'LEU', 'LYS', 'ARG', 'LEU', 'LEU',
                            'ARG', 'ARG', 'LEU', 'LYS', 'ARG', 'LEU']
        assert molecule_protein.resnames == resnames_compare
        resnames_compare[3] = 'Test'
        molecule_protein.resnames = resnames_compare
        assert molecule_protein.resnames == resnames_compare
        for res, res_test in zip(molecule_protein.residues, resnames_compare):
            assert res.resname == res_test
        for index, atom in enumerate(molecule_protein):
            res_index = molecule_protein._each_atom_resid[index]
            assert atom.resname == resnames_compare[res_index]

        molecule_protein.resnames = 'hello'  # type: ignore
        assert molecule_protein.resnames == ['hello'] * len(resnames_compare)
        for res, res_test in zip(molecule_protein.residues, resnames_compare):
            assert res.resname == 'hello'
        for atom in molecule_protein:
            assert atom.resname == 'hello'

    def test_resids(self, molecule_protein: Molecule,
                    molecule_bmim: Molecule):
        """
        Tests for the resids property
        """
        assert molecule_bmim.resids == [1]
        molecule_bmim.resids = [5]
        assert molecule_bmim.resids == [5]
        assert molecule_bmim.molecule_top.resids == [5]
        assert molecule_bmim.residues[0].resid == 5
        for atom in molecule_bmim:
            assert atom.top_resid == 5
            assert atom.gro_resid == 5

        with pytest.raises(ValueError):
            molecule_bmim.resids = [1, 2]
        with pytest.raises(TypeError):
            molecule_bmim.resids = 'test'  # type: ignore

        molecule_bmim.resids = 3  # type: ignore
        assert molecule_bmim.resids == [3]
        assert molecule_bmim.molecule_top.resids == [3]
        assert molecule_bmim.residues[0].resid == 3
        for atom in molecule_bmim:
            assert atom.top_resid == 3
            assert atom.gro_resid == 3

        resids_compare = list(range(1, 14))
        assert molecule_protein.resids == resids_compare
        resids_compare[3] = 8
        molecule_protein.resids = resids_compare
        assert molecule_protein.resids == resids_compare
        for res, res_test in zip(molecule_protein.residues, resids_compare):
            assert res.resid == res_test
        for index, atom in enumerate(molecule_protein):
            res_index = molecule_protein._each_atom_resid[index]
            assert atom.top_resid == resids_compare[res_index]
            assert atom.gro_resid == resids_compare[res_index]

        molecule_protein.resids = 49  # type: ignore
        assert molecule_protein.resids == [49] * len(resids_compare)
        for res, res_test in zip(molecule_protein.residues, resids_compare):
            assert res.resid == 49
        for atom in molecule_protein:
            assert atom.top_resid == 49
            assert atom.gro_resid == 49

    def test_get_set_attributes(self, molecule_top_bmim: MoleculeTop,
                                molecule_bmim: Molecule):
        """
        Tests get and set attributes for Molecule. Molecule getattr just access
        the molecule_top attributes.
        """
        # Name
        assert molecule_top_bmim.name == molecule_bmim.name
        molecule_bmim.name = 'Test'
        assert molecule_bmim.name == 'Test'
        assert molecule_bmim.molecule_top.name == 'Test'

        # Resname len list
        assert molecule_bmim.resname_len_list == [('BMIM', 25)]

    def test_bonds_distance(self, molecule_bmim: Molecule):
        """
        Test bonds distances for bmim.
        """
        bonds_dist = molecule_bmim.bonds_distance
        for index, atom in enumerate(molecule_bmim):
            assert set(val[0] for val in bonds_dist[index]) == atom.bonds
            for index_to, dist in bonds_dist[index]:
                diff = atom.position - molecule_bmim[index_to].position
                dist_test = np.linalg.norm(diff)
                assert round(dist, 5) == round(dist_test, 5)

    def test_position_velocities_bmim(self, molecule_bmim: Molecule):
        """
        Test for the atom position and velocities get and setter for bmim
        molecule.
        """
        test_bmim_pos = np.array([
            [1.593, 1.896, 0.729],
            [1.706, 1.984, 0.708],
            [1.807, 1.892, 0.660],
            [1.755, 1.761, 0.652],
            [1.626, 1.764, 0.693],
            [1.738, 2.029, 0.805],
            [1.809, 1.674, 0.618],
            [1.559, 1.679, 0.698],
            [1.464, 1.937, 0.778],
            [1.942, 1.930, 0.624],
            [2.044, 1.858, 0.713],
            [2.188, 1.899, 0.677],
            [2.289, 1.828, 0.765],
            [1.463, 2.046, 0.799],
            [1.441, 1.883, 0.872],
            [1.385, 1.914, 0.703],
            [1.960, 1.904, 0.517],
            [1.955, 2.039, 0.636],
            [2.024, 1.884, 0.819],
            [2.034, 1.748, 0.701],
            [2.200, 2.009, 0.689],
            [2.208, 1.872, 0.571],
            [2.392, 1.859, 0.737],
            [2.273, 1.854, 0.872],
            [2.281, 1.718, 0.753]
        ])
        assert np.isclose(test_bmim_pos, molecule_bmim.atoms_positions).all()
        assert molecule_bmim.atoms_velocities is None

        test_bmim_pos[1] = np.array((1, 1, 1))
        molecule_bmim.atoms_positions = test_bmim_pos
        assert np.isclose(test_bmim_pos, molecule_bmim.atoms_positions).all()

        change_atom = np.array((2, 3, 4))
        test_bmim_pos[3] = change_atom
        molecule_bmim[3].position = change_atom
        assert np.isclose(molecule_bmim[3].position, change_atom).all()
        assert np.isclose(test_bmim_pos, molecule_bmim.atoms_positions).all()

        geom_test = np.mean(test_bmim_pos, axis=0)
        assert np.isclose(geom_test, molecule_bmim.geometric_center).all()

        with pytest.raises(ValueError):
            molecule_bmim.atoms_positions = change_atom

        with pytest.raises(ValueError):
            molecule_bmim.atoms_velocities = change_atom

        molecule_bmim.atoms_velocities = test_bmim_pos
        assert np.isclose(test_bmim_pos, molecule_bmim.atoms_velocities).all()

        change_atom = np.array((5, 1, 3))
        test_bmim_pos[5] = change_atom
        assert np.isclose(molecule_bmim[5].velocity, change_atom).all()
        assert np.isclose(test_bmim_pos, molecule_bmim.atoms_velocities).all()

    def test_position_velocities_protein(self, molecule_protein: Molecule):
        """
        Test for the atom position and velocities get and setter for the
        molecule with multiple residues.
        """
        test_protein_pos = np.array([
            [2.490, 3.850, 5.330],
            [2.310, 4.040, 5.170],
            [2.190, 4.380, 5.220],
            [2.480, 3.530, 5.200],
            [2.280, 3.350, 5.350],
            [2.180, 3.040, 5.290],
            [2.700, 3.410, 5.120],
            [2.770, 3.370, 4.800],
            [2.900, 3.290, 5.390],
            [3.220, 3.190, 5.390],
            [3.440, 3.130, 5.540],
            [2.700, 3.030, 5.470],
            [2.490, 3.060, 5.730],
            [2.430, 3.160, 6.050],
            [2.710, 2.860, 5.200],
            [2.550, 2.950, 4.940],
            [3.040, 2.820, 5.150],
            [3.110, 2.980, 4.860],
            [3.090, 2.690, 5.460],
            [3.280, 2.580, 5.720],
            [3.120, 2.490, 5.990],
            [2.830, 2.450, 5.450],
            [2.750, 2.270, 5.670],
            [2.460, 2.210, 5.800],
            [2.910, 2.280, 5.170],
            [2.850, 2.430, 4.850],
            [3.210, 2.310, 5.160],
            [3.250, 2.010, 5.010],
            [3.330, 1.820, 5.160],
            [3.220, 2.140, 5.500],
            [3.550, 2.070, 5.510],
            [3.830, 2.170, 5.540],
            [3.050, 1.870, 5.590],
            [2.790, 1.770, 5.390]
        ])
        assert np.isclose(test_protein_pos, molecule_protein.atoms_positions).all()
        assert molecule_protein.atoms_velocities is None

        test_protein_pos[1] = np.array((1, 1, 1))
        molecule_protein.atoms_positions = test_protein_pos
        assert np.isclose(test_protein_pos, molecule_protein.atoms_positions).all()

        change_atom = np.array((2, 3, 4))
        test_protein_pos[3] = change_atom
        molecule_protein[3].position = change_atom
        assert np.isclose(molecule_protein[3].position, change_atom).all()
        assert np.isclose(test_protein_pos, molecule_protein.atoms_positions).all()

        geom_test = np.mean(test_protein_pos, axis=0)
        assert np.isclose(geom_test, molecule_protein.geometric_center).all()

        with pytest.raises(ValueError):
            molecule_protein.atoms_positions = change_atom

        with pytest.raises(ValueError):
            molecule_protein.atoms_velocities = change_atom

        molecule_protein.atoms_velocities = test_protein_pos
        assert np.isclose(test_protein_pos, molecule_protein.atoms_velocities).all()

        change_atom = np.array((5, 1, 3))
        test_protein_pos[5] = change_atom
        assert np.isclose(molecule_protein[5].velocity, change_atom).all()
        assert np.isclose(test_protein_pos, molecule_protein.atoms_velocities).all()

    def test_atoms_ids(self, molecule_protein: Molecule):
        """
        Test for the atoms_ids property for the molecule with multiple residues.
        """
        test_ids = list(range(1, 35))
        assert molecule_protein.atoms_ids == test_ids

        test_ids[2] = 5
        molecule_protein.atoms_ids = test_ids
        assert molecule_protein.atoms_ids == test_ids

        test_ids[4] = 40
        molecule_protein[4].atomid = 40
        assert molecule_protein.atoms_ids == test_ids

        with pytest.raises(IndexError):
            molecule_protein.atoms_ids = [1, 2]

        with pytest.raises(TypeError):
            test_wrong = [1] * (len(test_ids) - 1) + ['test']  # type: ignore
            molecule_protein.atoms_ids = test_wrong

    def test_hidden_methods(self, molecule_bmim: Molecule):
        """
        Test not implemented properties and methods.
        """
        with pytest.raises(AttributeError):
            test = molecule_bmim.resname
        with pytest.raises(AttributeError):
            molecule_bmim.resname = 'test'

        with pytest.raises(AttributeError):
            test = molecule_bmim.resid # type: ignore
        with pytest.raises(AttributeError):
            molecule_bmim.resid = 'test'  # type: ignore

        with pytest.raises(AttributeError):
            test = molecule_bmim.residname
        with pytest.raises(AttributeError):
            molecule_bmim.residname = 'test'  # type: ignore

        with pytest.raises(AttributeError):
            test = molecule_bmim.remove_atom(molecule_bmim[0]) # type: ignore

    def test_rotate(self, molecule_protein: Molecule):
        """
        Test for the rotate method.
        """
        rot_mat = rotation_matrix([0, 0, 1], 2*np.pi)
        old_pos = molecule_protein.atoms_positions
        molecule_protein.rotate(rot_mat)
        assert np.isclose(old_pos, molecule_protein.atoms_positions).all()

        rot_mat = rotation_matrix([0, 0, 1], 0)
        molecule_protein.rotate(rot_mat)
        assert np.isclose(old_pos, molecule_protein.atoms_positions).all()

    def test_move(self, molecule_protein: Molecule):
        """
        Tests for the moving methods.
        """
        # move_to
        move_to = [0, 1, 2]
        old_pos = molecule_protein.atoms_positions
        displ = move_to - molecule_protein.geometric_center
        molecule_protein.move_to(move_to)
        assert np.isclose(move_to, molecule_protein.geometric_center).all()
        assert np.isclose(old_pos + displ, molecule_protein.atoms_positions).all()

        # move
        move = [0, 1, 2]
        old_pos = molecule_protein.atoms_positions
        old_center = molecule_protein.geometric_center
        molecule_protein.move(move)
        assert np.isclose(move + old_center, molecule_protein.geometric_center).all()
        assert np.isclose(old_pos + move, molecule_protein.atoms_positions).all()

    def test_write_gro(self, molecule_protein: Molecule, tmp_path: Path):
        """
        Test for writing gro files.
        """
        subdir = tmp_path / "molecule_test"
        subdir.mkdir()
        fgrotmp = str(subdir / "test_write_gro.gro")
        molecule_protein.write_gro(fgrotmp)
        with GroFile(fgrotmp) as fgro:
            assert fgro.natoms == 34
            for line, atom in zip(fgro, molecule_protein):
                atom_gro = AtomGro(line)
                assert atom.atom_gro == atom_gro

    def test_update_from_top(self, molecule_bmim: Molecule):
        """
        Test for update_from_molecule_top method.
        """
        # Make a copy of the original top
        fname = os.path.join(ACTUAL_PATH, '../../gaddlemaps/data/BMIM_AA.itp')
        mol_top = MoleculeTop(fname)
        for at1, at2 in zip(mol_top, molecule_bmim):
            assert at1.name == at2.name

        mol_top[1].name = 'test'
        molecule_bmim.update_from_molecule_top(mol_top)
        for at1, at2 in zip(mol_top, molecule_bmim):
            assert at1.name == at2.name
        assert molecule_bmim[1].name == 'test'

    def test_distance_to(self, molecule_protein: Molecule):
        """
        Test for method related with distances.
        """
        center = molecule_protein.geometric_center
        # Distance to zero
        assert ((center**2).sum())**.5 == molecule_protein.distance_to_zero

        # Distance to self
        assert molecule_protein.distance_to(molecule_protein) == 0
        # Distance to zero
        assert molecule_protein.distance_to([0, 0, 0]) == molecule_protein.distance_to_zero
        # Distance to zero with box vector
        box_vects = np.diag(molecule_protein.geometric_center)
        assert round(molecule_protein.distance_to([0, 0, 0],
                                                  box_vects=box_vects), 8) == 0
        # Distance to zero with box vector with inv
        inv_box = np.linalg.inv(box_vects)
        assert round(molecule_protein.distance_to([0, 0, 0], box_vects=inv_box,
                                                  inv=True), 8) == 0

    def test_copy(self, molecule_bmim: Molecule):
        """
        Test the copy method of Molecule class.
        """
        copy_mol = molecule_bmim.copy()

        assert copy_mol == molecule_bmim
        assert copy_mol.resnames == molecule_bmim.resnames

        assert copy_mol.molecule_top == molecule_bmim.molecule_top
        assert copy_mol.molecule_top is molecule_bmim.molecule_top

        assert copy_mol.residues == molecule_bmim.residues
        assert copy_mol.residues is not molecule_bmim.residues

        # Tricky change in the resname of the topology
        copy_mol.molecule_top.resnames = ['Test']
        assert molecule_bmim.molecule_top.resnames == ['Test']

    def test_deep_copy(self, molecule_bmim: Molecule):
        """
        Test the deep_copy method of Molecule class.
        """
        copy_mol = molecule_bmim.deep_copy()

        assert copy_mol == molecule_bmim
        assert copy_mol.resnames == molecule_bmim.resnames

        assert copy_mol.molecule_top == molecule_bmim.molecule_top
        assert copy_mol.molecule_top is not molecule_bmim.molecule_top

        assert copy_mol.residues == molecule_bmim.residues
        assert copy_mol.residues is not molecule_bmim.residues

        copy_mol.molecule_top[0].name = 'T2'
        copy_mol.residues[0][0].name = 'T2'
        assert molecule_bmim != copy_mol

        # Tricky change in the resname of the topology
        copy_mol.molecule_top.resnames = ['Test']
        assert molecule_bmim.molecule_top.resnames == ['BMIM']


import logging
import numpy as np
import multiprocessing as mp

from superconductivity.utils import initialize_worker, map_async_stoppable
from superconductivity.phonon.acoustic_mismatch import (
    incident_l, incident_sh, incident_sv)

log = logging.getLogger(__name__)
log.addHandler(logging.NullHandler())

class Geometry:
    def __init__(self, width, thickness, density, longitudinal_speed,
                 transverse_speed, membrane=None, diffuse=False,
                 dimensions=3, seed=None):
        """
        Phonon ray tracing in a film with finite width and thickness but
        infinite length. All units are assumed to be SI base units.

        Args:
            width: float
                The width of the film.
            thickness: N array
                The thickness of each layer bottom to top not including the
                substrate.
            density: N + 1 array
                The density of each layer bottom to top including the
                substrate.
            longitudinal_speed: N + 1 array
                The longitudinal speed of sound bottom to top including the
                substrate.
            transverse_speed: N + 1 array
                The transverse speed of sound bottom to top including the
                substrate.
            membrane: N array
                The escape chance out of the side wall from bottom to top not
                including the substrate. If the layer is not a membrane set to
                0.
            diffuse: boolean
                If True, diffuse scattering is assumed for all reflected
                vectors. If False, the reflected angle is computed from the
                acoustic mismatch model (only for interfaces).
            dimensions: integer
                The number of dimensions to do the simulation in. If 2, the y
                dimension is not used (x is along the trace width, z is top
                to bottom, and y is down the trace).
            seed: integer
                A seed for the random number generator.
        """
        self.w = width
        self.t = np.array(thickness, ndmin=1, copy=False)
        self.rho = np.array(density, ndmin=1, copy=False)
        self.cl = np.array(longitudinal_speed, ndmin=1, copy=False)
        self.ct = np.array(transverse_speed, ndmin=1, copy=False)
        self.rng = np.random.default_rng(seed=seed)
        self.diffuse = diffuse
        if dimensions != 2 and dimensions != 3:
            raise ValueError("The 'dimensions' parameter must be 2 or 3.")
        self.dimensions = dimensions

        # derived parameters
        self.z = np.concatenate(([0], np.cumsum(self.t)))  # interfaces
        self.rho = np.concatenate((self.rho, [0]))  # add vacuum
        self.cl = np.concatenate((self.cl, [1]))
        self.ct = np.concatenate((self.ct, [1]))

        self.left_point = np.array([-self.w / 2, 0., 0.])
        self.left_normal = np.array([1., 0., 0.])

        self.right_point = np.array([self.w / 2, 0., 0.])
        self.right_normal = np.array([-1., 0., 0.])

        self.top_normal = np.array([0., 0., -1.])
        self.bottom_normal = np.array([0., 0., 1.])

        if membrane is None:
            membrane = np.zeros_like(self.t)
        self.membrane = membrane

        self.max_iterations = 10000

    def simulate(self, n=100000, parallel=False):
        # Define random positions along x on the top of the geometry.
        positions = self.w * self.rng.random(n) - self.w / 2
        positions = np.array([positions, np.zeros_like(positions),  # x, y, z
                              np.full_like(positions, self.z[-1])]).T

        # Define uniformly distributed directions into the geometry. The
        # normal distribution ensures that there is no angle dependence in
        # the random numbers.
        directions = self.rng.normal(size=(n, 3))
        zero = (directions[:, 2] == 0)  # zero z-velocity
        while zero.any():  # remove any phonons with zero z-velocity
            directions[zero, 2] = self.rng.normal(size=zero.sum())
            zero = (directions[:, 2] == 0)
        directions[directions[:, 2] > 0, 2] *= -1  # flip the up directions
        if self.dimensions == 2:
            directions[:, 1] = 0  # no y velocity if 2D
        directions /= np.linalg.norm(directions, axis=1, keepdims=True)

        # Randomly select the phonon modes
        mode_types = self.rng.integers(0, high=3, size=n)  # 0, 1, or 2
        modes = np.empty_like(directions)

        # SH waves (perpendicular to direction of motion and top normal).
        shear = (mode_types == 0) | (mode_types == 1)
        modes[shear, :] = np.cross(directions[shear, :], self.top_normal)

        # SV waves.
        sv = (mode_types == 1)
        modes[sv, :] = np.cross(modes[sv, :], directions[sv, :])

        # Longitudinal waves (direction of motion)
        modes[mode_types == 2, :] = directions[mode_types == 2, :]
        modes /= np.linalg.norm(modes, axis=1, keepdims=True)

        # Zero modes are created by directions = [0, 0, 1]
        # Set them to x-polarized.
        zero = (modes == 0).all(axis=1)
        modes[zero, 0] = 1

        # Pick rng seeds to make forked processes all different.
        seeds = self.rng.integers(1, 100000, size=mode_types.size)

        # Loop over phonons.
        times = np.empty((n, self.z.size - 1))
        args = zip(positions, directions, modes, seeds)
        parallel = mp.cpu_count() // 2 if parallel is True else parallel
        if parallel:  # calculate the escape times in parallel
            ctx = mp.get_context("fork")
            with ctx.Pool(parallel, initializer=initialize_worker) as pool:
                results = map_async_stoppable(pool, self.escape, args)
                # wait for the results
                try:
                    log.info("Waiting for results from parallel computation")
                    results.wait()
                except KeyboardInterrupt as error:
                    log.error("Keyboard Interrupt encountered: retrieving "
                              "computed fits before exiting")
                    pool.terminate()
                    pool.join()
                    raise error
                finally:
                    for index, result in enumerate(results.get()):
                        times[index, :] = result
                    log.info("Retrieved results from parallel computation")
        else:  # calculate the escape times serially
            for index, arg in enumerate(args):
                times[index, :] = self.escape(*arg)
                log.info(f"Phonon {index} escaped in {times[index]}")
        return times

    def escape(self, position, direction, mode, seed, plot=False):
        init = [position, direction, mode, seed]
        rng = np.random.default_rng(seed=seed)
        total_time = np.zeros(self.z.size - 1)
        i = 0
        stop = False
        x, z, axes = [], [], None  # for plotting
        while i <= self.max_iterations:
            if plot:  # for debugging
                # Defer import since matplotlib is not a package dependency.
                from matplotlib import pyplot as plt
                x.append(position[0])
                z.append(position[2])
                if i == 0:  # draw the borders
                    _, axes = plt.subplots()
                    for zi in self.z:
                        axes.plot([-self.w / 2, self.w / 2], [zi, zi], 'k--')
                    z_max = self.z.max()
                    axes.plot([self.w / 2, self.w / 2], [0, z_max], 'k--')
                    axes.plot([-self.w / 2, -self.w / 2], [0, z_max], 'k--')
                elif stop or i == self.max_iterations - 1:
                    axes.plot(x, z)
                    axes.plot([x[0], x[-1]], [z[0], z[-1]], linestyle='none',
                              marker='o')
                    plt.show()

            # Stop when the phonon enters the substrate.
            if stop:
                break

            # Get the next position, direction, mode, and time.
            position, direction, mode, time, level, stop = self.propagate(
                position, direction, mode, rng)
            total_time[level - 1] += time

            # Increment the iteration counter.
            i += 1

        # Warn if we iterated more than expected and had to stop.
        if i > self.max_iterations:
            with np.printoptions(precision=np.finfo(np.float64).precision + 1,
                                 floatmode='maxprec_equal'):
                log.warning("The maximum number of iterations was achieved "
                            "for these initial parameters: "
                            f"{init[0]}, {init[1]}, {init[2]}, {init[3]}.")

        return total_time

    def propagate(self, position, direction, mode, rng=None):
        down = (direction[2] <= 0)  # phonon traveling down
        left = (direction[0] <= 0)  # phonon traveling left
        if down:
            level = np.nonzero(position[2] <= self.z)[0][0]
            horizontal_point = np.array([0, 0, self.z[level - 1]])
            plane_normal = self.bottom_normal
            rho = [self.rho[level], self.rho[level - 1]]
            ct = [self.ct[level], self.ct[level - 1]]
            cl = [self.cl[level], self.cl[level - 1]]
        else:
            level = np.nonzero(position[2] < self.z)[0][0]
            horizontal_point = np.array([0, 0, self.z[level]])
            plane_normal = self.top_normal
            rho = [self.rho[level], self.rho[level + 1]]
            ct = [self.ct[level], self.ct[level + 1]]
            cl = [self.cl[level], self.cl[level + 1]]

        # Find where the phonon intersects with the vertical wall or
        # horizontal interface.
        new_position = self.intersection(
            position, direction, horizontal_point, plane_normal)
        if not (-self.w / 2 <= new_position[0] <= self.w / 2):
            # The phonon did not hit a horizontal interface so we compute
            # the intersection with the vertical wall.
            plane_normal = self.left_normal if left else self.right_normal
            vertical_point = self.left_point if left else self.right_point
            new_position = self.intersection(
                position, direction, vertical_point, plane_normal)
            # chance of leaving if level extends past the trace width
            if self.membrane[level - 1]:
                if rng.random() < self.membrane[level - 1]:
                    longitudinal = (direction == mode).all()
                    speed = self.cl[level] if longitudinal else self.ct[level]
                    time = np.linalg.norm(position - new_position) / speed
                    stop = True  # exit the material
                    return new_position, direction, mode, time, stop
            rho[1] = 0  # override material2 with vacuum
        else:
            # ensure the z component is identically one of the interfaces
            # to avoid rounding errors.
            new_position[2] = self.z[
                np.argmin(np.abs(new_position[2] - self.z))]

        # Compute the new direction and mode after interacting with the wall
        # or interface.
        new_direction, new_mode = self.interface(
            direction, mode, plane_normal, rho, ct, cl, rng)

        # Calculate the time spent between bounces.
        longitudinal = (direction == mode).all()
        speed = self.cl[level] if longitudinal else self.ct[level]
        time = np.linalg.norm(position - new_position) / speed
        stop = (new_position[2] == 0 and new_direction[2] < 0)
        return new_position, new_direction, new_mode, time, level, stop

    @staticmethod
    def intersection(position, direction, plane_point, plane_normal):
        """
        Compute the intersection between a line and a plane.

        Args:
            position: numpy.ndarray size 3
                A point on the line.
            direction: numpy.ndarray size 3
                The direction of the line.
            plane_point: numpy.ndarray size 3
                A point on the plane.
            plane_normal: numpy.ndarray size 3
                The normal vector to the plane.
        """
        dot = plane_normal.T @ direction
        if dot == 0:
            with np.errstate(invalid='ignore'):
                intersection = plane_point + np.nan_to_num(
                    direction * np.inf, nan=0.0, posinf=np.inf, neginf=-np.inf)
            return intersection
        w = position - plane_point
        fac = -(plane_normal.T @ w) / dot
        return position + fac * direction

    def interface(self, direction, mode, plane_normal, rho, ct, cl, rng=None):
        # Set the random number generator
        if rng is None:
            rng = np.random.default_rng()

        # Determine the incident angle
        incident_angle = np.arccos(np.dot(direction, -plane_normal))

        # Compute the reflection and transmission probabilities and angles
        if (mode == direction).all():  # longitudinal mode
            r = incident_l(incident_angle, rho, ct, cl)
        else:  # in a superposition of SV and SH
            sh_dir = np.cross(direction, plane_normal)  # direction of SH waves
            if (sh_dir == 0).all():
                sh_dir = mode  # mode is SH
            sh_dir /= np.linalg.norm(sh_dir)
            p_sh = np.dot(sh_dir, mode)**2
            p_sv = max(0., 1 - p_sh)  # max for rounding errors
            incident_mode_type = rng.choice([0, 1], p=[p_sh, p_sv])
            if incident_mode_type == 0:
                r = incident_sh(incident_angle, rho, ct)
            else:
                r = incident_sv(incident_angle, rho, ct, cl)

        # Pick a resulting mode
        options = [[2, False, r["L"]["T"]["angle"]],
                   [2, True, r["L"]["R"]["angle"]],
                   [1, False, r["SV"]["T"]["angle"]],
                   [1, True, r["SV"]["R"]["angle"]],
                   [0, False, r["SH"]["T"]["angle"]],
                   [0, True, r["SH"]["R"]["angle"]]]
        prob = [r["L"]["T"]["P"], r["L"]["R"]["P"],
                r["SV"]["T"]["P"], r["SV"]["R"]["P"],
                r["SH"]["T"]["P"], r["SH"]["R"]["P"]]
        new_mode_type, reflected, angle = rng.choice(options, p=prob)
        if reflected and (rho[1] == 0 or self.diffuse):
            # Randomize the reflected angle due to surface roughness if
            # the transmission interface is vacuum or we are assuming
            # diffuse reflection everywhere.
            # This is what they do in DeVisser et al. 2021
            # (doi:10.1103/PhysRevApplied.16.034051)
            new_direction = rng.normal(size=3)  # angle independent
            z = np.dot(new_direction, plane_normal)
            # Retry if we get a zero out of plane component
            while z == 0:
                new_direction = rng.normal(size=3)
                z = np.dot(new_direction, plane_normal)
            if self.dimensions == 2:
                new_direction[1] = 0  # remove y-component of the new direction
            # Ensure the result is a reflection.
            if z < 0:
                new_direction -= 2 * z * plane_normal
        else:
            # Determine the new direction by scaling the in plane component of
            # the direction vector and keeping the normal component the same.
            z = np.dot(direction, plane_normal) * plane_normal
            xy = direction - z
            ratio = np.tan(angle)
            z_mag = np.sqrt(np.dot(z, z))
            xy_mag = np.sqrt(np.dot(xy, xy)) if ratio != 0 else 1
            xy *= np.abs(ratio * z_mag / xy_mag)
            if reflected:  # specular reflection
                new_direction = xy - z
            else:
                new_direction = xy + z
        new_direction /= np.linalg.norm(new_direction)

        # Determine the new mode
        if new_mode_type == 2:
            new_mode = new_direction
        else:
            new_mode = np.cross(new_direction, plane_normal)  # SH
            if (new_mode == 0).all():  # angle = 0
                new_mode = mode  # SV or SH
            elif new_mode_type == 1:
                new_mode = np.cross(new_direction, new_mode)  # SV
        new_mode /= np.linalg.norm(new_mode)
        return new_direction, new_mode


import keras
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D, Activation

def PatchNet(input_shape,
             num_filters=(32, 64),
             filter_size=(3, 3),
             num_fc_units=(128,),
             drop_keep_prob=(0.5,),
             num_classes=77,
             optimizer='rmsprop'):
    # input
    model = Sequential()
    model.add(Conv2D(num_filters[0], filter_size, input_shape=input_shape))
    model.add(Activation('relu'))

    # build conv parts
    for n in num_filters[1:]:
        model.add(Conv2D(n, filter_size))
        model.add(Activation('relu'))
        model.add(MaxPooling2D(pool_size=(2, 2)))
        if len(drop_keep_prob) > 1:
            model.add(Dropout(drop_keep_prob[0]))

    # build fc parts
    model.add(Flatten())
    for n in num_fc_units:
        model.add(Dense(n))
        model.add(Activation('relu'))
        model.add(Dropout(drop_keep_prob[-1]))

    # output
    model.add(Dense(num_classes))
    model.add(Activation('softmax'))

    if optimizer == 'rmsprop':
        opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)
    elif optimizer == 'adam':
        opt = keras.optimizers.adam(lr=0.0001)
    elif optimizer == 'sgd':
        opt = keras.optimizers.sgd(lr=0.0001, momentum=0.9, nesterov=True)
    else:
        raise ValueError
    # opt = keras.optimizers.Adam(lr = 0.0001)
    model.compile(loss='categorical_crossentropy',
                  optimizer=opt,
                  metrics=['accuracy'])
    return model


import pandas as pd

def calculate_monthly_payment(total_amount, down_payment, interest_rate, amortization_period):
    total_amount = float(total_amount)
    amortization_period = int(amortization_period)
    down_payment = float(down_payment)
    interest_rate = float(interest_rate)
    total_amount -= down_payment
    if (interest_rate > 0) and (amortization_period > 0):
        monthly_interest_rate = interest_rate / 12
        payment = total_amount * (monthly_interest_rate * (1 + monthly_interest_rate) ** amortization_period) / ((1 + monthly_interest_rate) ** amortization_period - 1)
        print(total_amount)
        print(monthly_interest_rate),
        print(amortization_period)
        print(payment)
        return payment
    return 0

def generate_mortgage_schedule(total_amount, down_payment, interest_rate, amortization_period):
    total_amount = float(total_amount)
    amortization_period = int(amortization_period)*12
    down_payment = float(down_payment)
    interest_rate = float(interest_rate)

    monthly_payment = calculate_monthly_payment(total_amount,down_payment, interest_rate, amortization_period)
    remaining_balance = total_amount - down_payment
    mortgage_schedule = []

    for month in range(1, amortization_period + 1):
        interest_payment = remaining_balance * (interest_rate / 12)
        principal_payment = monthly_payment - interest_payment
        remaining_balance -= principal_payment

        mortgage_schedule.append({
            'Month': month,
            'Principal Payment': principal_payment,
            'Interest Payment': interest_payment,
            'Total Payment': monthly_payment,
            'Remaining Balance': remaining_balance
        })

    return mortgage_schedule

# mortgage_schedule = generate_mortgage_schedule(total,interest, amort_period*12)
# mortgage_schedule_df = pd.DataFrame(mortgage_schedule)


from django.urls import include, path
from rest_framework.routers import DefaultRouter

from custom_auth.views import (
    CustomAuthTokenLoginOrRequestMFACode,
    CustomAuthTokenLoginWithMFACode,
    FFAdminUserViewSet,
    delete_token,
)

app_name = "custom_auth"

ffadmin_user_router = DefaultRouter()
ffadmin_user_router.register(r"users", FFAdminUserViewSet)

urlpatterns = [
    # Override some endpoints for throttling requests and delete user
    path(
        "login/",
        CustomAuthTokenLoginOrRequestMFACode.as_view(),
        name="custom-mfa-authtoken-login",
    ),
    path(
        "login/code/",
        CustomAuthTokenLoginWithMFACode.as_view(),
        name="mfa-authtoken-login-code",
    ),
    path("", include(ffadmin_user_router.urls)),
    path("token/", delete_token, name="delete-token"),
    # NOTE: endpoints provided by `djoser.urls`
    # are deprecated and will be removed in the next Major release
    path("", include("djoser.urls")),
    path("", include("trench.urls")),  # MFA
    path("", include("trench.urls.djoser")),  # override necessary urls for MFA auth
    path("oauth/", include("custom_auth.oauth.urls")),
]


# -*- coding: utf-8 -*-
from numpy import arange, asarray, concatenate, convolve, exp, mat, zeros
from numpy.linalg import pinv
from scipy.misc import factorial
from matplotlib.pyplot import figure, plot

def deconvolve(x, y, sigma, noise, wsize=3):
    """ Deconvolves function y(x) by factor sigma.
        Noisy data  => large wsize (so derivatives don't fluctuate wildly)
        Large wsize => curve overshoots at base of peaks and peaks cannot
        become so narrow
        => We want smallest wsize possible while still getting smooth results
    """
    sigma = float(sigma)
    ret = zeros(y.size)
    dx = x[1] - x[0]

    # Find optimal window size for smoothing of y
    w0 = 3
    dw = durbin_watson(y, savitzky_golay(y, w0, 2, dx))
    while True:
        dw0 = durbin_watson(y, savitzky_golay(y, w0+2, 2, dx))
        if (abs(dw0-1) < abs(dw-1)):
            dw = dw0
            w0 = w0 + 2
        else:
            break

    i = 0
    while True:
        w = wsize*2*i + w0
        y0 = concatenate((zeros(w), y, zeros(w)))
        temp = savitzky_golay(y0, w, 2*i, dx, deriv=2*i)[w:-w]
        c = (-sigma**2/4)**i / factorial(i)
        temp *= c

        if max(abs(temp)) > max(abs(y)):    # Process probably won't converge
            print('Deconvolution Failed')
            return None

        elif max(abs(temp)) > noise:
            ret += temp
            i += 1

        else:
            break

        # Remove noise along x=0 at each iteration
        # (otherwise noise may be amplified)
        ret[ret < noise] = 0

    return ret

def durbin_watson(y1, y2):
    """ Returns the Durbin-Watson parameter for a pair of curves.
        The closer DW is to 1, the better y2 approximates y1
    """
    n = asarray(y1).size
    num = 0.
    den = (y1[0]-y2[0])**2

    for i in range(1, n):
        temp = (y1[i]-y2[i]) - (y1[i-1]-y2[i-1])
        num += temp**2
        den += (y1[i]-y2[i])**2

    n = float(n)

    return (n/(n-1)) * (num / (4*den))

def G(x, n, p):
    """ Returns the evaluation at x of the sum of n Gaussian curves of the form
        A * exp(-(x-m)^2/s).
        For the kth curve (k=0...n-1), A=p[3*k], m=p[3*k+1], s=p[3*k+2].
    """
    if n == 0:
        return 0
    else:
        return p[0] * exp(-(x-p[1])**2 / p[2]) + G(x, n-1, p[3:])

def multi_deconvolve(x, y, sigs, noise, wsize=3):
    """ Apply multiple recursive deconvolutions, with deconvolution parameters
        given by sigs. Applying many small recursive deconvolutions is often
        more stable than apply one large deconvolution.
    """
    n = len(sigs)

    if n > 1:
        yd = deconvolve(x, y, sigs[-1], noise, wsize)
        if yd is None:
            return None
        else:
            return multi_deconvolve(x, yd, sigs[:-1], noise, wsize)
    else:
        return deconvolve(x, y, sigs[0], noise, wsize)

def savitzky_golay(y, window_size, order, dx, deriv=0):
    """ Uses the Savitsky-Golay algorithm to return a smoothed version of the
        specified derivative of y
    """
    half_window = (window_size - 1) // 2
    b = mat([[k**i for i in range(order+1)]
             for k in range(-half_window, half_window+1)])
    m = pinv(b).A[deriv] * (1/dx)**deriv * factorial(deriv)

    firstvals = y[0] + abs(y[1:half_window+1][::-1] - y[0])
    lastvals = y[-1] - abs(y[-half_window-1:-1][::-1] - y[-1])

    y = concatenate((firstvals, y, lastvals))

    return convolve(m[::-1], y, mode="valid")

if __name__ == "__main__":
    """ A demonstration of the deconvolution of a superposition of two
        overlapping Gaussian curves
    """
    x = arange(-10, 10, 0.025)
    y = G(x, 2, [100, 0, 1, 70, 1.5, 1])
    figure(1)
    plot(x, y, c='b')                           # Plot sum of curves
    plot(x, G(x, 1, [100, 0, 1]), c='r')        # Plot curve 1
    plot(x, G(x, 1, [70, 1.5, 1]), c='r')       # Plot curve 2
    figure(2)
    plot(x, deconvolve(x, y, 0.5, 0.1), c='b')  # Plot deconvolution
    plot(x, G(x, 1, [100, 0, 1]), c='r')        # Plot curve 1
    plot(x, G(x, 1, [70, 1.5, 1]), c='r')       # Plot curve 2


from pybricks.pupdevices import Motor
from pybricks.tools import wait
from pybricks.parameters import Port
from pybricks import version

print(version)

# Initialize the motor.
motor = Motor(Port.A)

# Allocate the data logs.
DURATION = 12000
motor.log.start(DURATION)
motor.control.log.start(DURATION)

for i in range(11):
    motor.dc(i * 10)
    wait(1000)

# Wait so we can also log the stopped behavior.
motor.dc(0)
wait(500)
motor.stop()

# Transfer data logs.
print("Transferring data...")
motor.log.save("servo.txt")
motor.control.log.save("control.txt")
print("Done")


from flask import Flask, request, make_response, jsonify

app = Flask(__name__)
app.debug = True
DT = {}

@app.route("/kvs/<string:key_name>", methods=['GET', 'PUT', 'DELETE'])

def root(key_name):
	if (request.method == 'GET'):
		return getValue(key_name)

	if (request.method == 'PUT'):
		v = request.form['val']
		return putValue(key_name,v)

	if (request.method == 'DELETE'):
		#return "Delete value and key: %s" % key_name
		return delValue(key_name)
	else:
		return "Invalid request."

# Working
def getValue(key):
	if (key in DT):
		value = DT[key]
		j = jsonify(msg='success',value=value)
		return make_response(j,200,{'Content-Type':'application/json'})
	else:
		j = jsonify(msg='error',error='key does not exist')
		return make_response(j,404,{'Content-Type':'application/json'})

# Working
def putValue(key,value):
	if (key in DT):
		DT[key]=value
		j = jsonify(msg='success',replaced=1)
		return make_response(j,200, {'Content-Type' : 'application/json'})
	else:
		DT[key]=value
		j = jsonify(msg='success',replaced=0)
		return make_response(j,201,{'Content-Type' : 'application/json'})

# idk
def delValue(key):
	if (key in DT):
		DT.pop(key,None)
		j = jsonify(msg='success')
		return make_response(j,200,{'Content-Type':'application/json'})
	else:
		j = jsonify(msg='error',error='key does not exist')
		return make_response(j,404,{'Content-Type':'application/json'})

if __name__ == '__main__':
	app.run(port=8080)


import sys
import struct
import argparse
import numpy
import warnings

delimiter = ','

def fixInput(val):
    if val is None:
        return 0
    try:
        retval = int(val)
    except ValueError:
        retval = ord(val)

    return retval

if len(sys.argv) != 5 and len(sys.argv) != 6:
    print("Usage:")
    print("\tcsv_to_binary.py <input filename> <column choice> <datatype> <output filename> [delimiter]")
    print()
    print("This program converts one column of a text file containing a table of data,")
    print("(a comma-separated values file by default), into a binary file.")
    print()
    print("The <column choice> should be an integer in the range [0, N-1], where N is the number of columns.")
    print("The <datatype> option should be one of 'int', 'long', 'float', 'double', or 'string'.")
    print("'string' keeps the text, converting it to UTF-16 with no separators between the values.")
    print("The [delimiter] is an optional argument, and defaults to '%s'" % delimiter)
    print("Some delimiters may need to be surrounded by quotation marks or prefixed by a backslash, depending on")
    print("the shell, for example space, semicolon, or vertical pipe, due to the command line parsing")
    print("interpreting the space or semicolon as a parameter separator or command separator, instead of a")
    print("parameter to this script.")
    print()
    print("Examples:")
    print("    text_to_binary.py ExampleFloatData.csv 2 float ZValues.bin")
    print("    text_to_binary.py ExampleTable.txt 5 long Dates.bin '|'")
    print("    text_to_binary.py SpaceSeparatedData.txt 0 int FirstColumn.bin ' '")
    print()
    exit()

in_fname = sys.argv[1]
col_num = sys.argv[2]
datatype = sys.argv[3]
out_fname = sys.argv[4]

if len(sys.argv) == 6:
    delimiter = sys.argv[5]

# Add more datatypes if needed
if datatype == "int":
    dtype = "int32"
elif datatype == "long":
    dtype = "int64"
elif datatype == "float":
    dtype = "float32"
elif datatype == "double":
    dtype = "float64"
elif datatype == "string":
    dtype = "str"
else:
    print("Please select datatype int, long, float, double, or string")
    exit()

print("Reading column " + col_num + ", of type " + datatype + "...")

chunk_size = 10000000
finished = False
offset = 0
with open(str(in_fname), "r") as inFile:
    with open(str(out_fname), "wb") as newFile:
        with warnings.catch_warnings():
            while not finished:
                in_data=numpy.genfromtxt(inFile, dtype=dtype,
                max_rows=chunk_size, usecols=(int(col_num),), delimiter=delimiter, loose=False)

                if offset == 0:
                    # don't warn about an empty file after we have read something
                    warnings.filterwarnings('ignore', r'genfromtxt: Empty input file:')

                if in_data.size > 0:
                    in_data.tofile(newFile)
                    offset += in_data.size
                else:
                    finished = True
if offset != 0:
    print('Wrote '+str(offset)+' '+datatype+'s to '+str(out_fname))
else:
    print('Wrote no data')