import networkx as nx
import re
import uuid

def init_regex():
    # Aufsetzen der regulären Ausdrücke für die Erkennung von technischen Systemen der TSO
    system_naming_conventions_import = dict()
    tsystem_naming_conections_import = dict()
    result_dict = dict()
    results_dict_ts = dict()

    # Funktionale Systeme
    system_naming_conventions_import['Automation System'] = []
    system_naming_conventions_import['Data System'] = []
    system_naming_conventions_import['Electrical System'] = []
    system_naming_conventions_import['Safety System'] = []
    system_naming_conventions_import['Fluid System'] = []
    system_naming_conventions_import['Drainage System'] = ['[\w\- ]*regenwasser[\w\- ]*', '[\w\- ]*brauchwasser[\w\- ]*']
    system_naming_conventions_import['Sanitary System'] = ['[\w\- ]*trinkwasser[\w\- ]*', '[\w\- ]*pwc[\w\- ]*', '[\w\- ]*pwh[\w\- ]*', '[\w\- ]*abwasser[\w\- ]*', '[\w\- ]*schmutzwasser[\w\- ]*']
    system_naming_conventions_import['Ventilation System'] = ['v_[\w\- ]*', '[\w\- ]*ods[\w\- ]*', '[\w\- ]*eta[\w\- ]*', '[\w\- ]*eoa[\w\- ]*', '[\w\- ]*sea[\w\- ]*', '[\w\- ]*eha[\w\- ]*',
                                                              '[\w\- ]*sup[\w\- ]*', '[\w\- ]*zuluft[\w\- ]*', '[\w\- ]*abluft[\w\- ]*', '[\w\- ]*fortluft[\w\- ]*',
                                                              '[\w\- ]*aussenluft[\w\- ]*', '[\w\- ]*außenluft[\w\- ]*']
    system_naming_conventions_import['Heating System'] = ['h_[\w\- ]*', 'hrl[\w\- ]*', 'hvl[\w\- ]*', '[\w\- ]*HRL[\w\- ]*', '[\w\- ]*HVL[\w\- ]*']
    system_naming_conventions_import['Cooling System'] = ['c_[\w\- ]*', 'k_[\w\- ]*']

    # Systemteile
    tsystem_naming_conections_import['Heating System'] = {"Supply System": ['[\w\- ]*vorlauf[\w\- ]*', '[\w\- ]*vl[\w\- ]*'], "Return System": ['[\w\- ]*rücklauf[\w\- ]*', '\w*rl\w*']}

    tsystem_naming_conections_import['Cooling System'] = {"Supply System": ['[\w\- ]*vorlauf[\w\- ]*', '[\w\- ]*vl[\w\- ]*'], "Return System": ['[\w\- ]*rücklauf[\w\- ]*', '\w*rl\w*']}

    tsystem_naming_conections_import['Ventilation System'] = {"Supply System_1": ['[\w\- ]*zuluft[\w\- ]*', '[\w\- ]*sup[\w\- ]*'],
                                                              "Return System_1": ['[\w\- ]*abluft[\w\- ]*','[\w\- ]*eth[\w\- ]*', '[\w\- ]*eta[\w\- ]*'],
                                                              "Return System_2": ['[\w\- ]*fortluft[\w\- ]*', '[\w\- ]*ehh[\w\- ]*', '[\w\- ]*eha[\w\- ]*'],
                                                              "Supply System_2": ['[\w\- ]*oda[\w\- ]*','[\w\- ]*aussenluft[\w\- ]*', '[\w\- ]*außenluft[\w\- ]*'],
                                                              "Distribution System": ['[\w\- ]*sea[\w\- ]*']}

    tsystem_naming_conections_import['Sanitary System'] = {"Return System": ['[\w\- ]*abwasser[\w\- ]*', '[\w\- ]*schmutzwasser[\w\- ]*'],
                                                           "Supply System": ['[\w\- ]*trinkwasser[\w\- ]*', '[\w\- ]*pwc[\w\- ]*', '[\w\- ]*pwh[\w\- ]*']}

    tsystem_naming_conections_import['Drainage System'] = {"Distribution System": ['[\w\- ]*regenwasser[\w\- ]*', '[\w\- ]*brauchwasser[\w\- ]*', '[\w\- ]*abwasser[\w\- ]*']}

    # Kompilieren der regulären Ausdrücke
    for key, values in system_naming_conventions_import.items():
        result_dict[key] = list()
        for value in values:
            value = re.compile(value)
            result_dict[key].append(value)

    for fs_key, values in tsystem_naming_conections_import.items():
        results_dict_ts[fs_key] = dict()
        for ts_key, value in values.items():
            tmp_list = list()
            for val in value:
                val = re.compile(val)
                tmp_list.append(val)
            results_dict_ts[fs_key][ts_key] = tmp_list

    return result_dict, results_dict_ts

def check_for_functional_systems(info_dict, hierarchie_dict):
    # Anreicherung von funktionalen Systemen basierend auf den schwachen Zusammenshangkomponenten und den gegebenen IFC-Systemen
    # Initialisierung der regulären Ausdrücke
    system_naming_conventions, tmp = init_regex()

    system_dict = dict()
    # Betrachtung aller schwach zusammenhängender Systeme
    for key, system in info_dict['Systems'].items():
        if isinstance(system, int):
            continue
        set_ifc_systems = set(system['IFC-Systems'])
        matches_dict = dict()
        # Kontrolle der IFC-Systeme auf Basis der regulären Ausdrücke
        for fs, fs_regex_list in system_naming_conventions.items():
            if not fs_regex_list:
                continue
            k = 0
            for fs_regex in fs_regex_list:
                if k == 1:
                    continue
                for s in set_ifc_systems:
                    if s:
                        # Übernahme der Informationen der IFC-Systeme in anzureichernde technische Systeme
                        if re.match(fs_regex, s.lower()):
                            if fs not in matches_dict:
                                matches_dict[fs] = dict()
                                matches_dict[fs]['Total'] = 1
                                matches_dict[fs]['Components'] = []
                                matches_dict[fs]['IFC-Systems'] = []

                                ifc_systems_list = list(info_dict['IFC-Systems'].keys())
                                for ifc_system in ifc_systems_list:
                                    if ifc_system:
                                        if s == ifc_system[0]:
                                            s = ifc_system
                                matches_dict[fs]['Components'].extend(info_dict['IFC-Systems'][s]['Components'])
                                matches_dict[fs]['IFC-Systems'].append(s[0])

                            else:
                                matches_dict[fs]['Total'] += 1
                                ifc_systems_list = list(info_dict['IFC-Systems'].keys())
                                for ifc_system in ifc_systems_list:
                                    if ifc_system:
                                        if s == ifc_system[0]:
                                            s = ifc_system
                                matches_dict[fs]['Components'].extend(info_dict['IFC-Systems'][s]['Components'])
                                matches_dict[fs]['IFC-Systems'].append(s[0])
                            k = 1

        # Überführung der Informationen in die festgelegte Ausgabestruktur
        # Anlegen eines Systemverbundes und untergeordneter funktionaler Systeme
        if len(matches_dict) > 1:
            system_id = str(uuid.uuid4())
            hierarchie_dict['IS'][system_id] = dict()
            hierarchie_dict['IS'][system_id]['Classification'] = None
            hierarchie_dict['IS'][system_id]['Components'] = system['Components']
            hierarchie_dict['IS'][system_id]['IFC-Systems'] = system['IFC-Systems']
            hierarchie_dict['IS'][system_id]['FS'] = []
            for comp in system['Components']:
                if comp not in system_dict:
                    system_dict[comp] = dict()
                    system_dict[comp]['IS'] = []
                    system_dict[comp]['FS'] = []
                    system_dict[comp]['TS'] = []
                system_dict[comp]['IS'].append(system_id)

            for system_classification, system_value in matches_dict.items():
                fs_system_id = str(uuid.uuid4())
                hierarchie_dict['FS'][fs_system_id] = dict()
                hierarchie_dict['FS'][fs_system_id]['Classification'] = system_classification
                hierarchie_dict['FS'][fs_system_id]['Components'] = system_value['Components']
                hierarchie_dict['FS'][fs_system_id]['IFC-Systems'] = system_value['IFC-Systems']
                hierarchie_dict['FS'][fs_system_id]['TS'] = []
                hierarchie_dict['IS'][system_id]['FS'].append(fs_system_id)

                for comp in system_value['Components']:
                    if comp not in system_dict:
                        system_dict[comp] = dict()
                        system_dict[comp]['IS'] = []
                        system_dict[comp]['FS'] = []
                        system_dict[comp]['TS'] = []
                    system_dict[comp]['FS'].append(fs_system_id)

        # Anlegen eines funktionalen Systems
        elif len(matches_dict) == 1:
            system_id = str(uuid.uuid4())
            hierarchie_dict['FS'][system_id] = dict()
            hierarchie_dict['FS'][system_id]['Classification'] = list(matches_dict.keys())[0]
            hierarchie_dict['FS'][system_id]['Components'] = system['Components']
            hierarchie_dict['FS'][system_id]['IFC-Systems'] = system['IFC-Systems']
            hierarchie_dict['FS'][system_id]['TS'] = []

            for comp in system['Components']:
                if comp not in system_dict:
                    system_dict[comp] = dict()
                    system_dict[comp]['IS'] = []
                    system_dict[comp]['FS'] = []
                    system_dict[comp]['TS'] = []
                system_dict[comp]['FS'].append(system_id)

    return hierarchie_dict, system_dict

