import timeit
import re
from math import sqrt

def get_top_side(tile):
    return tile[0]

def get_top_side_flipped(tile):
    return tile[0][::-1]

def get_right_side(tile):
    return ''.join([x[-1] for x in tile])

def get_right_side_flipped(tile):
    return ''.join([x[-1] for x in tile[::-1]])

def get_bottom_side(tile):
    return tile[-1]

def get_bottom_side_flipped(tile):
    return tile[-1][::-1]

def get_left_side(tile):
    return ''.join([x[0] for x in tile])

def get_left_side_flipped(tile):
    return ''.join([x[0] for x in tile[::-1]])

def get_number_of_matching_sides(tile1, tile2):
    tile1_sides = [ get_left_side(tile1), get_right_side(tile1), get_top_side(tile1), get_bottom_side(tile1) ]
    tile2_sides = [ get_left_side(tile2), get_left_side_flipped(tile2), get_right_side(tile2), get_right_side_flipped(tile2),
                    get_top_side(tile2), get_bottom_side(tile2), get_top_side_flipped(tile2), get_bottom_side_flipped(tile2) ]

    return len(set(tile1_sides) & set(tile2_sides))

def rotate_tile(tile):
    x = list(zip(*tile[::-1]))
    return [''.join(i) for i in x]

def flip_tile(tile):
    for i in range(len(tile)):
        tile[i] = tile[i][::-1]
    return tile

def is_horizontal_match(tile_left, tile_right):
    return get_right_side(tile_left) == get_left_side(tile_right)

def is_vertical_match(tile_top, tile_bottom):
    return get_bottom_side(tile_top) == get_top_side(tile_bottom)

def print_tile(tile):
    for t in tile:
        print(t)

def print_image(tiles, image):
    size = int(sqrt(len(tiles)))
    tile_size = len(tiles[image[0]][0])

    for x in range(size):
        for y in range(tile_size):
            for z in range(size):
                print(tiles[image[(z % size) + (x * size)]][y % tile_size], end = " ")
            print(" ")
        print()

def create_image_from_tiles(tiles, image):
    size = int(sqrt(len(tiles)))
    tile_size = len(tiles[image[0]][0])
    single_image = []

    for x in range(size):
        for y in range(tile_size):
            single_image.append("")
            for z in range(size):
                single_image[-1] += tiles[image[(z % size) + (x * size)]][y % tile_size]

    # print_tile(single_image)
    return single_image

def create_monster():
    monster = []
    monster.append("                  # ")
    monster.append("#    ##    ##    ###")
    monster.append(" #  #  #  #  #  #   ")

    return monster

def check_monster_on_coords(image, monster, x, y):
    match = True

    for i in range(x, x + len(monster)):
        for j in range(y, y + len(monster[0])):
            if ((i >= len(image[0]) or j>= len(image)) or
                (monster[i - x][j - y] == '#' and image[i][j] != '#')):
                match = False
                break

    if match:
        for i in range(len(image)):
            image[i] = list(image[i])

        for i in range(x, x + len(monster)):
            for j in range(y, y + len(monster[0])):
                if (monster[i - x][j - y] == '#' and image[i][j] == '#'):
                    image[i][j] = "0"

    return match

def find_monsters(single_image):
    found = False
    monsters = 0
    counter = 8
    hashes = 0
    monster = create_monster()

    while not found and counter > 0:
        if counter % 4:
            flip_tile(monster)
        else:
            rotate_tile(monster)

        for x in range(0, len(single_image[0])):
            for y in range(0, len(single_image)):
                is_a_match = check_monster_on_coords(single_image, monster, x, y)
                if is_a_match:
                    found = True
                    monsters += 1
                    # hashes = 0
                    # for i in range(len(single_image)):
                    #     hashes += single_image[i].count('#')

        counter -= 1

    for i in range(len(single_image)):
        hashes += single_image[i].count('#')

    print("Found %d monsters and %d hashes" % ( monsters, hashes))

def can_tile_squeeze_here(tiles, tk, image, position):
    size = int(sqrt(len(tiles)))
    pos_top = position - size
    pos_left = position - 1
    it_fits = False

    if position == 0:
        it_fits = True

    elif position == 1:
        rotations_current = 4
        rotations_neighbour = 4
        flips = 4

        for fl in range(flips):
            for rc in range(rotations_current):
                for rn in range(rotations_neighbour):
                    if is_horizontal_match(tiles[image[pos_left]], tiles[tk]):
                        return True
                    else:
                        tiles[image[pos_left]] = rotate_tile(tiles[image[pos_left]])

                if is_horizontal_match(tiles[image[pos_left]], tiles[tk]):
                    return True
                else:
                    tiles[tk] = rotate_tile(tiles[tk])

            if is_horizontal_match(tiles[image[pos_left]], tiles[tk]):
                return True
            elif fl % 2 == 0:
                tiles[tk] = flip_tile(tiles[tk])
            elif fl % 2 == 1:
                tiles[image[pos_left]] = flip_tile(tiles[image[pos_left]])

    else:
        rotations_current = 4
        flips = 2
        possible_positions = rotations_current * flips

        while not it_fits and possible_positions > 0:

            if ((pos_top < 0 and is_horizontal_match(tiles[image[pos_left]], tiles[tk])) or
                (position % size == 0 and is_vertical_match(tiles[image[pos_top]], tiles[tk]) or
                (pos_top > 0 and is_vertical_match(tiles[image[pos_top]], tiles[tk])) and is_horizontal_match(tiles[image[pos_left]], tiles[tk]))):
                    it_fits = True
            elif possible_positions % 4 == 0:
                tiles[tk] = flip_tile(tiles[tk])
            else:
                tiles[tk] = rotate_tile(tiles[tk])

            possible_positions -= 1

    return it_fits

def does_this_tile_go_in_a_corner(position, size):
    return (position == (size - size) or
            position == (size - 1) or
            position == ((size * size) - size) or
            position == ((size * size) - 1))

def sort_tiles(image, corners, tiles, position, used):
    size = sqrt(len(tiles))

    if (len(image) < len(tiles)):
        for tk in tiles.keys():
            if tk not in image and tk not in used[position] and can_tile_squeeze_here(tiles, tk, image, position):
                if ((does_this_tile_go_in_a_corner(position, size) and tk in corners) or
                    (not does_this_tile_go_in_a_corner(position, size) and not tk in corners)):
                    image.append(tk)
                    used[position].append(tk)
                    sort_tiles(image, corners, tiles, position + 1, used)

        if (len(image) < len(tiles) and position > 0):
            image.pop()
            position -= 1
            used[position] = []

def main():
    start = timeit.default_timer()
    with open('input.txt') as f: lines = f.read()

    tiles_list = lines.split('\n\n')
    tiles = {}
    used = {}
    corners = []
    image = []
    count = 0

    for t in tiles_list:
        tiles[int(re.search(r'\d+', t)[0])] = t.split(':')[1].strip().split('\n')

    for tk in tiles.keys():
        m = 0
        for tk_to_match in tiles.keys():
            if tk != tk_to_match:
                m += get_number_of_matching_sides(tiles[tk], tiles[tk_to_match])
        if m == 2:
            corners.append(tk)

    for i in range(len(tiles)):
        used[i] = []

    sort_tiles(image, corners, tiles, 0, used)

    for t in tiles.keys():
        tiles[t] = tiles[t][1:-1]
        for x in range(len(tiles[t])):
            tiles[t][x] = tiles[t][x][1:-1]

    find_monsters(create_image_from_tiles(tiles, image))

    stop = timeit.default_timer()
    print("Time : %.4f ms" % (1000 * (stop - start)))

if __name__ == '__main__':
    main()


def valid_pairs(pw):
    matching_digits = []
    for i in range(1, len(pw)):
        if pw[i - 1] is pw[i]:
            matching_digits.append(pw[i])
        if pw[i - 1] > pw[i]:
            return []
    return matching_digits

def solve_part_1():
    nbr_valid = 0
    for i in range(388888, 799999 + 1):
        if len(valid_pairs(str(i))) >= 1:
            nbr_valid += 1
    return nbr_valid

def solve_part_2():
    nbr_valid = 0
    for i in range(388888, 799999 + 1):
        pairs = valid_pairs(str(i))
        if len([d for d in pairs if pairs.count(d) == 1]) >= 1:
            nbr_valid += 1
    return nbr_valid

def main():
    sol1 = solve_part_1()
    print('Part 1: {}'.format(sol1))
    sol2 = solve_part_2()
    print('Part 2: {}'.format(sol2))

if __name__ == "__main__":
    main()


import copy
from screeninfo import get_monitors
import numpy as np
import torch
import torchvision
import argparse
import cv2
from models.TomatoNN import TomatoNN
from torchvision.transforms import transforms
from Config.DatasetConfig import width,hight

def disp(result_img):
    monitors = get_monitors()
    monitor = monitors[0]
    H = monitor.height
    W = monitor.width

    scale = min(H/result_img.shape[0], W/result_img.shape[1]) * 0.8
    result_img = cv2.resize(result_img, (int(result_img.shape[1]*scale), int(result_img.shape[0]*scale)))
    cv2.imshow("result", result_img)
    cv2.waitKey()
    cv2.destroyAllWindows()

if(__name__=="__main__"):
    parser = argparse.ArgumentParser()
    parser.add_argument("--model")
    parser.add_argument("--img_path")
    parser.add_argument("--save", default="my_beloved_tomato.png")
    parser.add_argument("--cuda", default=False, action='store_true')
    opt = parser.parse_args()

    model = TomatoNN()
    model.load_state_dict(torch.load(opt.model)["model"])
    model.eval()

    src_img = cv2.imread(opt.img_path)

    img = copy.deepcopy(src_img)
    img = cv2.resize(img, (width, hight))
    img_norm = cv2.normalize(img, None, 0, 1.0, cv2.NORM_MINMAX, cv2.CV_32F)
    # combined_norm_t = torch.from_numpy(combined_norm)
    transform_list_output = [transforms.ToTensor()]
    output_transform = transforms.Compose(transform_list_output)

    img_t = output_transform(img_norm)[None,:,:,:]

    if(torch.cuda.is_available() and opt.cuda):
        combined_norm_t = img_t.cuda()
        model = model.cuda()

    with torch.no_grad():
        output = model(img_t)

    output = output.cpu().detach().numpy().reshape(1).item()

    # visualization
    result_bar = np.zeros((int(src_img.shape[0]/5),src_img.shape[1],3), dtype=np.uint8)
    if(output < 0.3):
        text = "non-split"
    else:
        text = "split"

    result_bar = cv2.putText(result_bar, text, (int(src_img.shape[1]/5),int(result_bar.shape[0]/2)), fontFace=1, fontScale=int(src_img.shape[1]/100), thickness=int(src_img.shape[1]/100), color=(255,0,0))
    result_img = np.concatenate((src_img, result_bar), axis=0)
    cv2.imwrite(opt.save, result_img)
    disp(result_img)

    a = 0


import numpy as np
import joblib
import random

def get_datasets(filepath, *, test_percent=0.1):
    episodes = joblib.load(filepath)
    cut = int(len(episodes) * test_percent)
    assert cut > 1

    trainset = Dataset(episodes[cut:])
    testset = Dataset(episodes[:cut])

    return trainset, testset

