import pandas as pd                ###FILE "fb-pages-food.edges" should be in the same directory with this code file.
import numpy as np
import random
import networkx as nx
from tqdm import tqdm
import time
import matplotlib.pyplot as plt
from networkx.algorithms import community as comm
from networkx.algorithms import centrality as cntr
import os
import itertools #new

############################### FG COLOR DEFINITIONS ###############################
class bcolors:
    HEADER      = '\033[95m'
    OKBLUE      = '\033[94m'
    OKCYAN      = '\033[96m'
    OKGREEN     = '\033[92m'
    WARNING     = '\033[93m'
    FAIL        = '\033[91m'
    ENDC        = '\033[0m'    # RECOVERS DEFAULT TEXT COLOR
    BOLD        = '\033[1m'
    UNDERLINE   = '\033[4m'

    def disable(self):
        self.HEADER     = ''
        self.OKBLUE     = ''
        self.OKGREEN    = ''
        self.WARNING    = ''
        self.FAIL       = ''
        self.ENDC       = ''

########################################################################################
############################## MY ROUTINES LIBRARY STARTS ##############################
########################################################################################

# SIMPLE ROUTINE TO CLEAR SCREEN BEFORE SHOWING A MENU...
def my_clear_screen():

    os.system('cls' if os.name == 'nt' else 'clear')

