from operator import itemgetter
import random
import pygame
from queue import PriorityQueue
from tkinter import *
from tkinter import messagebox
from tkinter import simpledialog
import os
from csv import DictWriter
from uuid import uuid4
import subprocess as s

BTTN_W = 140
BTTN_H = 40
GRID_COUNT = 50
WIDTH = 700
HEIGHT = WIDTH+BTTN_H
ALGORITHMS = ["A*", "Busca em Largura - BFS", "Busca em Profundidade - DFS","Busca em Profundidade Aleatória - DFSA", "Busca Gulosa Pela Melhor Escolha - BGME", "Busca Pela Melhor Escolha - BME", "Busca de Custo Uniforme - BCU", "Busca de Aprofundamento Iterativo - IDFS", "Busca Bidirecional"]
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Buscas")

RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)
YELLOW = (255,255,0)
WHITE = (255,255,255)
BLACK = (0,0,0)
ORANGE = (255,165,0)
GREY = (128,128,128)
DARK_GREY = (64,64,64)
LIGHT_GREY = (200,200,200)
TURQUOISE = (64,224,208)
EMERALD = (4,99,7)
LIGHT_GREEN = (127,255,0)

hash = ""
total_caminho = 0
expandidos = 0

pygame.init()
class Node:

    def __init__(self, row, col, width, total_rows):
        self.row = row
        self.col = col
        self.x = row * width
        self.y = col * width
        self.color = WHITE
        self.neighbors = []
        self.width = width
        self.total_rows = total_rows
        self.visited = False
        self.dualVisited = [False, False]
        self.weight = 0

    def __repr__(self) -> str:
        return f"x: {self.x} y: {self.y} color: {self.color} visited: {self.visited}"

    def get_pos(self):
        return self.row, self.col

    def is_barrier(self):
        return self.color == BLACK

    def reset(self):
        self.color = WHITE

    def make_weight(self):
        self.color = LIGHT_GREY
        self.weight = 500

    def make_closed(self):
        global expandidos
        expandidos += 1
        self.color = GREEN

    def make_open(self):
        self.color = EMERALD

    def make_barrier(self):
        self.color = BLACK

    def make_start(self):
        self.color = ORANGE

    def make_end(self):
        self.color = TURQUOISE

    def make_path(self):
        global total_caminho
        total_caminho += 1
        self.color = RED

    def draw(self, window):
        pygame.draw.rect(window, self.color, (self.x, self.y, self.width, self.width))

    #Adiciona os vizinhos adjacentes ao node que não são barreiras
    def update_neighbors(self, grid):
        self.neighbors = []
        if self.row > 0 and not grid[self.row - 1][self.col].is_barrier(): #Vizinho de cima
            self.neighbors.append(grid[self.row - 1][self.col])

        if self.row < self.total_rows - 1 and not grid[self.row + 1][self.col].is_barrier(): #Vizinho de baixo
            self.neighbors.append(grid[self.row + 1][self.col])

        if self.col < self.total_rows - 1 and not grid[self.row][self.col + 1].is_barrier(): #Vizinho da direita
            self.neighbors.append(grid[self.row][self.col + 1])

        if self.col > 0 and not grid[self.row][self.col - 1].is_barrier(): #Vizinho da esquerda
            self.neighbors.append(grid[self.row][self.col - 1])

    #Compara os pontos
    def __lt__(self, other):
        return False

def writeToCsv(info):
    arquivo = 'report.csv'
    if not os.path.exists(arquivo):
        with open(arquivo, "+w") as f:
            writer = DictWriter(f, fieldnames = info.keys())
            writer.writeheader()

    with open(arquivo, "a+") as f:
        writer = DictWriter(f, fieldnames = info.keys())
        writer.writerow(info)

def heuristic(p1, p2):
    x1, y1 = p1
    x2, y2 = p2
    #Caminho rápido
    #return abs(x1 - x2) + abs(y1 - y2)
    dx = abs(x1 - x2)
    dy = abs(y1 - y2)
    D = max(dx, dy)
    #return D * math.sqrt(dx * dx + dy * dy)
    #return D * (dx * dx + dy * dy)
    return D * (dx + dy)
    #return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)

def reconstruct_path(came_from, current, draw, grid):
    aux = []
    while current in came_from:
        current = came_from[current]

        current.make_path()

        aux.append(current)
        if len(set(aux)) != len(aux):
            return

    draw()

def iddfs(draw, grid, start, end, maximum_depth: int=200):

    for depth in range(0, maximum_depth):
        for i in range(len(grid)):
            for j in range(len(grid)):
                grid[i][j].visited = False

        if _dfs(draw, start, end, depth):
            start.make_start()
            end.make_end()
            print(counter() - 1)
            write_information("iddfs", grid, counter() - 1)
            counter(True)
            return

        start.make_start()
        end.make_end()
        draw()
    print(counter() - 1)
    write_information("iddfs", grid, counter() - 1)
    counter(True)

def counter(reset=False, i=[0]):
    if not reset:
        i[0]+=1
        return i[0]
    else:
        i[0] = 0
        return i[0]

def _dfs(draw, src, end, depth: int):
    if src == end:
        return True

    if depth <= 0:
        return False

    src.visited = True
    for edge in src.neighbors:
        edge.make_closed()
        if not edge.visited:
            counter()
            if _dfs(draw, edge, end, depth - 1):
                return True

    return False

def dfs(draw, grid, start, end):
    stack = []
    stack.append(start)
    came_from = {}
    start.visited = True
    while len(stack):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        node = stack.pop(-1)
        node.make_closed()
        for neighbor in node.neighbors:
            if node.color != BLACK and not neighbor.visited:
                stack.append(neighbor)
                neighbor.visited = True
                neighbor.make_open()
                came_from[neighbor] = node

        end.make_end()
        start.make_start()
        if node == end:
            reconstruct_path(came_from, end, draw, grid)
            end.make_end()
            start.make_start()
            write_information("dfs", grid, 0)
            break

        draw()

def dfs_random(draw, grid, start, end):
    stack = []
    stack.append(start)
    came_from = {}
    start.visited = True
    while len(stack):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        node = stack.pop(-1)
        node.make_closed()
        neighbors = list(node.neighbors)  # Convert neighbors to a list
        random.shuffle(neighbors)  # Shuffle the list of neighbors randomly
        for neighbor in neighbors:
            if node.color != BLACK and not neighbor.visited:
                stack.append(neighbor)
                neighbor.visited = True
                neighbor.make_open()
                came_from[neighbor] = node

        end.make_end()
        start.make_start()
        if node == end:
            reconstruct_path(came_from, end, draw, grid)
            end.make_end()
            start.make_start()
            write_information("dfs_random", grid, 0)
            break

        draw()

def bcu(draw, grid, start, end):
    count = 0
    queue = PriorityQueue()
    #Adiciona o node inicial com o seu f_score inical que é igal a 0 e um count para diferir elementos com o mesmo f_score adjacentes na fila
    queue.put((0, count, start))
    came_from = {}
    #Bruxaria
    g_score = {node: float("inf") for row in grid for node in row}
    g_score[start] = 0

    open_set_hash = {start}

    while not queue.empty():
        #interrompe o while caso vc queira
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        current = queue.get()[2]
        open_set_hash.remove(current)

        if current == end:
            reconstruct_path(came_from, end, draw, grid)
            # print('asdsad', g_score[end])
            print(g_score[current])
            write_information("bcu", grid, g_score[current])
            end.make_end()
            start.make_start()
            return True

        for neighbor in current.neighbors:
            temp_g_score = g_score[current] + 1 + neighbor.weight

            if temp_g_score < g_score[neighbor]:
                came_from[neighbor] = current
                g_score[neighbor] = temp_g_score
                if neighbor not in open_set_hash:
                    count += 1
                    queue.put((g_score[neighbor], count, neighbor))
                    open_set_hash.add(neighbor)
                    neighbor.make_open()

        #Animação da busca
        draw()

        if current != start:
            current.make_closed()

    return False

def bidirectional(draw, grid, start, end):
    src_queue = []
    dest_queue = []
    src_queue.append(start)
    dest_queue.append(end)
    start.visited = True
    end.visited = True
    src_came_from = {}
    dest_came_from = {}
    found = False
    while len(src_queue) and len(dest_queue):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        if found:
            reconstruct_path(src_came_from, check, draw, grid)
            reconstruct_path(dest_came_from, check, draw, grid)
            end.make_end()
            start.make_start()
            write_information("bi_d", grid, 0)
            return

        src_node = src_queue.pop(0)
        src_node.make_closed()

        if src_node.dualVisited == [True, True]:
            check = src_node
            found = True

        for neighbor in src_node.neighbors:
            if not neighbor.dualVisited[0]:
                src_queue.append(neighbor)
                neighbor.dualVisited[0] = True
                neighbor.make_open()
                start.make_start()
                src_came_from[neighbor] = src_node

        dest_node = dest_queue.pop(0)
        dest_node.make_closed()

        if dest_node.dualVisited == [True, True]:
            check = dest_node
            found = True

        for neighbor in dest_node.neighbors:
            if not neighbor.dualVisited[1]:
                dest_queue.append(neighbor)
                neighbor.dualVisited[1] = True
                neighbor.make_open()
                end.make_end()
                dest_came_from[neighbor] = dest_node

        draw()

def bfs(draw, grid, start, end):
    queue = []
    queue.append(start)
    came_from = {}
    start.visited = True
    while len(queue):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        node = queue.pop(0)
        node.make_closed()
        for neighbor in node.neighbors:
            if node.color != BLACK and not neighbor.visited:
                queue.append(neighbor)
                neighbor.visited = True
                neighbor.make_open()
                start.make_start()
                came_from[neighbor] = node

        if node == end:
            global total_caminho
            reconstruct_path(came_from, end, draw, grid)
            end.make_end()
            start.make_start()
            write_information("bfs", grid, 0)
            break

        draw()

def write_information(algorithm, grid, weight):
    verde = 0
    verde_escuro = 0
    verde_claro = 0
    for i in range(len(grid)):
        for j in range(len(grid)):
            if grid[i][j].color == EMERALD:
                verde_escuro += 1
            if grid[i][j].color == GREEN:
                verde += 1
            if grid[i][j].color == RED:
                verde_claro += 1

    caminho = 0
    expandidos = 0
    if verde_claro > 0:
        caminho = verde_claro + 1
    else:
        caminho = verde

    if algorithm == "bi_d":
        caminho = caminho + 1

    expandidos = verde_claro+verde_escuro+verde
    global hash
    info = {
        "algoritmo": algorithm,
        "hash": hash,
        "expandidos": expandidos,
        "caminho": caminho,
        "visitados" : verde if verde > 0 else verde_claro,
        "peso": weight
    }

    if algorithm == "iddfs":
        info["caminho"] = 0
        info["expandidos"] = 0
        info["visitados"] = weight
        info["peso"] = 0

    writeToCsv(info)

