import argparse
import sys
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from .all_functions import *

"""
This program takes data as input and
returns an answer to the question -
Will Books&Co be able to both pay back loan and
afford the next book purchase order?
"""

def main():

    # redirects all prints to log file
    temp = sys.stdout
    sys.stdout = open('log.txt', 'w')

    # Please specify these parameters to proceed: not mandatory
    parser = argparse.ArgumentParser(description="Please specify these parameters to proceed:")
    parser.add_argument('--orig_purchase_file', nargs='?', default='original_purchase_order.csv')
    parser.add_argument('--next_purchase_file', nargs='?', default='next_purchase_order.csv')
    parser.add_argument('--customer_file', nargs='?', default='customer_features.csv')
    parser.add_argument('--product_file', nargs='?', default='product_features.csv')
    parser.add_argument('--last_assortment_file', nargs='?', default='last_month_assortment.csv')
    parser.add_argument('--next_assortment_file', nargs='?', default='next_month_assortment.csv')

    args = parser.parse_args()
    orig_purchase_file = args.orig_purchase_file
    next_purchase_file = args.next_purchase_file
    customer_file = args.customer_file
    product_file = args.product_file
    last_assortment_file = args.last_assortment_file
    next_assortment_file = args.next_assortment_file

    print("Read all the files")
    orig_purchase = pd.read_csv(orig_purchase_file)
    next_purchase = pd.read_csv(next_purchase_file)
    customer_features = pd.read_csv(customer_file)
    product_features = pd.read_csv(product_file)
    last_assortment = pd.read_csv(last_assortment_file)
    next_assortment = pd.read_csv(next_assortment_file)

    print("############## CLEAN DATA ################################# ")
    print("Get merged data from three files\n - customer_features, product_features and last_month_assortment")
    merged_df = merge_data(cust_data=customer_features,
                           prod_data=product_features,
                           assort_data=last_assortment)

    # change data types of columns
    cols_not_cat = ['customer_id', 'product_id', 'length', 'purchased']
    for col in merged_df.columns:
        if col not in cols_not_cat:
            merged_df[col] = merged_df[col].astype('category')

    # One hot encoding for categorical variables
    merged_df = pd.get_dummies(merged_df,
                               columns=['age_bucket', 'is_returning_customer', 'genre', 'difficulty', 'fiction'],
                               prefix=['age', 'return', 'genre', 'diff', 'fic'])

    merged_df.purchased = merged_df.purchased.astype('int')

    print("################## BUILD MODEL using last assortment data ############################")
    # Get data ready for modeling
    X = np.array(merged_df.drop(['customer_id', 'product_id', 'purchased'], axis=1))
    y = merged_df.purchased

    print("Validate the model using 5-fold cross validation")
    model_validation(RandomForestClassifier(), X, y)

    # get the metrics like accuracy, fpr, fnr
    # these parameters slightly improves the performance compared to default model
    model_metrics(RandomForestClassifier(n_estimators=100, max_features=20), X, y)

    # Get the model to be used later for prediction
    rf_model = RandomForestClassifier(n_estimators=100, max_features=20).fit(X, y)

    # choose threshold based on accuracy, fpr and fnr
    threshold = 0.48

    print("################## PREDICTION on next assortment ###########################")
    print("Get merged data from three files - customer_features, product_features and next_month_assortment ")
    data_next_assortment = merge_data(cust_data=customer_features,
                                      prod_data=product_features,
                                      assort_data=next_assortment)

    # change data types of columns
    cols_not_cat = ['customer_id', 'product_id', 'length']
    for col in data_next_assortment.columns:
        if col not in cols_not_cat:
            data_next_assortment[col] = data_next_assortment[col].astype('category')

    # One hot encoding for categorical variables
    data_next_assortment = pd.get_dummies(data_next_assortment,
                                          columns=['age_bucket', 'is_returning_customer', 'genre', 'difficulty', 'fiction'],
                                          prefix=['age', 'return', 'genre', 'diff', 'fic'])

    # Predict probabilities whether customer will purchase or not on next assortment
    probas_ = rf_model.predict_proba(data_next_assortment.drop(['customer_id', 'product_id'], axis=1))
    data_next_assortment['purchased'] = probas_[:, 1] > threshold
    print("Predict whether customer will purchase or not on next assortment\n",
          data_next_assortment['purchased'].value_counts())
    data_next_assortment.purchased = data_next_assortment.purchased.astype('int')

    # Get predicted value for each (customer, product) pair on next assortment file
    next_assortment = pd.read_csv('next_month_assortment.csv')
    next_assort_predicted = data_next_assortment[['product_id', 'customer_id', 'purchased']].\
        merge(next_assortment, on=['product_id', 'customer_id'], how='right')

    print("Check whether the company would be able to repay loan and as well as next purchase order")
    profit = check_loan_repay_status(original_order=orig_purchase,
                                     next_order=next_purchase,
                                     last_assort=last_assortment,
                                     next_assort=next_assort_predicted)

    sys.stdout.close()
    sys.stdout = temp

    if profit >= 0:
        print("Yes")  # able to pay loan and afford next purchase order
    else:
        print("No")


import numpy as np
import cv2
import serial
import pygame
from pygame.locals import *
import socket
import time
import os

import matplotlib.pyplot as plt

class CollectTrainingData(object):

    def __init__(self, host, port, serial_port, input_size):

        # Create server
        self.server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.server_socket.bind((host, port))
        self.server_socket.listen(0)

        # accept a single connection
        self.connection = self.server_socket.accept()[0].makefile('rb')

        # connect to a serial port
        self.ser = serial.Serial(serial_port, 115200)
        self.send_inst = True

        self.input_size = input_size

        # initail pygame for driving the car
        pygame.init()
        pygame.display.set_mode((250, 250))

    # Detect the rigion of interest in image
    def region_of_interest(self,image):

        #take the hight and width of the image which mean y coordinate
        hight = image.shape[0]
        width = image.shape[1]

        #detect the parameters of polygon
        polys = np.array([[(0, 50), (0, hight),
             (width, hight), (width, 50) ,(250,25),(70,25)]])

        #make a black image and this size equal the oraginal image
        mask = np.zeros_like(image)

        #draw a polygon and fill inside it by a white color
        cv2.fillPoly(mask, polys, 1)

        #do an AND operator between the mask and the orginal image
        #the output will be only the line in the street
        image_AND = cv2.bitwise_and(image, mask)

        return image_AND

    # process the image
    def process_img(self,img):

        # reshape the image
        img = img.reshape(100,320,1)

        # minus max pixel value form other pixels
        # the output will make the black region to be white and the wight region to be black
        img = np.max(img)-img

        # normalize the image by divided it on the max pixel value
        img = img/np.max(img)

        # do threshhold on the image to convert it to black and wight
        ret, img = cv2.threshold(img, 0.66, 1, cv2.THRESH_BINARY)

        # take region of interest from image
        img = self.region_of_interest(img)

        return img

    def collect(self):

        saved_frame = 0
        total_frame = 0

        # collect images for training
        print("Start collecting images...")
        print("Press 'q' or 'x' to finish...")
        start = cv2.getTickCount()

        X = np.empty((0, self.input_size))
        y = np.empty((0, 1))
        print(y)

        # stream video frames one by one
        try:
            stream_bytes = b' '
            frame = 1
            while self.send_inst:
                stream_bytes += self.connection.read(1024)
                first = stream_bytes.find(b'\xff\xd8')
                last = stream_bytes.find(b'\xff\xd9')

                if first != -1 and last != -1:
                    jpg = stream_bytes[first:last + 2]
                    stream_bytes = stream_bytes[last + 2:]
                    image_gray = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_GRAYSCALE)
                    # image_gray = cv2.imdecode(np.frombuffer(jpg, dtype=np.uint8), cv2.IMREAD_COLOR)

                    # select lower half of the image
                    height, width = image_gray.shape
                    roi = image_gray[int(height/2):height-20, :]

                    cv2.imshow("img",roi)

                    roi = self.process_img(roi)
                    cv2.imshow('roi', roi)

                    # reshape the roi image into a vector
                    temp_array = roi.reshape(1, int(height/2)-20 * width).astype(np.float32)

                    frame += 1
                    total_frame += 1

                    # get input from human driver
                    for event in pygame.event.get():
                        if event.type == KEYDOWN:
                            key_input = pygame.key.get_pressed()

                            # simple orders
                            if key_input[pygame.K_UP]:
                                print("Forward")
                                saved_frame += 1
                                X = np.vstack((X, temp_array))
                                y = np.vstack((y, 2 )) #self.k[2]
                                self.ser.write(b'f')

                            elif key_input[pygame.K_DOWN]:
                                print("Reverse")
                                self.ser.write(b'b')

                            elif key_input[pygame.K_RIGHT]:
                                print("Right")
                                X = np.vstack((X, temp_array))
                                y = np.vstack((y, 1)) #self.k[1]
                                saved_frame += 1
                                self.ser.write(b'r')

                            elif key_input[pygame.K_LEFT]:
                                print("Left")
                                X = np.vstack((X, temp_array))
                                y = np.vstack((y, 0)) #self.k[0]
                                saved_frame += 1
                                self.ser.write(b'l')

                            elif key_input[pygame.K_x] or key_input[pygame.K_q]:
                                print("exit")
                                self.send_inst = False
                                self.ser.write(b's')
                                self.ser.close()
                                break

                        elif event.type == pygame.KEYUP:
                            self.ser.write(b's')

                    if cv2.waitKey(1) & 0xFF == ord('q'):
                        break

            # save data as a numpy file
            file_name = str(int(time.time()))
            directory = "training_data"
            if not os.path.exists(directory):
                os.makedirs(directory)
            try:
                np.savez(directory + '/' + file_name + '.npz', train=X, train_labels=y)
            except IOError as e:
                print(e)

            end = cv2.getTickCount()
            # calculate streaming duration
            print("Streaming duration: , %.2fs" % ((end - start) / cv2.getTickFrequency()))

            print(X.shape)
            print(y.shape)
            print("Total frame: ", total_frame)
            print("Saved frame: ", saved_frame)
            print("Dropped frame: ", total_frame - saved_frame)

            print(y)
            print(y.shape)

        finally:
            self.connection.close()
            self.server_socket.close()

if __name__ == '__main__':
    # host, port
    h, p = "", 8000

    # serial port
    serial_port = "COM8"

    # vector size, half of the image
    s = 100 * 320

    ctd = CollectTrainingData(h, p, serial_port, s)
    ctd.collect()


# coding=utf-8
import wx

class Example(wx.Frame):
    def __init__(self, parent, title, size):
        super(Example, self).__init__(parent, title=title, size=size)
        self.panel = wx.Panel(self)
        self.tranningData_1 = wx.TextCtrl(self.panel)  # 训练数据
        self.tranningData_2 = wx.TextCtrl(self.panel)
        self.path = wx.TextCtrl(self.panel)  # 文件保存路径
        self.InitUI()
        self.Centre()
        self.Show()

    def InitUI(self):
        sizer = wx.GridBagSizer(0, 0)
        sizer.Add(wx.StaticText(self.panel, label="训练数据范围"), pos=(1, 0), flag=wx.ALL, border=5)
        sizer.Add(self.tranningData_1, pos=(2, 0), flag=wx.EXPAND | wx.ALL, border=5)
        sizer.Add(self.tranningData_2, pos=(2, 1), flag=wx.EXPAND | wx.ALL, border=5)
        sizer.Add(wx.StaticText(self.panel, label="评估结果保存路径"), pos=(3, 0), flag=wx.ALL, border=5)
        sizer.Add(self.path, pos=(4, 0), flag=wx.ALL, border=5)
        button = wx.Button(self.panel, label="开始评估")
        sizer.Add(button, pos=(4, 1), flag=wx.ALL, border=5)
        self.panel.SetSizerAndFit(sizer)

app = wx.App()
Example(None, title='YiGuTong', size=(270, 220))
app.MainLoop()


#Simone Lopes dos Santos
#Lista de exercícios 01

# Exercício 01
a = 2
a_elevado_cubo = pow(a, 3)

r1 = a_elevado_cubo

#Exercício 02

b = -2
b_elevado_cubo = pow(b, 3)

r2 = b_elevado_cubo

#Exercício 03

c = 1
c_elevado_cubo = pow(c, 3)

r3 = c_elevado_cubo

#Exercício 04

d = -1
d_elevado_zero = pow(c, 0)

r4 = d_elevado_zero

#Exercício 05

e = 2
e_elevado_zero = pow(e, 0)