class Dataset(object):
    def __init__(self, episodes):

        self._episodes = episodes
        self._size = len(self._episodes)

    @property
    def episodes(self):
        return self._episodes

    @property
    def size(self):
        return self._size

    def next_episode(self):
        """Return next episode examples from this data set."""

        i = random.randint(0, self._size - 1)

        images, labels = self._episodes[i]
        images = np.array(images).astype(float)
        labels = np.array(labels).astype(float)
        return images, labels

    def next_episode_batch(self, batch_size):
        batch_images = []
        batch_labels = []
        for _ in range(batch_size):
            images, labels = self.next_episode()
            batch_images.append(images)
            batch_labels.append(labels)
        return batch_images, batch_labels


import networkx as nx
import matplotlib.pyplot as plt
from collections import defaultdict
import greedy
import dwave.inspector
# Size of the grid
size = (3,4)

lagrangian_objective = 1
# lagrangien structure
lagrangian_struct = 5
lagrangian_struct_start = lagrangian_struct
lagrangian_struct_end = lagrangian_struct
lagrangian_struct_path = lagrangian_struct

#convertion tuple coordonné en int index
def coord_to_index(coord):
    return coord[0] + coord[1]*size[0]

#convertion int index en tuple coordonné
def index_to_coord(index):
    return (index % size[0], index // size[0])

#affiche coordonné avec la position minimal sur x et maximal sur y, ça donne juste un graphe en longueur
def unique_edge(left_node_index, right_node_index): #prend un tuple en entré
    return (min(left_node_index, right_node_index), max(left_node_index, right_node_index)) #retourne un tuple

#n'est pas utilisé, remet le bord inférieur toujours a gauche de la liaison de G2 (composé de deux tuples)
def unique_edge_edge(edge1, edge2):
    if (edge1[0] < edge2[0]):
        return (edge1, edge2)
    elif (edge1[0] == edge2[0]):
        if (edge1[1] < edge2[1]):
            return (edge1, edge2)
        else:
            return (edge2, edge1)
    else:
        return (edge2, edge1)

#convertion coord en index vrai que si x : min et y : max
assert (unique_edge(3,2)) == index_to_coord(coord_to_index(unique_edge(3,2)))

# Start and end node
start_point = (1,1)
end_point = (2,3)

start_point_index = coord_to_index(start_point) #4
end_point_index = coord_to_index(end_point)#11

for j in range(0, size[1]):      #for(int j = 0, j < 4, i++)
    for i in range(0, size[0]):  #for(int i = 0, i < 3, i++)
        if (i, j) == start_point:
            print("S", end="")
        elif (i, j) == end_point:
            print("E", end="")
        else:
            print(".", end="")
    print("")

#fonction appliquée a G2
def plot_graph(graph, filename="graph_plot.png", path=[]):
    pos = nx.spring_layout(graph)
    nx.draw_networkx_nodes(graph, pos, node_color='grey')   #color tous les points en gris
    #ne sert a rien car seul les points de G1 sont caractérisés par un index
    #nx.draw_networkx_nodes(graph, pos, nodelist=[node for node in graph.nodes if node == coord_to_index(start_point[0], start_point[1])], node_color='red')
    #nx.draw_networkx_nodes(graph, pos, nodelist=[node for node in graph.nodes if node == coord_to_index(end_point[0], end_point[1])], node_color='black')

    #dessine en rouge la liste de point tel que la coordonné x du point ou y soit egale au coordonné du point start
    # start_point_index est le point de G1 qui commence, relié par 4 liaison devenu des points dans G2, il y a donc 4 points ayant la coordonnée de départ
    nx.draw_networkx_nodes(graph, pos, nodelist=[node for node in G2.nodes if node[0] == start_point_index or node[1] == start_point_index], node_color='r')
    #dessine en cyan la liste de point tel que la coordonné x du point ou y soit egale au coordonné du point end
    #de la même manière, il y a deux points ayant les coordonnées d'arrivé.
    nx.draw_networkx_nodes(graph, pos, nodelist=[node for node in G2.nodes if node[0] == end_point_index or node[1] == end_point_index], node_color='c')

    if len(path) > 0:
        nx.draw_networkx_nodes(graph, pos, nodelist=path, node_color='g') #tous les points du chemin sont colorié en vert
    nx.draw_networkx_edges(graph, pos) #dessine les liaisons

    #nx.draw_networkx_labels(graph, pos)

    plt.show()  #affiche le graphe
    #plt.savefig(filename)

# Initial graph
G1 = nx.Graph()
#les points de G1 ne servent a rien dans le programme
G1.add_nodes_from([(coord_to_index((i, j)), {"x":i,"y":j}) for i in range (0, size[0]) for j in range(0, size[1])])

#tous les points sont reliés sur l'axe x
for i in range(0, size[0]-1):
    G1.add_edges_from([unique_edge(coord_to_index((i, j)), coord_to_index((i+1, j))) for j in range(0, size[1])])

#tous les points sont reliés sur l'axe y
for j in range(0, size[1]-1):
    G1.add_edges_from([unique_edge(coord_to_index((i, j)), coord_to_index((i, j+1))) for i in range(0, size[0])])

G2 = nx.Graph()
#les chemins de G1 deviennent les points de G2
G2.add_nodes_from([unique_edge(u, v) for (u, v) in G1.edges])
#si deux coté de G1 sont relié par un point, alors on créé une liaison de G2 reliant les deux liaisons de G1 (devenu des points de G2)
for (u, v) in G1.edges:
    for (u_i, v_i) in G1.edges:
        if ((u in [u_i, v_i] or v in [u_i, v_i])):  #concordance d'une extremité entre deux liaisons
            G2.add_edge(unique_edge(u, v), unique_edge(u_i, v_i))

plot_graph(G2)

Q = defaultdict(int)
nodes_from_start = [node for node in G2.nodes if node[0] == start_point_index or node[1] == start_point_index]
nodes_from_end = [node for node in G2.nodes if node[0] == end_point_index or node[1] == end_point_index]

# QUBO trouvée : SUM(xii) - SUM(x)

# ajout des noeuds dans le graphe
print('-' * 60 + "TOUTES LES VALEURS BINAIRES DU GRAPHE" + '-' * 60)
#Constraint (1)
for node in G2.nodes:
    Q[(node, node)] = 1 * lagrangian_objective
    print("les noeuds ", (node, node), " ont comme valeur ", Q[(node, node)])

# contrainte sur le départ
print('-' * 60 + "TOUTES LES VALEURS DE DEPART DU GRAPHE" + '-' * 60)
# CONSTRAINT (2)
for node in nodes_from_start:
    Q[(node, node)] += -1 * lagrangian_struct_start
    print("les noeuds ", (node, node), " ont comme valeur ", Q[(node, node)])
    for node2 in nodes_from_start:
        if (node != node2):
            Q[(node, node2)] += 1 * lagrangian_struct_start
            print("les noeuds ", (node, node2), " ont comme valeur ", Q[(node, node2)])

# contrainte sur l'arrivée
print('-' * 60 + "TOUTES LES VALEURS D'ARRIVE DU GRAPHE" + '-' * 60)
# CONSTRAINT (2)
for node in nodes_from_end:
    Q[(node, node)] += -1 * lagrangian_struct_end
    print("les noeuds ", (node, node), " ont comme valeur ", Q[(node, node)])
    for node2 in nodes_from_end:
        if (node != node2):
            Q[(node, node2)] += 1 * lagrangian_struct_end
            print("les noeuds ", (node, node2), " ont comme valeur ", Q[(node, node2)])

print('-' * 60 + "TOUS LES POINTS ENTRANTS" + '-' * 60)
for node in G2.nodes:
    # arcs dits entrants
    #point voisin dont leur coordonnée partage une coordonnée entrante node[0] du point G2 qui correspond a l'extrémité de la liaison de G1 par lequel on entre
    entring_node = [enode for enode in G2.adj[node] if enode[0] == node[0] or enode[1] == node[0]]
    print ("Les points entrants de ", node," sont ", entring_node)

print('-' * 60 + "TOUS LES POINTS SORTANTS" + '-' * 60)
for node in G2.nodes:
    #point voisin dont leur coordonnée partage une coordonnée sortante node[0] du point G2 qui correspond au point de la liaison de G1 sortant
    exiting_node = [enode for enode in G2.adj[node] if enode[0] == node[1] or enode[1] == node[1]]
    print ("Les points sorants de ", node," sont ", exiting_node)

    ## contrainte sur les arcs de passage, représentés par des noeuds dans G2
    # On vérifie que le nombre total de voisins est bien égal à la somme des sortants et entrants
    #assert G2.degree[node] == len(entring_node) + len(exiting_node)

print('-' * 60 + "VALEURS DES POINTS ENTRANTS" + '-' * 60)
for node in G2.nodes:
    # Maintenant on définit les contraintes :
    # Sum_{entrants}(xi) + Sum_{sortants}(xi) + 2*Sum_{entrants,j>i}(xi*xj) + 2*Sum_{sortants,j>i}(xi*xj) - 2*Sum_{entrants}(xi)*Sum_{sortants}(xi)
    # Somme des xi entrants
    # CONSTRAINT (2)
    for enode in entring_node:
        Q[(enode, enode)] += 1 * lagrangian_struct_path
for enode in entring_node:
    print("valeur points entrant :", (enode, enode), Q[(enode, enode)], "pour la valeur des noeurs entrants : ", entring_node)

print('-' * 60 + "VALEURS DES POINTS SORTANTS" + '-' * 60)
for node in G2.nodes:
    # Somme des xi sortants
    # CONSTRAINT (2)
    for enode in exiting_node:
        Q[(enode, enode)] += 1 * lagrangian_struct_path
for enode in exiting_node:
    print("valeur points sortants :", (enode, enode), Q[(enode, enode)])

print('-' * 60 + "VALEURS DES ARCS ENTRANTS" + '-' * 60)
for node in G2.nodes:
    # 2*Sum_{entrants,j>i}(xi*xj)
    # CONSTRAINT (2)
    for enode in entring_node:
        for enode2 in entring_node: # Avec la double boucle on compte deux fois, donc on divise le coeff par 2
            if (enode != enode2):
                Q[(enode, enode2)] += 1 * lagrangian_struct_path
for enode in entring_node:
    for enode2 in entring_node:
        if (enode != enode2):
            print("valeur arcs entrant :", (enode, enode2), Q[(enode, enode2)])
print('-' * 60 + "VALEURS DES ARCS SORTANTS" + '-' * 60)
for node in G2.nodes:
    # 2*Sum_{sortants,j>i}(xi*xj)
    # CONSTRAINT (2)
    for enode in exiting_node:
        for enode2 in exiting_node: # Avec la double boucle on compte deux fois, donc on divise le coeff par 2
            if (enode != enode2):
                Q[(enode, enode2)] += 1 * lagrangian_struct_path
for enode in exiting_node:
        for enode2 in exiting_node: # Avec la double boucle on compte deux fois, donc on divise le coeff par 2
            if (enode != enode2):
                print("valeur arcs sortant :", (enode, enode2), Q[(enode, enode2)])
print('-' * 60 + "VALEURS DES ARCS ENTRANTS ET SORTANTS D'UN MÊME POINT" + '-' * 60)
for node in G2.nodes:
    # -2*Sum_{entrants}(xi)*Sum_{sortants}(xi)
    # CONSTRAINT (2)
    for enode in entring_node:
        for enode2 in exiting_node:
            Q[(enode, enode2)] += -2 * lagrangian_struct_path
for enode in entring_node:
        for enode2 in exiting_node:
            print("valeur des points entrants et sortants d'une liaison :", (enode, enode2), Q[(enode, enode2)])

for node in G2.nodes:
    ## Contrainte sur le nombre d'arcs sortants
    for i in range(0, len(exiting_node)-1):
        for j in range(i+1, len(exiting_node)):
            Q[(exiting_node[i], exiting_node[j])] += 1 * lagrangian_struct_path

#MANQUE CONSTRAINT (3)

for i in range(0, len(exiting_node)-1):
        for j in range(i+1, len(exiting_node)):
            print (Q[(exiting_node[i], exiting_node[j])])

print(Q)
#plot_graph(G2)

solver = greedy.SteepestDescentSolver()
sampleset = solver.sample_qubo(Q, num_reads=100)

print(sampleset)

# On affiche les trois premiers chemins
print(sampleset.variables)
for i in range(0, 3):
    sample = sampleset.record.sample[i]
    print("sample:")
    print(sample)
    path = [sampleset.variables[i] for i in range(0, len(sample)) if sample[i] == 1]
    print("path: " + str(path))
    plot_graph(G2, path=path)

    graph = [[0 for x in range(size[1])] for y in range(size[0]) ]
    for node in path:
        coord0 = index_to_coord(node[0])
        coord1 = index_to_coord(node[1])
        graph[coord0[0]][coord0[1]] = 1
        graph[coord1[0]][coord1[1]] = 1

    for j in range(0, size[1]):
        for i in range(0, size[0]):
            if (i, j) == start_point:
                print("S", end="")
            elif (i, j) == end_point:
                print("E", end="")
            elif graph[i][j] == 1:
                print("X", end="")
            else:
                print(".", end="")
        print("")


# 1
# https://leetcode.com/problems/01-matrix/

# https://leetcode.com/problems/01-matrix/discuss/1369741/C%2B%2BJavaPython-BFS-DP-solutions-with-Picture-Clean-and-Concise-O(1)-Space
# 그래도 조금 끄적여봣다가 포기...그래도 30분은 끄적여보면서 이것저것 시도..
# 패턴 조금 안듯...
# 문제부터 이해하는데 오래 걸렸다...또르르
from collections import deque
from typing import List
class Solution:
    def updateMatrix(self, mat: List[List[int]]) -> List[List[int]]:
        m, n = len(mat), len(mat[0])
        DIR = [0, 1, 0, -1, 0]

        q = deque([])
        for r in range(m):
            for c in range(n):
                if mat[r][c] == 0:
                    q.append((r, c))
                else:
                    mat[r][c] = -1  # Marked as not processed yet!

        while q:
            r, c = q.popleft()
            for i in range(4):
                nr, nc = r + DIR[i], c + DIR[i + 1]
                if nr < 0 or nr == m or nc < 0 or nc == n or mat[nr][nc] != -1: continue
                mat[nr][nc] = mat[r][c] + 1
                q.append((nr, nc))
        return mat

Leetcode Algorithms/Day9 (BFS,DFS)/542. 01 Matrix.py


import pytest

from src.auth import auth_register_v2
from src.channel import channel_addowner_v1
from src.channels import channels_create_v2
from src.other import clear_v2
from src.error import InputError, AccessError

# InputError when any of:

#         Channel ID is not a valid channel
#         When user with user id u_id is already an owner of the channel

# AccessError when the authorised user is not an owner of the **Dreams**, or an owner of this channel

# Parameters:(token, channel_id, u_id)
# Return Type:{}

# channel_addowner_v1()

@pytest.fixture
def reg_user():
    def _register_user(num):
        return auth_register_v2(f"example{num}@email.com", f"Password{num}", f"firstname{num}", f"lastname{num}")
    return _register_user

@pytest.fixture
def create_channel():
    def _create_channel(token):
        return channels_create_v2(token, 'channel_1', True)
    return _create_channel

# invaile token
def test_invalid_token(reg_user, create_channel):

    clear_v2()
    new_user = reg_user(0)
    user_id =  new_user['auth_user_id']
    user_token = new_user['token']
    channel_id = create_channel(user_token)['channel_id']

    with pytest.raises(AccessError):
        channel_addowner_v1("invalid_token", channel_id, user_id)

#         Channel ID is not a valid channel
def test_invalid_channel(reg_user, create_channel):

    clear_v2()
    new_user = reg_user(0)
    user_token = new_user['token']
    user_id =  new_user['auth_user_id']
    channel_id = create_channel(user_token)['channel_id']

    with pytest.raises(InputError):
        channel_addowner_v1(user_token, channel_id + 1, user_id)

#         When user with user id u_id is already an owner of the channel
def test_already_owner(reg_user, create_channel):

    clear_v2()
    first_user = reg_user(0)
    second_user = reg_user(1)
    first_token = first_user['token']
    second_id =  second_user['auth_user_id']

    channel_id = create_channel(first_token)['channel_id']
    channel_addowner_v1(first_token, channel_id, second_id)

    with pytest.raises(InputError):
        channel_addowner_v1(first_token, channel_id, second_id)

#         when the authorised user is not an owner of the **Dreams**, or an owner of this channel
def test_sender_non_channel_member(reg_user, create_channel):

    clear_v2()

    # first user is the owner of the **Dreams** as well as the owner of this channel, but the second user is neither of them
    first_user = reg_user(0)
    second_user = reg_user(1)
    third_user = reg_user(2)

    first_id = first_user['auth_user_id']
    second_token =  second_user['token']
    third_token = third_user['token']

    channel_id = create_channel(third_token)['channel_id']

    with pytest.raises(AccessError):
        channel_addowner_v1(second_token, channel_id, first_id)


from setuptools import setup, find_packages

with open("README.md", "r") as fh:
    long_description = fh.read()

setup(
    name="gptbase",
    version="0.3.0",
    author="callmexss",
    author_email="callmexss@126.com",
    description="A package for simplified interaction with OpenAI's GPT-3 and GPT-4 models.",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/callmexss/gptbase",
    packages=find_packages(include=["gptbase"]),
    install_requires=[
        "openai==1.1.1",
        "rich==13.0.1",
        "tiktoken==0.5.1",
        "click==8.0.3",
        "iso-639==0.4.5",
    ],
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires=">=3.10",
    entry_points={
        "console_scripts": [
            "gptbase-chat=gptbase.chat:chat",
        ],
    },
)


import sys
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn.utils.rnn import pad_packed_sequence, pack_padded_sequence
import numpy as np
from model import *
from configuration import get_config
import torch.optim as optim
from tensorboardX import SummaryWriter
import time
from dataloader import TextDataLoader
import os
import math
from evaluate import evaluate
from torch.optim.lr_scheduler import StepLR
from utils import result2dict
import csv
from torch import distributed, nn
from torch.utils.data.distributed import DistributedSampler
from trainer import Trainer

class Pretrainer(Trainer):
    def __init__(self, args, model, device, optimizer,
                        scheduler, writer, text_loader, epoch, monitor_loss, dataset_order, total_dataset_num):
        self.args = args
        self.model = model
        self.device = device
        self.epoch = epoch
        self.monitor_loss = monitor_loss
        self.optimizer = optimizer
        self.scheduler = scheduler
        self.writer = writer
        self.text_loader = text_loader
        self.dataset_order = dataset_order
        self.total_dataset_num = total_dataset_num

    def train_epoch(self):
        self.scheduler.step()
        # assert type(self.model).__name__ == "pretrained"
        self.monitor_loss = 0
        for i, (word, word_len, embedding) in enumerate(self.text_loader):
            word = word.to(self.device)
            embedding = embedding.to(self.device)
            self.optimizer.zero_grad()
            loss = self.model(word, word_len, embedding)
            loss.backward()
            if not self.args.model_name == 'sgns':
                torch.nn.utils.clip_grad_norm_(self.model.parameters(), self.args.clip)
            if self.args.multi_node:
                self.average_gradients()
                print('average gradient')
            self.optimizer.step()
            self.monitor_loss += loss.item()
            if i % self.args.log_frequency == 0:
                print('Train dataset: {} [{}/{} ({:.0f}%)] Loss: {:.8f}'.format(
                    (self.epoch), i * self.args.batch_size, len(self.text_loader.dataset),
                                          100. * i / len(self.text_loader),
                                          loss / self.args.batch_size))
                if self.args.dataset == "wiki_dump/":
                    step = i // self.args.log_frequency + math.ceil(
                        self.total_dataset_num // self.args.batch_size // self.args.log_frequency)
                else:
                    step = i // self.args.log_frequency + self.epoch * len(self.text_loader) // self.args.log_frequency
                self.writer.add_scalar('Batch loss', loss / self.args.batch_size, step)
                # plot_embedding(args, model, text_loader, writer, device)
        if self.args.evaluation:
            pass
        return self.monitor_loss

def train(args):
    device = args.device
    text_loader = PretrainedDataLoader(args.data_dir, args.batch_size, args.is_ngram)
    # TODO : make pretrained model class in model.py
    model = pretrained_test(args.vocab_size, args.char_embed_size, args.hidden_size,
                       args.num_layer, args.dropout, args.mlp_size, args.embed_size, args.neg_sample_size, args.bidirectional,
                       args.multigpu, args.device, args.model_category, args.is_attn, args.attn_size)

    model= model.to(device)
    print("made model")
    optimizer = optim.Adam(model.parameters(), lr=args.lr)
    scheduler = StepLR(optimizer, step_size=10, gamma=0.9)

    if args.load_model_code is not None:
        pass
    writer = SummaryWriter(args.log_dir + 'pretrain' + args.timestamp + '_' + args.config)
    train_loss = 0
    trainer = Pretrainer(args, model, device, optimizer, scheduler, writer,
                         text_loader, epoch=0, monitor_loss=0, dataset_order=0, total_dataset_num=0)

    for epoch in range(args.epochs):
        trainer.monitor_loss = 0
        trainer.epoch = epoch
        start_time = time.time()
        monitor_loss = trainer.train_epoch()
        print('====> Epoch: {} Average loss: {:.4f} / Time: {:.4f}'.format(
            (epoch), monitor_loss / len(text_loader.dataset), time.time() - start_time))
        trainer.writer.add_scalar('Train loss', monitor_loss/ len(text_loader.dataset), epoch)
        #if epoch % 10 == 0:
        #    plot_embedding(args, model, text_loader, device, epoch, writer)
        torch.save(model.state_dict(), args.log_dir + 'pretrain' + args.timestamp + '_' + args.config + '/' +'model.pt')
        print("Model saved")
        if train_loss > monitor_loss:
            torch.save(model.state_dict(), args.log_dir + 'pretrain'+ args.timestamp + '_' + args.config + '/model_best.pt')
            print("Best model saved")
            train_loss = monitor_loss
        trainer.writer.add_scalar('Epoch time', time.time() - start_time, epoch)

if __name__ == "__main__":
    train(get_config())


import os.path

from BubotObj.OcfDevice.subtype.Device.Device import Device

class AuthService(Device):
    file = __file__
    template = False

    def add_route(self, app):
        path_device = os.path.dirname(self.file)
        app.router.add_static(f'/{self.__class__.__name__}/ui/', f'{path_device}/static/ui')
        app.router.add_static(f'/{self.__class__.__name__}/i18n/', f'{self.path}/i18n')


from machine import Pin
from time import sleep

# GPIO16 (D0) is the internal LED for NodeMCU
led_blue = Pin(2, Pin.OUT)
led_red = Pin(16, Pin.OUT)

# The internal LED turn on when the pin is LOW
while True:
    led_blue.value(0)
    led_red.value(0)
    sleep(1)
    led_blue.value(1)
    led_red.value(1)
    sleep(1)


import itertools
import ete3
from msa_functions import get_msa_from_file, count_substitutions
from utils import compute_entropy, lists_diff
from definitions import *
from ete3 import Tree
import ete3.coretype.tree
import numpy as np

def get_newick_tree(tree):
	"""
	:param tree: newick tree string or txt file containing one tree
	:return:	tree: a string of the tree in ete3.Tree format
	"""
	if type(tree) == str:
		if os.path.exists(tree):
			with open(tree, 'r') as tree_fpr:
				tree = tree_fpr.read().strip()
		tree = Tree(tree, format=1)
	return tree

def rescale_tree_branch_lengths(tree, factor):
	"""
	:param tree: newick tree string or txt file containing one tree
	:param factor: the factor by which to multiply all branch lengths in tree
	:return:	reformatted_tree: a string of the scaled tree in Newick format
	"""
	tree = get_newick_tree(tree)
	tree_root = tree.get_tree_root()
	for node in tree_root.iter_descendants(): # the root dist is 1.0, we don't want it
		node.dist = node.dist * factor
	return tree.write(format=1, dist_formatter="%.10f")

def reroot_tree(tree, outgroup_name):
	tree = get_newick_tree(tree)
	tree.set_outgroup(tree & outgroup_name)
	return tree

def scale_tree_to_length(tree, target_dist, outgroup_name=None, scaling_method="tbl"):
	"""
	:param tree:  newick tree string or txt file containing one tree OR ete3.Tree object
	:param target_dist: numeric, the desired total tree distance
	:param outgroup_name: the name of a tree node if tree needs to be rooted,
	                      otherwise would calculate the distance from the inferred root (acoorsding to the newick order)
	:param scaling_method: "height" for longest distance from root to leaf, "tbl" for total branch lengths
	:return: a newick string of the rescaled tree
	"""
	t = get_newick_tree(tree)

	if outgroup_name: # re-root tree
		t = reroot_tree(t, outgroup_name)
	root = t.get_tree_root()

	if scaling_method.lower() == "tbl":
		dist = get_total_branch_lengths(root)
	elif scaling_method.lower() == "height":
		dist = get_tree_height(root)

	scaling_factor = target_dist / dist
	rescaled_tree = rescale_tree_branch_lengths(tree, scaling_factor)

	return rescaled_tree

def get_tree_height(tree_root):
	"""
	:param tree_root: ete3 node; because we traverse only its descendants
	(its dist is 1.0, we don't want it)
	:return: longest distance from root to leaf
	"""
	# the two configurations are the same - I compared! (Shiran)
	# current_length = 0
	# for leaf in tree_root:
	# 	current_length = max(current_length, tree_root.get_distance(leaf))
	return tree_root.get_farthest_leaf()[1]

def get_frac_of_cherries(tree):
	"""
	McKenzie, Andy, and Mike Steel. "Distributions of cherries for two models of trees."
	 Mathematical biosciences 164.1 (2000): 81-92.
	:param tree:
	:return:
	"""
	tree = get_newick_tree(tree)
	tree_root = tree.get_tree_root()
	leaves = list(tree_root.iter_leaves())
	cherries_cnt = 0
	for leaf1, leaf2 in itertools.combinations(leaves, 2):
		if leaf1.up is leaf2.up:
			cherries_cnt += 1
	return 2*cherries_cnt/len(leaves)

def get_leaves_branches(tree):
	"""
	:param tree:
	:return: a list of pendant edges lengths, i.e., the brnches that lead to leaves
	"""
	tree = get_newick_tree(tree)
	tree_root = tree.get_tree_root()
	leaves_bl = []
	for node in tree_root.iter_leaves():
		leaves_bl.append(node.dist)
	return leaves_bl

def get_sackin_index(tree):
	"""
	:param tree: ete3 tree; not rerooted!
	:return: sackin_index
	formula Sackin: https://academic.oup.com/sysbio/article/21/2/225/1713481
	"""
	node_depth_dict = {}
	sackin_index_lst = []
	for node in tree.traverse(strategy="levelorder"):
		if node.is_root():
			node_depth_dict[node] = 1
		else:
			node_depth_dict[node] = node_depth_dict[node.up] + 1

		if node.is_leaf():
			sackin_index_lst.append(node_depth_dict[node])

	return np.mean(sackin_index_lst)

def get_colless_index(tree):
	"""
	:param tree: ete3 tree; not rerooted!
	:return: colless_index
	formula Colless: https://onlinelibrary.wiley.com/doi/epdf/10.1111/j.1558-5646.1992.tb01171.x
	"""
	ndes_dict = {}
	colless_index = 0
	ntaxa = 0
	for node in tree.traverse(strategy="postorder"):
		if node.is_leaf():
			ndes_dict[node] = 1
			ntaxa += 1
		else:
			ndes_dict[node] = ndes_dict[node.children[0]] + ndes_dict[node.children[1]]
			colless_index += abs(ndes_dict[node.children[0]] - ndes_dict[node.children[1]])

	return colless_index/float((ntaxa-1)*(ntaxa-2)/2)

def get_stemminess_indexes(tree):
	"""
		:param tree: ete3 tree; not rerooting!
		:return: cumulative stemminess index Fiala and Sokal 1985
				 noncumulative stemminess index Rohlf 1990
		formula cumulative stemminess: https://onlinelibrary.wiley.com/doi/pdf/10.1111/j.1558-5646.1985.tb00398.x
		formula noncumulative stemminess: https://onlinelibrary.wiley.com/doi/epdf/10.1111/j.1558-5646.1990.tb03855.x
		"""
	subtree_blsum_dict = {}
	nodes_height_dict = {}
	stem85_index_lst = []
	stem90_index_lst = []
	for node in tree.traverse(strategy="postorder"):
		if node.is_leaf():
			subtree_blsum_dict[node] = 0
			nodes_height_dict[node] = 0
		elif node.is_root():
			continue
		else:
			subtree_blsum_dict[node] = subtree_blsum_dict[node.children[0]] + subtree_blsum_dict[node.children[1]] + \
			                           node.children[0].dist + node.children[1].dist
			nodes_height_dict[node] = max(nodes_height_dict[node.children[0]] + node.children[0].dist,
			                              nodes_height_dict[node.children[1]] + node.children[1].dist)
			stem85_index_lst.append(node.dist/(subtree_blsum_dict[node] + node.dist))
			stem90_index_lst.append(node.dist/(nodes_height_dict[node]) + node.dist)

	return np.mean(stem85_index_lst), np.mean(stem90_index_lst)

def pendant_to_internal_bl_ratio(tree):
	"""
	:param tree:
	:return: sort of stemminess index, i.e., how long are the inner branches compared to the external ones
	"""
	tree = get_newick_tree(tree)
	pendant_bl_sum = np.sum(get_leaves_branches(tree))
	internal_bl_sum = get_total_branch_lengths(tree) - pendant_bl_sum

	if pendant_bl_sum == 0:
		return 0
	if internal_bl_sum == 0:
		return 100000
	return pendant_bl_sum/float(internal_bl_sum)

def get_branch_lengths(tree):
	"""
	:param tree: Tree node or tree file or newick tree string;
	:return: total branch lengths
	"""
	# TBL
	tree = get_newick_tree(tree)
	tree_root = tree.get_tree_root()
	branches = []
	for node in tree_root.iter_descendants(): # the root dist is 1.0, we don't want it
		branches.append(node.dist)
	return branches

def get_branch_lengths_estimates(tree):
	"""
	:param tree: Tree node or tree file or newick tree string;
	:return:
	"""
	# TBL
	branches = get_branch_lengths(tree)
	entropy = compute_entropy(branches)

	return max(branches), min(branches), np.mean(branches), np.std(branches), entropy

def get_diameters_estimates(tree_filepath, actual_bl=True):
	"""
	if not actual_bl - function changes the tree! send only filepath
	:param tree_filepath: tree file or newick tree string;
	:param actual_bl: True to sum actual dists, False for num of branches
	:return: min, max, mean, and std of tree diameters
	"""
	# tree = copy.deepcopy(get_newick_tree(tree)) # do not deepcopy! when trees are large it exceeds recursion depth
	if not actual_bl:
		assert isinstance(tree_filepath, str)
	tree = get_newick_tree(tree_filepath)
	tree_root = tree.get_tree_root()
	if not actual_bl:
		for node in tree_root.iter_descendants():
			node.dist = 1.0
	tree_diams = []
	leaves = list(tree_root.iter_leaves())
	for leaf1, leaf2 in itertools.combinations(leaves, 2):
		tree_diams.append(leaf1.get_distance(leaf2))
	entropy = compute_entropy(tree_diams)

	return max(tree_diams), min(tree_diams), np.mean(tree_diams), np.std(tree_diams), entropy

def get_distance_from_tips(tree):
	"""
	:param tree: Tree node or tree file or newick tree string;
	:return: mean and std of terminal branches
	"""
	tree = get_newick_tree(tree)
	tree_root = tree.get_tree_root()
	terminal_branches = []
	for leaf in tree_root.iter_leaves():
		terminal_branches.append(leaf.dist)
	return np.mean(terminal_branches), np.std(terminal_branches)

def get_total_branch_lengths(tree):
	"""
	:param tree: Tree node or tree file or newick tree string;
	:return: total branch lengths
	"""
	tree = get_newick_tree(tree) #if it's a file
	branches = get_branch_lengths(tree)
	return sum(branches)

def rename_ids(tree_str, conversion_dict):
	"""
	:param tree_str:  Newick format tree string
	:param conversion_dict: {current_id: new_id}
	:return: an updated tree string
	"""
	for sp in conversion_dict:
		tree_str = re.sub(sp + ":", conversion_dict[sp] + ":", tree_str)
	return tree_str

def calc_branch_length(tree):
	branch_lengths = get_branch_lengths(tree)
	N = len(branch_lengths)
	total_BL = sum(branch_lengths)
	mean_BL = total_BL/N

	return total_BL, mean_BL

def assign_simulated_seqs_to_nodes(tree, msa_file, ancestral_seqs_file):
	"""
	:param tree: tree file or ete3 tree
	:param msa_file: sequences names should match tree leaves
	:param ancestral_seqs_file: sequences names should match tree inner nodes + root
	:return: ete3 tree, with feature "seq" inside each node
	To extract this value, use: node.seq
	"""
	tree = get_newick_tree(tree)
	msa = get_msa_from_file(msa_file)

	for rec in msa:
		rec_name = rec.id
		(tree & rec_name).add_feature("seq", rec._seq)
	for line in open(ancestral_seqs_file):
		rec = line.strip().split("\t")
		(tree & rec[0]).add_feature("seq", rec[1])

	return tree

def assign_simulated_seqs_to_indelible_tree(sim_dir, simulated_msa_filename):
	"""
	:param sim_dir: the directory in which indelible was simulated
	:param simulated_msa_filename: the name of the simulated msa file (to extract the simulated msa + ancestral seqs)
	:return: ete3 tree, with feature "seq" inside each node
	To extract this value, use: node.seq
	"""
	sim_msa_file = sim_dir + simulated_msa_filename
	trees_file = sim_dir + "trees.txt"
	ancestral_seqs_file = sim_dir + simulated_msa_filename[:-4] + "_ANCESTRAL.phy"
	tree = indelible_executions.get_indelible_tree_from_treesfile(trees_file)

	return assign_simulated_seqs_to_nodes(tree, sim_msa_file, ancestral_seqs_file)

def copy_nodes_names_from_similar_topology(ref_topology, tree):
	tree = get_newick_tree(tree)
	ref_topology = get_newick_tree(ref_topology)
	for node in tree.traverse("postorder"):
		if node.is_leaf():
			continue
		children = node.children
		node_in_ref = (ref_topology & children[0].name).up

		# assert that nodes in ref and in tree have the same children
		ref_children = node_in_ref.children
		assert ".".join(sorted([child.name for child in children])) == ".".join(sorted([child.name for child in ref_children]))

		node.name = node_in_ref.name
	return tree

def get_cumulative_num_of_substitutions(tree_with_seqs):
	"""
	:param tree_with_seqs: ete3 tree in which every node is assigned with a 'seq' feature
	:return: cumulative number of sequences along the tree (sum over average number of substitutions per branch)
	 over nucleotides and codons substitutions
	"""
	cum_differences_nuc = 0
	cum_differences_codon = 0

	for node in tree_with_seqs.traverse("postorder"):
		if node.name != "ROOT":
			seq1 = node.seq
			seq2 = node.up.seq
			cum_differences_nuc += count_substitutions(seq1, seq2, is_codons=False)
			try:
				cum_differences_codon += count_substitutions(seq1, seq2, is_codons=True)
			except AssertionError:
				cum_differences_codon = -1
		# bl_codons = node.dist

	return cum_differences_nuc, cum_differences_codon

def convert_hexadec_branches_to_decimal(tree_str):
	reformatted_number = re.compile(r'\d+[\.\d+]*e\-\d+') # reformat small numbers
	for number in reformatted_number.findall(tree_str):
		reformatted_num = str("{0:.10f}".format(float(number)))
		tree_str = tree_str.replace(number, reformatted_num, 1)

	return tree_str

def get_internal_and_external_leaves_relative_to_subroot(tree_root, subroot):
	all_leaves = tree_root.get_leaves()
	subtree_leaves = subroot.get_leaves()
	other_leaves = lists_diff(all_leaves, subtree_leaves)
	return subtree_leaves, other_leaves

def prune_tree_at_node(tree, node_as_subroot):
	assert(isinstance(node_as_subroot, ete3.TreeNode))
	subtree_leaves, other_leaves = get_internal_and_external_leaves_relative_to_subroot(tree, node_as_subroot)
	t1 = tree.prune(subtree_leaves, preserve_branch_length=True)
	t2 = tree.prune(other_leaves, preserve_branch_length=True)
	return t1, t2

def get_largest_branch(tree):
	tree = get_newick_tree(tree)
	tree_nodes = list(tree.traverse("levelorder"))
	max_bl_node = max(tree_nodes, key=lambda node: node.dist)
	return max_bl_node


import datetime
from dataclasses import asdict
from dataclasses import dataclass
from dataclasses import field
from enum import Enum
from typing import List
from typing import Union

try:
    from typing import Literal
except ImportError:
    from typing_extensions import Literal

from pglet import BarChart
from pglet import Checkbox
from pglet import Dialog
from pglet import Form
from pglet import SpinButton
from pglet import Stack
from pglet import Text
from pglet.barchart import Point
from pydantic import BaseModel
from pydantic import EmailStr
from pydantic import Field

try:
    from replit import db
except ImportError:
    db = dict()

class Content:

    def introduction(self):
        """
        # Easy web development for Python developers

        Web technologies are available everywhere, and they seem like a good way to create a graphical user interface
        for your Python application, either on the desktop or on the web. Unfortunately, most Python coders like
        Python, and would like to avoid using a lot of other languages (HTML, Javascript and CSS) just to get a UI
        created.

        Pglet ("pagelet") is a web server that is written in Go and uses Fluent UI React components. Actual applications
        ("clients") provide the content and react to events using a proprietary protocol.

        None of the above is visible to a Python developer, since the server comes nicely bundled in the client install.
        All you need is:

        `pip install pglet`

        and you are off to creating a web-enabled UI in pure Python (3.7+).

        As a taste of what pglet-python code looks like, this is the code for the box on the right:

        [code]
        ```
        pglet.page().add(main_view)
        ```

        As another example, this site has also been created with pglet-python, with no HTML and no CSS in the code.

        For more details, supported controls and a tutorial, see the [pglet Python docs](https://pglet.io/docs/).

        # Easy forms

        One of the most-repeated type of UI is some kind of form for entering and updating information. While creating
        forms is easy in pglet, it is nevertheless a task that provides little programming joy. The next pages
        show how to create forms using a Form control that eats annotated Python classes, for example, Python's
        [dataclasses](https://docs.python.org/3/library/dataclasses.html).

        # Easy forms with validation

        Typically you also need to somehow validate user input before you can use it for anything: you need to check
        that necessary values have been provided, check that numbers are numbers, check dates etc.

        You can avoid this repetitive code by giving the Form control a [pydantic](https://pydantic-docs.helpmanual.io/)
        object. Validation defined on the object is performed before the data is returned to you. In some cases,
        you can use the exact same data definition with your APIs (e.g. FastAPI) or when writing to a document or SQL
        data store.
        """

        from pglet import Image, Stack, Text

        main_view = Stack(
            horizontal_align="center",
            padding=20, gap=20,
            controls=[
                Image(width="50%", src="https://www.python.org/static/img/python-logo@2x.png"),
                Image(width="30%", src="https://pglet.io/img/logo_dark.svg"),
                Text(value="pydantic", bold=True, size="xxLarge"),
            ],
        )

        return main_view

    def data_first_forms(self):
        """
        Using the Form control, form definition focuses on the data you need out of it, so we define a class with
        necessary information as attributes, information types as annotations, and default values as assignements.

        Python dataclasses are convenient for this, as we do not need to spend time creating the `__init__` and other
        boilerplate.

        To create the form, all we need to do is give the class definition to the Form control:

        [code]

        You can see the resulting form on the right. Change values, and click "OK" to see the data you would get.

        Form control understands the following data types explicitly, others will be by default be represented with a
        basic text box on the form:
        - str (see later for how you can decide between single line and multiline controls for text)
        - int
        - float
        - bool
        - datetime
        - date (current DatePicker timezone restrictions prevent using it here)
        - time
        - Decimal (current SpinButton restrictions prevent using it here)
        """

        @dataclass
        class DataclassDataModel:
            name: str = "Dataclass Person"
            birthdate: datetime.datetime = "2000-01-01"
            address: str = "Some Street 1, Some Town, Some Country"
            age: int = 33
            happy_today: bool = True
            email: str = "some@email.com"

        def show_data_on_submit(event):
            event.control.page.add(Dialog(
                open=True,
                title="Submitted data",
                blocking=True,
                controls=[
                    Text(value=str(event.control.value.__dict__))
                ]
            ))

        form = Form(
            value=DataclassDataModel,
            width=500,
            on_submit=show_data_on_submit
        )

        return form

    data_first_forms.display_name = "Data-first forms"

    def selecting_values(self):
        """
        Python enums are supported for selecting from a specific set of values.

        [code]
        """
        class ContactOption(str, Enum):
            EMAIL = 'email'
            PHONE = 'phone'

        @dataclass
        class DataclassDataModel:
            name: str = "Dataclass Person"
            ok_to_contact: bool = True
            contact_option: ContactOption = ContactOption.EMAIL

        return Form(value=DataclassDataModel, width=500, on_submit=show_submitted_data)

    def more_values(self):
        """
        If there are more than 3 values, we switch to a dropdown. The threshold is configurable with the
        `threshold_for_dropdown` Form attribute.
        """
        class ContactOption(str, Enum):
            EMAIL = 'email'
            PHONE = 'phone'
            MESSAGE = 'message'
            DOVE = 'dove'

        @dataclass
        class DataclassDataModel:
            name: str = "Dataclass Person"
            ok_to_contact: bool = True
            contact_option: ContactOption = ContactOption.EMAIL

        return Form(value=DataclassDataModel, width=500, on_submit=show_submitted_data)

    def selecting_multiple_values(self):
        """
        Annotating a field with a **list** of enums allows for multiple selection.

        [code]
        """

        class ContactOption(str, Enum):
            EMAIL = "email"
            PHONE = "phone"
            PIGEON = "pigeon"
            SMOKE_SIGNALS = "smoke signals"

        @dataclass
        class DataclassDataModel:
            contact_option: List[ContactOption] = field(
                default_factory=lambda: [ContactOption.EMAIL]
            )

        return Form(value=DataclassDataModel, width=500, on_submit=show_submitted_data)

    def lists_of_fields(self):
        """
        Field with a list annotation of other type than an enum is turned into a list control.
        """

        @dataclass
        class DataclassDataModel:
            alphabet: List[str] = field(
                default_factory=lambda: ["a", "b", "c"]
            )

        return Form(value=DataclassDataModel, width=500, on_submit=show_submitted_data)

    def nested_class_definitions(self):
        """
        Often we need to reuse parts of data structures. Form control supports nested class definitions, like `Movie`
        in the example below.

        [code]
        """
        @dataclass
        class Movie:
            title: str
            year: int

        @dataclass
        class DataclassDataModel:
            name: str = "Dataclass Person"
            email: str = "some@email.com"
            favorite_movie: Movie = Movie(
                title="My Little Pony: The Movie",
                year=2017,
            )

        return Form(value=DataclassDataModel, width=500, on_submit=show_submitted_data)

    def several_nested_objects(self):
        """
        Several nested objects are supported with a List annotation. Lists rely on a sensible `str()` implementation to
        display nicely, open up in a separate panel for editing, and support adding and deleting items.

        [code]

        Note that to support adding more movies, we need to provide default values for Movie attributes.
        """
        @dataclass
        class Movie:
            title: str = ""
            year: int = 2000

            def __str__(self):
                return f"{self.title} ({self.year})"

        movies = [
            Movie(title="The Name of the Rose", year=1986),
            Movie(title="My Little Pony: The Movie", year=2017),
        ]

        @dataclass
        class DataclassDataModel:
            name: str = "Dataclass Person"
            email: str = "some@email.com"
            favorite_movies: List[Movie] = field(default_factory=lambda: movies)

        return Form(value=DataclassDataModel, width=500, on_submit=show_submitted_data)

    def styling_and_dimensions(self):
        """
        Form is a Stack control, and inherits all the
        [attributes of Stacks](https://pglet.io/docs/controls/stack#properties).

        You can toggle the switch on the left to experiment with the light and dark themes.

        Example below shows using:
        - `title` to add a form title at the top,
        - `control_style` to define an alternative general style, with underlined text boxes,
        - `toggle_for_bool` to use a toggle instead of a checkbox for boolean values, and
        - a standard Stack attribute `gap` to add extra space between the lines.

        [code]
        """

        @dataclass
        class DataclassDataModel:
            name: str = "Dataclass Person"
            birthdate: datetime.date = "2000-01-01"
            address: str = "Some Street 1, Some Town, Some Country"
            age: int = 33
            happy_today: bool = True
            email: str = "some@email.com"

        form = Form(
            value=DataclassDataModel,
            title="Your information",
            control_style="line",
            toggle_for_bool=True,
            gap=24,
            width=500,
            on_submit=show_submitted_data,
        )

        return form

    def customizing_controls(self):
        """
        There are several ways to customize controls in a `Form`:

        1. Additional parameters for a specific field, as part of the data definition
        2. Additional parameters for a specific field, in an `__init__` parameter
        3. Map a data type to a specific control, for a single form
        4. Map a data type to a specific control, globally

        Example below covers the first 3:

        1. For dataclasses, the contents of the pglet metadata dictionary is passed as parameters for the control.
           Here we turn a single-line Textbox into a multiline one.
        2. If you do not want to mix your data model with UI specifics, you can pass a parameter to the Form,
           containing extra control initialization kwargs by field name.
        3. If you want to set all fields of a type to be mapped to a specific control, provide additional type to
           control mappings to Form. Here we want all `Amount`s have two decimals in the UI.

        [code]

        For option #4, you can set a type/control mapping globally by updating the mapping in the `Form` class directly,
        like this:

        ```
        Form.default_data_to_control_mapping["Amount"] = partial(
            SpinButton, step=.01,
        )
        ```

        ... or, of course, by subclassing the Form and setting values in the subclass `__init__`.
        """
        from dataclasses import field
        from functools import partial
        from typing import NewType

        Amount = NewType('Amount', float)

        @dataclass
        class DataclassDataModel:
            item: str = field(
                default="",
                metadata={"pglet": {
                    "multiline": True  # <<< 1
                }}
            )
            description: str = ""
            amount: Amount = Amount(0)

        form = Form(
            value=DataclassDataModel,
            control_kwargs={
                'description': {'multiline': True}  # <<< 2
            },
            control_mapping={
                "Amount": partial(  # <<< 3
                    SpinButton, step=.01,
                )
            },
            width=500,
            on_submit=show_submitted_data,
        )

        return form

    def introducing_pydantic(self):
        """
        Pydantic is not a dependency of pglet nor of the Form control, so you need to install it separately with:

        ```
        pip install pydantic
        ```

        If you need validation of email addresses, you should also install:

        ```
        pip install pydantic[email]
        ```

        Once you have pydantic, your data can inherit from `pydantic.BaseModel` instead of being decorated as a `dataclass`.

        [code]

        Next pages will cover the benefits of having pydantic in place.
        """
        from pydantic import BaseModel

        class PydanticDataModel(BaseModel):
            name: str = "Pydantic Person"
            email: str = "some@email.com"

        return Form(value=PydanticDataModel(), width=500, on_submit=show_submitted_data)

    def change_the_labels(self):
        """
        Normally, the labels on each line derived from the attribute names in your data. If you need something
        different, for example punctuation, use `Field.title`.

        [code]

        See pydantic docs for the full documentation on the
        [Field function](https://pydantic-docs.helpmanual.io/usage/schema/#field-customization).
        """
        from pydantic import Field

        class PydanticDataModel(BaseModel):
            name: str = "Pydantic Person"
            happy: bool = Field(True, title="Are you happy today?")

        return Form(value=PydanticDataModel(), width=500, on_submit=show_submitted_data)

    def placeholders(self):
        """
        If an input does not have text, a placeholder is shown, defined by `Field.description`.
        """
        class PydanticDataModel(BaseModel):
            name: str = "Pydantic Person"
            email: str = Field("", description="Enter a valid email address")

        return Form(value=PydanticDataModel(), width=500, on_submit=show_submitted_data)

    def customizing_pydantic_fields(self):
        """
        Pydantic equivalent of the `dataclasses.field.metadata["pglet"]` is `Field.pglet`.

        Here we use this option for the `notes` attribute to get a multiline `Textbox`.

        [code]
        """
        class PydanticDataModel(BaseModel):
            name: str = "Pydantic Person"
            notes: str = Field("", pglet={'multiline': True})

        return Form(value=PydanticDataModel(), width=500, on_submit=show_submitted_data)

    def validation(self):
        """
        Main benefit of adding pydantic is that get your data validated with the minimum of boilerplate code.

        Experiment with this view to see how the validation works.

        [code]

        Errors are pydantic validation errors, in English.

        Check pydantic documentation on the available
        [validating field types](https://pydantic-docs.helpmanual.io/usage/types/) and their usage.

        Form control currently maps the field types below to specific controls - everything else is a Textbox.

        - ConstrainedDecimal
        - ConstrainedFloat
        - ConstrainedInt
        - EmailStr
        - FutureDate
        - NegativeFloat
        - NegativeInt
        - PastDate
        - PositiveFloat
        - PositiveInt
        - StrictBool
        - StrictFloat
        - StrictInt
        """
        from pydantic import conint
        from pydantic import EmailStr

        class PydanticDataModel(BaseModel):
            name: str = "Pydantic Person"
            birthdate: datetime.date = "2000-01-01"
            age: conint(ge=0, lt=150) = 0
            email: EmailStr = Field("", description="Enter a valid email address")

        return Form(value=PydanticDataModel(), width=500, on_submit=show_submitted_data)

    def cross_field_validation(self):
        """
        Pydantic supports defining more complex relationships between fields.

        In this example, email must be filled (and a valid email) if newsletters have been requested.

        If validator returns an error, the capitalised str value of the error is shown to the user as the error
        message under the field. Pydantic standard validation errors are in English.

        [code]

        Again, pydantic docs contain a lot more information about
        [validators](https://pydantic-docs.helpmanual.io/usage/validators/).
        """
        from pydantic import validator

        class PydanticDataModel(BaseModel):
            name: str = "Pydantic Person"
            newsletter_ok: bool = Field(
                False, title="Send me the monthly newsletter"
            )
            email: Union[EmailStr, Literal[""]] = Field("", description="Valid email needed for newsletter")

            @validator('email', pre=True, allow_reuse=True)
            def email_filled_if_needed(cls, value, values):
                if values.get("newsletter_ok") and not value:
                    raise ValueError("Need email for newsletter")
                return value

        return Form(value=PydanticDataModel(), width=500, on_submit=show_submitted_data)

    cross_field_validation.display_name = "Cross-field validation"

    def status(self):
        """
        The status of the Form control is: **early Proof of Concept for discussion and feedback**

        *Some* todo items remain.

        [no code]
        """
        todo = '''
        Ordering of lists
        Slider option for number ranges
        Proper Documentation
        Responsive layout
        Align/integrate with Grid control
        Dates with DatePicker
        Manage decimal.Decimal values
        '''

        done = '''
        Lists for basic types
        Toggle as an alternative for Checkbox
        Support custom control parameters (e.g. multiline)
        Support customising controls for types
        Multiple selection from enums
        Some tests
        Documentation in the shape of this demo
        '''

        return Stack(
            padding=20,
            width=400,
            controls=[
                Checkbox(label=label.strip(), value=False, disabled=True)
                for label in todo.strip().splitlines()
            ] + [
                Checkbox(label=label.strip(), value=True, disabled=True)
                for label in done.strip().splitlines()
            ]
        )

    def grande_finale(self):
        """
        As one last thing, let's combine the Form control, replit database utility and the pglet graph control into a
        quick poll.

        Please select the options you are interested in and click "OK".

        [code]

        The `db` object here is a replit database that is essentially a dict with per-program persistent contents.
        """
        @dataclass
        class PollData:
            pglet: bool = False
            python: bool = False
            pglet_with_python: bool = False
            pglet_with_some_other_language: bool = False
            pglet_with_forms: bool = False
            pydantic: bool = False
            pydantic_form_validation: bool = False

        chart = BarChart(
            data_mode='fraction',
            padding=20,
            width=210,
            points=[]
        )

        def update_chart():
            chart.points.clear()
            values = list(reversed(sorted(
                [
                    (field, db.get(field, 0))
                    for field in PollData.__annotations__
                    if field != "answers"
                ],
                key=lambda value: value[1]
            )))
            max_value = values[0][1]
            for field, value in values:
                display_name = field.replace("_", " ").capitalize()
                chart.points.append(
                    Point(legend=display_name, x=value, y=max_value),
                )
        update_chart()

        poll = Form(
            value=PollData,
            title="I am interested in...",
            width=300,
            label_width="100%",
            control_width="fit-content",
        )

        def update_db_values(event):
            value = event.control.value
            for key, value in asdict(value).items():
                if value:
                    db[key] = db.get("key", 0) + 1
            db["answers"] = db.get("answers", 0) + 1

            update_chart()
            poll.submit_button.disabled = True
            event.control.page.update()

        poll.on_submit = update_db_values

        stack = Stack(controls=[poll, chart])

        return stack

    grande_finale.display_name = "Grande Finale"

def show_submitted_data(event):
    value = event.control.value
    event.control.page.add(Dialog(open=True, title="Submitted data", blocking=True, controls=[
        Text(value=str(value.__dict__))
    ]))

content = [value for attribute, value in Content.__dict__.items() if callable(value)]


# Import the necessary libraries
import cv2 # pip install opencv-python
import openai # '$ pip install openai'
import urllib.request # pip install urllib3
import uuid
import time

openai.api_key = "YOUR_API_KEY_HERE"
# YOU NEED AN API KEY FROM OPENAI.COM FOR THIS TO WORK

'''
Inter-Dimensional (or parallel universe, whatever) Camera
rb@failmail.club Dec 2022

This is a camera that takes pictures from an alternate, parallel reality.

The concept here is that this camera that takes photos, but not normal photos.
It takes photos not from the real world, but from a neighboring dimension (or the
next parallel universe over, if you prefer.)

That's why the photos it takes are always... different.  It's not imaging what's in front
of the lens, it's imaging whatever is in front of the lens FROM ONE DIMENSION OVER.

That's also why the viewfinder is just lines. The camera is always seeing across dimensions,
but only dimly.  When you take a photo, it pierces the veil and gets a high-resolution color
shot (of whatever is in the other dimension) instead of just a dim preview.  Beware that
for the brief moment one can see into this other reality, one can also be seen...

Technically, this program will:
1. open an attached webcam
2. Display a preview video feed (filtered through opencv edge detection for the lines effect)
3. Wait for a keypress: SPACE (take a picture) or Q (for quit)
4. Freeze frame -> take picture -> send picture to openai DALL-E API -> receive AI-generated picture
5. Display AI-generated image in viewfinder
6. Wait for any keypress
7. GOTO 1

The program also saves the original and the AI-generated version of every snapshot
in the 'log' directory for a user's review and amusement.
'''

# Functions
# Exit now
def bail(s):
    from os import sys
    if s: print(s)
    sys.exit(0)

# Print an ASCII text banner
def banner(s):
    import pyfiglet
    result = pyfiglet.figlet_format(s) # , font="slant")
    print(result)

print("Inter-dimensional Camera v0.1")
banner("We Have Such Sights To Show You")
print("calibrating... please wait\n\n")

# Start capturing video from an attached camera
# and display it preview-style modified by opencv canny edge detection

# Import the necessary modules
import cv2

# Use the VideoCapture() function to create a video capture object
# for the attached USB webcam
cap = cv2.VideoCapture(0)

# Use the set() method of the video capture object to set the
# width and height of the preview window
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 512)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 512)

