# pyinstaller -F -w .\Python学习\exe文件qt\main.py

import sys
import datetime
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FC
from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QPushButton, QSlider,
                              QFormLayout, QMessageBox, QComboBox, QLineEdit, QCheckBox,
                              QDesktopWidget, QHBoxLayout, QFileDialog, QDialog, QVBoxLayout)
import pandas as pd
import networkx as nx
import xlwt
# import graphviz as gz
import pygraphviz as pgv
import os

import Scheduler.DAG_Generator as DG
import Scheduler.DAG_Features_Analysis as DFA
import Scheduler.DAG_Data_Processing as DDP
import Scheduler.DAG_WCET_Config as DWC

class GUI:
    def __init__(self):
        self.Flow_Data_Set = None
        self.app = QApplication(sys.argv)  # 1.1 获取命令行参数
        # ######### 操作区 ########
        """     create layout      """
        # ### 标题
        # self.formLayout = QVBoxLayout()
        self.formLayout = QFormLayout()
        self.window = QWidget()
        self.window.setLayout(self.formLayout)

        self.formLayout.addRow(QLabel("DAG Data Input"))
        self.Excel_DAG_data_InPath  = QLineEdit()
        self.Excel_DAG_param_InPath = QLineEdit()
        self.Excel_Flow_data_InPath = QLineEdit()

        self.Button_DAG_Data_Input = QPushButton('DAG参数抽象')
        self.Button_DAG_Data_Input.clicked.connect(lambda: self.Input_Addr('DAG_data', self.Excel_DAG_data_InPath))
        self.Button_DAG_Param_Input = QPushButton('DAG参数提取')
        self.Button_DAG_Param_Input.clicked.connect(lambda: self.Input_Addr('DAG_param', self.Excel_DAG_param_InPath))
        self.Button_Flow_Param_Input = QPushButton('Flow数据提取')
        self.Button_Flow_Param_Input.clicked.connect(lambda: self.Input_Addr('Flow_data', self.Excel_Flow_data_InPath))

        # 1.1 确定DAG_Excel输入地址
        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel("(1) 输入_Excel_Data_Addr_文件路径:"))
        hlayout.addWidget(self.Excel_DAG_data_InPath)
        hlayout.addWidget(self.Button_DAG_Data_Input)
        self.formLayout.addRow(hlayout)

        # 1.2 确定DAG_Excel输入地址
        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel("(2) 输入_Excel_Param_Addr_文件路径:"))
        hlayout.addWidget(self.Excel_DAG_param_InPath)
        hlayout.addWidget(self.Button_DAG_Param_Input)
        self.formLayout.addRow(hlayout)

        # hlayout = QHBoxLayout()
        # hlayout.addWidget(QLabel("(3) 输入_Excel_Flow_Addr_文件路径:"))
        # hlayout.addWidget(self.Excel_Flow_data_InPath)
        # hlayout.addWidget(self.Button_Flow_Param_Input)
        # self.formLayout.addRow(hlayout)

        # 2 DAG特性参数；
        # self.formLayout.addRow(QLabel('DAG Critical Features list'))
        self.label = QLabel("DAG Generator Tool")

        self.Number_Of_Nodes        = QLineEdit()   # (1.):
        self.Number_Of_Edges        = QLineEdit()   # (1.):
        self.Number_Of_Levels       = QLineEdit()   # (1.):
        self.Number_Of_Width        = QLineEdit()   # (1.):
        self.Gen_DAG_NUM            = QLineEdit()
        self.Connection_Rate        = QLineEdit()   # (1.):
        self.Jump_Level             = QLineEdit()   # (1.):
        self.DAG_volume             = QLineEdit()   # (1.):
        self.DAG_Cri_Rate           = QLineEdit()   # (1.):
        self.Param_Max_Shape        = QLineEdit()
        self.Param_Min_Shape        = QLineEdit()
        self.Param_Max_In_Degree    = QLineEdit()
        self.Param_Max_Out_Degree   = QLineEdit()
        self.Param_Max_WCET         = QLineEdit()
        self.Param_Min_WCET         = QLineEdit()

        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel("(1.1) Nodes_Num:"))
        hlayout.addWidget(self.Number_Of_Nodes)
        # hlayout.addWidget(QLabel("(1.2)DAG的边数量:"))
        # hlayout.addWidget(self.Number_Of_Edges)
        hlayout.addWidget(QLabel("(1.2) Level:"))
        hlayout.addWidget(self.Number_Of_Levels)
        hlayout.addWidget(QLabel("(1.3) Width:"))
        hlayout.addWidget(self.Number_Of_Width)
        hlayout.addWidget(QLabel("(1.4) Number_Of_DAGs:"))
        hlayout.addWidget(self.Gen_DAG_NUM)
        self.formLayout.addRow(hlayout)

        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel("(1.5) Connection_Rate:"))
        hlayout.addWidget(self.Connection_Rate)
        hlayout.addWidget(QLabel("(1.6) Jump_Level:"))
        hlayout.addWidget(self.Jump_Level)
        hlayout.addWidget(QLabel("(1.7) Max_WCET:"))
        hlayout.addWidget(self.Param_Max_WCET)
        hlayout.addWidget(QLabel("(1.8) Min_WCET:"))
        hlayout.addWidget(self.Param_Min_WCET)
        # hlayout.addWidget(QLabel("(1.7)DAG的体积:"))
        # hlayout.addWidget(self.DAG_volume)
        # hlayout.addWidget(QLabel("(1.8)DAG的关键路径比:"))
        # hlayout.addWidget(self.DAG_Cri_Rate)
        self.formLayout.addRow(hlayout)

        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel("(1.9) Max_Shape:"))
        hlayout.addWidget(self.Param_Max_Shape)
        hlayout.addWidget(QLabel("(1.10) Min_Shape:"))
        hlayout.addWidget(self.Param_Min_Shape)
        hlayout.addWidget(QLabel("(1.11) Max_In_Degree:"))
        hlayout.addWidget(self.Param_Max_In_Degree)
        hlayout.addWidget(QLabel("(1.12) Max_Out_Degree:"))
        hlayout.addWidget(self.Param_Max_Out_Degree)
        self.formLayout.addRow(hlayout)

        # 3 DAG生成参数；
        self.formLayout.addRow(QLabel('DAG Generation Paramter'))
        self.Arrive_time_bottom  = QLineEdit()
        self.Arrive_time_roof    = QLineEdit()
        self.Gen_Jitter          = QLineEdit()
        self.Branch_node         = QLineEdit()

        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel("Arrive time:"))
        hlayout.addWidget(QLabel("["))
        hlayout.addWidget(self.Arrive_time_bottom)
        hlayout.addWidget(QLabel("—"))
        hlayout.addWidget(self.Arrive_time_roof)
        hlayout.addWidget(QLabel("]\t"))

        hlayout.addWidget(QLabel("Random range(%):"))
        hlayout.addWidget(self.Gen_Jitter)
        hlayout.addWidget(QLabel("\t"))

        self.level_change = QCheckBox('level_change')
        hlayout.addWidget(self.level_change)
        hlayout.addWidget(QLabel("\t"))

        hlayout.addWidget(QLabel("branch_nodes:"))
        hlayout.addWidget(self.Branch_node)

        self.formLayout.addRow(hlayout)

        # 4 确定输出地址
        self.formLayout.addRow(QLabel("DAG Data Output:"))
        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel("输出_Excel_DAG_Data_文件路径:"))
        self.Excel_DAG_data_OutPath = QLineEdit()
        self.Output_Button = QPushButton('打开')
        self.Output_Button.clicked.connect(self.Output_Data_SELF)  # 按钮关联函数
        hlayout.addWidget(self.Excel_DAG_data_OutPath)
        hlayout.addWidget(self.Output_Button)
        self.formLayout.addRow(hlayout)

        self.formLayout.addRow(QLabel('DAG Output Model:'))
        # self.DAG_Generator_Param = QPushButton('基于参数生成DAG(1/2)\n(附加：生成DAG数量)')
        self.DAG_Generator_Param = QPushButton('DAG Generate')
        self.DAG_Generator_Param.clicked.connect(self.DAG_Generator_Param_Based)

        # self.DAG_Generator_Flow  = QPushButton('基于流生成DAG(3)\n(附加：生成DAG数量， 生成流数量和jitter比例)')
        # self.DAG_Generator_Flow.clicked.connect(self.DAG_Generator_Flow_Based)
        hlayout = QHBoxLayout()
        hlayout.addWidget(self.DAG_Generator_Param)
        # hlayout.addWidget(self.DAG_Generator_Flow)
        self.formLayout.addRow(hlayout)

        self.window.resize(500, 130)
        # self.window.setWindowTitle('DAG Critical Features Abstract')
        self.window.setWindowTitle("DAG Generator Tool")
        self.window.show()
        self.app.exec_()

    def Input_Addr(self, path, Qedit):
        download_path = QFileDialog.getOpenFileName(None)[0]
        print(download_path)
        if download_path == '':
            return False
        else:
            Qedit.setText(download_path)
            if path == 'DAG_data':
                self.DAG_Data_Abstract()
            elif path == 'DAG_param':
                self.DAG_Param_Input()
            elif path == 'Flow_data':
                self.Flow_Data_InPut()
            else:
                return False
            return True

    # (1) type 1
    def DAG_Data_Abstract(self):
        input_path = self.Excel_DAG_data_InPath.text()
        if (not os.path.exists(input_path)) or (".csv" not in input_path):
            QMessageBox.critical(self.window, '错误', '输入文件不存在', QMessageBox.Cancel | QMessageBox.Close, QMessageBox.Cancel)
            return False
        DAG_Obj = DG.Manual_Input('CSV', [input_path])[0]
        DFA.dag_param_critical_update(DAG_Obj, DAGType=1, DAG_id=1, Period=0)
        self.DAG_Critical_Features_Set(DAG_Obj)

    # (2) type 2
    def DAG_Param_Input(self):
        input_path = self.Excel_DAG_param_InPath.text()
        if (not os.path.exists(input_path)) or (".xlsx" not in input_path):
            QMessageBox.critical(self.window, '错误', '输入文件不存在', QMessageBox.Cancel | QMessageBox.Close, QMessageBox.Cancel)
            return False
        self.Data_Critical_Features_Set(DG.DAG_Feature_Input(input_path)[0])

    # (3) type 3
    def Flow_Data_InPut(self):
        input_path = self.Excel_Flow_data_InPath.text()
        if (not os.path.exists(input_path)) or (".xlsx" not in input_path):
            QMessageBox.critical(self.window, '错误', '输入文件不存在', QMessageBox.Cancel | QMessageBox.Close, QMessageBox.Cancel)
            return False
        self.Flow_Data_Set = DG.Manual_Input('XLSX', [input_path])
        for dag_id, dag_x in enumerate(self.Flow_Data_Set):
            DFA.dag_param_critical_update(dag_x, DAGType=dag_x.graph['DAG_ID'], DAG_id=dag_id, Period=0)
        self.Input_Flow_SetNum.setText(str(len(self.Flow_Data_Set)))

    def Data_Critical_Features_Set(self, DAG_Feature):
        rows, columns = DAG_Feature.shape
        temp_data_dict = {DAG_Feature[rows_x][0]:DAG_Feature[rows_x][1] for rows_x in range(rows)}
        self.Number_Of_Nodes.setText(str(temp_data_dict['Number_Of_Nodes']))
        self.Number_Of_Edges.setText(str(temp_data_dict['Number_Of_Edges']))
        self.Number_Of_Levels.setText(str(temp_data_dict['Number_Of_Level']))
        self.Number_Of_Width.setText(str(temp_data_dict["Width"]))
        self.Connection_Rate.setText(str(round(temp_data_dict["Connection_Rate"], 2)))
        # self.DAG_volume.setText(str(temp_data_dict["Volume"]))
        # self.DAG_Cri_Rate.setText(str(round(temp_data_dict["CriVolumeRate"], 2)))
        self.Jump_Level.setText(str(temp_data_dict["Jump_Level"]))

        self.Param_Max_Shape.setText(str(temp_data_dict["Max_Shape"]))
        self.Param_Min_Shape.setText(str(temp_data_dict["Min_Shape"]))
        self.Param_Max_In_Degree.setText(str(temp_data_dict["Max_In_Degree"]))
        self.Param_Max_Out_Degree.setText(str(temp_data_dict["Max_Out_Degree"]))
        self.Param_Max_WCET.setText(str(temp_data_dict["Max_WCET"]))
        self.Param_Min_WCET.setText(str(temp_data_dict["Min_WCET"]))

    def DAG_Critical_Features_Set(self, temp_DAG_obj):
        self.Number_Of_Nodes.setText(str(temp_DAG_obj.number_of_nodes()))
        self.Number_Of_Edges.setText(str(temp_DAG_obj.number_of_edges()))
        self.Number_Of_Levels.setText(str(temp_DAG_obj.graph['Number_Of_Level']))
        self.Number_Of_Width.setText(str(temp_DAG_obj.graph["Width"]))
        self.Connection_Rate.setText(str(round(temp_DAG_obj.graph["Connection_Rate"], 2)))
        # self.DAG_volume.setText(str(temp_DAG_obj.graph["Volume"]))
        # self.DAG_Cri_Rate.setText(str(round(temp_DAG_obj.graph["CriVolumeRate"], 2)))
        self.Jump_Level.setText(str(temp_DAG_obj.graph["Jump_Level"]))

        self.Param_Max_Shape.setText(str(temp_DAG_obj.graph["Max_Shape"]))
        self.Param_Min_Shape.setText(str(temp_DAG_obj.graph["Min_Shape"]))
        self.Param_Max_In_Degree.setText(str(temp_DAG_obj.graph["Max_In_Degree"]))
        self.Param_Max_Out_Degree.setText(str(temp_DAG_obj.graph["Max_Out_Degree"]))
        self.Param_Max_WCET.setText(str(temp_DAG_obj.graph["Max_WCET"]))
        self.Param_Min_WCET.setText(str(temp_DAG_obj.graph["Min_WCET"]))

    def Output_Data_SELF(self):
        self.Excel_DAG_data_OutPath.setText( QFileDialog.getExistingDirectory(None) )

    def DAG_Generator_Flow_Based(self):
        try:
            Dags_Num        = int(self.Gen_DAG_NUM.text())
            Flow_NUM        = int(self.Gen_Flow_NUM.text())
            Jitter_Rate     = float(self.Gen_Jitter.text())

            Param_dict = {'Flow_set': self.Flow_Data_Set, 'flow_num': Flow_NUM, 'WCET_interval': Jitter_Rate,
                          'DAG_Num': Dags_Num, 'arrival_interval': [0, 20 * 2260]}
            Total_DAG_list = DG.Algorithm_input('FLOW', Param_dict)
            for DAG_id, DAG_x in enumerate(Total_DAG_list):
                # DWC.WCET_Config(DAG_x, 'Uniform', Virtual_node=True, a=Min_WCET, b=Max_WCET)
                DFA.dag_param_critical_update(DAG_x, DAGType=DAG_id, DAG_id=DAG_id, Period=0)
                for node_x in DAG_x.nodes(data=True):
                    node_x[1]['WCET'] = int(node_x[1]['WCET'])
            new_time = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
            DDP.Exam_Data_Output(Total_DAG_list, 'ALL', self.Excel_DAG_data_OutPath.text() + '/Flow_Data_Generator/{0}/'.format(new_time))
        except:
            QMessageBox.critical(self.window, 'Faile', '生成失败', QMessageBox.Cancel | QMessageBox.Close, QMessageBox.Cancel)
            return False
        else:
            QMessageBox.question(self.window, "Success", "DAG生成完成！", QMessageBox.Yes)
            return True

    def Data_Trans_HTOS(self):      # Excel_(HUAWEI)到_(SELF)转换 包括（self and feature data）
        input_path = self.Excel_HUAWEI_Input_Path.text()
        output_path = self.Excel_SELF_Output_Path.text()
        if (output_path == '') or (not os.path.exists(input_path)) or (".xls" not in input_path) or (".xlsx" not in input_path):
            QMessageBox.critical(self.window, '错误', "文件错误", QMessageBox.Cancel | QMessageBox.Close, QMessageBox.Cancel)
            return False
        if input_path == output_path:
            QMessageBox.critical(self.window, '错误', "文件错误", QMessageBox.Cancel | QMessageBox.Close, QMessageBox.Cancel)
            return False
        DAG_list = []
        # todo 录入DAG数据
        with pd.ExcelFile(input_path) as data:
            all_sheet_names = data.sheet_names  # 获取所有sheet名字；
            for DAG_ID in all_sheet_names:
                temp_DAG = nx.DiGraph()
                temp_DAG.graph['DAG_ID'] = DAG_ID
                df = pd.read_excel(data, DAG_ID, index_col=None, na_values=["NA"])
                temp_node_index = 0
                self_node_dict = {}
                for row in df.index:
                    node_list_ID = df.loc[row]['JobTypeID']
                    if node_list_ID != node_list_ID:
                        continue
                    else:
                        node_list_ID = int(node_list_ID)
                    node_temp_num = int(df.loc[row]['InstanceNumber'])
                    node_precursor_list = df.loc[row]['TriggerJobTypeIDSet']
                    if node_precursor_list != node_precursor_list:
                        node_precursor_list = []
                    elif type(node_precursor_list) == str:
                        node_precursor_list = node_precursor_list.split(',')
                    else:
                        node_precursor_list = [node_precursor_list]
                    node_execution_time = df.loc[row]['ExecutionTime']
                    node_priority = int(df.loc[row]['QoS'])
                    self_node_dict[node_list_ID] = []
                    for x in range(node_temp_num):
                        temp_node_index += 1
                        node_temp_id = 'Job_' + str(node_list_ID) + '_' + str(x+1)
                        temp_DAG.add_node(temp_node_index, Node_Index=temp_node_index, Node_ID=node_temp_id, WCET=node_execution_time, Prio=node_priority)
                        self_node_dict[node_list_ID] .append(temp_node_index)
                        for node_precursor in node_precursor_list:
                            for u in self_node_dict[int(node_precursor)]:
                                temp_DAG.add_edge(u, temp_node_index)
                DAG_list.append(temp_DAG)
        # todo （1）保存DAG（self）_excel  文件
        workbook = xlwt.Workbook(encoding='utf-8')
        for obj_dag_i in (DAG_list):
            nodex = obj_dag_i.nodes[1]
            node_feature = [node_k for node_k, node_v in nodex.items()]
            worksheet = workbook.add_sheet("DAG_{0}".format(obj_dag_i.graph["DAG_ID"]))
            for j, node_j in enumerate(node_feature):
                worksheet.write(0, j, node_j)
            for j, node_j in enumerate(obj_dag_i.nodes(data=True)):
                for k, node_feature_k in enumerate(node_feature):
                    worksheet.write(j+1, k, node_j[1][node_feature_k])
        workbook.save(output_path + '/DAG_SELF.xls')
        # todo （2）保存DAG（feature）_excel  文件
        workbook = xlwt.Workbook(encoding='utf-8')
        worksheet = workbook.add_sheet("DAG_Critical_Features")
        Features_Tytle = ["DAG_ID", "Number_Of_Nodes",  "Number_Of_Edges",  "Number_Of_Level",
                          "Max_Shape",    "Min_Shape",    "Ave_Shape", "Std_Shape",
                          "Max_Re_Shape", "Min_Re_Shape", "Ave_Re_Shape", "Std_Re_Shape",
                          "Width", "Max_Degree", "Min_Degree", "Ave_Degree", "Std_Degree",
                          "Max_In_Degree", "Min_In_Degree", "Ave_In_Degree", "Std_In_Degree",
                          "Max_Out_Degree", "Min_Out_Degree", "Ave_Out_Degree", "Std_Out_Degree",
                          "Connection_Rate", "DAG_volume", "Max_WCET", "Min_WCET", "Ave_WCET", "Std_WCET", 'Jump_Level']
        for i, feature_i in enumerate(Features_Tytle):
            worksheet.write(i, 0, feature_i)
        for x, DAG_x in enumerate(DAG_list):
            self.dag_param_critical_update(DAG_x, x)        # 更新DAG特性；
            for i, feature_i in enumerate(Features_Tytle):
                worksheet.write(i, x + 1, DAG_x.graph[feature_i])
        workbook.save(output_path + '/DAG_Features.xls')
        # todo （3）保存DAG图png文件：
        for DAG_x in DAG_list:
            gvd = pgv.AGraph(directed=True, rankdir="LR")
            for node_x in DAG_x.nodes(data=True):
                temp_label = ''
                temp_label += str(node_x[0]) + '\n'
                temp_label += 'Node_ID:' + str(node_x[1]['Node_ID']) + '\n'
                temp_label += 'WCET:' + str(node_x[1]['WCET']) + '\n'
                temp_label += 'Prio:' + str(node_x[1]['Prio']) + '\n'
                gvd.add_node(node_x[0], label=temp_label)
            for edge_x in DAG_x.edges(data=True):
                gvd.add_edge(edge_x[0], edge_x[1])
            gvd.layout(prog='dot')  # 要有，否则会出现log文件
            gvd.draw(path=output_path + '/' + DAG_x.graph['DAG_ID'] + '.png', format='png')
        # todo （4）添加特性到参数表中：
        for DAG_x in DAG_list:
            self.Number_Of_Nodes.setText( str(DAG_x.graph["Number_Of_Nodes"]) )
            self.Number_Of_Edges.setText( str(DAG_x.graph["Number_Of_Edges"]) )
            self.Number_Of_Levels.setText( str(DAG_x.graph["Number_Of_Level"]) )
            self.Number_Of_Width.setText( str(DAG_x.graph["Width"]) )
            self.Connection_Rate.setText( str(round(DAG_x.graph["Connection_Rate"], 2)) )
            self.Jump_Level.setText( str(DAG_x.graph["Jump_Level"]) )

            self.Param_Max_Shape.setText( str(DAG_x.graph["Max_Shape"]) )
            self.Param_Min_Shape.setText( str(DAG_x.graph["Min_Shape"]) )
            self.Param_Max_In_Degree.setText( str(DAG_x.graph["Max_In_Degree"]) )
            self.Param_Max_Out_Degree.setText( str(DAG_x.graph["Max_Out_Degree"]) )

            self.Param_Max_WCET.setText( str(round(DAG_x.graph["Max_WCET"], 2)) )
            self.Param_Min_WCET.setText( str(round(DAG_x.graph["Min_WCET"], 2)) )
            self.Param_Ave_WCET.setText( str(round(DAG_x.graph["Ave_WCET"], 2)) )
            self.Param_Std_WCET.setText( str(round(DAG_x.graph["Std_WCET"], 2)) )

        # todo （0）返回正确信息：
        QMessageBox.question(self.window, "Success", "DAG特性抽象完成！", QMessageBox.Yes)
        return True

    def DAG_Generator_Param_Based(self):
        try:
            Dags_Num        = int(self.Gen_DAG_NUM.text())
            Width           = int(float(self.Number_Of_Width.text()))
            Node_Num        = int(float(self.Number_Of_Nodes.text()))
            Edge_Num        = int(float(self.Number_Of_Edges.text()))
            Critic_Path     = int(float(self.Number_Of_Levels.text()))
            Jump_level      = int(float(self.Jump_Level.text()))
            Max_Shape       = int(float(self.Param_Max_Shape.text()))
            Min_Shape       = int(float(self.Param_Min_Shape.text()))
            Max_in_degree   = int(float(self.Param_Max_In_Degree.text()))
            Max_out_degree  = int(float(self.Param_Max_Out_Degree.text()))
            Connection_Rate = float(self.Connection_Rate.text())
            Max_WCET        = float(self.Param_Max_WCET.text())
            Min_WCET        = float(self.Param_Min_WCET.text())

            ret_dag_list = DG.Algorithm_input("MINE", {'Node_Num': Node_Num,
                                                       'Edge_Num': Edge_Num,
                                                       'Critic_Path': Critic_Path,
                                                       'Jump_level': Jump_level,
                                                       'Max_Shape': Max_Shape,
                                                       'Min_Shape': Min_Shape,
                                                       'Max_in_degree': Max_in_degree,
                                                       'Max_out_degree': Max_out_degree,
                                                       'Conn_ratio': Connection_Rate,
                                                       'Width': Width,
                                                       'DAG_Num': Dags_Num})

            analysis_data = []
            for DAG_id, DAG_x in enumerate(ret_dag_list):
                DWC.WCET_Config(DAG_x, 'Uniform', Virtual_node=True, a=Min_WCET, b=Max_WCET)
                DFA.dag_param_critical_update(DAG_x, DAGType=DAG_id, DAG_id=DAG_id, Period=0)
                for node_x in DAG_x.nodes(data=True):
                    node_x[1]['WCET'] = int(node_x[1]['WCET'])
                analysis_data.append(DAG_x)
            new_time = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
            DDP.Exam_Data_Output(analysis_data, 'ALL', self.Excel_DAG_data_OutPath.text() + '/DAG_Data_Generator/{0}/'.format(new_time))
        except:
            QMessageBox.critical(self.window, 'Faile', '生成失败', QMessageBox.Cancel | QMessageBox.Close, QMessageBox.Cancel)
            return False
        else:
            QMessageBox.question(self.window, "Success", "DAG生成完成！", QMessageBox.Yes)
            return True