r5 = e_elevado_zero

#Exercício 06

f = 2 / 5
f_elevado_cubo = pow(f, 3)

r6 = f_elevado_cubo

#Exercício 07

g = 3
g_elevado_quadrado = pow(g, -2)

r7 = g_elevado_quadrado

#Exercício 08

h = 1 / 2
h_elevado_cubo = pow(h, -3)

r8 = h_elevado_cubo

#Exercício 09

j = -1
j_elevado_cubo = pow(j, 3)
j_elevado_quarta = pow(j_elevado_cubo, 4)

r9 = j_elevado_quarta

#Exercício 10

k = 0.5
k_elevado_cubo = pow(k, 3)

r10 = k_elevado_cubo

#Exercício 11

k = 0.25
k_elevado_quarta = pow(k, 4)

r11 = k_elevado_quarta

#Exercício 12

l = 0
l_elevado_quarta = pow(l, 4)

r12 = l_elevado_quarta

#Exercício 13

m = 1 + 0.41
m_elevado_quadrado = pow(m, 2)

r13 = m_elevado_quadrado

#Exercício 14

n = 5
n_elevado_quadrado = pow(n, 2)

o = -2
o_elevado_quarta = pow(o, -4)

p = 1 / 4

r14 = p + n_elevado_quadrado + o_elevado_quarta

#Exercício 15

q = 2
q_elevado_cubo = pow(q, -3)

r = -4
r_elevado_quinta = pow(r, -5)

r15 = q_elevado_cubo + r_elevado_quinta

#Exercício 16

s = 4 / 5
t = 1 / 2
s_com_t = s - t + 1

s_t_elevado_quadrado = pow(s_com_t, -2)

u = 3
u_elevado_quadrado = pow(u, 2)
v = 4 - 5
v_elevado_quadrado = pow(v, -2)
u_com_v = 1 + u_elevado_quadrado - v_elevado_quadrado

r16 = s_t_elevado_quadrado + (1 / u_com_v)

#Armazenando respostas em vetor e printando para o usuário
respostas =[r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16]

i = 1
exercicio = 1

for i in range (len(respostas)):
    print("Resposta", exercicio, ":", "%.2f" % respostas[i])
    i += 1
    exercicio += 1


#usr/bin/python
# -*- coding:utf-8 -*-
import easygui as g
import os
from pyhanlp import *
from test_utility import ensure_data
import numpy as np
import sys
from pyhanlp import SafeJClass

ouput_path = 'output'
if os.path.exists(ouput_path):
    os.system('rd /s /q '+ouput_path)
os.system('md '+ouput_path)
correct_classifications = ouput_path + '/correct_classifications.txt'
# if os.path.isfile(correct_classifications):
    # os.system("del "+ correct_classifications)
os.system("type nul> "+ correct_classifications)
incorrect_classifications = ouput_path + '/incorrect_classifications.txt'
# if os.path.isfile(incorrect_classifications):
    # os.system("del "+incorrect_classifications)
os.system("type nul> "+incorrect_classifications)
output_by_category = ouput_path + '/output_by_category.txt'
os.system("type nul> "+output_by_category)
output_by_paper = ouput_path + '/output_by_paper.txt'
os.system("type nul> "+output_by_paper)

WordVectorModel = JClass('com.hankcs.hanlp.mining.word2vec.WordVectorModel')
DocVectorModel = JClass('com.hankcs.hanlp.mining.word2vec.DocVectorModel')
model_path = os.path.join(
    ensure_data('hanlp-wiki-vec-zh', 'http://hanlp.linrunsoft.com/release/model/hanlp-wiki-vec-zh.zip'),
    'hanlp-wiki-vec-zh.txt')
word2vec = WordVectorModel(model_path)
doc2vec = DocVectorModel(word2vec)

def Welcome(Title):
    if g.msgbox("您好！\n欢迎使用（不）智能论文分类器！", title = Title, ok_button = "开始分类"):
        pass
    else:
        sys.exit(0)

def InputPaths(Title):
    path_names = ["类别文件地址","待分类文本地址"]
    paths_default = ["./categories/categories for test_1.txt","./categories/中文样本集/大数据采集、存储、分析、处理与应用"]
    input_paths = g.multenterbox("请提供以下文件的路径：",Title,path_names,paths_default)
    return input_paths
    if not input_paths:
        sys.exit(0)

def CategoryTxt(Title,lines):
    if g.textbox("请确认类别文件", Title, lines):
        pass
        return 0
    else:
        return 1

def Complete(Title):
    ifcontinue = g.ynbox("分类完毕！结果请见output文件夹！\n是否继续分类？",title = Title)
    return ifcontinue

def Exit(Title):
    g.msgbox("感谢使用，再见！", title=Title, ok_button="ヾ(￣▽￣)Bye~Bye~")
    sys.exit(0)

Title = "（不）智能论文分类器"
Welcome(Title)
input_paths = InputPaths(Title)

categories_file = input_paths[0]
if categories_file[-3:] != "txt":
    g.msgbox("请输入类别txt文件地址！", title = Title, ok_button="好的，我重输")
    input_paths = InputPaths(Title)

categories_file = input_paths[0]
docs = []
categories = open(categories_file,'r')
lines = categories.readlines()
for line in lines:
    line = line[line.find('*')+2:line.rfind('*')-2]
    docs.append(line)
for idx, doc in enumerate(docs):
    doc2vec.addDocument(idx, doc)
categories_check = CategoryTxt(Title,lines)
while categories_check !=0 :
    InputPaths(Title)
    categories_check = CategoryTxt(Title,lines)

papers_path = input_paths[1]
if papers_path[-1:] != '/':
    papers_path += '/'

correct_label = papers_path[papers_path.find("中文样本集")+6:-1]
correct_num = 0

input_list = os.listdir(papers_path)
count = 0
for input_file_name in input_list:
    input_file = open(papers_path + input_file_name,'r')
    lines = input_file.readlines()
    paper_title = lines[1]
    paper_keywords = lines[3]
    paper_abstract = ''
    for i in range(5,len(lines)):
        paper_abstract += lines[i]

    biases = [paper_title, paper_keywords, paper_abstract]
    biases_name = ["标题","关键词","摘要"]
    biases_weights = [1,1,1]
    confidence = np.zeros(len(docs))
    for i,bias in enumerate(biases):
        # key =str(HanLP.extractKeyword(input, 2))
        print(biases_name[i] + ': ' + bias)
        # g.msgbox(biases_name[i], title = Title, ok_button="ok")
        # print('↓↓↓↓##### 相似度 怕是假的吧 #####↓↓↓↓')

        # for res in doc2vec.nearest(title):
        #     print('%s = %.2f' % (docs[res.getKey().intValue()], res.getValue().floatValue()))
        output_list = []
        for res in doc2vec.nearest(bias):
            output_temp = str(docs[res.getKey().intValue()]) + ' ' + str(res.getValue().floatValue())
            output_list.append(output_temp)
        #print('%s = %.2f' % (docs[res.getKey().intValue()], res.getValue().floatValue()))
        # print (docs)
        for output in output_list:
            label = output[:output.rfind(' ')]
            confidence[docs.index(label)] += float(output[output.rfind(' ')+1:]) * biases_weights[i]
        print ('最相近类别：'+ output_list[0])
    print ("Correct Label: " + correct_label)
    predicted_label = docs[int(np.where(confidence == np.max(confidence))[0])]
    print("Prediction: " + predicted_label)
    # g.msgbox("分类结果：\n"+predicted_label, title=Title, ok_button="ok")
    count = count + 1
    print(count)

    # 训练用↓
    if predicted_label.find(correct_label) >=0:
        correct_num += 1
        which_file = correct_classifications
    else:
        which_file = incorrect_classifications
    with open(which_file,'a') as f:
            f.write('#'+str(count)+'\n')
            f.write(bias)
            f.write("Correct Label: " + correct_label+'\n')
            f.write("Prediction: " + output_list[0]+'\n')
            f.write("\n")

    # 输出结果
    with open(output_by_paper,'a') as f:
        f.write('#'+str(count) + ' ' + input_file_name + '\n')
        f.write(predicted_label + '\n')
ifcontinue = Complete(Title)
print ("Accuracy: "+ str(correct_num / count))
'''
if ifcontinue == True:
    os.system('python ui.py')
else:
    Exit(Title)
    '''


from netmiko import ConnectHandler
import subprocess

DEVICE_IP = {
'nn' : '1.1.1.1',
# 'minsk' : '2.2.2.2',
'kazan' : '3.3.3.3',
'rostov' : '4.4.4.4',
'vladimir' : '5.5.5.5',
'volgograd' : '6.6.6.6',
'cheboksary' : '7.7.7.7',
'chelyabinsk' : '8.8.8.8',
'omsk' : '9.9.9.9',
'sevastopol' : '10.10.10.10',
'perm' : '11.11.11.11',
'krasnoyarsk' : '12.12.12.12',
'spb' : '13.13.13.13',
'tomsk' : '14.14.14.14',
'belgorod' : '15.15.15.15',
# 'bookkeeping' : '16.16.16.16',
'irkutsk' : '17.17.17.17',
'khabarovsk' : '18.18.18.18',
'barbados' : '19.19.19.19',
'pimsk' : '20.20.20.20',
'cetk' : '21.21.21.21'
}

def copy_config():
    for CITY, IP in DEVICE_IP.items():
        reply = subprocess.run(['ping', '-n', '1', IP])
        if reply.returncode == 0:
            DEVICES_PARAMS = {
                'device_type' : 'cisco_asa',
                'ip'          : IP,
                'username'    : 'username',
                'password'    : 'password',
                'global_delay_factor' : 2 }
            connect = ConnectHandler(**DEVICES_PARAMS)
            connect.send_command('enable\n\n')
            config = connect.send_command('more system:running-config')

            with open(CITY, 'w') as cfg:
                cfg.writelines(config)

            connect.disconnect()

        else:
            continue

if __name__ == '__main__':
    copy_config()


import unittest
import os, glob
import filecmp
from shutil import rmtree, copy2
from scipy.stats import chisquare

import sys
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_PATH)

from random import randint, seed, uniform

import generator as g

# Specify the folderpaths used during testing
tmpfolder = "./tmp_test"
tmpcopyfolder = "./tmp_test_copy"
nonexistingfolder = "./temporary_test_folder_non_existant"