def bme(draw, grid, start, end):
    queue = []
    queue.append(start)
    start.visited = True
    current = start
    while(len(queue)):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        if current == end:
            write_information("bme", grid, 0)
            break

        compare = []
        for neighbor in current.neighbors:
            compare.append( (neighbor, heuristic(neighbor.get_pos(), end.get_pos())) )
        current = compare[compare.index(min(compare, key=itemgetter(1)))][0]
        current.visited = True
        current.make_closed()

        queue.pop()

        for neighbor in current.neighbors:
            if not neighbor.visited:
                queue.append(neighbor)
                neighbor.visited = True
                neighbor.make_open()

        end.make_end()
        start.make_start()

        draw()

def bgme(draw, grid, start, end):
    current = start
    while(current is not end):

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        current.color = WHITE

        compare = []
        for neighbor in current.neighbors:
            compare.append( (neighbor, heuristic(neighbor.get_pos(), end.get_pos())) )
        current = compare[compare.index(min(compare, key=itemgetter(1)))][0]
        current.make_open()

        end.make_end()
        start.make_start()

        draw()
    write_information("bgme", grid, 0)

def a_star(draw, grid, start, end):
    count = 0
    open_set = PriorityQueue()
    #Adiciona o node inicial com o seu f_score inical que é igal a 0 e um count para diferir elementos com o mesmo f_score adjacentes na fila
    open_set.put((0, count, start))
    came_from = {}
    #Bruxaria
    g_score = {node: float("inf") for row in grid for node in row}
    g_score[start] = 0
    f_score = {node: float("inf") for row in grid for node in row}
    f_score[start] = heuristic(start.get_pos(), end.get_pos())

    open_set_hash = {start}

    while not open_set.empty():
        #interrompe o while caso vc queira
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
            elif event.type == pygame.KEYDOWN: #Cancela o a função durante a execução
                if event.key == pygame.K_ESCAPE:
                    return False

        current = open_set.get()[2]
        open_set_hash.remove(current)

        if current == end:
            global expandidos
            global total_caminho
            reconstruct_path(came_from, end, draw, grid)
            end.make_end()
            start.make_start()
            print(g_score[current])
            write_information("a_star", grid, g_score[current])
            return True

        for neighbor in current.neighbors:
            temp_g_score = g_score[current] + 1 + neighbor.weight

            if temp_g_score < g_score[neighbor]:
                came_from[neighbor] = current
                g_score[neighbor] = temp_g_score
                f_score[neighbor] = temp_g_score + heuristic(neighbor.get_pos(), end.get_pos())# + neighbor.weight
                if neighbor not in open_set_hash:
                    count += 1
                    open_set.put((f_score[neighbor], count, neighbor))
                    open_set_hash.add(neighbor)
                    neighbor.make_open()

        #Animação da busca
        draw()

        if current != start:
            current.make_closed()

    return False

def make_grid(rows, width, clear):
    grid = []
    gap = width // rows
    for i in range(rows):
        grid.append([])
        for j in range(rows):
            node = Node(i, j, gap, rows)
            #Pré adiciona elementos
            if not clear:
                if random.randint(1, 100) > 65:
                    node.color = BLACK
            grid[i].append(node)

    return grid

def draw_grid(window, rows, width):
    gap = width // rows
    for i in range(rows):
        pygame.draw.line(window, GREY,(0, i * gap),(width, i * gap))
        for j in range(rows):
            pygame.draw.line(window, GREY,(j * gap, 0),(j * gap, width))

def draw(window, grid, rows, width, index):
    window.fill(WHITE)

    for row in grid:
        for node in row:
            node.draw(window)

    draw_grid(window, rows, width)

    draw_UI(window, index, rows)

    pygame.display.update()

def get_clicked_pos(pos, rows, width):
    gap = width // rows
    x, y = pos

    row = y // gap
    col = x // gap

    if col >= rows:
        col = rows-1
    if row >= rows:
        row = rows-1

    return row, col

def draw_UI(window, index, rows):
    mouse = pygame.mouse.get_pos()
    if 0 <= mouse[0] <= BTTN_W and WIDTH <= mouse[1] <= WIDTH+BTTN_H:
        pygame.draw.rect(window,DARK_GREY,[0,WIDTH,BTTN_W,BTTN_H])
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    Tk().wm_withdraw() #to hide the main window
                    messagebox.showinfo('INFORMAÇÕES!'
                    ,'Use ESC (2x)para reiniciar o grid ou (1x) para cancelar uma operação em andamento.\n\n'+
                    'Use o botão esquerdo do mouse para adicionar elementos à grid.\n'+
                    '(O primeiro elemento adicionado será o ponto inicial e o segundo o ponto final)\n\n'+
                    'Use o botão direito do mouse para remover elementos da grid.\n\n'+
                    'Use o botão do meio do mouse para remover elementos da grid.\n\n'+
                    'Use C para limpar a grid após uma execução.\n\n'+
                    'Use ESPAÇO para iniciar a operação de busca!\n\n'+
                    'Use as sétas [ ← → ] para alternar entre os algorítmos de busca.\n\n'+
                    'Use R para limpar completamente a grid.\n\n'+
                    'Use P para abrir o libreoffice com os dados dos algoritmos.'
                    )
    else:
        pygame.draw.rect(window,GREY,[0,WIDTH,BTTN_W,BTTN_H])

    if 0 <= mouse[0] >= WIDTH-BTTN_W and WIDTH <= mouse[1] <= WIDTH+BTTN_H:
        pygame.draw.rect(window,DARK_GREY,[WIDTH-BTTN_W,WIDTH,BTTN_W,BTTN_H])
        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    Tk().wm_withdraw()
                    USER_INP = simpledialog.askinteger(title="Tamanho", prompt="Digite um novo tamanho:", initialvalue=50, minvalue=10, maxvalue=200)
                    if isinstance(USER_INP, int):
                        main(WIN, WIDTH, USER_INP)
    else:
        pygame.draw.rect(window,GREY,[WIDTH-BTTN_W,WIDTH,BTTN_W,BTTN_H])

    text_font = pygame.font.SysFont('Arial',16)
    bttn1_text = text_font.render('AJUDA' , True , BLACK)
    bttn1_rect = bttn1_text.get_rect(center=(BTTN_W/2, WIDTH+BTTN_H/2))

    algInfo_text = text_font.render("Algorítmo: " + ALGORITHMS[index] , True , BLACK)
    algInfo_rect = algInfo_text.get_rect(midleft=(BTTN_W+10, WIDTH+BTTN_H/2))

    bttn2_text = text_font.render('TAMANHO' , True , BLACK)
    bttn2_rect = bttn2_text.get_rect(center=(WIDTH-BTTN_W/2, WIDTH+BTTN_H/2))

    window.blit(bttn1_text , bttn1_rect)
    window.blit(algInfo_text , algInfo_rect)
    window.blit(bttn2_text , bttn2_rect)

def change_hash():
    global hash
    hash = uuid4().hex
    print(hash)

def open_file():
    try:
        path = "C:\Program Files\LibreOffice\program\soffice"
        s.call([path, "--calc", "-o", "report.csv"])
    except:
        print("LibreOffice não encontrado!")