# Use the namedWindow() function to create a window for the preview
cv2.namedWindow("Parallel Universe Preview")

print("YOUR OPTIONS INCLUDE")
print("--------------------")
print("Space = My mind is strong; take an interdimensional photo!")
print("    q = Spare me these twisted visions!\n")

# For canny edge detection filter
#import necessary libraries
import numpy as np

while True:
    # Use the read() method of the video capture object to capture
    # the next frame from the webcam
    _, frame = cap.read()

    # Use the imshow() function to display the frame in the preview window
    #cv2.imshow("Preview", frame) # This displays the camera feed

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    #edges = cv2.Canny(gray, 50, 150, apertureSize = 3)
    edges = cv2.Canny(gray, 150, 216, apertureSize = 3)

    # This displays the edge detection filter version
    cv2.imshow("Parallel Universe Preview", edges)

    # Check if the user pressed the "q" key
    # p.s. waitKey(0) will display the window indefinitely until any keypress (suitable for showing images)
    key = cv2.waitKey(1)
    if key == ord("q"): # Quit
        break
    if key == ord(" "):
        # Generate a UUID for the file
        file_uuid = uuid.uuid4()

        # Get the current timestamp
        timestamp = time.strftime("%Y%m%d%H%M%S")

        # Create the file name by combining the UUID and timestamp
        OG_file_name = F"log\{file_uuid}_{timestamp}_0_og.png"
        AI_file_name = F"log\{file_uuid}_{timestamp}_1_ai.jpg"

        print(F"Taking photo of dimension #{file_uuid}...")
        print("(There is no going back)")

        #cv2.destroyAllWindows()
        #cv2.destroyWindow("Parallel Universe Preview")

        # Define the video capture object
        video_capture = cv2.VideoCapture(0)

        # Check if the webcam is opened correctly
        if not video_capture.isOpened():
            raise IOError("Cannot open webcam")

        # Capture a single frame from the webcam
        ret, frame = video_capture.read()

        # crop the image using array slices -- it's a NumPy array
        # after all!
        resized_frame = frame[80:576, 0:480] # the stop # is a stop sign, you stop before it not after it
        resized_frame = cv2.resize(resized_frame, (512,512))

        # Save the resized frame to a file
        cv2.imwrite(OG_file_name, resized_frame)

        # Release the video capture object
        video_capture.release()

        print(F"Transmitting image from dimension #{file_uuid}...")

        response = openai.Image.create_variation(
          image=open(OG_file_name, "rb"),
          n=1,
          size="512x512"
        )
        image_url = response['data'][0]['url']

        # Define the URL of the image to download
        image_url = image_url

        # Open the URL and download the image
        response = urllib.request.urlopen(image_url)
        image_data = response.read()

        print("Parting the veil...")

        # Save the downloaded image to a file
        with open(AI_file_name, "wb") as f:
            f.write(image_data)

        # Read the image file into a variable
        image = cv2.imread(AI_file_name)

        # Check if the image was successfully loaded
        if image is None:
            raise IOError("Failed to load the image")

        # Display the image in a window
        cv2.imshow("Parallel Universe Preview", image)

        print("Press any key to go on...")

        # Wait for the user to press any key to close the window
        cv2.waitKey(0)

        print("Re-calibrating, please wait...")

        # Use the VideoCapture() function to create a video capture object
        # for the attached USB webcam
        cap = cv2.VideoCapture(0)

        # Use the set() method of the video capture object to set the
        # width and height of the preview window
        cap.set(cv2.CAP_PROP_FRAME_WIDTH, 512)
        cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 512)

        # Use the namedWindow() function to create a window for the preview
        #cv2.namedWindow("Parallel Universe Preview")