class TestGenerator(unittest.TestCase):

	def setUp(self):
		"""This setUp procedure will be called before every test.
		Ensures that no cnf-files exist in the base folder.
		Ensures that the folders `tmp_test` and `tmp_test2` exists.
		These folders will be used for some tests.

		Creates the file `hidden.solution` in the base folder.
		This file contains the solution "1 -2 3 -4 5 ... 99 -100".

		Additionally, a file `hidden2.solution` will be created.
		This file contains line breaks in the solution.
		The solution itself will be randomly generated.
		For this, variables between 1 and 1000 will be used.
		"""

		for f in glob.glob("./gen*.cnf"):
			os.remove(f)

		os.makedirs(tmpfolder, exist_ok=True)
		os.makedirs(tmpcopyfolder, exist_ok=True)

		with open("hidden.solution", "w") as hidden_sol:
			sol = ""
			for i in range(1, 101):
				if i % 2 == 0:
					sol += "-"
				sol += str(i)+" "
			hidden_sol.write(sol[:-1]) # do not write the last " "

		seed()
		self.hidden2 = [] # The second hidden solution will additionaly be safed in a list.
		with open("hidden2.solution", "w") as hidden_sol:
			sol = ""
			# Count the amount of positive literals in the solution with a counter variable.
			# This measure is used to randomly include line breaks.
			# The beginning of the file `hidden2.solution` might look something like this:
			#    -1
			#    -2
			#    3 -4 -5 -6 7 -8 9 10 11 -12 13 -14 15 16 17 18
			#    19 -20 21 22 23 24 -25 26 27 -28 -29 -30 31 32 -33 -34 35
			#    36 -37 -38 -39 40 41 42 43 44 45 46 -47 -48 49 -50 -51 52
			#    -53
			#    54 -55 -56 57 -58 59 -60 -61 -62 -63 64 65 -66 67 68 69 -70 -71 -72 73 74
			#    -75
			counter = 0
			for i in range(1, 1001):
				# Choose randomly 0 or 1.
				# If rand == 1, the variable i will be added positively to the solution.
				# Otherwise, we will include -i as part of the hidden solution.
				rand = randint(0,1)
				if rand == 1:
					counter += 1
					sol += str(i)+" "
					self.hidden2.append(i)
				else:
					sol += "-"+str(i)+" "
				# Add the random line breaks:
				if counter % 10 == 0:
					sol += "\n"
			hidden_sol.write(sol[:-1]) # do not write the last " "

	def tearDown(self):
		"""Will be executed after every test.
		Ensures that all cnf-files that were generated during the test will be deleted after the test.
		Ensures that all temporary test folder will be deleted.
		Additionally, the files `hidden.solution` and `hidden2.solution` will be deleted.
		"""

		rmtree(tmpfolder)
		rmtree(tmpcopyfolder)
		rmtree(nonexistingfolder, ignore_errors=True) # this folder does only get generated during one test

		os.remove("hidden.solution")
		os.remove("hidden2.solution")

		for f in glob.glob("./gen_*.cnf"):
			os.remove(f)

	def test_okay(self):
		"""Test if the test framework works propertly by doing a very simple test.
		"""

		print(sys._getframe(  ).f_code.co_name)
		self.assertTrue(True)
		print("Test Framework okay.")
		print("Started from " + os.getcwd())

	def test_p_values(self):
		"""In this this test we check if invalid p-values will be declined.
		There are the following sources of error:
		1) A list of p-values was given but the length of this list is smaller than k.
		2) A list of p-values was given but
		   a) some entry is > 1.0, or
		   b) some entry is < 0.0, or
		   c) no entry is > 0.0.
		"""

		print(sys._getframe(  ).f_code.co_name)

		n = 1000
		m = 4000
		o = tmpfolder

		# The standard case is k=3. We pass a p-value list with only two entries.
		# This should raise a ValueError.
		p = [0.3, 0.5]
		with self.assertRaises(ValueError):
			g.main(n, m, p=p, o=o)

		# We also pass a list with too many entries. We expect a ValueError.
		p = [0.5, 0.3, 0.2, 0.1]
		with self.assertRaises(ValueError):
			g.main(n, m, p=p, o=o)

		# What happens when there is a p-value > 1.0? We again expect a ValueError.
		p = [0.3, 1.2, 0.5]
		with self.assertRaises(ValueError):
			g.main(n, m, p=p, o=o)

		# And what if there is a p-value < 0.0?
		p = [0.0, 1.0, -0.5]
		with self.assertRaises(ValueError):
			g.main(n, m, p=p, o=o)

		# Does the programm accept lists where no entry is > 0.0?
		p = [0.0, 0.0, 0.0]
		with self.assertRaises(ValueError):
			g.main(n, m, p=p, o=o)

	def test_valid_parameters(self):
		"""This test veryfies that only valid parameter combinations will be accepted.
		"""

		print(sys._getframe(  ).f_code.co_name)

		o = tmpfolder

		# Test if we can generate files without passing the required positional arguments `n` and `m`.
		with self.assertRaises(TypeError):
			g.main(o=o)

		with self.assertRaises(TypeError):
			g.main(n=30, o=o)

		with self.assertRaises(TypeError):
			g.main(m=90, o=o)

		# Test whether negative n-values will be declined.
		n = -1000
		m = 4000
		with self.assertRaises(ValueError):
			g.main(n, m, o=o)

		# Test whether negative m-values will be declined.
		n = 1000
		m = -4000
		with self.assertRaises(ValueError):
			g.main(n, m, o=o)

		# Test whether k-values smaller or equal than 0 will be declined.
		m = 4000
		k = 0
		with self.assertRaises(ValueError):
			g.main(n, m, k=k, o=o)

		# Check if a non-existing file will be accepted as a hidden solution.
		k = 3
		F = os.path.join(tmpfolder, "hid.sol")
		with self.assertRaises(FileNotFoundError):
			g.main(n, m, k=k, o=o, F=F)

	def calculate_file_name(self, n, m, k=3, s=42):
		"""Given the parameters `n`, `m`, `k` (optional, standard 3), `s` (optional, standard 42),
		this method returns the correct file name of the file generated with these parameters as a string.
		"""
		return f"gen_n{n}_m{m}_k{k}SAT_seed{s}.cnf"

	def test_generated_name(self):
		"""Checks if the generated file have the correct filename.
		Checks if the files are in the correct folders as well.
		"""

		print(sys._getframe(  ).f_code.co_name)

		# First, let's check if the file gets generated in the base folder if no output folder is specified.
		# The files then should be generated in "./".

		# The filename should read "gen_n100_m420_k3SAT_seed42.cnf".
		n = 100
		m = 420
		path = os.path.join('./', self.calculate_file_name(n, m))
		g.main(n, m)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# The filename should read "gen_n100_m420_k6SAT_seed42.cnf".
		n = 100
		m = 420
		k = 6
		path = os.path.join('./', self.calculate_file_name(n, m, k=k))
		g.main(n, m, k=k)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# The filename should read "gen_n999_m9090_k9SAT_seed1909.cnf".
		n = 999
		m = 9090
		k = 9
		s = 1909
		path = os.path.join('./', self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# The parameters `p` and `F` should have no influence on the filename.

		# Here, only the influence of `p` gets tested.
		n = 999
		m = 9090
		k = 9
		p = [0.1*c for c in range(1,k+1)]
		s = 1909
		path = os.path.join('./', self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, p=p, s=s)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# Here, only the `F` parameter gets tested.
		n = 1000
		m = 9090
		k = 9
		F = './hidden2.solution'
		s = 123
		path = os.path.join('./', self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s, F=F)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# Finally, both parameters get tested.
		n = 1000
		m = 9090
		k = 3
		p = [0.25*c for c in range(1,k+1)]
		s = 123
		F = './hidden2.solution'
		path = os.path.join('./', self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, p=p, s=s, F=F)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# Do the files get created properly in the specified output path?

		# Note, that the folder `./tmp_test` already exists (see setUp method).
		# To begin with, it gets tests if the program passes if
		# the output folder gets specified as `./tmp_test`.
		n = 1300
		m = 4201
		k = 5
		p = [0.16, 0.32, 0.64, 0.1, 0.00]
		s = 9
		o = tmpfolder
		path = os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, p=p, s=s, o=o)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# Next, we test what happens if the folder gets specified as `./tmp_test/`
		# (notice the additional / at the end of the string!).
		o = tmpfolder
		g.main(n, m, k=k, s=s, o=o)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

		# Finally, we test what happens if a non-existing folder gets passed.
		# The folder `nonexistingfolder` does not exist.
		o = f'./{nonexistingfolder}/tmp'
		path = os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s, o=o)
		self.assertTrue(os.path.isfile(path))
		os.remove(path)

	def test_reproducibility(self):
		"""Here, it gets tested whether two calls with identical seeds also deliver identical files.
		This test generates one file in `./tmp_test` and copies it to `./tmp_test2`.
		The file then gets generated again in `./tmp_test` and gets compared to the one in `./tmp_test2`.
		"""
		print(sys._getframe(  ).f_code.co_name)

		# First test:
		n = 100
		m = 420

		filename = self.calculate_file_name(n, m)
		path1 = os.path.join(tmpfolder, filename)
		path2 = os.path.join(tmpcopyfolder, filename)

		g.main(n, m, o=tmpfolder)
		copy2(path1, path2)
		g.main(n, m, o=tmpfolder)
		# filecmf.cmp(_,_) checks if two files are identical.
		# However, only statistics (hash values, etc.) get compared.
		# For a byte-by-byte check one has to set shallow=False.
		self.assertTrue(filecmp.cmp(path1, path2))

		# We run a second test including a hidden solution and all parameters.
		n = 1000
		m = 9999
		k = 8
		p = [0.3, 0.1, 0.5, 0.1, 0.3, 0.0, 0.7, 0.01]
		s = 9
		F = './hidden2.solution'

		filename = self.calculate_file_name(n, m, k=k, s=s)
		path1 = os.path.join(tmpfolder, filename)
		path2 = os.path.join(tmpcopyfolder, filename)

		g.main(n, m, k=k, p=p, s=s, F=F, o=tmpfolder)
		copy2(path1, path2)
		g.main(n, m, k=k, p=p, s=s, F=F, o=tmpfolder)
		self.assertTrue(filecmp.cmp(path1, path2))

	def get_header(self, filename, n_lines):
		"""This method reads the first `n_lines` of the file specified in `filename`
		and returns them as a list."""
		lines = []
		with open(filename, 'r') as file:
			for _ in range(n_lines):
				lines.append(file.readline())
		return lines

	def get_all_lines(self, filename):
		"""This method returns all lines of the given file as a list.
		"""
		with open(filename, 'r') as file:
			lines = file.readlines()
		return lines

	def test_header(self):
		"""Here we test whether the files get generated with the correct comment lines at the beginning of the file.
		We also check the parameter line.
		"""

		print(sys._getframe(  ).f_code.co_name)

		# First test case:
		n = 2222
		m = 10000
		k = 2
		p = [0.16, 0.32]
		s = 16
		o = tmpfolder
		path = os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, p=p, s=s, o=o)
		header = self.get_header(path, 6)
		# The header should have the following form:
		#   "c This file was generated by _"
		#   "c Created by calling python3 generator.py -n 2222 -m 6666 -k 2 -p 0.16 0.32 -s 16 -o ./tmp_test"
		#   "c Hidden solution: pos.int pos.int pos.int ..."
		#   "c p-values 0.16 0.32"
		#   "p cnf 2222 6666"
		#   "'int' 'int' 0"
		self.assertRegex(header[0], "^c This file was generated by[\w\s\.]+\n$")
		self.assertRegex(header[1], f"^c Created by calling python3 generator\.py -n {n} -m {m} -k {k} -p 0\.16 0\.32 -s {s} -o {tmpfolder}\n$")
		self.assertRegex(header[2], "^c Hidden solution: ([\d]+ )*[\d]+\n$")
		self.assertRegex(header[3], "^c p-values 0\.16 0\.32\n$")
		self.assertRegex(header[4], "^p cnf 2222 10000\n$")
		self.assertRegex(header[5], "^(-?[1-9][\d]* ){2}0\n$")

		# We also check the case where a hidden solution was delivered.
		n = 100
		m = 4312
		k = 3
		s = 166
		F = './hidden.solution'
		path =  os.path.join('./', self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s, F=F)
		header = self.get_header(path, 6)
		# The header should have the following form:
		#   "c This file was generated by _"
		#   "c Created by calling python3 generator.py -n 1010 -m 4312 -k 3 -s 166 -F ./hidden.solution"
		#   "c Hidden solution: 1 3 5 ... 99"
		#   "c p-values 1.00 1.00 1.00"
		#   "p cnf 1010 4312"
		#   "'int' 'int' 'int' 0"
		self.assertRegex(header[0], "^c This file was generated by[\w\s\.]+\n$")
		self.assertRegex(header[1], f"^c Created by calling python3 generator\.py -n {n} -m {m} -k {k} -s {s} -F \./hidden\.solution\n$")
		# The hidden solution will be checked in the next block of code.
		self.assertRegex(header[3], "^c p-values 1\.00 1\.00 1\.00\n$")
		self.assertRegex(header[4], f"^p cnf {n} {m}\n$")
		self.assertRegex(header[5], "^(-?[1-9][\d]* ){3}0\n$")

		# The line specifying the hidden solution will be split at every whitespace.
		# We then first check the part coming before the actual hidden solution.
		hidden_sol_line = header[2].split()
		self.assertEqual(hidden_sol_line[0], "c")
		self.assertEqual(hidden_sol_line[1], "Hidden")
		self.assertEqual(hidden_sol_line[2], "solution:")
		# Next, we iterate over the part specifying the hidden solution.
		# Recall that the hidden solution is 1 3 5 ... 9 (note that only positive literals get written to this line).
		hid = 1
		for i in range(3, len(hidden_sol_line)):
			self.assertEqual(hidden_sol_line[i], str(hid))
			hid += 2

		# A final, last test for the second randomly generated hidden solution file with an randomly generated p-list.
		n = 1000
		m = 4200
		k = 3
		p = [uniform(0.01, 1.00) for _ in range(k)] # randomly generate a p-list
		F = './hidden2.solution'
		path =  os.path.join('./', self.calculate_file_name(n, m))
		g.main(n, m, F=F)
		header = self.get_header(path, 6)
		# The header should have the following form:
		#   "c This file was generated by _"
		#   "c Created by calling python3 generator.py -n 1000 -m 4200 -F ./hidden2.solution"
		#   "c Hidden solution: {randomly generated}"
		#   "c p-values {randomly generated}"
		#   "p cnf 1000 4200"
		#   "'int' 'int' 'int' 0"
		self.assertRegex(header[0], "^c This file was generated by[\w\s\.]+\n$")
		self.assertRegex(header[1], f"^c Created by calling python3 generator\.py -n {n} -m {m} -F \./hidden2\.solution\n$")
		# The hidden solution will be checked in the next block of code.
		self.assertRegex(header[3], "^c p-values [\s\d\w\.]*\n$")
		self.assertRegex(header[4], "^p cnf 1000 4200\n$")
		self.assertRegex(header[5], "^(-?[1-9][\d]* ){3}0\n$")

		# The line specifying the hidden solution will be split at every whitespace.
		# We then first check the part coming before the actual hidden solution.
		hidden_sol_line = header[2].split()
		self.assertEqual(hidden_sol_line[0], "c")
		self.assertEqual(hidden_sol_line[1], "Hidden")
		self.assertEqual(hidden_sol_line[2], "solution:")
		# Next, we iterate over the part specifying the hidden solution.
		for i in range(3, len(hidden_sol_line)):
			self.assertEqual(hidden_sol_line[i], str(self.hidden2[i-3]))

	def check_clause_width_and_used_variables(self, lines, n, k=3):
		"""This method checks if all clauses given by the `lines` of a generated file
		have width `k`.
		We also check whether every variable (from 1 up to `n`) is used at least once
		and that every clause contains variables between 1 and `n` and ends with a "0".
		"""
		used_vars = set()
		for i in range(5, len(lines)):
			literals = lines[i].rstrip().split()
			# Each time we should discover a clause of width k, i.e., k literals plus a terminating 0.
			self.assertEqual(len(literals), k+1)
			self.assertEqual(literals[-1], "0")

			for l in literals[:-1]:
				lit = abs(int(l))
				self.assertGreaterEqual(lit, 1)
				self.assertLessEqual(lit, n)
				used_vars.add(lit)

		# Here comes the (double) check whether every variable is present in at least one clause:
		self.assertEqual(len(used_vars), n)

		for l in range(1, n+1):
			self.assertIn(l, used_vars)

	def test_parameters_n_m_k(self):
		"""Here we test whether the generated files abide by the boundary conditions,
		i.e., if when the progam gets called with `n`, `m`, and `k`,
		the generated formula is in k-CNF with n variables over m clauses.
		All clauses get checked for proper format.
		We also test the strict assumption (to make all SAT-solvers happy) that
		clauses in the generated file are not separated by empty lines.
		"""

		print(sys._getframe(  ).f_code.co_name)

		# We check the standard case k=3.
		n = 1234
		m = 5182
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m))
		g.main(n, m, o=o)
		lines = self.get_all_lines(path)

		# In the first 5 lines the header can be found.
		# The header, however, gets already checked in test_header.
		# Thus, we can skip the first 5 lines.
		# Our first check will be the number of lines: This number should be m+5.
		self.assertEqual(m+5, len(lines))

		# After that we check every line that contains a clause and make sure that
		# the width of the clause equals k.
		# Simultaneously, we check whether every variable is used at least once
		# and that every variable is between 1 and n.
		self.check_clause_width_and_used_variables(lines, n)

		# We repeat everything with k=5 and a hidden solution.
		n = 100
		m = 4000
		k = 5
		s = 99
		F = "./hidden.solution"
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s, o=o, F=F)
		lines = self.get_all_lines(path)

		# In the first 5 lines the header can be found.
		# The header, however, gets already checked in test_header.
		# Thus, we can skip the first 5 lines.
		# Our first check will be the number of lines: This number should be m+5.
		self.assertEqual(m+5, len(lines))

		# After that we check every line that contains a clause and make sure that
		# the width of the clause equals k.
		# Simultaneously, we check whether every variable is used at least once
		# and that every variable is between 1 and n.
		self.check_clause_width_and_used_variables(lines, n, k=k)

	def convert_hidden_sol_to_set(self, hid_string, n):
		"""This method converts the hidden solution string in a set.
		A literal is in this set if and only if it is part of the hidden solution.
		"""
		hid_list = hid_string.rstrip().split()
		hid = set()
		for i in range(1, n+1):
			if str(i) in hid_list:
				hid.add(str(i))
			else:
				hid.add(str(-i))
		return hid

	def check_if_hidden_solution_sats_clauses(self, hid, lines):
		"""This method checks if a given hidden solution (passed as a set) satisfies
		all clauses specified by the `lines` of the file.
		"""
		for i in range(5, len(lines)):
			# We convert every clause into a list of strings.
			# The last entry of every such list gets removed since it is equal to "0" (the end-of-clause-symbol).
			literals = lines[i].split()[:-1]
			satisfied = False
			for lit in literals:
				if lit in hid:
					satisfied = True # it suffices that one literal in every clause is satisfied
					break
			# if not satisfied:
			#     print(literals)
			#     print(sorted( [int(x) for x in hid], key=abs ))
			self.assertTrue(satisfied)

	def test_hidden_solution(self):
		"""Here we test if the given hidden solutions does indeed
		constitute a satisfying assignment of the generated instance.
		"""

		print(sys._getframe(  ).f_code.co_name)

		# We begin with the standard value of k=3.
		n = 700
		m = 2961
		s = 101010
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m, s=s))
		g.main(n, m, s=s, o=o)
		lines = self.get_all_lines(path)
		# The hidden solution is written in lines[2].
		hid = self.convert_hidden_sol_to_set(lines[2],n)
		self.check_if_hidden_solution_sats_clauses(hid, lines)

		# We also check if this works with a given hidden solution.
		n = 100
		m = 1200
		k = 4
		s = 111
		p = [0.1, 0.2, 0.4, 0.8]
		F = './hidden.solution'
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s, p=p, o=o, F=F)
		lines = self.get_all_lines(path)
		# The hidden solution is written in lines[2].
		hid = self.convert_hidden_sol_to_set(lines[2],n)
		self.check_if_hidden_solution_sats_clauses(hid, lines)

		# A final check with the second hidden solution file.
		n = 1000
		m = 6000
		k = 2
		s = 1919
		F = './hidden2.solution'
		path =  self.calculate_file_name(n, m, k=k, s=s)
		g.main(n, m, k=k, s=s, F=F)
		lines = self.get_all_lines(path)
		# The hidden solution is written in lines[2].
		hid = self.convert_hidden_sol_to_set(lines[2],n)
		self.check_if_hidden_solution_sats_clauses(hid, lines)

	def check_clause_validity(self, lines):
		"""This method checks if the clauses in `lines` are tautologies or improper k-SAT clauses
		(this second part of the test kind of duplicates check_clause_width_and_used_variables).
		"""
		for i in range(5, len(lines)):
			# We convert every clause in a list of strings.
			# The last entry of every list can be removed since it is equal to "0" (the end-of-clause-symbol).
			literals = lines[i].split()[:-1]
			lit_set = set( [ abs((int(s))) for s in literals ] )
			self.assertEqual(len(literals), len(lit_set))

	def test_constraints(self):
		"""We test the following two side constraints:
		1) The generator is not allowed to produce tautological clauses.
		2) The generator has to produce proper k-SAT clauses.
		"""

		print(sys._getframe(  ).f_code.co_name)

		# The higher k and the more clauses are present the liklier it gets
		# to generate a tautological clause or a clause with a double literal,
		# e.g., clauses of the form {x, y, -x} or {x, y, x}.
		n = 100
		m = 1000
		k = 40
		s = 19
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s, o=o)
		lines = self.get_all_lines(path)
		self.check_clause_validity(lines)

		# A second try:
		n = 1000
		m = 2000
		k = 6
		s = 199
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, k=k, s=s, o=o)
		lines = self.get_all_lines(path)
		self.check_clause_validity(lines)

	def calculate_rejected_prob(self, p):
		"""This function calculated the probability that an arbitrary clause that was generated will be
		rejected by the sampling process based on the given p-values.
		"""

		# Python is a zero-based numbering programming languages (one of its few drawbacks in our opinion).
		# The p-vector used in the description of the generator is one-based, i.e., p = (p_1, ..., p_k).
		# Thus, we extract the p-vector from the p-list to not confuse the reader.
		p1 = p[0]
		p2 = p[1]
		p3 = p[2]

		# If a 3-clause C = (a_1, a_2, a_3) gets sampled ramdomly, we can calculate the probability R
		# that C gets rejected by the p-list based sampling as follows:
		# R = Prob[C gets rej. \cap C contains 3 false literals w.r.t. the hidden solution]
		#   + Prob[C gets rej. \cap C contains 2 false literals w.r.t. the hidden solution]
		#   + Prob[C gets rej. \cap C contains 1 false literals w.r.t. the hidden solution]
		#   + Prob[C gets rej. \cap C contains 0 false literals w.r.t. the hidden solution]
		#   = 1 + 3 * (1-p_1) + 3 * (1-p_2) + (1-p_3),
		# since clauses with 3 false literals always get rejected and (3 choose 2) = (3 choose 1) = 3.
		return (1 + 3*(1-p1) + 3*(1-p2) + (1-p3))/8.0

	def calculate_add_probabilities(self, p, W):
		"""This function calculates the probabilities that a clause will be added.

		Fix a complete assignment alpha to all variables of the formula.
		For i = 0, ..., 3 we call a clause that has i literals set to true under alpha an i-clause.
		Given a randomly generated claues, consider the following scheme:
			FFF
			FFT <--
			FTF <--
			FTT
			TFF <--
			TFT
			TTF
			TTT
		It is easy to see that for example
			q_1 := Prob[a 1-clause will be created] = 3/8 (see arrows).
		More generally, letting
			q_i := Prob[a i-clause will be created],
		we have
			q_0 = 1/8,
			q_1 = 3/8,
			q_2 = 3/8,
			q_3 = 1/8.
		Thus
			P_1 := Prob[a 1-clause will be added]
				 = Prob[a 1-clause will be created in round 1 AND this 1-clause gets sampled in round 1]
				   + Prob[a non-1-clause will be created in round 1] * Prob[a 1-clause will be created in round 2 AND this 1-clause gets sampled in round 2]
				   + ...
				 = q_1 * p_1 + [q_0 * 1 + q_1 * (1 - p_1) + q_2 * (1 - p_2) + q_3 * (1 - p_3)] * q_1 * p_1 + ...
				 = q_1 * p_1 + (5/8 - 3/8 * p_2 - 1/8 * p_3) + ...
				 = q_1 * p_1 * sum_{t=0}^{infty} (5/8 - 3/8 * p_2 - 1/8 * p_3)^{t}
				 = 3/8 * p_1 * 1 / (1 - W_1),
		where
			W_i := q_0 + sum_{j=1}^{3} q_j * (1 - p_j).
		In general,
			P_i := Prob[an i-clause will be added]
				 = q_i * p_i * 1 / (1-W).
		"""
		p1 = p[0]
		p2 = p[1]
		p3 = p[2]
		return p1*3/(8.0 * (1-W)), p2*3/(8.0 * (1-W)), p3*1/(8.0 * (1-W))

	def count_p_clauses(self, clauses, hidden_list, total_num_of_vars):
		"""This method counts the number of "correct" literals in the list `clauses` w.r.t. the given `hidden_list`.
		The method needs also `total_num_of_vars`, the total number of variables appearing in the whole formula.
		Note, that this method is only implemented for the 3-CNF case.
		"""

		# We create a dictionary that in the end will contain the number of clauses with 1, 2, or 3 "correct" literals.
		# Nota bene: There cannot be a clause with 0 "correct" literals, as this would mean that the clause (and thus the whole formula)
		# gets falsified. However, we construct satisfiable formulas. This was checked via `test_hidden_solution`.
		clause_count = {1:0, 2:0, 3:0}

		# The passed `hidden_list` will be an incomplete hidden solution in the sense that only positive literals of
		# the underlying hidden solution will be listed.
		# Thus, we convert the list `hidden_list` into a dictionary `hidden_complete_dict` whereby we can identify ne negative literals.
		hidden_complete_dict = {}
		for i in range(1, total_num_of_vars+1):
			if i in hidden_list:
				hidden_complete_dict[i] = 1
			else:
				hidden_complete_dict[i] = -1

		# For each clause check how many "correct" literals it contains.
		for clause in clauses:
			# We convert each clause in a list of strings.
			# The last entry "0" (signaling the end of the line/clause) will be removed.
			literals = clause.split()[:-1]
			count_correct = 0
			for l in literals:
				lit = int(l)
				sign = hidden_complete_dict[abs(lit)]
				# Check if the literal in the clause and the literal in the hidden solution has the same signum.
				# If yes, then the literal is "correct" w.r.t. the hidden solution.
				if lit * sign > 0:
					count_correct += 1

			clause_count[count_correct] += 1

		return [clause_count[1], clause_count[2], clause_count[3]]

	def test_probabilities(self):
		"""This is a probabilistic check (using the chi-squared test) of the clause sampling due to the p-list.
		We only check the standard case k=3.
		"""

		print(sys._getframe(  ).f_code.co_name)

		p = [0.25, 0.125, 0.3]
		n = 1000
		m = 5000
		s = randint(1,10000) # We use a random seed. Repeating this test leads to more confidence in the result.
		o = tmpfolder
		F = './hidden2.solution'
		path =  os.path.join(o, self.calculate_file_name(n, m, s=s))
		g.main(n, m, s=s, o=o, p=p, F=F)
		lines = self.get_all_lines(path)

		# We count how many times a 1-correct, 2-correct, and 3-correct clause appears.
		# We compare these values to the expected values.
		# The clauses begin at lines[5].
		observed_frequency_list = self.count_p_clauses(lines[5:], self.hidden2, 1000)
		W = self.calculate_rejected_prob(p)
		P1, P2, P3 = self.calculate_add_probabilities(p, W)
		# Letting Q_i be the number of i-clauses (for this notion see `calculate_add_probabilities`),
		# it holds E[Q_i] = m * P_i, where P_i is calculated via `calculate_add_probabilities(p, W)`.
		exp_frequency_list = [m*P1, m*P2, m*P3]

		# We use Pearson's chi-squared test to determine whether there is a statistically significant difference between
		# the expected frequencies and the observed frequencies in the categories 1-correct, 2-correct, and 3-correct.
		_, p = chisquare(observed_frequency_list, exp_frequency_list)
		if p < 0.05:
			print("WARNING, WARNING. Self-destruction initialized.")
			print("p-value for the generated instance is", p)
		print("Everything's fine... probably. p-value for the generated instance is", p)

	def check_for_duplicate_clauses(self, lines):
		"""This method checks whether there are any duplicate clauses in `lines`.
		"""
		in_formula_set = set()
		for i in range(5, len(lines)):
			# We convert each clause in a list of strings.
			# The last entry "0" (signaling the end of the line/clause) will be removed.
			literals = lines[i].split()[:-1]
			lit_set = frozenset( [ int(s) for s in literals ] )
			# Ensures that the current clause has not been seen.
			self.assertNotIn(lit_set, in_formula_set)
			in_formula_set.add(lit_set)

	def test_unique_clauses(self):
		"""We test whether the generated formula contains only unique clauses.
		"""

		print(sys._getframe(  ).f_code.co_name)

		# We assume that there are no duplicate clauses. This tests checks that assumption.
		n = 20
		m = 1000
		s = 111
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m, s=s))
		g.main(n, m, s=s, o=o)
		lines = self.get_all_lines(path)
		self.check_for_duplicate_clauses(lines)

		# Let's do a second test.
		n = 100
		m = 10000
		s = 11
		k = 2
		o = tmpfolder
		path =  os.path.join(o, self.calculate_file_name(n, m, k=k, s=s))
		g.main(n, m, s=s, k=k, o=o)
		lines = self.get_all_lines(path)
		self.check_for_duplicate_clauses(lines)

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