def check_for_technical_systems(hierarchie_dict, import_graph, system_dict):
    # Anreicherung von technischen Systemen basierend auf den schwachen Zusammenshangkomponenten und den gegebenen IFC-Systemen
    # Initialisierung der regulären Ausdrücke
    tmp, technicalsystem_naming_conventions = init_regex()

    # Vorbereitung der Anreicherung
    lookup_dict_technical_sytems = import_graph.nodes.data('ifc_system')

    lookup_dict_components = dict()
    for node in import_graph.nodes(data=True):
        if not node[1]['ifc_system']:
            continue
        if node[1]['ifc_system'][0] not in lookup_dict_components:
            lookup_dict_components[node[1]['ifc_system'][0]] = list()
            lookup_dict_components[node[1]['ifc_system'][0]].append(node[0])
        else:
            lookup_dict_components[node[1]['ifc_system'][0]].append(node[0])

    # Anreicherung von Systemteilen für jedes vorhandene funktionale System
    for fs_id, fs_value in hierarchie_dict['FS'].items():
        matches_dict = dict()

        # Analyse der IFC-Systeme auf potentielle Matches
        for ifc_system in fs_value['IFC-Systems']:
            k = 0
            if not ifc_system:
                continue
            for ts, ts_regex_list in technicalsystem_naming_conventions[fs_value['Classification']].items():
                if not ts_regex_list:
                    continue
                if k == 1:
                    break
                for ts_regex in ts_regex_list:
                    if re.match(ts_regex, ifc_system.lower()):
                        if ts in matches_dict:
                            matches_dict[ts].append(ifc_system)
                        else:
                            matches_dict[ts] = list()
                            matches_dict[ts].append(ifc_system)
                        k = 1
                        break

        # Auswertung der Matches basierend auf dem Zusammenhang
        for match_key, match_systems in matches_dict.items():
            # Anlegen eines Systemteils
            if len(match_systems) == 1:
                ts_id = str(uuid.uuid4())
                hierarchie_dict['FS'][fs_id]['TS'].append(ts_id)
                hierarchie_dict['TS'][ts_id] = dict()

                if '_' in match_key:
                    system_classification = match_key.split('_')[0]
                else:
                    system_classification = match_key
                hierarchie_dict['TS'][ts_id]['Classification'] = system_classification
                hierarchie_dict['TS'][ts_id]['Components'] = lookup_dict_components[match_systems[0]]
                hierarchie_dict['TS'][ts_id]['IFC-Systems'] = match_systems
                hierarchie_dict['TS'][ts_id]['TS'] = []

                for comp in hierarchie_dict['TS'][ts_id]['Components']:
                    if comp not in system_dict:
                        system_dict[comp] = dict()
                        system_dict[comp]['IS'] = []
                        system_dict[comp]['FS'] = []
                        system_dict[comp]['TS'] = []
                    system_dict[comp]['TS'].append(ts_id)

            # Anlegen mehrerer Systemteile basierend auf dem Zusammenhang
            else:
                tmp_nodes = list()
                for match_system in match_systems:
                    tmp_nodes.extend(lookup_dict_components[match_system])
                tmp_subgraph = import_graph.subgraph(tmp_nodes)
                tmp_subgraph_weaklyconnsystems_list = sorted(nx.weakly_connected_components(tmp_subgraph), key=len, reverse=True)

                if len(tmp_subgraph_weaklyconnsystems_list) == 1:
                    ts_id = str(uuid.uuid4())
                    hierarchie_dict['FS'][fs_id]['TS'].append(ts_id)
                    hierarchie_dict['TS'][ts_id] = dict()

                    if '_' in match_key:
                        system_classification = match_key.split('_')[0]
                    else:
                        system_classification = match_key
                    hierarchie_dict['TS'][ts_id]['Classification'] = system_classification
                    hierarchie_dict['TS'][ts_id]['Components'] = tmp_nodes
                    hierarchie_dict['TS'][ts_id]['IFC-Systems'] = match_systems
                    hierarchie_dict['TS'][ts_id]['TS'] = []

                    for comp in tmp_nodes:
                        if comp not in system_dict:
                            system_dict[comp] = dict()
                            system_dict[comp]['IS'] = []
                            system_dict[comp]['FS'] = []
                            system_dict[comp]['TS'] = []
                        system_dict[comp]['TS'].append(ts_id)

                else:
                    for technical_system in tmp_subgraph_weaklyconnsystems_list:

                        ts_id = str(uuid.uuid4())
                        hierarchie_dict['FS'][fs_id]['TS'].append(ts_id)
                        hierarchie_dict['TS'][ts_id] = dict()

                        ifc_systems_list = list()
                        for comp in technical_system:
                            comp_ts = lookup_dict_technical_sytems[comp][0]
                            if comp_ts not in ifc_systems_list:
                                ifc_systems_list.append(comp_ts)

                        if '_' in match_key:
                            system_classification = match_key.split('_')[0]
                        else:
                            system_classification = match_key
                        hierarchie_dict['TS'][ts_id]['Classification'] = system_classification
                        hierarchie_dict['TS'][ts_id]['Components'] = list(technical_system)
                        hierarchie_dict['TS'][ts_id]['IFC-Systems'] = ifc_systems_list
                        hierarchie_dict['TS'][ts_id]['TS'] = []

                        for comp in tmp_nodes:
                            if comp not in system_dict:
                                system_dict[comp] = dict()
                                system_dict[comp]['IS'] = []
                                system_dict[comp]['FS'] = []
                                system_dict[comp]['TS'] = []
                            system_dict[comp]['TS'].append(ts_id)

    return hierarchie_dict, system_dict

def get_comp_by_ifc_system(system_list, info_dict):
    # Abfragen der Komponenten basierend auf dem übergebenen IFC-System
    result_set = set()
    ifc_system_list = list(info_dict['IFC-Systems'].keys())
    for system in system_list:
        for ifc_system in ifc_system_list:
            if not ifc_system:
                continue
            if system == ifc_system[0]:
                result_set.update(set(info_dict['IFC-Systems'][ifc_system]['Components']))
                break

    return list(result_set)

def get_system_hierarchy(system_json, info_dict, hierarchie_dict):
    # Anreicherung der Systemhierarchie basierend auf dem expliziten Input des Nutzers
    system_dict = dict()

    for key, value in system_json.items():
        # Anreicherung von Systemverbunden
        if key == 'IS':
            for system_key, system_value in value.items():
                system_id = system_key
                hierarchie_dict['IS'][system_id] = dict()
                hierarchie_dict['IS'][system_id]['Classification'] = None
                comp_list = get_comp_by_ifc_system(system_value['IFC-Systems'], info_dict)
                for comp in comp_list:
                    if comp not in system_dict:
                        system_dict[comp] = dict()
                        system_dict[comp]['IS'] = []
                        system_dict[comp]['FS'] = []
                        system_dict[comp]['TS'] = []
                    system_dict[comp]['IS'].append(system_id)
                hierarchie_dict['IS'][system_id]['Components'] = comp_list
                hierarchie_dict['IS'][system_id]['IFC-Systems'] = system_value['IFC-Systems']

        # Anreicherung von funktionalen Systemen
        elif key == 'FS':
            for system_key, system_value in value.items():
                system_id = system_key
                hierarchie_dict['FS'][system_id] = dict()
                hierarchie_dict['FS'][system_id]['Classification'] = system_value['Classification']
                comp_set = set(get_comp_by_ifc_system(system_value['IFC-Systems'], info_dict))
                comp_set.update(set(system_value['Components']['Add']))
                comp_set -= set(system_value['Components']['Delete'])
                comp_list = list(comp_set)
                for comp in comp_list:
                    if comp not in system_dict:
                        system_dict[comp] = dict()
                        system_dict[comp]['IS'] = []
                        system_dict[comp]['FS'] = []
                        system_dict[comp]['TS'] = []
                    system_dict[comp]['FS'].append(system_id)
                hierarchie_dict['FS'][system_id]['Components'] = comp_list
                hierarchie_dict['FS'][system_id]['IFC-Systems'] = system_value['IFC-Systems']

        # Anreicherung von Systemteilen
        elif key == 'TS':
            for system_key, system_value in value.items():
                system_id = system_key
                hierarchie_dict['TS'][system_id] = dict()
                hierarchie_dict['TS'][system_id]['Classification'] = system_value['Classification']
                comp_set = set(get_comp_by_ifc_system(system_value['IFC-Systems'], info_dict))
                comp_set.update(set(system_value['Components']['Add']))
                comp_set -= set(system_value['Components']['Delete'])
                comp_list = list(comp_set)
                for comp in comp_list:
                    if comp not in system_dict:
                        system_dict[comp] = dict()
                        system_dict[comp]['IS'] = []
                        system_dict[comp]['FS'] = []
                        system_dict[comp]['TS'] = []
                    system_dict[comp]['TS'].append(system_id)
                hierarchie_dict['TS'][system_id]['Components'] = comp_list
                hierarchie_dict['TS'][system_id]['IFC-Systems'] = system_value['IFC-Systems']

    # Übernahme der hierarchischen Untergliederung der technischen Systeme
    for key, value in hierarchie_dict.items():
        if key == 'IS':
            for system_id, system_value in value.items():
                system_value['FS'] = system_json[key][system_id]['FS']
        if key == 'FS':
            for system_id, system_value in value.items():
                system_value['TS'] = system_json[key][system_id]['TS']
        if key == 'TS':
            for system_id, system_value in value.items():
                system_value['TS'] = system_json[key][system_id]['TS']

    return hierarchie_dict, system_dict

def convert_import_graph_in_export_graph(import_graph, system_dict):
    # Parsen des Graphs und Übergabe der hierarchischen Informationen
    export_graph = nx.DiGraph()

    # Parsen der Knoten
    for node in import_graph.nodes(data=True):

        # Übergabe der hierarchischen Informationen
        if node[0] in system_dict:
            integrated_systems = system_dict[node[0]]['IS']
            functional_systems = system_dict[node[0]]['FS']
            technical_systems = system_dict[node[0]]['TS']
        else:
            integrated_systems = []
            functional_systems = []
            technical_systems = []

        export_graph.add_node(node[0], ifc_id=node[1]['ifc_id'], ifc_class=node[1]['ifc_class'], ifc_type=node[1]['ifc_type'], ifc_name=node[1]['ifc_name'], ifc_description=node[1]['ifc_description'],
                              ifc_system=node[1]['ifc_system'], ifc_position=node[1]['ifc_position'], elem_rds=node[1]['elem_rds'], additional_data=node[1]['additional_data'], integrated_systems=integrated_systems,
                              functional_systems=functional_systems, technical_systems=technical_systems)

    # Parsen der Kanten
    for edge in import_graph.edges:
        export_graph.add_edge(edge[0], edge[1], aggregated_nodes=[])

    return export_graph

def check_system_connections(import_graph, hierarchie_dict, system_dict):
    # Analyse der Schnittstellen der technischen Systeme
    tmp_list = list()
    for sys_hier, systems in hierarchie_dict.items():
        if len(systems) <= 1:
            for sys_key, sys_value in systems.items():
                sys_value['Schnittstellen'] = []
        else:
            comp_dict = dict()
            for sys_key, sys_value in systems.items():
                comp_dict[sys_key] = set(sys_value['Components'])
                sys_value['Schnittstellen'] = []

            connected_edges = set()
            for edge in import_graph.edges():
                systems_0 = set(system_dict[edge[0]][sys_hier])
                systems_1 = set(system_dict[edge[1]][sys_hier])

                if systems_0 == systems_1:
                    continue

                for sys_key, sys_comp in comp_dict.items():
                    if edge[0] in sys_comp:
                        for sys_key_2, sys_comp_2 in comp_dict.items():
                            if sys_key == sys_key_2:
                                continue
                            if edge[1] in sys_comp_2:
                                if edge not in connected_edges and (edge[1], edge[0]) not in connected_edges:
                                    systems[sys_key]['Schnittstellen'].append(sys_key_2)
                                    systems[sys_key_2]['Schnittstellen'].append(sys_key)
                                    connected_edges.add(edge)

                                    tmp_dict = {
                                        'Source_System': sys_key,
                                        'Target_System': sys_key_2,
                                        'Source_Component': edge[0],
                                        'Target_Component': edge[1]
                                    }
                                    tmp_list.append(tmp_dict)
                    if edge[1] in sys_comp:
                        for sys_key_2, sys_comp_2 in comp_dict.items():
                            if sys_key == sys_key_2:
                                continue
                            if edge[0] in sys_comp_2:
                                if edge not in connected_edges and (edge[1], edge[0]) not in connected_edges:
                                    systems[sys_key]['Schnittstellen'].append(sys_key_2)
                                    systems[sys_key_2]['Schnittstellen'].append(sys_key)
                                    connected_edges.add(edge)

                                    tmp_dict = {
                                        'Source_System': sys_key,
                                        'Target_System': sys_key_2,
                                        'Source_Component': edge[0],
                                        'Target_Component': edge[1]
                                    }
                                    tmp_list.append(tmp_dict)

    hierarchie_dict['Schnittstellen'] = tmp_list
    return hierarchie_dict