# Use the release() method of the video capture object to release
# the webcam and close the preview window
cap.release()
cv2.destroyAllWindows()

bail("desynchronizing...\ndone")


'''
Andrew ID: wzhao3

Code for the two types of starships, enemies and the player.
'''

import pygame
import math

from Classes import objects

def distance(x1, x2, y1, y2):
    distance = ((x1 - x2)**2 + (y1 - y2)**2)**0.5
    return distance

#Main ship class for player and enemy ships
class Ship(pygame.sprite.Sprite):
    def __init__(self, x, y, layout, connections):
        super().__init__()
        self.commandModuleX = x
        self.commandModuleY = y
        self.angle = 0
        self.layout = layout
        self.connections = connections
        self.laserGroup = pygame.sprite.Group()
        self.rocketGroup = pygame.sprite.Group()
        self.initItemsGroup()

    #Adds all items in layout to personal item group
    def initItemsGroup(self):
        self.itemsGroup = pygame.sprite.Group()
        for row in self.layout:
            for item in row:
                if item != None:
                    self.itemsGroup.add(item)

    def draw(self, screen, data):
        for item in self.itemsGroup:
            item.draw(screen, data)

    def doRotate(self, item):
        xDistance = item.coord[0] - self.commandModuleX
        yDistance = -(item.coord[1] - self.commandModuleY)
        angle = math.degrees(math.atan2((yDistance), (xDistance)))

        diagonal = distance(self.commandModuleX, item.coord[0],
                            self.commandModuleY, item.coord[1])
        dx = diagonal*math.cos(math.radians(self.angle + angle))
        dy = -diagonal*math.sin(math.radians(self.angle + angle))

        item.image = pygame.transform.rotate(item.baseImage, self.angle)
        item.x = self.commandModuleX + dx
        item.y = self.commandModuleY + dy

    def checkCollisions(self, item, data):
        if len(pygame.sprite.spritecollide(item,
                data.obstacleGroup, False)) > 0:
            return True

        if isinstance(self, PlayerShip):
            for enemy in data.enemiesGroup:
                if len(pygame.sprite.spritecollide(item,
                        enemy.itemsGroup, False)) > 0:
                    return True

        if isinstance(self, EnemyShip):
            if len(pygame.sprite.spritecollide(item,
                    data.player.itemsGroup, False)) > 0:
                return True
            # for enemy in data.enemiesGroup:
            #     if self != enemy:
            #         if len(pygame.sprite.spritecollide(item,
            #                 enemy.itemsGroup, False)) > 0:
            #             return True

    def checkMoveCollisions(self, data):
        dy = self.speed*math.cos(math.radians(self.angle))
        dx = self.speed*math.sin(math.radians(self.angle))
        for item in self.itemsGroup:
            item.update(dx, dy)
            collided = self.checkCollisions(item, data)
            item.update(-dx, -dy)
            if collided:
                return True
        return False

    def move(self, data):
        collided = self.checkMoveCollisions(data)
        # collided = False
        if not collided:
            dy = self.speed*math.cos(math.radians(self.angle))
            dx = self.speed*math.sin(math.radians(self.angle))
            for item in self.itemsGroup:
                item.update(dx, dy)
                if isinstance(item, objects.CommandModule):
                    self.commandModuleX += dx
                    self.commandModuleY += dy

    def checkRotationCollisions(self, angleChange, data):
        for item in self.itemsGroup:
            self.angle += angleChange
            self.doRotate(item)
            item.update(0, 0)
            if isinstance(item, objects.LaserGun):
                item.angle += angleChange

            collided = self.checkCollisions(item, data)
            self.angle -= angleChange
            self.doRotate(item)
            if isinstance(item, objects.LaserGun):
                item.angle -= angleChange
            if collided:
                return True
        return False

    def rotateHelper(self, data, angleChange):
        collided = self.checkRotationCollisions(angleChange, data)
        if not collided:
            self.angle += angleChange
            for item in self.itemsGroup:
                self.doRotate(item)
                if isinstance(item, objects.LaserGun) or \
                        isinstance(item, objects.RocketLauncher):
                    item.angle += angleChange

    #Lowers the cooldown of all Shooter type items
    def resetCooldowns(self, data):
        for item in self.itemsGroup:
            if isinstance(item, objects.LaserGun):
                if item.cooldownTimer == 0:
                    item.coolingDown = False
                if item.coolingDown:
                    item.cooldownTimer -= 1
            if isinstance(item, objects.RocketLauncher):
                if item.cooldownTimer == 0:
                    item.coolingDown = False
                if item.coolingDown:
                    item.cooldownTimer -= 1

    #Remove off screen lasers
    def removeProjectiles(self, data):
        for laser in self.laserGroup:
            inScreenX = 0 < laser.x + data.scrollX < data.width
            inScreenY = 0 < laser.y + data.scrollY < data.height
            if not inScreenX or not inScreenY or \
                    laser.currDistance > laser.maxDistance:
                self.laserGroup.remove(laser)
        for rocket in self.rocketGroup:
            inScreenX = 0 < rocket.x + data.scrollX < data.width
            inScreenY = 0 < rocket.y + data.scrollY < data.height
            if not inScreenX or not inScreenY or \
                    rocket.currDistance > rocket.maxDistance:
                self.rocketGroup.remove(rocket)

    #Destroys items with 0 health and items attached to destroyed items
    def destroyLinkedItems(self, sprite, row, col):
        if sprite.layout[row][col] == None:
            return
        else:
            #item that was just destroyed
            sprite.itemsGroup.remove(sprite.layout[row][col])
            sprite.layout[row][col] = None
            sprite.connections[row][col] = None
            #Branch out to find linked items
            if sprite.connections[row][col - 1] == 'RIGHT':
                self.destroyLinkedItems(sprite, row, col - 1)
            if sprite.connections[row][col + 1] == 'LEFT':
                self.destroyLinkedItems(sprite, row, col + 1)
            if sprite.connections[row - 1][col] == 'DOWN':
                self.destroyLinkedItems(sprite, row - 1, col)
            if sprite.connections[row + 1][col] == 'UP':
                self.destroyLinkedItems(sprite, row + 1, col)