# CREATE A LIST OF RANDOMLY CHOSEN COLORS...
def my_random_color_list_generator(REQUIRED_NUM_COLORS):

    my_color_list = [   'red',
                        'green',
                        'cyan',
                        'brown',
                        'olive',
                        'orange',
                        'darkblue',
                        'purple',
                        'yellow',
                        'hotpink',
                        'teal',
                        'gold']

    my_used_colors_dict = { c:0 for c in my_color_list }     # DICTIONARY OF FLAGS FOR COLOR USAGE. Initially no color is used...
    constructed_color_list = []

    if REQUIRED_NUM_COLORS <= len(my_color_list):
        for i in range(REQUIRED_NUM_COLORS):
            constructed_color_list.append(my_color_list[i])

    else: # REQUIRED_NUM_COLORS > len(my_color_list)
        constructed_color_list = ["#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)]) for i in range(REQUIRED_NUM_COLORS)]

    return(constructed_color_list)

# VISUALISE A GRAPH WITH COLORED NODES AND LINKS
def my_graph_plot_routine(G,fb_nodes_colors,fb_links_colors,fb_links_styles,graph_layout,node_positions):
    plt.figure(figsize=(10,8))

    if len(node_positions) == 0:
        if graph_layout == 'circular':
            node_positions = nx.circular_layout(G)
        elif graph_layout == 'random':
            node_positions = nx.random_layout(G, seed=50)
        elif graph_layout == 'planar':
            node_positions = nx.planar_layout(G)
        elif graph_layout == 'shell':
            node_positions = nx.shell_layout(G)
        else:   #DEFAULT VALUE == spring
            node_positions = nx.spring_layout(G)

    nx.draw(G,
        with_labels=True,           # indicator variable for showing the nodes' ID-labels
        style=fb_links_styles,      # edge-list of link styles, or a single default style for all edges
        edge_color=fb_links_colors, # edge-list of link colors, or a single default color for all edges
        pos = node_positions,       # node-indexed dictionary, with position-values of the nodes in the plane
        node_color=fb_nodes_colors, # either a node-list of colors, or a single default color for all nodes
        node_size = 100,            # node-circle radius
        alpha = 0.9,                # fill-transparency
        width = 0.5                 # edge-width
        )
    plt.show()

    return(node_positions)

########################################################################################
# MENU 1 STARTS: creation of input graph ###
########################################################################################
def my_menu_graph_construction(G,node_names_list,node_positions):

    my_clear_screen()

    breakWhileLoop  = False

    while not breakWhileLoop:
        print(bcolors.OKGREEN
        + '''
========================================
(1.1) Create graph from fb-food data set (fb-pages-food.nodes and fb-pages-food.nodes)\t[format: L,<NUM_LINKS>]
(1.2) Create RANDOM Erdos-Renyi graph G(n,p).\t\t\t\t\t\t[format: R,<number of nodes>,<edge probability>]
(1.3) Print graph\t\t\t\t\t\t\t\t\t[format: P,<GRAPH LAYOUT in {spring,random,circular,shell }>]
(1.4) Continue with detection of communities.\t\t\t\t\t\t[format: N]
(1.5) EXIT\t\t\t\t\t\t\t\t\t\t[format: E]
----------------------------------------
        ''' + bcolors.ENDC)

        my_option_list = str(input('\tProvide your (case-sensitive) option: ')).split(',')

        if my_option_list[0] == 'L':
            MAX_NUM_LINKS = 2102    # this is the maximum number of links in the fb-food-graph data set...

            if len(my_option_list) > 2:
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                print(bcolors.WARNING + "\tERROR MESSAGE: Too many parameters. Try again..." + bcolors.ENDC)
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

            else:
                if len(my_option_list) == 1:
                    NUM_LINKS = MAX_NUM_LINKS
                else: #...len(my_option_list) == 2...
                    NUM_LINKS = int(my_option_list[1])

                if NUM_LINKS > MAX_NUM_LINKS or NUM_LINKS < 1:
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tERROR Invalid number of links to read from data set. It should be in {1,2,...,2102}. Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                else:
                    # LOAD GRAPH FROM DATA SET...
                    G,node_names_list= read_graph_from_csv(NUM_LINKS)
                    print(  "\tConstructing the FB-FOOD graph with n =",G.number_of_nodes(),
                            "vertices and m =",G.number_of_edges(),"edges (after removal of loops).")

        elif my_option_list[0] == 'R':

            if len(my_option_list) > 3:
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                print(bcolors.WARNING + "\tERROR MESSAGE: Wrong number of parameters. Try again..." + bcolors.ENDC)
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

            else: # ...len(my_option_list) <= 3...
                if len(my_option_list) == 1:
                    NUM_NODES = 100                     # DEFAULT NUMBER OF NODES FOR THE RANDOM GRAPH...
                    ER_EDGE_PROBABILITY = 2 / NUM_NODES # DEFAULT VALUE FOR ER_EDGE_PROBABILITY...

                elif len(my_option_list) == 2:
                    NUM_NODES = int(my_option_list[1])
                    ER_EDGE_PROBABILITY = 2 / max(1,NUM_NODES) # AVOID DIVISION WITH ZERO...

                else: # ...NUM_NODES == 3...
                    NUM_NODES = int(my_option_list[1])
                    ER_EDGE_PROBABILITY = float(my_option_list[2])

                if ER_EDGE_PROBABILITY < 0 or ER_EDGE_PROBABILITY > 1 or NUM_NODES < 2:
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tERROR MESSAGE: Invalid probability mass or number of nodes of G(n,p). Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                else:
                    G = nx.erdos_renyi_graph(NUM_NODES, ER_EDGE_PROBABILITY)
                    print(bcolors.ENDC +    "\tConstructing random Erdos-Renyi graph with n =",G.number_of_nodes(),
                                            "vertices and edge probability p =",ER_EDGE_PROBABILITY,
                                            "which resulted in m =",G.number_of_edges(),"edges.")

                    node_names_list = [ x for x in range(NUM_NODES) ]

        elif my_option_list[0] == 'P':                  # PLOT G...
            print("Printing graph G with",G.number_of_nodes(),"vertices,",G.number_of_edges(),"edges and",nx.number_connected_components(G),"connected components." )

            if len(my_option_list) > 3:
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                print(bcolors.WARNING + "\tERROR MESSAGE: Wrong number of parameters. Try again..." + bcolors.ENDC)
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

            else:
                if len(my_option_list) <= 1:
                    graph_layout = 'spring'     # ...DEFAULT graph_layout value...
                    reset_node_positions = 'Y'  # ...DEFAULT decision: erase node_positions...

                elif len(my_option_list) == 2:
                    graph_layout = str(my_option_list[1])
                    reset_node_positions = 'Y'  # ...DEFAULT decision: erase node_positions...

                else: # ...len(my_option_list) == 3...
                    graph_layout = str(my_option_list[1])
                    reset_node_positions = str(my_option_list[2])

                if graph_layout not in ['spring','random','circular','shell']:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible choice for graph layout. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                elif reset_node_positions not in ['Y','y','N','n']:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible decision for resetting node positions. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                else:
                    if reset_node_positions in ['y','Y']:
                        node_positions = []         # ...ERASE previous node positions...

                    node_positions = my_graph_plot_routine(G,'grey','blue','solid',graph_layout,node_positions)

        elif my_option_list[0] == 'N':
            NUM_NODES = G.number_of_nodes()
            if NUM_NODES == 0:
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                print(bcolors.WARNING + "\tERROR MESSAGE: You have not yet constructed a graph to work with. Try again..." + bcolors.ENDC)
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
            else:
                my_clear_screen()
                breakWhileLoop = True

        elif my_option_list[0] == 'E':
            quit()

        else:
            print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
            print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible input was provided. Try again..." + bcolors.ENDC)
            print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

    return(G,node_names_list,node_positions)

########################################################################################
# MENU 2: detect communities in the constructed graph
########################################################################################
def my_menu_community_detection(G,node_names_list,node_positions,hierarchy_of_community_tuples):

    breakWhileLoop = False

    community_tuples=[]
    while not breakWhileLoop:
            print(bcolors.OKGREEN
                + '''
========================================
(2.1) Add random edges from each node\t\t\t[format: RE,<NUM_RANDOM_EDGES_PER_NODE>,<EDGE_ADDITION_PROBABILITY in [0,1]>]
(2.2) Add hamilton cycle (if graph is not connected)\t[format: H]
(2.3) Print graph\t\t\t\t\t[format: P,<GRAPH LAYOUT in { spring, random, circular, shell }>,<ERASE NODE POSITIONS in {Y,N}>]
(2.4) Compute communities with GIRVAN-NEWMAN\t\t[format: C,<ALG CHOICE in { O(wn),N(etworkx) }>,<GRAPH LAYOUT in {spring,random,circular,shell }>,<percent of nodes [0,100]>]
(2.5) Compute a binary hierarchy of communities\t\t[format: D,<NUM_DIVISIONS>,<GRAPH LAYOUT in {spring,random,circular,shell }>,<percent of nodes [0,100]>,{'1' for netowrkx implementation OR'2' for one-shot girvan_newman }]
(2.6) Compute modularity-values for all community partitions\t[format: M]
(2.7) Visualize the communities of the graph\t\t[format: V,<GRAPH LAYOUT in {spring,random,circular,shell}>]
(2.8) EXIT\t\t\t\t\t\t[format: E]
----------------------------------------
            ''' + bcolors.ENDC)

            my_option_list = str(input('\tProvide your (case-sensitive) option: ')).split(',')

            if my_option_list[0] == 'RE':                    # 2.1: ADD RANDOM EDGES TO NODES...

                if len(my_option_list) > 3:
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tERROR MESSAGE: Wrong number of parameters. [format: D,<NUM_RANDOM_EDGES>,<EDGE_ADDITION_PROBABILITY>]. Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                else:
                    if len(my_option_list) == 1:
                        NUM_RANDOM_EDGES = 1                # DEFAULT NUMBER OF RANDOM EDGES TO ADD (per node)
                        EDGE_ADDITION_PROBABILITY = 0.25    # DEFAULT PROBABILITY FOR ADDING EACH RANDOM EDGE (independently of other edges) FROM EAC NODE (independently from other nodes)...

                    elif len(my_option_list) == 2:
                        NUM_RANDOM_EDGES = int(my_option_list[1])
                        EDGE_ADDITION_PROBABILITY = 0.25    # DEFAULT PROBABILITY FOR ADDING EACH RANDOM EDGE (independently of other edges) FROM EAC NODE (independently from other nodes)...

                    else:
                        NUM_RANDOM_EDGES = int(my_option_list[1])
                        EDGE_ADDITION_PROBABILITY = float(my_option_list[2])

                    # CHECK APPROPIATENESS OF INPUT AND RUN THE ROUTINE...
                    if NUM_RANDOM_EDGES-1 not in range(5):
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: Too many random edges requested. Should be from {1,2,...,5}. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                    elif EDGE_ADDITION_PROBABILITY < 0 or EDGE_ADDITION_PROBABILITY > 1:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: Not appropriate value was given for EDGE_ADDITION PROBABILITY. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                    else:
                        add_random_edges_to_graph(G,node_names_list,NUM_RANDOM_EDGES,EDGE_ADDITION_PROBABILITY)

            elif my_option_list[0] == 'H':                  #2.2: ADD HAMILTON CYCLE...

                    add_hamilton_cycle_to_graph(G,node_names_list)

            elif my_option_list[0] == 'P':                  # 2.3: PLOT G...
                print("Printing graph G with",G.number_of_nodes(),"vertices,",G.number_of_edges(),"edges and",nx.number_connected_components(G),"connected components." )

                if len(my_option_list) > 2:
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tERROR MESSAGE: Wrong number of parameters. Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                else:
                    if len(my_option_list) <= 1:
                        graph_layout = 'spring'     # ...DEFAULT graph_layout value...

                    else: # ...len(my_option_list) == 2...
                        graph_layout = str(my_option_list[1])

                    if graph_layout not in ['spring','random','circular','shell']:
                            print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                            print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible choice for graph layout. Try again..." + bcolors.ENDC)
                            print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                    else:
                        if len(my_option_list) == 2:
                            node_positions = []         # ...ERASE previous node positions...

                        node_positions = my_graph_plot_routine(G,'grey','blue','solid',graph_layout,node_positions)

            elif my_option_list[0] == 'C':      # 2.4: COMPUTE ONE-SHOT GN-COMMUNITIES
                NUM_OPTIONS = len(my_option_list)

                if NUM_OPTIONS > 4:
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tERROR MESSAGE: Wrong number of parameters. Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                else:
                    if NUM_OPTIONS == 1:
                        alg_choice  = 'N'            # DEFAULT COMM-DETECTION ALGORITHM == NX_GN
                        graph_layout = 'spring'     # DEFAULT graph layout == spring

                    elif NUM_OPTIONS == 2:
                        alg_choice  = str(my_option_list[1])
                        perc= 50 #deafult value for nodes percentage
                        graph_layout = 'spring'     # DEFAULT graph layout == spring

                    elif NUM_OPTIONS==3: # ...NUM_OPTIONS == 3...
                        alg_choice      = str(my_option_list[1])
                        graph_layout    = str(my_option_list[2])
                        perc= 50 #deafult value for nodes percentage

                    else:
                        alg_choice      = str(my_option_list[1])
                        graph_layout    = str(my_option_list[2])
                        perc= int(my_option_list[3])
                    # CHECKING CORRECTNESS OF GIVWEN PARAMETERS...
                    if alg_choice == 'N' and graph_layout in ['spring','circular','random','shell']:
                        community_tuples=use_nx_girvan_newman_for_communities(G)

                    elif alg_choice == 'O'and graph_layout in ['spring','circular','random','shell']:
                        community_tuples=one_shot_girvan_newman_for_communities(G,perc)

                    else:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible parameters for executing the GN-algorithm. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

            elif my_option_list[0] == 'D':          # 2.5: COMUTE A BINARY HIERARCHY OF COMMUNITY PARRTITIONS
                NUM_OPTIONS = len(my_option_list)
                NUM_NODES = G.number_of_nodes()
                NUM_COMPONENTS = nx.number_connected_components(G)
                #MAX_NUM_DIVISIONS = min( 8*NUM_COMPONENTS , np.floor(NUM_NODES/4) )

                if NUM_OPTIONS > 5:
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tERROR MESSAGE: Wrong number of parameters. Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                else:
                    if NUM_OPTIONS == 1:
                        number_of_divisions = 2*NUM_COMPONENTS      # DEFAULT number of communities to look for
                        graph_layout = 'spring'                     # DEFAULT graph layout == spring
                        perc= 50                                    #default value for percentage of nodes
                        algo= 1                                     #default value algorithm choice

                    elif NUM_OPTIONS == 2:
                        number_of_divisions = int(my_option_list[1])
                        graph_layout = 'spring'                     # DEFAULT graph layout == spring
                        perc= 50                                    #default value for percentage of nodes
                        algo= 1                                     #default value algorithm choice

                    elif NUM_OPTIONS == 3:
                        number_of_divisions = int(my_option_list[1])
                        graph_layout = str(my_option_list[2])
                        perc= 50                                    #default value for percentage of nodes
                        algo= 1                                     #default value algorithm choice

                    elif NUM_OPTIONS == 4:
                        number_of_divisions = int(my_option_list[1])
                        graph_layout = str(my_option_list[2])
                        perc= int(my_option_list[3])
                        algo= 1                                     #default value algorithm choice

                    else:
                        number_of_divisions = int(my_option_list[1])
                        graph_layout = str(my_option_list[2])
                        perc= int(my_option_list[3])
                        algo= int(my_option_list[4])

                    # CHECKING SYNTAX OF GIVEN PARAMETERS...
                    if number_of_divisions < NUM_COMPONENTS:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: The graph has already",NUM_COMPONENTS,"connected components. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                    elif graph_layout not in ['spring','random','circular','shell']:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible choice of a graph layout. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                    elif perc<0 or perc>100:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tProbability should be a number between 0 and 100. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                    elif algo!=1 and algo!=2:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tChoose 1 for one-shot or 2 for netowrkx implementation. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    else:
                        hierarchy_of_community_tuples,community_tuples,graph_layout,node_positions= divisive_community_detection(G,number_of_divisions,graph_layout,[],perc,algo)

            elif my_option_list[0] == 'M':      # 2.6: DETERMINE PARTITION OF MIN-MODULARITY, FOR A GIVEN BINARY HIERARCHY OF COMMUNITY PARTITIONS
                community_tuples= determine_opt_community_structure(G,hierarchy_of_community_tuples,graph_layout,node_positions)

            elif my_option_list[0] == 'V':      # 2.7: VISUALIZE COMMUNITIES WITHIN GRAPH

                NUM_OPTIONS = len(my_option_list)

                if(len(community_tuples)==0):
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tYou have not created communities yet. Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                elif NUM_OPTIONS > 2:
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                    print(bcolors.WARNING + "\tERROR MESSAGE: Wrong number of parameters. Try again..." + bcolors.ENDC)
                    print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                else:

                    if NUM_OPTIONS == 1:
                        graph_layout = 'spring'                     # DEFAULT graph layout == spring

                    else: # ...NUM_OPTIONS == 2...
                        graph_layout = str(my_option_list[1])

                    if graph_layout not in ['spring','random','circular','shell']:
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                        print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible choice of a graph layout. Try again..." + bcolors.ENDC)
                        print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)

                    else:
                        visualize_communities(G,community_tuples,graph_layout,node_positions)

            elif my_option_list[0] == 'E':
                #EXIT the program execution...
                quit()

            else:
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
                print(bcolors.WARNING + "\tERROR MESSAGE: Incomprehensible input was provided. Try again..." + bcolors.ENDC)
                print(bcolors.WARNING + "\t++++++++++++++++++++++++++++++++++++++++" + bcolors.ENDC)
    ### MENU 2 ENDS: detect communities in the constructed graph ###