def aggregate_graph(directed_graph):
    # Aggregation des gerichteten Graphs
    # Vorbereitung der Aggregation
    aggregated_directed_graph = directed_graph.copy()
    aggregated_directed_graph_nodes = list(aggregated_directed_graph.nodes(data=True))

    # Festlegung von Elementklassifizierungen die aggregiert werden dürfen
    segment_set = {'IfcPipeSegment', 'IfcDuctSegment'}
    fitting_set = {'IfcPipeFitting', 'IfcDuctFitting'}
    complete_set = segment_set.union(fitting_set)

    # Startpunkt der Aggragation
    for node in aggregated_directed_graph_nodes:
        # Abrufen der Nachbarn des Knotens
        pre_edges = list(aggregated_directed_graph.predecessors(node[0]))
        suc_edges = list(aggregated_directed_graph.successors(node[0]))
        complete_edges = list(set(pre_edges) | set(suc_edges))

        # Aggregation
        if node[1]['ifc_class'] in complete_set:
            if len(complete_edges) == 0:
                aggregated_directed_graph.remove_node(node[0])
            elif len(complete_edges) == 1:
                aggregated_directed_graph.remove_node(node[0])
            elif len(pre_edges) == 1 and len(suc_edges) == 1:
                removed_nodes_set = set()
                try:
                    removed_nodes_set.update(aggregated_directed_graph.edges[pre_edges[0], node[0]]['aggregated_nodes'])
                except KeyError:
                    pass

                try:
                    removed_nodes_set.update(aggregated_directed_graph.edges[node[0], suc_edges[0]]['aggregated_nodes'])
                except KeyError:
                    pass

                removed_nodes_set.add(node[0])
                aggregated_directed_graph.remove_node(node[0])
                aggregated_directed_graph.add_edge(pre_edges[0], suc_edges[0], aggregated_nodes=list(removed_nodes_set))

            elif len(complete_edges) == 2:
                removed_nodes_set = set()
                try:
                    removed_nodes_set.update(aggregated_directed_graph.edges[node[0], complete_edges[0]]['aggregated_nodes'])
                except KeyError:
                    pass

                try:
                    removed_nodes_set.update(aggregated_directed_graph.edges[complete_edges[0], node[0]]['aggregated_nodes'])
                except KeyError:
                    pass

                try:
                    removed_nodes_set.update(aggregated_directed_graph.edges[node[0], complete_edges[1]]['aggregated_nodes'])
                except KeyError:
                    pass

                try:
                    removed_nodes_set.update(aggregated_directed_graph.edges[complete_edges[1], node[0]]['aggregated_nodes'])
                except KeyError:
                    pass

                removed_nodes_set.add(node[0])
                aggregated_directed_graph.remove_node(node[0])
                aggregated_directed_graph.add_edge(complete_edges[0], complete_edges[1], aggregated_nodes=list(removed_nodes_set))
        else:
            if len(complete_edges) == 0:
                aggregated_directed_graph.remove_node(node[0])

    return aggregated_directed_graph


from datetime import timedelta
from traceback import format_exc

import svgwrite
from schedaus.const import C
from schedaus.model import Task
from schedaus.utils import get_prefix_space_num, len_multibyte, calc_remain_days_in_month

class Renderer:
    def __init__(self, dwg=None):
        if dwg is None:
            self.dwg = svgwrite.Drawing()
        else:
            self.dwg = dwg

        # width per day
        self.wpd = C.width_per_day
        # height per line
        self.hpl = C.height_per_line
        # maximum width
        self.mw = -1
        # maximum height
        self.mh = -1

        self.scale = "daily"
        self.schedule_offset = self.hpl * 4

    def get_svg(self):
        return self.dwg

    def add_error(self):
        mw = int(self.dwg.attribs["width"][0:-2])
        mh = int(self.dwg.attribs["height"][0:-2])
        text, height = self._generate_error_text(mh)
        self.dwg.add(text)
        self.dwg.attribs["height"] = f"{height}px"
        if mw < 640:
            self.dwg.attribs["width"] = "640px"

    def draw_error(self):
        text, height = self._generate_error_text()
        self.dwg.add(text)
        self.dwg.attribs["width"] = "640px"
        self.dwg.attribs["height"] = f"{height}px"
        return self.dwg

    def _generate_error_text(self, mh=0):
        text_opts = {
            "font_size": "14",
            "font_weight": "bold",
            "fill": "red",
        }
        text_opts.update(C.text_common_opts)
        text = None
        dy_per = 16
        dy = dy_per
        lines = format_exc().splitlines()
        for line in lines:
            if text is None:
                text = self.dwg.text(line, (0, mh+4), **text_opts)
            else:
                spaces = get_prefix_space_num(line)
                text.add(self.dwg.tspan(line, (spaces*8, mh+4), dy=[dy]))
                dy += dy_per

        height = mh + 4 + len(lines) * dy_per

        return text, height

    def render(self, data):
        schedules_y = {}
        idx = 0

        self.change_scale(data["calendar"].scale)

        objs = self.calendar_to_svg(data["calendar"], data)

        def _find(items, name):
            return [x for x in items if x.name == name][0]

        for group in data["groups"]:
            if group.text:
                group_objs = self.group_to_svg(data["calendar"], group)
                y = self.schedule_offset + self.hpl * (idx * 2)
                g = self.dwg.g(id=f"group-{group.text}", transform=f"translate(0, {y})")
                for group_obj in group_objs:
                    g.add(group_obj)
                objs.append({"z": 20, "o": g})
                idx += 1
            for sc_name in group.member:
                sc = _find(data["schedules"], sc_name)
                y = self.schedule_offset + self.hpl * (idx * 2)
                g = self._render_schedule(sc, y, data)
                objs.append({"z": 20, "o": g})
                schedules_y[sc.name] = y
                idx += 1

        self.register_arrowhead_svg()
        for dpath in data["dependency_paths"]:
            obj = self.dpath_to_svg(data["calendar"], schedules_y, dpath)
            objs.append({"z": 30, "o": obj})

        for obj in sorted(objs, key=lambda x: x["z"]):
            self.dwg.add(obj["o"])

    def change_scale(self, scale):
        self.scale = scale
        if scale == "daily":
            self.wpd = 16
            self.schedule_offset = self.hpl * 4
        elif scale == "weekly":
            self.wpd = 8
            self.schedule_offset = self.hpl * 3

    def _render_schedule(self, schedule, y, data):
        if isinstance(schedule, Task):
            task = schedule
            task_objs = self.task_to_svg(data["calendar"], task)
            g = self.dwg.g(transform=f"translate(0, {y})")
            for task_obj in sorted(task_objs, key=lambda x: x["z"]):
                g.add(task_obj["o"])
        else:
            ms = schedule
            ms_objs = self.milestone_to_svg(data["calendar"], ms)
            g = self.dwg.g(transform=f"translate(0, {y})")
            for ms_obj in sorted(ms_objs, key=lambda x: x["z"]):
                g.add(ms_obj["o"])

        return g

    def calendar_to_svg(self, calendar, data):
        ph = 1
        self.mw = self.wpd * ((calendar.end - calendar.start).days + 1)
        self.mh = (self.hpl * 3) + (self.hpl * 2 * (len(data["schedules"]) + len(data["groups"])))
        self.dwg.attribs["width"] = f"{self.mw}px"
        self.dwg.attribs["height"] = f"{self.mh}px"
        self.dwg.add(self.dwg.rect((0, 0), (self.mw, self.mh), fill="#FFFFFF"))

        objs = []
        prev_month = None
        text_month_opts = {
            "font_size": "13",
            "font_weight": "bold",
        }
        text_month_opts.update(C.text_common_opts)
        text_day_opts = {
            "font_size": "11",
            "text_anchor": "middle",
        }
        text_day_opts.update(C.text_common_opts)
        x = 0
        date = calendar.start
        while date <= calendar.end:
            if date.month != prev_month:
                # draw 'YYYY/MM' text
                if calc_remain_days_in_month(date) >= 3:
                    month_text = "{}/{}".format(date.year, date.month)
                else:
                    month_text = "{}".format(date.month)
                objs.append({"z": 5, "o": self.dwg.text(month_text, (x+1, ph), **text_month_opts)})
                # draw line per month
                if self.scale == "daily":
                    h = self.mh
                elif self.scale == "weekly":
                    h = self.hpl
                objs.append({"z": 10, "o": self.dwg.line((x, 0), (x, h), **C.line_common_opts)})
                # update previous month
                prev_month = date.month

            if self.scale == "daily":
                # draw weekday text
                objs.append({"z": 5, "o": self.dwg.text(date.strftime("%a")[0:2], (x+self.wpd/2, self.hpl+ph), **text_day_opts)})
                # draw day text
                objs.append({"z": 5, "o": self.dwg.text(date.day, (x+self.wpd/2, self.hpl*2+ph), **text_day_opts)})
                # draw line per day
                objs.append({"z": 10, "o": self.dwg.line((x, self.hpl*3), (x, self.mh), **C.line_common_opts)})
            elif self.scale == "weekly":
                if date.weekday() == 0:
                    # draw day text
                    objs.append({"z": 5, "o": self.dwg.text(date.day, (x+self.wpd/2+2, self.hpl+ph), **text_day_opts)})
                    # draw line per week
                    objs.append({"z": 10, "o": self.dwg.line((x, self.hpl), (x, self.mh), **C.line_common_opts)})
            x += self.wpd
            date += timedelta(days=1)

        for closed in calendar.closed:
            offset_days = (closed - calendar.start).days
            if self.scale == "daily":
                xy = (self.wpd * offset_days, self.hpl)
            elif self.scale == "weekly":
                xy = (self.wpd * offset_days, self.hpl*2)
            objs.append({"z": 0, "o": self.dwg.rect(xy, (self.wpd, self.mh-self.hpl), fill="#D0D0D0")})

        # Outer frame
        objs.append({"z": 10, "o": self.dwg.line((self.mw, 0), (self.mw, self.mh), **C.line_common_opts)})
        objs.append({"z": 10, "o": self.dwg.line((0, self.hpl*0), (self.mw, self.hpl*0), **C.line_common_opts)})
        if self.scale == "daily":
            objs.append({"z": 10, "o": self.dwg.line((0, self.hpl*3), (self.mw, self.hpl*3), **C.line_common_opts)})
        elif self.scale == "weekly":
            objs.append({"z": 10, "o": self.dwg.line((0, self.hpl*1), (self.mw, self.hpl*1), **C.line_common_opts)})
            objs.append({"z": 10, "o": self.dwg.line((0, self.hpl*2), (self.mw, self.hpl*2), **C.line_common_opts)})

        # Today's line
        x = self.wpd * (calendar.today - calendar.start).days
        objs.append({"z": 99, "o": self.dwg.line((x, 0), (x, self.mh), stroke="#FF0000", stroke_width="3.0")})

        return objs

    def task_to_svg(self, calendar, task):
        delta_to_start = task.plan_start - calendar.start
        delta_to_end = task.plan_end - calendar.start + timedelta(days=1)

        objs = []
        xy = (self.wpd*delta_to_start.days, 0)
        wh = (self.wpd*(delta_to_end - delta_to_start).days, self.hpl)
        rect = self.dwg.rect(xy, wh, rx=6, ry=6, fill=task.color_plan_fill, stroke=task.color_plan_outline)
        objs.append({"z": 20, "o": rect})
        text_task_opts = {"font_size": "13", "color": task.color_text}
        text_task_opts.update(C.text_common_opts)
        text = task.text
        if task.assignee is not None:
            text += f"@{task.assignee}"
        objs.append({"z": 21, "o": self.dwg.text(text, (xy[0]+2, xy[1]+2), **text_task_opts)})

        if task.actual_start is not None and task.actual_end is not None:
            delta_to_start = task.actual_start - calendar.start
            delta_to_end = task.actual_end - calendar.start + timedelta(days=1)

            xy = (self.wpd*delta_to_start.days, self.hpl+2)
            wh = (self.wpd*(delta_to_end - delta_to_start).days, 4)
            if task.actual_completed is not None:
                opt = {"fill": "#FFFFFF", "stroke_dasharray": "4 3"}
            else:
                opt = {"fill": task.color_actual_fill}
            rect = self.dwg.rect(xy, wh, rx=2, ry=2, stroke=task.color_actual_outline, **opt)
            objs.append({"z": 20, "o": rect})

            if task.actual_progress is not None:
                xy = (self.wpd*delta_to_end.days+2, self.hpl+5)
                text_opts = {
                    "font_family": "Serif",
                    "font_size": 10,
                    "font_weight": "bold",
                    "dominant-baseline": "middle",
                    "alignment_baseline": "middle",
                }
                text = self.dwg.text(task.actual_progress, xy, **text_opts)
                objs.append({"z": 20, "o": text})

            if task.actual_completed is not None:
                delta_to_completed = task.actual_completed - calendar.start
                xy = (self.wpd*delta_to_start.days, self.hpl+2)
                wh = (self.wpd*(delta_to_completed - delta_to_start).days, 4)
                rect = self.dwg.rect(xy, wh, rx=2, ry=2, fill=task.color_actual_fill, stroke=task.color_actual_outline)
                objs.append({"z": 21, "o": rect})

            if task.actual_end is not None and task.actual_progress is None:
                xy = (self.wpd*delta_to_end.days+2, self.hpl+5)
                text_opts = {
                    "font_family": "Serif",
                    "font_size": 10,
                    "font_weight": "bold",
                    "dominant-baseline": "middle",
                    "alignment_baseline": "middle",
                }
                text = self.dwg.text("done", xy, **text_opts)
                objs.append({"z": 20, "o": text})

        return objs

    def milestone_to_svg(self, calendar, milestone):
        x = self.wpd * ((milestone.plan_happen - calendar.start).days + 0.5)
        s = self.hpl / 2
        path = [(0, 0), (-s, s), (0, s*2), (s, s), (0, 0)]
        m = self.dwg.polyline(path, fill=milestone.color_plan_fill, stroke=milestone.color_plan_outline)
        text_ms_opts = {"font_size": "13", "color": milestone.color_text}
        text_ms_opts.update(C.text_common_opts)
        t = self.dwg.text(milestone.text, (self.hpl/2+2, 2), **text_ms_opts)
        g = self.dwg.g(transform=f"translate({x}, 0)")
        g.add(m)
        g.add(t)
        return [{"z": 0, "o": g}]

    def register_arrowhead_svg(self):
        # a rightward arrowhead
        width = 5
        hight = 10
        path = [(0, 0), (0, hight), (width, hight/2), (0, 0)]
        p = self.dwg.polyline(path)
        g = self.dwg.g(id="arrowhead", transform=f"translate({-width}, {-hight/2})")
        g.add(p)
        self.dwg.defs.add(g)

    def dpath_to_svg(self, calendar, schedules_y, dpath):
        # determine the direction
        if schedules_y[dpath.start_name] < schedules_y[dpath.end_name]:
            if dpath.start_date < dpath.end_date:
                direction = ["down", "right"]
            else:
                direction = ["down", "left"]
        else:
            if dpath.start_date < dpath.end_date:
                direction = ["right", "up"]
            else:
                direction = ["left", "up"]

        if direction[0] == "down":
            sx = self.wpd * (dpath.start_date - calendar.start).days + self.wpd / 2
            sy = schedules_y[dpath.start_name] + self.hpl
        else:
            if direction[0] == "right":
                sx = self.wpd * (dpath.start_date - calendar.start).days + self.wpd
            else:
                sx = self.wpd * (dpath.start_date - calendar.start).days
            sy = schedules_y[dpath.start_name] + self.hpl / 2

        if direction[0] != "down":
            ex = self.wpd * (dpath.end_date - calendar.start).days + self.wpd / 2
            ey = schedules_y[dpath.end_name] + self.hpl
        else:
            if direction[1] == "right":
                ex = self.wpd * (dpath.end_date - calendar.start).days
            else:
                ex = self.wpd * (dpath.end_date - calendar.start).days + self.wpd
            ey = schedules_y[dpath.end_name] + self.hpl / 2

        # adjust for weekly scale
        if self.scale == "weekly":
            if direction[1] == "right":
                sx -= self.wpd * 0.5
            if direction[1] == "left":
                sx += self.wpd * 0.5

        if direction[0] == "down":
            mx = sx
            my = ey
        else:
            mx = ex
            my = sy

        l1 = self.dwg.line((sx, sy), (mx, my), stroke=dpath.color, stroke_width=2.0)
        l2 = self.dwg.line((mx, my), (ex, ey), stroke=dpath.color, stroke_width=2.0)

        r = {"right": 0, "down": 90, "left": 180, "up": 270}
        arrowhead = self.dwg.g(transform=f"translate({ex}, {ey}) rotate({r[direction[1]]})")
        arrowhead.add(self.dwg.use("#arrowhead", fill=dpath.color))

        g = self.dwg.g(id=f"path-{dpath.start_name}-to-{dpath.end_name}")
        g.add(l1)
        g.add(l2)
        g.add(arrowhead)
        return g

    def group_to_svg(self, calendar, group):
        objs = []
        text_size = len_multibyte(group.text) * 8 + 10
        line_opts = {"style": "stroke:dimgray; stroke-width=1.0"}
        text_opts = {
            "font_family": "Serif",
            "font_size": 13,
            "dominant_baseline": "middle",
            "alignment_baseline": "middle",
            "text_anchor": "middle"
        }
        objs.append(self.dwg.line((0, self.hpl/5*2), (self.mw/2 - text_size/2, self.hpl/5*2), **line_opts))
        objs.append(self.dwg.line((0, self.hpl/5*3), (self.mw/2 - text_size/2, self.hpl/5*3), **line_opts))
        objs.append(self.dwg.text(group.text, (self.mw/2, self.hpl/2), **text_opts))
        objs.append(self.dwg.line((self.mw/2 + text_size/2, self.hpl/5*2), (self.mw, self.hpl/5*2), **line_opts))
        objs.append(self.dwg.line((self.mw/2 + text_size/2, self.hpl/5*3), (self.mw, self.hpl/5*3), **line_opts))
        return objs