#!/usr/bin/python3

import logging

import fetcher.single
import detector.common
import detector.rising_edge

class MovingThresholdRisingEdgeDetector(detector.rising_edge.RisingEdgeDetector):
    def __init__(self, bearish_threshold: float, bullish_threshold: float,
                 gatherer: fetcher.single.TradingViewFetcherSingle):
        super().__init__(bearish_threshold, bullish_threshold)
        self.__original_bearish_threshold = bearish_threshold
        self.__original_bullish_threshold = bullish_threshold
        self.gatherer = gatherer
        self.first_signal_returned: bool = False

    @staticmethod
    def __saturate(value: float, threshold: float) -> float:
        if value > threshold:
            return threshold
        if value < (threshold * -1):
            return threshold * -1
        return value

    def check(self, indicator: float) -> detector.common.TradingAction:
        self.gatherer.fetch_technical_indicator()
        long_term_indicator = self.gatherer.get_technical_indicator()

        self.set_threshold(
            self.__saturate(self.__original_bearish_threshold -
                            long_term_indicator, 1.0),
            self.__saturate(self.__original_bullish_threshold - long_term_indicator, 1.0))

        logging.info(
            "New moving threshold: Long term = %f, Bearish = %f, Bullish = %f",
            long_term_indicator, self._bearish_threshold,
            self._bullish_threshold)

        result = super().check(indicator)
        logging.debug("New state of the moving threshold detector: %s",
                      str(result))

        if self.first_signal_returned:
            self._last_result = result
            return result

        self.first_signal_returned = True
        if indicator <= self._bearish_threshold:
            logging.debug("Initial state was overwritten by bearish")
            self._last_result = detector.common.TradingAction.BEARISH_SIGNAL
            return detector.common.TradingAction.BEARISH_SIGNAL

        if indicator >= self._bullish_threshold:
            logging.debug("Initial state was overwritten by bullish")
            self._last_result = detector.common.TradingAction.BULLISH_SIGNAL
            return detector.common.TradingAction.BULLISH_SIGNAL

        logging.debug("Initial state was overwritten by hold")
        self._last_result = detector.common.TradingAction.HOLD_SIGNAL
        return detector.common.TradingAction.HOLD_SIGNAL