########################################################################################
############################### MY ROUTINES LIBRARY ENDS ###############################
########################################################################################

########################################################################################
########################## ROUTINES LIBRARY STARTS ##########################
# FILL IN THE REQUIRED ROUTINES FROM THAT POINT ON...
########################################################################################

########################################################################################
def read_graph_from_csv(NUM_LINKS):

    fb_links= pd.read_csv("fb-pages-food.edges") #read edges
    lst1= fb_links["node_1"][0:NUM_LINKS] #keep only the first NUM_LINKS edges
    lst2= fb_links["node_2"][0:NUM_LINKS]
    final_1=[]
    final_2=[]
    node_names_list=[]
    fb_links_df= pd.DataFrame(list(zip(lst1, lst2)), columns =["node_1", "node_2"]) #create a new dataFrame with only the first NUM_LINKS edges
    for i in range(fb_links_df.shape[0]):
        if(fb_links_df["node_1"][i]!=fb_links_df["node_2"][i]): #add only the edges that are not loops
            final_1.append(fb_links_df["node_1"][i])
            final_2.append(fb_links_df["node_2"][i])
        if(not fb_links_df["node_1"][i] in node_names_list): #add the new nodes to the name list
            node_names_list.append(fb_links_df["node_1"][i])
        if(not fb_links_df["node_2"][i] in node_names_list): #add the new nodes to the name list
            node_names_list.append(fb_links_df["node_2"][i])
    fb_links_loopless_df= pd.DataFrame(list(zip(final_1, final_2)), columns =["node_1", "node_2"]) #final loopless links
    G = nx.from_pandas_edgelist(fb_links_loopless_df, "node_1", "node_2", create_using=nx.Graph()) #create graph
    return (G,node_names_list)
######################################################################################################################
def one_shot_girvan_newman_for_communities(G,percentage):

    start_time = time.time()

    community_tuples = [] #every element is a list with a connected_component
    components= sorted(nx.connected_components(G), key = len, reverse=True) #create components
    for cc in components: #keep components as lists and sorted by their size.
        community_tuples.append(list(cc))

    numOfNodes= len(community_tuples[0])*(percentage/100) #number of nodes we will take under consideration
    nodes=[] #nodes as roots to the bfs trees.
    j=0
    while(j<numOfNodes):
        nodes.append(community_tuples[0][j])
        j=j+1
    maxComp= G.subgraph(community_tuples[0]).copy()
    betweenness= nx.edge_betweenness_centrality_subset(maxComp,sources=nodes,targets=community_tuples[0],weight=None)
    sorted_betweenness= {k: v for k, v in sorted(betweenness.items(), key=lambda item: item[1], reverse= True)}
    list_form= list(sorted_betweenness.keys())
    i=0
    while(nx.is_connected(maxComp)):
        edge_to_remove= list_form[i]
        maxComp.remove_edge(edge_to_remove[0],edge_to_remove[1])
        G.remove_edge(edge_to_remove[0],edge_to_remove[1])
        i=i+1
    new_components= sorted(nx.connected_components(maxComp), key = len, reverse=True)
    community_tuples.pop(0)
    for lc in new_components:
        community_tuples.append(list(lc))

    end_time = time.time()

    print(bcolors.ENDC  + "\t===================================================")
    print(bcolors.ENDC  + "\tYOUR OWN computation of ONE-SHOT Girvan-Newman clustering for a graph with",G.number_of_nodes(),"nodes and",G.number_of_edges(),"links. "
                        + "Computation time =", end_time - start_time,"\n")
    return community_tuples
######################################################################################################################
# ...(b) USE NETWORKX IMPLEMENTATION OF ONE-SHOT GN-ALGORITHM...
######################################################################################################################
def use_nx_girvan_newman_for_communities(G):

    start_time = time.time()
    #At the beggining we have K components => K communities
    communities = comm.girvan_newman(G) #returns K+1 communities (breaks the biggest component to 2)

    community_tuples = []
    for com in next(communities): #save communities as lists of nodes
        community_tuples.append(list(com))

    end_time = time.time()
    print(bcolors.ENDC  + "\t===================================================")
    print(bcolors.ENDC  + "\tBUILT-IN computation of ONE-SHOT Girvan-Newman clustering for a graph with",G.number_of_nodes(),"nodes and",G.number_of_edges(),"links. "
                        + "Computation time =", end_time - start_time,"\n")
    return community_tuples
######################################################################################################################
def divisive_community_detection(G,number_of_divisions,graph_layout,node_positions,percentage,method):

    start_time = time.time()

    community_tuples = [] #every element is a list with a connected_component
    components= sorted(nx.connected_components(G), key = len, reverse=True)
    for cc in components:
        community_tuples.append(list(cc))
    numDiv= number_of_divisions
    while(len(community_tuples)>numDiv): #check if the divisions that were asked to happen where less than the number of components
        print("Wrong number of divisions... It should be at least ",len(community_tuples))
        numDiv= int(input("Give a new number: "))
    hierarchy_of_community_tuples=[] #list that holds list of communities
    hierarchy_of_community_tuples.append(tuple(community_tuples))
    if(method==1):
        while(len(community_tuples)<numDiv):
            sortListOfLists(community_tuples)
            maxComp= G.subgraph(community_tuples[0]).copy() #take the graph of the largest community
            new_communities=use_nx_girvan_newman_for_communities(maxComp)
            community_tuples.append(new_communities[0]) #add the new communities to the tuples
            community_tuples.append(new_communities[1])
            currentTriplet=(community_tuples[0],new_communities[0],new_communities[1])
            community_tuples.pop(0) #remove the old community from the tuples
            hierarchy_of_community_tuples.append(currentTriplet)
    else:
        while(len(community_tuples)<numDiv):
            sortListOfLists(community_tuples)
            maxComp= G.subgraph(community_tuples[0]).copy() #take the graph of the largest community
            new_communities=one_shot_girvan_newman_for_communities(maxComp,percentage)
            community_tuples.append(new_communities[0]) #add the new communities to the tuples
            community_tuples.append(new_communities[1])
            currentTriplet=(community_tuples[0],new_communities[0],new_communities[1])
            community_tuples.pop(0) #remove the old community from the tuples
            hierarchy_of_community_tuples.append(currentTriplet)

    end_time = time.time()
    print(bcolors.ENDC  + "\t===================================================")
    print(bcolors.ENDC  + "\tComputation of HIERARCHICAL BIPARTITION of G in communities, "
                        + "using the BUILT-IN girvan-newman algorithm, for a graph with",G.number_of_nodes(),"nodes and",G.number_of_edges(),"links. "
                        + "Computation time =", end_time - start_time,"\n")
    return (hierarchy_of_community_tuples,community_tuples,graph_layout,node_positions)
######################################################################################################################
def determine_opt_community_structure(G,hierarchy_of_community_tuples,graph_layout,node_positions):
    community_tuples=() #here we keep the picture of the graph after every division
    final_tuples_to_print=() #here we keep the communties with the max modularity value
    modularities=[] ##holds modularity for every division
    number_of_communities=[] ##holds the number of communities for every division
    modularity= -100
    i=0
    for i in range(len(hierarchy_of_community_tuples)):
        if(i==0):
            community_tuples=hierarchy_of_community_tuples[0] #start with the K communities
        else: #calculate K+1 communities
            lst= list(community_tuples)
            lst.remove(hierarchy_of_community_tuples[i][0]) #remove the bigger community
            lst.append(hierarchy_of_community_tuples[i][1]) #add the new community (1)
            lst.append(hierarchy_of_community_tuples[i][2]) #add the new community (2)
            community_tuples=tuple(lst)
        new_modularity= comm.modularity(G, community_tuples)
        modularities.append(new_modularity)
        if(modularity<new_modularity): #if we found the new max modularity refresh the value of the variant "modularity"
            modularity= new_modularity
            final_tuples_to_print=community_tuples
        number_of_communities.append(len(community_tuples))
        i=i+1
    fig = plt.figure(figsize = (10, 5))
    plt.bar(number_of_communities, modularities, color ='skyblue',width = 0.4)
    plt.grid(True, color = "grey", linewidth = "1.4", linestyle = "-")
    plt.xlabel("NUMBER OF COMMUNITIES IN PARTITION")
    plt.ylabel("MODAITY VALUE OF PARTITION")
    plt.title("Bar-Chart of Modality Values of Partiotions in Hierarchy")
    print("max modularity is: ",modularity)
    plt.show()
    visualize_communities(G,final_tuples_to_print,graph_layout,node_positions)
    return community_tuples

######################################################################################################################
def add_hamilton_cycle_to_graph(G,node_names_list):
    length_G=len(G)
    length_N=len(node_names_list)
    for i in range(length_N-1): #if it does not exist already. create edge from node i to node i+1
        if(not G.has_edge(node_names_list[i],node_names_list[i+1])):
            G.add_edge(node_names_list[i],node_names_list[i+1])
    if(not G.has_edge(node_names_list[-1],node_names_list[0])): #create edge from the last node to the first node so we have a circle
        G.add_edge(node_names_list[-1],node_names_list[0])

