# AG Implementation provided by IFERROUDJENE Mouloud, refactored into a command line program by Smail KOURTA
import numpy as np
import pandas as pd
import Parser
import time
import argparse

def PPV(graphe, v_depart=None):
    # Faire une copie du graphe vu qu'il va subir a des modification
    _graphe = graphe.copy()
    # La liste chemin gardera trace de notre parcour
    chemin = []
    # Selection d'un point de depart
    if v_depart is None:
        depart = v_depart = np.random.randint(0, len(graphe))
    depart = v_depart
    chemin.append(v_depart)
    # Creation de l'ensemble des noeuds non visités
    noeudsNonVisite = set(
        np.delete(np.arange(0, len(graphe)), v_depart).flatten())
    cout = 0
    while (len(noeudsNonVisite) != 0):
        # Retourner le plus proche voisin
        v_suivante = np.argmin(_graphe[v_depart, :])
        # Màj du chemin
        chemin.append(v_suivante)
        # Màj du cout
        cout += _graphe[v_depart, v_suivante]
        # Visiter le prochain neoud
        noeudsNonVisite.remove(v_suivante)
        v_depart = v_suivante
        # Mettre vers les noeuds deja visité a l'infini
        _graphe[v_depart, chemin] = float("inf")
        _graphe[chemin, v_depart] = float("inf")
    # Ajouter le cout de retour
    cout += graphe[v_suivante, depart]
    return np.array(chemin)

def generatePopulation(graphe, populationSize, algorithme="Random"):
    population = []
    grapheSize = len(graphe)

    if (algorithme == "Random"):
        for i in range(populationSize):
            # Generation d'un individu
            sol = np.random.permutation(grapheSize)
            # Ajouter l'individu a la population
            population.append(sol)
        return np.array(population)

    if (algorithme == "PPV"):
        for i in range(populationSize):
            # v_depart = np.random.randint(0,grapheSize)
            v_depart = i % grapheSize
            sol = PPV(graphe, v_depart)
            population.append(sol)
        return np.array(population)

def evaluateIndividu(indiv, graphe):
    cout = 0
    for i in range(len(graphe)):
        cout += graphe[indiv[i - 1]][indiv[i]]
    return cout

def evaluatePopulation(graphe, population):
    evaluated_population = []
    for pop in population:
        cout = evaluateIndividu(pop, graphe)
        fitness = 1 / cout
        evaluated_population.append([pop, cout, fitness])
    return np.array(evaluated_population)

def selectParents(evaluated_population, parents_Size, eliteSize=2, Algorithme="RouletteWheel"):
    # Liste ordonnée d'indices
    ind = np.argsort(evaluated_population[:, 1])
    sorted_population = evaluated_population[ind]

    if (Algorithme == "Elitiste"):
        # Prendre uniquement les elites (Meilleurs individus)
        parents = sorted_population[:parents_Size, :]
        return parents
    else:
        # Choisir le elites a inclure la liste des parents
        parents = sorted_population[:eliteSize, :]

    if (Algorithme == "Tournoi"):
        # Un certain nombre d'individus sont sélectionnés au hasard dans la population
        # Et l'elite du groupe est choisi comme premier parent.
        # Cette opération est répétée pour choisir le deuxième parent.
        populationSize = len(evaluated_population)
        for i in range(parents_Size - eliteSize):
            # Selectionner un nombre aleatoire d'individus pour un tournoi
            selectedSize = np.random.randint(1, populationSize)
            selectedIndice = np.unique(
                np.random.randint(0, populationSize, selectedSize))
            selected_population = evaluated_population[selectedIndice]
            # Prendre le meileure individus de ce trounois
            elite_indice = np.argmin(selected_population[:, 1])
            selected_elite = selected_population[elite_indice][:]
            # Ajouter l'elite a la liste des parents
            parents = np.insert(parents, 0, selected_elite, axis=0)
        return parents

    if (Algorithme == "RouletteWheel"):
        # Nous avons mis en place la roue de la roulette en calculant un poids de forme relatif pour chaque individu.
        df = pd.DataFrame(np.array(sorted_population),
                          columns=["Index", "cout", "Fitness"])
        # Calcules des somme cumulatives de fitness
        df['cum_sum'] = df.Fitness.cumsum()
        # Cacule
        df['cum_perc'] = 100 * df.cum_sum / df.Fitness.sum()

        # Ici nous comparons un nombre tiré au hasard à ces poids pour sélectionner le parents
        for i in range(0, parents_Size - eliteSize):
            # Faire tourner la roulette
            pick = np.random.uniform(0, 100)
            for i in range(0, len(sorted_population)):
                if pick <= df.iat[i, 4]:
                    parents = np.insert(
                        parents, 0, sorted_population[i][:], axis=0)
                    break
        return parents

def inter(lst1, lst2, offset=0):
    # retourne les elements dupliqués de la liste lst2 dans la liste lst1 avec leurs indices
    lst = [(lst1.index(value) + offset, value)
           for value in lst1 if value in lst2]
    return lst