arr1 = eval(input())
arr2 = eval(input())
#print(arr1)
#print(arr2)
temp = 0
result = []
for i in range(len(arr1)):
    for j in range(len(arr2)):
        temp = abs(arr1[i]-arr1[j])+abs(arr2[i]-arr2[j])+abs(i-j)
        result.append(temp)

print(max(result))


import vtk
from vtk.util.numpy_support import numpy_to_vtk, numpy_to_vtkIdTypeArray, vtk_to_numpy
import numpy as np

def numpy_to_vtk_cells(mat):
    cells = vtk.vtkCellArray()

    # Seemingly, VTK may be compiled as 32 bit or 64 bit.
    # We need to make sure that we convert the trilist to the correct dtype
    # based on this. See numpy_to_vtkIdTypeArray() for details.
    isize = vtk.vtkIdTypeArray().GetDataTypeSize()
    req_dtype = np.int32 if isize == 4 else np.int64
    n_elems = mat.shape[0]
    n_dim = mat.shape[1]
    cells.SetCells(n_elems,
                   numpy_to_vtkIdTypeArray(
                       np.hstack((np.ones(n_elems)[:, None] * n_dim,
                                  mat)).astype(req_dtype).ravel(),
                       deep=1))
    return cells

def numpy_rep_to_vtk(vertices, shapes, edges=None):
    """ converts a numpy representation of vertices and vertex connection graph
      to a polydata object and corresponding cell array

        :param vertices: a Nx3 numpy array of vertex locations
        :param shapes: a MxK numpy array of vertex connectivity
                       (could be triangles (K=3) or edges (K=2))

        :return: (vtkPolyData, vtkCellArray)
        a polydata object with point set according to vertices,
        and a vtkCellArray of the shapes
    """

    mesh = vtk.vtkPolyData()
    points = vtk.vtkPoints()
    points.SetData(numpy_to_vtk(vertices, deep=1))
    mesh.SetPoints(points)

    cells = numpy_to_vtk_cells(shapes)
    if edges is not None:
        edges = numpy_to_vtk_cells(edges)

    return mesh, cells, edges

def graph_to_vtk(vertices, edges):
    """ converts a numpy representation of vertices and edges
      to a vtkPolyData object

        :param vertices: a Nx3 numpy array of vertex locations
        :param edges: a Mx2 numpy array of vertex connectivity
        where the values are the indexes of connected vertices

        :return: vtkPolyData
        a polydata object with point set according to vertices
        and edges as its Lines

        :raises: ValueError
        if edges is not 2d or refers to out of bounds vertices
    """
    if edges.shape[1] != 2:
        raise ValueError('graph_to_vtk() only works on edge lists')
    if np.max(edges) >= len(vertices):
        msg = 'edges refer to non existent vertices {}.'
        raise ValueError(msg.format(np.max(edges)))
    mesh, cells, edges = numpy_rep_to_vtk(vertices, edges)
    mesh.SetLines(cells)
    return mesh

def trimesh_to_vtk(vertices, tris, graph_edges=None):
    """Return a `vtkPolyData` representation of a :map:`TriMesh` instance
    Parameters
    ----------
    vertices : numpy array of Nx3 vertex positions (x,y,z)
    tris: numpy array of Mx3 triangle vertex indices (int64)
    Returns
    -------
    `vtk_mesh` : `vtkPolyData`
        A VTK mesh representation of the Menpo :map:`TriMesh` data
    Raises
    ------
    ValueError:
        If the input trimesh is not 3D
        or tris refers to out of bounds vertex indices
    """

    if tris.shape[1] != 3:
        raise ValueError('trimesh_to_vtk() only works on 3D TriMesh instances')
    if np.max(tris) >= len(vertices):
        msg = 'edges refer to non existent vertices {}.'
        raise ValueError(msg.format(np.max(tris)))
    mesh, cells, edges = numpy_rep_to_vtk(vertices, tris, graph_edges)
    mesh.SetPolys(cells)
    if edges is not None:
        mesh.SetLines(edges)

    return mesh

def vtk_cellarray_to_shape(vtk_cellarray, ncells):
    cellarray = vtk_to_numpy(vtk_cellarray)
    cellarray = cellarray.reshape(ncells, int(len(cellarray)/ncells))
    return cellarray[:, 1:]

def decimate_trimesh(trimesh, reduction=.1):
    poly = trimesh_to_vtk(trimesh.vertices, trimesh.faces)
    dec = vtk.vtkDecimatePro()
    dec.SetTargetReduction(reduction)
    dec.PreserveTopologyOn()
    dec.SetInputData(poly)
    dec.Update()
    out_poly = dec.GetOutput()

    points = vtk_to_numpy(out_poly.GetPoints().GetData())
    ntris = out_poly.GetNumberOfPolys()
    tris = vtk_cellarray_to_shape(out_poly.GetPolys().GetData(), ntris)
    return points, tris

def remove_unused_verts(verts, faces):
    """removes unused vertices from a graph or mesh

    verts = NxD numpy array of vertex locations
    faces = MxK numpy array of connected shapes (i.e. edges or tris)
    (entries are indices into verts)

    returns:
    new_verts, new_face
    a filtered set of vertices and reindexed set of faces
    """
    used_verts = np.unique(faces.ravel())
    new_verts = verts[used_verts, :]
    new_face = np.zeros(faces.shape, dtype=faces.dtype)
    for i in range(faces.shape[1]):
        new_face[:, i] = np.searchsorted(used_verts, faces[:, i])
    return new_verts, new_face

def vtk_poly_to_mesh_components(poly):
    points = vtk_to_numpy(poly.GetPoints().GetData())
    ntris = poly.GetNumberOfPolys()
    tris = vtk_cellarray_to_shape(poly.GetPolys().GetData(), ntris)
    nedges = poly.GetNumberOfLines()
    if nedges > 0:
        edges = vtk_cellarray_to_shape(poly.GetLines().GetData(), nedges)
    else:
        edges = None
    return points, tris, edges

def filter_largest_cc(trimesh):
    poly = trimesh_to_vtk(trimesh.vertices, trimesh.faces, trimesh.graph_edges)
    connf = vtk.vtkConnectivityFilter()
    connf.SetInputData(poly)
    connf.SetExtractionModeToLargestRegion()
    connf.Update()
    clean = vtk.vtkCleanPolyData()
    clean.SetInputConnection(connf.GetOutputPort())
    clean.PointMergingOff()
    clean.Update()
    return vtk_poly_to_mesh_components(clean.GetOutput())

