#coding: utf-8

from grading_tools import TestConfiguration, ProgramTest, CheckOutputMixin, CheckStderrMixin, CheckMultiCorePerformance
import sys
import numpy as np
from sklearn.metrics.pairwise import euclidean_distances
import os
import io
import os.path as osp
import sys
from contextlib import redirect_stdout
import numba as nb
from numba.typed import List as nbList
import pprint

from colorama import init, Fore

def green(s):
    return Fore.GREEN + str(s) + Fore.RESET

def red(s):
    return Fore.RED + str(s) + Fore.RESET

def test_result(b):
    if b:
        return green(b)
    else:
        return red(b)

class BaseTSP:
    def parse_input(self, inp):
        lines = inp.split('\n')
        N = int(lines[0])
        points = np.zeros((N, 2))
        for i, l in enumerate(lines[1:-1]):
            p = l.split()
            points[i, 0] = float(p[0])
            points[i, 1] = float(p[1])

        distances = euclidean_distances(points)
        return N, points, distances

    def parse_output(self, stdout):
        try:
            lines = stdout.split('\n')
            distance, opt = lines[0].split()
            distance = float(distance)
            order = [int(i) for i in lines[1].split()]
        except:
            for i, l in enumerate(lines):
                print('linha', i, ':', l)
            raise Exception("Formato de saída inválido")

        return distance, opt, order

    def tamanho_tour(self, order, distances):
        tour = 0
        last = order[0]
        N = len(order)
        for i in range(1, N):
            tour += distances[last, order[i]]
            last = order[i]
        tour += distances[order[N-1], order[0]]

        return tour

    def test_caminho_tem_distancia_mostrada(self, test, stdout, stderr):
        N, points, distances = self.parse_input(test.input)
        distance, opt, order = self.parse_output(stdout)
        tour = self.tamanho_tour(order, distances)
        res = np.isclose(tour, distance)
        if not res:
            print('Distância calculada:', tour, 'Mostrada:', distance)
        return res

    def test_passa_por_todas_cidades(self, test, stdout, stderr):
        lines = stdout.split('\n')
        cities = [int(i) for i in lines[1].split()]
        cities.sort()
        return cities == list(range(len(cities)))

@nb.jit
def tamanho_tour_nb(order, distances):
        tour = 0
        last = order[0]
        N = len(order)
        for i in range(1, N):
            tour += distances[last, order[i]]
            last = order[i]
        tour += distances[order[N-1], order[0]]

        return tour

def tem_troca_py(order, distance, distances):
    N = len(order)
    for i in range(N):
        for j in range(i+1, N):
            new_order = order[:]
            new_order[i], new_order[j] = new_order[j], new_order[i]
            new_distance = tamanho_tour_nb(new_order, distances)
            is_close = abs(distance - new_distance)
            if is_close > 0.01 and distance > new_distance:
                return True, i, j, new_distance

    return False, 0, 0, 0.0

tem_troca_nb = nb.jit(tem_troca_py)

class TesteBuscaLocal(ProgramTest, BaseTSP):
    def tem_troca(self, order, distance, distances):
        distance = self.tamanho_tour(order, distances)
        ret, i, j, new_distance = tem_troca_nb(nbList(order), distance, distances)
        if ret:
            print('Troca encontrada: ', i, j)
            print('Melhoria:', distance, '->', new_distance)
        return ret

    def test_nao_tem_troca(self, test, stdout, stderr):
        N, points, distances = self.parse_input(test.input)
        distance, opt, order = self.parse_output(stdout)

        return not self.tem_troca(order, distance, distances)

    def parse_stderr(self, stderr):
        lines = stderr.split('\n')
        solutions = []
        for l in lines:
            l = l.strip()
            try:
                distance = float(l.split(' ')[1])
            except:
                print(l.split())
                raise Exception('Formato de saída de erros inválido')
            try:
                order = [int(i) for i in l.split(' ')[2:]]
            except:
                print(l.split(' ')[2:])
                raise Exception('Formato de saída de erros inválido')

            solutions.append((distance, order))

        return solutions

    def test_fez_10N_solucoes(self, test, stdout, stderr):
        N, points, distances = self.parse_input(test.input)
        distance, opt, order = self.parse_output(stdout)
        solutions = self.parse_stderr(stderr)
        return len(solutions) == 10 * N

    def test_toda_solucao_tem_caminho_correto(self, test, stdout, stderr):
        N, points, distances = self.parse_input(test.input)
        distance, opt, order = self.parse_output(stdout)
        solutions = self.parse_stderr(stderr)
        for i, sol in enumerate(solutions):
            dist_calculada = self.tamanho_tour(sol[1], distances)
            if not np.isclose(dist_calculada, sol[0]):
                print(f'Caminho incorreto na linha {i}. Mostrada {sol[0]}, calculada {dist_calculada}')
                print(sol[1], self.tamanho_tour(sol[1], distances), tamanho_tour_nb(sol[1], distances))
                return False
        return True

    def test_toda_solucao_eh_otimo_local(self, test, stdout, stderr):
        N, points, distances = self.parse_input(test.input)
        distance, opt, order = self.parse_output(stdout)
        solutions = self.parse_stderr(stderr)
        for i, sol in enumerate(solutions):
            if self.tem_troca(sol[1], sol[0], distances):
                print('Troca acima encontrada na linha', i)
                return False
        return True

class TesteHeuristico(ProgramTest, CheckOutputMixin, BaseTSP):
    pass

class TesteBuscaExaustiva(ProgramTest, CheckStderrMixin, BaseTSP):
    def test_tour_otimo(self, test, stdout, stderr):
        N, points, distances = self.parse_input(test.input)
        distance, opt, order = self.parse_output(stdout)
        correct_distance, correct_opt, correct_order = self.parse_output(test.output)
        return np.isclose(correct_distance, distance)

    def test_opt_1(self, test, stdout, stderr):
        distance, opt, order = self.parse_output(stdout)
        return int(opt) == 1

class TesteBuscaExaustivaPerf(ProgramTest, BaseTSP):
    def test_tour_otimo(self, test, stdout, stderr):
        N, points, distances = self.parse_input(test.input)
        distance, opt, order = self.parse_output(stdout)
        correct_distance, correct_opt, correct_order = self.parse_output(test.output)
        return np.isclose(correct_distance, distance)

    def test_opt_1(self, test, stdout, stderr):
        distance, opt, order = self.parse_output(stdout)
        return int(opt) == 1

class TestePerformance(ProgramTest, BaseTSP):
    pass

class TesteMultiCorePequeno(TesteBuscaLocal, CheckMultiCorePerformance):
    pass

def compila_programa(ext, nome, flags, nvcc=False):
    compilador = 'g++'
    if nvcc:
        compilador = 'nvcc'
    arquivos = ' '.join([arq for arq in os.listdir('.') if arq.split('.')[-1] == ext])
    ret = os.system(f'{compilador} {arquivos} -O3 {flags} -o {nome} 2>1 > /dev/null')
    if ret != 0 :
        raise IOError(f'Erro de compilação em {os.getcwd()}!')

def testa_heuristico():
    os.chdir('heuristico')
    compila_programa('cpp', 'heuristico', '')
    tests = TestConfiguration.from_pattern('.', 'in*.txt', 'out*txt')
    tester = TesteHeuristico('./heuristico', tests)
    res = tester.main()
    os.chdir('..')
    return res

def testa_busca_local_sequencial():
    os.chdir('busca-local')
    compila_programa('cpp', 'busca-local', '')
    tests = TestConfiguration.from_pattern('.', 'in*.txt', 'out*txt',
                                           environ={
                                               'DEBUG': '1'
                                           }, time_limit=1800)
    tester = TesteBuscaLocal('./busca-local', tests)
    res = tester.main()
    os.chdir('..')
    return res

def testa_busca_exaustiva():
    os.chdir('busca-exaustiva')
    compila_programa('cpp', 'busca-global', '')
    tests = TestConfiguration.from_pattern('.', 'in*.txt', 'out*txt',
                                           'err*txt', environ={
                                               'DEBUG': '1'
                                           })
    tester = TesteBuscaExaustiva('./busca-global', tests)
    res = tester.main()
    os.chdir('..')
    return res

def testa_busca_local_omp():
    os.chdir('busca-local')
    compila_programa('cpp', 'busca-local-paralela', '-fopenmp')
    tests = TestConfiguration.from_pattern('.', 'in*.txt', 'out*txt',
                                           environ={
                                               'DEBUG': '1'
                                           }, time_limit=1800)
    files_multicore = [f'./in-{i}.txt' for i in range(5, 10)]
    omp_tests = {k:v for k, v in tests.items() if k in files_multicore}
    del omp_tests['./in-7.txt']
    del omp_tests['./in-8.txt']

    teste_simples = TesteMultiCorePequeno('./busca-local-paralela', omp_tests)
    res = teste_simples.main()
    os.chdir('..')
    return res