class PlayerShip(Ship):
    def __init__(self, x, y, layout, connections):
        super().__init__(x, y, layout, connections)
        self.speed = 0
        self.forwardAccelerate = False
        self.backwardAccelerate = False
        self.turningLeft = False
        self.turningRight = False
        self.isShooting = False
        self.angleChange = 2.5
        self.maxSpeed = 8

    def rotateLeft(self):
        self.turningLeft = True

    def rotateRight(self):
        self.turningRight = True

    def stopTurningLeft(self):
        self.turningLeft = False

    def stopTurningRight(self):
        self.turningRight = False

    def accelerateForward(self):
        self.forwardAccelerate = True

    def accelerateBackward(self):
        self.backwardAccelerate = True

    def decelerateForward(self):
        self.forwardAccelerate = False

    def decelerateBackward(self):
        self.backwardAccelerate = False

    def rotate(self, data):
        if self.turningLeft:
            self.rotateHelper(data, self.angleChange)
        if self.turningRight:
            self.rotateHelper(data, -self.angleChange)

    #Mimics acceleration and deceleration
    def speedControl(self):
        if self.forwardAccelerate and self.speed > -self.maxSpeed:
            self.speed -= 0.3
        if self.backwardAccelerate and self.speed < self.maxSpeed/2:
            self.speed += 0.3
        if not self.forwardAccelerate and not self.backwardAccelerate:
            self.speed *= 0.9
            if -0.1 < self.speed < 0.1:
                self.speed = 0

    #Adds to sidescrolling if at edge of map
    def checkScrolling(self, data):
        collided = self.checkMoveCollisions(data)
        if data.mode == 'PLAYING' and not collided:
            dy = self.speed*math.sin(math.radians(self.angle + 90))
            dx = self.speed*math.cos(math.radians(self.angle + 90))
            data.scrollX += dx
            data.scrollY -= dy

    def update(self, data):
        self.removeProjectiles(data)
        self.resetCooldowns(data)
        self.speedControl()
        self.checkScrolling(data)
        self.rotate(data)
        self.move(data)
        self.shoot()
        self.checkHits(data)

    def shooting(self):
        self.isShooting = True

    def stopShooting(self):
        self.isShooting = False

    def shoot(self):
        if self.isShooting:
            for item in self.itemsGroup:
                if isinstance(item, objects.LaserGun) and not item.coolingDown:
                    item.coolingDown = True
                    item.cooldownTimer = item.cooldown
                    newLaser = objects.PlayerLaser(item.x, item.y,
                        item.angle, item.damage)
                    self.laserGroup.add(newLaser)
                elif isinstance(item, objects.RocketLauncher) and \
                        not item.coolingDown:
                    item.coolingDown = True
                    item.cooldownTimer = item.cooldown
                    magnitude = objects.RocketLauncher.height
                    width = objects.Rocket.width
                    dx = magnitude*math.cos(math.radians(item.angle))
                    dy = -magnitude*math.sin(math.radians(item.angle))
                    dx -= abs(width*math.cos(math.radians(item.angle)))
                    dy += abs(width/2*math.cos(math.radians(item.angle)))
                    newRocket = objects.Rocket(item.x + dx, item.y + dy,
                        item.angle, item.damage)
                    self.rocketGroup.add(newRocket)

    #Checks if any lasers or rockets hit an enemy
    def checkHits(self, data):
        for enemy in data.enemiesGroup:
            for row in range(len(enemy.layout)):
                for col in range(len(enemy.layout[0])):
                    item = enemy.layout[row][col]
                    if item != None:
                        collidedLasers = pygame.sprite.spritecollide(item,
                            data.player.laserGroup, True)
                        collidedRockets = pygame.sprite.spritecollide(item,
                            data.player.rocketGroup, True)
                        if len(collidedLasers) > 0:
                            for laser in collidedLasers:
                                item.health -= laser.damage
                                if isinstance(item, objects.CommandModule) and\
                                        item.health <= 0:
                                    enemy.defeated(data)
                                    break
                                elif item.health <= 0:
                                    self.destroyLinkedItems(enemy, row, col)
                                    break
                        if len(collidedRockets) > 0:
                            for rocket in collidedRockets:
                                item.health -= rocket.damage
                                if isinstance(item, objects.CommandModule) and\
                                        item.health <= 0:
                                    enemy.defeated(data)
                                    break
                                elif item.health <= 0:
                                    self.destroyLinkedItems(enemy, row, col)
                                    break