import argparse
import logging
from pprint import pprint

from kitty.lexer import Lexer
from kitty.parser import Parser
from kitty.validator import Validator

arg_parser = argparse.ArgumentParser()

group = arg_parser.add_mutually_exclusive_group(required=True)
group.add_argument(
    "-l", "--lex", action="store_true", help="lex input file and return token list"
)
group.add_argument(
    "-c", "--compile", action="store_true", help="compile the input file"
)
group.add_argument(
    "-p", "--parse", action="store_true", help="parse the input file and result AST"
)
group.add_argument(
    "-p+v",
    "--parse+validate",
    action="store_true",
    help="parse the input file, validate and solve expressions if possible and result AST",
)

group.add_argument(
    "-s",
    "--simulate",
    action="store_true",
    help="run kitty interpreter with file as input",
)

arg_parser.add_argument(
    "-d", "--debug", action="store_true", help="enable compiler debug mode"
)

arg_parser.add_argument(
    "-p-indent", type=int, help="indent count while print AST", default=2
)

arg_parser.add_argument(
    "file", type=str, help="run kitty interpreter with file as input"
)

def lex(filename: str):
    with open(filename, "r", encoding="utf-8") as f:
        text = f.read()

    lexer = Lexer(args.file, text)
    return lexer.tokenize()

def parse(tokens):
    parser = Parser(tokens)
    return parser.parse()

def validate(ast):
    validator = Validator(ast)
    return validator.validate()

if __name__ == "__main__":
    args = arg_parser.parse_args()

    if args.debug:
        logging.basicConfig(
            level=logging.DEBUG, format="%(filename)s : %(lineno)d : %(message)s"
        )

    if args.compile:
        print("Compiling not implemented yet!")

    elif args.simulate:
        print("Simulating not implemented yet!")

    elif args.lex:

        tokens, error = lex(args.file)

        if error:
            print(error)
            exit(-1)

        pprint(tokens)

    elif args.parse:
        tokens, error = lex(args.file)

        if error:
            print(error)
            exit(-1)

        res = parse(tokens)

        if res.error:
            print(res.error)
        elif res.node:
            print(res.node.pretty_repr(indent=" " * args.p_indent))
        else:
            print("Empty file!")
            exit(0)

    elif args.__dict__["parse+validate"]:
        tokens, error = lex(args.file)

        if error:
            print(error)
            exit(-1)

        res = parse(tokens)

        if res.error:
            print(res.error)
        elif not res.node:
            print("Empty file!")
            exit(0)

        res = validate(res.node)

        if res.error:
            print(res.error)
        else:
            print(res.ast.pretty_repr(indent=" " * args.p_indent))


from Item import Item
class Armor(Item):
    """armor class is subclass of item"""
    crit_decrease = 0 #decreases chance of critical hit on player
    damage_decrease = 0 #decreases amount of blood loss to player
    body_part = '' #helmet is head, chestplate is body, gauntlets are arms, boots are legs
    def __init__(self, name, crit_decrease, damage_decrease, body_part):
        self.crit_decrease = crit_decrease
        self.damage_decrease = damage_decrease
        self.body_part = body_part
        super(Armor, self).__init__(False, name)


import torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo
import torch.nn.functional as F

from collections import namedtuple
import numpy as np

from .resnet import BasicBlock, Bottleneck, remove_fc, resnet50
MODEL_REGISTRY = {}

# def build_textual_model(model_name, *args, **kwargs):
#     print('Getting registed visual model {}'.format(model_name))
#     return MODEL_REGISTRY[model_name](*args, **kwargs)

# def register_model(name):
#     '''DEcorator and register a new model'''
#     def register_model_cls(cls):
#         if name in MODEL_REGISTRY:
#             raise ValueError('Cannot register duplicate model ({})'.format(name))
#         MODEL_REGISTRY[name] = cls
#         return cls
#     return register_model_cls