######################################################################################################################
# ADD RANDOM EDGES TO A GRAPH...
######################################################################################################################
def add_random_edges_to_graph(G,node_names_list,NUM_RANDOM_EDGES,EDGE_ADDITION_PROBABILITY):
    length_G=len(G)
    length_N=len(node_names_list)
    for i in range(length_G): #for every node in G
        if(i in G): #check if the node with new_id=i is not in graph (might be removed while reading the file)
            counter=0
            while(counter<NUM_RANDOM_EDGES): #try to create new NUM_RANDOM_EDGES from node i
                counter= counter+1
                randomNode= random.randint(0, length_N-1)
                if (not G.has_edge(i,node_names_list[randomNode])) and (i!=node_names_list[randomNode]): #check if edge already exists
                    prob= random.randint(0,9)
                    if(prob<EDGE_ADDITION_PROBABILITY*10): #create this edge with probability=EDGE_ADDITION_PROBABILITY
                        G.add_edge(i,node_names_list[randomNode])
    return G

######################################################################################################################
# VISUALISE COMMUNITIES WITHIN A GRAPH
######################################################################################################################
def visualize_communities(G,community_tuples,graph_layout,node_positions):
    color_map= my_random_color_list_generator(len(community_tuples))
    final_colors=[]
    for node in G:
        for i in range(len(community_tuples)):
            if node in community_tuples[i]: #give to node the color of the community that it belongs
                final_colors.append(color_map[i])
                continue
    print("Showing ",len(color_map)," communities")
    my_graph_plot_routine(G,final_colors,"purple","solid",graph_layout,node_positions)

########################################################################################
########################### ROUTINES LIBRARY ENDS ###########################
########################################################################################
def sortListOfLists(array):
    for i in range(len(array)):
        for j in range(len(array)):
            if(len(array[i])>len(array[j])):
                tmp=array[j]
                array[j]=array[i]
                array[i]=tmp

########################################################################################
############################# MAIN MENUS OF USER CHOICES ###############################
########################################################################################

############################### GLOBAL INITIALIZATIONS #################################
G = nx.Graph()                      # INITIALIZATION OF THE GRAPH TO BE CONSTRUCTED
node_names_list = []
node_positions = []                 # INITIAL POSITIONS OF NODES ON THE PLANE ARE UNDEFINED...
community_tuples = []               # INITIALIZATION OF LIST OF COMMUNITY TUPLES...
hierarchy_of_community_tuples = []  # INITIALIZATION OF HIERARCHY OF COMMUNITY TUPLES

G,node_names_list,node_positions = my_menu_graph_construction(G,node_names_list,node_positions)
my_menu_community_detection(G,node_names_list,node_positions,hierarchy_of_community_tuples)


from django.shortcuts import render, redirect
from fish_bowl_monitor.models import TankTemp
from rest_framework.views import APIView
from rest_framework.response import Response

def fish_home_chart(request):
    check_db = TankTemp.objects.latest('date_time_stamp')
    oldest = TankTemp.objects.earliest('date_time_stamp')
    return render(request, 'chart.html', {
        'current_tank_temp': check_db.temperature_data,
        'earliest_tank_temp': oldest.temperature_data,
        'earliest_date': oldest.date_time_stamp,
    })

def fish_home(request):
    check_db = TankTemp.objects.latest('date_time_stamp')
    oldest = TankTemp.objects.earliest('date_time_stamp')
    return render(request, 'home.html', {
        'current_tank_temp': check_db.temperature_data,
        'earliest_tank_temp': oldest.temperature_data,
        'earliest_date': oldest.date_time_stamp,
        }
    )

def add_records():
    for idx in range(0, 2000):
        print("hello?!?!")
        TankTemp.objects.create(temperature_data=0, weather_data_temp=0)

def set_up_db(request):
    add_records()
    # return redirect(f'/')

class ChartData(APIView):
    authentication_classes = []
    permission_classes = []

    def get(self, reqest):
        # Petty sure this can get cleaned up, but I need to get some reps in before it's clear how
        total = TankTemp.objects.all().count()
        qset = TankTemp.objects.filter().order_by('date_time_stamp').values_list('date_time_stamp', 'temperature_data', 'weather_data_temp')[total - 1440:total:60]
        labels = []
        default_data = []
        outside_temp = []
        for stuff in qset:
            default_data.append(stuff[1])
            dtg = stuff[0].strftime("%b %d %Y %H:%m")
            labels.append(dtg)
            outside_temp.append(stuff[2])

        data = {
            'labels': labels,
            'default_data': default_data,
            'outside_temp': outside_temp,
        }
        return Response(data)


from evaluation.metrics import evaluate
from models.predictor import predict
from utils.argcheck import check_float_positive, check_int_positive
from utils.io import load_numpy
from utils.modelnames import models
from utils.progress import WorkSplitter, inhour

import argparse
import numpy as np
import time

def main(args):
    # Progress bar
    progress = WorkSplitter()

    # Show hyper parameter settings
    progress.section("Parameter Setting")
    print("Data Path: {}".format(args.path))
    print("Train File Name: {}".format(args.train))
    if args.validation:
        print("Valid File Name: {}".format(args.valid))
    print("Algorithm: {}".format(args.model))
    print("Rank: {}".format(args.rank))
    print("Lambda: {}".format(args.lamb))
    print("Trainig Epoch: {}".format(args.epoch))
    print("Evaluation Ranking Topk: {}".format(args.topk))

    # Load Data
    progress.section("Loading Data")
    start_time = time.time()
    R_train = load_numpy(path=args.path, name=args.train)
    print("Elapsed: {}".format(inhour(time.time() - start_time)))
    print("Train U-I Dimensions: {}".format(R_train.shape))

    progress.section("Train")
    RQ, Yt, Bias = models[args.model](R_train, epoch=args.epoch, lamb=args.lamb,
                                      rank=args.rank, corruption=args.corruption)
    Y = Yt.T

    progress.section("Predict")
    prediction = predict(matrix_U=RQ,
                         matrix_V=Y,
                         bias=Bias,
                         topK=args.topk,
                         matrix_Train=R_train,
                         gpu=args.gpu)

    if args.validation:
        progress.section("Create Metrics")
        start_time = time.time()

        metric_names = ['R-Precision', 'NDCG', 'Clicks', 'Recall', 'Precision', 'MAP']

        R_valid = load_numpy(path=args.path, name=args.valid)
        result = evaluate(prediction, R_valid, metric_names, [args.topk])
        print("-")
        for metric in result.keys():
            print("{}:{}".format(metric, result[metric]))
        print("Elapsed: {}".format(inhour(time.time() - start_time)))

if __name__ == "__main__":
    # Commandline arguments
    parser = argparse.ArgumentParser(description="Main")

    parser.add_argument('--corruption', dest='corruption', type=check_float_positive, default=0.5)
    parser.add_argument('--disable_validation', dest='validation', action='store_false')
    parser.add_argument('--epoch', dest='epoch', type=check_int_positive, default=1)
    parser.add_argument('--gpu', dest='gpu', action='store_true')
    parser.add_argument('--lamb', dest='lamb', type=check_float_positive, default=100)
    parser.add_argument('--model', dest='model', default="VAE-CF")
    parser.add_argument('--path', dest='path', default="data/yelp/")
    parser.add_argument('--rank', dest='rank', type=check_int_positive, default=100)
    parser.add_argument('--topk', dest='topk', type=check_int_positive, default=50)
    parser.add_argument('--train', dest='train', default='Rtrain.npz')
    parser.add_argument('--valid', dest='valid', default='Rvalid.npz')
    args = parser.parse_args()

    main(args)


#!/usr/bin/python3
"""sends a request to the URL and displays the body of the response"""
import urllib.request
import urllib.error
import sys

def send_error():
    """sends a request to the URL and displays the body of the response"""
    try:
        with urllib.request.urlopen(sys.argv[1]) as response:
            html = response.read()
            print(html.decode("utf-8"))
    except urllib.error.HTTPError as error:
        print("Error code: {}".format(error.code))

if __name__ == "__main__":
    send_error()


# import deendencies
import openml
import time
import json
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn import svm
from sklearn.preprocessing import MinMaxScaler
import pickle
from sklearn import metrics

'''
Function to extract dataset and task from openML
'''

def extract_data_openml(data_id,task_id):
    data = openml.datasets.get_dataset(data_id)
    print(data.features)
    task = openml.tasks.get_task(task_id)
    print ("task details: ", task)
    print("estimation Procedure: ", task.estimation_procedure)
    print('evaluation_measure:', task.estimation_procedure)
    X, y, categorical_indicator, attribute_names = data.get_data(dataset_format='array',target=data.default_target_attribute)
    print(pd.DataFrame(X).head())
    print(pd.DataFrame(y).head())

    return  task.estimation_procedure,X,y

'''
Function to preprocess the data
'''

def data_preprocess(X,y):

    min_max_scaler = MinMaxScaler()
    X = min_max_scaler.fit_transform(X)
    print(pd.DataFrame(X).head())
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.40, random_state=42, stratify=y)

    print(X_train.shape)
    print(y_train.shape)

    return X_train, X_test, y_train, y_test

'''
Function to Model Creation
'''

def model(X_train,y_train):
    # model = GaussianNB()
    model = svm.SVC(gamma="scale", kernel="poly", degree=7)

    '''
    The following commented lines could b used if we need to run out model on the task of openML.
    '''
    # r, f = openml.runs.run_model_on_task(model=gnb, task=task, avoid_duplicate_runs=False, upload_flow=False,
    # return_flow=True)
    # score = []
    # evaluations = r.fold_evaluations['predictive_accuracy'][0]
    # print(evaluations)
    # for key in evaluations:
    #     print(key)
    #     print(evaluations[key])
    #     score.append(evaluations[key])
    # print("Mean Accuracy:",np.mean(score))

    model.fit(X_train,y_train)

    # save the model to disk as Pickle File
    Pkl_Filename = "Model.pkl"

    with open(Pkl_Filename, 'wb') as file:
        pickle.dump(model, file)

    return Pkl_Filename

'''
Function to test model performance
'''