class EnemyShip(Ship):
    def __init__(self, x, y, layout, connections, level, speed=-2):
        super().__init__(x, y, layout, connections)
        self.speed = speed
        self.maxSpeed = speed
        self.level = level

    #If enemy is defeated, remove from game and drop all items
    def defeated(self, data):
        for item in self.itemsGroup:
            if not isinstance(item, objects.CommandModule):
                data.droppedItemsGroup.add(item)
        self.itemsGroup.empty()
        data.enemiesGroup.remove(self)
        if self.level > data.crimeLevel:
            data.crimeLevel += 1

    #Causes the enemy to rotate towards the player
    def directionFinder(self, data):
        xDistance = self.commandModuleX - data.player.commandModuleX
        yDistance = -(self.commandModuleY - data.player.commandModuleY)
        targetAngle = math.degrees(math.atan2((yDistance), (xDistance))) + 180
        pointDir = self.angle + 90
        pointDir %= 360

        if pointDir - targetAngle < 0 and abs(pointDir - targetAngle) < 180 or\
                360 + targetAngle - pointDir < 180:
            self.rotateHelper(data, 1)
        else:
            self.rotateHelper(data, -1)

    def update(self, data):
        self.removeProjectiles(data)
        self.resetCooldowns(data)
        if data.timer % 2 == 0:
            self.directionFinder(data)
        self.move(data)
        self.shoot(data)
        self.checkHits(data)

    #Only shoots if enemy is relatively pointing towards the player
    def shoot(self, data):
        for item in self.itemsGroup:
            if isinstance(item, objects.LaserGun) and not item.coolingDown:
                item.coolingDown = True
                item.cooldownTimer = item.cooldown
                newLaser = objects.EnemyLaser(item.x, item.y,
                    item.angle, item.damage)
                self.laserGroup.add(newLaser)
            elif isinstance(item, objects.RocketLauncher) and \
                    not item.coolingDown:
                item.coolingDown = True
                item.cooldownTimer = item.cooldown
                magnitude = objects.RocketLauncher.height
                width = objects.Rocket.width
                dx = magnitude*math.cos(math.radians(item.angle))
                dy = -magnitude*math.sin(math.radians(item.angle))
                dx -= abs(width*math.cos(math.radians(item.angle)))
                dy += abs(width/2*math.cos(math.radians(item.angle)))
                newRocket = objects.Rocket(item.x + dx, item.y + dy,
                    item.angle, item.damage, item.speed)
                self.rocketGroup.add(newRocket)

    def checkHits(self, data):
        for row in range(len(data.player.layout)):
            for col in range(len(data.player.layout[0])):
                item = data.player.layout[row][col]
                if item != None:
                    collidedLasers = pygame.sprite.spritecollide(item,
                        self.laserGroup, True)
                    collidedRockets = pygame.sprite.spritecollide(item,
                        self.rocketGroup, True)
                    if len(collidedLasers) > 0:
                        for laser in collidedLasers:
                            item.health -= laser.damage
                            if isinstance(item, objects.CommandModule) and \
                                    item.health <= 0:
                                data.mode = data.modes[3]
                            elif item.health <= 0:
                                self.destroyLinkedItems(data.player, row, col)
                                break
                    if len(collidedRockets) > 0:
                        for rocket in collidedRockets:
                            item.health -= rocket.damage
                            if isinstance(item, objects.CommandModule) and\
                                    item.health <= 0:
                                data.mode = data.modes[3]
                            elif item.health <= 0:
                                self.destroyLinkedItems(data.player, row, col)
                                break

        pygame.sprite.groupcollide(data.player.rocketGroup, self.laserGroup, True, True)