if __name__ == '__main__':
    # # 创建应用
    # window_application = QApplication(sys.argv)
    # # 设置登录窗口
    # login_ui = LoginDialog()
    # # 校验是否验证通过
    # if login_ui.exec_() == QDialog.Accepted:
    G = GUI()

DAG Generator And Simulator/Src/DAG_Generator/DAG_Generator_Tool/DAG_Generator_Tool.py


import os
from setuptools import setup, find_packages

runtime_dependencies = [
]

build_dependencies = [
    'wheel',
]

dev_dependencies = build_dependencies + [
    'pylint',
    'flake8',
    'flake8-docstrings',
    'pytest',
    'pytest-cov',
    'pytest-html',
    'pytest-xdist',
    'coverage',
]

setup(
    name='pydoit-project-builder',
    version='0.1.0',
    author='Loïc Schneider',
    description='Templates to be used when creating projects.',
    long_description=open(os.path.join(os.path.dirname(__file__), 'README.md')).read(),
    license='Apache 2',
    url='https://github.com/schneiderlo/pydoit-project-builder',
    packages=find_packages(exclude=['tests*']),
    include_package_data=True,
    setup_requires=build_dependencies,
    install_requires=runtime_dependencies,
    extras_require={
        'dev': dev_dependencies
    }
)