def testa_busca_local_gpu():
    os.chdir('busca-local')
    compila_programa('cu', 'busca-local-gpu', '', True)
    tests = TestConfiguration.from_pattern('.', 'in*.txt', 'out*txt',
                                        environ={
                                            'DEBUG': '1'
                                        })
    tester = TesteBuscaLocal('./busca-local-gpu', tests)

    perf_tests = {}
    perf_tests['in6.txt'] = TestConfiguration.from_file('in-6.txt',
                                                '',
                                                check_stderr=False,
                                                environ={'DEBUG': '0'},
                                                time_limit=30)
    perf_tests['in11.txt'] = TestConfiguration.from_file('perf-in-11.txt',
                                                '',
                                                check_stderr=False,
                                                environ={'DEBUG': '0'},
                                                time_limit=30)

    perf_tester = TestePerformance('./busca-local-gpu', perf_tests)
    res = tester.main() and perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_local_gpu2():
    os.chdir('busca-local')
    compila_programa('cu', 'busca-local-gpu2', '', True)
    tests = TestConfiguration.from_pattern('.', 'in*.txt', 'out*txt',
                                        environ={
                                            'DEBUG': '1'
                                        })
    tester = TesteBuscaLocal('./busca-local-gpu2', tests)

    perf_tests = {}
    perf_tests['in6.txt'] = TestConfiguration.from_file('in-6.txt',
                                                '',
                                                check_stderr=False,
                                                environ={'DEBUG': '0'},
                                                time_limit=10)
    perf_tests['in11.txt'] = TestConfiguration.from_file('perf-in-11.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=9)

    perf_tests['in12.txt'] = TestConfiguration.from_file('perf-in-12.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=65)

    perf_tester = TestePerformance('./busca-local-gpu2', perf_tests)
    res = tester.main() and perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_local_perf():
    os.chdir('busca-local')
    compila_programa('cpp', 'busca-local-perf', '')
    performance_tests = {}
    performance_tests['in10.txt'] = TestConfiguration.from_file('in-10.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=0.5)

    performance_tests['in11.txt'] = TestConfiguration.from_file('perf-in-11.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=9)

    performance_tests['in12.txt'] = TestConfiguration.from_file('perf-in-12.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=180)

    perf_tester = TestePerformance('./busca-local-perf', performance_tests)
    res = perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_exaustiva_perf1():
    os.chdir('busca-exaustiva')
    compila_programa('cpp', 'busca-global-perf', '-fopenmp')
    performance_tests = {}
    performance_tests['in-5.txt'] = TestConfiguration.from_file('in-5.txt',
                                                    'out-5.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['in-6.txt'] = TestConfiguration.from_file('in-6.txt',
                                                    'out-6.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['in-7.txt'] = TestConfiguration.from_file('in-7.txt',
                                                    'out-7.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=5)

    perf_tester = TesteBuscaExaustivaPerf('./busca-global-perf', performance_tests)
    res = perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_exaustiva_perf2():
    os.chdir('busca-exaustiva')
    compila_programa('cpp', 'busca-global-perf2', '-fopenmp')
    performance_tests = {}
    performance_tests['in-7.txt'] = TestConfiguration.from_file('in-7.txt',
                                                    'out-7.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['perf-in-8.txt'] = TestConfiguration.from_file('perf-in-8.txt',
                                                    'perf-out-8.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=2)
    performance_tests['perf-in-9.txt'] = TestConfiguration.from_file('perf-in-9.txt',
                                                    'perf-out-9.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=30)

    perf_tester = TesteBuscaExaustivaPerf('./busca-global-perf2', performance_tests)
    res = perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_exaustiva_perf3():
    os.chdir('busca-exaustiva')
    compila_programa('cpp', 'busca-global-perf3', '-fopenmp')
    performance_tests = {}
    performance_tests['in-7.txt'] = TestConfiguration.from_file('in-7.txt',
                                                    'out-7.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['perf-in-8.txt'] = TestConfiguration.from_file('perf-in-8.txt',
                                                    'perf-out-8.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['perf-in-9.txt'] = TestConfiguration.from_file('perf-in-9.txt',
                                                    'perf-out-9.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=10)
    performance_tests['perf-in-10.txt'] = TestConfiguration.from_file('perf-in-10.txt',
                                                    'perf-out-10.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=25)

    perf_tester = TesteBuscaExaustivaPerf('./busca-global-perf3', performance_tests)
    res = perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_exaustiva_perf4():
    os.chdir('busca-exaustiva')
    compila_programa('cpp', 'busca-global-perf4', '-fopenmp')
    performance_tests = {}
    performance_tests['in-7.txt'] = TestConfiguration.from_file('in-7.txt',
                                                    'out-7.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['perf-in-8.txt'] = TestConfiguration.from_file('perf-in-8.txt',
                                                    'perf-out-8.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['perf-in-9.txt'] = TestConfiguration.from_file('perf-in-9.txt',
                                                    'perf-out-9.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=2)
    performance_tests['perf-in-10.txt'] = TestConfiguration.from_file('perf-in-10.txt',
                                                    'perf-out-10.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=3)
    performance_tests['perf-in-11.txt'] = TestConfiguration.from_file('perf-in-11.txt',
                                                    'perf-out-11.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=3)
    perf_tester = TesteBuscaExaustivaPerf('./busca-global-perf4', performance_tests)
    res = perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_exaustiva_perf5():
    os.chdir('busca-exaustiva')
    compila_programa('cpp', 'busca-global-perf5', '-fopenmp')
    performance_tests = {}
    performance_tests['in-7.txt'] = TestConfiguration.from_file('in-7.txt',
                                                    'out-7.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['perf-in-8.txt'] = TestConfiguration.from_file('perf-in-8.txt',
                                                    'perf-out-8.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=1)
    performance_tests['perf-in-9.txt'] = TestConfiguration.from_file('perf-in-9.txt',
                                                    'perf-out-9.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=2)
    performance_tests['perf-in-10.txt'] = TestConfiguration.from_file('perf-in-10.txt',
                                                    'perf-out-10.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=3)
    performance_tests['perf-in-11.txt'] = TestConfiguration.from_file('perf-in-11.txt',
                                                    'perf-out-11.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=3)
    performance_tests['perf-in-12.txt'] = TestConfiguration.from_file('perf-in-12.txt',
                                                    'perf-out-12.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=45)
    performance_tests['perf-in-13.txt'] = TestConfiguration.from_file('perf-in-13.txt',
                                                    'perf-out-13.txt',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=70)

    perf_tester = TesteBuscaExaustivaPerf('./busca-global-perf5', performance_tests)
    res = perf_tester.main()
    os.chdir('..')
    return res

def testa_busca_local_omp_perf():
    os.chdir('busca-local')
    compila_programa('cpp', 'busca-local-perf-omp', '-fopenmp')
    performance_tests = {}
    performance_tests['in10.txt'] = TestConfiguration.from_file('in-10.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=0.5)

    performance_tests['in11.txt'] = TestConfiguration.from_file('perf-in-11.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=3)

    performance_tests['in12.txt'] = TestConfiguration.from_file('perf-in-12.txt',
                                                    '',
                                                    check_stderr=False,
                                                    environ={'DEBUG': '0'},
                                                    time_limit=60)

    perf_tester = TestePerformance('./busca-local-perf-omp', performance_tests)
    res = perf_tester.main()
    os.chdir('..')
    return res

if __name__ == "__main__":

    ignorar = io.StringIO()

    testesD = {
        'heuristico': ('Heuristico (sequencial)', testa_heuristico),
        'local': ('Busca local (sequencial)', testa_busca_local_sequencial),
        'global': ('Busca exaustiva (sequencial)', testa_busca_exaustiva),
        'local-paralela': ('Busca local (paralela)', testa_busca_local_omp),
        'local-gpu': ('Busca local (GPU)', testa_busca_local_gpu)
    }

    if len(sys.argv) > 1:
        tst = sys.argv[1]
        if tst in testesD:
            tst = testesD[tst]
            print(tst[0], ':', tst[1]())
        else:
            print('Testes disponíveis:')
            pprint.pprint(testesD)
        sys.exit(0)

    print(f'''
===========================================
Rubrica D
===========================================''')

    with open('feedback-heuristico.txt', 'w') as f:
        with redirect_stdout(f):
            res_heuristico = testa_heuristico()

    print(f'Heuristico (sequencial): {test_result(res_heuristico)}')

    with open('feedback-local.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_local_sequencial = testa_busca_local_sequencial()

    print(f'Busca local (sequencial): {test_result(res_busca_local_sequencial)}')

    with open('feedback-global.txt', 'w') as f:
        with redirect_stdout(f):
            res_global = testa_busca_exaustiva()
    print(f'Busca exaustiva (sequencial): {test_result(res_global)}')

    with open('feedback-local-paralela.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_local_paralela = testa_busca_local_omp()

    print(f'Busca local (paralela): {test_result(res_busca_local_paralela)}')

    with open('feedback-local-gpu.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_local_gpu = testa_busca_local_gpu()

    print(f'Busca local (GPU): {test_result(res_busca_local_gpu)}')

    print('''===========================================
Rubrica A+
===========================================''')

    with open('feedback-local-perf.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_local_perf = testa_busca_local_perf()

    print(f'Busca local (desempenho sequencial): {test_result(res_busca_local_perf)}')

    with open('feedback-local-perf-omp.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_local_omp_perf = testa_busca_local_omp_perf()

    print(f'Busca local (desempenho paralelo 1): {test_result(res_busca_local_omp_perf)}')

    with open('feedback-local-perf-gpu2.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_local_perf_gpu = testa_busca_local_gpu2()

    print(f'Busca local (desempenho GPU 1): {test_result(res_busca_local_perf_gpu)}')

    with open('feedback-global-perf.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_global_p1 = testa_busca_exaustiva_perf1()

    print(f'Busca global (desempenho nível 1): {test_result(res_busca_global_p1)}')

    with open('feedback-global-perf2.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_global_p2 = testa_busca_exaustiva_perf2()

    print(f'Busca global (desempenho nível 2): {test_result(res_busca_global_p2)}')

    with open('feedback-global-perf3.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_global_p3 = testa_busca_exaustiva_perf3()

    print(f'Busca global (desempenho nível 3): {test_result(res_busca_global_p3)}')

    with open('feedback-global-perf4.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_global_p4 = testa_busca_exaustiva_perf4()

    print(f'Busca global (desempenho nível 4): {test_result(res_busca_global_p4)}')

    with open('feedback-global-perf5.txt', 'w') as f:
        with redirect_stdout(f):
            res_busca_global_p5 = testa_busca_exaustiva_perf5()

    print(f'Busca global (desempenho nível 5): {test_result(res_busca_global_p5)}')


import re
import torch
import torch.nn as nn
from torch.nn import init
import functools
from torch.optim import lr_scheduler
import torch.nn.functional as F
from torch.nn.utils.spectral_norm import spectral_norm as SpectralNorm
######################################################################################
# base function for network structure
######################################################################################

# def init_weights(net, init_type='normal', gain=0.02):
#     """Get different initial method for the network weights"""
#     def init_func(m):
#         classname = m.__class__.__name__
#         if hasattr(m, 'weight') and (classname.find('Conv')!=-1 or classname.find('Linear')!=-1):
#             if init_type == 'normal':
#                 init.normal_(m.weight.data, 0.0, gain)
#             elif init_type == 'xavier':
#                 init.xavier_normal_(m.weight.data, gain=gain)
#             elif init_type == 'kaiming':
#                 init.kaiming_normal_(m.weight.data, a=0, mode='fan_in')
#             elif init_type == 'orthogonal':
#                 init.orthogonal_(m.weight.data, gain=gain)
#             else:
#                 raise NotImplementedError('initialization method [%s] is not implemented' % init_type)
#             if hasattr(m, 'bias') and m.bias is not None:
#                 init.constant_(m.bias.data, 0.0)
#         elif classname.find('BatchNorm2d') != -1:
#             init.normal_(m.weight.data, 1.0, 0.02)
#             init.constant_(m.bias.data, 0.0)

#     print('initialize network with %s' % init_type)
#     net.apply(init_func)

# Creates SPADE normalization layer based on the given configuration
# SPADE consists of two steps. First, it normalizes the activations using
# your favorite normalization method, such as Batch Norm or Instance Norm.
# Second, it applies scale and bias to the normalized output, conditioned on
# the segmentation map.
# The format of |config_text| is spade(norm)(ks), where
# (norm) specifies the type of parameter-free normalization.
#       (e.g. syncbatch, batch, instance)
# (ks) specifies the size of kernel in the SPADE module (e.g. 3x3)
# Example |config_text| will be spadesyncbatch3x3, or spadeinstance5x5.
# Also, the other arguments are
# |norm_nc|: the #channels of the normalized activations, hence the output dim of SPADE
# |label_nc|: the #channels of the input semantic feature map, hence the input dim of SPADE

def calc_mean_std(feat, eps=1e-5):
    # eps is a small value added to the variance to avoid divide-by-zero.
    size = feat.size()
    assert (len(size) == 4)
    N, C = size[:2]
    feat_var = feat.view(N, C, -1).var(dim=2) + eps
    feat_std = feat_var.sqrt().view(N, C, 1, 1)
    feat_mean = feat.view(N, C, -1).mean(dim=2).view(N, C, 1, 1)
    return feat_mean, feat_std

def adaptive_instance_normalization(content_feat, style_feat):
    assert (content_feat.size()[:2] == style_feat.size()[:2])
    size = content_feat.size()
    style_mean, style_std = calc_mean_std(style_feat)
    content_mean, content_std = calc_mean_std(content_feat)

    normalized_feat = (content_feat - content_mean.expand(
        size)) / content_std.expand(size)
    return normalized_feat * style_std.expand(size) + style_mean.expand(size)

def _calc_feat_flatten_mean_std(feat):
    # takes 3D feat (C, H, W), return mean and std of array within channels
    assert (feat.size()[0] == 3)
    assert (isinstance(feat, torch.FloatTensor))
    feat_flatten = feat.view(3, -1)
    mean = feat_flatten.mean(dim=-1, keepdim=True)
    std = feat_flatten.std(dim=-1, keepdim=True)
    return feat_flatten, mean, std

def get_norm_layer(norm_type='batch'):
    """Get the normalization layer for the networks"""
    if norm_type == 'batch':
        norm_layer = functools.partial(nn.BatchNorm2d, momentum=0.1, affine=True)
    elif norm_type == 'instance':
        norm_layer = functools.partial(nn.InstanceNorm2d, affine=True)
    elif norm_type == 'adain':
        norm_layer = functools.partial(ADAIN)
    elif norm_type == 'spade':
        norm_layer = functools.partial(SPADE, config_text='spadeinstance3x3')
    elif norm_type == 'none':
        norm_layer = None
    else:
        raise NotImplementedError('normalization layer [%s] is not found' % norm_type)

    if norm_type != 'none':
        norm_layer.__name__ = norm_type

    return norm_layer

def get_nonlinearity_layer(activation_type='PReLU'):
    """Get the activation layer for the networks"""
    if activation_type == 'ReLU':
        nonlinearity_layer = nn.ReLU()
    elif activation_type == 'SELU':
        nonlinearity_layer = nn.SELU()
    elif activation_type == 'LeakyReLU':
        nonlinearity_layer = nn.LeakyReLU(0.1)
    elif activation_type == 'PReLU':
        nonlinearity_layer = nn.PReLU()
    else:
        raise NotImplementedError('activation layer [%s] is not found' % activation_type)
    return nonlinearity_layer

def get_scheduler(optimizer, opt):
    """Get the training learning rate for different epoch"""
    if opt.lr_policy == 'lambda':
        def lambda_rule(epoch):
            lr_l = 1.0 - max(0, epoch+1+1+opt.iter_count-opt.niter) / float(opt.niter_decay+1)
            return lr_l
        scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule)
    elif opt.lr_policy == 'step':
        scheduler = lr_scheduler.StepLR(optimizer, step_size=opt.lr_decay_iters, gamma=0.1)
    elif opt.lr_policy == 'exponent':
        scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.95)
    else:
        raise NotImplementedError('learning rate policy [%s] is not implemented', opt.lr_policy)
    return scheduler

def print_network(net):
    """print the network"""
    num_params = 0
    for param in net.parameters():
        num_params += param.numel()
    print(net)
    print('total number of parameters: %.3f M' % (num_params/1e6))

def init_net(net, init_type='normal', activation='relu', gpu_ids=[]):
    """print the network structure and initial the network"""
    print_network(net)

    if len(gpu_ids) > 0:
        assert(torch.cuda.is_available())
        net.cuda()
        net = torch.nn.DataParallel(net, gpu_ids)
    init_weights(net, init_type)
    return net

def _freeze(*args):
    """freeze the network for forward process"""
    for module in args:
        if module:
            for p in module.parameters():
                p.requires_grad = False

def _unfreeze(*args):
    """ unfreeze the network for parameter update"""
    for module in args:
        if module:
            for p in module.parameters():
                p.requires_grad = True

def _freeze_flow(*args):
    """ unfreeze the network for parameter update"""
    for module in args:
        if module:
            for p in module.parameters():
                p.requires_grad = True

def spectral_norm(module, use_spect=True):
    """use spectral normal layer to stable the training process"""
    if use_spect:
        return SpectralNorm(module)
    else:
        return module

def coord_conv(input_nc, output_nc, use_spect=False, use_coord=False, with_r=False, **kwargs):
    """use coord convolution layer to add position information"""
    if use_coord:
        return CoordConv(input_nc, output_nc, with_r, use_spect, **kwargs)
    else:
        return spectral_norm(nn.Conv2d(input_nc, output_nc, **kwargs), use_spect)

######################################################################################
# Network basic function
######################################################################################
class AddCoords(nn.Module):
    """
    Add Coords to a tensor
    """
    def __init__(self, with_r=False):
        super(AddCoords, self).__init__()
        self.with_r = with_r

    def forward(self, x):
        """
        :param x: shape (batch, channel, x_dim, y_dim)
        :return: shape (batch, channel+2, x_dim, y_dim)
        """
        B, _, x_dim, y_dim = x.size()

        # coord calculate
        xx_channel = torch.arange(x_dim).repeat(B, 1, y_dim, 1).type_as(x)
        yy_cahnnel = torch.arange(y_dim).repeat(B, 1, x_dim, 1).permute(0, 1, 3, 2).type_as(x)
        # normalization
        xx_channel = xx_channel.float() / (x_dim-1)
        yy_cahnnel = yy_cahnnel.float() / (y_dim-1)
        xx_channel = xx_channel * 2 - 1
        yy_cahnnel = yy_cahnnel * 2 - 1

        ret = torch.cat([x, xx_channel, yy_cahnnel], dim=1)

        if self.with_r:
            rr = torch.sqrt(xx_channel ** 2 + yy_cahnnel ** 2)
            ret = torch.cat([ret, rr], dim=1)

        return ret

class CoordConv(nn.Module):
    """
    CoordConv operation
    """
    def __init__(self, input_nc, output_nc, with_r=False, use_spect=False, **kwargs):
        super(CoordConv, self).__init__()
        self.addcoords = AddCoords(with_r=with_r)
        input_nc = input_nc + 2
        if with_r:
            input_nc = input_nc + 1
        self.conv = spectral_norm(nn.Conv2d(input_nc, output_nc, **kwargs), use_spect)

    def forward(self, x):
        ret = self.addcoords(x)
        ret = self.conv(ret)

        return ret

class EncoderBlock(nn.Module):
    def __init__(self, input_nc, output_nc, norm_layer=nn.BatchNorm2d, nonlinearity=nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(EncoderBlock, self).__init__()

        kwargs_down = {'kernel_size': 4, 'stride': 2, 'padding': 1}
        kwargs_fine = {'kernel_size': 3, 'stride': 1, 'padding': 1}

        conv1 = coord_conv(input_nc,  output_nc, use_spect, use_coord, **kwargs_down)
        conv2 = coord_conv(output_nc, output_nc, use_spect, use_coord, **kwargs_fine)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(conv1, nonlinearity, nonlinearity, conv2)
        else:
            self.model = nn.Sequential(norm_layer(input_nc),  nonlinearity, conv1,
                                       norm_layer(output_nc), nonlinearity, conv2)

    def forward(self, x):
        out = self.model(x)
        return out

class NormalBlock(nn.Module):
    def __init__(self, input_nc, output_nc, norm_layer=nn.BatchNorm2d, nonlinearity=nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(NormalBlock, self).__init__()

        kwargs_fine = {'kernel_size': 3, 'stride': 1, 'padding': 1}

        conv = coord_conv(input_nc, output_nc, use_spect, use_coord, **kwargs_fine)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(nonlinearity, conv)
        else:
            self.model = nn.Sequential(norm_layer(input_nc),  nonlinearity, conv)

    def forward(self, x):
        out = self.model(x)
        return out

class ResBlock(nn.Module):
    """
    Define an Residual block for different types
    """
    def __init__(self, input_nc, output_nc=None, hidden_nc=None, norm_layer=nn.BatchNorm2d, nonlinearity= nn.LeakyReLU(),
                learnable_shortcut=False, use_spect=False, use_coord=False):
        super(ResBlock, self).__init__()

        hidden_nc = input_nc if hidden_nc is None else hidden_nc
        output_nc = input_nc if output_nc is None else output_nc
        self.learnable_shortcut = True if input_nc != output_nc else learnable_shortcut

        kwargs = {'kernel_size': 3, 'stride': 1, 'padding': 1}
        kwargs_short = {'kernel_size': 1, 'stride': 1, 'padding': 0}

        conv1 = coord_conv(input_nc, hidden_nc, use_spect, use_coord, **kwargs)
        conv2 = coord_conv(hidden_nc, output_nc, use_spect, use_coord, **kwargs)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(nonlinearity, conv1, nonlinearity, conv2,)
        else:
            self.model = nn.Sequential(norm_layer(input_nc), nonlinearity, conv1,
                                       norm_layer(hidden_nc), nonlinearity, conv2,)

        if self.learnable_shortcut:
            bypass = coord_conv(input_nc, output_nc, use_spect, use_coord, **kwargs_short)
            self.shortcut = nn.Sequential(bypass,)

    def forward(self, x):
        if self.learnable_shortcut:
            out = self.model(x) + self.shortcut(x)
        else:
            out = self.model(x) + x
        return out

class ResBlocks(nn.Module):
    """docstring for ResBlocks"""
    def __init__(self, num_blocks, input_nc, output_nc=None, hidden_nc=None, norm_layer=nn.BatchNorm2d,
                 nonlinearity= nn.LeakyReLU(), learnable_shortcut=False, use_spect=False, use_coord=False):
        super(ResBlocks, self).__init__()
        hidden_nc = input_nc if hidden_nc is None else hidden_nc
        output_nc = input_nc if output_nc is None else output_nc

        self.model=[]
        if num_blocks == 1:
            self.model += [ResBlock(input_nc, output_nc, hidden_nc,
                           norm_layer, nonlinearity, learnable_shortcut, use_spect, use_coord)]

        else:
            self.model += [ResBlock(input_nc, hidden_nc, hidden_nc,
                           norm_layer, nonlinearity, learnable_shortcut, use_spect, use_coord)]
            for i in range(num_blocks-2):
                self.model += [ResBlock(hidden_nc, hidden_nc, hidden_nc,
                               norm_layer, nonlinearity, learnable_shortcut, use_spect, use_coord)]
            self.model += [ResBlock(hidden_nc, output_nc, hidden_nc,
                            norm_layer, nonlinearity, learnable_shortcut, use_spect, use_coord)]

        self.model = nn.Sequential(*self.model)

    def forward(self, inputs):
        return self.model(inputs)

class ResBlockDecoder(nn.Module):
    """
    Define a decoder block
    """
    def __init__(self, input_nc, output_nc, hidden_nc=None, norm_layer=nn.BatchNorm2d, nonlinearity= nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(ResBlockDecoder, self).__init__()

        hidden_nc = input_nc if hidden_nc is None else hidden_nc

        conv1 = spectral_norm(nn.Conv2d(input_nc, hidden_nc, kernel_size=3, stride=1, padding=1), use_spect)
        conv2 = spectral_norm(nn.ConvTranspose2d(hidden_nc, output_nc, kernel_size=3, stride=2, padding=1, output_padding=1), use_spect)
        bypass = spectral_norm(nn.ConvTranspose2d(input_nc, output_nc, kernel_size=3, stride=2, padding=1, output_padding=1), use_spect)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(nonlinearity, conv1, nonlinearity, conv2,)
        else:
            self.model = nn.Sequential(norm_layer(input_nc), nonlinearity, conv1, norm_layer(hidden_nc), nonlinearity, conv2,)

        self.shortcut = nn.Sequential(bypass)

    def forward(self, x):
        out = self.model(x) + self.shortcut(x)
        return out

class ResBlockEncoder(nn.Module):
    """
    Define a decoder block
    """
    def __init__(self, input_nc, output_nc, hidden_nc=None, norm_layer=nn.BatchNorm2d, nonlinearity= nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(ResBlockEncoder, self).__init__()

        hidden_nc = input_nc if hidden_nc is None else hidden_nc

        conv1 = spectral_norm(nn.Conv2d(input_nc, hidden_nc, kernel_size=3, stride=1, padding=1), use_spect)
        conv2 = spectral_norm(nn.Conv2d(hidden_nc, output_nc, kernel_size=4, stride=2, padding=1), use_spect)
        bypass = spectral_norm(nn.Conv2d(input_nc, output_nc, kernel_size=1, stride=1, padding=0), use_spect)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(nonlinearity, conv1, nonlinearity, conv2,)
        else:
            self.model = nn.Sequential(norm_layer(input_nc), nonlinearity, conv1,
                                       norm_layer(hidden_nc), nonlinearity, conv2,)
        self.shortcut = nn.Sequential(nn.AvgPool2d(kernel_size=2, stride=2),bypass)

    def forward(self, x):
        out = self.model(x) + self.shortcut(x)
        return out

class Output(nn.Module):
    """
    Define the output layer
    """
    def __init__(self, input_nc, output_nc, kernel_size = 3, norm_layer=nn.BatchNorm2d, nonlinearity= nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(Output, self).__init__()

        kwargs = {'kernel_size': kernel_size, 'padding':0, 'bias': True}

        self.conv1 = coord_conv(input_nc, output_nc, use_spect, use_coord, **kwargs)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(nonlinearity, nn.ReflectionPad2d(int(kernel_size/2)), self.conv1, nn.Tanh())
        else:
            self.model = nn.Sequential(norm_layer(input_nc), nonlinearity, nn.ReflectionPad2d(int(kernel_size / 2)), self.conv1, nn.Tanh())

    def forward(self, x):
        out = self.model(x)

        return out

class Jump(nn.Module):
    """
    Define the output layer
    """
    def __init__(self, input_nc, output_nc, kernel_size = 3, norm_layer=nn.BatchNorm2d, nonlinearity= nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(Jump, self).__init__()

        kwargs = {'kernel_size': kernel_size, 'padding':0, 'bias': True}

        self.conv1 = coord_conv(input_nc, output_nc, use_spect, use_coord, **kwargs)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(nonlinearity, nn.ReflectionPad2d(int(kernel_size/2)), self.conv1)
        else:
            self.model = nn.Sequential(norm_layer(input_nc), nonlinearity, nn.ReflectionPad2d(int(kernel_size / 2)), self.conv1)

    def forward(self, x):
        out = self.model(x)
        return out

class LinearBlock(nn.Module):
    def __init__(self, input_nc, output_nc, norm_layer=nn.BatchNorm2d,  nonlinearity= nn.LeakyReLU(),
                 use_spect=False):
        super(LinearBlock, self).__init__()
        use_bias = True

        self.fc = spectral_norm(nn.Linear(input_nc, output_nc, bias=use_bias), use_spect)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(nonlinearity, self.fc)
        else:
            self.model = nn.Sequential(norm_layer(input_nc), nonlinearity, self.fc)

    def forward(self, x):
        out = self.model(x)
        return out

def extract_image_patches(images, ksizes, strides, rates, padding='same'):
    """
    Extract patches from images and put them in the C output dimension.
    :param padding:
    :param images: [batch, channels, in_rows, in_cols]. A 4-D Tensor with shape
    :param ksizes: [ksize_rows, ksize_cols]. The size of the sliding window for
     each dimension of images
    :param strides: [stride_rows, stride_cols]
    :param rates: [dilation_rows, dilation_cols]
    :return: A Tensor
    """
    assert len(images.size()) == 4
    assert padding in ['same', 'valid']

    if padding == 'same':
        images = same_padding(images, ksizes, strides, rates)
    elif padding == 'valid':
        pass
    else:
        raise NotImplementedError('Unsupported padding type: {}.\
                Only "same" or "valid" are supported.'.format(padding))

    unfold = torch.nn.Unfold(kernel_size=ksizes,
                             dilation=rates,
                             padding=0,
                             stride=strides)
    patches = unfold(images)
    return patches  # [N, C*k*k, L], L is the total number of such blocks

def reduce_mean(x, axis=None, keepdim=False):
    if not axis:
        axis = range(len(x.shape))
    for i in sorted(axis, reverse=True):
        x = torch.mean(x, dim=i, keepdim=keepdim)
    return x

def reduce_sum(x, axis=None, keepdim=False):
    if not axis:
        axis = range(len(x.shape))
    for i in sorted(axis, reverse=True):
        x = torch.sum(x, dim=i, keepdim=keepdim)
    return x

def same_padding(images, ksizes, strides, rates):
    assert len(images.size()) == 4
    batch_size, channel, rows, cols = images.size()
    out_rows = (rows + strides[0] - 1) // strides[0]
    out_cols = (cols + strides[1] - 1) // strides[1]
    effective_k_row = (ksizes[0] - 1) * rates[0] + 1
    effective_k_col = (ksizes[1] - 1) * rates[1] + 1
    padding_rows = max(0, (out_rows-1)*strides[0]+effective_k_row-rows)
    padding_cols = max(0, (out_cols-1)*strides[1]+effective_k_col-cols)
    # Pad the input
    padding_top = int(padding_rows / 2.)
    padding_left = int(padding_cols / 2.)
    padding_bottom = padding_rows - padding_top
    padding_right = padding_cols - padding_left
    paddings = (padding_left, padding_right, padding_top, padding_bottom)
    images = torch.nn.ZeroPad2d(paddings)(images)
    return images

class TCAM(nn.Module):
    def __init__(self, ksize=3, stride=1, rate=2, in_channel=512):
        super(TCAM, self).__init__()
        self.ksize = ksize
        self.stride = stride
        self.rate = rate
        self.get_mask = nn.Sequential(nn.Conv2d(in_channel, 1, kernel_size=3, stride=1, padding=1, bias=True),
                                          nn.Sigmoid())

    def forward(self, f_ct, f_cs, f_xs, test=False):
        raw_int_f_ct = list(f_ct.size())  # b*c*h*w
        raw_int_f_cs = list(f_cs.size())  # b*c*h*w

        kernel = 2 * self.rate

        phi_s = extract_image_patches(f_xs, ksizes=[kernel, kernel],
                                      strides=[self.rate * self.stride,
                                               self.rate * self.stride],
                                      rates=[1, 1],
                                      padding='same')  # [N, C*k*k, L]
        phi_s = phi_s.view(raw_int_f_cs[0], raw_int_f_cs[1], kernel, kernel, -1)
        phi_s = phi_s.permute(0, 4, 1, 2, 3)  # raw_shape: [N, L, C, k, k]
        phi_s_groups = torch.split(phi_s, 1, dim=0)

        f_ct = F.interpolate(f_ct, scale_factor=1. / self.rate, mode='nearest')
        f_cs = F.interpolate(f_cs, scale_factor=1. / self.rate, mode='nearest')
        int_f_ct = list(f_ct.size())
        int_f_cs = list(f_cs.size())
        f_ct_groups = torch.split(f_ct, 1, dim=0)

        omiga_s = extract_image_patches(f_cs, ksizes=[self.ksize, self.ksize],
                                  strides=[self.stride, self.stride],
                                  rates=[1, 1],
                                  padding='same')

        omiga_s = omiga_s.view(int_f_cs[0], int_f_cs[1], self.ksize, self.ksize, -1)
        omiga_s = omiga_s.permute(0, 4, 1, 2, 3)
        omiga_s_groups = torch.split(omiga_s, 1, dim=0)

        f_xt = []
        offsets = []

        for f_ct_i, omiga_s_i, phi_s_i in zip(f_ct_groups, omiga_s_groups, phi_s_groups):
            escape_NaN = torch.FloatTensor([1e-4]).cuda()
            omiga_s_i = omiga_s_i[0]
            max_omiga_s_i = torch.max(torch.sqrt(reduce_sum(torch.pow(omiga_s_i, 2),
                                                     axis=[1, 2, 3],
                                                     keepdim=True)),
                               escape_NaN)
            omiga_s_i_normed = omiga_s_i / max_omiga_s_i

            f_ct_i = same_padding(f_ct_i, [self.ksize, self.ksize], [1, 1], [1, 1])

            f_xt_i = F.conv2d(f_ct_i, omiga_s_i_normed, stride=1)

            f_xt_i = f_xt_i.view(1, int_f_cs[2] * int_f_cs[3], int_f_ct[2], int_f_ct[3])

            f_xt_i = F.softmax(f_xt_i * 10, dim=1)

            if not test:
                offset = torch.argmax(f_xt_i, dim=1, keepdim=True)

                offset = torch.cat([offset % int_f_ct[3], offset // int_f_ct[3]], dim=1)

            # deconv for patch pasting
            omiga_s_i_center = phi_s_i[0]
            if self.rate == 1:
                f_xt_i = F.pad(f_xt_i, [0, 1, 0, 1])
            f_xt_i = F.conv_transpose2d(f_xt_i, omiga_s_i_center, stride=self.rate, padding=1) / 4.
            f_xt.append(f_xt_i)
            if not test:
                offsets.append(offset)

        f_xt = torch.cat(f_xt, dim=0)

        f_xt.contiguous().view(raw_int_f_ct)
        mask = self.get_mask(f_xt)

        flow = None
        grids = None
        if not test:
            offsets = torch.cat(offsets, dim=0)
            offsets = offsets.view(int_f_ct[0], 2, *int_f_ct[2:])

            grids = offsets.type(torch.FloatTensor)
            grids = grids.cuda()

            h_add = torch.arange(int_f_ct[2]).view([1, 1, int_f_ct[2], 1]).expand(int_f_ct[0], -1, -1, int_f_ct[3])
            w_add = torch.arange(int_f_ct[3]).view([1, 1, 1, int_f_ct[3]]).expand(int_f_ct[0], -1, int_f_ct[2], -1)
            ref_coordinate = torch.cat([h_add, w_add], dim=1)
            ref_coordinate = ref_coordinate.cuda()

            offsets = offsets - ref_coordinate
            flow = offsets.type(torch.FloatTensor).cuda()

        return f_xt, mask, flow, grids

class ResBlockEncoderOptimized(nn.Module):
    """
    Define an Encoder block for the first layer of the discriminator
    """
    def __init__(self, input_nc, output_nc, hidden_nc=None, norm_layer=nn.BatchNorm2d, nonlinearity= nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(ResBlockEncoderOptimized, self).__init__()

        hidden_nc = input_nc if hidden_nc is None else hidden_nc

        conv1 = spectral_norm(nn.Conv2d(input_nc, hidden_nc, kernel_size=3, stride=1, padding=1), use_spect)
        conv2 = spectral_norm(nn.Conv2d(hidden_nc, output_nc, kernel_size=4, stride=2, padding=1), use_spect)
        bypass = spectral_norm(nn.Conv2d(input_nc, output_nc, kernel_size=1, stride=1, padding=0), use_spect)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(conv1, nonlinearity, conv2,)
        else:
            self.model = nn.Sequential(conv1, norm_layer(hidden_nc), nonlinearity, conv2,)
        self.shortcut = nn.Sequential(nn.AvgPool2d(kernel_size=2, stride=2), bypass)

    def forward(self, x):
        out = self.model(x) + self.shortcut(x)
        return out

class EncoderBlockOptimized(nn.Module):
    """
        Define an Encoder block for the first layer of the generator
    """
    def __init__(self, input_nc, output_nc, norm_layer=nn.BatchNorm2d, nonlinearity=nn.LeakyReLU(),
                 use_spect=False, use_coord=False):
        super(EncoderBlockOptimized, self).__init__()

        kwargs_down = {'kernel_size': 4, 'stride': 2, 'padding': 1}
        kwargs_fine = {'kernel_size': 3, 'stride': 1, 'padding': 1}

        conv1 = coord_conv(input_nc,  output_nc, use_spect, use_coord, **kwargs_down)
        conv2 = coord_conv(output_nc, output_nc, use_spect, use_coord, **kwargs_fine)

        if type(norm_layer) == type(None):
            self.model = nn.Sequential(conv1, nonlinearity, conv2)
        else:
            self.model = nn.Sequential(conv1, norm_layer(output_nc), nonlinearity, conv2)

    def forward(self, x):
        out = self.model(x)
        return out


class Label():
    '''
    According to Boost documentation labels in SPPRC stores:

        -Resident vertex
        -Predecessor Arc over which it has been extended
        -Predecessor label
        -Current Vector of resource values
    '''
    def __init__(self, resVert=None,predEdge=None,predLabel=None,
                  resVector=None, labelID=0):
        self.residentVertex = resVert
        self.predEdge = predEdge
        self.predLabel = predLabel
        self.resourceVector = resVector

        self.isDominated = False
        self.isProcessed = False
        self.isFeasible = True
        self.id = labelID

    def __eq__(self,other):
        if isinstance(other, Label):
            return ((self.resourceVector == other.resourceVector)
                    and (self.id == other.id))

    def __ne__(self,other):
        if isinstance(other, Label):
            for first,second in zip(self.resourceVector, other.resourceVector):
                if second != first:
                    return True
            return (self.id != other.id)

    def __lt__(self, other):
        if isinstance(other, Label):
            for first,second in zip(self.resourceVector, other.resourceVector):
                if second <= first:
                    return False
            return True

    def __le__(self,other):
        if isinstance(other, Label):
            for first,second in zip(self.resourceVector, other.resourceVector):
                if second < first :
                    return False
            return True

    def __gt__(self, other):
        if isinstance(other, Label):
            for first,second in zip(self.resourceVector, other.resourceVector):
                if second >= first :
                    return False
            return True

    def __ge__(self,other):
        if isinstance(other, Label):
            for first,second in zip(self.resourceVector, other.resourceVector):
                if second > first :
                    return False
            return True


from django.shortcuts import render, get_object_or_404
from shop.models import Category, Product
from shop.products_tracking import RecentlyVisitedProducts
from cart.forms import CartAddProductForm

def product_list(request, category_slug=None):
    category = None
    products = Product.objects.filter(is_available=True)
    categories = Category.objects.all()
    cart_add_product_form = CartAddProductForm()
    if category_slug:
        category = get_object_or_404(Category, slug=category_slug)
        products = Product.objects.filter(categories=category)
    ctx = {
        'category': category,
        'categories': categories,
        'products': products,
        'cart_add_product_form': cart_add_product_form,
    }
    return render(request, 'shop/products/list.html', ctx)

def product_detail(request, id, slug):
    product = get_object_or_404(Product, id=id, slug=slug)
    recently_visted_products = RecentlyVisitedProducts(request)
    recently_visted_products.add(product.id)
    cart_add_product_form = CartAddProductForm()
    ctx = {'product': product, 'cart_add_product_form': cart_add_product_form,
           'recently_visted_products': recently_visted_products}
    return render(request, 'shop/products/detail.html', ctx)


import os

import numpy as np
from pandas.io.parsers import read_csv
from sklearn.utils import shuffle
from utils import reshape2d_by_image_dim_ordering
from keras import backend as K

FTRAIN = 'data/training.csv'
FTEST = 'data/test.csv'

def load(test=False, cols=None):
    """testがTrueの場合はFTESTからデータを読み込み、Falseの場合はFTRAINから読み込みます。
    colsにリストが渡された場合にはそのカラムに関するデータのみ返します。
    """

    fname = FTEST if test else FTRAIN
    df = read_csv(os.path.expanduser(fname)) # pandasのdataframeを使用

    # スペースで句切られているピクセル値をnumpy arrayに変換
    df['Image'] = df['Image'].apply(lambda im: np.fromstring(im, sep=' '))

    if cols:  # カラムに関連するデータのみを抽出
        df = df[list(cols) + ['Image']]

    print(df.count())  # カラム毎に値が存在する行数を出力
    df = df.dropna()  # データが欠けている行は捨てる

    X = np.vstack(df['Image'].values) / 255.  # 0から1の値に変換
    X = X.astype(np.float32)

    if not test:  # ラベルが存在するのはFTRAINのみ
        y = df[df.columns[:-1]].values
        y = (y - 48) / 48  # -1から1の値に変換
        X, y = shuffle(X, y, random_state=42)  # データをシャッフル
        y = y.astype(np.float32)
    else:
        y = None

    return X, y

def load2d(test=False, cols=None):
    X, y = load(test, cols)
    # image_dim_orderring に合わせて2D画像のshapeを変える
    X, _ = reshape2d_by_image_dim_ordering(X)
    return X, y

if __name__ == '__main__':
    X, y = load()
    print("X.shape == {}; X.min == {:.3f}; X.max == {:.3f}".format(
        X.shape, X.min(), X.max()))
    print("y.shape == {}; y.min == {:.3f}; y.max == {:.3f}".format(
        y.shape, y.min(), y.max()))


import unittest
from transform import format_yahooquery_dataclass
from tests.test_data.test_data_reader import get_yahooquery_test_data_as_ticker

class TransformTest(unittest.TestCase):

    def test_format_yahooquery_dataclass(self):
        test_data = get_yahooquery_test_data_as_ticker()
        expected_years = [2019, 2020, 2021]
        results = format_yahooquery_dataclass(test_data, test_data.symbols[0], 'Microsoft')

        for date in results.income_statement:
            self.assertTrue(date.year in expected_years)

        for date in results.balance_sheet:
            self.assertTrue(date.year in expected_years)

        for date in results.cash_flow:
            self.assertTrue(date.year in expected_years)


#Import necessary libraries -- obtain adafruit lib from repository or website
import time
import usb_hid
from adafruit_hid.keycode import Keycode
from adafruit_hid.keyboard import Keyboard
import board
import digitalio

#Map GPIO Pins to keycaps based on wiring configuration
btn1_pin = board.GP9
btn2_pin = board.GP8
btn3_pin = board.GP7
btn4_pin = board.GP19
btn5_pin = board.GP20
btn6_pin = board.GP21

#Configure buttons
btn1 = digitalio.DigitalInOut(btn1_pin)
btn1.direction = digitalio.Direction.INPUT
btn1.pull = digitalio.Pull.DOWN

btn2 = digitalio.DigitalInOut(btn2_pin)
btn2.direction = digitalio.Direction.INPUT
btn2.pull = digitalio.Pull.DOWN

btn3 = digitalio.DigitalInOut(btn3_pin)
btn3.direction = digitalio.Direction.INPUT
btn3.pull = digitalio.Pull.DOWN

btn4 = digitalio.DigitalInOut(btn4_pin)
btn4.direction = digitalio.Direction.INPUT
btn4.pull = digitalio.Pull.DOWN

btn5 = digitalio.DigitalInOut(btn5_pin)
btn5.direction = digitalio.Direction.INPUT
btn5.pull = digitalio.Pull.DOWN

btn6 = digitalio.DigitalInOut(btn6_pin)
btn6.direction = digitalio.Direction.INPUT
btn6.pull = digitalio.Pull.DOWN

#Init keyboard
keyboard = Keyboard(usb_hid.devices)

#Define keycap sequences to combine based on datasheet layout
#These are based on the client request, you can make these do anything you want
def copy():
    keyboard.send(Keycode.CONTROL, Keycode.F7)

def paste():
    keyboard.send(Keycode.CONTROL, Keycode.F8)

def screenshot():
    keyboard.send(Keycode.CONTROL, Keycode.F9)

def save():
    keyboard.send(Keycode.CONTROL, Keycode.F10)

def power_control():
    keyboard.send(Keycode.CONTROL, Keycode.F11)

#Execute client's requested commands based on button pressed
#Call corresponding command and map it to button of choice
while True:
    if btn1.value:
        copy()
        time.sleep(0.1)

    if btn2.value:
        paste()
        time.sleep(0.1)

    if btn3.value:
        screenshot()
        time.sleep(0.1)

    if btn4.value:
        save()
        time.sleep(0.1)

    if btn5.value:
        power_control()
        time.sleep(0.1)

    if btn6.value:
        keyboard.send(Keycode.CONTROL, Keycode.F12)
        time.sleep(0.1)
    time.sleep(0.1)


"""
@Time : 2021/6/5 16:40
@Author : 远秋
@Email : gongyuanqiu@163.com
@File : public.py
@Project : AppUIAutomation
@feature :
@实现步骤：
"""
def getSize(driver):
    x = driver.get_window_size()['width']
    y = driver.get_window_size()['height']
    return (x, y)

#上划
def swipeUp(driver,t=1000):
    size = getSize(driver)
    x1 = int(size[0] * 0.5) #x 坐标
    y1 = int(size[1] * 0.75) #起始 y 坐标
    y2 = int(size[1] * 0.25) #终点 y 坐标
    driver.swipe(x1, y1, x1, y2,duration=t)

#下滑
def swipeDown(driver,t=1000):
    size = getSize(driver)
    x1 = int(size[0] * 0.5) #x 坐标
    y1 = int(size[1] * 0.25) #起始 y 坐标
    y2 = int(size[1] * 0.75) #终点 y 坐标
    driver.swipe(x1, y1, x1, y2,duration=t)

#屏幕向左滑动
def swipLeft(driver,t=1000):
    size = getSize(driver)
    x1=int(size[0]*0.75)
    y1=int(size[1]*0.5)
    x2=int(size[0]*0.05)
    driver.swipe(x1,y1,x2,y1,duration=t)

#屏幕向右滑动
def swipRight(driver,t=1000):
    size = getSize(driver)
    x1=int(size[0]*0.05)
    y1=int(size[1]*0.5)
    x2=int(size[0]*0.75)
    driver.swipe(x1,y1,x2,y1,duration=t)


from QgramIndexProcess import QgramIndex
from  invertedIndexProcess import invertedIndex
from IntersectionProcess import Intersection
from RankerProcess import Ranker

# basic index
BSC_IX = invertedIndex()
# query index
QUR_IX = invertedIndex()

# basic Q-gram index
BSC_QG_IX = QgramIndex(3)
# query Q-gram index
QUR_QG_IX = QgramIndex(3)

BSC_IX.readFromFile("dataset/andQueryMatch.txt")
QUR_IX.readFromFile("dataset/query.txt")

BSC_IX.presentKeywordsIndex()
print("----------------------------------")

QUR_IX.presentKeywordsIndex()
print("----------------------------------")

BSC_QG_IX.buildIndex(BSC_IX)

# foreach term in Query create n-grams

for term in QUR_IX.inverted_lists.keys():
    print("==> ( ", term, " ) " )
    ngrams = BSC_QG_IX.Ngrams(term)

    print('--------- Matches ---------')
    BSC_QG_IX.QgramMatches(ngrams)

    if (not len(BSC_QG_IX.grams_matche.items())):
        continue;

    BSC_QG_IX.QgramFrequence()

    for word_id, freq in BSC_QG_IX.gram_frequences:
        print(f"{BSC_QG_IX.word_ids[word_id]} => {freq}")

    print("----------------TOP K------------------")

    result = BSC_QG_IX.Top_k_words(BSC_IX, 3)

    for word, ids in result.items():
        print(f"{word}=>{ids}")

    print("----------------After Edit Distance------------------")
    result = BSC_QG_IX.matchTermCadediates(result,term)
    print(result)
    BSC_QG_IX.proposalTermsArr.append(result)

    print("----------------- End -----------------")
    print("---------------------------------------")

#BSC_QG_IX.presentIndex()
#print("----------------------------------")
#BSC_QG_IX.presentWords()

max_row = len(BSC_QG_IX.proposalTermsArr)

print("-------Prposal Array --------- ")
print( BSC_QG_IX.proposalTermsArr )

intersecProc = Intersection()
queryResult = set()
BSC_QG_IX.max_propTermArr = len(BSC_QG_IX.proposalTermsArr)
ranker = Ranker(BSC_IX)
BSC_QG_IX.permutation([] , [], 0 ,  intersecProc , ranker )

print('Proposal Docs for Query ')
print(BSC_QG_IX.combinedPropDocs)


import numpy as np
import pandas as pd
import seaborn as sns

data= pd.read_csv("C:\\Users\\manish\\Desktop\\Datasets\\07_Udemy_Courses.csv")
data.info()
"pd.drop(data[""])"

"1. For how must subjects udemy is giving the courses"
data["subject"].unique()

"2. Subject that has the max no.of coures"
data["subject"].max()

"3. Counts the value of each subject in series data"
data["subject"].value_counts()

"4. Show all the courses which free of cost"
Free_course=data[data.is_paid== False].count()
Free_course=data[data.is_paid== True]
data[data.is_paid== True].count()

"5. Course Wih most subscriber"
data.sort_values(by= ['num_subscribers'], ascending = False).head()

"6. Course Wih most subscriber"
data.sort_values(by= ['num_subscribers']).head()

"7. Graphic Designs course with price below 100rs "
len(data[(data.subject=="Graphic Design") & (data.price<="100")].count())

"8. List All the courses related to python"
len(data[data.course_title.str.contains("Python")])

"9. Coures that published in 2015"
data.dtypes
data['published_timestamp']=pd.to_datetime(data['published_timestamp'])
data.dtypes
data['years']=data['published_timestamp'].dt.year
len(data[data.years==2015])

"10. max no. of subscriberes in each level of course"
data.groupby('level')["num_subscribers"].max()
data.groupby('level')["num_subscribers"].min()


import os
import shutil
from bot import EMILIA
from .nhentai import _download, nhentai_data
from .mal import data_from_id
from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, InputMediaDocument

@EMILIA.on_callback_query()
async def _callback(client, CallbackQuery):
    query = CallbackQuery.data.split()
    try:
        if query[0] == "anime":
            text, mal_url, trailer = data_from_id(query[0], query[-1])
            if trailer:
                buttons = [
                            [InlineKeyboardButton("More Info!", url = mal_url), InlineKeyboardButton("Watch Trailer!", url = trailer)]
                        ]
            else:
                buttons = [
                            [InlineKeyboardButton("More Info!", url = mal_url)]
                        ]
            await CallbackQuery.edit_message_text(text = text, reply_markup = InlineKeyboardMarkup(buttons))
        elif query[0] == "manga" or query[0] == "char":
            text, mal_url = data_from_id(query[0], query[-1])
            buttons = [
                        [InlineKeyboardButton("More Info!", url = mal_url)]
                    ]
            await CallbackQuery.edit_message_text(text = text, reply_markup = InlineKeyboardMarkup(buttons))

        elif query[0] == "download":
            title, num_pages, artist, lang, tags, page_links = await nhentai_data(query[-1])
            cwd = os.getcwd()
            new_dir = query[-1]
            dl_path = os.path.join(cwd, new_dir)
            os.mkdir(dl_path)
            outfile_path = os.path.join(dl_path, f"{new_dir}.pdf")
            await _download(query[-1], dl_path, outfile_path)
            print()
            text = f"**{title}**\n\n**Language:** {', '.join(lang)}\n**Artist:** {', '.join(artist)}\n**Pages:** {num_pages}\n\n**Tags:** {', '.join(tags)}"
            await CallbackQuery.edit_message_media(InputMediaDocument(outfile_path, caption = text))
            shutil.rmtree(dl_path)
    except Exception as e:
        await CallbackQuery.edit_message_text(text = f"**Error:**\n{e}")


def missingElement(my_arr,n):
    exp_sum = num_of_ele *(num_of_ele+1) /2
    actual_sum =sum(my_arr)

    return exp_sum - actual_sum

if __name__ == "__main__":

    my_arr = []

    num_of_ele = int(input("Enter the total number of element \n"))

    for i in range(num_of_ele-1):
        array = int(input("Enter the element in the array \n"))
        my_arr.append(array)

missing_number  = missingElement(my_arr,num_of_ele)
print("The missing element in the above array is{}".format(missing_number))


# Đỉnh thắt là đỉnh mà tất cả đường đi từ u tới v đều đi qua nó

from sys import *
setrecursionlimit(pow(10, 8))
def stdin_gen():
    for x in stdin.read().split():
        yield int(x)
cin = stdin_gen()

t = next(cin)
for _ in range(t):
    n, m, u, v = next(cin), next(cin), next(cin), next(cin)
    ke, visited, dem = [[] for _ in range(n + 2)], [0] * 102, 0
    for i in range(m):
        x, y = next(cin), next(cin)
        ke[x].append(y)
    def DFS(u):
        visited[u] = 1
        for v in ke[u]:
            if not visited[v]:
                DFS(v)
    for i in range(1, n + 1):
        if i == u or i == v: continue
        visited = [0] * 102
        visited[i] = 1
        DFS(u)
        if not visited[v]: dem += 1
    print(dem)


# 导入需要的库
from sklearn.preprocessing import LabelBinarizer
from sklearn.metrics import classification_report
from keras.models import load_model
from keras.datasets import cifar10
import numpy as np
import argparse
import glob
import os

# 构建命令行参数列表
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--models", required=True, help="path to models directory")
args = vars(ap.parse_args())

# 加载数据集，然后缩放到0,1范围内
(testX, testY) = cifar10.load_data()[1]
testX = testX.astype("float") / 255.0
# 初始化数据集标签，并将Y标签从整数转为向量形式
labelNames = ["airplane", "automobile", "bird", "cat", "deer",
"dog", "frog", "horse", "ship", "truck"]
lb = LabelBinarizer()
testY = lb.fit_transform(testY)

# 收集模型路径，并初始化模型列表
modelPaths = os.path.sep.join([args["models"], "*.model"])
modelPaths = list(glob.glob(modelPaths))
models = []

# 从磁盘加载每一个模型,添加到model list列表中
for (i, modelPath) in enumerate(modelPaths):
    print("[INFO] loading model {}/{}".format(i + 1, len(modelPaths)))
    models.append(load_model(modelPath))

# 初始化预测列表
print("[INFO] evaluating ensemble...")
predictions = []

# 循环所有的模型
for model in models:
    # 使用列表中的每一个模型对testX数据集进行预测
    predictions.append(model.predict(testX, batch_size=64))

# 平均所有的模型预测结果，并做出来报告分析
predictions = np.average(predictions, axis=0)
print(classification_report(testY.argmax(axis=1),
predictions.argmax(axis=1), target_names=labelNames))


from manimlib.imports import *

class Helix(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes() # creates a 3D Axis

        helix1=ParametricFunction(
                lambda u : np.array([
                1.5*np.cos(u),
                1.5*np.sin(u),
                u/4
            ]),color=PURPLE,t_min=-TAU,t_max=TAU,
            )

        helix2=ParametricFunction(
                lambda u : np.array([
                2*np.cos(u),
                2*np.sin(u),
                u/2
            ]),color=GREEN_C,t_min=-TAU,t_max=TAU,
            )

        function = TexMobject("f(", "r", ",", "\\theta", ")", "=", "{ \\begin{bmatrix} r \\cos\\theta \\\ r \\sin \\theta \\\ h \\theta \\end{bmatrix}}" ).scale(0.6).to_corner(UL)

        function.set_color_by_tex(r"\theta", BLUE_C)
        function.set_color_by_tex(r"r", RED_C)
        function.set_color_by_tex(r"\cos", GREEN_C)
        function.set_color_by_tex(r"\sin", YELLOW_C)
        function[0].set_color(ORANGE)
        function[4].set_color(ORANGE)

        slope_text = TextMobject(r"$\theta = $").move_to(3*UP+3*RIGHT)
        number = DecimalNumber(0,unit=r" rad", color=RED_C).next_to(slope_text, RIGHT)

        self.add_fixed_in_frame_mobjects(function,slope_text, number)

        self.set_camera_orientation(phi=60*DEGREES, theta = 45*DEGREES)

        self.add(axes)

        axis = TextMobject(r"X",r"Y",r"Z")
        axis[0].move_to(6*RIGHT)
        axis[1].move_to(6*UP)
        axis[2].move_to(np.array([0,0,3.7]))

        self.add_fixed_orientation_mobjects(axis[2])
        self.add_fixed_orientation_mobjects(axis[0])
        self.add_fixed_orientation_mobjects(axis[1])

        dot1 = Dot().rotate(PI/2).set_color(RED_C)
        alpha1 = ValueTracker(0)
        vector1 = self.get_vector(alpha1.get_value(),helix1)
        dot1.add_updater(lambda m: m.move_to(vector1.get_end()))
        number.add_updater(lambda m: m.set_value(alpha1.get_value()*4*np.pi))
        number.add_updater(lambda m: self.add_fixed_in_frame_mobjects(m))

        self.play(
            ShowCreation(helix1),
            GrowFromCenter(dot1),
            GrowArrow(vector1)

            )

        vector1.add_updater(
            lambda m: m.become(
                    self.get_vector(alpha1.get_value()%1,helix1)
                )
            )

        self.add(vector1,dot1)

        self.play(alpha1.increment_value, 1, run_time=10, rate_func=linear)

        self.play(FadeOut(vector1), FadeOut(dot1), FadeOut(number))

        self.move_camera(phi=0* DEGREES,theta=90*DEGREES)

        alpha1 = ValueTracker(0)

        self.add(vector1,dot1)

        self.play(alpha1.increment_value, 1, run_time=10, rate_func=linear)

    def get_vector(self, proportion, curve):
        vector = Line(np.array([0,0,0]), curve.point_from_proportion(proportion), color = YELLOW_C, buff=0)
        return vector


import typing

import copy
import itertools

import numpy as np
import pandas as pd
import scipy.stats
import sklearn.ensemble

class AbstractSelector:
    def fit(
        self,
        X: pd.DataFrame,
        y: pd.DataFrame,
        minima: typing.Dict[int, typing.Dict[str, float]],
        maxima: typing.Dict[int, typing.Dict[str, float]],
    ) -> None:
        raise NotImplementedError()

    def predict(
        self, X: pd.DataFrame, y: typing.Optional[pd.DataFrame] = None
    ) -> pd.DataFrame:
        prediction = self._predict(X, y)
        for col, series in prediction.iteritems():
            assert series.dtype == float, (col, series)
        np.testing.assert_array_almost_equal(
            prediction.sum(axis="columns").to_numpy(),
            np.ones(X.shape[0]),
            err_msg=prediction.to_csv(),
        )
        return prediction

    def _predict(
        self, X: pd.DataFrame, y: typing.Optional[pd.DataFrame]
    ) -> pd.DataFrame:
        raise NotImplementedError()

class OneVSOneSelector(AbstractSelector):
    def __init__(self, configuration, random_state, tie_break_order):
        self.configuration = configuration
        self.single_strategy_idx = None
        self.rng = random_state
        self.tie_break_order = tie_break_order
        self.models = None
        self.target_indices = None
        self.selectors_ = None
        self.weights_ = {}
        self.strategies_ = None

    def fit(
        self,
        X: pd.DataFrame,
        y: pd.DataFrame,
        minima: typing.Dict[int, typing.Dict[str, float]],
        maxima: typing.Dict[int, typing.Dict[str, float]],
    ) -> None:

        self.maxima = copy.deepcopy(maxima)
        target_indices = np.array(list(range(y.shape[1])))
        models = dict()
        weights = dict()

        self.strategies_ = copy.deepcopy(y.columns.to_list())
        y_pd = y.copy()
        y = y.to_numpy()

        for i in range(len(target_indices)):
            models[i] = dict()
            weights[i] = dict()
            for j in range(i + 1, len(target_indices)):

                if self.configuration["normalization"] in (
                    "all",
                    "binary",
                    "y",
                    "all1",
                    "binary1",
                ):
                    minimum2 = np.ones(len(X)) * np.inf
                    maximum2 = np.zeros(len(X))

                    if self.configuration["normalization"] in ("all", "all1"):
                        for idx, task_id in enumerate(X.index):
                            for method_id in range(len(target_indices)):
                                minimum2[idx] = np.nanmin(
                                    (
                                        minimum2[idx],
                                        minima[task_id][self.strategies_[method_id]],
                                    )
                                )
                                maximum2[idx] = np.nanmax(
                                    (
                                        maximum2[idx],
                                        maxima[task_id][self.strategies_[method_id]],
                                    )
                                )
                        if self.configuration["normalization"] == "all1":
                            maximum2 = np.ones_like(maximum2)
                    elif self.configuration["normalization"] in ("binary", "binary1"):
                        for idx, task_id in enumerate(X.index):
                            for method_id in (i, j):
                                minimum2[idx] = np.nanmin(
                                    (
                                        minimum2[idx],
                                        minima[task_id][self.strategies_[method_id]],
                                    )
                                )
                                maximum2[idx] = np.nanmax(
                                    (
                                        maximum2[idx],
                                        maxima[task_id][self.strategies_[method_id]],
                                    )
                                )
                        if self.configuration["normalization"] == "binary1":
                            maximum2 = np.ones_like(maximum2)
                    elif self.configuration["normalization"] == "y":
                        for idx, task_id in enumerate(X.index):
                            minimum2[idx] = np.nanmin(
                                (minimum2[idx], y_pd.loc[task_id].min())
                            )
                            maximum2[idx] = np.nanmax(
                                (maximum2[idx], y_pd.loc[task_id].max())
                            )
                    else:
                        raise ValueError(self.configuration["normalization"])

                    y_i_j = y[:, i] < y[:, j]
                    mask = np.isfinite(y[:, i]) & np.isfinite(y[:, j])
                    X_ = X.to_numpy()[mask]
                    y_i_j = y_i_j[mask]

                    minimum = minimum2[mask]
                    maximum = maximum2[mask]

                    diff = maximum - minimum
                    diff[diff == 0] = 1
                    normalized_y_i = (y[:, i][mask].copy() - minimum) / diff
                    normalized_y_j = (y[:, j][mask].copy() - minimum) / diff

                    weights_i_j = np.abs(normalized_y_i - normalized_y_j)

                elif self.configuration["normalization"] == "rank":
                    y_i_j = y[:, i] < y[:, j]
                    mask = np.isfinite(y[:, i]) & np.isfinite(y[:, j])
                    X_ = X.to_numpy()[mask]
                    y_i_j = y_i_j[mask]
                    ranks = scipy.stats.rankdata(y[mask], axis=1)
                    weights_i_j = np.abs(ranks[:, i] - ranks[:, j])

                elif self.configuration["normalization"] == "None":
                    y_i_j = y[:, i] < y[:, j]
                    mask = np.isfinite(y[:, i]) & np.isfinite(y[:, j])
                    X_ = X.to_numpy()[mask]
                    y_i_j = y_i_j[mask]
                    weights_i_j = np.ones_like(y_i_j).astype(int)

                else:
                    raise ValueError(self.configuration["normalization"])

                if len(y_i_j) == 0:
                    models[i][j] = None
                    weights[i][j] = None
                    continue

                if np.all([target == y_i_j[0] for target in y_i_j]):
                    n_zeros = int(np.ceil(len(y_i_j) / 2))
                    n_ones = int(np.floor(len(y_i_j) / 2))
                    import sklearn.dummy

                    base_model = sklearn.dummy.DummyClassifier(
                        strategy="constant", constant=y_i_j[0]
                    )
                    base_model.fit(
                        X_,
                        np.array(([[0]] * n_zeros) + ([[1]] * n_ones)).flatten(),
                        sample_weight=weights_i_j,
                    )
                else:
                    if self.configuration.get("max_depth") == 0:
                        import sklearn.dummy

                        loss_i = np.sum((y_i_j == 0) * weights_i_j)
                        loss_j = np.sum((y_i_j == 1) * weights_i_j)

                        base_model = sklearn.dummy.DummyClassifier(
                            strategy="constant",
                            constant=1 if loss_i < loss_j else 0,
                        )
                        base_model.fit(
                            X_,
                            np.ones_like(y_i_j) * int(loss_i < loss_j),
                            sample_weight=weights_i_j,
                        )
                    else:
                        base_model = self.fit_pairwise_model(
                            X_,
                            y_i_j,
                            weights_i_j,
                            self.rng,
                            self.configuration,
                        )
                models[i][j] = base_model
                weights[i][j] = weights_i_j
        self.models = models
        self.weights_ = weights
        self.target_indices = target_indices

    def _predict(
        self, X: pd.DataFrame, y: typing.Optional[pd.DataFrame]
    ) -> pd.DataFrame:

        if y is not None:
            raise ValueError("y must not be provided")

        raw_predictions = dict()
        raw_probas = dict()
        for i in range(len(self.target_indices)):
            for j in range(i + 1, len(self.target_indices)):
                if self.models[i][j] is not None:
                    raw_predictions[(i, j)] = self.models[i][j].predict(X)
                    raw_probas[(i, j)] = self.models[i][j].predict_proba(X)

        if len(raw_predictions) == 0:
            predictions = pd.DataFrame(
                0, index=X.index, columns=self.strategies_
            ).astype(float)
            predictions.iloc[:, self.single_strategy_idx] = 1.0
            return predictions

        predictions = {}
        for x_idx in range(X.shape[0]):
            wins = np.zeros(self.target_indices.shape)
            for i in range(len(self.target_indices)):
                for j in range(i + 1, len(self.target_indices)):
                    if (i, j) in raw_predictions:
                        if self.configuration["prediction"] == "soft":
                            if raw_probas[(i, j)].shape[1] == 1:
                                proba = raw_probas[(i, j)][x_idx][0]
                            else:
                                proba = raw_probas[(i, j)][x_idx][1]
                            wins[i] += proba
                            wins[j] += 1 - proba
                        elif self.configuration["prediction"] == "hard":
                            prediction = raw_predictions[(i, j)][x_idx]
                            if prediction == 1:
                                wins[i] += 1
                            else:
                                wins[j] += 1
                        else:
                            raise ValueError(self.configuration["prediction"])

            n_prev = np.inf
            # Tie breaking
            while True:
                most_wins = np.max(wins)
                most_wins_mask = most_wins == wins
                if n_prev == np.sum(most_wins_mask):
                    n_prev = np.sum(most_wins_mask)
                    hit = False
                    for method in self.tie_break_order:
                        if method not in self.strategies_:
                            continue
                        method_idx = self.strategies_.index(method)
                        if most_wins_mask[method_idx]:
                            wins[method_idx] += 1
                            hit = True
                            break
                    if not hit:
                        wins[
                            int(self.rng.choice(np.argwhere(most_wins_mask).flatten()))
                        ] += 1
                elif np.sum(most_wins_mask) > 1:
                    n_prev = np.sum(most_wins_mask)
                    where = np.argwhere(most_wins_mask).flatten()
                    for i, j in itertools.combinations(where, 2):
                        if (i, j) in raw_predictions:
                            prediction = raw_predictions[(i, j)][x_idx]
                            if prediction == 1:
                                wins[i] += 1
                            else:
                                wins[j] += 1
                        else:
                            method_i = self.strategies_[i]
                            method_j = self.strategies_[j]
                            if self.tie_break_order.index(
                                method_i
                            ) < self.tie_break_order.index(method_j):
                                wins[i] += 1
                            else:
                                wins[j] += 1
                else:
                    break

            wins = wins / np.sum(wins)
            predictions[X.index[x_idx]] = wins

        return_value = {
            task_id: {
                strategy: predictions[task_id][strategy_idx]
                for strategy_idx, strategy in enumerate(self.strategies_)
            }
            for task_id in X.index
        }
        return_value = pd.DataFrame(return_value).transpose().astype(float)
        return_value = return_value[self.strategies_]
        return_value = return_value.fillna(0.0)
        return return_value

    def fit_pairwise_model(self, X, y, weights, rng, configuration):
        raise NotImplementedError()

class OVORF(OneVSOneSelector):
    def __init__(self, n_estimators, **kwargs):
        super().__init__(**kwargs)
        self.n_estimators = n_estimators

    def fit_pairwise_model(self, X, y, weights, rng, configuration):
        base_model = sklearn.ensemble.RandomForestClassifier(
            random_state=rng,
            n_estimators=self.n_estimators,
            bootstrap=True if configuration["bootstrap"] == "True" else False,
            min_samples_split=configuration["min_samples_split"],
            min_samples_leaf=configuration["min_samples_leaf"],
            max_features=int(configuration["max_features"]),
            max_depth=configuration["max_depth"],
        )
        base_model.fit(X, y, sample_weight=weights)
        return base_model

class FallbackWrapper(AbstractSelector):
    def __init__(self, selector, default_strategies: typing.List[str]):
        self.selector = selector
        self.default_strategies = default_strategies

    def fit(
        self,
        X: pd.DataFrame,
        y: pd.DataFrame,
        minima: typing.Dict[int, typing.Dict[str, float]],
        maxima: typing.Dict[int, typing.Dict[str, float]],
    ) -> None:
        self.X_ = X
        self.strategies_ = y.columns
        self.return_value_ = np.array(
            [
                (len(self.strategies_) - self.default_strategies.index(strategy) - 1)
                / (len(self.strategies_) - 1)
                for strategy in self.strategies_
            ]
        )
        self.return_value_ = self.return_value_ / np.sum(self.return_value_)
        self.selector.fit(X, y, minima, maxima)

    def _predict(
        self, X: pd.DataFrame, y: typing.Optional[pd.DataFrame]
    ) -> pd.DataFrame:

        if y is not None:
            prediction = self.selector.predict(X, y)
        else:
            prediction = self.selector.predict(X)
        for task_id, x in X.iterrows():
            counter = 0
            te = x.copy()
            assert len(te) == self.X_.shape[1]
            for _, tr in self.X_.iterrows():
                tr = tr.to_numpy()
                if all([tr[i] >= te[i] for i in range(self.X_.shape[1])]):
                    counter += 1

            if counter == 0:
                prediction.loc[task_id] = pd.Series(
                    {
                        strategy: value
                        for strategy, value in zip(self.strategies_, self.return_value_)
                    }
                )

        return prediction


from Transparency.Trainers.TrainerTransformer import go
from argparse import ArgumentParser
import os

def run_experiments(arg):
    result = go(arg)
    return result

#parser.add_argument("--csv_path",
#                    help = "Path to the csv containing the preprocessed information",
#                    type = str, default = )

if __name__ == "__main__":

    parser = ArgumentParser()

    parser.add_argument("--model_type",
                            choices= ["diversity_transformer", "vanilla_transformer"],
                            required=True, type=str)

    parser.add_argument("--dataset_name",
                        help = "Name of the dataset to train on",
                        required = True,type = str)
    parser.add_argument("--random-seed", help = "What random seed to use?",
                        default = 0, type = int)

    arg = parser.parse_args()
    run_experiments(arg)

"""
parser.add_argument("-e", "--num-epochs",
                dest="num_epochs",
                help="Number of epochs.",
                default=10, type=int)

parser.add_argument("-b", "--batch-size",
                dest="batch_size",
                help="The batch size.",
                default=4, type=int)

parser.add_argument("-l", "--learn-rate",
                dest="lr",
                help="Learning rate",
                default=0.0001, type=float)

#parser.add_argument("-T", "--tb_dir", dest="tb_dir",
#                    help="Tensorboard logging directory",
#                    default='./runs')

parser.add_argument("--max_pool", dest="max_pool",
                help="Use max pooling in the final classification layer.",
                action="store_true")

parser.add_argument("-E", "--embedding", dest="embedding_size",
                help="Size of the character embeddings.",
                default=300, type=int) # 128

parser.add_argument("-M", "--max", dest="max_length",
                help="Max sequence length. Longer sequences are clipped (-1 for no limit).",
                default=512, type=int)

parser.add_argument("-H", "--heads", dest="num_heads",
                help="Number of attention heads.",
                default=8, type=int)

parser.add_argument("-d", "--depth", dest="depth",
                help="Depth of the network (nr. of self-attention layers/ transformer blocks)",
                default=1, type=int) # 6

parser.add_argument("-r", "--random-seed",
                dest="seed",
                help="RNG seed. Negative for random",
                default=1, type=int)

parser.add_argument("--gradient_clipping",
                    dest="gradient_clipping",
                    help="Gradient clipping.",
                    default=1.0, type=float)

parser.add_argument("--diversity_transformer",
                    help="Add diveristy term to loss?",
                    default=False, type=bool)

parser.add_argument("--diversity_weight",
                    help="Diversity weight for the diveristy part of the loss",
                    default=0.5, type=float)

parser.add_argument("--delete_prop",
                    help="What proportion of the values with the heighest weight should be set to 0?",
                    default=0, type=float)

parser.add_argument("--dataset_name",
                help = "Name of the dataset to train on",
                required = True,type = str)

parser.add_argument("--lr-warmup",
            dest="lr_warmup",
            help="Learning rate warmup.",
            default=5_000, type=int)
"""


from datetime import datetime, date
from sqlite3.dbapi2 import Date

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

chrome_options = webdriver.ChromeOptions();
chrome_options.add_experimental_option("excludeSwitches",
['enable-automation']);

driver = webdriver.Chrome("C:\\Users\\kalmarevelin\\chromedriver.exe", options=chrome_options)
driver.get("http://www.learnwebservices.com/locations/?size=100")

def click_on_create_location():
    driver.find_element(By.ID, "create-location-link").click()

def wait_for_name_input():
    WebDriverWait(driver, 10).until(
        expected_conditions.visibility_of_element_located(
            (By.ID, "location-name")
        )
    )
    return driver.find_element(By.ID, "location-name")

def wait_for_coords_input():
    WebDriverWait(driver, 10).until(
        expected_conditions.visibility_of_element_located(
            (By.ID, "location-coords")
        )
    )
    return driver.find_element(By.ID, "location-coords")

def create_name():
    now = datetime.now()
    timestamp = datetime.timestamp(now)
    location_name = 'Ajax_' + str(timestamp)
    return location_name

def type_location_name():
    location_name = create_name()
    driver.find_element(By.ID, "location-name").send_keys(location_name)

def type_coords(coord1, coord2):
    driver.find_element(By.ID, "location-coords").send_keys(coord1, ",", coord2)

def create_new_location():
    driver.find_element(By.XPATH, "//*[@id='location-form']/input[1]").click()

def location_has_created():
    WebDriverWait(driver, 10).until(
        expected_conditions.text_to_be_present_in_element((By.ID, "message-div"),
                                                              "Location has created"))
    message = driver.find_element(By.ID, "message-div").text
    print(message)

def wait_for_location_in_table():
    location_name = create_name()
    WebDriverWait(driver, 10).until(
        expected_conditions.text_to_be_present_in_element((By.ID, "locations-table"),
                                                          "Ajax_"))

def assert_coords():
    coords = driver.find_element(By.XPATH, "//tr[td[contains(text(), 'Ajax_')]]/td[3]").text
    print(type(coords))
    #assert coords == 10.0, 10.0

def blank_name_message():
    WebDriverWait(driver, 10).until(
        expected_conditions.text_to_be_present_in_element((By.ID, "message-div"),
                                                              "must not be blank"))
    message = driver.find_element(By.ID, "message-div").text
    print(message)

def blank_coord_message():
    WebDriverWait(driver, 10).until(
        expected_conditions.text_to_be_present_in_element((By.ID, "message-div"),
                                                              "invalid coordinates"))
    message = driver.find_element(By.ID, "message-div").text
    print(message)

def invalid_coord_message():
    WebDriverWait(driver, 10).until(
        expected_conditions.text_to_be_present_in_element((By.ID, "message-div"),
                                                              "invalid coordinates"))
    message = driver.find_element(By.ID, "message-div").text
    print(message)

def click_on_cancel():
    driver.find_element(By.ID, "create-cancel-button").click()

click_on_create_location()
wait_for_name_input()
wait_for_coords_input()
create_name()
type_location_name()
type_coords(10, 10)
create_new_location()
location_has_created()
wait_for_location_in_table()
assert_coords()

#click_on_create_location()
#wait_for_name_input()
#wait_for_coords_input()
#type_coords(11, 11)
#create_new_location()
#blank_name_message()
#click_on_cancel()

click_on_create_location()
wait_for_name_input()
wait_for_coords_input()
create_name()
type_location_name()
create_new_location()
blank_coord_message()
click_on_cancel()

click_on_create_location()
wait_for_name_input()
wait_for_coords_input()
create_name()
type_location_name()
type_coords(10,  10)
invalid_coord_message()