from __future__ import absolute_import

import datetime
import uuid

from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext

from . import importer_worker
from podcasts.models import Podcast, PodcastEpisode

class AssetImportRequest(models.Model):
    created = models.DateTimeField(auto_now_add=True)

    podcast = models.ForeignKey(Podcast, null=True, blank=True)
    episode = models.ForeignKey(PodcastEpisode, null=True, blank=True)

    expiration = models.DateTimeField()
    audio_source_url = models.URLField(blank=True, max_length=500)
    image_source_url = models.URLField(blank=True, max_length=500)
    access_token = models.CharField(max_length=128)

    resolved = models.BooleanField(default=False)

    failed = models.BooleanField(default=False)
    failure_message = models.TextField(null=True, blank=True)

    @classmethod
    def create(cls, *args, **kwargs):
        if 'access_token' not in kwargs:
            kwargs['access_token'] = AssetImportRequest.get_token()
        return cls(*args, **kwargs)

    @classmethod
    def get_token(cls):
        return str(uuid.uuid4())

    def get_json_payload(self):
        return {
            'id': self.id,
            'item_type': 'image' if self.image_source_url else 'audio',
            'entity':
                {'id': str(self.episode.id), 'name': self.episode.title, 'type': 'episode'} if
                self.episode else
                {'id': str(self.podcast.id), 'name': self.podcast.name, 'type': 'podcast'},
            'entity_type': 'episode' if self.episode else 'podcast',
            'resolved': self.resolved,
            'failed': self.failed,
            'failure_message': self.failure_message,
        }

    def resolve(self, new_url):
        if self.resolved:
            raise Exception(ugettext('Attempting to double-resolve import request'))

        if self.expiration < datetime.datetime.now():
            raise Exception(ugettext('Attempting to resolve expired import request'))

        if self.podcast:
            if self.audio_source_url:
                raise Exception(ugettext('Invalid import request'))

            orig_url = self.podcast.cover_image
            self.podcast.cover_image = new_url
            self.podcast.save()

            eps = self.podcast.podcastepisode_set.filter(image_url=orig_url)
            for ep in eps:
                ep.image_url = new_url
                ep.save()

        elif self.episode:
            if self.audio_source_url:
                self.episode.audio_url = new_url
            else:
                self.episode.image_url = new_url

            if (settings.S3_BUCKET in self.episode.audio_url and
                settings.S3_BUCKET in self.episode.image_url):
                self.episode.awaiting_import = False
            self.episode.save()

        else:
            raise Exception(ugettext('Invalid import request for neither podcast nor ep'))

        self.resolved = True
        self.save()

    def get_payload(self):
        source = self.audio_source_url or self.image_source_url
        clean_source = source
        if '#' in clean_source:  # Strip off hashes
            clean_source = clean_source[:clean_source.index('#')]
        if '?' in clean_source:  # Strip off query params
            clean_source = clean_source[:clean_source.index('?')]

        if self.podcast:
            key = 'podcasts/covers/'
        else:
            key = 'podcasts/%s/%s/' % (
                str(self.episode.podcast.id),
                'image' if self.image_source_url else 'audio')
        key = '%s%s/%s' % (key, str(uuid.uuid4()), clean_source[clean_source.rindex('/') + 1:])

        pod = self.podcast or self.episode.podcast

        return {
            'type': 'import_asset',
            'token': self.access_token,
            'id': self.id,
            'url': source,
            'bucket': settings.S3_BUCKET,
            'key': key,
        }

    def trace_podcast(self):
        return self.podcast or self.episode.podcast

    def asset_type(self):
        return 'image' if self.image_source_url else 'audio'

    def status(self):
        if self.resolved:
            return 'resolved'
        elif self.failed:
            return 'failed'
        else:
            return 'unresolved'

    def __str__(self):
        return self.audio_source_url or self.image_source_url