def test(X_test ,y_test,Pkl_Filename):

    with open(Pkl_Filename, 'rb') as file:
        model1 = pickle.load(file)

    y_pred= model1.predict(X_test)
    result = model1.score(X_test, y_test)
    print("Result on Test Data:",result)

    # Matrices
    confusion_matrix = metrics.confusion_matrix(y_test, y_pred)
    precision= metrics.precision_score(y_test, y_pred,average='weighted')
    recall = metrics.recall_score(y_test, y_pred,average='weighted')
    print(precision, recall)
    print(confusion_matrix)
    confusion_matrix = confusion_matrix.tolist()
    report = {}
    report["precision"]= precision
    report["recall"]= recall
    report["confusion_matrix"] = confusion_matrix
    return report

'''
Function to create JSON
'''

def create_json(report, time,task):
    dict1={}
    dict= {}
    report["time"]= str(time)
    dict["dataset"]='gas-drift'
    dict["classifier:"]="sklearn.svm.SVC(gamma="'scale'", kernel="'poly'", degree=7)"
    dict["settings:"]= task
    dict["result"]= report

    dict1["svm-openml"]= dict
    dict1 = json.dumps(dict1)

    return dict1

'''
Function to call all other methods
'''

def main():
    task, X, y = extract_data_openml(1476,9986)
    start_time = time.clock()
    X_train, X_test, y_train, y_test = data_preprocess(X,y)
    Pkl_Filename = model(X_train,y_train)
    tim = (time.clock() - start_time)
    print("Training Time:",tim ,"seconds")
    report = test(X_test,y_test, Pkl_Filename)
    jsn = create_json(report, tim,task )
    print(jsn)

if __name__ == "__main__":
    main()


import js
from pyodide.ffi import create_proxy, to_js

d3 = js.d3

fruits = [
    {"name": "🍊", "count": 21},
    {"name": "🍇", "count": 13},
    {"name": "🍏", "count": 8},
    {"name": "🍌", "count": 5},
    {"name": "🍐", "count": 3},
    {"name": "🍋", "count": 2},
    {"name": "🍎", "count": 1},
    {"name": "🍉", "count": 1},
]

fn = create_proxy(lambda d, *_: d["count"])
data = d3.pie().value(fn)(to_js(fruits))

arc = (
    d3.arc()
    .innerRadius(210)
    .outerRadius(310)
    .padRadius(300)
    .padAngle(2 / 300)
    .cornerRadius(8)
)

py = d3.select("#py")
py.select(".loading").remove()

svg = (
    py.append("svg")
    .attr("viewBox", "-320 -320 640 640")
    .attr("width", "400")
    .attr("height", "400")
)

for d in data:
    d_py = d.to_py()

    (svg.append("path").style("fill", "steelblue").attr("d", arc(d)))

    text = (
        svg.append("text")
        .style("fill", "white")
        .attr("transform", f"translate({arc.centroid(d).join(',')})")
        .attr("text-anchor", "middle")
    )

    (
        text.append("tspan")
        .style("font-size", "24")
        .attr("x", "0")
        .text(d_py["data"]["name"])
    )

    (
        text.append("tspan")
        .style("font-size", "18")
        .attr("x", "0")
        .attr("dy", "1.3em")
        .text(d_py["value"])
    )


st = "Programming is fun for many and for some it is not that fun"

words = {}
for word in st.split():
    if word in words:
        words[word] = words[word] + 1   # Increment count for existing word
    else:
        words[word] = 1   # First time word is encountered

print(words)


class Bstree:
    def __init__(self,val):
        self.root = val
        self.left = None
        self.right = None

    def inorder(self):
        print("inorder")
        if self.left:
            self.left.inorder()
        print(self.root)
        if self.right:
            self.right.inorder()

def minimal_tree(array):

    if not array:
        return None

    middle = len(array) // 2

    root = Bstree(array[middle ])

    root.left = minimal_tree(array[:middle ])

    root.right = minimal_tree(array[middle+1:])

    return root

def inorder(root):

    if root.left:
        inorder(root.left)
    print(root.root)
    if root.right:
        inorder(root.right)

array = [1,2,3,4,5,6,7,8]
response = minimal_tree(array)

inorder(response)


load("//infra/bazel:build.bzl", "foreign_go_binary")

def gen_targets(matrix):
  pkg = "./infra/control/main"
  output = "v2ctl"

  for (os, arch, ver) in matrix:

    if arch in ["arm"]:
      bin_name = "v2ctl_" + os + "_" + arch + "_" + ver
      foreign_go_binary(
        name = bin_name,
        pkg = pkg,
        output = output,
        os = os,
        arch = arch,
        ver = ver,
        arm = ver,
        gotags = "confonly",
      )

    else:
      bin_name = "v2ctl_" + os + "_" + arch
      foreign_go_binary(
        name = bin_name,
        pkg = pkg,
        output = output,
        os = os,
        arch = arch,
        ver = ver,
        gotags = "confonly",
      )

      if arch in ["mips", "mipsle"]:
        bin_name = "v2ctl_" + os + "_" + arch + "_softfloat"
        foreign_go_binary(
          name = bin_name,
          pkg = pkg,
          output = output + "_softfloat",
          os = os,
          arch = arch,
          ver = ver,
          mips = "softfloat",
          gotags = "confonly",
        )


""" PyXLL extension loader.

Loads all the extensions contributing to the pyxll.modules namespace. This
module is supposed to be added to you pyxll.cfg file to automatically load all
the installed packages that contributes to PyXLL through setuptools.

"""
import errno
import logging
import os

from stevedore import extension

from pyxll import get_config
from .ribbon_synthesizer import RibbonSynthesizer

logger = logging.getLogger(__name__)

# Keyword args to pass to plugin initializers
invoke_kwds = {}

config = dict(get_config().items('PYXLL'))
default_ribbon_path = config.get('default_ribbon', '')
ribbon_path = config.get('ribbon', '')

should_make_ribbon = (default_ribbon_path and ribbon_path and
                      default_ribbon_path != ribbon_path)
ribbon_synthesizer = RibbonSynthesizer.from_file(default_ribbon_path)

if should_make_ribbon:
    invoke_kwds['submit_ribbon_tab'] = ribbon_synthesizer.submit_ribbon_tab
else:
    logger.info("The ribbon will not be modified because your config does"
                " not define both PYXLL::ribbon and PYXLL::default_ribbon")

extension_manager = extension.ExtensionManager(
    namespace='pyxll.extensions',
    invoke_on_load=True,
    invoke_kwds=invoke_kwds,
)

extension_names = extension_manager.names()

if len(extension_names) > 0:
    logger.info(
        'Extensions loaded:\n{}'.format('\n'.join(extension_names))
    )
else:
    logger.info('No extension loaded')

if should_make_ribbon:
    target_dir = os.path.dirname(ribbon_path)
    try:
        # If the ribbon dirs don't exist, make them.
        os.makedirs(target_dir)
        logger.info("Created ribbon directory: {}".format(target_dir))
    except Exception as e:
        if not e.errno == errno.EEXIST:
            raise
    with open(ribbon_path, 'wb') as f:
        f.write(ribbon_synthesizer.to_bytes())
        logger.info("Wrote extended ribbon to {}".format(ribbon_path))


# Given the roots of two binary trees p and q, write a function to check if they are the same or not.
# Two binary trees are considered the same if they are structurally identical, and the nodes have the same value.

import math
from typing import Optional