def calculate_cross_sections(mesh, graph_verts, graph_edges, calc_centers=True):

    mesh_polydata = trimesh_to_vtk(mesh.vertices, mesh.faces)

    cutter = vtk.vtkPlaneCutter()
    cutter.SetInputData(mesh_polydata)
    plane = vtk.vtkPlane()
    cd = vtk.vtkCleanPolyData()
    cf = vtk.vtkPolyDataConnectivityFilter()
    cf.SetInputConnection(cd.GetOutputPort())
    cf.SetExtractionModeToClosestPointRegion()
    cutter.SetPlane(plane)
    cutStrips = vtk.vtkStripper()
    cutStrips.JoinContiguousSegmentsOn()
    cutStrips.SetInputConnection(cf.GetOutputPort())

    cross_sections = np.zeros(len(graph_edges), dtype=np.float)

    if calc_centers:
        centers = np.zeros((len(graph_edges), 3))

    massfilter = vtk.vtkMassProperties()
    massfilter.SetInputConnection(cutter.GetOutputPort())
    t = vtk.vtkTriangleFilter()
    dvs = graph_verts[graph_edges[:, 0], :]-graph_verts[graph_edges[:, 1], :]
    dvs = (dvs / np.linalg.norm(dvs, axis=1)[:, np.newaxis])
    for k, edge in enumerate(graph_edges):
        dv = dvs[k, :]

        dv = dv.tolist()

        v = graph_verts[graph_edges[k, 0], :]
        v = v.tolist()
        plane.SetNormal(*dv)
        plane.SetOrigin(*v)

        cutter.Update()
        pd = cutter.GetOutputDataObject(0).GetBlock(0).GetPiece(0)

        cd.SetInputData(pd)
        cf.SetClosestPoint(*v)
        cutStrips.Update()

        cutPoly = vtk.vtkPolyData()
        cutPoly.SetPoints(cutStrips.GetOutput().GetPoints())
        cutPoly.SetPolys(cutStrips.GetOutput().GetLines())

        t.SetInputData(cutPoly)
        if calc_centers:
            pts = vtk_to_numpy(cf.GetOutput().GetPoints().GetData())
            # centerOfMassFilter = vtk.vtkCenterOfMass()
            # centerOfMassFilter.SetInputConnection(t.GetOutputPort())
            # centerOfMassFilter.Update()
            centers[k, :] = np.mean(pts, axis=0)

        massfilter = vtk.vtkMassProperties()
        massfilter.SetInputConnection(t.GetOutputPort())
        massfilter.Update()

        cross_sections[k] = massfilter.GetSurfaceArea()

    return cross_sections, centers

def make_vtk_skeleton_from_paths(verts, paths):
    cell_list = []
    num_cells = 0
    for p in paths:
        cell_list.append(len(p))
        cell_list += p
        num_cells += 1
    cell_array = np.array(cell_list)

    mesh = vtk.vtkPolyData()
    points = vtk.vtkPoints()
    points.SetData(numpy_to_vtk(verts, deep=1))
    mesh.SetPoints(points)

    cells = vtk.vtkCellArray()

    # Seemingly, VTK may be compiled as 32 bit or 64 bit.
    # We need to make sure that we convert the trilist to the correct dtype
    # based on this. See numpy_to_vtkIdTypeArray() for details.
    isize = vtk.vtkIdTypeArray().GetDataTypeSize()
    req_dtype = np.int32 if isize == 4 else np.int64

    cells.SetCells(num_cells,
                   numpy_to_vtkIdTypeArray(cell_array, deep=1))
    mesh.SetLines(cells)
    return mesh

def vtk_super_basic(actors, camera=None, do_save=False, filepath=None, back_color=(.1, .1, .1),
                    VIDEO_WIDTH=1080, VIDEO_HEIGHT=720, save_scale=5):
    """
    Create a window, renderer, interactor, add the actors and start the thing

    Parameters
    ----------
    actors :  list of vtkActors

    Returns
    -------
    nothing
    """
    if do_save:
        assert(filepath is not None)
    # create a rendering window and renderer
    ren = vtk.vtkRenderer()
    renWin = vtk.vtkRenderWindow()
    renWin.AddRenderer(ren)
    renWin.SetSize(VIDEO_WIDTH, VIDEO_HEIGHT)
    # renderWindow.SetAlphaBitPlanes(1)

    ren.SetBackground(*back_color)
    # create a renderwindowinteractor
    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(renWin)

    for a in actors:
        # assign actor to the renderer
        ren.AddActor(a)

    # render
    if camera is None:
        ren.ResetCamera()
    else:
        ren.SetActiveCamera(camera)
        ren.ResetCameraClippingRange()
        camera.ViewingRaysModified()
    renWin.Render()
    if do_save:
         # render
        imageFilter = vtk.vtkWindowToImageFilter()
        imageFilter.SetInput(renWin)
        imageFilter.SetInputBufferTypeToRGBA()
        imageFilter.SetScale(save_scale)
        imageFilter.ReadFrontBufferOff()
        imageFilter.Update()

        #Setup movie writer
        moviewriter = vtk.vtkPNGWriter()
        moviewriter.SetInputConnection(imageFilter.GetOutputPort())

        moviewriter.SetFileName(filepath)
        #Export a single frame
        imageFilter.Modified()
        moviewriter.Write()
    else:
        trackCamera = vtk.vtkInteractorStyleTrackballCamera()
        iren.SetInteractorStyle(trackCamera)
        # enable user interface interactor
        iren.Initialize()
        iren.Start()
    renWin.Finalize()

    return ren

def vtk_camera_from_quat(pos_nm, orient_quat, camera_distance=10000, ngl_correct=True):
    """define a vtk camera with a particular orientation
        Parameters
        ----------
        pos_nm: np.array, list, tuple
            an iterator of length 3 containing the focus point of the camera
        orient_quat: np.array, list, tuple
            a len(4) quatenerion (x,y,z,w) describing the rotation of the camera
            such as returned by neuroglancer x,y,z,w all in [0,1] range
        camera_distance: float
            the desired distance from pos_nm to the camera (default = 10000 nm)

        Returns
        -------
        vtk.vtkCamera
            a vtk camera setup according to these rules
    """
    camera = vtk.vtkCamera()
    # define the quaternion in vtk, note the swapped order
    # w,x,y,z instead of x,y,z,w
    quat_vtk=vtk.vtkQuaterniond(orient_quat[3],
                                orient_quat[0],
                                orient_quat[1],
                                orient_quat[2])
    # use this to define a rotation matrix in x,y,z
    # right handed units
    M = np.zeros((3, 3), dtype=np.float32)
    quat_vtk.ToMatrix3x3(M)
    # the default camera orientation is y up
    up = [0, 1, 0]
    # calculate default camera position is backed off in positive z
    pos = [0, 0, camera_distance]

    # set the camera rototation by applying the rotation matrix
    camera.SetViewUp(*np.dot(M,up))
    # set the camera position by applying the rotation matrix
    camera.SetPosition(*np.dot(M,pos))
    if ngl_correct:
        # neuroglancer has positive y going down
        # so apply these azimuth and roll corrections
        # to fix orientatins
        camera.Azimuth(-180)
        camera.Roll(180)

    # shift the camera posiiton and focal position
    # to be centered on the desired location
    p=camera.GetPosition()
    p_new = np.array(p)+pos_nm
    camera.SetPosition(*p_new)
    camera.SetFocalPoint(*pos_nm)
    return camera

def vtk_camera_from_ngl_state(state_d, zoom_factor=300.0):
    """define a vtk camera from a neuroglancer state dictionary
        Parameters
        ----------
        state_d: dict
            an neuroglancer state dictionary
        zoom_factor: float
            how much to multiply zoom by to get camera backoff distance
            default = 300 > ngl_zoom = 1 > 300 nm backoff distance

        Returns
        -------
        vtk.vtkCamera
            a vtk camera setup that mathces this state
    """

    orient = state_d.get('perspectiveOrientation', [0.0,0.0,0.0,1.0])
    zoom = state_d.get('perspectiveZoom', 10.0)
    position = state_d['navigation']['pose']['position']
    pos_nm = np.array(position['voxelCoordinates'])*position['voxelSize']
    camera = vtk_camera_from_quat(pos_nm, orient, zoom*zoom_factor, ngl_correct=True)

    return camera

def make_mesh_actor(mesh, color=(0, 1, 0),
                    opacity=0.1,
                    vertex_scalars=None,
                    lut=None,
                    calc_normals=True):

    mesh_poly = trimesh_to_vtk(mesh.vertices, mesh.faces, mesh.graph_edges)
    if vertex_scalars is not None:
        mesh_poly.GetPointData().SetScalars(numpy_to_vtk(vertex_scalars))
    mesh_mapper = vtk.vtkPolyDataMapper()
    if calc_normals and mesh.graph_edges is None:
        norms = vtk.vtkTriangleMeshPointNormals()
        norms.SetInputData(mesh_poly)
        mesh_mapper.SetInputConnection(norms.GetOutputPort())
    else:
        mesh_mapper.SetInputData(mesh_poly)
    mesh_actor = vtk.vtkActor()

    if lut is not None:
        mesh_mapper.SetLookupTable(lut)
    mesh_mapper.ScalarVisibilityOn()
    mesh_actor.SetMapper(mesh_mapper)
    mesh_actor.GetProperty().SetColor(*color)
    mesh_actor.GetProperty().SetOpacity(opacity)
    return mesh_actor

def vtk_skeleton_actor(sk,
                       edge_property=None,
                       vertex_property=None,
                       normalize_property=True,
                       color=(0, 0, 0),
                       line_width=3,
                       opacity=0.7,
                       lut_map=None):
    sk_mesh = graph_to_vtk(sk.vertices, sk.edges)
    mapper = vtk.vtkPolyDataMapper()
    mapper.SetInputData(sk_mesh)
    if edge_property is not None:
        data = sk.edge_properties[edge_property]
        if normalize_property:
            data = data / np.nanmax(data)
        sk_mesh.GetCellData().SetScalars(numpy_to_vtk(data))
        lut = vtk.vtkLookupTable()
        if lut_map is not None:
            lut_map(lut)
        lut.Build()
        mapper.SetLookupTable(lut)
    if vertex_property is not None:
        data = sk.vertex_properties[vertex_property]
        if normalize_property:
            data = data / np.nanmax(data)
        sk_mesh.GetPointData().SetScalars(numpy_to_vtk(data))
        lut = vtk.vtkLookupTable()
        if lut_map is not None:
            lut_map(lut)
        lut.Build()
        mapper.ScalarVisibilityOn()
        mapper.SetLookupTable(lut)

    actor = vtk.vtkActor()
    actor.SetMapper(mapper)
    actor.GetProperty().SetLineWidth(line_width)
    actor.GetProperty().SetOpacity(opacity)
    actor.GetProperty().SetColor(color)
    return actor

def make_point_cloud_actor(xyz,
                           size=100,
                           color=(0, 0, 0),
                           opacity=0.5):

    points = vtk.vtkPoints()
    points.SetData(numpy_to_vtk(xyz, deep=True))

    pc = vtk.vtkPolyData()
    pc.SetPoints(points)

    if np.isscalar(size):
        size = np.full(len(xyz), size)
    elif len(size) != len(xyz):
        raise ValueError('Size must be either a scalar or an len(xyz) x 1 array')
    pc.GetPointData().SetScalars(numpy_to_vtk(size))

    ss = vtk.vtkSphereSource()
    ss.SetRadius(1)

    glyph = vtk.vtkGlyph3D()
    glyph.SetInputData(pc)

    glyph.SetSourceConnection(ss.GetOutputPort())
    glyph.SetScaleModeToScaleByScalar()
    glyph.ScalingOn()
    glyph.Update()

    mapper = vtk.vtkPolyDataMapper()
    mapper.SetInputConnection(glyph.GetOutputPort())

    actor = vtk.vtkActor()
    mapper.ScalarVisibilityOff()
    actor.SetMapper(mapper)
    actor.GetProperty().SetColor(*color)
    actor.GetProperty().SetOpacity(opacity)

    return actor

def vtk_linked_point_actor(vertices_a, inds_a,
                           vertices_b, inds_b,
                           line_width=1, color=(0, 0, 0), opacity=0.2):
    if len(inds_a) != len(inds_b):
        raise ValueError('Linked points must have the same length')

    link_verts = np.vstack((vertices_a[inds_a], vertices_b[inds_b]))
    link_edges = np.vstack((np.arange(len(inds_a)),
                            len(inds_a)+np.arange(len(inds_b))))
    link_poly = graph_to_vtk(link_verts, link_edges.T)

    mapper = vtk.vtkPolyDataMapper()
    mapper.SetInputData(link_poly)

    link_actor = vtk.vtkActor()
    link_actor.SetMapper(mapper)
    link_actor.GetProperty().SetLineWidth(line_width)
    link_actor.GetProperty().SetColor(color)
    link_actor.GetProperty().SetOpacity(opacity)
    return link_actor