class Collaborator(models.Model):
    created = models.DateTimeField(auto_now_add=True)

    podcast = models.ForeignKey(Podcast, null=True, blank=True, related_name='collaborators')
    collaborator = models.ForeignKey(User, related_name='collaborated_podcasts')

    def __str__(self):
        return '%s - %s' % (self.podcast.name, self.collaborator.email)


import numpy as np
from math import sqrt

G = 6.67408 * pow(10,-11)

class Universe:
    def __init__(self):
        self.list_of_objects = []

    def add_object(self,obj):
        self.list_of_objects.append(obj)

    def __move(self,dt):
        n = len(self.list_of_objects)
        for i in range(n):
            a = np.array([0,0])
            for j in range(n):
                if i != j:
                    a = a + self.list_of_objects[i].gravitational_force_acc(self.list_of_objects[j])
            self.list_of_objects[i].a = a
        for planet in self.list_of_objects:
            planet.v = planet.v + planet.a * dt
            planet.r = planet.r + planet.v * dt
            planet.xlist.append(planet.r[0])
            planet.ylist.append(planet.r[1])

    def evolve(self,T,dt):
        t = 0
        while t < T:
            self.__move(dt)
            t = t + dt

class Planet:
    def __init__(self,name,color,m,r0,v0):
        self.name = name
        self.color = color
        self.m = m
        self.r0 = r0
        self.v0 = v0
        self.a0 = np.array([0,0])
        self.r = r0
        self.v = v0
        self.a = self.a0
        self.xlist = []
        self.ylist = []

    def reset(self):
        del self.name
        del self.color
        del self.m
        del self.r0
        del self.v0
        del self.r
        del self.v
        del self.a
        del self.xlist
        del self.ylist

    def return_to_start(self):
        name = self.name
        color = self.color
        m = self.m
        r0 = self.r0
        v0 = self.v0
        self.reset()
        self.name = name
        self.color = color
        self.m = m
        self.r0 = r0
        self.v0 = v0
        self.r = r0
        self.v = v0
        self.a = self.a0
        self.xlist = []
        self.ylist = []

    def __distance(self,other):
        x1 = self.r[0]
        x2 = other.r[0]
        y1 = self.r[1]
        y2 = other.r[1]
        x = x1 - x2
        y = y1 - y2
        d = sqrt(pow(x,2) + pow(y,2))
        return d

    def gravitational_force_acc(self,other):
        distance = self.__distance(other)
        r12 = (self.r - other.r) * (1 / distance)
        a = - ((G * other.m)/(pow(distance,2))) * r12
        return a