class TreeNode:
    def __init__(self, val:int=None, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

# signigicant Runtime and Memory performance
class Solution:
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        if not p and not q:
            return True
        if not p or not q:
            return False
        if p.val == q.val:
            return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
        return False

# class Solution:
#     def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
#         if not p and not q:
#             return True
#         if not p or not q:
#             return False

#         lf, ln = [p], []
#         rt, rn = [q], []
#         while lf and rt:
#             if len(lf)!=len(rt):
#                 return False

#             for i in range(len(lf)):
#                 if lf[i].val != rt[i].val:
#                     return False

#                 if lf[i].left and rt[i].left:
#                     ln.append(lf[i].left)
#                     rn.append(rt[i].left)
#                 elif not lf[i].left and not rt[i].left:
#                     pass
#                 else:
#                     return False

#                 if lf[i].right and rt[i].right:
#                     ln.append(lf[i].right)
#                     rn.append(rt[i].right)
#                 elif not lf[i].right and not rt[i].right:
#                     pass
#                 else:
#                     return False
#             lf, ln = ln, []
#             rt, rn = rn, []

#         if lf or rt:
#             return False
#         else:
#             return True

s = Solution()
print(s.isSameTree(
    TreeNode(1, TreeNode(2), TreeNode(3)),
    TreeNode(1, TreeNode(2), TreeNode(3))
    ))
print(s.isSameTree(
    TreeNode(1, TreeNode(2)),
    TreeNode(1, None, TreeNode(2))
    ))
print(s.isSameTree(
    TreeNode(1, TreeNode(2), TreeNode(1)),
    TreeNode(1, TreeNode(1), TreeNode(2))
    ))
print(s.isSameTree(TreeNode(), TreeNode()))


countries = ['Netherlands','Nigeria','Jordan','Nepoal','Niger','Japan']
population = [17_500_00, 198_000_000, 10_000_000, 30_000_000, 24_000_000, 128_000_000]

max(population)
min(countries)

# when comparing strings, it counts the alphabetical order and the lenngth
# of the string
# Niger < Nigeria
# Japan < Niger
# Nepal < Niger

# When comparing tuples, it compares the first index first and once it finds
# the min or max of that it stops looking.

country_pop = list(zip(countries,population))
min(country_pop) # returns Japan

def get_population(pair):
    country,population = pair
    return population

min(zip(countries,population), key=get_population) #returns jordan as iot should

min(zip(countries,population, key = lambda x: x[1])) # simplifies it using a labda function

min(zip(population, countries)) #does things properly taking into account mijn and max eccenticities


import numpy as np
import pygame
from OpenGL.GL import *
from pygame.locals import *

from lab0809.utils import project_with_angle

class MyOpenGL:
    def __init__(self, width, height):
        self._width = width
        self._height = height

        self._clock = pygame.time.Clock()

        self._init()

        self.objects = []

    def _init(self):
        pygame.init()
        pygame.display.set_mode((self._width, self._height), DOUBLEBUF | OPENGL)

        glOrtho(0, self._width, 0, self._height, 2000, -2000)
        glTranslatef(self._width / 2, self._height / 2, 0)

        glEnable(GL_DEPTH_TEST)
        glEnable(GL_LIGHTING)
        glEnable(GL_BLEND)
        glEnable(GL_NORMALIZE)
        glShadeModel(GL_SMOOTH)

        glEnable(GL_COLOR_MATERIAL)
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)

        glEnable(GL_LIGHT0)
        glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.2, 0.2, 1])
        glLightfv(GL_LIGHT0, GL_DIFFUSE, [4, 4, 4, 1])
        glLightfv(GL_LIGHT0, GL_POSITION, [0, 1, 0, 0])

    @staticmethod
    def draw(objects):
        rotations = [0 for _ in objects]
        # light_angle = [0, 0, 1]
        glRotatef(-40, 1, .5, 0)

        while True:
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)

            shadow_casters = []
            draw_bottom_plane()

            # light_angle = project_with_angle(light_angle, 0.1, 0.05, 0.025)
            #
            # glLightfv(GL_LIGHT0, GL_POSITION, light_angle + [0])

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    quit()

            for tor_i, tor in enumerate(objects):
                glPushMatrix()
                rotations[tor_i] += (tor_i + 1) * 1.5

                glRotatef(rotations[tor_i], 0, 1, 0)
                glRotatef(rotations[tor_i], 0, 0, 1)
                for face in tor.draw_gl():
                    rotation = np.radians(rotations[tor_i])
                    shadow_casters.append(
                        [project_with_angle(v, theta_x=0, theta_y=-rotation, theta_z=-rotation) for v in face]
                    # [rotate(v, theta_x=rotation) for v in face]
                    # [rotate(v, theta_y=-rotation) for v in face]
                    # [rotate(v, theta_z=-rotation) for v in face]
                    )

                glPopMatrix()

            glBegin(GL_TRIANGLES)
            for shadow_caster in shadow_casters:
                glColor3fv((0, 0, 0))
                for (x, y, z) in shadow_caster:
                    glVertex3fv((x, -349.5, z))
            glEnd()

            pygame.display.flip()
            pygame.time.wait(10)

def draw_bottom_plane():
    bottom_y = -350
    size = 1200

    glColor3fv((.9, .3, .1))
    v1, v2, v3, v4 = \
        (-size / 2, bottom_y, -size / 2), \
        (-size / 2, bottom_y, size / 2), \
        (size / 2, bottom_y, size / 2), \
        (size / 2, bottom_y, -size / 2)

    glBegin(GL_QUADS)
    for v in (v1, v2, v3, v4):
        glVertex3fv(v)
    glEnd()

    return [0, 0, 0, bottom_y]


import logging
import shutil
import nibabel as nib
from typing import Union
from pathlib import Path
from dtip.utils import show_exec_time

__all__ = ["locate_dti_files"]

def locate_dti_files(
    input_path: Union[str, Path],
    output_path: Union[str, Path],
    protocol_names: list,
    n_gradients: int,
    ret_paths: bool = True,
) -> Union[dict, int]:
    """Locate DTI-related data and metadata file

    Args:
        input_path: folder containing DTI files.
        output_path: copy located files (if any) to `output_path`
            and rename as `dtidata.<extension>.`
        ret_paths: if True, return dict with selected files paths where
            key is file extension and value is the file path.

    Returns:
        if ret_paths is True, return dict with selected files paths where
        key is file extension and value is the file path.
        Otherwise, return 0 on successful execution.
    """

    input_path, output_path = Path(input_path), Path(output_path)
    output_path.mkdir(parents=True, exist_ok=True)

    # First, look for .bvec and .bval files and get the corresponding DTI file
    bval_paths = sorted([p for p in input_path.glob("*") if p.suffix == ".bval"])
    bvec_paths = sorted([p for p in input_path.glob("*") if p.suffix == ".bvec"])

    _err_msg = f"found {len(bval_paths)} .bval files and {len(bvec_paths)} .bval files"
    assert len(bval_paths) == len(bvec_paths), _err_msg

    if len(bvec_paths) == 0:
        _errmsg = "No .bvec file found. Cannot proceed without .bvec file."
        logging.error(_errmsg)
        raise FileNotFoundError(_errmsg)

    # Make protocol and series based pairs
    dti_series_dict = {}
    for protocol_name in protocol_names:
        for bval_filename, bvec_filename in zip(bval_paths, bvec_paths):
            _errmsg = ".bval and .bvec have different filenames."
            assert bvec_filename.stem == bval_filename.stem, _errmsg

            if protocol_name in bvec_filename.stem:
                dti_series_dict[bvec_filename.stem] = {
                    "bvec": bvec_filename,
                    "bval": bval_filename,
                }

    # Add DTI nifti files to series
    for name, paths in dti_series_dict.items():
        for p in input_path.glob("*"):
            if (str(p).endswith(".nii.gz")) and (p.stem == f"{name}.nii"):
                # Check if the DTI volume has required gradients directions
                if nib.load(p).shape[3] == n_gradients:
                    paths.update({"nifti": p})

    # Keep series with all the required files
    dti_series_dict = {  # series at least have ['bvec', 'bval', 'nifti']
        k: v for k, v in dti_series_dict.items() if len(v) >= 3
    }

    if len(dti_series_dict) == 0:
        _errmsg = "No matching DTI series found."
        logging.error(_errmsg)
        raise FileNotFoundError(_errmsg)

    if ret_paths:
        return dti_series_dict

    # Rest of the series are same. Use anyone of them
    if len(dti_series_dict) > 1:
        _msg = f"Located multiple matching series. \n{dti_series_dict.keys()}"
        logging.info(_msg)
    else:
        logging.info(f"Matched series = {dti_series_dict.keys()}")

    for series_name, selected_paths in dti_series_dict.items():
        if protocol_name[0] in series_name:
            break

    # Get related metadata JSON file
    if "DTImediumiso" in series_name:
        series, acq = series_name.replace("xDTImediumiso", "")[1:].split("a")
        selected_json = [
            p
            for p in input_path.glob("*")
            if (("DTI_medium_iso" in p.stem) and (p.suffix == ".json"))
        ]
        if len(selected_json) > 0:
            selected_json = [
                p for p in selected_json if p.stem.split("_")[-1] == series
            ]
    else:
        _series = series_name.split("_")[-1]
        selected_json = [
            p
            for p in input_path.glob("*")
            if (("DTI_medium_iso" in p.stem) and (p.suffix == ".json"))
        ]
        if len(selected_json) > 0:
            selected_json = [
                p for p in selected_json if p.stem.split("_")[-1] == _series
            ]
    if len(selected_json) > 0:  # Add the selected JSON to selected_paths
        selected_paths["json"] = selected_json[0]

    # Copy selected files to the output folder
    for name, src in selected_paths.items():
        dst = output_path / src.name
        shutil.copy(src, dst)
        logging.info(f"Copied {name} @ {dst}")
        selected_paths[name] = dst

    return 0, selected_paths


from pysql.core import FieldKey, FieldType, TYPE_MAP
from pysql.core import SerializedInterface

class Field(SerializedInterface):
    def __init__(self, data_type, keys=FieldKey.NULL, default=None):
        self.__type = data_type
        self.__keys = keys
        self.__default = default
        self.__values = []
        self.__rows = 0

    def add(self, value):
        if value is None:
            value = self.__default
        self.__values.append(value)
        self.__rows += 1

    def serialized(self):
        return SerializedInterface.json.dumps({
            'key': [key.value for key in self.__keys],
            'type': self.__type.value,
            'values': self.__values,
            'default': self.__default
        })

    @staticmethod
    def deserialized(data):
        json_data = SerializedInterface.json.loads(data)
        keys = [FieldKey(key) for key in json_data['key']]
        obj = Field(FieldType(json_data['type']), keys, default=json_data['default'])
        for value in json_data['values']:
            obj.add(value)
        return object


import sys
sys.path.append("../")
sys.path.append("../tfidf")
sys.path.append("../tfidf/tfidf_/")
sys.path.append("../tfidf/tftdf_data/")
from fileObject import FileObj
from sentenceSimilarity import SentenceSimilarity
from sentence import Sentence
from sklearn.externals import joblib
from cutWords import *
from BaseRank import *
sys.path.append('../dataprocess')
from dataprocess.unit import *
from BaseRank import *