class ResNet(nn.Module):
    def __init__(self, model_arch, res5_stride=2, res5_dilation=1, use_c4=False, pretrained=True, num_stripes=0):
        super(ResNet, self).__init__()
        block = model_arch.block
        layers = model_arch.stage

        self.inplanes = 64
        self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3,
                               bias=False)
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
        self.layer1 = self._make_layer(block, 64, layers[0])
        self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
        self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
        self.layer4 = self._make_layer(block, 512, layers[3], stride=1)
        self.out_channels = 512 * block.expansion

        # stripes
        self.num_stripes = num_stripes
        self.avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        # !attention, 这俩要添加self.inplanes的设置
        if self.num_stripes != 0:
            self.inplanes = 512
            self.layer3_part = self._make_layer(block, 256, layers[2], stride=2)
            self.layer4_part = self._make_layer(block, 512, layers[3], stride=1)

        pretrained = True
        if pretrained:
            self.load_state_dict(
                remove_fc(model_zoo.load_url(model_arch.url)), strict=False
            )
        else:
            self._init_weight()

        self.use_c4 = use_c4
        if self.use_c4:
            del self.layer4
            self.out_channels = 256 * block.expansion

    def _make_layer(self, block, planes, blocks, stride=1, dilation=1):
        downsample = None
        if stride != 1 or self.inplanes != planes * block.expansion:
            downsample = nn.Sequential(
                nn.Conv2d(self.inplanes, planes * block.expansion,
                          kernel_size=1, stride=stride, bias=False),
                nn.BatchNorm2d(planes * block.expansion),
            )

        layers = []
        layers.append(block(self.inplanes, planes, stride, downsample, dilation))
        self.inplanes = planes * block.expansion
        for i in range(1, blocks):
            layers.append(block(self.inplanes, planes))

        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)

        # branch global
        x_global = self.layer3(x)
        x_global = self.layer4(x_global)

        if self.num_stripes != 0:
            # branch local
            # x_local = self.layer3_part(x)
            # x_local = self.layer4_part(x_local)
            stripe_h = int(x_local.size(2) / self.num_stripes)
            local_feature_list = [] #torch.zeros(x.size(0), self.num_stripes, x.size(-1))
            for i in range(self.num_stripes):
                local_x = self.avg_pool(x_local[:,:, i*stripe_h: (i+1)*stripe_h, :]).squeeze()
                local_feature_list.append(local_x)
            return x_global, local_feature_list
            # return x_global, x_global

        return x_global

    def _init_weight(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

resnet = namedtuple('resnet', ['block', 'stage', 'url'])
model_archs = {}
model_archs['resnet18'] = resnet(BasicBlock, [2, 2, 2, 2],
                                 'https://download.pytorch.org/models/resnet18-5c106cde.pth')
model_archs['resnet34'] = resnet(BasicBlock, [3, 4, 6, 3],
                                 'https://download.pytorch.org/models/resnet34-333f7ec4.pth')
model_archs['resnet50'] = resnet(Bottleneck, [3, 4, 6, 3],
                                 'https://download.pytorch.org/models/resnet50-19c8e357.pth')
model_archs['resnet101'] = resnet(Bottleneck, [3, 4, 23, 3],
                                  'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth')
model_archs['resnet152'] = resnet(Bottleneck, [3, 8, 36, 3],
                                  'https://download.pytorch.org/models/resnet152-b121ed2d.pth')

# -----------------
# PCB Model
# -----------------
# from torchvision.models import resnet50
import torch.nn.init as init
class PCBresnet(nn.Module):
    def __init__(self, last_conv_stride=1, last_conv_dilation=1, num_stripes=6, out_channels=256, num_classes=11003):
        super(PCBresnet, self).__init__()
        self.base = resnet50(
            pretrained=True,
            last_conv_stride=last_conv_stride,
            last_conv_dilation=last_conv_dilation)
        self.num_stripes = num_stripes

        self.local_conv_list = nn.ModuleList()
        for strip in range(num_stripes):
            self.local_conv_list.append(nn.Sequential(
                nn.Conv2d(2048, out_channels, 1),
                nn.BatchNorm2d(out_channels),
                nn.ReLU(inplace=True)
            ))

        if num_classes > 0:
            self.fc_list = nn.ModuleList()
            for stripe in range(num_stripes):
                fc = nn.Linear(out_channels, num_classes)
                init.normal_(fc.weight, std=0.001)
                init.constant_(fc.bias, 0)
                self.fc_list.append(fc)

        self.out_channels = out_channels * self.num_stripes

    def forward(self, x):
        # shape [N, C, H, W]
        feat = self.base(x)
        assert feat.size(2) % self.num_stripes == 0
        stripe_h = int(feat.size(2) / self.num_stripes)
        local_feat_list = []
        logits_list = []
        for i in range(self.num_stripes):
            local_feat = F.avg_pool2d(
                feat[:,:, i*stripe_h: (i+1) * stripe_h, :],
                (stripe_h, feat.size(-1))
            )
            local_feat = self.local_conv_list[i](local_feat)
            local_feat = local_feat.view(local_feat.size(0), -1)
            local_feat_list.append(local_feat)
            if hasattr(self, 'fc_list'):
                logits_list.append(self.fc_list[i](local_feat))

        global_feat = torch.cat(local_feat_list, dim=1)

        if hasattr(self, 'fc_list'):
            return global_feat, logits_list
        return global_feat

def build_resnet(cfg):
    print('Getting registed visual model {}'.format(cfg.MODEL.VISUAL_MODEL.NAME))
    if 'resnet' in cfg.MODEL.VISUAL_MODEL.NAME[:6]:
        model_arch = model_archs[cfg.MODEL.VISUAL_MODEL.NAME]
        model = ResNet(model_arch,
                        cfg.MODEL.VISUAL_MODEL.RES5_STRIDE,
                        cfg.MODEL.VISUAL_MODEL.RES5_DILATION,
                        use_c4=cfg.MODEL.VISUAL_MODEL.USE_C4,
                        pretrained=True,
                        num_stripes = cfg.MODEL.VISUAL_MODEL.NUM_STRIPES)

    elif 'PCB' in cfg.MODEL.VISUAL_MODEL.NAME:
        model = PCBresnet(
            last_conv_stride=1,
            last_conv_dilation=1,
            num_stripes=6,
            out_channels=256,
            num_classes=11003
        )

    return model

if __name__ == '__main__':
    resnet = build_resnet()
    print(resnet)


from enum import Enum
from typing import Annotated

import pytorch_lightning as pl
import torch
import typer
from pytorch_lightning.callbacks import ModelCheckpoint
from pytorch_lightning.loggers.csv_logs import CSVLogger
from pytorch_lightning.loggers.wandb import WandbLogger
from transformers.models.bart.tokenization_bart_fast import BartTokenizerFast

from engawa.data_loader import get_dataloader
from engawa.model import EngawaModel

class ModelType(str, Enum):
    base = "base"
    large = "large"

def train_model(
    tokenizer_file: Annotated[str, typer.Option(help="Path to a tokenizer file.")],
    train_file: Annotated[
        str, typer.Option(help="Line separated text file for training.")
    ],
    val_file: Annotated[
        str, typer.Option(help="Line separated text file for validation.")
    ],
    default_root_dir: Annotated[
        str, typer.Option(help="Path to save generated files.")
    ],
    ckpt_path: Annotated[
        str, typer.Option(help="If given, start from that checkpoint.")
    ] = None,
    wandb_proj_name: Annotated[
        str, typer.Option(help="If given, log to that wandb project.")
    ] = None,
    val_check_interval: float = 0.25,
    max_steps: Annotated[
        int, typer.Option(help="Set -1 for infinite training.")
    ] = 500000,
    model_type: Annotated[
        ModelType, typer.Option(help="BART type, `base` or `large`.")
    ] = ModelType.large,
    seed: int = 10,
    bs: int = 32,
    max_length: int = 1024,
    lr: float = 0.0004,
    weight_decay: float = 0.01,
    num_warmup_steps: int = 10000,
    mask_ratio: float = 0.3,
    poisson_lambda: float = 3.5,
):
    tokenizer = BartTokenizerFast(tokenizer_file=tokenizer_file)

    pl.seed_everything(seed)

    train_dl = get_dataloader(
        train_file,
        "train",
        bs,
        tokenizer,
        mask_ratio,
        poisson_lambda,
        max_length=max_length,
    )
    val_dl = get_dataloader(
        val_file,
        "validation",
        bs,
        tokenizer,
        mask_ratio,
        poisson_lambda,
        max_length=max_length,
    )

    if wandb_proj_name is not None:
        logger = WandbLogger(project=wandb_proj_name, save_dir=default_root_dir)
    else:
        logger = CSVLogger(default_root_dir)

    checkpoint_callback = ModelCheckpoint(monitor="val_loss", save_top_k=1, mode="min")

    num_gpus = torch.cuda.device_count()
    devices = None if num_gpus == 0 else num_gpus
    accelerator = "cpu" if num_gpus == 0 else "gpu"

    trainer = pl.Trainer(
        max_epochs=-1,
        max_steps=max_steps,
        val_check_interval=val_check_interval,
        deterministic=True,
        default_root_dir=default_root_dir,
        callbacks=[checkpoint_callback],
        logger=logger,
        accelerator=accelerator,
        devices=devices,
    )

    model = EngawaModel(
        tokenizer,
        lr,
        weight_decay,
        num_warmup_steps,
        max_steps if max_steps > -1 else 500000,
        model_type=model_type,
    )

    if ckpt_path is not None:
        print(f"Resume training from {ckpt_path}...")
    trainer.fit(model, train_dl, val_dl, ckpt_path=ckpt_path)


class Solution:
  # recursive solution with two pointers
  def sortedListToBST(self, head):
    # sanity checks
    if not head:
      return None
    if not head.next:
      return TreeNode(head.val)

    return self.recurse(head, None)

  def recurse(self, head, tail):
    if head is tail:
      return None
    slow = fast = head

    while fast is not tail and fast.next is not tail:
      slow = slow.next
      fast = fast.next.next

    # recursively construct subtrees
    left = self.recurse(head, slow)
    right = self.recurse(slow.next, tail)
    return TreeNode(slow.val, left, right)

Problems/0109. (M) Convert Sorted List to Binary Search Tree/Python3/solution.py


# CS39AB - Cloud Computing - Summer 2021
# Instructor: Thyago Mota
# Description: Activity 25 - Write a message to a queue

def lambda_handler(event, context):

    # get message packaged in event
    for record in event['Records']:
        payload = record["body"]
        return {
            'message' : str(payload)
        }


import os #for system
import pandas as pd #for data from massouad
fileExtansions = pd.read_csv('FileExtansionsDataSet.csv')
fileFormats = {fileExtansions[column].name: [y for y in fileExtansions[column] if not pd.isna(y)] for column in fileExtansions} #dict comprehension (dict of lists) for dataframe with drop for each nan value.
fileFormatsReversed = {file_format: folder for folder, fileFormat in fileFormats.items()  for file_format in fileFormat} #reversing key and value in list for future uses
##def funcwrite(name,type,path):
##    # write 2 file 1 for path of all file of extention type
##    name1=name+".Hossam" #txt file with path of all files
##    #.hossam file for not enter with search because no extantion called Hossam
##    name2=name+"1.Hossam"#txt file with name of all files
##    files = [] #list of all files
##    names=[] #list of all names
##    re='txt' #to don't get eror with r
##    pathfile=open(name1,'w')#open path1  to write it
##    namefile=open(name2,'w')#open path2 to write it
##    # r=root, d=directories, f = files
##    for r, d, f in os.walk(path):
##        for file in f:#search for all files
##            for i in type:#search for type
##                if i in file:#search in file type
##                    if '.Hossam' in file :continue
##                    else:
##                        files.append(os.path.join(r, file))
##                        names.append(os.path.join(r,name, file))
##        re=str(r);
##    for f in files:#write on file
##        f=str(f)
##        pathfile.write(f)
##        pathfile.write('\n')
##    pathfile.close()
##    for f in names:#write on file
##        f=str(f)
##        namefile.write(f)
##        namefile.write('\n')
##    namefile.close()
##    return re
##
##def makefolderfun(name,type,path):
##    name1=name+".Hossam" #txt file with path of all files
##    name2=name+"1.Hossam"#txt file with name of all files
##    pf=open(name1,'r')#open path1  to read it
##    nf=open(name2,'r')#open path2 to read it
##    os.chdir(path)
##    p=[]#path
##    n=[]#newname
##    h=[1,2,3]#namefile
##    for x in pf:
##        x=x.rstrip('\n')
##        p.append(x)
##    for x in nf:
##        x=x.rstrip('\n')
##        n.append(x)
##    if (os.path.isdir(os.path.join(os.getcwd(),name))):
##        pass
##    else:
##        os.makedirs(name)
##    pf.close()
##    nf.close()
##    for i in range(len(n)):
##        os.rename(p[i],n[i])

def doTheJob(path):
    os.chdir(path)
    for file in os.scandir():
        print(file.path)
        if file.is_dir():
            continue
        fileFormat = os.path.splitext(file)[1]
        if fileFormat in fileFormatsReversed:
            if not os.path.exists(fileFormatsReversed[fileFormat]):
                os.makedirs(fileFormatsReversed[fileFormat])
            os.rename(file.path, os.path.join(os.getcwd(),fileFormatsReversed[fileFormat],file.name))

search_path =input("Enter path : ")
#video=[".mp4",".flv",".mkv"]
# r=root, d=directories, f = files
#fileExtansions = pd.read_csv('FileExtansionsDataSet.csv')
#fileFormats = {fileExtansions[column].name: [y for y in fileExtansions[column] if not pd.isna(y)] for column in fileExtansions}
#dict comprehension (dict of lists) for dataframe with drop for each nan value.
#fileFormatsReversed = {file_format: folder for folder, fileFormat in fileFormats.items()  for file_format in fileFormat}
#reversing key and value in list for future uses
#key=list(fileFormats.keys())
#value=list(fileFormats.values())
#for i in range(len(key)):
#    r=str(funcwrite(key[i],value[i],search_path))
#    makefolderfun(key[i],value[i],r)
doTheJob(search_path)


#PyPoll
import os
import csv

# Get file path
os.chdir(os.path.dirname(os.path.abspath(__file__)))
csvpath = os.path.join('.', 'Resources', 'election_data.csv')
txtpath = os.path.join('.', 'Resources', 'pypoll.txt')

# Declare variables, set default values
total_vote_count = 0

candidate_list = []
candidate_vote_count = {}

candidate_name = []
candidate_vote_percent = []
candidate_vote_total = []

# Read file
with open(csvpath, newline='') as csvfile:
    csvreader = csv.reader(csvfile, delimiter=',')
    csv_header = next(csvreader)

    # Loop through each row
    for row in csvreader:
        # Calculate Total Votes
        total_vote_count = total_vote_count + 1

        # Populate values
        if row[2] not in candidate_list:
            candidate_list.append(row[2])
            candidate_vote_count[row[2]] = 0
        candidate_vote_count[row[2]] += 1

# Calculate the winner
winner = max(candidate_vote_count, key=candidate_vote_count.get)

# Calculate result for each candidate
for key,value in candidate_vote_count.items():
    candidate_name.append(key)
    candidate_vote_total.append(value)

for item in candidate_vote_total:
    candidate_vote_percent.append(round(item/total_vote_count*100,1))

result = list(zip(candidate_name,candidate_vote_percent,candidate_vote_total))

# Print the analysis to the terminal
print("Election Results")
print("-------------------------")
print("Total Votes: " + str(total_vote_count))
print("-------------------------")

for item in result:
    print(item[0] + ": " + str("%.3f" % item[1]) + "% (" + str(item[2]) + ")")

print("-------------------------")
print("Winner: " + str(winner))
print("-------------------------")

# Export results to text file
with open(txtpath, mode='w', newline='') as txtfile:
    txtfile.writelines("Election Results"+"\n")
    txtfile.writelines("-------------------------"+"\n")
    txtfile.writelines("Total Votes: " + str(total_vote_count)+"\n")
    txtfile.writelines("-------------------------"+"\n")

    for item in result:
        txtfile.writelines(item[0] + ": " + str("%.3f" % item[1]) + "% (" + str(item[2]) + ")"+"\n")

    txtfile.writelines("-------------------------"+"\n")
    txtfile.writelines("Winner: " + str(winner)+"\n")
    txtfile.writelines("-------------------------"+"\n")


from pathlib import Path

import bisect
from PIL import Image
from tqdm import tqdm
import pandas as pd
import imageio

import torch
import torchvision.transforms as tfms
from torch.utils.data import Dataset, ConcatDataset, Subset

from utils import logger

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

DATA_BASE = Path('data').resolve()
EXT_DATA_BASE = DATA_BASE.joinpath('pa_experiment').resolve()

label_name = ['Bone>Fracture>.', 'Bone>Non-fracture>.', 'Diaphragm>Diaphragm>.',
       'Foreign body>.>.', 'Hilar/mediastinum>Aorta>.',
       'Hilar/mediastinum>Cardiomegaly>.', 'Hilar/mediastinum>Hilar area>.',
       'Hilar/mediastinum>Mediastinum>.',
       'Lung density>Decreased density (Lucency)>Cavity/Cyst',
       'Lung density>Decreased density (Lucency)>Emphysema',
       'Lung density>Increased lung density>Atelectasis',
       'Lung density>Increased lung density>Nodule/mass',
       'Lung density>Increased lung density>Other interstitial opacity',
       'Lung density>Increased lung density>Pulmonary edema',
       'Lung density>Increased lung density>pneumonia',
       'Lung volume>Decreased lung volume>.',
       'Lung volume>Increased lung volume>.', 'Pleura>Other pleural lesions>.',
       'Pleura>Pleural effusion>.', 'Pleura>Pneumothorax>.']

folder_name = ['b_f', 'b_nf', 'd_d', 'fb', 'hm_a',
        'hm_c', 'hm_ha', 'hm_m', 'ld_dd_cc', 'ld_dd_e',
        'ld_ild_a', 'ld_ild_nm', 'ld_ild_oio', 'ld_ild_pe', 'ld_ild_p',
        'lv_dlv', 'lv_ilv', 'p_opl', 'p_pe', 'p_p']

def _tb_load_manifest(file_path, num_labels=31, name_labels=None, name_paths=None, ext_data=False, fl_balance=False, r_seed=-1):
    if not file_path.exists():
        logger.error(f"manifest file {file_path} not found.")
        raise RuntimeError

    logger.debug(f"loading dataset manifest {file_path} ...")
    df = pd.read_csv(str(file_path)).fillna(0)

    if (not ext_data) and (True): # using the clean-set
        # cleanset
        if True:
            ## MGH validation set
            df = df.loc[(df['bad_age'] == 0) & (df['bad_quality'] == 0)]
            if r_seed != -1:
                df = df.sample(n=1000, replace=True, random_state=r_seed)

            ## CheXpert trainset
            #df = df.loc[(df['bad_age'] == 0) & (df['bad_quality'] == 0)][0:100]

            if (True): # in order to add clinical information to network
                df['ScaledSex'] = df.sex.replace(0, -1)
                weight_gender = 10
                weight_age = 100
                min_age = 11.0
                max_age = 100.0
                df['ScaledAge'] = (df.PatientAge-min_age)/(max_age-min_age)
                df.ScaledAge = weight_age * (df.ScaledAge - 0.5)
                df['ScaledSex'] = weight_gender * df.ScaledSex

            df.reset_index(drop=True, inplace=True)

    else:
        try:
            df['ScaledSex'] = df.sex.replace(0, -1)
            weight_gender = 10
            weight_age = 100
            min_age = 11.0
            max_age = 117.0
            df['ScaledAge'] = (df.PatientAge-min_age)/(max_age-min_age)
            df.ScaledAge = weight_age * (df.ScaledAge - 0.5)
            df['ScaledSex'] = weight_gender * df.ScaledSex
        except:
            df['ScaledAge'] = 0
            df['ScaledSex'] = 0

    LABELS = df.columns[-(num_labels+1):-1] if name_labels == None else name_labels
    labels = df[LABELS].astype(int)
    paths = df['PATH'] if name_paths == None else df[name_paths]
    ages = df['ScaledAge'].astype(float)
    genders = df['ScaledSex'].astype(float)
    df_tmp = pd.concat([paths, ages, genders, labels], axis=1)

    entries = df_tmp

    logger.debug(f"{len(entries)} entries are loaded.")
    return entries

# data augmentation - 512
train_transforms = tfms.Compose([
    tfms.ToPILImage(),
    tfms.Resize(562, Image.LANCZOS),
    tfms.RandomRotation((-10, 10)),
    tfms.RandomCrop((512, 512)),
    tfms.RandomHorizontalFlip(p=0.01), #with 1% horizontal flip
    tfms.ToTensor(),
])

test_transforms = tfms.Compose([
    tfms.ToPILImage(),
    tfms.Resize((512, 512), Image.LANCZOS),
    tfms.ToTensor(),
])

def get_image(img_path, transforms):
    image = imageio.imread(img_path)
    image_tensor = transforms(image)
    image_tensor = image_tensor[:1, :, :]
    return image_tensor

class CxrDataset(Dataset):
    transforms = train_transforms

    def __init__(self, base_path, manifest_file, num_labels=31, name_labels=None, name_paths=None, ext_data=False, csv_path=None, fl_balance=False, r_seed=-1, *args, **kwargs):
        super().__init__(*args, **kwargs)
        manifest_path = base_path.joinpath(manifest_file).resolve() if csv_path == None else csv_path.joinpath(manifest_file).resolve()
        self.entries = _tb_load_manifest(manifest_path, num_labels=num_labels, name_labels=name_labels, name_paths=name_paths, ext_data=ext_data, fl_balance=fl_balance, r_seed = r_seed)
        self.base_path = base_path
        self.name_labels = name_labels

    def __getitem__(self, index):
        # need to debug
        def get_entries(index):
            df = self.entries.loc[index]
            paths = self.base_path.joinpath(df[0]).resolve()
            label = df[3:].tolist() if self.name_labels == None else df[self.name_labels].tolist()
            age = df[1]
            gender = df[2]

            return paths, label, age, gender

        img_path, label, age, gender = get_entries(index)
        image_tensor = get_image(img_path, CxrDataset.transforms)
        target_tensor = torch.FloatTensor(label)
        clinic_tensor = torch.FloatTensor([age, gender])

        return image_tensor, target_tensor, clinic_tensor

    def __len__(self):
        return len(self.entries)

    def get_label_counts(self, indices=None):
        df = self.entries if indices is None else self.entries.loc[indices]
        counts = [df[x].value_counts() for x in self.labels]
        new_df = pd.concat(counts, axis=1).fillna(0).astype(int)
        return new_df

    @property
    def labels(self):
        return self.entries.columns[3:].values.tolist()

    @staticmethod
    def train():
        CxrDataset.transforms = train_transforms

    @staticmethod
    def eval():
        CxrDataset.transforms = test_transforms


import numpy as np
import matplotlib.pyplot as plt
from scipy.special import binom
from scipy.spatial.distance import pdist

def pdist_block(pdist_vec, i, j):
  """Slice the pdist ndarray as if it were a squareform
  matrix.

  Args:
    pdist_vec: ndarray output from pdist()
    i: ndarray row index of matrix
    j: ndarray col index

  Returns:
    (i.size, j.size) ndarray from distance matrix
  """
  col_ind, row_ind = np.meshgrid(j, i)
  row_ind, col_ind = row_ind.flatten(), col_ind.flatten()
  return pdist_vec[pdist_idx(pdist_vec.size, row_ind, col_ind)].reshape((i.size, j.size))

def pdist_idx(N, i, j):
  """Get indices for an ndarray returned by pdist, so we
  can reference it as if it were the full square matrix.

  Args:
    N: length of the pdist array, e.g. pdist(x).size
    i: ndarray row index of matrix
    j: ndarray col index of matrix

  Returns:
    ndarray consisting of one pdist index per each i,j pair

  """
  D = int((1+np.sqrt(1+8*N))/2)
  if type(i) is int:
    i = np.array([i])
    j = np.array([j])

  idx_where_flip = i > j
  i[idx_where_flip], j[idx_where_flip] = j[idx_where_flip], i[idx_where_flip]

  unique_i = np.unique(i)
  fib_vals = binom(unique_i + 2, 2)
  fib_dict = dict(zip(unique_i, fib_vals))

  return (D * i + j - np.array([fib_dict[val] for val in i])).astype(int)

def affinity(pdist_vec):
  med_dist = np.median(pdist_vec)
  kernel_size = 3 * med_dist
  return np.exp(-pdist_vec/(2*kernel_size**2))

def ismember(a, b):
  """Return an array with the same size of 'a' that
  indicates if an element of 'a' is in 'b'. Can be
  used as an index for slicing since it contains bool
  elements
  """
  a = np.array(a)
  b = np.array(b)
  member_ind = np.zeros_like(a)
  for element in b:
    member_ind[a == element] = 1
  return member_ind>0

def translate_to_counting_numbers(lab_orig):
  """Perform a 1-to-1 mapping of the numbers from a numerical
  array to a new array with elements that are in the set
  {0,1,...,M-1} where M is the number of unique elements in
  the original array
  """
  lab_orig = np.array(lab_orig)
  lab_unique = np.unique(a)
  d = dict(zip(lab_unique, np.arange(lab_unique.size)))
  return np.array([d[val] for val in lab_orig]).astype(int)

def all_pairwise(label_set):
  N = label_set.size
  clus_list = np.setdiff1d(np.unique(label_set), [0])
  all_ind = np.arange(N)
  all_constrained = all_ind[label_set>0]
  big_constraint_mat = np.zeros((0,3))
  for i in clus_list:
    this_clus = all_ind[label_set==i]
    other_clus = np.setdiff1d(all_constrained, this_clus)

    x, y = np.meshgrid(this_clus, this_clus)
    x, y = x.reshape((-1,1)), y.reshape((-1,1))
    ml_block = np.concatenate((x, y, np.ones(x.shape)), axis=1)

    x, y = np.meshgrid(this_clus, other_clus)
    x, y  = x.reshape((-1,1)), y.reshape((-1,1))
    cl_block = np.concatenate((x, y, np.zeros(x.shape)), axis=1)

    big_constraint_mat = np.concatenate((big_constraint_mat, ml_block, cl_block), axis=0)
  return big_constraint_mat

def determine_relevance(cons_mat, labs):
  vote_eligible = np.zeros((cons_mat.shape[0], cons_mat.shape[0])).astype(int)
  for i, cons in enumerate(cons_mat):
    clus1, clus2 = labs[cons[0]], labs[cons[1]]
    col1 = labs[cons_mat[:,0]][:,None]
    col2 = labs[cons_mat[:,1]][:,None]
    labs_of_cons = np.concatenate([col1, col2], axis=1)

    n_matches1 = np.sum(labs_of_cons == np.asarray([clus1, clus2])[None,:], axis=1)
    n_matches2 = np.sum(labs_of_cons == np.asarray([clus2, clus1])[None,:], axis=1)
    n_matches = np.maximum(n_matches1, n_matches2)

    if cons[2] == 1:
      valid_ind = n_matches == 2
    else:
      valid_ind = n_matches >= 1

    vote_eligible[i, valid_ind] = 1

  return vote_eligible

def find_errors(labels, constraint_mat):
  true_conn_mat = (labels[:,None] == labels[None,:]).astype(int)
  true_cons = true_conn_mat[constraint_mat[:,0], constraint_mat[:,1]]
  err_ind = true_cons != constraint_mat[:,2]
  return err_ind

def plot_removal(data, labels, keep_ind, constraint_mat):
  err_ind = find_errors(labels, constraint_mat)

  # The ones we kept
  plt.subplot(2,2,1)
  plot_constraints(data, labels=labels)
  plt.title('True grouping')

  # Errors
  plt.subplot(2,2,2)
  plot_constraints(data, constraint_mat=constraint_mat[err_ind,:])
  plt.title('Oracle errors')

  # Good ones we removed
  plt.subplot(2,2,3)
  toss_ind = np.logical_not(keep_ind)
  correct_ind = np.logical_not(err_ind)
  correct_tossed = np.logical_and(toss_ind, correct_ind)
  plot_constraints(data, constraint_mat=constraint_mat[correct_tossed,:])
  plt.title('Correct constraints removed')

  # Errors we left
  plt.subplot(2,2,4)
  plot_constraints(data, constraint_mat=constraint_mat[np.logical_and(keep_ind, err_ind),:])
  plt.title('Errors remaining')

def plot_constraints(data, labels=None, constraint_mat=None):
  """Plot the data and possibly labels and/or pairwise constraints.
  ML constraints will be solid lines, while CL constraints will be
  dashed lines.

    Args:
      data: (N,D) ndarray of the data
      labels: array of class labels
      constraint_mat: (num_constraints, 3) ndarray, where each row
                      is (index of sample 1, index of sample 2,
                      constraint type), where constraint type is 1
                      for must-link, -1 for cannot-link

  """
  marker_size = 7
  if labels is None:
    plt.plot(data[:,0], data[:,1], 'o',
       markerfacecolor=[0.7, 0.7, 0.7],
       markersize=marker_size)

  else:
    classes = set(labels)
    colors = plt.cm.Spectral(np.linspace(0, 1, len(classes)))
    for lab, col in zip(classes, colors):
      ind = labels == lab
      plt.plot(data[ind, 0], data[ind, 1], 'o',
        markerfacecolor=col, markersize=marker_size)

  if constraint_mat is not None:
    for cons in constraint_mat:
      sampPair = cons[:2]
      if cons[2] == 1:
        lineType = '-'
      else:
        lineType = '--'
      plt.plot(data[sampPair,0], data[sampPair,1],
         lineType, color='black', linewidth=3)

  plt.show()


import numpy as np
import pandas as pd
from PIL import Image
from tqdm import tqdm
import os

# konwersja string - integer
def atoi(s):
    n = 0
    for i in s:
        n = n*10 + ord(i) - ord("0")
    return n

# tworzenie folderów
main_dirs = ['test','train']
emotions = ['angry', 'disgusted', 'fearful', 'happy', 'sad', 'surprised', 'neutral']
os.makedirs('data', exist_ok=True)
for dir in main_dirs:
    os.makedirs(os.path.join('data',dir), exist_ok=True)
    for emotion in emotions:
        os.makedirs(os.path.join('data',dir,emotion), exist_ok=True)

# do zliczania plików png w kazdej kategorii
angry = 0
angry_test = 0
disgusted = 0
disgusted_test = 0
fearful = 0
fearful_test = 0
happy = 0
happy_test = 0
sad = 0
sad_test = 0
surprised = 0
surprised_test = 0
neutral = 0
neutral_test = 0

# załadowanie pliku csv, obliczenie Training
data = pd.read_csv('./fer2013/fer2013.csv')
#print(data['Usage'].value_counts())
train_count = int(data['Usage'].value_counts()[0:1])
val_count = num_val = int(data['Usage'].value_counts()[1:2])+int(data['Usage'].value_counts()[2:3])
png = np.zeros((48,48),dtype=np.uint8)
bytes = 48*48
print("Saving images...")

# czytanie pliku csv linia po linii
for i in tqdm(range(len(data))):
    txt = data['pixels'][i]
    words = txt.split()

    # rozmiar obrazów 48x48
    for j in range(bytes):
        xind = j // 48
        yind = j % 48
        png[xind][yind] = atoi(words[j])

    img = Image.fromarray(png)

    # train - czyli wlatuje Training (28709)
    if i < train_count:
        if data['emotion'][i] == 0:
            img.save('data/train/angry/im'+str(angry)+'.png')
            angry += 1
        elif data['emotion'][i] == 1:
            img.save('data/train/disgusted/im'+str(disgusted)+'.png')
            disgusted += 1
        elif data['emotion'][i] == 2:
            img.save('data/train/fearful/im'+str(fearful)+'.png')
            fearful += 1
        elif data['emotion'][i] == 3:
            img.save('data/train/happy/im'+str(happy)+'.png')
            happy += 1
        elif data['emotion'][i] == 4:
            img.save('data/train/sad/im'+str(sad)+'.png')
            sad += 1
        elif data['emotion'][i] == 5:
            img.save('data/train/surprised/im'+str(surprised)+'.png')
            surprised += 1
        elif data['emotion'][i] == 6:
            img.save('data/train/neutral/im'+str(neutral)+'.png')
            neutral += 1

    # test - czyli wlatuje PublicTest i PrivateTest (3589 x 2)
    else:
        if data['emotion'][i] == 0:
            img.save('data/test/angry/im'+str(angry_test)+'.png')
            angry_test += 1
        elif data['emotion'][i] == 1:
            img.save('data/test/disgusted/im'+str(disgusted_test)+'.png')
            disgusted_test += 1
        elif data['emotion'][i] == 2:
            img.save('data/test/fearful/im'+str(fearful_test)+'.png')
            fearful_test += 1
        elif data['emotion'][i] == 3:
            img.save('data/test/happy/im'+str(happy_test)+'.png')
            happy_test += 1
        elif data['emotion'][i] == 4:
            img.save('data/test/sad/im'+str(sad_test)+'.png')
            sad_test += 1
        elif data['emotion'][i] == 5:
            img.save('data/test/surprised/im'+str(surprised_test)+'.png')
            surprised_test += 1
        elif data['emotion'][i] == 6:
            img.save('data/test/neutral/im'+str(neutral_test)+'.png')
            neutral_test += 1

print("Your Dataset is Ready!")


#!/usr/bin/env python

# Project: Person recognition, IKR
# Description: Z vysledku obou klasifikatoru sestavi dalsi klasifikaci na zaklade jejich vyhodnoceni
# Author: Radim Lipka, Tomas Scavincky
# Email: xlipka01@stud.fit.vutbr.cz, xscavn00@stud.fit.vutbr.cz
# Date: 29.4.2016
# File: speech_image.py

__author__ = "Radim Lipka, Tomas Scavincky"
__version__ = "1.0"
__email__ = "xlipka01@stud.fit.vutbr.cz, xscavn00@stud.fit.vutbr.cz"
__date__ = "29.4.2016"

import sys, os, numpy as np

# Input arguments: Speech_log_file, Image_log_file, Output_log_file

def main(args, argv):

	if(args != 3):
		exit(1)

	try:
		speech_file = open(argv[0], "r")
		image_file = open(argv[1], "r")
		speech_image_file = open(argv[2], "w")
	except:
		exit(5)

	speech_data = sorted(speech_file.readlines(), key=lambda result: result[0])
	image_data = sorted(image_file.readlines(), key=lambda result: result[0])

	for dato in range(len(speech_data)):
		speech_data[dato] = speech_data[dato].split(' ')

	for dato in range(len(image_data)):
		image_data[dato] = image_data[dato].split(' ')

	for dato in range(len(speech_data) if (len(speech_data) == len(image_data)) else 0):
		if(image_data[dato][0] != speech_data[dato][0]):
			exit(6)

	SI_agree = int(0)
	SI_disagree = int(0)

	for index in range(len(speech_data)):
		mixed_score = (float(speech_data[index][1]) + float(image_data[index][1])) / 2
		hard_score = 1 if(mixed_score > 0.5) else 0

		print("FILE:  " + speech_data[index][0] + "\tSCORE:  " + str(round(mixed_score, 3)) + "\t\tHARD_DECISION:  " + str(hard_score))
		speech_image_file.write(speech_data[index][0] + " " + str(mixed_score) + " " + str(hard_score) + "\n")

		SI_agree += 1 if(speech_data[index][2] == image_data[index][2]) else 0
		SI_disagree += 1 if(speech_data[index][2] != image_data[index][2]) else 0

	print("\nRESULTS\n")
	print("Same results with botw clasiffiers:      " + str(SI_agree))
	print("Different result with botw claffiers: " + str(SI_disagree))

	print("\nFINISHED\n")

if __name__ == '__main__':
	main(len(sys.argv) - 1, sys.argv[1:])

# End of file speech_image.py


def wrap_by_k(lst, k):
    for i in range(k):
        for i in range(len(lst)-2,-1,-1):
            lst[i+1],lst[i] = lst[i],lst[i+1]
    return lst

A = [1,2,3,4,5,6,7,8]
print("List before wrap  by k: ", A)
print("Wrap by K: ", wrap_by_k(A,3))
#print("This would run at O(n) complexity as it has to transverse through the entire array")


import telebot
import ResoursesGSB
import ChooseTypeOfResourses
import DBdirectioner

bot = telebot.TeleBot('Тут токен')
resourses = ResoursesGSB

"""
    этот метот старта бота, можно написать чо угодно
"""

@bot.message_handler(commands=['start'])
def start_message(message):
    bot.send_message(message.chat.id, 'Привет, ты написал мне /start')

"""
    это пин приказа
"""

@bot.message_handler(commands=['pin'])
def pinCommandInChates(message):
    chatsID = ['Тут айди чатов, где надо пинить приказы', 'и тут тоже', 'и так далее']
    admins = ['тут айди наших админов, которые могут отдавать приказы, чтобы простые смертные не имели такой привелегии']
    truePin = 0
    for i in admins:
        if message.chat.id == i:  # Проверяем, кто отправил приказ
            placeToAttack = message.text[message.text.find('n ') + 2:]  # достаем место куда надо идти
            messageToAttack = "Мои дорогие, все идем на: " + placeToAttack  # вот тут можно изменять сообщение которое будет пиниться
            truePin = 1

        if truePin == 1:
            for j in chatsID:  # Пробегаем по всем чатам и пиним там сообщения
                sendMessageVal = bot.send_message(j, messageToAttack)
                bot.pin_chat_message(j, sendMessageVal.message_id)
            else:
                bot.send_message(message.chat.id, 'У вас нет таких привелегий;(')

"""
    Это парсер рюкзака
    пока не до-конца готов)
    надо будет разобраться с БД и я все сделаю по-человечески
"""

@bot.message_handler(content_types=['text'])
def get_text_messages(message):
    if (message.forward_from.username == "@rf_telegram_bot"):
        DBdirectioner.create(message.text)

bot.polling() #Строчка обязательная


# -*- coding: utf-8 -*-
# @Author: amish
# @Date:   2021-02-27 17:06:11
# @Last Modified by:   Amish Sharma
# @Last Modified time: 2021-03-03 10:56:12

from string import ascii_lowercase, ascii_uppercase, digits
import sys
from modules import information, read

def clean_all(txt):
	temp = [ascii_lowercase, ascii_uppercase, digits]
	txt2 = txt
	for ch in txt:
		if ch in ascii_lowercase or ch in ascii_uppercase or ch in digits:
			continue
		else:
			txt2 = txt2.replace(ch, " ")
	txt = txt2[:]
	return txt

def clean_text(text):
	try:
		ctry = cit = ""
		cities = read.read_file("worldcities.csv"); countries = read.read_file("countries.txt")
		countries = list(map(lambda x: x.strip().lower(), countries))
		cities = list(map(lambda x: x.strip().lower(), cities))
		txt = clean_all(text)
		for word in txt.split():
			if word.lower() in countries:
				ctry = word
				if word in text.split():
					text.replace(word, "")

		for word in txt.split():
			if word.lower() in cities:
				cit = word
				if word in text.split():
					text.replace(word, "")

		text2 = ""
		for i in range(0, len(text)):
			if text[i] == ' ' or text[i] == '-':
				j = i; ct1 = ct2 = 0; j -= 1
				while j >= 0 and (text[j] >= '0' and text[j] <= '9'):
					j -= 1; ct1 += 1
				j = i; j += 1
				while j < len(text) and (text[j] >= '0' and text[j] <= '9'):
					j += 1; ct2 += 1
				if ct1 >= 2 and ct2 >= 2:
					continue
				else:
					text2 += text[i]
			else:
				text2 += text[i]
		text = text2[:]

		text2 = text[:]
		for ch in text:
			if ch in ['`', '~', '#', '*', '^', '$', '%']:
				text2 = text.replace(ch, "")
		text = text2[:]
		return [text, ctry, cit]
	except:
		# print("Text can't be cleaned!!\n")
		# print(sys.exc_info()[1])
		return [text, "", ""]

def clean_words(text):
	try:
		words = text.split("\n")
		temp = words.copy()
		for sentence in words:
			if information.info(sentence) < 5:
				temp.remove(sentence)
		words = temp.copy()
		return words
	except:
		# print("Words cannot be cleaned!!\n")
		# print(sys.exc_info()[1])
		return []


import pandas as pd
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import SMOTE
import matplotlib.pyplot as plt

# Provided GAN code goes here
def generate_synthetic_data(data, target_column, n_samples):
    # Define the generator model
    # Define the generator model
    class Generator(nn.Module):
        def __init__(self, input_dim, output_dim):
            super(Generator, self).__init__()
            self.fc1 = nn.Linear(input_dim, 128)
            self.fc2 = nn.Linear(128, 256)
            self.fc3 = nn.Linear(256, 512)
            self.fc4 = nn.Linear(512, output_dim)
            self.activation = nn.LeakyReLU(0.2)
            self.dropout = nn.Dropout(0.3)
            self.noise = nn.Parameter(torch.zeros(input_dim))

        def forward(self, x):
            x = x + self.noise * torch.randn(x.size())
            x = self.activation(self.fc1(x))
            x = self.dropout(x)
            x = self.activation(self.fc2(x))
            x = self.dropout(x)
            x = self.activation(self.fc3(x))
            x = torch.tanh(self.fc4(x))
            return x

    # Define the discriminator model
    class Discriminator(nn.Module):
        def __init__(self, input_dim):
            super(Discriminator, self).__init__()
            self.fc1 = nn.Linear(input_dim, 512)
            self.fc2 = nn.Linear(512, 256)
            self.fc3 = nn.Linear(256, 128)
            self.fc4 = nn.Linear(128, 1)
            self.activation = nn.LeakyReLU(0.2)
            self.dropout = nn.Dropout(0.3)

        def forward(self, x):
            x = self.activation(self.fc1(x))
            x = self.dropout(x)
            x = self.activation(self.fc2(x))
            x = self.dropout(x)
            x = self.activation(self.fc3(x))
            x = torch.sigmoid(self.fc4(x))
            return x

    # Initialize models
    input_dim = data.shape[1]
    output_dim = data.shape[1]
    generator = Generator(input_dim, output_dim)
    discriminator = Discriminator(output_dim)

    # Define loss and optimizers
    criterion = nn.BCELoss()
    optimizer_G = optim.Adam(generator.parameters(), lr=0.0002, betas=(0.5, 0.999))
    optimizer_D = optim.Adam(discriminator.parameters(), lr=0.0002, betas=(0.5, 0.999))

    # Train the GAN model
    batch_size = 8
    epochs = 100
    for epoch in range(epochs):
        real_data = torch.tensor(data.sample(n=batch_size).values, dtype=torch.float32)
        real_labels = torch.ones((batch_size, 1))
        noise = torch.randn((batch_size, input_dim))
        synthetic_data = generator(noise)
        synthetic_labels = torch.zeros((batch_size, 1))

        # Train discriminator
        optimizer_D.zero_grad()

        real_loss = criterion(discriminator(real_data), real_labels)
        synthetic_loss = criterion(discriminator(synthetic_data.detach()), synthetic_labels)
        d_loss = (real_loss + synthetic_loss) / 2

        d_loss.backward()
        optimizer_D.step()

        # Train generator
        optimizer_G.zero_grad()

        g_loss = criterion(discriminator(synthetic_data), real_labels)

        g_loss.backward()
        optimizer_G.step()

        if epoch % 100 == 0:
            print(f"Epoch {epoch}, Discriminator Loss: {d_loss.item()}, Generator Loss: {g_loss.item()}")

    # Generate synthetic samples
    noise = torch.randn((n_samples, input_dim))
    synthetic_samples = generator(noise).detach().numpy()

    # Round synthetic samples to integers
    synthetic_samples = np.round(synthetic_samples)

    # Convert synthetic samples to pandas DataFrame
    synthetic_data = pd.DataFrame(synthetic_samples, columns=data.columns)

    # Clip has_sepsis values to be within the range [0, 1]
    synthetic_data[target_column] = np.clip(synthetic_data[target_column], 0, 1)

    # Convert has_sepsis values to integers
    synthetic_data[target_column] = synthetic_data[target_column].astype(int)

    # Make sure signal_length is a positive integer value
    synthetic_data['signal_length'] = synthetic_data['signal_length'].abs().astype(int)

    return synthetic_data

def plot_original_vs_synthetic_gan(original_data, synthetic_data, target_column):
    fig, axes = plt.subplots(1, 2, figsize=(10, 5))

    original_counts = original_data[target_column].value_counts(normalize=True)
    synthetic_counts = synthetic_data[target_column].value_counts(normalize=True)

    axes[0].bar(original_counts.index, original_counts.values, alpha=0.5, label="Original Data")
    axes[1].bar(synthetic_counts.index, synthetic_counts.values, alpha=0.5, label="Synthetic Data")

    axes[0].set_title("Original Data")
    axes[1].set_title("Synthetic Data")
    axes[0].set_ylabel("Proportion")
    axes[1].set_ylabel("Proportion")

    for ax in axes:
        ax.set_xlabel(target_column)
        ax.legend()

    plt.show()

def smote_gan(data, target_column, n_samples):
    # Split data into features and target
    X = data.drop(target_column, axis=1)
    y = data[target_column]

    # Apply SMOTE
    smote = SMOTE()

    X_resampled, y_resampled = smote.fit_resample(X, y)

    # Create new DataFrame with SMOTE-resampled data
    resampled_data = pd.concat([X_resampled, y_resampled], axis=1)

    # Apply GAN to generate synthetic data
    synthetic_data = generate_synthetic_data(resampled_data,target_column, n_samples)

    return synthetic_data


# Python program to check if a number is palindrome or not

# Receive input string
s = input("Enter a string:")
# Remove leading and trailing whitespace from the string
s = s.strip()

# Initialize low and high indices for comparison
low = 0
high = len(s) - 1

# Iterate until low is less than high
while low < high:
    # Print the current indices for debugging
    print(low, "    ", high)
    # Compare characters at low and high indices
    if s[low] == s[high]:
        # Move to the next pair of characters
        low += 1
        high -= 1
    else:
        # If characters don't match, print "Not Palindrome" and break the loop
        print("Not Palindrome")
        break

# Check if low is greater than or equal to high, indicating that all characters matched
if low >= high:
    print("Is Palindrome")