from flask import Flask, request, jsonify, render_template
import search_q
from search_q import search, process_query
from search_q_pure_text import search_text, process_query_text
from search_q_amb import search_amb, process_query_amb
from search_q_stem import search_stem, process_query_stem
import build_idx

app = Flask(__name__, template_folder='templates_folder', static_folder='static')

def format_results(hits):
    results = []
    for hit in hits:
        result = {
            "rank": hit["rank"],
            "score": hit["score"],
            "docno": hit["docno"],
            "snippet": hit["text"]
        }
        results.append(result)
    return results

@app.route('/')
def home():
    return render_template('index.html')

# search matched words
@app.route('/search', methods=['POST'])
def search_handler():
    query_str = request.form.get('query', '')
    n = int(request.form.get('n', 10))

    free_text, phrases = process_query(query_str)
    query_string = " ".join(free_text)
    for phrase in phrases:
        query_string += ' "{}"'.format(phrase)

    hits = search(query_string, n=n)
    results = format_results(hits)

    return render_template('results.html', query=query_str, results=results)

# search matched texts
@app.route('/search_text', methods=['POST'])
def search_handler_text():
    query_str = request.form.get('query', '')
    n = int(request.form.get('n', 10))

    free_text, phrases = process_query_text(query_str)
    query_string = " ".join(free_text)
    for phrase in phrases:
        query_string += ' "{}"'.format(phrase)

    hits = search_text(query_string, n=n)
    results = format_results(hits)

    return render_template('results.html', query=query_str, results=results)

# search with ambiguity
@app.route('/search_amb', methods=['POST'])  # add ambiguous matching
def search_handler_amb():
    query_str = request.form.get('query', '')
    n = int(request.form.get('n', 10))

    free_text, phrases = process_query_amb(query_str)
    query_string = " ".join(free_text)
    for phrase in phrases:
        query_string += ' "{}"'.format(phrase)

    hits = search_amb(query_string, n=n)
    results = format_results(hits)

    return render_template('results.html', query=query_str, results=results)

# search by stemming
@app.route('/search_stem', methods=['POST'])  # add stemming option
def search_handler_stem():
    query_str = request.form.get('query', '')
    n = int(request.form.get('n', 10))

    free_text, phrases = process_query_stem(query_str)
    query_string = " ".join(free_text)
    for phrase in phrases:
        query_string += ' "{}"'.format(phrase)

    hits = search_stem(query_string, n=n)
    results = format_results(hits)

    return render_template('results.html', query=query_str, results=results)

# build index
@app.route('/build-index', methods=['POST'])
def build_index():
    build_idx.create_index()
    return render_template('built.html')

if __name__ == '__main__':
    app.run(debug=True)


from django.core.validators import RegexValidator
from rest_framework import serializers
from .models import *
from User.models import (
    User,
    Profile,
)

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = (
            'first_name', 'last_name', 'email', 'phone_number'
        )

class AddressSerializer(serializers.ModelSerializer):
    class Meta:
        model = Address
        fields = (
            'province', 'city', 'address_detail', 'postal_code', 'receiver_name', 'customer', 'receiver_phone_number')

class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = '__all__'

class ProfileSerializer(serializers.HyperlinkedModelSerializer):
    # customer = CustomerSerializer(read_only=True, many=True)
    address = serializers.HyperlinkedRelatedField(view_name='user_addresses', many=True, read_only=True)

    class Meta:
        model = Profile
        fields = '__all__'
        extra_fields = ('address', 'information', 'wish_list', 'order_history')


from django import template
from ..models import BlogTag, Blog
import datetime
import math

register = template.Library()

@register.inclusion_tag('blog/blog_tag_list.html')
def blog_tag_list():
    tags = list(set(BlogTag.objects.all()))
    for article in Blog.objects.all():
        for tag in article.get_all_tags():
            if not tag in tags:
                tags.append(tag)
    return {
        'tags': tags,
    }

@register.inclusion_tag('blog/blog_archive.html')
def blog_archive():
    m_list = []

    def add2m_list(dt):
        ndt = datetime.datetime(dt.year, dt.month, 1)
        if ndt not in m_list:
            m_list.append(ndt)

    for m in Blog.objects.values_list('date'):
        add2m_list(m[0])

    cs = int(math.ceil(len(m_list) / 2))
    m_lists = [
        m_list[:cs],
        m_list[cs:],
    ]

    return {
        'month_lists': m_lists
    }


# /**
# * @author Rajat Verma
# * https://www.linkedin.com/in/rajat-v-3b0685128/
# * https://github.com/rajatt95
# * https://rajatt95.github.io/
# *
# * Course: Learn API Automation Testing with Python & BDD Framework (https://www.udemy.com/course/python-sdet-rest-api-automation/)
# * Tutor: Rahul Shetty (https://www.udemy.com/user/rahul445/)
# */
#
# /***************************************************/

# https://realpython.com/python-requests/
# https://docs.python-requests.org/en/latest/
# Requests is an elegant and simple HTTP library for Python, built for human beings.
import requests
from PayLoad import *

# POST request
response_AddBook = requests.post('http://216.10.245.166/Library/Addbook.php',
                                 # Request Body
                                 json=addBookPayLoad("123f23221"),
                                 # 3rd argument takes information like Header
                                 headers={'Content-Type': 'application/json'},
                                 # 4th argument - Optional
                                 )

response_AddBook_InJSON = response_AddBook.json()
print(response_AddBook_InJSON['ID'])
book_ID = response_AddBook_InJSON['ID']

# DELETE request
response_deleteBook = requests.post('http://216.10.245.166/Library/DeleteBook.php',
              # Request Body
              json={"ID": book_ID},
              # 3rd argument takes information like Header
              headers={'Content-Type': 'application/json'},
              # 4th argument - Optional
              )
print(response_deleteBook.status_code)
response_deleteBook_InJSON = response_deleteBook.json()
print(response_deleteBook_InJSON['msg'])


import threading
from multiprocessing import Queue

results = []
results2 = []

def take_numbers(q):
    print('Enter the numbers:')
    for i in range(0,3):
        num1 = int(input('Enter first number: '))
        num2 = int(input('Enter second number: '))
        q.put(num1)
        q.put(num2)

def add_num(q):
    for i in range(0,3):
        num1 = q.get()
        num2 = q.get()
        results.append(num1+num2)
        results2.append(num1-num2)

q = Queue()
t2 = threading.Thread(target=add_num, args=(q, ))
t1 = threading.Thread(target=take_numbers, args=(q, ))

t2.start()
t1.start()
t2.join()
t1.join()
q.close()

for result in results:
    print ("adunare =",  result)

for result in results2:
    print ("scadere =",  result)


def process_photo(file):
    """convert image to jpg format"""
    from PIL import Image
    image = Image.open(file)
    width, height = image.size
    image = image.crop((0, 0, width, width / 290 * 418))
    image.thumbnail((290, 418))
    image.save("./export.jpg", "JPEG", quality=90)

import sys

process_photo(sys.argv[1])


# %%
from concurrent import futures
import os
import time
import grpc
import json
import uuid
import shutil
import grpc.basic_pb2 as basic_pb2
import grpc.basic_pb2_grpc as basic_pb2_grpc
from ner.trainer import NERTrainer

# %%
class Task(basic_pb2_grpc.TaskServicer):

    def createTrain(self, request, context):
        guid = str(uuid.uuid1()).split('-')[0]
        save_dir = './tmp/{}'.format(guid)
        os.makedirs(save_dir)
        required_list = {
            'bert_config_file': False,
            'pretrained_file': False,
            'train_file': False,
            'vocab_file': False,
            'tags_file': False,
            'eval_file': False
        }
        for r in request:
            cur_name = r.name
            required_list[r.name] = True
            with open(os.path.join(save_dir, cur_name), 'ab') as f:
                f.write(r.file)
        for key in required_list.keys():
            if not required_list[key]:
                shutil.rmtree(save_dir)
                return basic_pb2.Response(status='failed', result='missing {}'.format(required_list[key]))
        return basic_pb2.Response(status='success', result=guid)

    def getModel(self, request, context):
        guid = request.guid
        model_id = request.model_id
        save_dir = './tmp/{}'.format(guid)
        model_dir = './save_model'
        if not os.path.isfile(os.path.join(model_dir, 'bert', '{}_bert.pth'.format(model_id))):
            yield basic_pb2.FileBody(file=None, name='missing model file')
        common_files = os.listdir(save_dir)
        for fi in common_files:
            with open(os.path.join(save_dir, fi), mode='rb') as f:
                lines = f.readlines()
            for line in lines:
                yield basic_pb2.FileBody(file=line, name=fi)
        with open(os.path.join(model_dir, 'bert', '{}_bert.pth'.format(model_id)), mode='rb') as f:
                lines = f.readlines()
        for line in lines:
            yield basic_pb2.FileBody(file=line, name=fi)
        with open(os.path.join(model_dir, 'lstm_crf', '{}_lstm_crf.pth'.format(model_id)), mode='rb') as f:
                lines = f.readlines()
        for line in lines:
            yield basic_pb2.FileBody(file=line, name=fi)

    def createPredict(self, request, context):
        guid = request.guid
        return basic_pb2.Response(status='success', result=guid)

class Train(basic_pb2_grpc.TrainServicer):

    def train(self, request, context):
        save_dir = './tmp/{}'.format(request.guid)
        trainer = NERTrainer(request.num_epochs, request.num_gpus,
            bert_config_file_name= os.path.join(save_dir, 'bert_config_file'),
            pretrained_file_name= os.path.join(save_dir, 'pretrained_file'),
            hidden_dim= request.hidden_dim,
            train_file_name= os.path.join(save_dir, 'train_file'),
            vocab_file_name= os.path.join(save_dir, 'vocab_file'),
            tags_file_name= os.path.join(save_dir, 'tags_file'),
            eval_file_name= os.path.join(save_dir, 'eval_file'),
            batch_size=request.batch_size,
            eval_batch_size=request.eval_batch_size)
        for info in trainer():
            yield basic_pb2.Response(status='training', result=json.dumps(info))

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    basic_pb2_grpc.add_TaskServicer_to_server(Task(), server)
    basic_pb2_grpc.add_TrainServicer_to_server(Train(), server)
    server.add_insecure_port('[::]:8088')
    server.start()

    try:
        while True:
            time.sleep(60*60*24)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()

# %%


import json
import threading
import time
import traceback
from base64 import b64decode
from urllib.parse import quote_plus
from zlib import decompress, MAX_WBITS