class TfidfRank(BaseRank):
    def __init__(self,train_sentences,q_train_sentences,answer):
        self.unitset = None
        self.context = train_sentences
        self.answer = answer
        self.question = q_train_sentences
        self.seg = Seg()
        self.ss = SentenceSimilarity(self.seg)
        self.ss.set_sentences(train_sentences)
        self.ss.TfidfModel()
        self.qss = SentenceSimilarity(self.seg)
        self.qss.set_sentences(self.question)
        self.qss.TfidfModel()

    def distance(self,s1,s2):
        s1 =','.join(s1.context)
        s2 = ','.join(s2.context[:-1])
        sim = self.ss.sensimilarity(s1,s2)
        return sim

    def search(self,s,n):
        q_sims = self.qss.similarity(s.context[-1])
        context_sims = self.ss.similarity(' '.join(s.context[:-1]))
        sims = [sum(sim) for sim in zip(q_sims,context_sims)]
        sims = sorted(list(enumerate(sims)), key=lambda item: item[1], reverse=True)
        con_sorted_sims = [self.context[sim[0]].split(',') for sim in sims][:n]
        q_sorted_sims = [[self.question[sim[0]]] for sim in sims][:n]
        a_sorted_sims = [[self.answer[sim[0]]] for sim in sims][:n]
        sorted_sim = [sim[0]+sim[1]+sim[2] for sim in zip(con_sorted_sims,q_sorted_sims,a_sorted_sims)]
        return sorted_sim
#
# file_obj = FileObj(r"../tfidf/tfidf_data/context.txt")
# train_sentences = file_obj.read_lines()
# baserank = TfidfRank(train_sentences)
# baserank.set('../data/five_set.set')
# s = baserank.unitset[0]
# result = baserank.search(s)
# for r in result:
#     print(r)


AFNUM_RESERVED = 0  # Reserved
AFNUM_INET = 1  # IP (IP version 4)
AFNUM_INET6 = 2  # IP6 (IP version 6)
AFNUM_NSAP = 3  # NSAP
AFNUM_HDLC = 4  # HDLC (8-bit multidrop)
AFNUM_BBN1822 = 5  # BBN 1822
AFNUM_802 = 6  # 802 (includes all 802 media plus Ethernet "canonical format")
AFNUM_E163 = 7  # E.163
AFNUM_E164 = 8  # E.164 (SMDS, Frame Relay, ATM)
AFNUM_F69 = 9  # F.69 (Telex)
AFNUM_X121 = 10  # X.121 (X.25, Frame Relay)
AFNUM_IPX = 11  # IPX
AFNUM_ATALK = 12  # Appletalk
AFNUM_DECNET = 13  # Decnet IV
AFNUM_BANYAN = 14  # Banyan Vines
AFNUM_E164NSAP = 15  # E.164 with NSAP format subaddress
AFNUM_DNS = 16  # DNS (Domain Name System)
AFNUM_DISTNAME = 17  # Distinguished Name
AFNUM_AS_NUMBER = 18  # AS Number
AFNUM_XTP_IP4 = 19  # XTP over IP version 4
AFNUM_XTP_IP6 = 20  # XTP over IP version 6
AFNUM_XTP = 21  # XTP native mode XTP
AFNUM_FC_WWPN = 22  # Fibre Channel World-Wide Port Name
AFNUM_FC_WWNN = 23  # Fibre Channel World-Wide Node Name
AFNUM_GWID = 24  # GWID
# draft-kompella-ppvpn-l2vpn
AFNUM_L2VPN = 25
AFNUM_L2VPN_OLD = 196
AFNUM_EIGRP_COMMON = 16384  # EIGRP Common Service Family
AFNUM_EIGRP_IPV4 = 16385  # EIGRP IPv4 Service Family
AFNUM_EIGRP_IPV6 = 16386  # EIGRP IPv6 Service Family
AFNUM_LCAF = 16387  # LISP Canonical Address Format
AFNUM_BGPLS = 16388  # rfc7752


import requests

import csv
import datetime
import json
import os

class SlackAgentClient(object):
    """
    Class to post messages to slack
    """
    def webhook_post(self, district, message):
        url = os.getenv('SLACK_WEBHOOK_URL')
        text = f"*[Vaccine Slot Alert][{district}]*\n```{message}```"
        payload = {
            "text": text
        }
        requests.request("POST", url, data=json.dumps(payload), headers={}, params={})

class VaccineSlotFinder:

    AGE_LIMIT = 18
    DATE_FORMAT = '%d-%m-%Y'

    PIN_WHITELIST = ['245304']

    DISTRICT_MAP = json.loads(os.getenv('DISTRICT_MAP'))

    DAY_COUNT = 90

    def __init__(self):
        self.base_url = "https://cdn-api.co-vin.in/api/v2"
        self.found = False

    def _process_response(self, response, string):
        center_found = False
        for center in response["centers"]:
            sessions = center["sessions"]
            for session in sessions:
                if session["min_age_limit"] == self.AGE_LIMIT and session["available_capacity"] > 0:
                    center_found = True
                    self.found = True

                    timings = ", ".join(session["slots"])
                    message = f"""
                    Date: {session["date"]}
                    No. of Slots: {session["available_capacity"]}
                    Timings: {timings}
                    Vaccine: {session["vaccine"]}
                    Center Name:    {center["name"]}
                    Block:  {center["block_name"]}
                    District: {center["district_name"]}
                    Charges: {center["fee_type"]}
                    Pincode: {center["pincode"]}
                    """
                    SlackAgentClient().webhook_post(center["district_name"], message)

        if not center_found:
            print("Not found")
            # SlackAgentClient().webhook_post(f"""Not found for {string}""")

    def controller(self):
        for district in self.DISTRICT_MAP.keys():
            date = None
            for week in range(int(self.DAY_COUNT/7)):
                date = self._get_next_date(date)
                print(f"Checking for district {self.DISTRICT_MAP[district]} on date {date}")
                self.find_by_district(district, date)

            # if not self.found:
            #     SlackAgentClient().webhook_post(self.DISTRICT_MAP[district], "Slots not available")

    def find_by_pincode(self, pincode, date):
        path = "/appointment/sessions/public/calendarByPin"
        url = self.base_url + path
        querystring = {"pincode": pincode, "date": date}

        payload = ""

        response = requests.request("GET", url, data=payload, headers={}, params=querystring)

        response = response.json()

        self._process_response(response, f"{pincode} on {date}")

    def find_by_district(self, district, date):
        path = "/appointment/sessions/public/calendarByDistrict"
        url = self.base_url + path
        querystring = {"district_id": district, "date": date}

        payload = ""

        response = requests.request("GET", url, data=payload, headers={}, params=querystring)

        response = response.json()

        self._process_response(response, f"{self.DISTRICT_MAP[district]} on {date}")

    def print_states_csv(self):
        path= "/admin/location/states"
        url = self.base_url + path

        payload = ""
        response = requests.request("GET", url, data=payload, headers={}, params={})

        response = response.json()
        with open('districts.csv', 'w', newline='') as file:
            writer = csv.writer(file)
            writer.writerow(["State Name", "State ID", "District Name", "District ID"])

        for res in response["states"]:
            # print(f"""{res["state_name"]},{res["state_id"]}""")
            self._print_districts_csv(res["state_id"], res["state_name"])

    def _print_districts_csv(self, state_id, state_name):
        path = f"/admin/location/districts/{state_id}"
        url = self.base_url + path

        payload = ""
        response = requests.request("GET", url, data=payload, headers={}, params={})

        response = response.json()
        with open('districts.csv', 'a', newline='') as file:
            writer = csv.writer(file)
            for res in response["districts"]:
                # print(f"""{state_name},{state_id},{res["district_name"]},{res["district_id"]}""")
                writer.writerow([state_name, state_id, res["district_name"], res["district_id"]])

    def _get_next_date(self, cur_date=None):
        if cur_date:
            date = datetime.datetime.strptime(cur_date, self.DATE_FORMAT)
            next = date + datetime.timedelta(days=7)
            return next.strftime(self.DATE_FORMAT)

        # Returns default date as today in IST
        return (datetime.datetime.now()+ datetime.timedelta(hours=5,minutes=30)).strftime(self.DATE_FORMAT)

# AWS Lambda invoker
def lambda_handler(event, context):
    VaccineSlotFinder().controller()

# For local testing
if __name__ == "__main__":
    print("here")
    VaccineSlotFinder().controller()


import subprocess
import os
from zipfile import ZipFile, error
from colorama import Fore
from tqdm import tqdm

def load_data():

    filepath = 'inputs/cell-images-for-detecting-malaria.zip'
    output = 'inputs/'
    if 'cell-images-for-detecting-malaria.zip' not in os.listdir('inputs'):
        print(f'{Fore.YELLOW}Downloading the dataset from Kaggle...')
        subprocess.run(f'kaggle datasets download -d iarunava/cell-images-for-detecting-malaria -p {output}',
                       stdout=subprocess.PIPE,
                       shell=True)
        print(f'{Fore.YELLOW}Download done.')

    with ZipFile(filepath, 'r') as zipObj:
        try:
            for i in tqdm(zipObj.infolist(), desc=f'{Fore.YELLOW}Extracting:'):
                zipObj.extract(i, output)
            print(f'{Fore.GREEN}Files successfully extracted.')
            return True
        except error as e:
            print(f'{Fore.RED}Cannot extract the files:\n{e}')


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
        count=0
        def helper(root,target):
            nonlocal count
            if not root:
                return
            if target==root.val:
                count+=1
            helper(root.left,target-root.val)
            helper(root.right,target-root.val)
        def dfs(root,target):
            if not root:
                return
            helper(root,target)
            dfs(root.left,target)
            dfs(root.right,target)
        dfs(root,targetSum)
        return count


import sys
from io import StringIO
import unittest