def croisement(parent1, parent2, nbPointsCroissement):
    fils1 = parent1.tolist()
    fils2 = parent2.tolist()
    # Choisi aleatoirement deux points de découpe
    rng = np.random.default_rng()
    points = np.sort(rng.choice(
        len(parent1), size=nbPointsCroissement, replace=False), axis=0).tolist()
    co_points = [0]
    for i in range(len(points)):
        if i % 2 == 0:
            co_points.append(points[i])
        else:
            co_points.append(points[i] + 1)
    co_points.append(len(parent1))
    # Les sous chaines s_a1,s_a2=[],[]
    s_a1, s_a2 = [], []
    for i in range(len(points) // 2):
        s_a1.append(fils1[points[2 * i]:points[2 * i + 1] + 1])
        s_a2.append(fils2[points[2 * i]:points[2 * i + 1] + 1])
    # intervertir entre les deux parcours
    for i in range(len(points) // 2):
        fils1[points[2 * i]: points[2 * i + 1] + 1] = s_a2[i]
        fils2[points[2 * i]: points[2 * i + 1] + 1] = s_a1[i]
    # recenser les villes qui n'apparaissent pas dans chacun des deux villes
    index1, index2 = [], []
    for i in range(len(points) // 2 + 1):
        for v in s_a2:
            index1 += inter(fils1[co_points[2 * i]                                  :co_points[2 * i + 1]], v, co_points[2 * i])
        for u in s_a1:
            index2 += inter(fils2[co_points[2 * i]                                  :co_points[2 * i + 1]], u, co_points[2 * i])
    # Remplir les trous dans chaque parcours
    for i in range(len(index1)):
        fils1[index1[i][0]] = index2[i][1]
        fils2[index2[i][0]] = index1[i][1]
    return fils1, fils2

def croisementPopulation(parents, nbPointsCroissement=2):
    fils = []
    random_list = np.random.permutation(len(parents))
    for i in range(0, len(parents) - 1, 2):
        fils1, filsP2 = croisement(
            parents[random_list[i], 0], parents[random_list[i + 1], 0], nbPointsCroissement)
        fils.extend((fils1, filsP2))
    return np.array(fils)

def swap_mutation(individu, probaMutation):
    for i in range(len(individu)):
        chance = np.random.uniform()
        if (chance < probaMutation):
            j = int(chance * len(individu))
            individu[i], individu[j] = individu[j], individu[i].copy()
    return individu

def mutationPopulation(population, probaMutation=None):
    mutated_population = []
    # C'est une probabilité tres faible [0.01:0.001] ou P=1/len(indiv)
    if probaMutation is None:
        probaMutation = 1 / population.shape[1]
    for individu in population:
        mutated_population.append(swap_mutation(individu, probaMutation))
    return np.array(mutated_population)

def remplacement(init_population, new_population, methode="Generationnel"):
    if methode == "Elitiste":
        size = len(new_population)
        pop = np.append(init_population, new_population, axis=0)
        ind = np.argsort(pop[:, 1])
        sorted_pop = pop[ind]
        return sorted_pop[:size, :]

    if methode == "RouletteWheel":
        size = len(new_population)
        pop = np.append(init_population, new_population, axis=0)
        return selectParents(pop, size, Algorithme="RouletteWheel")

    if methode == "Tournoi":
        size = len(new_population)
        pop = np.append(init_population, new_population, axis=0)
        return selectParents(pop, size, Algorithme="Tournoi")

    if methode == "Generationnel":
        return new_population

def nextGeneration(graphe, population, parents_size, eliteSize, SelectionAlgo, nbPointCroisement, probaMutation,
                   remplacementAlgo):
    # creation d'un copie de la population initial car on désir la garder pour l operateur de remplacement
    init_population = population.copy()
    # SelectionAlgo : [RouletteWheel, Tournoi, Elitiste]
    parents = selectParents(population, parents_size, eliteSize, SelectionAlgo)
    # Croisement
    fils = croisementPopulation(parents, nbPointCroisement)
    # Mutation
    mutated_fils = evaluatePopulation(
        graphe, mutationPopulation(fils, probaMutation))
    new_population = np.array(np.append(parents, mutated_fils, axis=0))
    # Remplacement
    # Modes: Elitiste , Generationnel, RouletteWheel, Tournoi
    pop = remplacement(init_population, new_population, remplacementAlgo)
    return pop

def geneticAlgorithm(graphe, population_size, nbgenerations, parents_size, eliteSize=2, genAlgo="Random",
                     SelectionAlgo="RouletteWheel",
                     nbPointCroisement=4, probaMutation=None, remplacementAlgo="Generationnel"):
    # Generation de population initial
    # genAlgo: Random,PPV
    populationGen = generatePopulation(graphe, population_size, genAlgo)
    # Evaluation de population
    population = evaluatePopulation(graphe, populationGen)
    for i in range(0, nbgenerations):
        population = nextGeneration(graphe, population, parents_size, eliteSize, SelectionAlgo, nbPointCroisement,
                                    probaMutation, remplacementAlgo)
    sol = population[np.argsort(population[:, 1]), :2][0].tolist()
    return sol[0].tolist(), sol[1]

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("instance")
    parser.add_argument("--population_size",
                        help="Size of Population", default=28)
    parser.add_argument("--nbgenerations",
                        help="Number of Generations", default=2)
    parser.add_argument("--parents_size",
                        help="Size of parent", default=14)
    parser.add_argument("--eliteSize",
                        help="Size of Elite", default=2)
    parser.add_argument("--genAlgo",
                        help="Generation Algorithm", default="PPV")
    parser.add_argument("--SelectionAlgo",
                        help="Selection Algorithm", default="Tournoi")
    parser.add_argument("--nbPointCroisement",
                        help="Selection Algorithm", default=2)
    parser.add_argument("--probaMutation",
                        help="Mutation Probability", default=None)
    parser.add_argument("--remplacementAlgo",
                        help="Replacement Algorithm", default="Tournoi")
    args = parser.parse_args()
    instance = Parser.TSPInstance(args.instance)
    instance.readData()
    probaMutation = None
    if args.probaMutation != "None" and args.probaMutation is not None:
        probaMutation = float(args.probaMutation)
    start_time = time.time()
    tour, cost = geneticAlgorithm(np.array(instance.data), population_size=int(args.population_size),
                                  nbgenerations=int(args.nbgenerations), parents_size=int(args.parents_size),
                                  eliteSize=int(args.eliteSize), genAlgo=args.genAlgo, SelectionAlgo=args.SelectionAlgo,
                                  nbPointCroisement=int(
                                      args.nbPointCroisement),
                                  probaMutation=probaMutation, remplacementAlgo=args.remplacementAlgo)
    end_time = time.time()
    print(tour)
    print(cost)
    print(end_time - start_time)


import tensorflow as tf
import sklearn
import scipy.sparse
import numpy as np
import os, time, collections, shutil
from collections import defaultdict

# NFEATURES = 28**2
# NCLASSES = 10

# Common methods for all models

class base_model(object):
    def __init__(self):
        self.seg_classes = {'Earphone': [16, 17, 18], 'Motorbike': [30, 31, 32, 33, 34, 35], 'Rocket': [41, 42, 43],
                            'Car': [8, 9, 10, 11], 'Laptop': [28, 29], 'Cap': [6, 7], 'Skateboard': [44, 45, 46],
                            'Mug': [36, 37], 'Guitar': [19, 20, 21], 'Bag': [4, 5], 'Lamp': [24, 25, 26, 27],
                            'Table': [47, 48, 49], 'Airplane': [0, 1, 2, 3], 'Pistol': [38, 39, 40],
                            'Chair': [12, 13, 14, 15], 'Knife': [22, 23]}
        self.label_to_cat = {}
        for key in self.seg_classes.keys():
            for label in self.seg_classes[key]:
                self.label_to_cat[label] = key
        self.regularizers = []

    # High-level interface which runs the constructed computational graph.

    def predict(self, data, cat, labels, sess=None):
        loss = 0
        size = data.shape[0]
        predictions = np.empty((size, data.shape[1]))
        sess = self._get_session(sess)
        for begin in range(0, size, self.batch_size):
            end = begin + self.batch_size
            end = min([end, size])

            batch_data = np.zeros((self.batch_size, data.shape[1], 6))
            batch_cat = np.zeros((self.batch_size))
            # batch_lap = np.zeros((self.batch_size, data.shape[1], data.shape[1]))
            tmp_data = data[begin:end, :]
            tmp_cat = cat[begin:end]
            # tmp_lap = lap[begin:end, :, :]
            if type(tmp_data) is not np.ndarray:
                tmp_data = tmp_data.toarray()  # convert sparse matrices
            batch_data[:end - begin] = tmp_data
            batch_cat[:end - begin] = tmp_cat
            # batch_lap[:end - begin] = tmp_lap
            feed_dict = {self.ph_data: batch_data, self.ph_cat: batch_cat, self.ph_dropout: 1}

            # Compute loss if labels are given.
            if labels is not None:
                batch_labels = np.zeros((self.batch_size, data.shape[1]))
                batch_labels[:end - begin] = labels[begin:end]
                feed_dict[self.ph_labels] = batch_labels
                batch_pred, batch_loss = sess.run([self.op_prediction, self.op_loss], feed_dict)
                loss += batch_loss
            else:
                batch_pred = sess.run(self.op_prediction, feed_dict)

            predictions[begin:end] = batch_pred[:end - begin]

        if labels is not None:
            return predictions, loss * self.batch_size / size
        else:
            return predictions

    def evaluate(self, data, cat, labels, sess=None):
        """
        Runs one evaluation against the full epoch of data.
        Return the precision and the number of correct predictions.
        Batch evaluation saves memory and enables this to run on smaller GPUs.

        sess: the session in which the model has been trained.
        op: the Tensor that returns the number of correct predictions.
        data: size N x M
            N: number of signals (samples)
            M: number of vertices (features)
        labels: size N
            N: number of signals (samples)
        """

        t_process, t_wall = time.process_time(), time.time()
        predictions, loss = self.predict(data, cat, labels, sess)
        # print(predictions)
        ncorrects = np.mean(predictions == labels, axis=1)
        print(ncorrects)
        accuracy = np.mean(ncorrects)
        f1 = 0

        cat_iou = defaultdict(list)

        tot_iou = []
        for i in range(predictions.shape[0]):
            segp = predictions[i, :]
            segl = labels[i, :]
            cat = self.label_to_cat[segl[0]]
            part_ious = [0.0 for _ in range(len(self.seg_classes[cat]))]

            for l in self.seg_classes[cat]:
                if (np.sum(segl == l) == 0) and (np.sum(segp == l) == 0):  # part is not present, no prediction as well
                    part_ious[l - self.seg_classes[cat][0]] = 1.0
                else:
                    part_ious[l - self.seg_classes[cat][0]] = np.sum((segl == l) & (segp == l)) / float(
                        np.sum((segl == l) | (segp == l)))
            cat_iou[cat].append(np.mean(part_ious))
            tot_iou.append(np.mean(part_ious))

        for key, value in cat_iou.items():
            print(key + ': {:.4f}, total: {:d}'.format(np.mean(value), len(value)))
        # print(tot_iou)
        # accuracy = 100 * sklearn.metrics.accuracy_score(labels, predictions)
        # f1 = 100 * sklearn.metrics.f1_score(labels, predictions, average='weighted')
        string = 'accuracy: {:.4f} ({:d} / {:d}), iou (weighted): {:.4f}, loss: {:.2e}'.format(
            accuracy, np.sum(predictions == labels), labels.shape[0] * labels.shape[1], np.mean(tot_iou), loss)
        if sess is None:
            string += '\ntime: {:.0f}s (wall {:.0f}s)'.format(time.process_time() - t_process, time.time() - t_wall)
        return string, accuracy, f1, loss

    def fit(self, train_data, train_cat, train_labels, val_data, val_cat, val_labels, is_continue=True):
        f = open("train.log", "a")
        t_process, t_wall = time.process_time(), time.time()
        if is_continue is True:
            sess = self._get_session(sess=None)
        else:
            sess = tf.Session(graph=self.graph)
            sess.run(self.op_init)
            shutil.rmtree(self._get_path('checkpoints'), ignore_errors=True)
            os.makedirs(self._get_path('checkpoints'))

        writer = tf.summary.FileWriter(self._get_path('summaries'), self.graph)
        path = os.path.join(self._get_path('checkpoints'), 'model')

        # Training.
        accuracies = []
        losses = []
        indices = collections.deque()
        num_steps = int(self.num_epochs * train_data.shape[0] / self.batch_size)
        for step in range(1, num_steps + 1):

            # Be sure to have used all the samples before using one a second time.
            if len(indices) < self.batch_size:
                indices.extend(np.random.permutation(train_data.shape[0]))

            idx = [indices.popleft() for i in range(self.batch_size)]
            batch_data, batch_cat, batch_labels = train_data[idx, :], train_cat[idx], train_labels[idx]
            if type(batch_data) is not np.ndarray:
                batch_data = batch_data.toarray()  # convert sparse matrices
            feed_dict = {self.ph_data: batch_data, self.ph_cat: batch_cat, self.ph_labels: batch_labels,
                         self.ph_dropout: self.dropout}
            learning_rate, loss_average, loss = sess.run([self.op_train, self.op_loss_average, self.op_loss], feed_dict)
            # print(sess.run([self.op_logits],feed_dict))
            print(loss, loss_average)
            f.write(str(loss) + str(loss_average))
            # Periodical evaluation of the model.
            if step % self.eval_frequency == 0 or step == num_steps:
                epoch = step * self.batch_size / train_data.shape[0]
                print('step {} / {} (epoch {:.2f} / {}):'.format(step, num_steps, epoch, self.num_epochs))
                f.write('step {} / {} (epoch {:.2f} / {}):'.format(step, num_steps, epoch, self.num_epochs))
                print('  learning_rate = {:.2e}, loss_average = {:.2e}'.format(learning_rate, loss_average))
                f.write('  learning_rate = {:.2e}, loss_average = {:.2e}'.format(learning_rate, loss_average))
                string, accuracy, f1, loss = self.evaluate(val_data, val_cat, val_labels, sess)
                accuracies.append(accuracy)
                losses.append(loss)
                print('  validation {}'.format(string))
                f.write('  validation {}'.format(string))
                print('  time: {:.0f}s (wall {:.0f}s)'.format(time.process_time() - t_process, time.time() - t_wall))
                f.write('  time: {:.0f}s (wall {:.0f}s)'.format(time.process_time() - t_process, time.time() - t_wall))

                # Summaries for TensorBoard.
                summary = tf.Summary()
                summary.ParseFromString(sess.run(self.op_summary, feed_dict))
                summary.value.add(tag='validation/accuracy', simple_value=accuracy)
                summary.value.add(tag='validation/f1', simple_value=f1)
                summary.value.add(tag='validation/loss', simple_value=loss)
                writer.add_summary(summary, step)

                # Save model parameters (for evaluation).
                self.op_saver.save(sess, path, global_step=step)

        print('validation accuracy: peak = {:.2f}, mean = {:.2f}'.format(max(accuracies), np.mean(accuracies[-10:])))
        writer.close()
        sess.close()

        t_step = (time.time() - t_wall) / num_steps
        return accuracies, losses, t_step

    # Methods to construct the computational graph.

    def build_graph(self, M_0):
        """Build the computational graph of the model."""
        self.graph = tf.Graph()
        with self.graph.as_default():
            # Inputs.
            with tf.name_scope('inputs'):
                # self.pj_graph = tf.placeholder(tf.float32, (self.batch_size, M_0, M_0), 'lapacian')
                self.ph_data = tf.placeholder(tf.float32, (self.batch_size, M_0, 6), 'data')
                self.ph_labels = tf.placeholder(tf.int32, (self.batch_size, M_0), 'labels')
                self.ph_cat = tf.placeholder(tf.int32, (self.batch_size), 'labels')
                self.ph_dropout = tf.placeholder(tf.float32, (), 'dropout')

            # Model.
            op_logits = self.inference(self.ph_data, self.ph_cat, self.ph_dropout)

            run_meta = tf.RunMetadata()
            opts = tf.profiler.ProfileOptionBuilder.float_operation()
            flops = tf.profiler.profile(self.graph, run_meta=run_meta, cmd='op', options=opts)
            print('Total flops' + str(flops.total_float_ops))

            self.op_logits = op_logits
            self.op_loss, self.op_loss_average = self.loss(op_logits, self.ph_labels, self.regularization)
            self.op_train = self.training(self.op_loss, self.learning_rate,
                                          self.decay_steps, self.decay_rate, self.momentum)
            self.op_prediction = self.prediction(op_logits)

            # Initialize variables, i.e. weights and biases.
            self.op_init = tf.global_variables_initializer()

            # Summaries for TensorBoard and Save for model parameters.
            self.op_summary = tf.summary.merge_all()
            self.op_saver = tf.train.Saver(max_to_keep=5)

        self.graph.finalize()

    def inference(self, data, cat, dropout):
        """
        It builds the model, i.e. the computational graph, as far as
        is required for running the network forward to make predictions,
        i.e. return logits given raw data.

        data: size 2 x N x M
        first part:
            N: number of signals (samples)
            M: number of vertices (features)
        second part:
            K: k layers of graph
        training: we may want to discriminate the two, e.g. for dropout.
            True: the model is built for training.
            False: the model is built for evaluation.
        """
        # TODO: optimizations for sparse data
        logits = self._inference(data, cat, dropout)
        return logits

    def probabilities(self, logits):
        """Return the probability of a sample to belong to each class."""
        with tf.name_scope('probabilities'):
            probabilities = tf.nn.softmax(logits)
            return probabilities

    def prediction(self, logits):
        """Return the predicted classes."""
        with tf.name_scope('prediction'):
            prediction = tf.argmax(logits, axis=2)
            return prediction

    def loss(self, logits, labels, regularization):
        """Adds to the inference model the layers required to generate loss."""
        with tf.name_scope('loss'):
            with tf.name_scope('cross_entropy'):
                labels = tf.to_int64(labels)
                cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=labels)
                cross_entropy = tf.reduce_mean(cross_entropy)
            with tf.name_scope('regularization'):
                regularization *= tf.add_n(self.regularizers)
            loss = cross_entropy + regularization

            # Summaries for TensorBoard.
            tf.summary.scalar('loss/cross_entropy', cross_entropy)
            tf.summary.scalar('loss/regularization', regularization)
            tf.summary.scalar('loss/total', loss)
            with tf.name_scope('averages'):
                averages = tf.train.ExponentialMovingAverage(0.9)
                op_averages = averages.apply([cross_entropy, regularization, loss])
                tf.summary.scalar('loss/avg/cross_entropy', averages.average(cross_entropy))
                tf.summary.scalar('loss/avg/regularization', averages.average(regularization))
                tf.summary.scalar('loss/avg/total', averages.average(loss))
                with tf.control_dependencies([op_averages]):
                    loss_average = tf.identity(averages.average(loss), name='control')
            return loss, regularization

    def training(self, loss, learning_rate, decay_steps, decay_rate=0.95, momentum=0.8):
        """Adds to the loss model the Ops required to generate and apply gradients."""
        with tf.name_scope('training'):
            # Learning rate.
            global_step = tf.Variable(0, name='global_step', trainable=False)
            if decay_rate != 1:
                learning_rate = tf.train.exponential_decay(
                    learning_rate, global_step, decay_steps, decay_rate, staircase=True)
            tf.summary.scalar('learning_rate', learning_rate)
            # Optimizer.
            if momentum == 0:
                # optimizer = tf.train.GradientDescentOptimizer(learning_rate)
                optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
            else:
                optimizer = tf.train.MomentumOptimizer(learning_rate, momentum)
            grads = optimizer.compute_gradients(loss)
            op_gradients = optimizer.apply_gradients(grads, global_step=global_step)
            # Histograms.
            for grad, var in grads:
                if grad is None:
                    print('warning: {} has no gradient'.format(var.op.name))
                else:
                    tf.summary.histogram(var.op.name + '/gradients', grad)
            # The op return the learning rate.
            with tf.control_dependencies([op_gradients]):
                op_train = tf.identity(learning_rate, name='control')
            return op_train

    # Helper methods.

    def _get_path(self, folder):
        path = os.path.dirname(os.path.realpath(__file__))
        return os.path.join(path, '..', folder, self.dir_name)

    def _get_session(self, sess=None):
        """Restore parameters if no session given."""
        if sess is None:
            sess = tf.Session(graph=self.graph)
            filename = tf.train.latest_checkpoint(self._get_path('checkpoints'))
            self.op_saver.restore(sess, filename)
        return sess

    def _weight_variable(self, shape, regularization=True):
        initial = tf.truncated_normal_initializer(0, 0.1)
        var = tf.get_variable('weights', shape, tf.float32, initializer=initial)
        if regularization:
            self.regularizers.append(tf.nn.l2_loss(var))
        tf.summary.histogram(var.op.name, var)
        return var

    def _bias_variable(self, shape, regularization=True):
        initial = tf.constant_initializer(0.1)
        var = tf.get_variable('bias', shape, tf.float32, initializer=initial)
        if regularization:
            self.regularizers.append(tf.nn.l2_loss(var))
        tf.summary.histogram(var.op.name, var)
        return var

    def _conv2d(self, x, W):
        return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

class rgcnn(base_model):
    """
    Graph CNN which uses the Chebyshev approximation.

    The following are hyper-parameters of graph convolutional layers.
    They are lists, which length is equal to the number of gconv layers.
        F: Number of features.
        K: List of polynomial orders, i.e. filter sizes or number of hopes.

    The following are hyper-parameters of fully connected layers.
    They are lists, which length is equal to the number of fc layers.
        M: Number of features per sample, i.e. number of hidden neurons.
           The last layer is the softmax, i.e. M[-1] is the number of classes.

    Training parameters:
        num_epochs:    Number of training epochs.
        learning_rate: Initial learning rate.
        decay_rate:    Base of exponential decay. No decay with 1.
        decay_steps:   Number of steps after which the learning rate decays.
        momentum:      Momentum. 0 indicates no momentum.

    Regularization parameters:
        regularization: L2 regularizations of weights and biases.
        dropout:        Dropout (fc layers): probability to keep hidden neurons. No dropout with 1.
        batch_size:     Batch size. Must divide evenly into the dataset sizes.
        eval_frequency: Number of steps between evaluations.

    Directories:
        dir_name: Name for directories (summaries and model parameters).
    """

    def __init__(self, vertice, F, K, M, filter='chebyshev5', brelu='b1relu', pool='mpool1',
                 num_epochs=20, learning_rate=0.1, decay_rate=0.95, decay_steps=None, momentum=0.9,
                 regularization=0, dropout=0, batch_size=100, eval_frequency=200,
                 dir_name=''):
        super().__init__()

        # Verify the consistency w.r.t. the number of layers.
        assert len(F) == len(K)

        # Keep the useful Laplacians only. May be zero.
        M_0 = vertice
        # Print information about NN architecture.
        Ngconv = len(F)
        Nfc = len(M)
        print('NN architecture')
        print('  input: M_0 = {}'.format(vertice))
        for i in range(Ngconv):
            print('  layer {0}: gconv{0}'.format(i + 1))
            print('    representation: M_{0} * F_{1}= {2} * {3} = {4}'.format(
                i, i + 1, vertice, F[i], vertice * F[i]))
            F_last = F[i - 1] if i > 0 else 1
            print('    weights: F_{0} * F_{1} * K_{1} = {2} * {3} * {4} = {5}'.format(
                i, i + 1, F_last, F[i], K[i], F_last * F[i] * K[i]))
            print('    biases: F_{} = {}'.format(i + 1, F[i]))
        for i in range(Nfc):
            name = 'fc{}'.format(i + 1)
            print('  layer {}: {}'.format(Ngconv + i + 1, name))
            print('    representation: M_{} = {}'.format(Ngconv + i + 1, M[i]))
            M_last = M[i - 1] if i > 0 else vertice if Ngconv == 0 else vertice * F[-1]
            print('    weights: M_{} * M_{} = {} * {} = {}'.format(
                Ngconv + i, Ngconv + i + 1, M_last, M[i], M_last * M[i]))
            print('    biases: M_{} = {}'.format(Ngconv + i + 1, M[i]))

        # Store attributes and bind operations.
        self.F, self.K, self.M = F, K, M
        self.num_epochs, self.learning_rate = num_epochs, learning_rate
        self.decay_rate, self.decay_steps, self.momentum = decay_rate, decay_steps, momentum
        self.regularization, self.dropout = regularization, dropout
        self.batch_size, self.eval_frequency = batch_size, eval_frequency
        self.dir_name = dir_name
        self.filter = getattr(self, filter)
        self.brelu = getattr(self, brelu)

        # Build the computational graph.
        self.build_graph(M_0)

    def chebyshev5(self, x, L, Fout, K):
        # If K == 1 it is equivalent to fc layer
        N, M, Fin = x.get_shape()
        N, M, Fin = int(N), int(M), int(Fin)
        x0 = x  # N x M x Fin
        x = tf.expand_dims(x0, 0)

        def concat(x, x_):
            x_ = tf.expand_dims(x_, 0)  # 1 x M x Fin*N
            return tf.concat([x, x_], axis=0)  # K x M x Fin*N

        if K > 1:
            x1 = tf.matmul(L, x0)
            x = concat(x, x1)
        for k in range(2, K):
            x2 = 2 * tf.matmul(L, x1) - x0
            x = concat(x, x2)
            x0, x1 = x1, x2
        # K x N x M x Fin
        x = tf.transpose(x, perm=[1, 2, 3, 0])  # N x M x Fin x K
        x = tf.reshape(x, [N * M, Fin * K])  # N*M x Fin*K
        # Filter: Fin*Fout filters of order K, i.e. one filterbank per feature pair.
        W = self._weight_variable([Fin * K, Fout], regularization=False)
        x = tf.matmul(x, W)  # N*M x Fout
        return tf.reshape(x, [N, M, Fout])  # N x M x Fout

    def b1relu(self, x):
        """Bias and ReLU. One bias per filter."""
        N, M, F = x.get_shape()
        b = self._bias_variable([1, 1, int(F)], regularization=False)
        return tf.nn.relu(x + b)

    def b2relu(self, x):
        """Bias and ReLU. One bias per vertex per filter."""
        N, M, F = x.get_shape()
        b = self._bias_variable([1, int(M), int(F)], regularization=False)
        return tf.nn.relu(x + b)

    def fc(self, x, Mout, relu=True):
        """Fully connected layer with Mout features."""
        N, Min = x.get_shape()
        W = self._weight_variable([int(Min), Mout], regularization=True)
        b = self._bias_variable([Mout], regularization=True)
        x = tf.matmul(x, W) + b
        return tf.nn.relu(x) if relu else x

    def pairwise_distance(self, point_cloud):
        """Compute pairwise distance of a point cloud.

        Args:
            point_cloud: tensor (batch_size, num_points, num_dims)

        Returns:
            pairwise distance: (batch_size, num_points, num_points)
        """

        og_batch_size = point_cloud.get_shape().as_list()[0]
        point_cloud = tf.squeeze(point_cloud)
        if og_batch_size == 1:
            point_cloud = tf.expand_dims(point_cloud, 0)

        point_cloud_transpose = tf.transpose(point_cloud, perm=[0, 2, 1])
        point_cloud_inner = tf.matmul(point_cloud, point_cloud_transpose)
        point_cloud_inner = -2 * point_cloud_inner
        point_cloud_square = tf.reduce_sum(tf.square(point_cloud), axis=-1, keep_dims=True)
        point_cloud_square_tranpose = tf.transpose(point_cloud_square, perm=[0, 2, 1])
        adj_matrix = point_cloud_square + point_cloud_inner + point_cloud_square_tranpose
        adj_matrix = tf.exp(-adj_matrix)
        return adj_matrix

    def knn(self, adj_matrix, k=30):
        """Get KNN based on the pairwise distance.
        Args:
            pairwise distance: (batch_size, num_points, num_points)
            k: int

        Returns:
            nearest neighbors: (batch_size, num_points, k)
        """
        # neg_adj = -adj_matrix
        _, nn_idx = tf.nn.top_k(adj_matrix, k=k)
        return nn_idx

    def get_laplacian(self, adj_matrix, normalize=True):
        """Compute pairwise distance of a point cloud.

        Args:
            pairwise distance: tensor (batch_size, num_points, num_points)

        Returns:
            pairwise distance: (batch_size, num_points, num_points)
        """
        if normalize:
            D = tf.reduce_sum(adj_matrix, axis=1)  # (batch_size,num_points)
            eye = tf.ones_like(D)
            eye = tf.matrix_diag(eye)
            D = 1 / tf.sqrt(D)
            D = tf.matrix_diag(D)
            L = eye - tf.matmul(tf.matmul(D, adj_matrix), D)
        else:
            D = tf.reduce_sum(adj_matrix, axis=1)  # (batch_size,num_points)
            # eye = tf.ones_like(D)
            # eye = tf.matrix_diag(eye)
            # D = 1 / tf.sqrt(D)
            D = tf.matrix_diag(D)
            L = D - adj_matrix
        return L

    def get_one_matrix_knn(self, matrix, k):
        values, indices = tf.nn.top_k(matrix, k,
                                      sorted=False)  # indices will be [[0, 1], [1, 2]], values will be [[6., 2.], [4., 5.]]

        my_range = tf.expand_dims(tf.range(0, indices.get_shape()[0]), 1)  # will be [[0], [1]]
        my_range_repeated = tf.tile(my_range, [1, k])  # will be [[0, 0], [1, 1]]

        # change shapes to [N, k, 1] and [N, k, 1], to concatenate into [N, k, 2]
        full_indices = tf.concat([tf.expand_dims(my_range_repeated, 2), tf.expand_dims(indices, 2)], axis=2)
        full_indices = tf.reshape(full_indices, [-1, 2])

        to_substract = tf.sparse_to_dense(full_indices, matrix.get_shape(), tf.reshape(values, [-1]), default_value=0.,
                                          validate_indices=False)

        # res = matrix - to_substract  # res should be all 0.
        return to_substract

    def _inference(self, x, cat, dropout):
        L = self.pairwise_distance(x)
        # L_ =self.get_laplacian(L,normalize=False)
        # L = tf.stack([self.get_one_matrix_knn(matrix = L[o],k = 30) for o in range(L.get_shape()[0])])
        L = self.get_laplacian(L)
        cat = tf.expand_dims(cat, axis=1)
        cat = tf.one_hot(cat, 16, axis=-1)
        cat = tf.tile(cat, [1, 2048, 1])
        x = tf.concat([x, cat], axis=2)

        x1 = 0 #cache for layer1
        for i in range(len(self.F)):
            with tf.variable_scope('conv{}'.format(i)):
                with tf.name_scope('filter'):
                    if i == 4:
                        x = tf.concat([x, x1], axis=2)
                    x = self.filter(x, L, self.F[i], self.K[i])
                    if i == 1:
                        x1 = x
                    self.regularizers.append(tf.nn.l2_loss(tf.matmul(tf.matmul(tf.transpose(x, perm=[0, 2, 1]), L), x)))
                with tf.name_scope('bias_relu'):
                    x = self.brelu(x)
        return x


from keras.models import Sequential
from keras.layers import Dense

class TensorFlowEstimator():

    def __init__(self, X_train, y_train):
        self.X_train = X_train
        self.y_train = y_train

    def fit(self):
        # Initialize the constructor
        model = Sequential()

        # Add an input layer
        model.add(Dense(12, activation='relu', input_shape=(self.X_train.shape[1],)))

        # Add one hidden layer
        model.add(Dense(8, activation='relu'))

        # Add an output layer
        model.add(Dense(1, activation='sigmoid'))

        # model fit
        model.compile(loss='binary_crossentropy',
                      optimizer='adam',
                      metrics=['accuracy'])

        return model.fit(self.X_train, self.y_train, epochs=20, batch_size=1, verbose=0)


from django.shortcuts import render
from django.http import HttpResponse
from common.models import Users,Goods,Types
from django.core.paginator import Paginator
from PIL import Image
from datetime import datetime
import time,os
from django.db.models import Q
from django.shortcuts import redirect
from django.core.urlresolvers import reverse

#获取产品类别索引
def typelist(request):
	try:
		context={}
		utype=Types.objects.filter(pid=0)
		context['utype']=utype
		return context
	except Exception as info:
		context={'info':info}
		return render(request,"myweb/info.html",context)

#获取单品
def list(request,uid):
	try:
		context=typelist(request)
		ulist=Goods.objects.get(id=uid)
		utype=Types.objects.get(id=ulist.typeid)
		context['list']=ulist
		context['typename']=utype.name
		return render(request,"myweb/vipuser/list.html",context)
	except Exception as info:
		context={'info':info}
		return render(request,"myweb/info.html",context)
#喜欢点赞
def love(request,uid):
	try:
		context=typelist(request)
		ulist=Goods.objects.get(id=uid)
		ulist.clicknum+=1
		ulist.save()
		utype=Types.objects.get(id=ulist.typeid)
		context['list']=ulist
		context['typename']=utype.name
		return render(request,"myweb/vipuser/list.html",context)
	except Exception as info:
		context={'info':info}
		return render(request,"myweb/info.html",context)


import datetime
import dateutil.parser
import logging
import os
import sys
import shutil
from typing import List, Optional, Iterable, TypeVar, Callable, Tuple

from crontab import CronTab
import yaml

if sys.version_info >= (3, 8):
    from typing import TypedDict, Literal
else:
    from typing_extensions import TypedDict, Literal

DEV_ENVIRONMENT =  os.environ.get("DEV_ENVIRONMENT") == '1'
APP_NAME = "satnogs-gut"
COMMENT_PASS_TAG = APP_NAME + "-Pass"
COMMENT_PLAN_TAG = APP_NAME + "-Plan"

CONFIG_DIRECTORY: str = os.environ.get("SATNOGS_GUT_CONFIG_DIR") # type: ignore
if CONFIG_DIRECTORY is None:
    if DEV_ENVIRONMENT:
        CONFIG_DIRECTORY = os.path.abspath("./config")
    else:
        CONFIG_DIRECTORY = os.path.expanduser("~/.config/%s" % (APP_NAME,))

CONFIG_PATH = os.path.join(CONFIG_DIRECTORY, "config.yml")
LOG_FILE = os.path.join(CONFIG_DIRECTORY, "log") if not DEV_ENVIRONMENT else None

if not os.path.exists(CONFIG_DIRECTORY):
    os.makedirs(CONFIG_DIRECTORY, exist_ok=True)

logging.basicConfig(level=logging.DEBUG if DEV_ENVIRONMENT else logging.ERROR,
                    format='%(asctime)s %(levelname)s %(filename)s:%(lineno)d: %(message)s',
                    filename=LOG_FILE)

class LocationConfiguration(TypedDict):
    elevation: float
    latitude: float
    longitude: float
    name: str

SATELLITE_SAVE_MODE = Literal["SIGNAL", "PRODUCT", "ALL", "INHERIT", "NONE"]
GLOBAL_SAVE_MODE = Literal["SIGNAL", "PRODUCT", "ALL", "NONE"]

class SatelliteConfiguration(TypedDict, total=False):
    freq: str
    name: str
    submit: Optional[bool]
    save_to_disk: Optional[SATELLITE_SAVE_MODE]
    aos_at: Optional[int]
    max_elevation_greater_than: Optional[int]
    disabled: Optional[bool]
    recipe: Optional[str]

class ServerConfiguration(TypedDict):
    id: str
    secret: str
    url: str

STRATEGY = Literal["max-elevation", "aos"]

class Configuration(TypedDict):
    aos_at: int
    location: LocationConfiguration
    max_elevation_greater_than: int
    norad: List[str]
    satellites: List[SatelliteConfiguration]
    save_to_disk: Optional[GLOBAL_SAVE_MODE]
    server: ServerConfiguration
    strategy: Optional[STRATEGY]
    submit: Optional[bool]
    obsdir: Optional[str]

def _get_command(directory, filename):
    if directory is None:
        directory = sys.argv[0]
    if not os.path.isdir(directory):
        directory = os.path.dirname(directory)
    path = os.path.join(directory, filename)
    path = os.path.abspath(path)
    return "python3 %s " % path

def get_planner_command(directory=None):
    return _get_command(directory, "planner.py")

def get_receiver_command(directory=None):
    return _get_command(directory, "receiver.py")

def open_crontab() -> CronTab:
    if DEV_ENVIRONMENT:
        path = os.path.join(CONFIG_DIRECTORY, "crontab")
        if not os.path.exists(path):
            with open(path, "x") as _:
                pass
        return CronTab(tabfile=path)
    else:
        return CronTab(user=True)

def open_config() -> Configuration:
    config_path = CONFIG_PATH
    config_exists = os.path.exists(config_path)
    if not config_exists:
        directory = os.path.dirname(config_path)
        os.makedirs(directory, exist_ok=True)
        shutil.copyfile('config.yml.template', config_path)

    with open(config_path) as f:
        return yaml.safe_load(f) # type: ignore

def save_config(config: Configuration):
    with open(CONFIG_PATH, "w") as f:
        return yaml.safe_dump(config, f)

T = TypeVar("T")

def first(iterable: Iterable[T], condition: Callable[[T], bool] = lambda x: True) -> Optional[T]:
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    Return `None` if no item satysfing the condition is found.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    None
    """
    try:
        return next(x for x in iterable if condition(x))
    except StopIteration:
        return None

class EditWatcher:
    def __init__(self, obj):
        object.__setattr__(self, "_obj", obj)
        object.__setattr__(self, "_edited", False)

    def __getattr__(self, key):
        return getattr(self._obj, key)

    def __setattr__(self, name, value):
        object.__setattr__(self, "_edited", True)
        return setattr(self._obj, name, value)

    def __getitem__(self, key):
        return self._obj[key]

    def __setitem__(self, key, value):
        object.__setattr__(self, "_edited", True)
        self._obj[key] = value

    def is_edited(self):
        return self._edited

    def get_wrapped(self):
        return self._obj

def utc_to_local(utc_dt):
    return utc_dt.replace(tzinfo=datetime.timezone.utc).astimezone(tz=None)

def is_safe_filename_character(c: str) -> bool:
    return c.isalpha() or c.isdigit() or c in (' ', '.', '-', ' ', '_')

def safe_filename(filename: str, replacement: str="_") -> str:
    chars = [c if is_safe_filename_character(c) else replacement for c in filename]
    return "".join(chars).rstrip()

def get_location(config: Configuration) -> Tuple[str, float, float, float]:
    location = (config["location"]["name"],
            config["location"]["latitude"], config["location"]["longitude"],
            config["location"]["elevation"])
    return location

def set_satellite_defaults(config: Configuration, satellite: SatelliteConfiguration):
    '''
    If satellite structure doesn't contains optional values
    then they will be set using inherited, global configuration.
    '''
    if 'submit' not in satellite:
        global_submit = config.get('submit', None)
        if global_submit is None:
            global_submit = True
        satellite['submit'] = global_submit
    if 'save_to_disk' not in satellite or satellite['save_to_disk'] == 'INHERIT':
        global_ = config.get('save_to_disk')
        if global_ is None:
            global_ = "NONE"
        satellite["save_to_disk"] = global_
    if 'aos_at' not in satellite:
        global_ = config.get('aos_at')
        if global_ is None:
            global_ = 0
        satellite['aos_at'] = global_
    if 'max_elevation_greater_than' not in satellite:
        global_ = config.get('max_elevation_greater_than')
        if global_ is None:
            global_ = 0
        satellite['max_elevation_greater_than'] = global_
    if 'disabled' not in satellite:
        satellite['disabled'] = False

def get_satellite(config: Configuration, sat: str) -> SatelliteConfiguration:
    '''
    Search for satellite configuration. Throw LookupError if not found.
    If found then omitted optional fields will be set using inherited, global
    configuration.
    '''
    satellites = config['satellites']
    satellite = first(satellites, lambda s: s['name'] == sat)
    if satellite is None:
        raise LookupError("Satellite %s not found" % (sat,))
    set_satellite_defaults(config, satellite)
    return satellite

def from_iso_format(raw: str) -> datetime.datetime:
    if sys.version_info <= (3, 6):
        return dateutil.parser.isoparse(raw)
    else:
        return datetime.datetime.fromisoformat(raw)


# -*- coding: utf-8 -*-
"""
oauthlib.oauth2.rfc6749
~~~~~~~~~~~~~~~~~~~~~~~

This module is an implementation of various logic needed
for consuming OAuth 2.0 RFC6749.
"""
import base64
import hashlib
import re
import secrets
import time
import warnings

from oauthlib.common import generate_token
from oauthlib.oauth2.rfc6749 import tokens
from oauthlib.oauth2.rfc6749.errors import (
    InsecureTransportError, TokenExpiredError,
)
from oauthlib.oauth2.rfc6749.parameters import (
    parse_token_response, prepare_token_request,
    prepare_token_revocation_request,
)
from oauthlib.oauth2.rfc6749.utils import is_secure_transport

AUTH_HEADER = 'auth_header'
URI_QUERY = 'query'
BODY = 'body'

FORM_ENC_HEADERS = {
    'Content-Type': 'application/x-www-form-urlencoded'
}

class Client:
    """Base OAuth2 client responsible for access token management.

    This class also acts as a generic interface providing methods common to all
    client types such as ``prepare_authorization_request`` and
    ``prepare_token_revocation_request``. The ``prepare_x_request`` methods are
    the recommended way of interacting with clients (as opposed to the abstract
    prepare uri/body/etc methods). They are recommended over the older set
    because they are easier to use (more consistent) and add a few additional
    security checks, such as HTTPS and state checking.

    Some of these methods require further implementation only provided by the
    specific purpose clients such as
    :py:class:`oauthlib.oauth2.MobileApplicationClient` and thus you should always
    seek to use the client class matching the OAuth workflow you need. For
    Python, this is usually :py:class:`oauthlib.oauth2.WebApplicationClient`.

    """
    refresh_token_key = 'refresh_token'

    def __init__(self, client_id,
                 default_token_placement=AUTH_HEADER,
                 token_type='Bearer',
                 access_token=None,
                 refresh_token=None,
                 mac_key=None,
                 mac_algorithm=None,
                 token=None,
                 scope=None,
                 state=None,
                 redirect_url=None,
                 state_generator=generate_token,
                 code_verifier=None,
                 code_challenge=None,
                 code_challenge_method=None,
                 **kwargs):
        """Initialize a client with commonly used attributes.

        :param client_id: Client identifier given by the OAuth provider upon
        registration.

        :param default_token_placement: Tokens can be supplied in the Authorization
        header (default), the URL query component (``query``) or the request
        body (``body``).

        :param token_type: OAuth 2 token type. Defaults to Bearer. Change this
        if you specify the ``access_token`` parameter and know it is of a
        different token type, such as a MAC, JWT or SAML token. Can
        also be supplied as ``token_type`` inside the ``token`` dict parameter.

        :param access_token: An access token (string) used to authenticate
        requests to protected resources. Can also be supplied inside the
        ``token`` dict parameter.

        :param refresh_token: A refresh token (string) used to refresh expired
        tokens. Can also be supplied inside the ``token`` dict parameter.

        :param mac_key: Encryption key used with MAC tokens.

        :param mac_algorithm:  Hashing algorithm for MAC tokens.

        :param token: A dict of token attributes such as ``access_token``,
        ``token_type`` and ``expires_at``.

        :param scope: A list of default scopes to request authorization for.

        :param state: A CSRF protection string used during authorization.

        :param redirect_url: The redirection endpoint on the client side to which
        the user returns after authorization.

        :param state_generator: A no argument state generation callable. Defaults
        to :py:meth:`oauthlib.common.generate_token`.

        :param code_verifier: PKCE parameter. A cryptographically random string that is used to correlate the
        authorization request to the token request.

        :param code_challenge: PKCE parameter. A challenge derived from the code verifier that is sent in the
        authorization request, to be verified against later.

        :param code_challenge_method: PKCE parameter. A method that was used to derive code challenge.
        Defaults to "plain" if not present in the request.
        """

        self.client_id = client_id
        self.default_token_placement = default_token_placement
        self.token_type = token_type
        self.access_token = access_token
        self.refresh_token = refresh_token
        self.mac_key = mac_key
        self.mac_algorithm = mac_algorithm
        self.token = token or {}
        self.scope = scope
        self.state_generator = state_generator
        self.state = state
        self.redirect_url = redirect_url
        self.code_verifier = code_verifier
        self.code_challenge = code_challenge
        self.code_challenge_method = code_challenge_method
        self.code = None
        self.expires_in = None
        self._expires_at = None
        self.populate_token_attributes(self.token)

    @property
    def token_types(self):
        """Supported token types and their respective methods

        Additional tokens can be supported by extending this dictionary.

        The Bearer token spec is stable and safe to use.

        The MAC token spec is not yet stable and support for MAC tokens
        is experimental and currently matching version 00 of the spec.
        """
        return {
            'Bearer': self._add_bearer_token,
            'MAC': self._add_mac_token
        }

    def prepare_request_uri(self, *args, **kwargs):
        """Abstract method used to create request URIs."""
        raise NotImplementedError("Must be implemented by inheriting classes.")

    def prepare_request_body(self, *args, **kwargs):
        """Abstract method used to create request bodies."""
        raise NotImplementedError("Must be implemented by inheriting classes.")

    def parse_request_uri_response(self, *args, **kwargs):
        """Abstract method used to parse redirection responses."""
        raise NotImplementedError("Must be implemented by inheriting classes.")

    def add_token(self, uri, http_method='GET', body=None, headers=None,
                  token_placement=None, **kwargs):
        """Add token to the request uri, body or authorization header.

        The access token type provides the client with the information
        required to successfully utilize the access token to make a protected
        resource request (along with type-specific attributes).  The client
        MUST NOT use an access token if it does not understand the token
        type.

        For example, the "bearer" token type defined in
        [`I-D.ietf-oauth-v2-bearer`_] is utilized by simply including the access
        token string in the request:

        .. code-block:: http

            GET /resource/1 HTTP/1.1
            Host: example.com
            Authorization: Bearer mF_9.B5f-4.1JqM

        while the "mac" token type defined in [`I-D.ietf-oauth-v2-http-mac`_] is
        utilized by issuing a MAC key together with the access token which is
        used to sign certain components of the HTTP requests:

        .. code-block:: http

            GET /resource/1 HTTP/1.1
            Host: example.com
            Authorization: MAC id="h480djs93hd8",
                                nonce="274312:dj83hs9s",
                                mac="kDZvddkndxvhGRXZhvuDjEWhGeE="

        .. _`I-D.ietf-oauth-v2-bearer`: https://tools.ietf.org/html/rfc6749#section-12.2
        .. _`I-D.ietf-oauth-v2-http-mac`: https://tools.ietf.org/html/rfc6749#section-12.2
        """
        if not is_secure_transport(uri):
            raise InsecureTransportError()

        token_placement = token_placement or self.default_token_placement

        case_insensitive_token_types = {
            k.lower(): v for k, v in self.token_types.items()}
        if self.token_type.lower() not in case_insensitive_token_types:
            raise ValueError("Unsupported token type: %s" % self.token_type)

        if not (self.access_token or self.token.get('access_token')):
            raise ValueError("Missing access token.")

        if self._expires_at and self._expires_at < time.time():
            raise TokenExpiredError()

        return case_insensitive_token_types[self.token_type.lower()](uri, http_method, body,
                                                                     headers, token_placement, **kwargs)

    def prepare_authorization_request(self, authorization_url, state=None,
                                      redirect_url=None, scope=None, **kwargs):
        """Prepare the authorization request.

        This is the first step in many OAuth flows in which the user is
        redirected to a certain authorization URL. This method adds
        required parameters to the authorization URL.

        :param authorization_url: Provider authorization endpoint URL.
        :param state: CSRF protection string. Will be automatically created if
            not provided. The generated state is available via the ``state``
            attribute. Clients should verify that the state is unchanged and
            present in the authorization response. This verification is done
            automatically if using the ``authorization_response`` parameter
            with ``prepare_token_request``.
        :param redirect_url: Redirect URL to which the user will be returned
            after authorization. Must be provided unless previously setup with
            the provider. If provided then it must also be provided in the
            token request.
        :param scope: List of scopes to request. Must be equal to
            or a subset of the scopes granted when obtaining the refresh
            token. If none is provided, the ones provided in the constructor are
            used.
        :param kwargs: Additional parameters to included in the request.
        :returns: The prepared request tuple with (url, headers, body).
        """
        if not is_secure_transport(authorization_url):
            raise InsecureTransportError()

        self.state = state or self.state_generator()
        self.redirect_url = redirect_url or self.redirect_url
        # do not assign scope to self automatically anymore
        scope = self.scope if scope is None else scope
        auth_url = self.prepare_request_uri(
            authorization_url, redirect_uri=self.redirect_url,
            scope=scope, state=self.state, **kwargs)
        return auth_url, FORM_ENC_HEADERS, ''

    def prepare_token_request(self, token_url, authorization_response=None,
                              redirect_url=None, state=None, body='', **kwargs):
        """Prepare a token creation request.

        Note that these requests usually require client authentication, either
        by including client_id or a set of provider specific authentication
        credentials.

        :param token_url: Provider token creation endpoint URL.
        :param authorization_response: The full redirection URL string, i.e.
            the location to which the user was redirected after successful
            authorization. Used to mine credentials needed to obtain a token
            in this step, such as authorization code.
        :param redirect_url: The redirect_url supplied with the authorization
            request (if there was one).
        :param state:
        :param body: Existing request body (URL encoded string) to embed parameters
                     into. This may contain extra parameters. Default ''.
        :param kwargs: Additional parameters to included in the request.
        :returns: The prepared request tuple with (url, headers, body).
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        state = state or self.state
        if authorization_response:
            self.parse_request_uri_response(
                authorization_response, state=state)
        self.redirect_url = redirect_url or self.redirect_url
        body = self.prepare_request_body(body=body,
                                         redirect_uri=self.redirect_url, **kwargs)

        return token_url, FORM_ENC_HEADERS, body

    def prepare_refresh_token_request(self, token_url, refresh_token=None,
                                      body='', scope=None, **kwargs):
        """Prepare an access token refresh request.

        Expired access tokens can be replaced by new access tokens without
        going through the OAuth dance if the client obtained a refresh token.
        This refresh token and authentication credentials can be used to
        obtain a new access token, and possibly a new refresh token.

        :param token_url: Provider token refresh endpoint URL.
        :param refresh_token: Refresh token string.
        :param body: Existing request body (URL encoded string) to embed parameters
            into. This may contain extra parameters. Default ''.
        :param scope: List of scopes to request. Must be equal to
            or a subset of the scopes granted when obtaining the refresh
            token. If none is provided, the ones provided in the constructor are
            used.
        :param kwargs: Additional parameters to included in the request.
        :returns: The prepared request tuple with (url, headers, body).
        """
        if not is_secure_transport(token_url):
            raise InsecureTransportError()

        # do not assign scope to self automatically anymore
        scope = self.scope if scope is None else scope
        body = self.prepare_refresh_body(body=body,
                                         refresh_token=refresh_token, scope=scope, **kwargs)
        return token_url, FORM_ENC_HEADERS, body

    def prepare_token_revocation_request(self, revocation_url, token,
                                         token_type_hint="access_token", body='', callback=None, **kwargs):
        """Prepare a token revocation request.

        :param revocation_url: Provider token revocation endpoint URL.
        :param token: The access or refresh token to be revoked (string).
        :param token_type_hint: ``"access_token"`` (default) or
            ``"refresh_token"``. This is optional and if you wish to not pass it you
            must provide ``token_type_hint=None``.
        :param body:
        :param callback: A jsonp callback such as ``package.callback`` to be invoked
            upon receiving the response. Not that it should not include a () suffix.
        :param kwargs: Additional parameters to included in the request.
        :returns: The prepared request tuple with (url, headers, body).

        Note that JSONP request may use GET requests as the parameters will
        be added to the request URL query as opposed to the request body.

        An example of a revocation request

        .. code-block:: http

            POST /revoke HTTP/1.1
            Host: server.example.com
            Content-Type: application/x-www-form-urlencoded
            Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

            token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token

        An example of a jsonp revocation request

        .. code-block:: http

            GET /revoke?token=agabcdefddddafdd&callback=package.myCallback HTTP/1.1
            Host: server.example.com
            Content-Type: application/x-www-form-urlencoded
            Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

        and an error response

        .. code-block:: javascript

            package.myCallback({"error":"unsupported_token_type"});

        Note that these requests usually require client credentials, client_id in
        the case for public clients and provider specific authentication
        credentials for confidential clients.
        """
        if not is_secure_transport(revocation_url):
            raise InsecureTransportError()

        return prepare_token_revocation_request(revocation_url, token,
                                                token_type_hint=token_type_hint, body=body, callback=callback,
                                                **kwargs)

    def parse_request_body_response(self, body, scope=None, **kwargs):
        """Parse the JSON response body.

        If the access token request is valid and authorized, the
        authorization server issues an access token as described in
        `Section 5.1`_.  A refresh token SHOULD NOT be included.  If the request
        failed client authentication or is invalid, the authorization server
        returns an error response as described in `Section 5.2`_.

        :param body: The response body from the token request.
        :param scope: Scopes originally requested. If none is provided, the ones
            provided in the constructor are used.
        :return: Dictionary of token parameters.
        :raises: Warning if scope has changed. :py:class:`oauthlib.oauth2.errors.OAuth2Error`
            if response is invalid.

        These response are json encoded and could easily be parsed without
        the assistance of OAuthLib. However, there are a few subtle issues
        to be aware of regarding the response which are helpfully addressed
        through the raising of various errors.

        A successful response should always contain

        **access_token**
                The access token issued by the authorization server. Often
                a random string.

        **token_type**
            The type of the token issued as described in `Section 7.1`_.
            Commonly ``Bearer``.

        While it is not mandated it is recommended that the provider include

        **expires_in**
            The lifetime in seconds of the access token.  For
            example, the value "3600" denotes that the access token will
            expire in one hour from the time the response was generated.
            If omitted, the authorization server SHOULD provide the
            expiration time via other means or document the default value.

         **scope**
            Providers may supply this in all responses but are required to only
            if it has changed since the authorization request.

        .. _`Section 5.1`: https://tools.ietf.org/html/rfc6749#section-5.1
        .. _`Section 5.2`: https://tools.ietf.org/html/rfc6749#section-5.2
        .. _`Section 7.1`: https://tools.ietf.org/html/rfc6749#section-7.1
        """
        scope = self.scope if scope is None else scope
        self.token = parse_token_response(body, scope=scope)
        self.populate_token_attributes(self.token)
        return self.token

    def prepare_refresh_body(self, body='', refresh_token=None, scope=None, **kwargs):
        """Prepare an access token request, using a refresh token.

        If the authorization server issued a refresh token to the client, the
        client makes a refresh request to the token endpoint by adding the
        following parameters using the `application/x-www-form-urlencoded`
        format in the HTTP request entity-body:

        :param refresh_token: REQUIRED.  The refresh token issued to the client.
        :param scope:  OPTIONAL.  The scope of the access request as described by
            Section 3.3.  The requested scope MUST NOT include any scope
            not originally granted by the resource owner, and if omitted is
            treated as equal to the scope originally granted by the
            resource owner. Note that if none is provided, the ones provided
            in the constructor are used if any.
        """
        refresh_token = refresh_token or self.refresh_token
        scope = self.scope if scope is None else scope
        return prepare_token_request(self.refresh_token_key, body=body, scope=scope,
                                     refresh_token=refresh_token, **kwargs)

    def _add_bearer_token(self, uri, http_method='GET', body=None,
                          headers=None, token_placement=None):
        """Add a bearer token to the request uri, body or authorization header."""
        if token_placement == AUTH_HEADER:
            headers = tokens.prepare_bearer_headers(self.access_token, headers)

        elif token_placement == URI_QUERY:
            uri = tokens.prepare_bearer_uri(self.access_token, uri)

        elif token_placement == BODY:
            body = tokens.prepare_bearer_body(self.access_token, body)

        else:
            raise ValueError("Invalid token placement.")
        return uri, headers, body

    def create_code_verifier(self, length):
        """Create PKCE **code_verifier** used in computing **code_challenge**.
        See `RFC7636 Section 4.1`_

        :param length: REQUIRED. The length of the code_verifier.

        The client first creates a code verifier, "code_verifier", for each
        OAuth 2.0 [RFC6749] Authorization Request, in the following manner:

        .. code-block:: text

               code_verifier = high-entropy cryptographic random STRING using the
               unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
               from Section 2.3 of [RFC3986], with a minimum length of 43 characters
               and a maximum length of 128 characters.

        .. _`RFC7636 Section 4.1`: https://tools.ietf.org/html/rfc7636#section-4.1
        """
        code_verifier = None

        if not length >= 43:
            raise ValueError("Length must be greater than or equal to 43")

        if not length <= 128:
            raise ValueError("Length must be less than or equal to 128")

        allowed_characters = re.compile('^[A-Zaa-z0-9-._~]')
        code_verifier = secrets.token_urlsafe(length)

        if not re.search(allowed_characters, code_verifier):
            raise ValueError("code_verifier contains invalid characters")

        self.code_verifier = code_verifier

        return code_verifier

    def create_code_challenge(self, code_verifier, code_challenge_method=None):
        """Create PKCE **code_challenge** derived from the  **code_verifier**.
        See `RFC7636 Section 4.2`_

        :param code_verifier: REQUIRED. The **code_verifier** generated from `create_code_verifier()`.
        :param code_challenge_method: OPTIONAL. The method used to derive the **code_challenge**. Acceptable values include `S256`. DEFAULT is `plain`.

               The client then creates a code challenge derived from the code
               verifier by using one of the following transformations on the code
               verifier::

                   plain
                      code_challenge = code_verifier
                   S256
                      code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

               If the client is capable of using `S256`, it MUST use `S256`, as
               `S256` is Mandatory To Implement (MTI) on the server.  Clients are
               permitted to use `plain` only if they cannot support `S256` for some
               technical reason and know via out-of-band configuration that the
               server supports `plain`.

               The plain transformation is for compatibility with existing
               deployments and for constrained environments that can't use the S256 transformation.

        .. _`RFC7636 Section 4.2`: https://tools.ietf.org/html/rfc7636#section-4.2
        """
        code_challenge = None

        if code_verifier is None:
            raise ValueError("Invalid code_verifier")

        if code_challenge_method is None:
            code_challenge_method = "plain"
            self.code_challenge_method = code_challenge_method
            code_challenge = code_verifier
            self.code_challenge = code_challenge

        if code_challenge_method == "S256":
            h = hashlib.sha256()
            h.update(code_verifier.encode(encoding='ascii'))
            sha256_val = h.digest()
            code_challenge = bytes.decode(base64.urlsafe_b64encode(sha256_val))
            # replace '+' with '-', '/' with '_', and remove trailing '='
            code_challenge = code_challenge.replace("+", "-").replace("/", "_").replace("=", "")
            self.code_challenge = code_challenge

        return code_challenge

    def _add_mac_token(self, uri, http_method='GET', body=None,
                       headers=None, token_placement=AUTH_HEADER, ext=None, **kwargs):
        """Add a MAC token to the request authorization header.

        Warning: MAC token support is experimental as the spec is not yet stable.
        """
        if token_placement != AUTH_HEADER:
            raise ValueError("Invalid token placement.")

        headers = tokens.prepare_mac_header(self.access_token, uri,
                                            self.mac_key, http_method, headers=headers, body=body, ext=ext,
                                            hash_algorithm=self.mac_algorithm, **kwargs)
        return uri, headers, body

    def _populate_attributes(self, response):
        warnings.warn("Please switch to the public method "
                      "populate_token_attributes.", DeprecationWarning)
        return self.populate_token_attributes(response)

    def populate_code_attributes(self, response):
        """Add attributes from an auth code response to self."""

        if 'code' in response:
            self.code = response.get('code')

    def populate_token_attributes(self, response):
        """Add attributes from a token exchange response to self."""

        if 'access_token' in response:
            self.access_token = response.get('access_token')

        if 'refresh_token' in response:
            self.refresh_token = response.get('refresh_token')

        if 'token_type' in response:
            self.token_type = response.get('token_type')

        if 'expires_in' in response:
            self.expires_in = response.get('expires_in')
            self._expires_at = round(time.time()) + int(self.expires_in)

        if 'expires_at' in response:
            try:
                self._expires_at = round(float(response.get('expires_at')))
            except:
                self._expires_at = None

        if 'mac_key' in response:
            self.mac_key = response.get('mac_key')

        if 'mac_algorithm' in response:
            self.mac_algorithm = response.get('mac_algorithm')


import cloudkeeper.logging
import threading
from datetime import datetime, timezone
from cloudkeeper.graph import Graph
from cloudkeeper.baseplugin import BasePlugin
from cloudkeeper.baseresources import BaseCloud, BaseAccount, BaseRegion
from cloudkeeper_plugin_aws.resources import (
    AWSALBTargetGroup,
    AWSEC2KeyPair,
    AWSEC2NetworkAcl,
    AWSVPC,
)
from cloudkeeper.paralleltagger import ParallelTagger
from cloudkeeper.args import ArgumentParser
from cloudkeeper.event import (
    Event,
    EventType,
    add_event_listener,
    remove_event_listener,
)
from prometheus_client import Summary, Counter

log = cloudkeeper.logging.getLogger("cloudkeeper." + __name__)

metrics_ctime_tags = Counter(
    "cloudkeeper_plugin_tag_aws_ctime_tags_total",
    "Tag AWS ctime Plugin Number of ctime tags applied",
    ["cloud", "account", "region"],
)
metrics_tag_ctime = Summary(
    "cloudkeeper_plugin_tag_aws_ctime_tag_ctime_seconds",
    "Tag AWS ctime Plugin Time it took the tag_ctime() method",
)

class TagAWSCtimePlugin(BasePlugin):
    def __init__(self):
        super().__init__()
        self.name = "tag_aws_ctime"
        self.exit = threading.Event()
        self.run_lock = threading.Lock()
        if ArgumentParser.args.tag_aws_ctime:
            log.debug("AWS ctime Tagger plugin initializing")
            add_event_listener(EventType.SHUTDOWN, self.shutdown)
            add_event_listener(
                EventType.COLLECT_FINISH,
                self.aws_ctime_tagger,
                blocking=False,
                timeout=900,
            )
        else:
            self.exit.set()

    def __del__(self):
        remove_event_listener(EventType.COLLECT_FINISH, self.aws_ctime_tagger)
        remove_event_listener(EventType.SHUTDOWN, self.shutdown)

    def go(self):
        self.exit.wait()

    def aws_ctime_tagger(self, event: Event):
        if not self.run_lock.acquire(blocking=False):
            log.error("AWS ctime Tagger is already running")
            return

        graph = event.data
        log.info("AWS ctime Tagger called")
        try:
            self.tag_ctime(graph)
        except Exception:
            raise
        finally:
            self.run_lock.release()

    @metrics_tag_ctime.time()
    def tag_ctime(self, graph: Graph):
        now = datetime.utcnow().replace(tzinfo=timezone.utc).isoformat()
        pt = ParallelTagger(self.name)
        with graph.lock.read_access:
            for node in graph.nodes:
                if not isinstance(
                    node, (AWSALBTargetGroup, AWSEC2NetworkAcl, AWSEC2KeyPair, AWSVPC)
                ):
                    continue

                if "cloudkeeper:ctime" not in node.tags:
                    cloud = node.cloud(graph)
                    account = node.account(graph)
                    region = node.region(graph)
                    if (
                        not isinstance(cloud, BaseCloud)
                        or not isinstance(account, BaseAccount)
                        or not isinstance(region, BaseRegion)
                    ):
                        log.error(
                            (
                                f"Resource {node.resource_type} {node.dname} has no valid "
                                "cloud, account or region associated with it"
                            )
                        )
                        continue

                    log.debug(
                        (
                            f"Resource {node.resource_type} {node.dname} in cloud {cloud.name} account "
                            f"{account.dname} region {region.name} has no cloudkeeper:ctime tag "
                            f"- setting it because ctime is not available via the AWS API"
                        )
                    )
                    pt_key = f"{cloud.id}-{account.id}-{region.id}"
                    pt.add(node, "cloudkeeper:ctime", now, pt_key)
                    metrics_ctime_tags.labels(
                        cloud=cloud.name, account=account.dname, region=region.name
                    ).inc()
        pt.run()

    @staticmethod
    def add_args(arg_parser: ArgumentParser) -> None:
        arg_parser.add_argument(
            "--tag-aws-ctime",
            help="Tag AWS ctime (default: False)",
            dest="tag_aws_ctime",
            action="store_true",
            default=False,
        )

    def shutdown(self, event: Event):
        log.debug(
            f"Received event {event.event_type} - shutting down AWS ctime tagging plugin"
        )
        self.exit.set()


import os
import sys
import time
import numpy as np
import imgaug

from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
from pycocotools import mask as maskUtils

# Root directory of the project
ROOT_DIR = os.path.abspath("Mask_RCNN/")

# Import Mask RCNN
sys.path.append(ROOT_DIR)  # To find local version of the library
from mrcnn.config import Config
from mrcnn import model as modellib, utils

class DatasetConfig(Config):
    NAME = "hake"
    IMAGES_PER_GPU = 2
    NUM_CLASSES = 1 + 1
    STEPS_PER_EPOCH = 100
    VALIDATION_STEPS = 1

class InferenceConfig(DatasetConfig):
    GPU_COUNT = 1
    IMAGES_PER_GPU = 1

class Dataset(utils.Dataset):
    def load_dataset(self, image_path=None, annotations_file=None):
        coco = COCO(annotations_file)

        class_ids = sorted(coco.getCatIds())
        image_ids = list(coco.imgs.keys())

        # Add classes
        for i in class_ids:
            self.add_class("coco", i, coco.loadCats(i)[0]["name"])

        # Add images
        for i in image_ids:
            self.add_image(
                "coco", image_id=i,
                path=os.path.join(image_path, coco.imgs[i]['file_name']),
                width=coco.imgs[i]["width"],
                height=coco.imgs[i]["height"],
                annotations=coco.loadAnns(coco.getAnnIds(imgIds=[i], catIds=class_ids, iscrowd=None)))

    def load_mask(self, image_id):
        image_info = self.image_info[image_id]

        instance_masks = []
        class_ids = []
        annotations = self.image_info[image_id]["annotations"]
        # Build mask of shape [height, width, instance_count] and list
        # of class IDs that correspond to each channel of the mask.
        for annotation in annotations:
            class_id = self.map_source_class_id(
                "coco.{}".format(annotation['category_id']))
            if class_id:
                m = self.annToMask(annotation, image_info["height"],
                                   image_info["width"])
                # Some objects are so small that they're less than 1 pixel area
                # and end up rounded out. Skip those objects.
                if m.max() < 1:
                    continue
                # Is it a crowd? If so, use a negative class ID.
                if annotation['iscrowd']:
                    # Use negative class ID for crowds
                    class_id *= -1
                    # For crowd masks, annToMask() sometimes returns a mask
                    # smaller than the given dimensions. If so, resize it.
                    if m.shape[0] != image_info["height"] or m.shape[1] != image_info["width"]:
                        m = np.ones([image_info["height"], image_info["width"]], dtype=bool)
                instance_masks.append(m)
                class_ids.append(class_id)

        # Pack instance masks into an array
        if class_ids:
            mask = np.stack(instance_masks, axis=2).astype(np.bool)
            class_ids = np.array(class_ids, dtype=np.int32)
            return mask, class_ids
        else:
            # Call super class to return an empty mask
            return super(Dataset, self).load_mask(image_id)

    def image_reference(self, image_id):
        super(Dataset, self).image_reference(image_id)

    # The following two functions are from pycocotools with a few changes.

    def annToRLE(self, ann, height, width):
        """
        Convert annotation which can be polygons, uncompressed RLE to RLE.
        :return: binary mask (numpy 2D array)
        """
        segm = ann['segmentation']
        if isinstance(segm, list):
            # polygon -- a single object might consist of multiple parts
            # we merge all parts into one mask rle code
            rles = maskUtils.frPyObjects(segm, height, width)
            rle = maskUtils.merge(rles)
        elif isinstance(segm['counts'], list):
            # uncompressed RLE
            rle = maskUtils.frPyObjects(segm, height, width)
        else:
            # rle
            rle = ann['segmentation']
        return rle

    def annToMask(self, ann, height, width):
        """
        Convert annotation which can be polygons, uncompressed RLE, or RLE to binary mask.
        :return: binary mask (numpy 2D array)
        """
        rle = self.annToRLE(ann, height, width)
        m = maskUtils.decode(rle)
        return m


from .models import User, Store, Review, Review_img, Tag, Menu, Bhour, Amenity, Like_store
from .serializers import UserSerializer, StoreSerializer, ReviewSerializer, ReviewImgSerializer, TagSerializer, MenuSerializer, BhourSerializer
from django.http import Http404
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.decorators import api_view

from django.db.models import Q
from django.db.models import Avg
from django.db.models import Subquery
from django.db.models import functions
from django.db.models import Count

from django.core.paginator import Paginator

from rest_framework.decorators import api_view

from api import models, serializers
from rest_framework import viewsets
from rest_framework.pagination import PageNumberPagination
from rest_framework.decorators import action

import json
import numbers
import math
import time
import random

# 음식 뭐먹을지 서베이 끝났을 때
@api_view(['GET', 'POST'])
def SurveySearch(request):
    if request.method == 'GET':
        print("==========================================")
        print("SurveySearch GET!!")
        survey = request.query_params.get("data", "")
        print(survey)

        food = dict()
        food[0] = ["훠거","마라탕","육개장","샤브샤브","탄탄면","파스타","닭도리탕"]
        food[1] =["김치찌개","부대찌개","라멘","탄탄면","만두","막국수","훠궈","마라탕","돈까스","카레"]
        food[2] =["불족발","닭발","꼬치","닭갈비","마라샹궈","등갈비","깐풍기","매운갈비찜","찜닭"]
        food[3] =["김치볶음밥","닭꼬치","라자냐","피자","돈까스","불족발","찜닭","꼬치","닭갈비","마라샹궈","등갈비","깐풍기","매운갈비찜"]
        food[4] =["감자탕","샤브샤브","치킨","뼈해장국","나베","샤브샤브","우육면","어묵","설렁탕"]
        food[5] =["백숙","불고기","갈비","만두국","설렁탕","순대국","해장국","쌀국수","라멘","우육면","갈비탕"]
        food[6] = ["육회","곱창","등갈비","삼겹살","대패삼겹살","막창","족발","보쌈","치킨","양꼬치","버거","순대","탕수육","양고기","슈바인학센","양갈비","갈비","어향가지","지삼선"]
        food[7] =["치킨","짜장면","만두","볶음밥","스테이크","오리고기","순대국","리조또","갈비","피자","샤오룽바오","돈까스","버거"]
        food[8] = ["해물탕","매운탕","알탕","갈치찜","고등어조림","라멘","떡볶이","짬뽕","비빔냉면","파스타","라면","커리","파스타"]
        food[9] = ["떡볶이","짬뽕","비빔냉면","커리","파스타","라면","해물탕","매운탕","알탕","갈치찜","고등어조림","라멘"]
        food[10] =   ["회덮밥","쭈꾸미","골뱅이소면","오삼불고기","아구찜","해물찜","깐쇼새우","타코야키"]
        food[11] =  ["돈까스","김치만두","비빔면","파스타","타코야끼","떡볶이","쫄면","깐쇼새우"]
        food[12] = ["조개탕","나베","감바스","연포탕","샤브샤브","미역국"]
        food[13] =  ["쌀국수","된장찌개","김치찌개","추어탕","돈까스","파스타","냉면","똠양꿍","우동","파스타"]
        food[14] =  ["연어","회","해물파전","모둠전","꽃새우","모둠전","삼합","참치","대하","교자","푸빳뽕커리","마파두부","연어숙회"]
        food[15] =  ["짜장면","월남쌈","순대","김밥","만두","오믈렛","타코야키","반비","잡채","간장새우",'팟타이',"파스타","샌드위치","샐러드","토스트","뇨끼","주먹밥","나시고랭","연어","회","버거"]
        index = int(survey, 2)
        return Response(random.sample(food[index], 5))

class StoreSearch(APIView):
    # Store 검색을 위한 클래스
    '''
    # Store 검색
    '''

    def get(self, request, word, format=None):
        print("==========================================")
        print("StoreSearch Total GET!")
        word = word.split(" ")

        used = set()
        tag_list = set()
        tag = Tag.objects.all()
        for w in word:
            for t in tag:
                if w in t.tag:
                    tag_list.add(t.store_id)
                    used.add(w)

        amenity_list = set()
        amenity = Amenity.objects.all()
        for w in word:
            for a in amenity:
                if w in a.amenity:
                    amenity_list.add(a.store_id)
                    used.add(w)

        tmp_list = list(tag_list | amenity_list)
        store1 = Store.objects.all().filter(id__in=tmp_list)

        for w in set(word) - set(used):
            store1 = store1.filter((
                Q(area__contains=w) | Q(address__contains=w)
                | Q(store_name__contains=w) | Q(category__contains=w)))

        store2 = Store.objects.all()
        for w in word:
            store2 = store2.filter((
                Q(area__contains=w) | Q(address__contains=w)
                | Q(store_name__contains=w) | Q(category__contains=w)))

        queryset = store1 | store2
        serializer = StoreSerializer(store1 | store2, many=True)
        return Response(serializer.data)

@api_view(['GET', 'POST'])
def SurveyType(request):
    if request.method == 'GET':
        print("get 시작")
        surveyArr = request.query_params.get("data", "")
        print(surveyArr)
        food_table = [["불고기", "간장게장", "닭볶음탕", "비빔밥", "잡채"],
                      ["스테이크", "감바스", "로스티드 터키", "시저샐러드", "햄버거"],
                      ["탕수육", "유산슬", "깐풍기", "어향가지", "잡탕밥"],
                      ["규카츠", "초밥", "오야꼬동", "야채고로케", "라멘"],
                      ["분짜", "푸팟뽕커리", "탄두리치킨", "월남쌈", "반미"],
                      ["순대", "어묵", "닭꼬치", "야채튀김", "김밥"],
                      ]

        adjective_table = ["레시피보고 만든", "엄마가 만들어 준", "둘이 먹다 셋이 죽는", "또 먹고 싶은",
                           "지옥에서 온", "눈물 젖은", "심혈을 기울여 만든", "죽기 전에 마지막으로 먹고 싶은",
                           "먹다 남긴", "줘도 안먹는", "실수로 소금을 쏟은", "까맣게 타버린",
                           "어쩌다 이렇게 된", "수줍게 선보인 나의", "혀 끝을 휘감는", "장인이 만든"]

        taste_table = ["쓴 맛 나는 ", "싱거운", "기름진", "촉촉한",
                       "짠", "달달한", "매콤한", "떫은",
                       "고소한", "새콤한", "칼칼한", "오묘한",
                       "맛없는", "맛있는", "눅눅한", "바삭한"]

        food = food_table[int(surveyArr[1])][int(surveyArr[4])]
        adjective = adjective_table[int(surveyArr[5:9], 2)]

        index = "" + str(format(int(surveyArr[0]), 'b')) + str(
            format(int(surveyArr[2]), 'b')) + str(format(int(surveyArr[3]), 'b'))
        index = int(index, 2)
        taste = taste_table[index]

        ret = [adjective, taste, food]
        print(ret)
        return Response(ret)

class LodationBased(APIView):
    # 강남역   127.02758, 37.49794

    def get(self, request, latitude, longitude, km, format=None):
        print("==========================================")
        print("Location Based Recommendation GET!")
        print("latitude: "+latitude+" longitude: " + longitude + ", km="+km)
        start = time.time()
        latitude = float(latitude)
        longitude = float(longitude)
        km = float(km)

        # print(type(latitude))
        # print(latitude)

        # print(type(longitude))
        # print(longitude)

        store = Store.objects.all()
        review_object = Review.objects.values(
            "store_id").annotate(dcount=Count('store_id')).order_by('-dcount').values_list('store_id', flat=True)
        store_list = [r for r in review_object]

        store = Store.objects.all().filter(id__in=store_list)
        # store = sorted(store, key=lambda s: store_list.index(s.id))

        ret = []
        for s in store.iterator():
            if s.longitude is None:
                continue
            if s.longitude == 0:
                continue
            if km >= GeoUtil.get_harversion_distance(
                    longitude, latitude, float(s.longitude), float(s.latitude)):
                ret.append(s)
        print("time :", time.time() - start)  # 현재시각 - 시작시간 = 실행 시간
        print(len(ret))

        num_store = len(ret)

        # 페이징 적용
        paginator = Paginator(ret, 15)
        num_page = paginator.num_pages
        page = request.GET.get('page')
        pagestore = paginator.get_page(page)
        serializer = StoreSerializer(pagestore, many=True)

        result = serializer.data
        user_id = request.GET.get('user_id')
        like = 0
        if user_id is not "":
            for r in result:
                like = Like_store.objects.filter(
                    store_id=r['id'], user_id=user_id).count()
                r['like'] = like
        else:
            for r in result:
                r['like'] = like

        return Response({
            'num_store': num_store,
            'num_page': num_page,
            'data': serializer.data
        })

class LodationBasedLikeList(APIView):
    def get(self, request, user_id, latitude, longitude, km, format=None):
        print("==========================================")
        print("Location Based Recommendation GET!")
        print("user_id:"+user_id+", latitude: "+latitude +
              " longitude: " + longitude + ", km="+km)
        latitude = float(latitude)
        longitude = float(longitude)
        km = float(km)
        store_object = Store.objects.extra(tables=['api_like_store'], where=[
            'api_store.id=api_like_store.store_id', "api_like_store.user_id="+user_id])

        store_list = [s.id for s in store_object]

        store = Store.objects.all().filter(id__in=store_list)
        ret = []
        for s in store:
            if s.longitude is None:
                continue
            if s.longitude == 0:
                continue
            print(s.longitude)
            if km >= GeoUtil.get_harversion_distance(
                    longitude, latitude, float(s.longitude), float(s.latitude)):
                ret.append(s)
        print(ret)
        print("==========================================")

        num_store = len(ret)

        # 페이징 적용
        paginator = Paginator(ret, 15)
        num_page = paginator.num_pages
        page = request.GET.get('page')
        pagestore = paginator.get_page(page)
        serializer = StoreSerializer(pagestore, many=True)

        result = serializer.data
        user_id = request.GET.get('user_id')
        like = 0
        if user_id is not "":
            for r in result:
                like = Like_store.objects.filter(
                    store_id=r['id'], user_id=user_id).count()
                r['like'] = like
        else:
            for r in result:
                r['like'] = like

        return Response({
            'num_store': num_store,
            'num_page': num_page,
            'data': serializer.data
        })

class GeoUtil:
    """
    Geographical Utils
    """

    @staticmethod
    def degree2radius(degree):
        return degree * (math.pi/180)

    # 실제 km를 반환
    @staticmethod
    def get_harversion_distance(x1, y1, x2, y2, round_decimal_digits=5):
        """
        경위도 (x1,y1)과 (x2,y2) 점의 거리를 반환
        Harversion Formula 이용하여 2개의 경위도간 거래를 구함(단위:Km)
        """
        if x1 is None or y1 is None or x2 is None or y2 is None:
            return None

        # print("x1="+str(x1)+", y1="+str(y1)+", x2="+str(x2)+", y2="+str(y2))
        # longitude(경도) 범위
        assert isinstance(x1, numbers.Number) and -180 <= x1 and x1 <= 180
        # latitude(위도) 범위
        assert isinstance(y1, numbers.Number) and -90 <= y1 and y1 <= 90
        assert isinstance(x2, numbers.Number) and -180 <= x2 and x2 <= 180
        assert isinstance(y2, numbers.Number) and -90 <= y2 and y2 <= 90

        R = 6371  # 지구의 반경(단위: km)
        dLon = GeoUtil.degree2radius(x2-x1)
        dLat = GeoUtil.degree2radius(y2-y1)

        a = math.sin(dLat/2) * math.sin(dLat/2) \
            + (math.cos(GeoUtil.degree2radius(y1))
               * math.cos(GeoUtil.degree2radius(y2))
               * math.sin(dLon/2) * math.sin(dLon/2))
        b = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
        return round(R * b, round_decimal_digits)


import cv2
import numpy as np

img = cv2.imread('spider.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(gray,100,200)
contours,hierarchy = cv2.findContours(canny,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

img_contours = cv2.drawContours(img,contours,-1,(0,255,0),3)

cv2.imshow('original',img)
cv2.imshow('canny',canny)
cv2.imshow('contours',img_contours)
cv2.waitKey(0)
cv2.destroyAllWindows()


"""
 demo1_mpi.py
 a parallelized Gaussian Process regression example

 author: Nick Hand
 contact: nhand@berkeley.edu
 creation date: 05/14/2013
"""
import pylab as pl
import numpy as np
import argparse
import sys
sys.path.append("..")
from gaussian_process_mpi import GaussianProcessMPI
from mpi4py import MPI

def run_demo(args):
    """
    @brief a Gaussian Process regression example using an input covariance
    model. The demo divides the observations along the x-axis and computes in
    parallel using mpi4py
    """

    np.random.seed(1)

    def f(x):
        """
        @brief the function to predict.
        """
        return x * np.sin(x)

    # the inpute data points
    X = np.linspace(0.1, 9.9, 500)

    # make the observations with added noise
    y = f(X).ravel()
    dy = 0.5 + 1.0 * np.random.random(y.shape)
    noise = np.random.normal(0, dy)
    y += noise

    # mesh the input space for evaluations of the prediction
    x = np.linspace(-2, 12, 2*len(X))

    # instanciate a Gaussian Process model, allowing all params to vary
    gp = GaussianProcessMPI(theta0 = [0.5, 2.0, 1.0],
                         covfunction=args.covariance, verbose=True,
                         fixed=[False, False, False])

    # fit to data using Maximum Likelihood Estimation of the parameters
    gp.fit(X, y, dy)

    # make the prediction on the meshed x-axis
    y_pred, sigma = gp.predict(x)

    if MPI.COMM_WORLD.Get_rank() == 0:

        # plot the function, the prediction and the 95% confidence interval based on
        # the standard deviation
        fig = pl.figure()

        pl.plot(x, f(x), 'r:', label=r'$f(x) = x \ \mathrm{sin}(x)$')
        pl.errorbar(X.ravel(), y, dy, label='Observations')
        pl.plot(x, y_pred, label='Prediction')
        pl.fill(np.concatenate([x, x[::-1]]),
                np.concatenate([y_pred - 1.9600 * sigma,
                               (y_pred + 1.9600 * sigma)[::-1]]),
                alpha=.2, fc='DarkGoldenRod', ec="None", label='95% confidence interval')

        pl.xlabel('$x$', fontsize=16)
        pl.ylabel('$f(x)$', fontsize=16)
        pl.ylim(-15, 20)
        pl.legend(loc='upper left')

        pl.show()
#end run_demo

#-------------------------------------------------------------------------------
if __name__ == "__main__":

    # parse the command line arguments
    parser = argparse.ArgumentParser(description="example 1 of a Gaussian "
                                     "Process regression")
    parser.add_argument("--covariance", type=str,
                        choices=['squared_exponential',
                        'matern32', 'matern52'],
                        default='squared_exponential',
                        help="the covariance model to use")
    args = parser.parse_args()

    # run the demo
    run_demo(args)


from flask import request, redirect, render_template, abort, jsonify, g
from treasure.app import app
from treasure.collection.models import Category, Treasure, Photo

def treasures(id=None):
    cotegories = Category.query.all()
    qs = Treasure.query.filter_by(pick=True).\
        order_by(Treasure.position, Treasure.created_at)
    try:
        page = int(request.args.get('page', 1))
    except ValueError:
        page = 1
    if id is None:
        qs = qs
    else:
        category = Category.query.get(id)
        try:
            qs = category.treasure.filter()
        except Exception as e:
            app.logger.error(e)
            return redirect('treasures')
    treasures = qs.paginate(page, 20, False)
    return render_template('treasures.html',
                           cotegories=cotegories,
                           treasures=treasures,
                           id=id or None,
                           page=page,
                           count=qs.count())

def treasure(id=None):
    if id is not None:
        cotegories = Category.query.all()
        treasure = Treasure.query.get_or_404(id)
        photos = treasure.photo.all()
        return render_template('treasure.html',
                               cotegories=cotegories,
                               treasure=treasure,
                               photos=photos,
                               id=id,
                               user=g.user)
    abort(404)

def get_one_pic(id=None):
    try:
        photo = Photo.query.get(id)
    except Exception:
        photo = None
    if not photo:
        rv = jsonify(ok=False, data={})
    else:
        rv = jsonify(ok=True, data=photo._json)
    return rv