import requests
import websocket

NAME = 'Bittrex'

class BittrexWebsocket:

    def __init__(self, symbol):
        """Constructor"""
        self.__symbol = symbol
        self.__token = ""
        self.__orderbook_bid = {}
        self.__orderbook_ask = {}
        self.__ws = None

        # Constants
        self.__NAME = 'Bittrex'
        self.__REST_ENDPOINT = "https://socket.bittrex.com/signalr/"
        self.__WS_ENDPOINT = "wss://socket.bittrex.com/signalr/"
        self.__TRANSPORT = "webSockets"
        self.__CLIENT_PROTOCOL = "1.5"
        self.__CONNECTION_DATA = "%5B%7B%22name%22%3A%22c2%22%7D%5D"

    def __get_conn_token(self):
        r = requests.get(f'{self.__REST_ENDPOINT}negotiate?'
                         f'clientProtocol={self.__CLIENT_PROTOCOL}&'
                         f'connectionData={self.__CONNECTION_DATA}'
                         f'&_={time.time()}')
        if r is not None:
            self.__token = quote_plus(json.loads(r.text)['ConnectionToken'])
        self.__TID = "3"

    def __start_ws(self):
        self.__get_conn_token()
        r = requests.get(
            f'{self.__REST_ENDPOINT}start?'
            f'transport={self.__TRANSPORT}&'
            f'clientProtocol={self.__CLIENT_PROTOCOL}&'
            f'connectionToken={self.__token}&'
            f'connectionData={self.__CONNECTION_DATA}&'
            f'_={time.time()}'
        )
        if r is not None:
            if json.loads(r.text)['Response'] == 'started':
                return True
        return False

    @staticmethod
    def __process_message(message):
        try:
            deflated_msg = decompress(b64decode(message, validate=True), -MAX_WBITS)
        except SyntaxError:
            deflated_msg = decompress(b64decode(message, validate=True))
        return json.loads(deflated_msg.decode())

    def __on_message(self, message):
        message = json.loads(message)
        # print(message)

        if "R" in message:
            message = self.__process_message(message['R'])
            # print(message)
            self.__orderbook_bid = dict((x['R'], x['Q']) for x in message['Z'])
            self.__orderbook_ask = dict((x['R'], x['Q']) for x in message['S'])

        if "M" in message and len(message['M']) > 0:
            message = message["M"][0]
            if 'A' in message and len(message['A']) > 0:
                message = self.__process_message(message['A'][0])
                # print(message)

                for x in message['Z']:
                    if x['TY'] == 0 or x['TY'] == 2:
                        self.__orderbook_bid.update({x['R']: x['Q']})
                    if x['TY'] == 1:
                        self.__orderbook_bid.pop(x['R'])
                for x in message['S']:
                    if x['TY'] == 0 or x['TY'] == 2:
                        self.__orderbook_ask.update({x['R']: x['Q']})
                    if x['TY'] == 1:
                        self.__orderbook_ask.pop(x['R'], None)

                ask = list(sorted(self.__orderbook_ask.keys()))[0]
                bid = list(sorted(self.__orderbook_bid.keys(), reverse=True))[0]
                bid_amount = self.__orderbook_bid[bid]
                ask_amount = self.__orderbook_ask[ask]

                print(self.__symbol, json.dumps({
                    'bid': bid,
                    'ask': ask,
                    'bid_amount': bid_amount,
                    'ask_amount': ask_amount,
                    'timestamp': int(time.time() * 1000)
                }))

                # print(self.__orderbook_bid)
                # print(self.__orderbook_ask)

    def __on_error(self, error):
        print(f'ws{self.__NAME}: {error}')

    def __on_close(self):
        print(f'ws{self.__NAME}: {"closed"}')

    def __on_open(self):
        print(f'ws{self.__NAME} subscribe {self.__symbol}')

        symbol = self.__symbol.split('_')[1] + '-' + self.__symbol.split('_')[0]
        self.__ws.send(json.dumps({"H": "c2", "M": "SubscribeToExchangeDeltas", "A": [symbol], "I": 3}))
        self.__ws.send(json.dumps({"H": "c2", "M": "QueryExchangeState", "A": [symbol], "I": 7}))

    def run(self):
        if self.__start_ws():
            self.__ws = websocket.WebSocketApp(
                f"{self.__WS_ENDPOINT}connect?"
                f"transport={self.__TRANSPORT}&"
                f"clientProtocol={self.__CLIENT_PROTOCOL}&"
                f"connectionToken={self.__token}&"
                f"connectionData={self.__CONNECTION_DATA}",
                on_open=self.__on_open,
                on_message=self.__on_message,
                on_error=self.__on_error,
                on_close=self.__on_close)

            self.__ws.run_forever()

def run_ws(symbol):
    bittrex = BittrexWebsocket(symbol)
    bittrex.run()

if __name__ == "__main__":
    try:
        SYMBOLS = ['ETH_BTC',
                   'ETH_USDT',
                   'BTC_USDT',
                   'LTC_BTC',
                   'LTC_ETH',
                   'LTC_USDT'
                   ]

        print(f'ws{NAME}: start')

        for s in SYMBOLS:
            th = threading.Thread(target=run_ws, kwargs={'symbol': s})
            th.start()
    except KeyboardInterrupt:
        exit()
    except:
        print(traceback.format_exc())


from typing import *
from .Letters import *
from .SpecificPositions import SpecificPositions
from typing import Literal

Colors = Literal["red", "blue"]
MotionTypes = Literal["pro", "anti", "dash", "static"]
Locations = Literal["n", "e", "s", "w", "ne", "se", "sw", "nw"]
RotationDirections = Literal["cw", "ccw"]
Turns = float | Literal["fl", "0", "0.5", "1", "1.5", "2", "2.5", "3"]

### STAFF ATTRIBUTES ###

PropAttributes = Literal[
    "color", "prop_type", "location", "layer", "axis", "orientation"
]
PropTypes = Literal[
    "staff",
    "bigstaff",
    "buugeng",
    "club",
    "fan",
    "minihoop",
    "triad",
    "bigtriad",
    "bighoop",
    "doublestar",
    "bigdoublestar",
    "quiad",
    "sword",
    "guitar",
    "ukulele",
    "chicken",
]
Layers = Literal[1, 2]
Axes = Literal["vertical", "horizontal"]
Orientations = Literal[
    "in",
    "clock-in",
    "clock",
    "clock-out",
    "out",
    "counter-out",
    "counter",
    "counter-in",
]

OrientationCombinations = Literal[
    "in_vs_in",
    "in_vs_clock-in",
    "in_vs_clock",
    "in_vs_clock-out",
    "in_vs_out",
    "in_vs_counter-out",
    "in_vs_counter",
    "in_vs_counter-in",
    "clock-in_vs_clock-in",
    "clock-in_vs_clock",
    "clock-in_vs_clock-out",
    "clock-in_vs_out",
    "clock-in_vs_counter-out",
    "clock-in_vs_counter",
    "clock-in_vs_counter-in",
    "clock_vs_clock",
    "clock_vs_clock-out",
    "clock_vs_out",
    "clock_vs_counter-out",
    "clock_vs_counter",
    "clock_vs_counter-in",
    "clock-out_vs_clock-out",
    "clock-out_vs_out",
    "clock-out_vs_counter-out",
    "clock-out_vs_counter",
    "clock-out_vs_counter-in",
    "out_vs_out",
    "out_vs_counter-out",
    "out_vs_counter",
    "out_vs_counter-in",
    "counter-out_vs_counter-out",
    "counter-out_vs_counter",
    "counter-out_vs_counter-in",
    "counter_vs_counter",
    "counter_vs_counter-in",
    "counter-in_vs_counter-in",
]

RotationAngles = Literal[0, 90, 180, 270]
Positions = Literal["alpha", "beta", "gamma"]
Direction = Literal["right", "left", "down", "up"]
ColorsHex = Literal["#ED1C24", "#2E3192"]
LetterType = Literal[
    "Dual-Shift", "Shift", "Cross-Shift", "Dash", "Dual-Dash", "Static"
]

GridModes = Literal["Box", "Diamond"]

class SpecificStartEndPositionsDicts(TypedDict):
    start_position: SpecificPositions
    end_position: SpecificPositions

OptimalLocationsEntries = Dict[Literal["x", "y"], float]

class OptimalLocationsDicts(TypedDict):
    optimal_red_location: OptimalLocationsEntries
    optimal_blue_location: OptimalLocationsEntries

class PropAttributesDicts(TypedDict):
    color: Colors
    prop_type: PropTypes
    prop_location: Locations
    layer: Layers
    orientation: Orientations

### MOTION ATTRIBUTES ###
class MotionAttributesDicts(TypedDict):
    color: Colors
    motion_type: MotionTypes
    rotation_direction: RotationDirections
    location: Locations
    turns: Turns

    start_location: Locations
    start_orientation: Orientations
    start_layer: Layers

    end_location: Locations
    end_orientation: Orientations
    end_layer: Layers

class ArrowAttributesDicts(TypedDict):
    color: Colors
    location: Locations
    motion_type: MotionTypes
    turns: Turns

class MotionAttributesDicts(TypedDict):
    color: Colors
    motion_type: MotionTypes
    rotation_direction: RotationDirections
    turns: Turns
    start_location: Locations
    end_location: Locations
    start_orientation: Orientations
    end_orientation: Orientations
    start_layer: Layers
    end_layer: Layers

MotionTypesCombinations = Literal[
    "pro_vs_pro",
    "anti_vs_anti",
    "static_vs_static",
    "pro_vs_anti",
    "static_vs_pro",
    "static_vs_anti",
    "dash_vs_pro",
    "dash_vs_anti",
    "dash_vs_static",
    "dash_vs_dash",
]

StartEndLocationsTuple = Tuple[Locations, Locations]
PreprocessedStartEndCombinations = Dict[
    SpecificStartEndPositionsDicts,
    List[Tuple[Letters, List[MotionAttributesDicts]]],
]
OptimalLocationsDicts = Dict[str, OptimalLocationsEntries]
DictVariants = (
    MotionAttributesDicts | SpecificStartEndPositionsDicts | OptimalLocationsDicts
)
DictVariantsLists = List[DictVariants]
LetterDictionary = Dict[Letters, List[DictVariantsLists]]

Mode = Optional[Literal["TS", "TO", "SS", "SO", "QTS", "QTO"]]

class PictographAttributesDict(TypedDict):
    start_position: Positions
    end_position: Positions
    letter_type: LetterType
    mode: Mode
    motion_type_combination: MotionTypesCombinations

HybridCombinations = Literal[
    "pro_vs_anti",
    "static_vs_pro",
    "static_vs_anti",
    "dash_vs_pro",
    "dash_vs_anti",
    "dash_vs_static",
]

### LETTER GROUPS ###

LetterGroupsByMotionTypes = Literal[
    "ADGJMPS",
    "BEHKNQT",
    "CFILORUV",
    "WYΣθ",
    "XZΔΩ",
    "W-Y-Σ-θ-",
    "X-Z-Δ-Ω-",
    "ΦΨΛ",
    "Φ-Ψ-Λ-",
    "αβΓ",
]

MotionTypesLetterGroupMap = Dict[MotionTypesCombinations, LetterGroupsByMotionTypes]
ParallelCombinationsSet = Set[Tuple[str, str, str, str]]


import time
import pickle
from dolfin import *
set_log_level(LogLevel.ERROR)
import numpy as np
import numba

# import required functions from twoDim
from statFEM_analysis.twoDim import mean_assembler, kernMat, cov_assembler
from scipy.linalg import sqrtm
from tqdm import tqdm

# set up mean and kernel functions
f_bar = Constant(1.0)

σ_f = 0.1
κ = 4

@numba.jit
def c_f(x,y):
    return (σ_f**2)*np.exp(-κ*np.abs(x-y))

@numba.jit
def k_f(x):
    return (σ_f**2)*np.exp(-κ*np.abs(x))

def fem_prior(h,f_bar,k_f,grid):
    J = int(np.round(1/h))
    μ = mean_assembler(h,f_bar)
    Σ = cov_assembler(J,k_f,grid,False,True)
    return μ,Σ

N = 41
x_range = np.linspace(0,1,N)
grid = np.array([[x,y] for x in x_range for y in x_range])