class TestClass(unittest.TestCase):
    maxDiff = None
    def assertIO(self, input, output):
        stdout, stdin = sys.stdout, sys.stdin
        sys.stdout, sys.stdin = StringIO(), StringIO(input)
        resolve()
        sys.stdout.seek(0)
        out = sys.stdout.read()[:-1]
        sys.stdout, sys.stdin = stdout, stdin
        self.assertEqual(out, output)

    def test_Sample_Input_1(self):
        input = """AtCoder"""
        output = """Yes"""
        self.assertIO(input, output)

    def test_Sample_Input_2(self):
        input = """Aa"""
        output = """Yes"""
        self.assertIO(input, output)

    def test_Sample_Input_3(self):
        input = """atcoder"""
        output = """No"""
        self.assertIO(input, output)

    def test_Sample_Input_4(self):
        input = """Perfect"""
        output = """No"""
        self.assertIO(input, output)

def resolve():
  alpha2num = lambda c: ord(c) - ord('a')

  from collections import defaultdict

  inf = 10**18+1
  S = list(input())
  N = len(S)
  big = 0
  small = 0
  count = defaultdict(int)
  for i in range(N):
    count[S[i]] += 1
    if count[S[i]] > 1:
      print("No")
      return
    if S[i] <= "Z":
      big += 1
    else:
      small += 1

  print("Yes" if big >= 1 and small >= 1 else "No")

import sys
if sys.argv[-1] == './Main.py':
  resolve()

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


"""Simple built-in debugger. Runs pdb on reception of SIGUSR1 signal."""
import signal

__all__ = ()

def handle_sigusr1(sig, frame):
    print("\nSIGUSR1 signal received. Starting interactive debugger...", flush=True)
    import pdb

    pdb.Pdb().set_trace(frame)

signal.signal(signal.SIGUSR1, handle_sigusr1)


def solve():
    a = input()
    b = input()
    memo = {}
    def recurse(x,y):
        if (x,y) in memo:
            return memo[(x,y)]
        if x == y:
            return True
        if len(x)%2 == 1:
            return x == y
        a1,a2 = x[:len(x)//2],x[len(x)//2:]
        b1,b2 = y[:len(y)//2],y[len(y)//2:]

        memo[(x,y)] = (recurse(a1,b1) and recurse(a2,b2)) or (recurse(a1,b2) and recurse(a2,b1))
        return memo[(x,y)]
    if recurse(a,b):
        return "YES"
    return "NO"

print(solve())


import psycopg2
from psycopg2.extras import DictCursor

import utils

def add_user(steamid:str, name: str):
    try:
        dbConn = psycopg2.connect(utils.utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute(f"INSERT INTO profiles (steamid, name) SELECT '{steamid}', '{name}' WHERE NOT EXISTS(SELECT * FROM profiles WHERE steamid=('{steamid}'));")
        print(f"added user {name} with steamid {steamid}")
    finally:
        dbConn.commit()
        cursor.close()
        dbConn.close()

def get_users():
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute("SELECT * FROM profiles;")
        return cursor.fetchall()
    finally:
        cursor.close()
        dbConn.close()

def add_item(name: str, type: str):
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute(f"""INSERT INTO items (name, type) SELECT '{name}', '{type}' WHERE NOT EXISTS(SELECT * FROM items WHERE name=('{name}'));""")
        print(f"added item {name} with type {type}")
    finally:
        dbConn.commit()
        cursor.close()
        dbConn.close()

def add_item_price(item_name: str, price: int, date: str):
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute(f"INSERT INTO item_prices (item, price, date) SELECT '{item_name}', '{price}', '{date}';")
        print(f"added price {price} to item {item_name}")
    finally:
        dbConn.commit()
        cursor.close()
        dbConn.close()

def add_to_inventory(steamid: str, item_name: str, quantity: int):
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute(f"INSERT INTO profile_items (profile, item, quantity) VALUES ('{steamid}', '{item_name}','{quantity}');")
        print(f"added item {item_name} to inventory of {steamid}")
    finally:
        dbConn.commit()
        cursor.close()
        dbConn.close()

def set_inventory(steamid: str, inventory: dict):
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        clear_inventory(steamid)
        for item_name in inventory:
            add_item(item_name, inventory[item_name]["type"])
            add_to_inventory(steamid, item_name, inventory[item_name]["quantity"])
    finally:
        dbConn.commit()
        cursor.close()
        dbConn.close()

def clear_inventory(steamid: str):
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute(f"DELETE FROM profile_items WHERE profile = '{steamid}';")
        print(f"cleared inventory of {steamid}")
    finally:
        dbConn.commit()
        cursor.close()
        dbConn.close()

def get_inventory(steamid: str):
    '''[profile, item_name, quantity, price, type, date]'''
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        querry = f"""
            SELECT ip.item, quantity, price
            FROM item_prices ip
            JOIN (
            SELECT item, MAX(date) AS max_date
            FROM item_prices
            GROUP BY item
            ) latest ON ip.item = latest.item AND ip.date = latest.max_date
            JOIN profile_items ON profile_items.item = ip.item
            WHERE profile = '{steamid}';
            """
        # cursor.execute(f"SELECT profile, profile_items.item, quantity, price, type, date FROM profile_items JOIN items on item = items.name join item_price on profile_items.item=item_price.item WHERE profile = '{steamid}' order by date;")
        cursor.execute(querry)
        return cursor.fetchall()
    finally:
        cursor.close()
        dbConn.close()

def get_item_list():
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute(f"""
        SELECT items.name, sum(profile_items.quantity) as qnts FROM items
        JOIN profile_items ON profile_items.item = items.name
        GROUP BY items.name
        order by qnts desc
        ;
        """)
        return cursor.fetchall()
    finally:
        cursor.close()
        dbConn.close()

def get_latest_prices():
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute(f"""
            SELECT price, sum(profile_items.quantity) AS qnts, this.item FROM profile_items
            JOIN (
                SELECT ip.item, ip.date, ip.price
                FROM item_prices ip
                JOIN (
                SELECT item, MAX(date) AS max_date
                FROM item_prices
                GROUP BY item
                ) latest ON ip.item = latest.item AND ip.date = latest.max_date
                ORDER BY price DESC
            ) this ON this.item = profile_items.item
            GROUP BY this.item, price
            ORDER BY qnts DESC
            ;
            """)
        return cursor.fetchall()
    finally:
        cursor.close()
        dbConn.close()

def display():
    try:
        dbConn = psycopg2.connect(utils.DB_CONNECTION_STRING)
        cursor = dbConn.cursor(cursor_factory=DictCursor)
        cursor.execute("""
            SELECT profiles.name, profile_items.item, profile_items.quantity, item_prices.date, item_prices.price FROM profiles
            JOIN profile_items ON profiles.steamid = profile_items.profile
            JOIN item_prices ON profile_items.item = item_prices.item
            ORDER BY item_prices.date DESC
        """)
        return cursor.fetchall()
    finally:
        cursor.close()
        dbConn.close()


from __future__ import annotations

from typing import Any, Dict, List, Union

import attr

from binarylane.types import UNSET, Unset

@attr.s(auto_attribs=True)
class Size:
    slug: str
    size_type: str
    available: bool
    regions: List[str]
    price_monthly: float
    price_hourly: float
    disk: int
    memory: int
    transfer: float
    excess_transfer_cost_per_gigabyte: float
    vcpus: int
    vcpu_units: str
    options: Dict[str, str]
    description: Union[Unset, None, str] = UNSET
    cpu_description: Union[Unset, None, str] = UNSET
    storage_description: Union[Unset, None, str] = UNSET

    def to_dict(self) -> Dict[str, Any]:
        slug = self.slug
        size_type = self.size_type

        available = self.available
        regions = self.regions

        price_monthly = self.price_monthly
        price_hourly = self.price_hourly
        disk = self.disk
        memory = self.memory
        transfer = self.transfer
        excess_transfer_cost_per_gigabyte = self.excess_transfer_cost_per_gigabyte
        vcpus = self.vcpus
        vcpu_units = self.vcpu_units
        options = self.options

        description = self.description
        cpu_description = self.cpu_description
        storage_description = self.storage_description

        field_dict: Dict[str, Any] = {}
        field_dict.update(
            {
                "slug": slug,
                "size_type": size_type,
                "available": available,
                "regions": regions,
                "price_monthly": price_monthly,
                "price_hourly": price_hourly,
                "disk": disk,
                "memory": memory,
                "transfer": transfer,
                "excess_transfer_cost_per_gigabyte": excess_transfer_cost_per_gigabyte,
                "vcpus": vcpus,
                "vcpu_units": vcpu_units,
                "options": options,
            }
        )
        if description is not UNSET:
            field_dict["description"] = description
        if cpu_description is not UNSET:
            field_dict["cpu_description"] = cpu_description
        if storage_description is not UNSET:
            field_dict["storage_description"] = storage_description

        return field_dict


""" Module containing DocumentPublisher class """
from ..common.common import SectionHandler

# pylint: disable=too-few-public-methods
class DocumentPublisher(SectionHandler):
    """ Responsible for converting the DocumentPublisher section:
      - /cvrf:cvrfdoc/cvrf:DocumentPublisher
     """
    type_category_mapping = {
        'Vendor': 'vendor',
        'Coordinator': 'coordinator',
        'User': 'user',
        'Discoverer': 'discoverer',
        'Other': 'other',
    }

    def __init__(self, config):
        super().__init__()
        self.name = config.get('publisher_name')
        self.namespace = config.get('publisher_namespace')

    def _process_mandatory_elements(self, root_element):
        self.csaf['name'] = self.name
        self.csaf['namespace'] = self.namespace
        self.csaf['category'] = self.type_category_mapping[root_element.attrib['Type']]

    def _process_optional_elements(self, root_element):
        # optional values
        if hasattr(root_element, 'ContactDetails'):
            self.csaf['contact_details'] = root_element.ContactDetails.text
        if hasattr(root_element, 'IssuingAuthority'):
            self.csaf['issuing_authority'] = root_element.IssuingAuthority.text