def main(window, width, grid_count):
    change_hash()
    ROWS = grid_count
    grid = make_grid(ROWS, width, False)
    alg_index = 0
    start = None
    end = None

    run = True
    started = False

    draw(window, grid, ROWS, width, alg_index)
    while run:
        #draw(window, grid, ROWS, width)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False

            if started:
                continue

            if pygame.mouse.get_pressed()[0]: #Botão esquerdo do mouse
                pos = pygame.mouse.get_pos()
                if(pos[0] < WIDTH and pos[1] < WIDTH):
                    col, row = get_clicked_pos(pos, ROWS, width)

                    node = grid[row][col]
                    change_hash()
                    if not start and node != end:
                        start = node
                        start.make_start()

                    elif not end and node != start:
                        end = node
                        end.make_end()

                    elif node != end and node != start:
                        node.make_barrier()

            elif pygame.mouse.get_pressed()[2]: #Botão direito do mouse
                pos = pygame.mouse.get_pos()
                if(pos[0] < WIDTH and pos[1] < WIDTH):
                    col, row = get_clicked_pos(pos, ROWS, width)
                    node = grid[row][col]
                    change_hash()
                    node.weight = 0
                    node.reset()
                    if node == start:
                        start = None
                    elif node == end:
                        end = None

            elif pygame.mouse.get_pressed()[1]: #Botão do meio do mouse
                pos = pygame.mouse.get_pos()
                if(pos[0] < WIDTH and pos[1] < WIDTH):
                    col, row = get_clicked_pos(pos, ROWS, width)
                    node = grid[row][col]
                    change_hash()
                    if node != start and node != end and node.color != BLACK:
                        node.make_weight()

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE and not started:
                    for row in grid:
                        for node in row:
                            node.update_neighbors(grid)

                    for i in range(ROWS):
                        for j in range(ROWS):
                            current = grid[i][j].color
                            grid[i][j].visited = False
                            grid[i][j].dualVisited = [False, False]
                            if current == GREEN or current == EMERALD or current == RED:
                                grid[i][j].reset()
                            if grid[i][j].weight > 0:
                                grid[i][j].make_weight()

                    #Lambda é uma variável que chama uma função
                    if start != None and end != None:
                        if alg_index == 0:
                            a_star(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 1:
                            bfs(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 2:
                            dfs(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 3:
                            dfs_random(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 4:
                            bgme(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 5:
                            bme(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 6:
                            bcu(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 7:
                            iddfs(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                        if alg_index == 8:
                            bidirectional(lambda: draw(window, grid, ROWS, width, alg_index), grid, start, end)
                if event.key == pygame.K_ESCAPE:
                    change_hash()
                    global expandidos
                    global total_caminho
                    total_caminho = 0
                    expandidos = 0
                    start = None
                    end = None
                    grid = make_grid(ROWS, width, False)

                if event.key == pygame.K_r:
                    change_hash()
                    start = None
                    end = None
                    grid = make_grid(ROWS, width, True)

                if event.key == pygame.K_c:
                    for i in range(ROWS):
                        for j in range(ROWS):
                            current = grid[i][j].color
                            grid[i][j].visited = False
                            grid[i][j].dualVisited = [False, False]
                            if current == GREEN or current == EMERALD or current == RED:
                                grid[i][j].reset()
                            if grid[i][j].weight > 0:
                                grid[i][j].make_weight()

                if event.key == pygame.K_LEFT:
                    alg_index = (alg_index - 1) % len(ALGORITHMS)

                if event.key == pygame.K_RIGHT:
                    alg_index = (alg_index + 1) % len(ALGORITHMS)

                if event.key == pygame.K_p:
                    open_file()

        draw(window, grid, ROWS, width, alg_index)
    pygame.quit()

main(WIN, WIDTH, GRID_COUNT)


# 611<BFS>[s3]2606_py
from collections import deque
n = int(input())
k = int(input())
node = [[] for _ in range(n+1)]
for _ in range(k):
    a,b =map(int, input().split())
    node[a].append(b)
    node[b].append(a)

dq = deque()
dq.append(node[1])
vis=[0]*(n+1)
vis[1]=1
cnt=0
while dq :
    now = dq.popleft();
    for m in now :
        if vis[m]==1 : continue
        vis[m]=1
        dq.append(node[m])
        cnt+=1
print(cnt)


from flask import render_template, request, abort
from pymongo import ASCENDING, DESCENDING
from acadome import db
from acadome.articles import articles

@articles.route('/')
def home():
    query = request.args.get('search')
    if query:
        query = query.strip()
        articles = db.articles.find({'$text': {'$search': query}})
        return render_template(
            'search.html',
            title=query,
            query=query,
            articles=articles
        )
    return render_template('home.html')

@articles.route('/fields_of_research')
def fields():
    fields_ = db.fields.find().sort('name')
    return render_template(
        'fields.html',
        title='Fields of research',
        fields=fields_
    )

@articles.route('/field/<string:field>')
def subfields(field):
    field_ = field[0].upper() + field.replace('_', ' ')[1:]
    field = db.fields.find_one({'name': field_})
    if not field:
        abort(404)
    return render_template(
        'fields.html',
        title=field_,
        field=field
    )

@articles.route('/subfield/<string:subfield>')
def subfield_search(subfield):
    subfield_ = subfield.replace('_', ' ')
    if not db.fields.find_one({'subs': subfield_}):
        abort(404)
    articles = db.articles.find({'subfields': subfield_}).sort([('year', DESCENDING), ('title', ASCENDING)])
    return render_template(
        'search.html',
        title=subfield_,
        query=subfield_,
        articles=articles
    )

@articles.route('/author/<string:author>')
def author_search(author):
    author_ = author.replace('_', ' ')
    if not db.authors.find_one({'name': author_}):
        abort(404)
    articles = db.articles.find({'authors': author_}).sort([('year', DESCENDING), ('title', ASCENDING)])
    return render_template(
        'search.html',
        title=author_,
        query=author_,
        articles=articles
    )

@articles.route('/article/<string:ref>')
def article(ref):
    article = db.articles.find_one({'ref': ref})
    return render_template(
        'article.html',
        title=article['title'],
        article=article
    )


from app import app
from sqlalchemy.sql import text
from flask import render_template, request, redirect, url_for, session, jsonify, flash

from flask_wtf import Form
from wtforms.validators import DataRequired
from wtforms.fields import TextField, TextAreaField, PasswordField, BooleanField, RadioField, HiddenField, SelectField

def flash_errors(form):
    for field, errors in form.errors.items():
        for error in errors:
            flash("error", error)

def reports_to(node,accu):
    query = "SELECT * from nodes where reports_to=:nid"
    row = app.engine.execute(text(query),nid=node['id'])
    # accu[node['name']] = []
    accu[node['name']] = {'job_title':node['job_title'],'id':node['id'],'children':[]}

    if not row:
        pass
    else:
        for child in row:
            # accu[node['name']].append(reports_to(child,{}))
            accu[node['name']]['children'].append(reports_to(child,{}))

    return accu

def reports_to_ids(node,accu):
    accu.append(node['id'])

    query = "SELECT * from nodes where reports_to=:nid"
    row = app.engine.execute(text(query),nid=node['id'])

    if not row:
        pass
    else:
        for child in row:
            accu.extend(reports_to_ids(child,[]))

    return accu

@app.route('/',methods=['GET'])
@app.route('/index',methods=['GET'])
def hierarchy():
    # the root answers to no one
    query = "SELECT * FROM nodes where reports_to is NULL;"
    row = app.engine.execute(query).fetchone()

    if row == None:
        flash('error','You must have a CEO')
        return redirect(url_for('add'))

    node = dict(row)
    hierarchy = reports_to(node,{})

    query = """SELECT * from nodes"""
    nodes = app.engine.execute(text(query))
    choices = [(g['id'], g['name'],g['job_title']) for g in nodes]

    return render_template('hierarchy.html',hierarchy=hierarchy,choices=choices)

@app.route('/add',methods=['GET'])
def add():
    form = AddUserForm()

    query = """SELECT * from nodes"""
    nodes = app.engine.execute(text(query))
    choices = [(g['id'], g['name']) for g in nodes]
    choices.insert(0, (0,'No one'))

    return render_template('add.html',form=form,choices=choices)

@app.route('/new',methods=['POST'])
def new():
    form = AddUserForm()
    if not form.validate_on_submit():
        flash_errors(form)
        return redirect(url_for('add'))

    if form.reports_to.data == 0:
        # Is there a CEO yet?
        query = "SELECT * FROM nodes where reports_to IS NULL";
        ceo = app.engine.execute(query).fetchone()
        if ceo:
            # Find former root
            query = """SELECT id FROM nodes WHERE reports_to IS NULL"""
            result = app.engine.execute(text(query))
            former_ceo = result.fetchone()['id']

            # Promote new
            query = """INSERT INTO nodes (name, job_title, reports_to)
                       VALUES (:name,'CEO',NULL)
                       RETURNING id;"""
            result = app.engine.execute(text(query),name=form.name.data)
            new_ceo = result.fetchone()['id']
            # Demote old
            query = """UPDATE nodes SET reports_to=:reports_to, job_title='Former CEO' WHERE id=:former_ceo"""
            app.engine.execute(text(query),reports_to=new_ceo,former_ceo=former_ceo)
        else:
          # We're creating the first CEO!
          query = """INSERT INTO nodes (name, job_title, reports_to)
                     VALUES (:name,'CEO',NULL)"""
          app.engine.execute(text(query),name=form.name.data)

    else:
        # Regular new employee
        query = """INSERT INTO nodes (name, job_title, reports_to)
                   VALUES (:name,:job_title,:reports_to)
                   RETURNING id;"""

        reslt = app.engine.execute(text(query),name=form.name.data,
                                       job_title=form.job_title.data,
                                       reports_to=form.reports_to.data)

    flash('info','Added new node')
    return redirect(url_for('hierarchy'))

@app.route('/edit/<id>',methods=['GET'])
def edit(id=None):
    form = EditUserForm()

    query = "SELECT * FROM nodes where id=:id";
    row = app.engine.execute(text(query),id=id).fetchone()

    if not row:
        flash('error',"User ID doesn't exist")
        return redirect(url_for('hierarchy'))
    user = dict(row)

    ids = reports_to_ids(user,[])
    exclude = ', '.join(str(i) for i in ids)

    hierarchy = reports_to(user,{})

    query = """SELECT * from nodes where id NOT IN ({0});""".format(exclude)
    nodes = app.engine.execute(text(query))

    choices = [(g['id'], g['name'],g['job_title']) for g in nodes]
    choices.insert(0, (0,'No one'))

    return render_template('edit.html', user=user, hierarchy=hierarchy, form=form,choices=choices)

@app.route('/update',methods=['POST'])
def update():
    form = EditUserForm()

    if not form.validate_on_submit():
        flash_errors(form)
        return redirect(url_for('edit',id=form.userid.data))

    if form.reports_to.data == 0:
        form.reports_to.data = None

        query = "SELECT * FROM nodes where reports_to IS NULL";
        ceo = app.engine.execute(query).fetchone()

        if form.userid.data != ceo['id']:
            # This means we're promoting a new ceo!
            query = """UPDATE nodes SET reports_to=NULL, job_title='CEO' WHERE id=:id"""
            app.engine.execute(text(query),id=form.userid.data)

            query = """UPDATE nodes SET reports_to=:reports_to, job_title='Former CEO' WHERE id=:former_ceo"""
            app.engine.execute(text(query),reports_to=form.userid.data,former_ceo=ceo['id'])

    query = """UPDATE nodes SET name =:name, job_title=:job_title,reports_to=:reports_to WHERE id=:id;"""
    app.engine.execute(text(query),name=form.name.data,
                                   job_title=form.job_title.data,
                                   reports_to=form.reports_to.data,
                                   id=form.userid.data)
    flash('info','Updated node.')
    return redirect(url_for('hierarchy'))

@app.errorhandler(404)
def page_not_found(e):
    flash('error','Page not found')
    return redirect(url_for('hierarchy'))

class SelectUserForm(Form):
    query = """SELECT * from nodes"""
    nodes = app.engine.execute(text(query))
    choices = [(g['id'], g['name']) for g in nodes]

    choices.insert(0, (0,'No one'))
    reports_to = SelectField("node",choices=choices,default=0,coerce=int)

# Having some trouble creating dynamic SelectFields, had to forgo the wtforms validation
class NonValidatingSelectField(SelectField):
    def pre_validate(self, form):
        pass

# Add user form
class AddUserForm(Form):
    name = TextField("name", [DataRequired("Name required.")])
    job_title = TextField("job_title")
    reports_to = NonValidatingSelectField("node",choices=[],coerce=int)

# Edit user form
class EditUserForm(Form):
    userid = TextField("userid",[DataRequired("ID required")])
    name = TextField("name", [DataRequired("Name required.")])
    job_title = TextField("job_title", [DataRequired("Job title required.")])
    reports_to = NonValidatingSelectField("node",choices=[],default=0,coerce=int)


"""Basic tests for checking degradation between versions and profiles."""

import os
import git
import pytest

import perun.utils.log as log
import perun.logic.config as config
import perun.logic.store as store
import perun.check.factory as check
import perun.check.average_amount_threshold as aat
import perun.check.best_model_order_equality as bmoe
import perun.check.fast_check as fast
import perun.check.exclusive_time_outliers as eto

__author__ = 'Tomas Fiedor'

def test_degradation_precollect(monkeypatch, pcs_full, capsys):
    """Set of basic tests for testing degradation in concrete minor version point

    Expects correct behaviour
    """
    matrix = config.Config('local', '', {
        'vcs': {'type': 'git', 'url': '../'},
        'cmds': ['ls'],
        'args': ['-al'],
        'workloads': ['.', '..'],
        'collectors': [
            {'name': 'time', 'params': {
                'warmup': 1,
                'repeat': 1
            }}
        ],
        'postprocessors': [],
        'execute': {
            'pre_run': [
                'ls | grep "."',
            ]
        },
        'degradation': {
            'collect_before_check': 'true',
            'apply': 'first',
            'strategies': [{
                'method': 'aat'
            }]
        }
    })
    monkeypatch.setattr("perun.logic.config.local", lambda _: matrix)
    git_repo = git.Repo(pcs_full.get_vcs_path())
    head = str(git_repo.head.commit)

    check.degradation_in_minor(head)
    out, err = capsys.readouterr()
    assert err == ""

    def raise_sysexit(*_):
        """Raises System Exit ;)"""
        raise SystemExit()
    check.pre_collect_profiles.minor_version_cache.clear()
    monkeypatch.setattr("perun.logic.runner.run_matrix_job", raise_sysexit)
    check.degradation_in_minor(head)
    out, err = capsys.readouterr()
    assert err == ""

def test_degradation_in_minor(pcs_with_degradations, capsys):
    """Set of basic tests for testing degradation in concrete minor version point

    Expects correct behaviour
    """
    git_repo = git.Repo(pcs_with_degradations.get_vcs_path())
    head = str(git_repo.head.commit)

    check.degradation_in_minor(head)
    out, err = capsys.readouterr()
    assert "Optimization" in out
    assert err == ""

def test_degradation_in_history(pcs_with_degradations):
    """Set of basic tests for testing degradation in while history

    Expects correct behaviour
    """
    git_repo = git.Repo(pcs_with_degradations.get_vcs_path())
    head = str(git_repo.head.commit)

    result = check.degradation_in_history(head)
    assert check.PerformanceChange.Degradation in [r[0].result for r in result]

def test_degradation_between_profiles(pcs_with_degradations, capsys):
    """Set of basic tests for testing degradation between profiles

    Expects correct behaviour
    """
    pool_path = os.path.join(os.path.split(__file__)[0], 'profiles', 'degradation_profiles')
    profiles = [
        store.load_profile_from_file(os.path.join(pool_path, 'linear_base.perf'), True),
        store.load_profile_from_file(os.path.join(pool_path, 'linear_base_degradated.perf'), True),
        store.load_profile_from_file(os.path.join(pool_path, 'quad_base.perf'), True),
        store.load_profile_from_file(os.path.join(pool_path, 'zero.perf'), True)
    ]
    tracer_profiles = [
        store.load_profile_from_file(os.path.join(pool_path, 'tracer_baseline.perf'), True),
        store.load_profile_from_file(os.path.join(pool_path, 'tracer_target.perf'), True)
    ]

    # Test degradation detection using ETO
    result = list(eto.exclusive_time_outliers(tracer_profiles[0], tracer_profiles[1]))
    expected_changes = {check.PerformanceChange.TotalDegradation, check.PerformanceChange.NoChange}
    assert expected_changes & set(r.result for r in result)

    # Test degradation detection using ETO on the same profile - no Degradation should be found.
    result = list(eto.exclusive_time_outliers(tracer_profiles[0], tracer_profiles[0]))
    # We allow TotalDegradation and TotalOptimization since one them is always reported
    allowed = {
        check.PerformanceChange.NoChange, check.PerformanceChange.TotalDegradation,
        check.PerformanceChange.TotalOptimization
    }
    # No other result should be present here
    assert not set(r.result for r in result) - allowed

    # Cannot detect degradation using BMOE strategy betwen these pairs of profiles,
    # since the best models are same with good confidence
    result = list(bmoe.best_model_order_equality(profiles[0], profiles[1]))
    assert check.PerformanceChange.NoChange in [r.result for r in result]

    # Can detect degradation using BMOE strategy betwen these pairs of profiles
    result = list(bmoe.best_model_order_equality(profiles[1], profiles[2]))
    assert check.PerformanceChange.Degradation in [r.result for r in result]

    result = list(bmoe.best_model_order_equality(profiles[0], profiles[2]))
    assert check.PerformanceChange.Degradation in [r.result for r in result]

    result = list(aat.average_amount_threshold(profiles[1], profiles[2]))
    assert check.PerformanceChange.Degradation in [r.result for r in result]

    # Can detect optimizations both using BMOE and AAT and Fast
    result = list(aat.average_amount_threshold(profiles[2], profiles[1]))
    assert check.PerformanceChange.Optimization in [r.result for r in result]

    result = list(fast.fast_check(profiles[2], profiles[1]))
    assert check.PerformanceChange.MaybeOptimization in [r.result for r in result]

    result = list(bmoe.best_model_order_equality(profiles[2], profiles[1]))
    assert check.PerformanceChange.Optimization in [r.result for r in result]

    # Try that we printed confidence
    deg_list = [(res, "", "") for res in result]
    log.print_list_of_degradations(deg_list)
    out, _ = capsys.readouterr()
    assert 'with confidence' in out

    # Try that nothing is wrong when the average is 0.0
    result = list(aat.average_amount_threshold(profiles[3], profiles[3]))
    # Assert that DegradationInfo was yield
    assert result
    # Assert there was no change
    assert check.PerformanceChange.NoChange in [r.result for r in result]

    # Test incompatible profiles
    pool_path = os.path.join(os.path.split(__file__)[0], 'profiles', 'full_profiles')
    lhs = store.load_profile_from_file(os.path.join(pool_path, 'prof-1-time-2017-03-19-19-17-36.perf'), True)
    rhs = store.load_profile_from_file(os.path.join(pool_path, 'prof-3-memory-2017-05-15-15-43-42.perf'), True)
    with pytest.raises(SystemExit):
        check.degradation_between_files(lhs, rhs, "HEAD", 'all')
    _, err = capsys.readouterr()
    assert 'incompatible configurations' in err

def test_strategies():
    """Set of basic tests for handling the strategies

    Expects correct behaviour
    """
    pool_path = os.path.join(os.path.split(__file__)[0], 'profiles', 'degradation_profiles')
    profile = store.load_profile_from_file(os.path.join(pool_path, 'linear_base.perf'), True)
    rule = {
        'method': 'average_amount_threshold',
        'collector': 'complexity',
        'postprocessor': 'regression_analysis'
    }
    assert check.is_rule_applicable_for(rule, profile)

    rule = {
        'method': 'average_amount_threshold',
        'postprocessor': 'regression_analysis',
        'collector': 'complexity'
    }
    assert check.is_rule_applicable_for(rule, profile)

    rule = {
        'method': 'average_amount_threshold',
        'postprocessor': 'regression_analysis',
        'collector': 'memory'
    }
    assert not check.is_rule_applicable_for(rule, profile)

    rule = {
        'method': 'average_amount_threshold',
        'collector': 'complexity',
        'postprocessor': 'normalizer'
    }
    assert not check.is_rule_applicable_for(rule, profile)

    rule = {
        'method': 'average_amount_threshold',
        'collector': 'complexity',
        'cmd': 'bogus'
    }
    assert not check.is_rule_applicable_for(rule, profile)


import socket
from pathlib import Path
from typing import Any, Callable, TypeVar, overload

import numpy as np

from ._sockutil import Socket
from .affetto import Affetto

R = TypeVar("R")

def split_received_msg(
    data: bytes | str,
    function: Callable[[str], R] = float,
    sep: str | None = None,
    strip: bool = True,
) -> list[R]:
    """Returns a list of values converted from received bytes."""
    if isinstance(data, bytes):
        decoded_data = data.decode()
    elif isinstance(data, str):
        decoded_data = data
    else:
        raise TypeError(f"unsupported type: {type(data)}")
    if strip:
        decoded_data = decoded_data.strip(sep)
    return list(map(function, decoded_data.split(sep)))

def convert_array_to_string(
    array: list[float] | list[int] | np.ndarray,
    sep: str = " ",
    f_spec: str = ".0f",
    precision: int | None = None,
) -> str:
    """Returns a string of array joined with specific format."""
    if precision is None:
        formatted_array = [f"{x:{f_spec}}" for x in array]
    else:
        formatted_array = [f"{x:.{precision}f}" for x in array]
    return sep.join(formatted_array)

def convert_array_to_bytes(
    array: list[float] | list[int] | np.ndarray,
    sep: str = " ",
    f_spec: str = ".0f",
    precision: int | None = None,
) -> bytes:
    """Returns bytes encoded array joined with specific format."""
    return convert_array_to_string(array, sep, f_spec, precision).encode()

def unzip_array_as_ndarray(
    array: list[float] | list[int] | np.ndarray, ncol: int = 3
) -> np.ndarray:
    ret = np.array(array).reshape((int(len(array) / ncol), ncol))
    return ret.T

def unzip_array(array: list[float] | list[int] | np.ndarray, n: int = 3) -> list[Any]:
    arr = unzip_array_as_ndarray(array, ncol=n)
    return arr.tolist()

def zip_arrays_as_ndarray(
    *arrays: list[float] | list[int] | np.ndarray,
) -> np.ndarray:
    arr = np.stack(arrays, axis=1)
    return arr.flatten()

@overload
def zip_arrays(*arrays: list[float]) -> list[float]:
    ...

@overload
def zip_arrays(*arrays: list[int]) -> list[int]:
    ...

@overload
def zip_arrays(*arrays: np.ndarray) -> list[float]:
    ...

def zip_arrays(
    *arrays: list[float] | list[int] | np.ndarray,
) -> list[float] | list[int]:
    return list(zip_arrays_as_ndarray(*arrays))

class AffComm(Affetto):
    comm_config: dict[str, Any]
    sensory_socket: Socket
    command_socket: Socket

    def __init__(self, config_path: Path | str | None = None) -> None:
        self.sensory_socket = Socket()
        self.command_socket = Socket()
        super().__init__(config_path)

    def __repr__(self) -> str:
        return "%s.%s()" % (self.__class__.__module__, self.__class__.__qualname__)

    def __str__(self) -> str:
        try:
            cpath = self._config_path
        except AttributeError:
            cpath = None
        return f"""\
AffComm configuration:
  Config file: {str(cpath)}
   Receive at: {str(self.sensory_socket)}
      Send to: {str(self.command_socket)}
"""

    def load_config(self, config: dict[str, Any]) -> None:
        super().load_config(config)
        self.load_comm_config()

    def load_comm_config(self, config: dict[str, Any] | None = None) -> None:
        if config is not None:
            c = config
        else:
            c = self.config
        self.comm_config = c["comm"]
        self.sensory_socket.addr = self.comm_config["local"]
        self.command_socket.addr = self.comm_config["remote"]

    def create_sensory_socket(
        self, addr: tuple[str, int] | None = None
    ) -> socket.socket:
        s = self.sensory_socket.create()
        self.sensory_socket.bind(addr)
        return s

    def create_command_socket(self) -> socket.socket:
        return self.command_socket.create()

    def create_sockets(
        self, sensory_addr: tuple[str, int] | None = None
    ) -> tuple[socket.socket, socket.socket]:
        return (self.create_sensory_socket(sensory_addr), self.create_command_socket())

    def close_sensory_socket(self) -> None:
        if self.sensory_socket.is_created():
            self.sensory_socket.close()

    def close_command_socket(self) -> None:
        if self.command_socket.is_created():
            self.command_socket.close()

    def close(self) -> None:
        self.close_sensory_socket()
        self.close_command_socket()

    def receive(self, bufsize=1024) -> bytes:
        return self.sensory_socket.recvfrom(bufsize)

    def receive_as_list(
        self,
        bufsize=1024,
        function: Callable[[str], R] = float,
    ) -> list[R]:
        return split_received_msg(
            self.sensory_socket.recvfrom(bufsize), function=function
        )

    def receive_as_2darray(
        self,
        bufsize=1024,
    ) -> np.ndarray:
        sarr = split_received_msg(self.sensory_socket.recvfrom(bufsize), function=float)
        return unzip_array_as_ndarray(sarr, ncol=3)

    def send(self, send_bytes: bytes, addr: tuple[str, int] | None = None) -> int:
        return self.command_socket.sendto(send_bytes, addr)

    def send_commands(
        self,
        *arrays: np.ndarray,
        addr: tuple[str, int] | None = None,
    ) -> int:
        return self.command_socket.sendto(
            convert_array_to_bytes(zip_arrays_as_ndarray(*arrays)), addr
        )


#!/usr/bin/python

from __future__ import print_function

from copy import deepcopy
import os.path
import getpass
import subprocess
import sys

import networkx as nx

import gkno.adminUtils
from gkno.adminUtils import *

import gkno.commandLine
from gkno.commandLine import *

import configurationClass.configurationClass
from configurationClass.configurationClass import *

import gkno.gknoErrors
from gkno.gknoErrors import *

import gkno.exportInstance
from gkno.exportInstance import *

import gkno.files
from gkno.files import *

import gkno.gknoConfigurationFiles
from gkno.gknoConfigurationFiles import *

import gkno.helpClass
from gkno.helpClass import *

import gkno.makefileData
from gkno.makefileData import *

import gkno.tracking
from gkno.tracking import *

import gkno.writeToScreen
from gkno.writeToScreen import *

__author__ = "Alistair Ward"
__version__ = "0.211"
__date__ = "April 2014"

def main():

  # Initialise variables:
  phoneHomeID       = ''
  generateMakefiles = True
  hasIsolatedNode   = False

  # Define the errors class.
  errors = gknoErrors()

  # Define the source path of all the gkno machinery.
  sourcePath                     = os.path.abspath(sys.argv[0])[0:os.path.abspath(sys.argv[0]).rfind('/src/gkno.py')]
  configurationFilesPath         = sourcePath + '/config_files/'
  gknoConfigurationFilePath      = sourcePath + '/config_files/'
  pipelineConfigurationFilesPath = sourcePath + '/config_files/pipes/'
  toolConfigurationFilesPath     = sourcePath + '/config_files/tools/'
  resourcePath                   = sourcePath + '/resources/'
  toolsPath                      = sourcePath + '/tools/'

  # Get the latest commitID for gkno (the environment variable was set up in the shell script).
  gknoCommitID = os.getenv('GKNOCOMMITID')

  # Define an admin utilities object. This handles all of the build/update steps
  # along with 'resource' management.
  admin = adminUtils(sourcePath)

  # Define a configurationClass object.  This is part of the configurationClass library.  Also
  # define an object that handles finding configuration files for gkno (e.g. gkno specific
  # configuration file operations.
  config     = configurationMethods()
  gknoConfig = gknoConfigurationFiles()

  # Create a graph object.  This is a directed graph consisting of nodes representing the tasks
  # in the pipeline as well as data being fed into the nodes.  All relevant tool information
  # will be attached to the task nodes and information about the data is attached to the data
  # nodes.  The edges between the nodes define the command line argument for the data and tool.
  pipelineGraph = nx.DiGraph()

  # Define a command line options, get the first command line argument
  # and proceed as required.
  commands = commandLine()

  # Define a class for outputting graphics.
  draw = drawGraph()

  # Generate a class for storing details for use in the makefile.
  make = makefileData()

  # Define a help class.  This provides usage information for the user. Also define an object used
  # for writing to screen.
  gknoHelp = helpClass()
  write    = writeToScreen()

  # Get information on the mode being run. The pipelineName is the name of the pipeline
  # or the tool if being run in tool mode.
  admin.isRequested, admin.mode = commands.isAdminMode(admin.allModes)
  isPipeline                    = commands.setMode(admin.isRequested)
  config.isPipeline             = isPipeline
  runName                       = commands.getPipelineName(isPipeline)

  # Read in information from the gkno specific configuration file.
  gknoConfig.gknoConfigurationData = config.fileOperations.readConfigurationFile(gknoConfigurationFilePath + '/gknoConfiguration.json')
  gknoConfig.validateConfigurationFile()
  gknoConfig.addGknoSpecificNodes(pipelineGraph, config, isPipeline)
  gknoConfig.eraseConfigurationData()

  # Check to see if gkno is being run in verbose mode or debug mode.
  isVerbose, isDebug = commands.checkVerbose(pipelineGraph, config, admin)

  # Each of the tools available to gkno should have a config file to describe its operation,
  # purpose etc.  These are contained in config_files/tools.  Find all of the config files
  # and create a hash table with all available tools.
  if isDebug: write.writeDebug('Finding all json configuration files.')
  gknoConfig.getJsonFiles(configurationFilesPath)

  # Check if the pipeline/tool name is valid.
  if commands.mode != 'admin': gknoConfig.checkPipelineName(gknoHelp, isPipeline, runName)
  if isDebug: write.writeDebug('Checked pipeline name.')

  # Check if help has been requested on the command line.  Search for the '--help'
  # or '-h' arguments on the command line. If help has been requested, print out the required
  # help.
  gknoHelp.checkForHelp(isPipeline, runName, admin, commands.mode)
  instanceName = commands.getInstanceName(pipelineGraph, config, isPipeline)
  if gknoHelp.printHelp and not gknoHelp.specificPipelineHelp:
    gknoHelp.printUsage(pipelineGraph, config, gknoConfig, admin, toolConfigurationFilesPath, pipelineConfigurationFilesPath, runName, instanceName)

  # Print gkno title and version to the screen.
  write.printHeader(__version__, __date__, gknoCommitID)

  # No admin mode requested. Prepare to setup our tool or pipeline run.
  if not admin.isRequested:

    # Make sure we've actually been "built" before doing any real processing.
    # Skip this requirement if we're only going to be printing a help message later.
    if not gknoHelp.printHelp and not admin.isBuilt():
      errors.gknoNotBuilt()
      errors.terminate()

    # If a pipeline is being run, check that configuration files exist for the selected
    # pipeline.  This should be in directory config_files and have the name <$ARGV[1]>.json.
    # If the file exists, parse the json file.
    if isPipeline:
      phoneHomeID               = 'pipes/' + runName
      filename                  = pipelineConfigurationFilesPath + runName + '.json'
      pipelineConfigurationData = config.fileOperations.readConfigurationFile(filename)
      config.pipeline.processConfigurationData(pipelineConfigurationData, runName, gknoConfig.jsonFiles['tools'], allowTermination = True)
      config.instances.checkInstances(runName, pipelineConfigurationData['instances'], isPipeline, isExternal = False)
      config.instances.checkExternalInstances(config.fileOperations, filename, runName, gknoConfig.jsonFiles['tools'], isPipeline)
      del(pipelineConfigurationData)

    # If gkno is being run in tool mode, set the phoneHomeID.
    else: phoneHomeID = 'tools/' + runName

  # Process all of the tool configuration files that are used.
  if commands.mode == 'pipeline':

    # Find all of the tasks to be used in the pipeline.
    for task in config.pipeline.taskAttributes:
      tool                  = config.pipeline.taskAttributes[task].tool
      toolFile              = toolConfigurationFilesPath + tool + '.json'
      toolConfigurationData = config.fileOperations.readConfigurationFile(toolFile)

      # Ensure that the tool configuration file is well constructed and put all of the data
      # in data structures.  Each tool in each configuration file gets its own data structure.
      config.tools.processConfigurationData(tool, toolConfigurationData, allowTermination = True)
      del(toolConfigurationData)

    # Check that any argument in a pipeline configuration file node taht defines a filename stub
    # is linked to other stub arguments, or that the desired extension is included in the node.
    config.pipeline.checkCommonNodes(config.tools)

    # For each task in the pipeline, build an individual graph consisting of a task node, input
    # option nodes (all input and output file arguments are treated as option nodes) and finally
    # all input and output files are given file nodes.  Nodes are merged later to generate the
    # final pipeline.
    config.buildTaskGraph(pipelineGraph, config.pipeline.taskAttributes.keys())

    # Attach additional information from the pipeline configuration file to the nodes.
    config.assignPipelineAttributes(pipelineGraph, config.pipeline.taskAttributes.keys())

    # Add the pipeline arguments to the nodeIDs dictionary.
    config.nodeMethods.getPipelineArgumentNodes(pipelineGraph, config)

    # Now that every task in the pipeline has an individual graph built, use the information
    # in the pipeline configuration file to merge nodes and build the final pipeline graph.
    config.mergeNodes(pipelineGraph)

    # Generate the workflow using a topological sort of the pipeline graph.
    config.pipeline.workflow = config.generateWorkflow(pipelineGraph)

    # Check to see if any tasks in the pipeline are isolated, i.e. they share no files with any other task
    # in the pipeline.
    hasIsolatedNode, isolatedNodes = config.checkForIsolatedNodes(pipelineGraph)

    # Loop over all of the nodes and determine which require a value.  Also check to see if there
    # are missing edges.  For example, if a tool has an argument that is required, but is not included
    # in the pipeline configuration file (as a pipeline argument or via connections), the graph cannot
    # be completely defined.
    config.nodeMethods.setRequiredNodes(pipelineGraph, config.tools)

    # If help was requested on this specific pipeline, the information now exists to generate
    # the help information.
    if gknoHelp.specificPipelineHelp:
      gknoHelp.specificPipelineUsage(pipelineGraph, config, gknoConfig, runName, toolConfigurationFilesPath, instanceName)

  # If being run in the tool mode.
  elif commands.mode == 'tool':
    toolFile              = toolConfigurationFilesPath + runName + '.json'
    toolConfigurationData = config.fileOperations.readConfigurationFile(toolFile)

    # Ensure that the tool configuration file is well constructed and put all of the data
    # in data structures.  Each tool in each configuration file gets its own data structure.
    config.tools.processConfigurationData(runName, toolConfigurationData, allowTermination = True)
    config.instances.checkInstances(runName, toolConfigurationData['instances'], isPipeline, isExternal = False)
    config.instances.checkExternalInstances(config.fileOperations, toolFile, runName, gknoConfig.jsonFiles['tools'], isPipeline)
    del(toolConfigurationData)

    # Define the tasks structure. Since a single tool is being run, this is simply the name
    # of the tool. Set the tasks structure in the pipeline configuration object as well.
    config.pipeline.definePipelineAttributesForTool(runName)
    config.buildTaskGraph(pipelineGraph, config.pipeline.taskAttributes.keys())
  if isDebug: write.writeDebug('Read configuration files.')

  # Parse the command line and put all of the arguments into a list.
  if commands.mode != 'admin':
    write.writeReadingCommandLineArguments()
    commands.getCommandLineArguments(pipelineGraph, config, gknoConfig, runName, isPipeline)
    write.writeDone()
  if isDebug: write.writeDebug('Parsed command line.')

  # If help was requested or there were problems (e.g. the tool name or pipeline
  # name did not exist), print out the required usage information.
  if gknoHelp.printHelp and not gknoHelp.specificPipelineHelp:
    gknoHelp.printUsage(pipelineGraph, config, gknoConfig, admin, toolConfigurationFilesPath, pipelineConfigurationFilesPath, runName, instanceName)

  # Populate the tl.arguments structure with the arguments with defaults from the tool configuration files.
  # The x.arguments structures for each of the classes used in gkno has the same format (a dictionary whose
  # keys are the names of tools: each tool is itself a dictionary of command line arguments, the value being
  # a list of associated parameters/files etc).  The command line in the makefile is then constructed from
  # the x.arguments structures in a strict hierarchy.  tl.arguments contains the defaults found in the
  # configuration file.  These are overwritten (if conflicts occur) by the values in ins.arguments (i.e.
  # arguments drawn from the specified instance). Next, the cl.arguments are the commands defined on the
  # command line by the user and then finally, mr.arguments pulls information from the given multiple runs
  # file if one exists.

  # If admin mode requested, then run it & terminate script.
  # No need to bother with running tools or pipes.
  if admin.isRequested:
    success = admin.run(sys.argv)
    if success:

      # Check that all of the tools were successfully built. If not, post a warning about which tools cannot
      # be used.
      if admin.allBuilt: exit(0)
      else: errors.failedToolBuilds(admin.builtTools)
    else: errors.terminate()

  # Check if an instance was requested by the user.  If so, get the data and add the values to the data nodes.
  write.writeCheckingInstanceInformation()

  # Check to see if the requested instance is available.
  config.instances.checkRequestedInstance(configurationFilesPath, runName, instanceName, gknoConfig.jsonFiles, isPipeline)

  # Check to see if any of the instance arguments are gkno specific arguments. First check default
  # arguments, then the specified instance.
  gknoConfig.attachInstanceArgumentsToNodes(config, pipelineGraph, runName, 'default')
  gknoConfig.attachInstanceArgumentsToNodes(config, pipelineGraph, runName, instanceName)

  # Now handle the rest of the instance arguments. Again, attach default instance arguments first. These will
  # be overwritten if specified in the requested instance or the command line.
  if isPipeline:
    config.attachPipelineInstanceArgumentsToNodes(pipelineGraph, runName, 'default')
    config.attachPipelineInstanceArgumentsToNodes(pipelineGraph, runName, instanceName)
  else:
    config.attachToolInstanceArgumentsToNodes(pipelineGraph, runName, 'default')
    config.attachToolInstanceArgumentsToNodes(pipelineGraph, runName, instanceName)
  write.writeDone()
  if isDebug: write.writeDebug('Checked instance information.')

  # Attach the values of the pipeline arguments to the relevant nodes.
  write.writeAssignPipelineArgumentsToNodes()
  if isPipeline: commands.attachPipelineArgumentsToNodes(pipelineGraph, config, gknoConfig)
  else: commands.attachToolArgumentsToNodes(pipelineGraph, config, gknoConfig)
  write.writeDone()
  if isDebug: write.writeDebug('Attached argument values to the nodes.')

  # Check if multiple runs or internal loops have been requested.
  hasMultipleRuns, hasInternalLoop = gknoConfig.hasLoop(pipelineGraph, config, resourcePath, isPipeline, runName)
  if hasMultipleRuns or hasInternalLoop:
    write.writeAssignLoopArguments(hasMultipleRuns)
    gknoConfig.addLoopValuesToGraph(pipelineGraph, config, isPipeline, runName)
    write.writeDone()
    if isDebug: write.writeDebug('Assigned arguments for multiple runs.')

  # Now that the command line argument has been parsed, all of the values supplied have been added to the
  # option nodes.  All of the file nodes can take their values from their corresponding option nodes.
  commands.mirrorFileNodeValues(pipelineGraph, config)
  if isDebug: write.writeDebug('Mirrored file node values')

  # Not all of the following operation need to be performed if an instance is being exported. Since exporting
  # an instance just requires that the supplied values are valid, but not that all values are supplied (since
  # gkno will not be executed).
  isExportInstance = True if config.nodeMethods.getGraphNodeAttribute(pipelineGraph, 'GKNO-EXPORT-INSTANCE', 'values') else False
  if isDebug: write.writeDebug('Export instance: ' + str(isExportInstance))
  if not isExportInstance:

    # Construct all filenames.  Some output files from a single tool or a pipeline do not need to be
    # defined by the user.  If there is a required input or output file and it does not have its value set,
    # determine how to construct the filename and populate the node with the value.
    gknoConfig.constructFilenames(pipelineGraph, config, isPipeline)

    # Check that all required files and values have been set. All files and parameters that are listed as
    # required by the individual tools should already have been checked, but if the pipeline has some
    # additional requirements, these may not yet have been checked.
    config.checkRequiredFiles(pipelineGraph)

    # If flags are linked in a pipeline configuration file, but none of them were set on the command line,
    # the nodes will have no values. This will cause problems when generating the makefiles. Search all
    # option nodes looking for flags and set any unset nodes to 'unset'.
    config.searchForUnsetFlags(pipelineGraph)

  # Check that all files have a path set.
  outputPaths = gknoConfig.setFilePaths(pipelineGraph, config)
  if isDebug: write.writeDebug('Set file paths')

  # Check if there are instructions for evaluating commands and if so, attach them to the required
  # nodes. Then recalculate the workflow, since some of the tasks may now use outputs from previous
  # tasks for evaluating these commands, creating new dependencies.
  write.writeCheckingEvaluateCommands()
  config.evaluateCommands(pipelineGraph)
  write.writeDone()
  if config.hasCommandToEvaluate: config.pipeline.workflow = config.generateWorkflow(pipelineGraph)
  if isPipeline: write.writePipelineWorkflow(pipelineGraph, config, gknoHelp)

  if isDebug: write.writeDebug('Identified commands to evaluate')

  # Prior to filling in missing filenames, check that all of the supplied data is consistent with
  # expectations.  This includes ensuring that the inputted data types are correct (for example, if
  # an argument expects an integer, check that the values are integers), filename extensions are valid
  # and that multiple values aren't given to arguments that are only allowed a single value.
  gknoConfig.checkData(pipelineGraph, config, not isExportInstance)
  if isDebug: write.writeDebug('Checked data')

  # Check that all of the supplied values for the gkno specific nodes are valid.
  gknoConfig.checkNodeValues(pipelineGraph, config)
  if isDebug: write.writeDebug('Checked node values')

  # All of the nodes have been checked to ensure that they have data assosciated if required, but this is
  # not always sufficient. In some cases, more than one pipeline argument can point to the same node. If
  # both pipeline arguments are required, but only one is set, the node being pointed to will have data, so
  # no error will be thrown. So, check that all pipeline arguments that are listed as required, have indeed
  # been set.
  config.checkArguments(pipelineGraph, commands, runName, instanceName)
  if isDebug: write.writeDebug('Checked arguments')

  # If the --export-config has been set, then the user is attempting to create a
  # new configuration file based on the selected tool/pipeline.  This can only be
  # if multiple runs are NOT being performed.
  if isExportInstance:
    if hasMultipleRuns: config.errors.exportInstanceForMultipleRuns()
    if isPipeline: config.exportInstance(pipelineGraph, pipelineConfigurationFilesPath, runName, isPipeline)
    else: config.exportInstance(pipelineGraph, toolConfigurationFilesPath, runName, isPipeline)
    config.nodeMethods.addValuesToGraphNode(pipelineGraph, 'GKNO-EXECUTE', [False], write = 'replace')
    generateMakefiles = False

  # Find the maximum number of datasets for each task.
  config.getNumberOfDataSets(pipelineGraph)
  if isDebug: write.writeDebug('Got number of data sets')

  # Identify file nodes that are streaming.
  config.identifyStreamingNodes(pipelineGraph)
  if isDebug: write.writeDebug('Streaming nodes identified')

  # Generate the makefiles.
  if generateMakefiles:

    # Generate the makefiles. The manner in which the files are created depend on whether there are
    # multiple runs (and so multiple makefiles) or a single tool/pipeline or an internal loop for
    # which there is only one makefile.
    if hasMultipleRuns: make.generateMultipleMakefiles(pipelineGraph, config, runName, sourcePath, gknoCommitID, outputPaths, __version__, __date__)
    else: make.generateSingleMakefile(pipelineGraph, config, runName, sourcePath, gknoCommitID, outputPaths, __version__, __date__)
    if isDebug: write.writeDebug('Generated makefiles')

    # If there are files required for the makefiles to run and they don't exist, write a warning to the
    # screen and ensure that the makefiles aren't executed.
    gknoConfig.writeMissingFiles(pipelineGraph, config, make.missingFiles)
    if isDebug: write.writeDebug('Missing files written')

    # Check for files/directories that cannot be present for the pipeline to run.
    gknoConfig.checkForDisallowedFiles(pipelineGraph, config, resourcePath)
    if isDebug: write.writeDebug('Disallowed files identified')

    # Check that all of the executable files exist.
    gknoConfig.checkExecutables(config, pipelineGraph, toolsPath)
    if isDebug: write.writeDebug('Executable check complete')

  # If the pipeline contains any isolated nodes, print a warning to screen.
  if hasIsolatedNode:
    errors.isolatedNodes(pipelineGraph, config, isolatedNodes)

    # Force the uset to acknowledge that the warning was read,
    doNotExecute   = config.nodeMethods.getGraphNodeAttribute(pipelineGraph, 'GKNO-DO-NOT-EXECUTE', 'values')[1][0]
    noHardWarnings = config.nodeMethods.getGraphNodeAttribute(pipelineGraph, 'GKNO-NO-HARD-WARNINGS', 'values')[1][0]
    if doNotExecute == 'unset' and noHardWarnings == 'unset': raw_input('Press Enter to continue...')
  if isDebug: write.writeDebug('Isolated nodes handled')

  # Check if a plotting the pipeline was requested. If so, check that a name for the output file was given and
  # draw the pipeline.
  if config.nodeMethods.getGraphNodeAttribute(pipelineGraph, 'GKNO-DRAW-PIPELINE', 'values'): gknoConfig.drawPipeline(pipelineGraph, config, draw)
  if isDebug: write.writeDebug('Plotting requirements fulfilled')

  # Having established the mode of operation and checked that the command lines are
  # valid etc., ping the website to log use of gkno.
  if config.nodeMethods.getGraphNodeAttribute(pipelineGraph, 'GKNO-DNL', 'values')[1][0] == 'unset':
    write.writeTracking(phoneHomeID)
    phoneHome(sourcePath, phoneHomeID)
    write.writeDone()
  write.writeBlankLine()

  # Execute the generated script unless the execute flag has been unset or if multiple runs were used.
  success = 0
  isExecute = config.nodeMethods.getGraphNodeAttribute(pipelineGraph, 'GKNO-DO-NOT-EXECUTE', 'values')[1][0]
  if not hasMultipleRuns and isExecute == 'unset':
    for phaseID in make.makefileNames:
      for iteration, makefileName in enumerate(make.makefileNames[phaseID]):

        # Check if the '--number-jobs' option is set.  If so, request this number of jobs.
        numberOfJobs = config.nodeMethods.getGraphNodeAttribute(pipelineGraph, 'GKNO-JOBS', 'values')[1][0]
        execute      = 'make -j ' + str(numberOfJobs) + ' --file ' + makefileName
        write.writeExecuting(execute)
        success = subprocess.call(execute.split())
        write.writeComplete(success)

  # If the makefile was succesfully run, finish gkno with the exit condition of 0.
  # If the makefile failed to run, finish with the exit condition 3.  A failure
  # prior to execution of the makefile uses the exit condition 2.
  if success == 0 and not make.missingFiles: exit(0)
  elif success == 0 and gknoConfig.hasMissingFiles: exit(4)
  else: exit(3)

if __name__ == "__main__":
  main()


import sqlite3

# Create db is does not exist, or just connect
conn = sqlite3.connect('example.db')

c = conn.cursor()

# Test Tables
# transactions: date, site, item_id, price
# purchases: date, site, item_id, item_group, count, unit_cost
# labour: date, site, employee_id, hours, rate
c.execute('''CREATE TABLE transactions
            (date text, site text, item_id text, price real)''')
c.execute('''CREATE TABLE purchases
            (date text, site text, item_id text, item_group text, count real, unit_cost real)''')
c.execute('''CREATE TABLE labour
            (date text, site text, emp_id text, hours real, rate real)''')

# Insert some test data
transactions = [('2019-07-31', 'Burnley', 'A10', 5),
                ('2019-07-31', 'Blackburn', 'A10', 5),
                ('2019-07-31', 'Blackburn', 'A15', 6),
                ('2019-08-01', 'Burnley', 'B10', 5),
                ('2019-08-01', 'Burnley', 'A12', 4),
                ('2019-08-01', 'Blackburn', 'A15', 5),
            ]
c.executemany('INSERT INTO transactions VALUES (?,?,?,?)', transactions)

purchases = [('2019-07-31', 'Blackburn', 'A15', 'drink', 10, 3),
             ('2019-07-31', 'Burnley', 'A10', 'drink', 25, 2),
             ('2019-08-01', 'Blackburn', 'A12', 'food', 4, 1.5),
             ('2019-08-01', 'Burnley', 'A15', 'drink', 12, 3),
            ]
c.executemany('INSERT INTO purchases VALUES (?,?,?,?,?,?)', purchases)

labour = [('2019-07-31', 'Blackburn', '001', 8, 7),
          ('2019-07-31', 'Burnley', '005', 6, 6),
          ('2019-08-01', 'Blackburn', '001', 8, 7),
          ('2019-08-01', 'Burnley', '009', 6, 8),
        ]
c.executemany('INSERT INTO labour VALUES (?,?,?,?,?)', labour)

conn.commit()
conn.close()


# -*- coding: utf-8 -*-
"""
Created on Mon Oct 19 14:06:09 2020

@author: rafaw
"""
#
import os

row_specifiers = ['X', 'Y', 'Z']
#correlating first coord [X, Y, Z] with numbers respectively[1, 2, 3]
cord_dict = {'X': 0, 'Y': 1, 'Z': 2}
current_user = 'user1'
user_names = {'user1': 'User 1', 'user2': 'User 2'}

def display_board():
    i=0
    print('      1 2 3\n')
    for row in board:
        print(f'{row_specifiers[i]}    |', end='')
        for column in board[i][:]:
            print(column, end='|')
        i += 1
        print('\n')

def clear():
    #print('\n'*100)
    print('\n'*100)

def user_input():
    choice = 'wrong'
    i = 0
    while choice not in ['X', 'O', 'x', 'o']:
        clear()
        if i > 0:
            print('You entered wrong input')
        if choice.isdigit():
            print('Why would you try to enter a number?')
        choice = input('User 1: Do you want to be X or O? Type X or O\n')
        i += 1

    #assign users' markers
    choice = choice.upper()
    if choice == 'X':
        marker_user2 = 'O'
    else:
        marker_user2 = 'X'

    #make a dictionary of markers
    d = {'user1': choice, 'user2': marker_user2}
    return d

def input_coordinates(board):
    first_coord = 'wrong'
    second_coord = 'wrong'
    i = 0
    clear()
    display_board()

    while True:
        choice = input(f'{user_names[current_user]}: Enter coordinates, e. g. X2\n')

        #wrong input handling
        #print warning when input error
        if i>0:
            print('You entered wrong input!')
        #check if input is in correct format
        if len(choice) == 2:
            if choice[0].isalpha():
                first_coord = choice[0].upper()
            if choice[1].isdigit():
                second_coord = int(choice[1])
            if first_coord not in row_specifiers or not choice[0].isalpha():
                print('First coordinate is wrong. The first character must be X, Y or Z')
            if second_coord not in range(1,4) or not choice[1].isdigit():
                print('Second coordinate is wrong. The second character must be an integer in range [1,3]')
        else:
            print('Wrong length of input: input must consist of 2 characters')

        #checking if spot is empty
        if (first_coord in row_specifiers and second_coord in [1,2,3]):
            if board[cord_dict[first_coord]][second_coord-1] == '-':
                break
            else:
                print('This place is already occupied')
                first_coord = 'wrong'
                second_coord = 'wrong'

        i += 1

    return (first_coord, second_coord)

def place_marker(board, marker, coordinates):

    #changing board
    board[cord_dict[coordinates[0]]][coordinates[1]-1] = marker

def win_check(board, mark):
    list_of_win_marks = [mark, mark, mark]

    #checking rows
    if list_of_win_marks in board:
        return True

    #checking collumns
    for i in range(0, len(board[0][:])):
        column = [board[0][i], board[1][i], board[2][i]]
        if column == list_of_win_marks:
            return True

    #checking diagonals
    diagonal1 = [board[0][0], board[1][1], board[2][2]]
    diagonal2 = [board[2][0], board[1][1], board[0][2]]
    if diagonal1 == list_of_win_marks or diagonal2 == list_of_win_marks:
        return True

    return False

def full_board_check():
    i=0
    for row in board:
        #searching every spot in a row
        for spot in board[i][:]:
            #return true if there is a spot left
            if spot == '-':
                return True
        i += 1

    return False

def replay():
    i = 0
    while True:
        if i>0:
            print('Wrong input!')
        choice = input('Do you want to play again? [Y/N]')
        if choice.isalpha():
            choice = choice.upper()
        if choice == 'Y':
            return True
        elif choice == 'N':
            return False
        i += 1

while True:
    i = 0
    clear()
    board = [['-','-','-'],\
             ['-','-','-'],\
             ['-','-','-']]
    current_user = 'user1'
    marker_dict = user_input()
    play = True

    while play:
        display_board()
        coordinates = input_coordinates(board)
        place_marker(board, marker_dict[current_user], coordinates)

        #stop if someone won
        if win_check(board, marker_dict[current_user]):
            clear()
            display_board()
            print(f"{user_names[current_user]} with marker '{marker_dict[current_user]}' won!")
            play = False

        #stop if there's no room on board
        if not full_board_check():
            clear()
            display_board()
            print("It's a tie!")
            play = False

        #switching the user
        if current_user == 'user1':
            current_user = 'user2'
        else:
            current_user = 'user1'

        if play:
            clear()

        i += 1
    if not replay():
        break


import sys
import os
import os.path
import concurrent.futures
from io import BytesIO

import tqdm

import interface
import db_cache

def try_load(filename, db):

	loaded = interface.try_load_file(filename)
	if loaded.known_file():
		db.insert_file(filename, internal_path='text', content=loaded.get_text())
		for image_idx, image in enumerate(loaded.get_images()):
			im_bytes = BytesIO()
			image.save(im_bytes, 'jpeg')

			db.insert_file(filename,
				internal_path = 'image_{}'.format(image_idx),
				content       = im_bytes.getvalue()
				)

	loaded = interface.try_load_file(filename)
	if loaded.known_file():
		loaded.get_text()
		loaded.get_images()

def scan_directory(path):
	items = []
	exts = {}

	cache = db_cache.DbInterface("files.db")
	with concurrent.futures.ThreadPoolExecutor() as tpe:
		for root, directories, files in tqdm.tqdm(os.walk(path)):
			for filen in files:
				if filen == "metadata.opf":
					continue
				if filen == "cover.jpg":
					continue

				if filen.lower().endswith(".jpg"):
					continue

				_, ext = os.path.splitext(filen)

				fqpath = os.path.join(root, filen)

				res = tpe.submit(try_load, fqpath, cache)
				items.append(res)

				exts.setdefault(ext, 0)
				exts[ext] += 1

				if sum(exts.values()) % 1000 == 0:
					print("Found extensions: ", exts)

			if len(items) > 1000:
				for item in tqdm.tqdm(items):
					item.result()

	print("Found extensions: ", exts)

def go():
	if len(sys.argv) != 2:
		print("No path!")
		sys.exit(1)

	if not os.path.exists(sys.argv[1]):
		print("Path '%s' doesn't exist!", sys.argv[1])
		sys.exit(2)

	scan_directory(sys.argv[1])

if __name__ == '__main__':
	import util.logSetup
	util.logSetup.initLogging()
	go()


import codecs
import os
import re
import shutil

diff_pattern = re.compile('@@ -\d+,\d+ \+\d+,(\d+) @@')

class FileFilter:
    def __init__(self, report_dir, lines_of_code, discard_file_name_list, ignore_case):
        self.report_dir = report_dir
        self.final_dir = os.path.join(self.report_dir, 'diff_final')
        self.LOC = lines_of_code
        self.discard_file_name_list = discard_file_name_list
        self.ignore_case = ignore_case
        self.discard_file_name_re = [re.compile('[\s\S]*{}[\s\S]*'.format(name), re.I if self.ignore_case else 0)
                                     for name in discard_file_name_list]
        if not os.path.exists(self.final_dir):
            os.makedirs(self.final_dir)

    def start_filtering(self):
        filter_folder = os.path.join(self.report_dir, 'diff_keyword_second')
        if not os.path.exists(filter_folder):
            filter_folder = os.path.join(self.report_dir, 'diff_keyword_first')
        if not os.path.exists(filter_folder):
            print('No diff_result record, filter diff_restrict? (y/n)')
            raw_input = input()
            while raw_input != 'y' or raw_input != 'n' or raw_input != 'yes' or raw_input != 'no':
                print('Wrong input, try again. (y/n)')
                raw_input = input()
            if raw_input == 'n' or raw_input == 'no':
                print('Goodbye!')
                exit(0)
            else:
                filter_folder = os.path.join(self.report_dir, 'diff_restrict')

        count = 0
        if not os.path.exists(filter_folder):
            print('No diff record!')
            exit(-1)

        for _, dirs, _ in os.walk(filter_folder):
            for commit_dir in dirs:
                for _, _, file_names in os.walk(os.path.join(filter_folder, commit_dir)):
                    file_list = []
                    file_names.remove('description.txt')
                    for diff in file_names:
                        result = None
                        for pattern in self.discard_file_name_re:
                            result = pattern.search(diff) if result is None else result
                        if result is None:  # no match
                            file_list.append(diff)

                    loc_list = []
                    for file in file_list:
                        with codecs.open(os.path.join(filter_folder, commit_dir, file), 'r', encoding='utf-8',
                                         errors='ignore') as f:
                            content = f.read()
                            diff_result = diff_pattern.findall(content)
                            loc = 0
                            for diff_loc in diff_result:
                                loc += int(diff_loc)
                            loc_list.append(loc)
                    if len(loc_list) == 0:
                        continue
                    if self.LOC < 0 or sorted(loc_list)[-1] <= self.LOC:
                        shutil.copytree(os.path.join(filter_folder, commit_dir),
                                        os.path.join(self.final_dir, commit_dir))
                        count += 1
                        print(count)


class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        dct = {}
        st = 0
        ret = 0
        for (ind, ch) in enumerate(s):
            last_pos = dct.get(ch, -1)
            dct[ch] = ind
            st = max(st, last_pos + 1)
            ret = max(ret, ind - st + 1)
        return max(ret, len(s) - st)


import os
import unittest

import numpy as np
import pandas as pd

import metafeatures.core.object_analyzer

class TestObjectAnalyzer(unittest.TestCase):

    def setUp(self):
        current_directory = os.path.dirname(__file__)
        data_path = os.path.join(current_directory,
                                 '../../datasets/weather_year.csv')
        self.data = pd.read_csv(data_path)

    def test_object_analyzer(self):
        data, metadata = metafeatures.core.object_analyzer.analyze_pd_dataframe(
            self.data, None)
        self.assertIsInstance(data, np.ndarray)
        self.assertEqual(data.dtype, np.float)
        self.assertIsInstance(metadata, dict)

    def test__normalize_pd_column_names(self):
        data = metafeatures.core.object_analyzer._normalize_pd_column_names(
            self.data)
        self.assertEqual(list(data.columns),
                         ['EDT', 'MaxTemperatureF', 'MeanTemperatureF',
                          'MinTemperatureF', 'MaxDewPointF', 'MeanDewPointF',
                          'MinDewpointF', 'MaxHumidity', 'MeanHumidity',
                          'MinHumidity', 'MaxSeaLevelPressureIn',
                          'MeanSeaLevelPressureIn', 'MinSeaLevelPressureIn',
                          'MaxVisibilityMiles', 'MeanVisibilityMiles',
                          'MinVisibilityMiles', 'MaxWindSpeedMPH',
                          'MeanWindSpeedMPH', 'MaxGustSpeedMPH',
                          'PrecipitationIn', 'CloudCover', 'Events',
                          'WindDirDegrees'])

    def test__get_pd_attribute_types(self):
        # Test for int
        data = pd.DataFrame([{'column1': 1, 'column2': 2},
                             {'column1': 3, 'column2': 4}])
        attributes = metafeatures.core.object_analyzer\
            ._get_pd_attribute_types(data, None)
        self.assertEqual(attributes, {0: {'type': 'numerical',
                                          'name': 'column1',
                                          'is_target': False},
                                      1: {'type': 'numerical',
                                          'name': 'column2',
                                          'is_target': False}})

        # Test for float
        data = pd.DataFrame([{'column1': 1.3, 'column2': 2.7},
                             {'column1': 3.2, 'column2': 3.9}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, None)
        self.assertEqual(attributes, {0: {'type': 'numerical',
                                          'name': 'column1',
                                          'is_target': False},
                                      1: {'type': 'numerical',
                                          'name': 'column2',
                                          'is_target': False}})

        # Test for object
        data = pd.DataFrame([{'column1': 1.3, 'column2': "Bla"},
                             {'column1': 3.2, 'column2': "Bla"}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, None)
        self.assertEqual(attributes, {0: {'type': 'numerical',
                                          'name': 'column1',
                                          'is_target': False},
                                      1: {'type': 'categorical',
                                          'name': 'column2',
                                          'is_target': False}})

    def test__get_pd_attribute_types_target_types(self):
        data = pd.DataFrame([{'column1': 1, 'column2': 2},
                             {'column1': 3, 'column2': 4}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, [0])
        print(attributes)
        self.assertTrue(attributes[0]['is_target'])
        self.assertFalse(attributes[1]['is_target'])

        data = pd.DataFrame([{'column1': 1, 'column2': 2},
                             {'column1': 3, 'column2': 4}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, 1)
        self.assertFalse(attributes[0]['is_target'])
        self.assertTrue(attributes[1]['is_target'])

        data = pd.DataFrame([{'column1': 1, 'column2': 2},
                             {'column1': 3, 'column2': 4}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, [])
        self.assertFalse(attributes[0]['is_target'])
        self.assertFalse(attributes[1]['is_target'])

        data = pd.DataFrame([{'column1': 1, 'column2': 2},
                             {'column1': 3, 'column2': 4}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, [0, 1])
        self.assertTrue(attributes[0]['is_target'])
        self.assertTrue(attributes[1]['is_target'])

        data = pd.DataFrame([{'column1': 1, 'column2': 2},
                             {'column1': 3, 'column2': 4}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, ['column1'])
        self.assertTrue(attributes[0]['is_target'])
        self.assertFalse(attributes[1]['is_target'])

        data = pd.DataFrame([{'column1': 1, 'column2': 2},
                             {'column1': 3, 'column2': 4}])
        attributes = metafeatures.core.object_analyzer \
            ._get_pd_attribute_types(data, 'column2')
        self.assertFalse(attributes[0]['is_target'])
        self.assertTrue(attributes[1]['is_target'])

    def test__replace_objects_by_integers(self):
        data = pd.DataFrame([{'column1': 1.3, 'column2': "Bla"},
                             {'column1': 3.2, 'column2': "Bla"},
                             {'column1': 2.7, 'column2': "Aha"}])
        data = metafeatures.core.object_analyzer \
            ._replace_objects_by_integers(data,
                                          {0: {'type': 'numerical',
                                               'name': 'column1',
                                               'is_target': False},
                                           1: {'type': 'categorical',
                                               'name': 'column2',
                                               'is_target': False}})
        print(data)
        self.assertEqual(data.dtypes[0], np.float)
        self.assertEqual(data.dtypes[1], np.float)
        np.testing.assert_allclose(data.iloc[:, 1], [0, 0, 1])


a, b = map(int, input().split())
x=[0]*(b+1)

#에라토스테네스의 체
def prime_list(n):
    s = [True] * n

    m = int(n ** 0.5)
    for i in range (2, m+1):
        if s[i] == True:
            for j in range (i+i,n,i):
                s[j] = False
    return [i for i in range (2, n) if s[i] == True]

def nFac(n):
    if n == 2:
        x[n] = 1
        return x[n]
    if n == 3:
        x[n] = 1
        return x[n]
    if n == 5:
        x[n] = 1
        return x[n]
    if n == 7:
        x[n] = 1
        return x[n]
    if x[n] != 0: return x[n]
    else:
        if n % 2 == 0:
            x[n] = nFac(n//2) + 1
        else:
            x[n] = nFac(n // 2)
    return x[n]

#소수만 판별
prime_b = prime_list(b)

for i in range (a, b+1):
    nFac(i)

print(x[a:b])
count = 0

for j in range (a, b+1):
    i = x[j]
    if  i!= 0 and i != 1 and i % 2 != 0:
        count += 1
    if i == 2:
        count +=1
print(count)


from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

from uvicorn import run

from app.errors import (
    NotFoundError,
    NoContentError,
    ValidationError,
    DatabaseError,
    BadRequestError,
)
from app import views

tags_metadata = [
    {
        "name": "phones",
        "description": "Операции с телефонами",
    },
]

app = FastAPI(
    title="Simple phonebook on FastAPI",
    version="1.0.0",
    contact={
        "name": "Igor Potemkin",
        "email": "ipotemkin@rambler.ru",
    },
    openapi_tags=tags_metadata,
    docs_url="/",
)

app.include_router(views.router)

@app.on_event("startup")
async def on_startup():
    pass

@app.on_event("shutdown")
async def on_shutdown():
    pass

# exception handlers
@app.exception_handler(404)
@app.exception_handler(NotFoundError)
def not_found_error(request: Request, exc: NotFoundError):
    return JSONResponse(status_code=404, content={"message": "Not Found"})

@app.exception_handler(NoContentError)
def no_content_error(request: Request, exc: NoContentError):
    return JSONResponse(status_code=204, content={"message": "No Content"})

@app.exception_handler(DatabaseError)
def database_error(request: Request, exc: DatabaseError):
    return JSONResponse(status_code=400, content={"message": "Database Error"})

@app.exception_handler(BadRequestError)
def bad_request_error(request: Request, exc: BadRequestError):
    return JSONResponse(status_code=400, content={"message": "Bad Request"})

@app.exception_handler(ValidationError)
def validation_error(request: Request, exc: ValidationError):
    return JSONResponse(status_code=400, content={"message": "Validation Error"})

if __name__ == "__main__":
    run("run:app", host="localhost", port=5000, reload=True)


import numpy as np
from sklearn.neighbors import KNeighborsRegressor
import matplotlib.pyplot as plt

n_dots = 40
X = 5 * np.random.randn(n_dots, 1)
y = np.cos(X).ravel()

# 加入噪音
y += 0.2 * np.random.rand(n_dots) - 0.1

# 训练模型
k = 5
knn = KNeighborsRegressor(k)
knn.fit(X, y)

# 生成足够密集的点并进行预测
T = np.linspace(0, 5, 500).reshape(-1, 1)
y_pred = knn.predict(T)
knn.score(X, y)

# 画出拟合曲线
plt.figure(figsize=(16, 10), dpi=200)
plt.scatter(X, y, c='g', label='data', s=100)
plt.plot(T, y_pred, c='k', label='prediction', lw=4)
plt.xticks()
plt.xlim(0, 10)
plt.axis('tight')
plt.title('KNeighborsRegressor(k={})'.format(k))
plt.show()