def compute_cov_diff(C1,C2,tol=1e-10):
    N = np.sqrt(C1.shape[0])
    C1_sqrt = np.real(sqrtm(C1))
    rel_error_1 = np.linalg.norm(C1_sqrt @ C1_sqrt - C1)/np.linalg.norm(C1)
    assert rel_error_1 < tol

    C12 = C1_sqrt @ C2 @ C1_sqrt
    C12_sqrt = np.real(sqrtm(C12))
    rel_error_12 = np.linalg.norm(C12_sqrt @ C12_sqrt - C12)/np.linalg.norm(C12)
    assert rel_error_12 < tol

    hSq = (1/(N-1))**2
    return hSq*(np.trace(C1) + np.trace(C2) - 2*np.trace(C12_sqrt))

def W(μ_1,μ_2,Σ_1,Σ_2):
    mean_error = errornorm(μ_1,μ_2,'L2')
    cov_error = compute_cov_diff(Σ_1,Σ_2)
    cov_error = np.sqrt(np.abs(cov_error))
    error = mean_error + cov_error
    return error

def refine(h,n,f_bar,k_f,grid):
    # set up empty lists to hold h-values and errors (this being the ratios)
    h_range = []
    errors = []
    # get the statFEM prior for h and h/2
    μ_1, Σ_1 = fem_prior(h,f_bar,k_f,grid)
    μ_2, Σ_2 = fem_prior(h/2,f_bar,k_f,grid)
    # compute the distance between these and store in numerator variable
    numerator = W(μ_1,μ_2,Σ_1,Σ_2)
    # succesively refine the mesh by halving and do this n times
    for i in tqdm(range(n), desc="inner loop", position=1, leave=False):
    # for i in range(n):
        # print(f"inner loop iteration #{i} out of {n}")
        # store mean and cov for h/2 in storage for h
        μ_1, Σ_1 = μ_2, Σ_2
        # in storage for h/2 store mean and cov for h/4
        μ_2, Σ_2 = fem_prior(h/4,f_bar,k_f,grid)
        # compute the distance between the priors for h/2 and h/4
        # and store in denominator variable
        denominator = W(μ_1,μ_2,Σ_1,Σ_2)
        # compute the ratio and store in error
        error = numerator/denominator
        # append the current value of h and the ratio
        h_range.append(h)
        errors.append(error)
        # store denominator in numerator and halve h
        numerator = denominator
        h = h/2
    # return the list of h-values together with the ratios for these values
    return h_range,errors

my_list = [(0.25,4),(0.2,3),(0.175,3),(0.22,4),(0.176,3),(0.177,3),(0.178,3),(0.179,3),(0.18,3),(0.21,3),(0.215,3),(0.1,2),(0.3,4),(0.31,4),(0.315,4),(0.24,4),(0.245,4),(0.25,4),(0.14,2),(0.16,2),(0.15,2),(0.18,2)]

set_log_level(LogLevel.ERROR)

n_outer=len(my_list)

h_range = []
errors = []
print("Starting computation.")
start = time.time()
# for i,(h,n) in enumerate(my_list):
    # print(f"outer loop iteration #{i} out of {n_outer}")
for (h, n) in tqdm(my_list, desc="outer", position=0):
    h_range_tmp, errors_tmp = refine(h,n,f_bar,k_f,grid)
    h_range.extend(h_range_tmp)
    errors.extend(errors_tmp)

end = time.time()
print("Finished computation.")
print(f"time elapsed: {end - start}")

results = {'my_list': my_list, 'h_range': h_range, 'errors': errors}
with open('results/twoDim_prior_matern_results', 'wb') as f:
    pickle.dump(results, f)


#!/usr/bin/python -u
#
# this tests the basic APIs of the XmlTextReader interface
#
import libxml2
import sys
try:
    import StringIO
    str_io = StringIO.StringIO
except:
    import io
    str_io = io.StringIO

# Memory debug specific
libxml2.debugMemory(1)

def tst_reader(s):
    f = str_io(s)
    input = libxml2.inputBuffer(f)
    reader = input.newTextReader("tst")
    res = ""
    while reader.Read():
        res=res + "%s (%s) [%s] %d\n" % (reader.NodeType(),reader.Name(),
				      reader.Value(), reader.IsEmptyElement())
        if reader.NodeType() == 1: # Element
            while reader.MoveToNextAttribute():
                res = res + "-- %s (%s) [%s]\n" % (reader.NodeType(),
						   reader.Name(),reader.Value())
    return res

expect="""1 (test) [None] 0
1 (b) [None] 1
1 (c) [None] 1
15 (test) [None] 0
"""

res = tst_reader("""<test><b/><c/></test>""")

if res != expect:
    print("Did not get the expected error message:")
    print(res)
    sys.exit(1)

# Memory debug specific
libxml2.cleanupParser()
if libxml2.debugMemory(1) == 0:
    print("OK")
else:
    print("Memory leak %d bytes" % (libxml2.debugMemory(1)))
    libxml2.dumpMemory()


import random
import traceback

import numpy as np
import matplotlib.colors as colors
import time

from PySide2.QtCore import QThread

from Model.AsteroidModel import AsteroidModel
from Model.BoltModel import BoltModel
from Model.MotionModel import MotionModel, MotionState
from Network.NetworkState import CommandNetwork as Cmd
from Controller.BaseController import BaseController
from Network.ServerNetwork import ServerNetwork
from Model.ShipModel import ShipModel
from Model.PartyModel import PartyState, PartyKey, PartyConst, PartyModel
from Utils.Logger import LogError
from Model.ModelType import ModelType

DEBUG_MODE = False
DEBUG_DISPLAY_PERIOD_S = .2
SERVICE_REAL_TIME_PERIOD_S = 0.02
SERVICE_STATE_PERIOD_S = 0.5
SERVICE_TIME_LEFT_PERIOD_S = 1

class TimerSettings:
    KEY_SHOOT = 'k_shoot'
    TIMER_SHOOT_PERIOD_S = .25