arr = list(eval(input()))
target = int(input())
list1 = []
list2 = []
for value in range(target // len(arr), max(arr) + 1):
    list1.append(value)
    temp = 0
    for i in arr:
        if i > value:
            temp += value
        else:
            temp += i
    list2.append(abs(target - temp))
print(list1[list2.index(min(list2))])


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

class Metrics():
    # https://en.wikipedia.org/wiki/Confusion_matrix
    def __init__(self, tp, fp, fn, tn):
        self.TP = tp
        self.FP = fp
        self.FN = fn
        self.TN = tn
        self.P = self.TP + self.FN  # 真真+假真
        self.N = self.FP + self.TN  # 真假+假假
        self.T = self.TP + self.FP
        self.F = self.FN + self.TN

        self.recall = self.TPR = 1.0 * self.TP / self.P  # 敏感度 实际为【真】的里面预测【正确】【比例】
        self.FNR = 1.0 * self.FN / self.P  # 丢失率

        self.FPR = 1.0 * self.FP / self.N  # 假阳率 实际为【假】的里面预测【错误】【比例】
        self.TNR = self.SPC = 1.0 * self.TN / self.N  # 特异性，真阴率

        self.precision = self.PPV = 1.0 * self.TP / self.T  # 精确率
        self.FDR = 1.0 * self.FP / self.T  # 伪发现率
        self.FOR = 1.0 * self.FN / self.F
        self.NPV = 1.0 * self.TN / self.F  # 阴性预测值

        self.LR_PLUS = self.TPR / self.FPR
        self.LR_SUB = self.FNR / self.TNR
        self.DOR = self.LR_PLUS / self.LR_SUB
        self.total = self.P + self.N
        self.accuracy = 1.0 * (self.TP + self.TN) / self.total  # 准确率
        self.F1 = 2 * (self.precision * self.recall) / (self.precision + self.recall)
        # self.recall_tp = 1.0 * self.tp / self.p  # 实际为【真】的里面预测【正确】【比例】
        # self.recall_tn = 1.0 * self.tn / self.p  # 实际为【假】的里面预测【正确】【比例】
        # self.precision_tp = 1.0 * self.tp / self.t
        # self.precision_f = 1.0 * self.fn / self.f

        # self.f_measure = 2 / (1 / self.precision + 1 / self.recall)

def describe(self):
    data = [
        [self.TP, self.FP, self.T, self.PPV, self.FDR],
        [self.FN, self.TN, self.F, self.FOR, self.NPV],
        [self.P, self.N, self.total, self.accuracy, 1 - self.accuracy],
        [self.TPR, self.FPR, np.nan, self.LR_PLUS, self.DOR],
        [self.FNR, self.SPC, np.nan, self.LR_SUB, self.F1],
    ]
    describe_data = pd.DataFrame(data, index=['true', 'false', 'total', 'recall', 'rate'],
                                 columns=['pos', 'neg', 'total', 'precision', 'accuracy'])
    return describe_data

def normalize_inputs(x):
    m = len(x)
    lambd = 1. / m * np.sum(x, axis=0)
    x = x - lambd
    sigma_square = 1. / m * np.sum(x ** 2, axis=0)
    x = x / sigma_square
    return x

def xavier_init(n_l_1, n_l):
    return np.random.randn(n_l_1, n_l) * np.sqrt(1 / n_l_1)

def msra_init(n_l_1, n_l):
    return np.random.randn(n_l_1, n_l) * np.sqrt(2 / n_l_1)

#################激活函数###################
# exp 以自然常数e为底的指数函数
def exp(x):
    return np.exp(x)

# 激活函数 sigmoid
def sigmoid(x):
    return 1 / (1 + exp(-x))

# sigmoid函数梯度
def sigmoid_derivative(sigmoid_x):
    return sigmoid_x * (1 - sigmoid_x)

# 激活函数 tanh
def tanh(x):
    # return 2 * sigmoid(x) - 1
    return (exp(x) - exp(-x)) / (exp(x) + exp(-x))

# tanh 函数梯度
def tanh_derivative(tanh_x):
    return 1 - np.square(tanh_x)

# 激活函数 relu
# relu(x) = max(0,x) = x(x>0)|0(x<=0)
def relu(x):
    return np.where(x < 0, 0, x)

def relu_derivative(x):
    return np.where(x < 0, 0, 1)

def leaky_relu(x):
    return np.where(x < 0, 0.1 * x, x)

def leaky_relu_derivative(x):
    return np.where(x < 0, 0.1, 1)

def elu(x, a):
    return np.where(x <= 0, a * exp(x) - 1, x)

def elu_derivative(x, a):
    return np.where(x <= 0, a * exp(x), 1)

def softmax(x):
    x_e = exp(x - np.max(x))
    x_e_s = np.sum(x_e, axis=1, keepdims=True)
    return x_e / x_e_s

# y是一维分类，yhat是分类onehot
def softmax_loss(y, yhat):
    n = len(y)
    corect_logprobs = -np.log(yhat[range(n), y])
    return np.sum(corect_logprobs)

# y是onehot，yhat是onehot
def softmax_onehot_loss(y, yhat):
    minval = 0.000000000001
    corect_logprobs = y * -np.log(yhat.clip(min=minval))
    return np.sum(corect_logprobs)

def onehot(y):
    b = np.zeros((len(y), np.unique(y).size))
    b[np.arange(len(y)), y] = 1
    return b

def draw_function(ax, x, label, y, d_label, dx):
    ax.plot(x, y, label=label)
    ax.plot(x, dx, label=d_label)
    ax.legend()

def draw_sigmoid(ax, x):
    draw_function(ax, x, 'sigmoid', sigmoid(x), 'sigmoid_derivative', sigmoid_derivative(sigmoid(x)))

def draw_tanh(ax, x):
    draw_function(ax, x, 'tanh', tanh(x), 'tanh_derivative', tanh_derivative(tanh(x)))

def draw_relu(ax, x):
    draw_function(ax, x, 'relu', relu(x), 'relu_derivative', relu_derivative(x))

def draw_leaky_relu(ax, x):
    draw_function(ax, x, 'leaky_relu', leaky_relu(x), 'leaky_relu_derivative', leaky_relu_derivative(x))

def draw_all_activation_function(x):
    figure = plt.figure()
    ax = figure.add_subplot(3, 2, 1)
    draw_sigmoid(ax, x)
    ax = figure.add_subplot(3, 2, 2)
    draw_tanh(ax, x)
    ax = figure.add_subplot(3, 2, 3)
    draw_relu(ax, x)
    ax = figure.add_subplot(3, 2, 4)
    draw_leaky_relu(ax, x)
    plt.show()

#################正则###################
def l1(theta):
    return np.sum(np.abs(theta))

def l2(theta):
    return np.sum(theta ** 2)

def lf(theta):
    return np.sum(theta ** 2)

def dropout(a, keep_prob=0.8):
    d = np.random.rand(a.shape[0], a.shape[1]) < keep_prob
    a = np.multiply(a, d)
    a /= keep_prob

#################优化###################

# 速度快，会走弯路，也容易走过头
def momentum(theta, m, g, alpha, beta=0.9):
    m = beta * m + (1 - beta) * g
    theta += -alpha * m
    return theta, m

def rms(a, epsilon=1e-08):
    return np.sqrt(a) + epsilon

# 不会出现学习率越来越低的问题，自己调节学习率
def rmsprop(theta, n, g, alpha=0.001, beta=0.999, epsilon=1e-08):
    n = beta * n + (1 - beta) * (g ** 2)
    theta += -alpha * g / rms(n, epsilon)
    return theta, n

# 样本出现越多，学习率越小，样本出现越少，
# 优势学习率越大，自动调节学习率，缺点 迭代次数增多，学习率越来越低，最终趋近于0
def adagrad(theta, n, g, alpha=0.01, epsilon=1e-06):
    n += np.square(g)
    theta += -alpha * g / rms(n, epsilon)
    return theta, n

# 不需要学习率，最快到达终点！！！！！！！！！！！！
def adadelta(theta, e_g2, e_dx2, g, alpha=1.0, rho=0.95, epsilon=1e-06):
    e_g2 = rho * e_g2 + (1 - rho) * (g ** 2)
    dx = - rms(e_dx2, epsilon) / rms(e_g2, epsilon) * g
    # theta += dx
    theta += alpha * dx  # keras 增加了alpha，原论文没有乘alpha，所以alpha默认值是1.0
    e_dx2 = rho * e_dx2 + (1 - rho) * (dx ** 2)
    return theta, e_g2, e_dx2

#
def adam(theta, m, v, g, t, alpha=0.001, beta1=0.9, beta2=0.999, epsilon=1e-08):
    t += 1
    m = beta1 * m + (1 - beta1) * g  # momentum 衰减的梯度
    v = beta2 * v + (1 - beta2) * (g ** 2)  # rmsprop 衰减的平方梯度
    m_hat = m / (1 - beta1 ** t)
    v_hat = v / (1 - beta2 ** t)
    theta += -alpha * m_hat / rms(v_hat, epsilon)
    return theta, m, v, t

def adamax(theta, m, u, g, t, alpha=0.002, beta1=0.9, beta2=0.999):
    t += 1
    m = beta1 * m + (1 - beta1) * g  # momentum
    u = np.maximum(beta2 * u, np.abs(g))  # 比较2个参数大小用maximum，而不是max
    theta += -alpha / (1 - beta1 ** t) * m / u
    # theta += -alpha  * m / (u + epsilon) # keras去掉了1 - beta1 ** t，增加了epsilon
    return theta, m, u, t

# 还没有测试
def nadam(theta, m, m_schedule, n, g, t, alpha=0.002, beta1=0.9, beta2=0.999, epsilon=1e-08, schedule_decay=0.004):
    t = t + 1
    momentum_cache_t = beta1 * (1. - 0.5 * (0.96 ** (t * schedule_decay)))
    momentum_cache_t_1 = beta1 * (1. - 0.5 * (0.96 ** ((t + 1) * schedule_decay)))
    m_schedule_new = m_schedule * momentum_cache_t
    m_schedule_next = m_schedule * momentum_cache_t * momentum_cache_t_1
    m_schedule = m_schedule_new

    g_hat = g / (1 - m_schedule_new)
    m = beta1 * m + (1 - beta1) * g
    m_hat = m / (1 - m_schedule_next)
    n = beta2 * n + (1 - beta2) * (g ** 2)
    n_hat = n / (1 - beta2 ** t)
    m_bar = (1 - momentum_cache_t) * g_hat + (momentum_cache_t_1 * m_hat)
    theta += - alpha * m_bar / rms(n_hat, epsilon)
    return theta, m, m_schedule, n, t

def get_tprs_fprs(y, y_predict):
    tprs = []
    fprs = []
    sorted_predict = y_predict
    # sorted_predict = y_predict[np.argsort(-y_predict)]
    # print sorted_predict
    num = len(sorted_predict) if len(sorted_predict) < 100 else 100
    for thresholds in np.linspace(sorted_predict.max(), sorted_predict.min(), num):
        y_class = (y_predict >= thresholds).astype(int)
        tp = (y & y_class).sum()
        tn = ((1 - y) & (1 - y_class)).sum()
        fn = (y & (1 - y_class)).sum()
        fp = ((1 - y) & y_class).sum()
        tpr = 1.0 * tp / (tp + fn)
        fpr = 1.0 * fp / (fp + tn)
        tprs.append(tpr)
        fprs.append(fpr)
        print
        thresholds, tp, fp, fn, tn, fpr, tpr
    return tprs, fprs

# y_predict = np.array([0.1, 0.4, 0.35, 0.8])
# y = np.array([0, 0, 1, 1])
# https://blog.csdn.net/chekongfu/article/details/86235791
def draw_roc(y, y_predict):
    tprs, fprs = get_tprs_fprs(y, y_predict)
    area = np.trapz(fprs, tprs)
    label = 'ROC (area = %0.2f)' % (area)
    print
    label
    plt.plot(fprs, tprs, label=label)
    plt.legend()
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('ROC Curves')
    plt.show()

def draw_ks(y, y_predict):
    tprs, fprs = get_tprs_fprs(y, y_predict)
    # area = np.trapz(fprs, tprs)
    # label = 'ROC (area = %0.2f)' % (area)
    # print label
    plt.plot(range(len(tprs)), tprs, label='tprs')
    plt.plot(range(len(fprs)), fprs, label='fprs')
    plt.legend()
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('ROC Curves')
    plt.show()

import matplotlib.pyplot as plt

if __name__ == '__main__':
    # x = np.array([[1, 3], [2, 6], [4, 4]])
    # y = np.sum(x ** 2, axis=1)
    # # print y
    # # print lf(x)
    #
    # x_nor = normalize_inputs(x)
    # plt.scatter(x_nor[:, 0], x_nor[:, 1])
    # # plt.scatter(x[:, 0], x[:, 1])
    # # plt.xlim(-10,10)
    # # plt.ylim(-10,10)
    # plt.show()

    # metrics = Metrics(99713, 45007, 11564, 66233)
    # print metrics.describe()
    print('main')
    y_predict = np.array([0.1, 0.4, 0.35, 0.8])
    y = np.array([0, 0, 1, 1])
    draw_ks(y, y_predict)


from django import template

register = template.Library()

def do_current_time(parser, token):
    try:
        # split_contents() knows not to split quoted strings.
        tag_name, format_string = token.split_contents()
    except ValueError:
        msg = '%r tag requires a single argument' % tag_name
        raise template.TemplateSyntaxError(msg)
    return CurrentTimeNode(format_string[1:-1])

import datetime

class CurrentTimeNode(template.Node):
    def __init__(self, format_string):
        self.format_string = str(format_string)
#rewrite render
    def render(self, context):
        now = datetime.datetime.now()
        return now.strftime(self.format_string)
#reg
register.tag('current_time', do_current_time)


import pandas as pd

from src.config.config import BLANK_DESCRIPTION
from src.types.experiment_description import ExperimentDescriptionType
from src.types.experiment_summarization_fields import ExperimentSummarizationFields
from src.types.results import ResultType

def merge_content(
    confusion_matrix, metrics, description, summarization, directory, logs
):
    """Merge all files to one record.

    Args:
        confusion_matrix (_type_): confusion matrix.csv
        metrics (_type_): metrics.csv
        description (_type_): ExperimentDescription ... description.csv
        summarization (_type_): ExperimentSummarization ... summarization.csv
        directory (_type_): directory
        logs (_type_): log.csv

    Returns:
        _type_: DataFrame which represents experiment
    """
    concat_df = pd.DataFrame()

    for df in [metrics, description, summarization, logs]:
        if df is not None:
            concat_df = pd.concat([concat_df, df])

    keys = concat_df.loc[:, "id"]
    values = concat_df.loc[:, "value"]

    if metrics is None:
        print(f"No metrics in {directory}")
        append_keys = [
            x.value for x in list(ResultType) if x != ResultType.ConsfusionMatrix
        ]
        keys = keys + append_keys
        values = values + [BLANK_DESCRIPTION] * len(append_keys)

    if description is None:
        print(f"No description in {directory}")
        append_keys = [x.value for x in list(ExperimentDescriptionType)]
        keys = keys + append_keys
        values = values + [BLANK_DESCRIPTION] * len(append_keys)

    if summarization is None:
        print(f"No summarization in {directory}")
        append_keys = [x.value for x in list(ExperimentSummarizationFields)]
        keys = keys + append_keys
        values = values + [BLANK_DESCRIPTION] * len(append_keys)

    dic = {k: v for k, v in zip(keys, values)}

    dic["ConfusionMatrix"] = (
        confusion_matrix.values if confusion_matrix is not None else BLANK_DESCRIPTION
    )
    dic["Directory"] = directory

    record = pd.DataFrame.from_dict(dic, orient="index").T

    return record


'''
Signal Analysis: Audio (Ge Fang "Method 5")
'''

#def run(cursor, sid, ECoG_filepath)#
#output table: channel number - score0#
#need math, scipy, csv, sys, os
import scipy
import scipy.io as aio
import csv
import math
import numpy as np
import sys
import os
from scipy.fftpack import fft,ifft
from scipy.stats.stats import pearsonr

#import matlab.engine
#import pylab
#method = 5
#import wrap1
#import function_wil_be_called
#data = matlab.engine.start_matlab()
#tf = data.load_bcidat('ECOG001S001R01.dat')
#print(type(tf))
import psycopg2
import psycopg2.extras

'''select_subject = """SELECT * from subjects WHERE name=%s"""
cursor.execute(select_subject, (name,))
subject_names = cursor.fetchall()
print(subject_names)
'''

def csv_to_db_function(csv_file_path):
    return_table = [[],[]]
    with open(csv_file_path,newline = '') as f:
        reader = csv.reader(f)
        next(reader)
        for row in reader:
            return_table[0].append(int(row[0]))
            return_table[1].append(float(row[1]))
    f.close()
    return(return_table)

def run(cursor, sid, ECoG_filepath):
    method = 5
    code_path = os.path.split(os.path.realpath(__file__))[0]
    dat_file_path = os.path.split(ECoG_filepath)[0]
    #print(dat_file_path)
    dat_file_name = os.path.split(ECoG_filepath)[1][:-4]
    #print(dat_file_name)
    print("code directory = ",code_path)
    print("dat file path = ",os.path.split(ECoG_filepath))
    csv_folder_name = "\\data for speech_correlation\\"
    csv_folder_path = '{}{}'.format(code_path,csv_folder_name)

    createFolder(csv_folder_path)
    print("data stored in =",csv_folder_path)
    cmd = int(str(input("input '0' for getting ECOG path, input '1'for uploading scores to database:")))
    if (cmd == 0):
        with open('{}{}{}{}'.format(csv_folder_path,"ECoG_path_SID_",sid,".txt"), "w") as f:
            f.write("SID"+"\n")
            f.write(str(sid)+"\n")
            f.write("ECOG FILE PATH"+"\n")
            f.write(ECoG_filepath+"\n")
            f.closed
        print("txt tile generated")
    else:
        if(cmd ==1):
            upload_path = (str(input("input the absolute path of score file you wish to upload,please use '\\\' instead of '\\'")))
            csv_to_db = csv_to_db_function(upload_path)
            for i in range(len(csv_to_db[0])):
                insert_scores = "INSERT INTO scores(sid,channel,method,score0) VALUES(%s, %s, %s, %s);"
                cursor.execute(insert_scores, (sid, csv_to_db[0][i], method, csv_to_db[1][i]))
            print("data has been uploaded to database")
        else:
            print("wrong command")

def createFolder(directory):
    try:
        if not os.path.exists(directory):
            os.makedirs(directory)
            print("folder created:",directory)
        else:
            print("folder existed:",directory)
    except OSError:
        print ('Error: Creating directory. ' +  directory)

#run(cursor, sid, ECoG_filepath)


import base64
import io
import json
import os
import re
import sys
from concurrent.futures import ThreadPoolExecutor

from PySide6.QtCore import QAbstractListModel, Qt, QModelIndex, QObject, Slot, Property, Signal
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine
from loguru import logger
from qrcode import QRCode
from requests import head

from config import Config
from crawler import run_crawler
from resource import qInitResources

class QDataListModel(QAbstractListModel):
    NAME = Qt.UserRole + 1000
    TIME = Qt.UserRole + 1001
    SIZE = Qt.UserRole + 1002
    HOT = Qt.UserRole + 1003
    MAGNET = Qt.UserRole + 1004

    def __init__(self, model):
        QAbstractListModel.__init__(self)
        self.model = model

    def rowCount(self, parent = None) -> int:
        return len(self.model)

    def roleNames(self):
        return {
            self.NAME:   b'name',
            self.TIME:   b'time',
            self.SIZE:   b'size',
            self.HOT:    b'hot',
            self.MAGNET: b'magnet',
        }

    def data(self, index: QModelIndex, role: int = None):
        index = index.row()
        row = self.model[index]

        return row[str(self.roleNames()[role], 'utf-8')]

class MainWindow(QObject):
    def __init__(self, app: QGuiApplication, config: Config):
        QObject.__init__(self)
        self._app = app
        self._config = config
        self._pool = ThreadPoolExecutor()
        self._search_result_model = QDataListModel([])

    @Slot(result = 'QVariant')
    def get_rules(self):
        pro_list = ['bitsearch', 'btsow_proxy', 'zooqle']
        rules = []

        file_list = os.listdir('rule')
        for file in file_list:
            if file == '_temp.json' or file == 'bak':
                continue
            with open('rule/' + file, encoding = 'utf-8') as f:
                rule_obj = json.load(f)
                rules.append({
                    'pro':   rule_obj['id'] in pro_list,
                    'key':   rule_obj['id'],
                    'value': rule_obj['name']
                })
        rules.sort(key = lambda x: x['value'], reverse = False)
        return rules

    @Slot(result = 'QVariant')
    def get_config(self):
        return self._config.get_config()

    @Slot(str, int, result = str)
    def test_proxy(self, server, port):
        logger.info(f'检查代理是否符合规则: {server}:{port}')
        # 校验代理地址
        server_reg = re.compile(r'^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}')
        if server != 'localhost' and server_reg.match(server) is None:
            logger.error(f'代理校验失败, 代理地址不符合规则')
            return 'failed'
        # 校验代理端口号
        if port == 0:
            logger.error(f'代理校验失败, 代理端口不符合规则')
            return 'failed'

        logger.info(f'检查代理是否可用: {server}:{port}')
        proxies = {'http': f'http://{server}:{port}', 'https': f'http://{server}:{port}'}
        try:
            r = head('https://www.google.com/', proxies = proxies, timeout = 5)
            if r.status_code == 200:
                logger.success('代理校验成功')
                return 'success'
            else:
                logger.error('代理校验失败')
                return 'failed'
        except Exception as e:
            logger.error(f'代理校验失败: {e}')
            return 'failed'

    @Slot(str, result = str)
    def magnet_qr_code(self, magnet):
        logger.info(f'生成二维码: {magnet}')
        qr = QRCode()
        qr.make(fit = True)
        qr.add_data(magnet)
        qr_img = qr.make_image()

        buf = io.BytesIO()
        qr_img.save(buf, format = 'PNG')
        image_stream = buf.getvalue()
        buf.close()
        return f'data:image/png;base64,{base64.b64encode(image_stream).decode()}'

    @Slot(str)
    def download(self, magnet):
        print('调用迅雷下载')
        # os.system(f'"D:/apps/Thunder/Program/ThunderStart.exe" {magnet}')
        print(magnet)

    @Slot(str)
    def copy_to_clipboard(self, magnet):
        logger.info(f'复制内容: {magnet} 到剪切板')
        cb = self._app.clipboard()
        cb.setText(magnet)

    def search_done(self, future):
        if future.result() is None:
            # 通知页面数据加载失败（忽视编辑器莫名其妙的警告）
            # noinspection PyUnresolvedReferences
            self.loadStateChanged.emit('error', 0)
        else:
            result = future.result()
            self._search_result_model = QDataListModel(result["list"])
            # 通知页面数据加载完成（忽视编辑器莫名其妙的警告）
            # noinspection PyUnresolvedReferences
            self.loadStateChanged.emit('loaded', result["page"])

    @Slot(str, str, int)
    def search(self, key, search_terms, page):
        logger.info(f'提交搜索任务, 网站规则: {key}, 搜索词: {search_terms}, 页数: {page}')
        # 提交任务
        proxy_config = self._config.get_config()["proxy"]
        proxies = {}
        if proxy_config["enable"]:
            proxies = {
                'http':  f'{proxy_config["type"]}://{proxy_config["host"]}:{proxy_config["port"]}',
                'https': f'{proxy_config["type"]}://{proxy_config["host"]}:{proxy_config["port"]}'
            }
        self._pool.submit(run_crawler, key, search_terms, page, '', proxies).add_done_callback(self.search_done)
        # 通知页面处于加载状态（忽视编辑器莫名其妙的警告）
        # noinspection PyUnresolvedReferences
        self.loadStateChanged.emit('loading', 0)

    loadStateChanged = Signal(str, int)

    # 绑定QML数据
    @Property(QObject, constant = False, notify = loadStateChanged)
    def search_result_model(self):
        return self._search_result_model

def main():
    logger.remove(handler_id = None)
    logger.add(sys.stdout)
    logger.add(sink = 'log/magnet.log', rotation = '100 MB', retention = '30 days',
               enqueue = True, compression = 'zip', level = 'DEBUG', encoding = 'utf-8',
               format = '{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {file}:{line} | {name}:{function} | {message}')

    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()

    # 初始化资源
    qInitResources()

    # 绑定视图
    backend = MainWindow(app, Config())
    engine.rootContext().setContextProperty('backend', backend)
    engine.load('qrc:/main.qml')

    if not engine.rootObjects():
        return -1

    return app.exec()

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