class ServerController(BaseController):
    def __init__(self):
        self.network = ServerNetwork(self)
        self.hosted_parties = dict()
        self.hosted_players = set()
        self.core_timers = {TimerSettings.KEY_SHOOT: dict()}

        self.players = dict()

        # Async handlers
        self.services_are_running = True
        self.thread_services = list()
        self.init_services()

        # Async variable
        self.create_asteroid = 0

    def init_services(self):
        thread_service_timeleft = QThread()
        thread_service_timeleft.run = self.service_party_time_left

        thread_service_motion = QThread()
        thread_service_motion.run = self.service_party_motion

        thread_service_collision = QThread()
        thread_service_collision.run = self.service_party_collision

        thread_service_states = QThread()
        thread_service_states.run = self.service_party_states

        thread_service_debug = QThread()
        thread_service_debug.run = self.service_debug

        self.thread_services.append(thread_service_motion)
        self.thread_services.append(thread_service_collision)
        self.thread_services.append(thread_service_states)
        self.thread_services.append(thread_service_timeleft)
        self.thread_services.append(thread_service_debug)

    def start(self):
        self.network.start()
        for _service in self.thread_services:
            _service.start()

    def join(self):
        self.network.join()
        self.join_services()

    def join_services(self):
        self.services_are_running = False
        for _service in self.thread_services:
            _service.terminate()
            _service.wait()

    def set_connexion_info(self, host: str, port: int):
        self.network.set_connexion(host, port)

    # -------------------- SERVER MODEL --------------------
    def update_player_model(self, name, party, motion):
        self.hosted_parties[party][PartyKey.KEY_MOTION][name] = MotionModel().from_dict(motion)

    def get_model_player(self, name):
        return self.players[name].to_dict()

    def get_model_party(self, party_name, player):
        _party = self.hosted_parties.get(party_name)
        if _party is None:
            _party = PartyModel.get_ended_pkg(party_name, player)
        _party_cp = _party.copy()
        _party_cp[PartyKey.KEY_MOBS] = {PartyKey.KEY_MOB_PLAYERS: [_mob.to_dict() for _mob in _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS]],
                                        PartyKey.KEY_MOB_ASTEROIDS: [_mob.to_dict() for _mob in _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS]],
                                        PartyKey.KEY_MOB_BOLTS: [_mob.to_dict() for _mob in _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_BOLTS]]}
        _party_cp[PartyKey.KEY_MOTION] = {_player: _motion.to_dict() for _player, _motion in _party[PartyKey.KEY_MOTION].items()}
        _party_cp[PartyKey.KEY_DEAD_ZONE] = _party[PartyKey.KEY_DEAD_ZONE].copy()
        _party_cp[PartyKey.KEY_DEAD_ZONE][PartyKey.KEY_DEAD_ZONE_CENTER] = _party[PartyKey.KEY_DEAD_ZONE][PartyKey.KEY_DEAD_ZONE_CENTER].tolist()
        return _party_cp

    def get_players(self):
        return [_model.to_dict() for _model in self.players.values()]

    def get_model(self, player_name: str):
        _model = self.get_players()
        _server_model = dict()
        _server_model[Cmd.MOBILE_OBJECTS_INFO] = _model
        _server_model[Cmd.GAME_INFO] = 'unknown'
        _server_model[Cmd.PLAYER_INFO] = self.get_model_player(player_name)
        return _server_model

    def is_player_already_connected(self, player):
        return player in self.hosted_players

    @LogError
    def create_player(self, player):
        self.hosted_players.add(player)

    @LogError
    def delete_player(self, player):
        self.hosted_players.remove(player)

    def is_party_exist(self, name: str, limit: int, creator: str) -> bool:
        if name in self.hosted_parties:
            return False
        self.hosted_parties[name] = {PartyKey.KEY_LIMIT: limit, PartyKey.KEY_PLAYERS: dict(),
                                     PartyKey.KEY_MOBS: {PartyKey.KEY_MOB_PLAYERS: list(),
                                                         PartyKey.KEY_MOB_BOLTS: list(),
                                                         PartyKey.KEY_MOB_ASTEROIDS: list()},
                                     PartyKey.KEY_CREATED: time.time(),
                                     PartyKey.KEY_CREATOR: creator,
                                     PartyKey.KEY_STATE: PartyState.STATE_WAITING_FOR_PLAYERS,
                                     PartyKey.KEY_PREV_STATE: PartyState.STATE_INIT,
                                     PartyKey.KEY_TIME_LEFT: PartyConst.TIME_MAX_WAITING,
                                     PartyKey.KEY_NAME: name,
                                     PartyKey.KEY_MOTION: dict(),
                                     PartyKey.KEY_DEAD_POOL: list(),
                                     PartyKey.KEY_DEAD_ZONE: {PartyKey.KEY_DEAD_ZONE_RADIUS: PartyConst.DEAD_ZONE_MAX_RADIUS,
                                                              PartyKey.KEY_DEAD_ZONE_CENTER: np.array([0, 0])},
                                     }
        return True

    def get_party_list(self) -> list:
        return [(_name, '{}/{}'.format(len(_model[PartyKey.KEY_PLAYERS]), _model[PartyKey.KEY_LIMIT]), _model[PartyKey.KEY_CREATOR]) for _name, _model in self.hosted_parties.items()]

    def join_party(self, _name, _player) -> tuple:
        if _name not in self.hosted_parties:
            print('> Player {} try to join an unknown party ({})'.format(_player, _name))
            return False, 'Party is unknown'

        if len(self.hosted_parties[_name][PartyKey.KEY_PLAYERS]) >= self.hosted_parties[_name][PartyKey.KEY_LIMIT]:
            print('> Player {} try to join a full party ({})'.format(_player, _name))
            return False, 'Party is full'

        if _player in self.hosted_parties[_name][PartyKey.KEY_PLAYERS]:
            print('> Player {} is already in this party ({})'.format(_player, _name))
            return False, 'Player is already in this party'

        self.hosted_parties[_name][PartyKey.KEY_PLAYERS][_player] = time.time()
        print('> Player {} join party {}'.format(_player, _name))
        return True, 'Have fun !'

    def watch_party(self, _name, _player) -> tuple:
        if _name not in self.hosted_parties:
            print('> Player {} try to observe an unknown party ({})'.format(_player, _name))
            return False, 'Party is unknown'

        print('> Player {} watch party {}'.format(_player, _name))
        return True, 'Have fun watcher !'

    def delete_party(self, name):
        del self.hosted_parties[name]
        print('> Server: Party {} has been deleted'.format(name))

    def startup_party_elements(self, party_name):
        _party = self.hosted_parties[party_name]
        assert _party[PartyKey.KEY_LIMIT] > 0, 'Party is set with player limit at zero (0)'
        _color_part = 1 / _party[PartyKey.KEY_LIMIT]

        # PREDEFINE SPAWN (PLAYER AND ASTEROIDS)
        # > Generate player spawns
        _iterative_angle = 2 * np.pi / _party[PartyKey.KEY_LIMIT]
        _player_spawns = [np.array([np.cos(_i * _iterative_angle), np.sin(_i * _iterative_angle)]) *
                                   (PartyConst.DEAD_ZONE_MAX_RADIUS / 2) for _i in range(_party[PartyKey.KEY_LIMIT])]
        random.shuffle(_player_spawns)

        # > Generate asteroid spawns to hide sight
        _asteroid_spawns = []
        for _i, _p_spawn in enumerate(_player_spawns):
            for _p_spawn_2 in _player_spawns[_i+1:]:
                _dist = np.linalg.norm(_p_spawn - _p_spawn_2)
                if _dist == 0:  # When is the same spawn point
                    continue
                _dir = (_p_spawn_2 - _p_spawn) / _dist
                _asteroid_pst = _p_spawn + _dir * _dist // 2
                _asteroid_spawns.append(_asteroid_pst)

        # > Create Ship for players
        for _i, (_spawn, _player) in enumerate(zip(_player_spawns, _party[PartyKey.KEY_PLAYERS])):
            try:
                np.random.seed(int(time.time())+_i)
                _ship = ShipModel()
                _ship.name = _player
                _ship.color = (colors.hsv_to_rgb([_i * _color_part, 1, 1]) * 255).tolist()
                _ship.set_angle(np.random.randint(0, 359))
                _ship.set_position(_spawn)
                _ship.set_speed(np.array([0, 0]))
                _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS].append(_ship)
            except Exception as e:
                print("> [ERR] startup party players > {}: {}".format(type(e).__name__, e))

        # > Create asteroid
        for _asteroid_spawn in _asteroid_spawns:
            try:
                _asteroid = AsteroidModel(radius=np.random.randint(50, 150))
                _asteroid.set_angle(np.random.randint(0, 359))
                _asteroid.set_speed(np.array([np.random.randint(2, 10), 0]))
                _asteroid.set_position(_asteroid_spawn)
                _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS].append(_asteroid)
            except Exception as e:
                print("> [ERR] startup party asteroid > {}: {}".format(type(e).__name__, e))

    # -------------------- SERVICES --------------------
    def service_party_motion(self):
        """ Update motion """
        _service_name = 'Service::Motion'
        print('> {} is running...'.format(_service_name))
        while self.services_are_running:
            _begin = time.time()
            try:
                for _name, _party in self.hosted_parties.items():
                    if not _party[PartyKey.KEY_STATE] == PartyState.STATE_IN_PROGRESS:
                        continue

                    # DEAD ZONE CIRCLE
                    _dt_radius = PartyConst.DEAD_ZONE_MAX_RADIUS / PartyConst.TIME_MAX_DEAD_ZONE * SERVICE_REAL_TIME_PERIOD_S
                    _party[PartyKey.KEY_DEAD_ZONE][PartyKey.KEY_DEAD_ZONE_RADIUS] -= _dt_radius
                    if _party[PartyKey.KEY_DEAD_ZONE][PartyKey.KEY_DEAD_ZONE_RADIUS] < 0:
                        _party[PartyKey.KEY_DEAD_ZONE][PartyKey.KEY_DEAD_ZONE_RADIUS] = 0

                    _all_mobs = _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS] + _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_BOLTS] + _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS]
                    for _mob in _all_mobs:
                        if _mob.is_alive and _mob.type == ModelType.SHIP_MODEL:
                            _command = _party[PartyKey.KEY_MOTION].get(_mob.name)
                            _prop_ppm = 0
                            _rot_ppm = 0
                            if _command is not None:
                                if _command.state == MotionState.STATE_MOVE:
                                    _prop_ppm = _mob.get_propulsion_speed() + _command.acceleration * PartyConst.MOTION_MAX_PROPULSION_ACCEL
                                    _rot_ppm = _command.rotation * PartyConst.MOTION_MAX_ROTATION_ACCEL
                                elif _command.state == MotionState.STATE_STOP:
                                    _prop_ppm = _mob.get_propulsion_speed() - 1. * PartyConst.MOTION_MAX_PROPULSION_ACCEL
                                    _rot_ppm = 0
                                elif _command.state == MotionState.STATE_SHOOT:
                                    _shoot_dir = _command.shoot_xy
                                    _bolt = self.start_shoot(_mob, _shoot_dir)
                                    _prop_ppm = _mob.get_propulsion_speed()
                                    _rot_ppm = 0
                                    if _bolt is not None:
                                        _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_BOLTS].append(_bolt)
                                        _prop_ppm = _mob.get_propulsion_speed()
                                        _rot_ppm = _mob.get_rotation_speed()

                            # CROP SPEED LIMIT
                            if PartyConst.MOTION_MAX_PROPULSION_SPEED < _prop_ppm:
                                _prop_ppm = PartyConst.MOTION_MAX_PROPULSION_SPEED

                            if _prop_ppm < PartyConst.MOTION_MIN_PROPULSION_SPEED:
                                _prop_ppm = PartyConst.MOTION_MIN_PROPULSION_SPEED

                            if PartyConst.MOTION_MAX_ROTATION_SPEED < _rot_ppm:
                                _rot_ppm = PartyConst.MOTION_MAX_ROTATION_SPEED

                            if _rot_ppm < PartyConst.MOTION_MIN_ROTATION_SPEED:
                                _rot_ppm = PartyConst.MOTION_MIN_ROTATION_SPEED

                            # SET POSITION INCREMENT
                            _dt_prop = _prop_ppm * SERVICE_REAL_TIME_PERIOD_S
                            _dt_rot = _rot_ppm * SERVICE_REAL_TIME_PERIOD_S
                            _mob.set_dir(np.dot(np.array([[np.cos(_dt_rot), -np.sin(_dt_rot)],
                                                          [np.sin(_dt_rot), np.cos(_dt_rot)]]),
                                                _mob.dir))
                            _mob.set_position(_mob.xy + _mob.dir * _dt_prop)
                            _mob.set_speed(np.array([_prop_ppm, _rot_ppm]))
                        else:
                            _mob.set_position(_mob.xy + _mob.dir * _mob.get_propulsion_speed() * SERVICE_REAL_TIME_PERIOD_S)

                    if _party[PartyKey.KEY_STATE] not in [PartyState.STATE_IN_PROGRESS,
                                                          PartyState.STATE_WINNER]:
                        continue

                    #  > Asteroid condition
                    _asteroid_to_create = PartyConst.ASTEROID_COUNT - len(_party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS])
                    for _ in range(_asteroid_to_create):
                        _too_close = True
                        while _too_close:
                            _dir = np.random.uniform(-1, 1, 2)
                            _dir = _dir / np.linalg.norm(_dir)
                            _pst_xy = _dir * PartyConst.ASTEROID_RADIUS_SPAWN

                            _size = np.random.randint(*PartyConst.ASTEROID_SIZE_LIMIT)
                            _speed = np.array([np.random.randint(*PartyConst.ASTEROID_SPEED_LIMIT), 0])

                            _dt_rot = np.random.uniform(-5/180*np.pi, 5/180*np.pi)
                            _dir_xy = _dir * -1
                            _dir_xy = np.array([[np.cos(_dt_rot), -np.sin(_dt_rot)],
                                                [np.sin(_dt_rot), np.cos(_dt_rot)]]).dot(_dir_xy)
                            m_asteroid = AsteroidModel(pst_xy=_pst_xy, dir_xy=_dir_xy, v_xy=_speed,
                                                       radius=_size)
                            _test_proximity = False
                            for _mob in _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS] + _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS]:
                                if np.linalg.norm(_mob.xy - m_asteroid.xy) < (_mob.radius + m_asteroid.radius) // 2:
                                    _test_proximity = True
                                    break

                            if not _test_proximity:
                                _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS].append(m_asteroid)
                                _too_close = False

                _end = time.time()
                _run_time = SERVICE_REAL_TIME_PERIOD_S - (_end - _begin)
                if _run_time > 0:
                    QThread.currentThread().msleep(int(_run_time * 1000))
                else:
                    print('> [WARNING]{}: Runtime {} ms.'.format(_service_name, _run_time * 1000))
            except Exception as e:
                print('ERR: {} > {}:{}'.format(_service_name, type(e).__name__, e))
                traceback.print_exc()

        print('> {} ended'.format(_service_name))

    def service_party_time_left(self):
        """ Update time left """
        _service_name = 'Service::Timeleft'
        print('> {} is running...'.format(_service_name))
        while self.services_are_running:
            try:
                for _name, _party in self.hosted_parties.items():
                    _state = _party[PartyKey.KEY_STATE]
                    if _state == PartyState.STATE_WAITING_FOR_PLAYERS:
                        if _party[PartyKey.KEY_TIME_LEFT] > 0:
                            _party[PartyKey.KEY_TIME_LEFT] -= 1
                        else:
                            print('> Party [{}] change state to [{}]'.format(_name, PartyState.STATE_PARTY_ENDED))
                            _party[PartyKey.KEY_STATE] = PartyState.STATE_PARTY_ENDED
                            _party[PartyKey.KEY_TIME_LEFT] = 10
                    elif _state == PartyState.STATE_READY_TO_FIGHT:
                        if _party[PartyKey.KEY_TIME_LEFT] > 0:
                            _party[PartyKey.KEY_TIME_LEFT] -= 1
                        else:
                            print('> Party [{}] change state to [{}]'.format(_name, PartyState.STATE_IN_PROGRESS))
                            _party[PartyKey.KEY_STATE] = PartyState.STATE_IN_PROGRESS
                            _party[PartyKey.KEY_TIME_LEFT] = 0

                    elif _state == PartyState.STATE_IN_PROGRESS:
                        if _party[PartyKey.KEY_TIME_LEFT] > 0:
                            _party[PartyKey.KEY_TIME_LEFT] -= 1
                        else:
                            print('> Party [{}] change state to [{}]'.format(_name, PartyState.STATE_PARTY_ENDED))
                            _party[PartyKey.KEY_STATE] = PartyState.STATE_PARTY_ENDED
                            _party[PartyKey.KEY_TIME_LEFT] = 0

                    elif _state == PartyState.STATE_WINNER:
                        if _party[PartyKey.KEY_TIME_LEFT] > 0:
                            _party[PartyKey.KEY_TIME_LEFT] -= 1
                        else:
                            print('> Party [{}] change state to [{}]'.format(_name, PartyState.STATE_PARTY_ENDED))
                            _party[PartyKey.KEY_STATE] = PartyState.STATE_PARTY_ENDED
                            _party[PartyKey.KEY_TIME_LEFT] = 10

                QThread.currentThread().msleep(SERVICE_TIME_LEFT_PERIOD_S * 1000)
            except Exception as e:
                pass
        print('> {} ended'.format(_service_name))

    def service_party_collision(self):
        """ Solve collisions """
        _service_name = 'Service::Collision'
        print('> {} is running... '.format(_service_name))
        while self.services_are_running:
            _begin = time.time()
            try:
                for _name, _party in self.hosted_parties.items():

                    if _party[PartyKey.KEY_STATE] in [PartyState.STATE_IN_PROGRESS,
                                                      PartyState.STATE_WINNER]:
                        _all_mobs = _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS] + _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS] + _party[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_BOLTS]
                        for _i, _mob in enumerate(_all_mobs):

                            # Dead zone damage
                            _dt_mob_from_dz_center = np.linalg.norm(_mob.xy - _party[PartyKey.KEY_DEAD_ZONE][PartyKey.KEY_DEAD_ZONE_CENTER])
                            if _dt_mob_from_dz_center > _party[PartyKey.KEY_DEAD_ZONE][PartyKey.KEY_DEAD_ZONE_RADIUS]:
                                if _party[PartyKey.KEY_STATE] == PartyState.STATE_IN_PROGRESS:
                                    if _mob.is_alive:
                                        _mob.set_damage(PartyConst.DEAD_ZONE_DAMAGE_PER_S * SERVICE_REAL_TIME_PERIOD_S)
                                        if _mob.type == ModelType.SHIP_MODEL and not _mob.is_alive:
                                            _party[PartyKey.KEY_DEAD_POOL].append(_mob.name)

                            # Detect collision
                            for _collider in _all_mobs[_i:]:
                                if not _collider.name == _mob.name:
                                    _dist = np.linalg.norm(_collider.xy - _mob.xy)
                                    if _dist == 0:
                                        continue
                                    if _dist < (_collider.radius + _mob.radius)/2:
                                        if ModelType.BOLT_MODEL not in [_mob.type, _collider.type]:
                                            # print('COLLISION:', _mob.name, 'with', _collider.name)
                                            # Set position offset after collision
                                            _diff = (_collider.radius + _mob.radius)/2 - _dist
                                            # _diff += 5
                                            _diff_ratio1 = _mob.weight / (_mob.weight + _collider.weight) * _diff
                                            _diff_ratio2 = _collider.weight / (_mob.weight + _collider.weight) * _diff

                                            _mob.set_position(_mob.xy + _diff_ratio2 * (_mob.xy - _collider.xy) / _dist)
                                            _collider.set_position(_collider.xy + _diff_ratio1 * (_collider.xy - _mob.xy) / _dist)

                                            # Set orientation offset after collision
                                            # -- Normal
                                            _normal = (_collider.xy - _mob.xy) / _dist

                                            # -- Tangente
                                            _tx = -_normal[1]
                                            _ty = _normal[0]

                                            # -- dot product tangent
                                            _dp_tan_1 = _mob.dir_x * _mob.get_propulsion_speed() * _tx + _mob.dir_y * _mob.get_propulsion_speed() * _ty
                                            _dp_tan_2 = _collider.dir_x * _collider.get_propulsion_speed() * _tx + _collider.dir_y * _collider.get_propulsion_speed() * _ty

                                            # -- dot product normal
                                            _dp_normal_mob = _mob.dir_x * _mob.get_propulsion_speed() * _normal[0] + _mob.dir_y * _mob.get_propulsion_speed() * _normal[1]
                                            _dp_normal_collider = _collider.dir_x * _collider.get_propulsion_speed() * _normal[0] + _collider.dir_y * _collider.get_propulsion_speed() * _normal[1]

                                            # -- conservation of momentum
                                            _m1 = (_dp_normal_mob * (_mob.weight - _collider.weight) + 2 * _collider.weight * _dp_normal_collider) / (_mob.weight + _collider.weight)
                                            _m2 = (_dp_normal_collider * (_collider.weight - _mob.weight) + 2 * _mob.weight * _dp_normal_mob) / (_mob.weight + _collider.weight)

                                            _speed1 = np.array([_tx*_dp_tan_1+_normal[0]*_m1,
                                                                _ty*_dp_tan_1+_normal[1]*_m1])
                                            _speed2 = np.array([_tx*_dp_tan_2+_normal[0]*_m2,
                                                                _ty*_dp_tan_2+_normal[1]*_m2])

                                            _prop1 = np.linalg.norm(_speed1)
                                            _prop2 = np.linalg.norm(_speed2)

                                            _dir1 = _speed1 / _prop1
                                            _mob.set_dir(_dir1)
                                            _mob.set_speed(np.array([_prop1, _mob.speed[1]]))

                                            _dir2 = _speed2 / _prop2
                                            _collider.set_dir(_dir2)
                                            _collider.set_speed(np.array([_prop2, _collider.speed[1]]))

                                        # SET DAMAGE
                                        if _mob.is_alive:
                                            _mob.set_damage(_collider.damage)
                                            if _mob.type == ModelType.SHIP_MODEL and not _mob.is_alive:
                                                _party[PartyKey.KEY_DEAD_POOL].append(_mob.name)
                                        if _collider.is_alive:
                                            _collider.set_damage(_mob.damage)
                                            if _collider.type == ModelType.SHIP_MODEL and not _collider.is_alive:
                                                _party[PartyKey.KEY_DEAD_POOL].append(_collider.name)

            except Exception as e:
                print('> [ERR] {} | {}:{}'.format(_service_name, type(e).__name__, e))
                traceback.print_exc()

            _end = time.time()
            _run_time = SERVICE_REAL_TIME_PERIOD_S - (_end - _begin)
            if _run_time > 0:
                QThread.currentThread().msleep(int(_run_time * 1000))
        print('> {} ended'.format(_service_name))

    def service_party_states(self):
        """ Solve party states """
        _service_name = 'Service::States'
        print('> {} is running...'.format(_service_name))
        while self.services_are_running:
            try:
                for _name, _model in self.hosted_parties.items():
                    _state = _model[PartyKey.KEY_STATE]
                    if _state == PartyState.STATE_WAITING_FOR_PLAYERS:
                        if len(_model[PartyKey.KEY_PLAYERS]) >= _model[PartyKey.KEY_LIMIT]:
                            print('> Party [{}] change state to [{}]'.format(_name, PartyState.STATE_READY_TO_FIGHT))
                            self.startup_party_elements(_name)
                            _model[PartyKey.KEY_STATE] = PartyState.STATE_READY_TO_FIGHT
                            _model[PartyKey.KEY_TIME_LEFT] = PartyConst.TIME_MAX_READY
                    elif _state == PartyState.STATE_READY_TO_FIGHT:
                        if _model[PartyKey.KEY_TIME_LEFT] <= 0:
                            print('> Party [{}] change state to [{}]'.format(_name, PartyState.STATE_IN_PROGRESS))
                            _model[PartyKey.KEY_STATE] = PartyState.STATE_IN_PROGRESS
                            _model[PartyKey.KEY_TIME_LEFT] = PartyConst.TIME_MAX_GAME
                    elif _state == PartyState.STATE_IN_PROGRESS:
                        if sum([int(_mob.is_alive) for _mob in _model[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS]]) <= 1:
                            print('> Party [{}] change state to [{}]'.format(_name, PartyState.STATE_WINNER))
                            for _mob in _model[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS]:
                                if _mob.is_alive:
                                    _model[PartyKey.KEY_DEAD_POOL].append(_mob.name)
                                    break
                            _model[PartyKey.KEY_STATE] = PartyState.STATE_WINNER
                            _model[PartyKey.KEY_TIME_LEFT] = PartyConst.TIME_MAX_WIN
                            _has_winner = False
                            for _mob in _model[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_PLAYERS]:
                                if _mob.is_alive:
                                    _has_winner = True
                                    print('> Party [{}] Winner is: [{}]'.format(_name, _mob.name))
                                    break
                            if not _has_winner:
                                print('> Party [{}] NO WINNER !')
                    _model[PartyKey.KEY_PREV_STATE] = _state

                    # ***** Delete dead mobs
                    _bolt_temp = list()
                    for _bolt in _model[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_BOLTS]:
                        if _bolt.is_alive:
                            _bolt_temp.append(_bolt)

                    _asteroid_temp = list()
                    for _asteroid in _model[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS]:
                        if not -PartyConst.WIDTH < _asteroid.x < PartyConst.WIDTH:
                            continue
                        if not -PartyConst.HEIGHT < _asteroid.y < PartyConst.HEIGHT:
                            continue
                        _asteroid_temp.append(_asteroid)

                    _model[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_ASTEROIDS] = _asteroid_temp
                    _model[PartyKey.KEY_MOBS][PartyKey.KEY_MOB_BOLTS] = _bolt_temp

                    # ***** Delete party
                    if _state == PartyState.STATE_PARTY_ENDED:
                        self.delete_party(_name)
                QThread.currentThread().msleep(int(SERVICE_STATE_PERIOD_S * 1000))

            except Exception as e:
                print('> [ERR] {} > {}: {}'.format(_service_name, type(e).__name__, e))
        print('> {} ended'.format(_service_name))

    def service_debug(self):
        """ Debug display """
        if not DEBUG_MODE: return

        _service_name = 'Service::Debug'
        print('> {} is running...'.format(_service_name))
        while self.services_are_running:
            try:
                QThread.currentThread().msleep(int(DEBUG_DISPLAY_PERIOD_S * 1000))
            except Exception as e:
                pass
        print('> {} ended'.format(_service_name))

    def start_shoot(self, mob, direction: np.ndarray):
        """  """
        if mob.type == ModelType.SHIP_MODEL:
            _time = time.time()
            if self.core_timers[TimerSettings.KEY_SHOOT].get(mob.name) is None:
                self.core_timers[TimerSettings.KEY_SHOOT][mob.name] = _time
            else:
                if _time - self.core_timers[TimerSettings.KEY_SHOOT][mob.name] > TimerSettings.TIMER_SHOOT_PERIOD_S:
                    self.core_timers[TimerSettings.KEY_SHOOT][mob.name] = _time
                    _bolt = BoltModel(creator=mob.name)
                    _bolt.set_dir(direction)
                    _bolt.set_position(mob.xy + direction * mob.radius)
                    _bolt.set_speed(np.array([1000, 0]))
                    return _bolt


# imports
import yake
import spacy
from rake_nltk import Rake
from gensim.summarization import keywords
from utility import *

bill_data = "Sample_Bill_Data.txt"

# Wordcount seems to have some use but we would need
# to filter out some of the most used words that are
# generic or irrelevant
def wordcount_exploration(string):
    print(get_word_counts(string))

# YAKE: yet another keyword extractor
# Takes a statistical approach to keyword extraction
# Works pretty good, high level of customizability (e.g. n-gram specification)
# Some filtering would still be good
def yake_exploration(string):
    custom_kw_extractor = yake.KeywordExtractor(lan="en", n=3, dedupLim=0.9, top=20, features=None)
    keywords = custom_kw_extractor.extract_keywords(string)

    for word in keywords:
        print(word)

# spacy
# need to run "python3 -m spacy download en_core_web_lg" to get model
# can get smaller models using "en_core_web_md" and "en_core_web_sm"
# Not bad, still gets too many keywords
def spacy_exploration(string):
    nlp = spacy.load("en_core_web_lg")
    doc = nlp(string)
    print(doc.ents)

# NON-FUNCTIONAL CURRENTLY
# RAKE: Rapid Automatic Keyword Extraction
# uses nltk stopwords and cannot download
def rake_exploration(string):
    rake_nltk_var = Rake()

    rake_nltk_var.extract_keywords_from_text(string)
    keyword_extracted = rake_nltk_var.get_ranked_phrases()

    print(keyword_extracted)

# Gensim
# Way too broad
# maybe useful for filtering keywords in other methods?
def gensim_exploration(string):
    print(keywords(string))

# Graph based approach to find keywords? (Textrank?)

# Can find keywords with BERT. A lot of work :(

# list of keywords to exclude - WIP
bad_keywords = ["united states", "act", "section", "united", "states", "united states of america", "secretary", "federal", "federal government", "government", "congressional", "congress", "bill", "bills", "congress", "senate", "america", "state", "agency", "federal agency", "military", "spending", "healthcare"]

def cleanup(my_list: list):
    # remove confidence scores
    my_list = list(map(lambda x: x[0], my_list))

    # remove any monograms
    my_list = filter(lambda x: len(x.split(" ")) > 1, my_list)

    # remove unuseful keywords
    my_list = [word for word in my_list if word not in bad_keywords]

    return my_list[:10] if len(my_list) > 9 else my_list

def remove_dups(my_list: list):
    """ Returns a set of tuples with unique first values (keyword) """

    unique_results = []
    for element in my_list:
        unique_words = list(map(lambda word: word[0], unique_results))
        if element[0] not in unique_words:
            unique_results.append(element)
    return unique_results

def yake_extraction(summary: str, full_text: str):
    # YAKE keyword extraction parameters
    language = "en"
    max_ngram_size = 3
    deduplication_thresold = 0.25
    deduplication_algo = 'seqm'
    windowSize = 1
    numOfKeywords = 20

    # get keywords
    extractor = yake.KeywordExtractor(lan=language, n=max_ngram_size, dedupLim=deduplication_thresold, dedupFunc=deduplication_algo, windowsSize=windowSize, top=numOfKeywords, features=None)
    keywords = extractor.extract_keywords(summary + " " + full_text)

    return cleanup(keywords)

if __name__ == "__main__":
    bill_num = "2"

    bill_data = "/Users/ianwu/Desktop/Senior Design/SeniorDesign/app/machine_learning/keyword_extraction/ian/tests/Sample_Bill_Data.txt"
    data = load_json_from_file(bill_data)
    summary = data[bill_num]["summary"].replace("\n", '')
    full_text = data[bill_num]["full_text"].replace("\n", '')
    # string = remove_stopwords(string)

    words = yake_extraction(summary, full_text)

    for i in words:
        print(i)


import os

from flask import Flask
from flask_restful import Api

from adapters.handlers.error_handling import handler
from controllers.user_controller import user_bp

app = Flask(__name__)
app.register_blueprint(user_bp)
app.register_blueprint(handler)
app.config['SQLALCHEMY_DATABASE_URI'] = '{db}://{u}:{p}@{ur}:{pr}/{n}'.format(
    db='postgresql',
    u='postgres',
    p=os.environ.get('DB_PASS'),
    ur='localhost',
    pr='5432',
    n='python_test'
)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

app.config["JSON_SORT_KEYS"] = False

# flask_restful
api = Api(app)

@app.before_first_request
def initiate_database():
    # db.drop_all()
    db.create_all()

@app.route('/health-check', methods=['GET'])
def teste():
    return {'app': 'running'}

if __name__ == '__main__':
    from adapters.sql_alchemy import db
    db.init_app(app)
    app.run(debug=False)


import pytest

from distributed.deploy.cluster import Cluster

@pytest.mark.asyncio
async def test_eq(cleanup):
    clusterA = Cluster(asynchronous=True, name="A")
    clusterA2 = Cluster(asynchronous=True, name="A2")
    clusterB = Cluster(asynchronous=True, name="B")
    assert clusterA != "A"
    assert not (clusterA == "A")
    assert clusterA == clusterA
    assert not (clusterA != clusterA)
    assert clusterA != clusterA2
    assert not (clusterA == clusterA2)
    assert clusterA != clusterB
    assert not (clusterA == clusterB)

@pytest.mark.asyncio
async def test_repr(cleanup):
    cluster = Cluster(asynchronous=True, name="A")
    assert cluster.scheduler_address == "<Not Connected>"
    res = repr(cluster)
    expected = "Cluster(A, '<Not Connected>', workers=0, threads=0, memory=0 B)"
    assert res == expected

@pytest.mark.asyncio
async def test_logs_deprecated(cleanup):
    cluster = Cluster(asynchronous=True)
    with pytest.warns(FutureWarning, match="get_logs"):
        cluster.logs()


from typing import Tuple
from uuid import uuid4

import streamlit as st

from mortgage_repayment_calculator.account import (
    ANZOffset,
    Offset,
    UbankOffset,
    Value,
)

def settings() -> Tuple[Offset, int, Value]:
    st.header("Offset Account")
    use_offset_account = st.selectbox("Use Offset Account", ["True", "False"])
    offset_account = None
    deposit_schedule = None
    deposit_amount = None
    if use_offset_account == "True":
        _, col, _ = st.columns([0.05, 0.9, 0.05])
        with col:
            offset_type = st.selectbox("Provider", ["Ubank", "ANZ"])
            starting_balance = st.number_input(
                "Starting Balance $", value=50000, step=10000
            )
            deposit_schedule = st.number_input(
                "Deposit Frequency (days)", value=15, step=1
            )
            deposit_amount = st.number_input(
                "Base Deposit Amount (this scales with salary) $",
                value=2000,
                step=100,
            )
            deposit_amount = Value.from_dollars(deposit_amount)

            if offset_type == "Ubank":
                offset_account = UbankOffset(
                    Value.from_dollars(starting_balance)
                )
            if offset_type == "ANZ":
                offset_account = ANZOffset(
                    Value.from_dollars(starting_balance)
                )

    return offset_account, deposit_schedule, deposit_amount


import sys
from collections import deque
sys.stdin = open('input.txt')

N = int(input())

q = deque()

for n in range(N):
    order = sys.stdin.readline().split()
    # print(order)
    if order[0] == 'push':
        q.append(order[1])
    elif q and order[0] == 'pop':
        print(q.popleft())
    elif order[0] == 'size':
        print(len(q))
    elif order[0] == 'empty':
        if q:
            print(0)
        else:
            print(1)
    elif q and order[0] == 'front':
        print(q[0])
    elif q and order[0] == 'back':
        print(q[-1])
    else:
        print(-1)


# coding: utf-8

from __future__ import absolute_import

from flask import json
from six import BytesIO

from swagger_server.models.generator import Generator  # noqa: E501
from swagger_server.models.graph import Graph  # noqa: E501
from swagger_server.test import BaseTestCase

class TestGeneratorsController(BaseTestCase):
    """GeneratorsController integration test stubs"""

    def test_generate(self):
        """Test case for generate

        Generate certain graph
        """
        body = Generator()
        response = self.client.open(
            '/api/graph/generate',
            method='POST',
            data=json.dumps(body),
            content_type='application/json')
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))

    def test_get_generators(self):
        """Test case for get_generators

        Returns supported NetworkX generators
        """
        response = self.client.open(
            '/api/graph/generators',
            method='GET')
        self.assert200(response,
                       'Response body is : ' + response.data.decode('utf-8'))

if __name__ == '__main__':
    import unittest
    unittest.main()


import unittest
from selenium import webdriver
from selenium.webdriver.common.by import By

def some_function(arg: int):
    sums = arg + 2
    return sums

class ReviewsTest(unittest.TestCase):
    def setUp(self):
        self.browser = webdriver.Firefox()

    def tearDown(self):
        self.browser.quit()

    def test_get_review_url(self):
        response = self.browser.get("http://127.0.0.1:8000/movies/oppengeimer")
        # client click on review block at the right side of film detail
        # and then redirects to review page of current film
        # there he would see comments and like button or dislike button
        button_name = self.browser.find_element(By.CLASS_NAME, "review_button")
        self.assertEqual(button_name.text, "Обзор фильма")
        self.assertEqual(
            button_name.get_attribute("href"),
            "http://127.0.0.1:8000/movies/oppengeimer/review",
        )

if __name__ == "__main__":
    unittest.main()
    some_function(1)


# pgen2's setup.py

import setuptools

with open('README.md') as fh:
    long_description = fh.read()

setuptools.setup(
    name = "pgen2",
    packages = ["pgen2"],
    version = "0.1.1",
    description = "Pure Python implementation of pgen, the Python parser "
    "generator",
    long_description = long_description,
    long_description_content_type="text/markdown",
    author = "Jon Riehl",
    author_email = "jon.riehl@gmail.com",
    url = "https://github.com/mythonlang/pgen2/",
    keywords = ["parser", "generator"],
    classifiers = [
        "Programming Language :: Python",
        "Programming Language :: Python :: 2.7",
        "Programming Language :: Python :: 3",
        "Development Status :: 4 - Beta",
        "Environment :: Other Environment",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: BSD License",
        "Operating System :: OS Independent",
        "Topic :: Software Development",
        "Topic :: Software Development :: Libraries :: Python Modules",
        ],
)


"""
Procedures:

    1. Use Bader analysis to calculate the charge density and partial charges
       of each atom

    2. Select the atoms that have the top five most highest partial charges

    3. Select the atom that has the lowest partial charges
       procedure 2 and 3 excludes hydrogen atoms

    4. Collect a list of vectors that contain point from the least negative to most negative atoms

    5. Use these vectors to align the salt molecules

"""
import numpy as np

from ase.io import read, write

def data_handle(file): # read the process the Bader analysis partial charge file ACF.dat

    with open(file) as f:

        data = []
        for line in f:
            line_data = line.split()
            data.append(line_data)

    del data[0:2]
    for line in data:
        del line[1:4]
        del line[2:6]
    data = data[:-4]

    return data

def high_low_charge_density_vectors(charges, positions):

    """
    - get the highest and lowest charges, locate the associated atoms
    - use atoms to produce vectors

    """
    # sort from the lowest to the highest
    lst = len(charges)

    for i in range(lst):

        for j in range(0, lst-1):
            if (float(charges[j][1]) > float(charges[j + 1][1])):

                temp = charges[j]

                charges[j]= charges[j + 1]

                charges[j + 1]= temp

    # check if the charge ranking is done correctly
    rank_charge_inspect(charges)

    # get atom positions and calculate vectors
    # I used 3 top vectors, can add more if necessary

    num_vectors = 3

    low_atom_idx = int(charges[0][0])-1

    vectors = []

    high_atom_pos = []
    high_atom_idx = []

    for i in range(num_vectors):

        high_atom_idx.append(int(charges[-(i+1)][0])) # partial charge index, start from 1
        high_atom_pos.append(positions[int(charges[-(i+1)][0])-1]) # index for partial charge start with 1

    for item in high_atom_pos:
        diff = item - positions[low_atom_idx]
        vectors.append(diff)

    return vectors

def rank_charge_inspect(charges):

    check_lst = []
    for item in charges:
        check_lst.append(float(item[1]))

    min_idx = check_lst.index(min(check_lst))

    if min_idx != 0:

        print("partial charge ranking has error")
        print("the partial charge array is {}".format(charges))

    return

def smart_translate(salt_unit_vec, mol_unit_vec, salt_atoms):

    # overlap the salt and solvent vectors first
    dist = salt_unit_vec - mol_unit_vec
    salt_atoms.translate(dist)

    # find the span of solvent molecule in x,y,z directions
    x_dir = []
    y_dir = []
    z_dir = []

    for item in mol_positions:
        x_dir.append(item[0])
        y_dir.append(item[1])
        z_dir.append(item[2])

    x_sp = max(x_dir) - min(x_dir)
    y_sp = max(y_dir) - min(y_dir)
    z_sp = max(z_dir) - min(z_dir)
    sps = [x_sp, y_sp, z_sp]
#    print('x_sp {}, y_sp {}, z_sp {}'.format(x_sp, y_sp, z_sp))

    shortest_sp = sps.index(min(sps))
    min_dist = 2.8
    # now move the salt molveuls about 3A in the direction where the span is the smallest
    if shortest_sp == 0:
        trans = [min_dist,0,0]
    elif shortest_sp == 1:
        trans = [0,min_dist,0]
    else:
        trans = [0,0,min_dist]

    salt_atoms.translate(trans)

    return salt_atoms

# =============================================================================
#                            Start main program
# =============================================================================
# read in files
mol_charge_file = "C4H8O2_ACF.dat"
mol_atoms_file = "C4H8O2.traj"

salt_charge_file = "salt.dat"
salt_atoms_file = "salt.xyz"

# Solvent Molecule: get the partial charges of each atom and calculate the top vectors
# vector dierection, point from highest partial charge to lowest partial charge
mol_charges = data_handle(mol_charge_file)
mol_atoms = read(mol_atoms_file)

mol_symbols = mol_atoms.get_chemical_symbols()
mol_positions = mol_atoms.get_positions()
mol_vectors = high_low_charge_density_vectors(mol_charges,mol_positions)

# Salt molecule: get the partial charges of each atom and calculate the top vectors
salt_charges = data_handle(salt_charge_file)
salt_atoms = read(salt_atoms_file)
salt_symbols = salt_atoms.get_chemical_symbols()
salt_positions = salt_atoms.get_positions()
salt_vectors = high_low_charge_density_vectors(salt_charges,salt_positions)

# the code will give three top vectors, but for salt, only need the first one
# and needs to be negated to align Li of salt with the highest energy-density of solvent
salt_vectors = salt_vectors[0]
salt_vectors = -salt_vectors

# start align the salt molecule with the solvent molecule, first, find unit vectors
mol_unit_vec = mol_vectors / np.linalg.norm(mol_vectors, axis=-1)[:,None]
salt_unit_vec = salt_vectors /  np.linalg.norm(salt_vectors, axis=-1)

# Here is to pick top three solvent molecule vectors as reference for salt rotation
for i in range(len(mol_unit_vec)):

    # make a copy of salt_atoms object, because each roation, it will start from the original salt molecule
    rot_salt = salt_atoms.copy()

    # keep a copy of original salt_vectors before rotation
    rot_salt_vectors = salt_vectors

    # rotate the salt molecule based on the solvent vectors
    rot_salt.rotate(rot_salt_vectors, mol_unit_vec[i], center="COM")

    #now the salt is rotated, move it to the correct place respect to the solvent
    trans_salt_atoms = smart_translate(rot_salt_vectors, mol_unit_vec[i], rot_salt)

    new_mol_atoms = mol_atoms.copy()
    salt_mol = new_mol_atoms.extend(trans_salt_atoms)
    salt_mol.write('salt_molecules_cluster_'+ str(i) +'.xyz')

# some useful matrix for other applications
#Rot,rmsd = R.align_vectors(mol_unit_vec[0][None,:], salt_unit_vec[None,:])
#rot_matrix = Rot.as_matrix()
#euler_angles = Rot.as_euler("xyz", degrees=True)