import copy
import math
import operator
import sys
import random
import statistics
import time
import timeit

from matplotlib import pyplot as plt
import numpy as np
from numpy import cumsum, resize
from numpy.random import randint

GA_POPSIZE = 2048  # genome population size
GA_MAXITER = 16384  # maximum iterations (generations)
GA_ELITRATE = 0.1  # elitism rate
GA_MUTATIONRATE = 0.25  # mutation rate
GA_MUTATION = sys.maxsize * GA_MUTATIONRATE
QUEENS = 8
e = 2.71828182846

class GA_struct:

    # Citizens of our Population
    def __init__(self, string, fitness):
        self.string = string
        self.fitness = fitness
        self.age = 0
        self.species = -1

class GeneticAlgorithm:
    def initPop(self, population, buffer):
        # initialize population
        for i in range(GA_POPSIZE):
            randArray = [randint(0, QUEENS - 1) for i in range(QUEENS)]  # random numbers array
            x = GA_struct(randArray, 0)
            population[i] = x
            #self.buffer.append(GA_struct([], 0))

        return population

    def calc_fitness_queens(self, population):
        # num of queens that collide
        for agent in population:
            count = [0] * QUEENS
            bad_count = 0
            for j in agent.string:  # count row collisions
                count[int(j)] += 1
            for x in count:
                if x != 1:  # if collide
                    bad_count += 1
            for i in range(QUEENS):
                for j in range(QUEENS):
                    if i == j:
                        continue
                    if abs(i - j) == abs(int(agent.string[i]) - int(agent.string[j])):
                        bad_count += 1

            agent.fitness = bad_count

    def elitism(self, population, buffer, esize):
        temp = population[:esize].copy()
        buffer[:esize] = temp
        return

    def mutate(self, agent):
        # to mutate we choose a position randomly and change it to a random character
        target_size = QUEENS
        ipos = randint(0, target_size - 1)
        delta = randint(0, QUEENS - 1)
        agent.string = agent.string[:ipos] + [(agent.string[ipos] + delta) % QUEENS] + agent.string[ipos + 1:]

    def get_two_random_parents_with_same_species(self, population, species):
        count = 0
        genomes = []
        for i in range(len(population)):
            if population[i].species == species:
                genomes.append(population[i])
                count += 1
        r1 = random.randrange(0, count)
        r2 = random.randrange(0, count)
        return genomes[r1], genomes[r2]

    def get_num_of_species(self, population):
        species = []
        for i in range(len(population)):
            found = 0
            for j in range(len(species)):
                if population[i].species == species[j]:
                    found = 1
            if found == 0:
                species.append(population[i].species)
        return len(species)

    def get_num_of_genomes_in_species(self, population, species):  # afkr fsh 7aji
        count = 0
        for i in range(len(population)):
            if population[i].species == species:
                count += 1
        return count

    def mate(self, population, buffer, cross_over_type, selection_method=None, probabilities=None):
        esize = int(GA_POPSIZE * GA_ELITRATE)
        target_size = QUEENS
        self.elitism(population, buffer, esize)
        # we choose the parents depending on the method specified by the given input "selection_method"
        # and to each pair of parents we do a cross over according to the given input "cross_over_type"
        parents_idx = 0
        if selection_method == "sus":
            parents = self.sus(population, GA_POPSIZE - esize)

        # mate the rest
        for i in range(esize, GA_POPSIZE):

            if selection_method == None:
                i1 = random.randrange(0, GA_POPSIZE // 2)
                i2 = random.randrange(0, GA_POPSIZE // 2)

            if i != (GA_POPSIZE - 1) and selection_method == "sus":
                i1 = parents[parents_idx]
                i2 = parents[parents_idx + 1]
                parents_idx += 1

            if selection_method == "threshold":
                num_of_species = self.get_num_of_species(population)
                s = random.randrange(0, num_of_species)
                i1, i2 = self.get_two_random_parents_with_same_species(population, s)

            if selection_method == "rws":  # we choose the parents according to the RWS algo we learned
                i1 = self.rws(population, probabilities)
                i2 = self.rws(population, probabilities)

            if selection_method == "tournament":  # we choose the parents according to the tournament selection algo we learned
                k = 3
                i1, i2 = self.tournament_selection(population, k)

            # we choose the parents according to the minimal conflict heuristic
            if selection_method == "min_conf" and i<GA_POPSIZE-1:
                i1 = self.miniConf(population[i].string, QUEENS, iters=100)
                i2 = self.miniConf(population[i+1].string, QUEENS, iters=100)

            if cross_over_type == "SINGLE":
                pos = random.randrange(1, target_size)
                if selection_method == "min_conf":
                    buffer[i] = GA_struct(i1[0: pos] + i2[pos:], 0)

                elif selection_method != None and selection_method != "min_conf":
                    buffer[i] = GA_struct(i1.string[0: pos] + i2.string[pos:], 0)
                else:
                    buffer[i] = GA_struct(population[i1].string[0: pos] + population[i2].string[pos:], 0)

            elif cross_over_type == "DOUBLE":
                pos1 = random.randrange(0, target_size - 2)
                pos2 = random.randrange(pos1 + 1, target_size - 1)
                if selection_method == "min_conf":
                    buffer[i] = GA_struct(i1[0: pos1] + i2[pos1:pos2] + i1[pos2:], 0)

                elif selection_method != None and selection_method != "min_conf":
                    buffer[i] = GA_struct(i1.string[0: pos1] + i2.string[pos1:pos2] + i1.string[pos2:], 0)
                else:
                    buffer[i] = GA_struct(
                        population[i1].string[0: pos1] + population[i2].string[pos1:pos2] + population[i1].string[pos2:], 0)

            elif cross_over_type == "UNIFORM":
                gen = []
                for j in range(target_size):
                    r = random.randrange(0, 2)
                    if r == 0:
                        if selection_method == "min_conf":
                            gen.append(i1[j])

                        elif selection_method != None and selection_method != "min_conf":
                            gen.append(i1.string[j])
                        else:
                            gen.append(population[i1].string[j])

                    else:
                        if selection_method == "min_conf":
                            gen.append(i2[j])

                        elif selection_method != None and selection_method != "min_conf":
                            gen.append(i2.string[j])
                        else:
                            gen.append(population[i2].string[j])
                buffer[i] = GA_struct(gen, 0)

            if random.randrange(sys.maxsize) < GA_MUTATION:
                self.mutate(buffer[i])

        return buffer

    def calcAVG(self):
        # calculates and returns the average fitness of the population
        sum = 0

        for i in range(len(population)):
            sum += population[i].fitness

        return sum / GA_POPSIZE

    def calcStd(self):
        # calculates and returns the STD of the population
        fitness = []

        for i in range(len(population)):
            fitness.append(population[i].fitness)

        return statistics.stdev(fitness)

    def rws(self, population, probabilities):
        # Roulette wheel selection algorithm
        # implements the pseudocode we saw in class
        rndNumber = random.random()
        offset = 0.0
        for i in range(GA_POPSIZE):
            offset += probabilities[i]
            if rndNumber < offset:
                return population[i]

    def get_subset_sum(self, population, index):
        sum = 0
        for i in range(index):
            sum += population[i].fitness
        return sum

    def sus(self, population, N):
        # Stochastic universal sampling
        sum = 0
        for i in range(len(population)):
            sum += 1 / population[i].fitness if population[i].fitness else 0
        point_distance = sum / N
        start_point = random.uniform(0, point_distance)
        points = [start_point + i * point_distance for i in range(N)]
        parents = set()
        while len(parents) < N:
            random.shuffle(population)
            i = 0
            while i < len(points) and len(parents) < N:
                j = 0
                while j < len(population):
                    if self.get_subset_sum(population, j) < points[i]:
                        parents.add(population[j])
                        break
                    j += 1
                i += 1

        return list(parents)

    def tournament_selection(self, population, k):
        # we randomly choose a sample from the population with size k and choose the two best ones
        sample = []
        for i in range(k):
            sample.append(population[random.randrange(0, len(population) - 1)])

        sample.sort(key=lambda x: x.fitness)
        return sample[0], sample[1]

    @staticmethod
    def positive_random(rng, conflicts, filter):
        return random.choice([i for i in range(rng) if filter(conflicts[i])])

    def init_roulette(self, population):
        # build the roulette with the probabilities being according to the fitnesses
        problem.calc_fitness_queens(population)
        probs = []
        total_fitness = 0
        for i in range(GA_POPSIZE):
            total_fitness += population[i].fitness
        for i in range(GA_POPSIZE):
            probs.append(population[i].fitness / total_fitness)
        return probs

    def random_position(self, li, filter, num_rows):
        return random.choice([i for i in range(num_rows) if filter(li[i])])

    def miniConf(self, solution, num_rows, iters=100):
        # the minimal conflict heuristic chooses solution that minimizes the conflicts(collisions)
        for k in range(iters):
            confs = self.find_conflicts(solution, num_rows)
            if sum(confs) == 0:
                return solution
            col = self.random_position(confs, lambda elt: elt > 0, num_rows)
            vconfs = [self.hits(solution, num_rows, col, row) for row in range(num_rows)]
            solution[col] = self.random_position(vconfs, lambda elt: elt == min(vconfs), num_rows)

        return solution

    def find_conflicts(self, solution, num_rows):
        res = []
        for row in range(num_rows):
            res.append(self.hits(solution, num_rows, row, solution[row]))
        return res

    def hits(self, solution, num_rows, col, row):
        total = 0
        for i in range(num_rows):
            if i == col:
                continue
            if solution[i] == row or abs(i - col) == abs(solution[i] - row):
                total += 1
        return total

    def kendall_tau_distance(self, values1, values2):
        """Compute the Kendall tau distance."""
        n = len(values1)
        assert len(values2) == n, "Both lists have to be of equal length"
        i, j = np.meshgrid(np.arange(n), np.arange(n))
        a = np.argsort(values1)
        b = np.argsort(values2)
        ndisordered = np.logical_or(np.logical_and(a[i] < a[j], b[i] > b[j]),
                                    np.logical_and(a[i] > a[j], b[i] < b[j])).sum()
        return ndisordered / 2

    def calculate_distance(self, first, second):

        diff = problem.kendall_tau_distance(first.permutation, second.permutation)

        return diff

    def get_num_of_genomes_with_fitness(self, population, fitness):
        i = 0
        num = 0
        for i in range(len(population)):
            if population[i].fitness == fitness:
                num += 1
                i += 1

        return num

    def calculate_genetic_diversity(self, population):
        ans = 0
        for i in range(len(population)):

            for j in range(len(population)):
                ans += self.calculate_distance(population[i], population[j])
        return ans / len(population)

    def threshold(self, population):
        threshold = GA_POPSIZE * 0.3
        speciation = [[]] * 30
        num_of_species = 30
        for i in range(len(population)):
            check = 0
            for j in range(len(speciation)):

                for x in range(len(speciation[j])):

                    if self.calculate_distance(population[i], speciation[j][x]) <= threshold:
                        check += 1

                if check == len(speciation[j]):
                    speciation[j].append(population[i])
                    population[i].species = j

            if population[i].species == -1:
                population[i].species = num_of_species
                num_of_species += 1
                speciation.append([population[i]])

if __name__ == "__main__":

    problem = GeneticAlgorithm()
    random.seed()
    pop_alpha = [None] * GA_POPSIZE
    pop_beta = [None] * GA_POPSIZE

    population = problem.initPop(pop_alpha, pop_beta)  #initialize the population

    buffer = pop_beta
    start_t = time.time()  # starting time

    probabilities = problem.init_roulette(population)

    generation_num = 0

    for i in range(GA_MAXITER):

        time2 = time.time()  # clock ticks

        generation_num += 1

        problem.calc_fitness_queens(population)
        population.sort(key=lambda x: x.fitness)  # sort population array by fitness
        print("Best:", population[0].string, "fitness: ", population[0].fitness)  # print string with best fitness
        print("mean of generation is: " + str(problem.calcAVG()))
        print("standard deviation of generation is: " + str(problem.calcStd()))

        clock_ticks = time.time() - time2
        E_T = time.time() - start_t
        print("Clock ticks: " + str(clock_ticks))
        print("Elapsed time: " + str(E_T))

        best_fitness = population[0].fitness
        number_of_best_genomes = problem.get_num_of_genomes_with_fitness(population, best_fitness)
        mid = len(population) / 2
        mid = math.floor(mid)
        mid_fitness = population[mid].fitness
        num_of_mid_genomes = problem.get_num_of_genomes_with_fitness(population, mid_fitness)
        print("best fitness = " + str(best_fitness))
        print("num of best fitness = " + str(number_of_best_genomes))
        print("mid fitness = " + str(mid_fitness))
        print("num of mid fitness = " + str(num_of_mid_genomes))
        prob_best = number_of_best_genomes / len(population)
        prob_mid = num_of_mid_genomes / len(population)
        selection_pressure = 0
        if prob_mid != 0:
            selection_pressure = prob_best / prob_mid

        print("selection pressure:" + str(selection_pressure))

        # genetic_diversity = self.calculate_genetic_diversity(population)

        # print("Genetic Diversity: " + str(genetic_diversity))

        # fitness = []
        # for j in range(len(population)):
        #     fitness.append(population[j].fitness)

        # plt.xlabel('Fitness')
        # plt.ylabel('Number of Genomes')
        # plt.hist(fitness)
        # plt.show()

        if population[0].fitness == 0:
            break

        buffer = problem.mate(population, buffer, "SINGLE", "tournament")  # mate
        population, buffer = buffer, population

        for genome in population:
            genome.age += 1

            # uniform decay mutation
            # rate = GA_MUTATIONRATE * (1 / GA_MAXITER)
            # GA_MUTATIONRATE = GA_MUTATIONRATE - rate

            # Adaptive decrease function mutation
            pmax = 0.3
            r = 0.5
            helper1 = 2 * (pmax ** 2) * (e ** (r * generation_num))
            helper2 = pmax + (pmax * (e ** (r * generation_num)))
            GA_MUTATIONRATE = helper1 / helper2

        E_T = time.time() - start_t
        clock_ticks = time.time() - time2
        print("Elapsed time: " + str(E_T) + " Clock Ticks: " + str(clock_ticks))


  ####----- Tisha Patel 20CE106-----####

  ###    DICTIONARY           ###

# Write a Python script to check whether a given key already exists in a dictionary.
def checkKey(dict, key):
    if key in dict.keys():
        print("Present, ", end=" ")
        print("value =", dict[key])
    else:
        print("Not present")
dict = {'a': 10, 'b': 20, 'c': 30}
key = 't'
checkKey(dict, key)
key = 'a'
checkKey(dict, key)

# Write a Python script to merge two Python dictionaries.
def Merge(dict1, dict2):
    return (dict2.update(dict1))
dict1 = {'a': 10, 'b': 8}
dict2 = {'d': 6, 'c': 4}
print(Merge(dict1, dict2))
print(dict2)

#Write a Python program to sum all the items in a dictionary.
def returnSum(myDict):
    list = []
    for i in myDict:
        list.append(myDict[i])
    final = sum(list)
    return final
dict = {'a': 10, 'b': 20, 'c': 30}
print("Sum :", returnSum(dict))

#Write a Python script to add a key to a dictionary.
d = {0:11, 12:22}
print(d)
d.update({28:33})
print(d)

#Write a Python script to concatenate following dictionaries to create a new one.
dic1={1:11, 2:22}
dic2={3:33, 4:44}
dic3={5:55,6:66}
dic4 = {}
for d in (dic1, dic2, dic3): dic4.update(d)
print(dic4)

###         TUPLE           ###

#Write a Python program to create a tuple with different data types.
tuplex = ("tuple", False, 3.3, 3)
print(tuplex)

#Write a Python program to create a tuple with numbers and print one item.
tuplex = 4, 8, 12, 16, 20
print(tuplex)
tuplex = 4,
print(tuplex)

#Write a Python program to add an item in a tuple.
a_tuple = ("element1", "element2")
print(a_tuple)
new_element = ("element3",)
a_tuple = a_tuple + new_element
print(a_tuple)

#Write a Python program to convert a tuple to a string.
def convertTuple(tup):
    str = ''
    for item in tup:
        str = str + item
    return str
tuple = ('t', 'i', 's', 'h', 'a')
str = convertTuple(tuple)
print(str)

#Write a Python program to find the length of a tuple.
tuplex = ('patel',)
print(len(tuplex))

###         SET           ###

#Write a Python program to add member(s) in a set and clear a set
color_set = set()
print(color_set)
print("\nAdd single element:")
color_set.add("Red")
print(color_set)
print("\nAdd multiple items:")
color_set.update(["Blue", "magenta"])
print(color_set)
color_set.clear()

#Write a Python program to remove an item from a set if it is present in the set.
num_set = set([0, 1, 2, 3, 4, 5])
print("Original set elements:")
print(num_set)
print("\nRemove 0 from the said set:")
num_set.discard(4)
print(num_set)
print("\nRemove 5 from the said set:")
num_set.discard(5)
print(num_set)
print("\nRemove 2 from the said set:")
num_set.discard(5)
print(num_set)
print("\nRemove 7 from the said set:")
num_set.discard(15)
print(num_set)

#Write a Python program to create an intersection, Union, difference of sets.
A = {0, 2, 4, 6, 8};
B = {1, 2, 3, 4, 5};
print("Union :", A | B)
print("Intersection :", A & B)
print("Difference :", A - B)

#Write a Python program to find maximum and the minimum value in a set.
setn = {5, 10, 3, 15, 2, 20}
print("Original set elements:")
print(setn)
print("\nMaximum value of the said set:")
print(max(setn))
print("\nMinimum value of the said set:")
print(min(setn))

#Write a Python program to find the most common elements and their counts from list, tuple, dictionary.
#most common from list
def most_frequent(List):
    return max(set(List), key = List.count)
List = [2, 1, 2, 2, 1, 3]
a=most_frequent(List)
print(a,List.count(a))
#most common from tuple
def most_frequent(List):
    return max(set(List), key = List.count)
List = list((2, 1, 2, 2, 1, 3,))#converted tuple to list
a=most_frequent(List)
print(a,List.count(a))
#most common from dictionary
def most_frequent(List):
    return max(set(List), key = List.count)
dict={1:11,2:22,3:33,4:11,5:11}
List = list(dict.values())
a=most_frequent(List)
print(a,List.count(a))


from flask import Flask, render_template, request
import google_keyword

app = Flask(__name__)

@app.route('/')
def index():
    # get을 통해 전달받은 데이터 확인
    key1 = request.args.get('keyword1')
    key2 = request.args.get('keyword2')

    if not key1 or not key2:
        return render_template('9_index.html')
    else:
        # 모듈로 키워드 개수 가져오기
        value1 = google_keyword.get_keyword_number(key1)
        value2 = google_keyword.get_keyword_number(key2)

        # 사용자 보낼 데이터
        data = {key1: value1, key2: value2}

        return render_template('9_index.html', data=data)

if __name__ == "__main__":
    app.run(debug=True)


"""
File: linkedbst.py
Author: Ken Lambert
"""

from math import log

from abstractcollection import AbstractCollection
from bstnode import BSTNode
from linkedstack import LinkedStack
from linkedqueue import LinkedQueue

class LinkedBST(AbstractCollection):
    """An link-based binary search tree implementation."""

    def __init__(self, source_collection=None):
        """Sets the initial state of self, which includes the
        contents of source_collection, if it's present."""
        self._root = None
        AbstractCollection.__init__(self, source_collection)

    # Accessor methods
    def __str__(self):
        """Returns a string representation with the tree rotated
        90 degrees counterclockwise."""

        def recurse(node, level):
            string = ""
            if node:
                string += recurse(node.right, level + 1)
                string += "| " * level
                string += str(node.data) + "\n"
                string += recurse(node.left, level + 1)
            return string

        return recurse(self._root, 0)

    def __iter__(self):
        """Supports a preorder traversal on a view of self."""
        if not self.isEmpty():
            stack = LinkedStack()
            stack.push(self._root)
            while not stack.isEmpty():
                node = stack.pop()
                yield node.data
                if node.right:
                    stack.push(node.right)
                if node.left:
                    stack.push(node.left)

    def preorder(self):
        """Supports a preorder traversal on a view of self."""
        return None

    def inorder(self):
        """Supports an inorder traversal on a view of self."""
        lst = list()

        def recurse(node):
            if node:
                recurse(node.left)
                lst.append(node.data)
                recurse(node.right)

        recurse(self._root)
        return iter(lst)

    def postorder(self):
        """Supports a postorder traversal on a view of self."""
        return None

    def levelorder(self):
        """Supports a levelorder traversal on a view of self."""
        return None

    def __contains__(self, item):
        """Returns True if target is found or False otherwise."""
        return item == self.find(item)

    def find(self, item):
        """If item matches an item in self, returns the
        matched item, or None otherwise."""

        def recurse(node):
            if not node:
                return None
            elif item == node.data:
                return node.data
            elif item < node.data:
                return recurse(node.left)
            else:
                return recurse(node.right)

        return recurse(self._root)

    # Mutator methods
    def clear(self):
        """Makes self become empty."""
        self._root = None
        self._size = 0

    def add(self, item):
        """Adds item to the tree."""

        # Helper function to search for item's position
        def recurse(node):
            # New item is less, go left until spot is found
            if item < node.data:
                if not node.left:
                    node.left = BSTNode(item)
                else:
                    recurse(node.left)
            # New item is greater or equal,
            # go right until spot is found
            elif not node.right:
                node.right = BSTNode(item)
            else:
                recurse(node.right)
                # End of recurse

        # Tree is empty, so new item goes at the root
        if self.isEmpty():
            self._root = BSTNode(item)
        # Otherwise, search for the item's spot
        else:
            recurse(self._root)
        self._size += 1

    def remove(self, item):
        """Precondition: item is in self.
        Raises: KeyError if item is not in self.
        postcondition: item is removed from self."""
        if item not in self:
            raise KeyError("Item not in tree.""")

        # Helper function to adjust placement of an item
        def lift_max_in_left_subtree_to_top(top):
            # Replace top's datum with the maximum datum in the left subtree
            # Pre:  top has a left child
            # Post: the maximum node in top's left subtree
            #       has been removed
            # Post: top.data = maximum value in top's left subtree
            node_parent = top
            current_node = top.left
            while current_node.right:
                node_parent = current_node
                current_node = current_node.right
            top.data = current_node.data
            if node_parent == top:
                top.left = current_node.left
            else:
                node_parent.right = current_node.left

        # Begin main part of the method
        if self.isEmpty():
            return None

        # Attempt to locate the node containing the item
        item_removed = None
        pre_root = BSTNode(None)
        pre_root.left = self._root
        parent = pre_root
        direction = 'L'
        current_node = self._root
        while current_node:
            if current_node.data == item:
                item_removed = current_node.data
                break
            parent = current_node
            if current_node.data > item:
                direction = 'L'
                current_node = current_node.left
            else:
                direction = 'R'
                current_node = current_node.right

        # Return None if the item is absent
        if not item_removed:
            return None

        # The item is present, so remove its node

        # Case 1: The node has a left and a right child
        #         Replace the node's value with the maximum value in the
        #         left subtree
        #         Delete the maximum node in the left subtree
        if current_node.left and current_node.right:
            lift_max_in_left_subtree_to_top(current_node)
        else:

            # Case 2: The node has no left child
            if not current_node.left:
                new_child = current_node.right

                # Case 3: The node has no right child
            else:
                new_child = current_node.left

                # Case 2 & 3: Tie the parent to the new child
            if direction == 'L':
                parent.left = new_child
            else:
                parent.right = new_child

        # All cases: Reset the root (if it hasn't changed no harm done)
        #            Decrement the collection's size counter
        #            Return the item
        self._size -= 1
        if self.isEmpty():
            self._root = None
        else:
            self._root = pre_root.left
        return item_removed

    def replace(self, item, new_item):
        """
        If item is in self, replaces it with new_item and
        returns the old item, or returns None otherwise."""
        probe = self._root
        while probe:
            if probe.data == item:
                old_data = probe.data
                probe.data = new_item
                return old_data
            elif probe.data > item:
                probe = probe.left
            else:
                probe = probe.right
        return None

    def height(self):
        """
        Return the height of tree
        :return: int
        """
        def height1(top):
            """
            Helper function
            :param top:
            :return:
            """
            if not top:
                return -1
            else:
                return 1 + max(height1(top.left), height1(top.right))

        return height1(self._root)

    def is_balanced(self):
        """
        Return True if tree is balanced
        :return:
        """
        height = self.height()
        size = self._size

        return height < 2 * log(size + 1, 2) - 1

    def range_find(self, low, high):
        """
        Returns a list of the items in the tree, where low <= item <= high.
        """

    def rebalance(self):
        """
        Rebalances the tree.
        :return:
        """
        item_list = []
        for item in self.inorder():
            item_list.append(item)
            self.remove(item)

        self._recurse_add(item_list)

    def _recurse_add(self, item_list: list) -> None:
        """Helper method for the rebalance method."""
        if not item_list:
            return None

        if len(item_list) % 2 == 0:
            mid_index = int((len(item_list) / 2) - 1)
        else:
            mid_index = int(len(item_list) // 2)

        self.add(item_list[mid_index])
        self._recurse_add(item_list[:mid_index])
        self._recurse_add(item_list[mid_index + 1:])

    def successor(self, item):
        """
        Returns the smallest item that is larger than
        item, or None if there is no such item.
        :param item:
        :type item:
        :return:
        :rtype:
        """

    def predecessor(self, item):
        """
        Returns the largest item that is smaller than
        item, or None if there is no such item.
        :param item:
        :type item:
        :return:
        :rtype:
        """

    def demo_bst(self, path):
        """
        Demonstration of efficiency binary search tree for the search tasks.
        :param path:
        :type path:
        :return:
        :rtype:
        """


"""
Problem Overview:
    The Function that accepts an input graph of
    nodes representing the total network and
    identifies the node with the most number
    of connections And Return the label of the node.

Complexity :
        o(n)

Returns:
    _type_: str
"""

def identify_router(graph):
    router_dict = dict()
    nodes = graph.split(" -> ")

    for index, node in enumerate(nodes, start=0):
        if index == 0:
            router_dict[node] = 1
        elif index + 1 == len(nodes):
            router_dict[node] += 1
        else:
            if node in router_dict:
                value = router_dict[node]
                value += 2
                router_dict[node] = value
            else:
                router_dict[node] = 2

    max_router_values = max(router_dict.values())
    router = [
        key for key, value in router_dict.items() if value == max_router_values
        ]

    network_point = ", "
    network_point = network_point.join(router)
    return network_point


# -*- coding:utf-8 -*-
from utils.mail import send_mail
from utils.sms import sms
from django.template.loader import render_to_string
from django.conf import settings
from django.contrib.auth.models import User
from problem.models import getProblemType
from staff.models import Staff
from django.template import Context, Template
from utils.constants import priority_dict
from staff.models import Staff
from celery import task

# ----------发送给申报人------------
# 1.派发工单提醒
@task
def distribute_customer(order):
    # 1) 生成邮件内容
    # 工程师的手机号
    engineer = Staff.objects.get(accountname=order.dealer_accountname)
    content = render_to_string('notify/distribute_customer.html', {'order': order, 'engineer_phone':engineer.phone,
        'service_address':settings.SERVICE_ADDRESS})
    # 2) 发送邮件
    send_mail(u'IT工单系统', order.email, u'派发工单提醒', content)

# 2.转发工单提醒
@task
def retransmission_customer(order):
    # 1) 生成邮件内容
    # 工程师的手机号
    engineer = Staff.objects.get(accountname=order.dealer_accountname)
    content = render_to_string('notify/retransmission_customer.html', {'order': order, 'engineer_phone':engineer.phone,
        'service_address':settings.SERVICE_ADDRESS})
    # 2) 发送邮件
    send_mail(u'IT工单系统',order.email, u'转发工单提醒', content)

# 3.工单已解决&等待评价提醒
@task
def wait_appraise_customer(order):
    # 1) 生成邮件内容
    # 工程师的手机号
    engineer = Staff.objects.get(accountname=order.dealer_accountname)
    content = render_to_string('notify/wait_appraise_customer.html',
            {'order': order,'service_address':settings.SERVICE_ADDRESS, 'engineer_phone':engineer.phone})
    # 2) 发送邮件
    send_mail(u'IT工单系统',order.email, u'工单已解决&等待评价提醒', content)

# 4.工单被取消
@task
def cancel_customer(order, user, reason):
    # 1) 生成邮件内容
    # 工程师的手机号
    content = render_to_string('notify/cancel_customer.html',
            {'order': order, 'reason':reason, 'user':user})
    # 2) 发送邮件
    send_mail(u'IT工单系统',order.email, u'工单取消通知', content)

# ----------发送给IT工程师------------
# 1.工单派发
# ----------发送给申报人------------
# 1.派发工单提醒
@task
def distribute_engineer(order):
    # 1) 生成邮件内容
    # 提交人
    submiter = User.objects.get(username=order.creator).userprofile.realname
    # 问题类别
    problem_type = getProblemType(order.problem_type_third)
    # 动作
    operation = u'分派'
    content = render_to_string('notify/distribute_engineer.html', {'order': order,
        'submiter':submiter, 'problem_type':problem_type, 'operation':operation})

    staff = Staff.objects.get(accountname=order.dealer_accountname)
    # 2) 发送邮件
    send_mail(u'IT工单系统',staff.email, u'派发工单提醒', content)

# 2.工单转发
@task
def retransmission_engineer(order):
    # 1) 生成邮件内容
    # 提交人
    submiter = User.objects.get(username=order.creator).userprofile.realname
    # 问题类别
    problem_type = getProblemType(order.problem_type_third)
    # 动作
    operation = u'转发'
    content = render_to_string('notify/distribute_engineer.html', {'order': order,
        'submiter':submiter, 'problem_type':problem_type, 'operation':operation})

    staff = Staff.objects.get(accountname=order.dealer_accountname)
    # 2) 发送邮件
    send_mail(u'IT工单系统',staff.email, u'工单转发提醒', content)

@task
def sms_notify(order):
    # 1) 生成短信内容
    t = Template(u"工单号：{{order.case_number}}，用户：{{order.realname}}，办公区：{{order.area.name}}，电话：{{order.phone}}，部门：{{order.department}}，优先级：{{priority}}。请及时联系用户并协助处理。")
    priority = priority_dict[order.priority_level]
    c = Context({'order': order, 'priority':priority})
    content = t.render(c)

    # 2) 发送短信
    # 处理人手机号
    staff = Staff.objects.get(accountname = order.dealer_accountname)
    sms(staff.phone, content)

@task
def ittask_create_notify(task):
    # 1) 邮件内容
    ll = []
    to_addrs = []
    for staff in task.executors.all():
        ll.append(staff.realname)
        to_addrs.append(staff.email)

    content = render_to_string('notify/ittask_create.html',
        {'task':task,'executors':','.join(ll)})

    to_addrs.append(task.director.email)
    # 2) 发送邮件
    send_mail(u'IT工单系统', to_addrs, u'IT项目创建通知', content)

# 通知IT任务的负责人修改任务执行的最新状态
def update_status_notify(task):
    # 1) 邮件内容
    ll = []
    for staff in task.executors.all():
        ll.append(staff.realname)
    content = render_to_string('notify/ittask_update_status.html',
        {'task':task,'executors':','.join(ll)})

    # 2) 发送邮件
    send_mail(u'IT工单系统',task.director.email, u'IT项目更新提醒', content)


"""
@brief:     Custom loss functions in PyTorch
"""
import numpy as np
import torch
from torch import nn
from torch.distributions import MultivariateNormal

def nll_loss(true_val,
             pred_mean,
             pred_L):
    """
    Log likelihood loss
    Args:
        true_val: true target values
        pred_mean: predicted mean of the Normal distribution
        pred_L: alternatively, use predicted Cholesky Decomposition

    Returns:
        log likelihood

    """
    # Shape of true_val:
    # [*add_dim, dim_val]
    #
    # Shape of pred_mean:
    # [*add_dim, dim_val]
    #
    # Shape of pred_L:
    # [*add_dim, dim_val, dim_val]

    # Construct distribution
    mvn = MultivariateNormal(loc=pred_mean, scale_tril=pred_L,
                             validate_args=False)

    # Compute log likelihood
    ll = mvn.log_prob(true_val).mean()

    # Loss
    ll_loss = -ll
    return ll_loss

def nmll_loss(true_val,
              pred_mean,
              pred_L,
              mc_smp_dim: int = -3):
    """
    Marginal log likelihood loss
    Args:
        true_val: true target values
        pred_mean: predicted mean of the Normal distribution
        pred_L: alternatively, use predicted Cholesky Decomposition
        mc_smp_dim: where is the mc sample dimension

    Returns:

    """
    # Shape of true_val:
    # [*add_dim_1, num_mc_smp, *add_dim_2, dim_val]
    #
    # Shape of pred_mean:
    # [*add_dim_1, num_mc_smp, *add_dim_2, dim_val]
    #
    # Shape of pred_L:
    # [*add_dim_1, num_mc_smp, *add_dim_2, dim_val, dim_val]

    # Check dimensions
    if mc_smp_dim < 0:
        mc_smp_dim = pred_mean.ndim + mc_smp_dim
    shapes = pred_mean.shape
    dimensions = list(range(pred_mean.ndim))
    add_dim_1 = torch.tensor(shapes[:mc_smp_dim])
    num_mc_smp = shapes[mc_smp_dim]
    add_dim_2 = torch.tensor(shapes[mc_smp_dim + 1:-1])

    # Construct distribution
    mvn = MultivariateNormal(loc=pred_mean, scale_tril=pred_L,
                             validate_args=False)

    # Compute log likelihood loss for each trajectory
    ll = mvn.log_prob(true_val)

    # Sum among additional dimensions part 2
    ll = torch.sum(ll, dim=dimensions[mc_smp_dim + 1:-1])

    # MC average
    ll = torch.logsumexp(ll, dim=mc_smp_dim)

    # Average among additional dimensions part 1
    ll = torch.sum(ll, dim=dimensions[:mc_smp_dim])
    assert ll.ndim == 0

    # Get marginal log likelihood
    ll = ll - torch.prod(add_dim_1) * np.log(num_mc_smp)
    mll = ll / (torch.prod(add_dim_1) * torch.prod(add_dim_2))

    # Loss
    mll_loss = -mll
    return mll_loss

def mse_loss(true_val, pred):
    """
    Mean squared error

    Args:
        true_val: Ground truth
        pred: predicted value

    Returns:
        mse
    """
    mse = nn.MSELoss()
    return mse(pred, true_val)


from enum import Enum
from urllib.parse import unquote

from django.conf import settings
from django.contrib import messages
from django.http import Http404
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils import timezone
from django.utils.dateformat import format as date_format
from django.utils.functional import cached_property
from django.utils.http import is_safe_url
from django.utils.translation import gettext_lazy as _
from django.views.generic import FormView, TemplateView
from mtp_common.analytics import genericised_pageview
from mtp_common.auth.api_client import get_api_session
from mtp_common.auth.exceptions import HttpNotFoundError
from requests.exceptions import RequestException

from security.context_processors import initial_params
from security.export import ObjectListXlsxResponse
from security.tasks import email_export_xlsx

SEARCH_FORM_SUBMITTED_INPUT_NAME = 'form_submitted'

class ViewType(Enum):
    """
    Enum for the different variants of views for a specific class of objects.
    """
    simple_search_form = 'simple_search_form'
    advanced_search_form = 'advanced_search_form'
    search_results = 'search_results'
    export_download = 'export_download'
    export_email = 'export_email'
    detail = 'detail'

class SimpleSecurityDetailView(TemplateView):
    """
    Base view for showing a single templated page without a form
    """
    title = NotImplemented
    template_name = NotImplemented
    object_context_key = NotImplemented
    list_title = NotImplemented
    list_url = NotImplemented

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.object = None

    @cached_property
    def session(self):
        return get_api_session(self.request)

    def get_object_request_params(self):
        raise NotImplementedError

    def get_object(self):
        try:
            return self.session.get(**self.get_object_request_params()).json()
        except HttpNotFoundError:
            raise Http404('%s not found' % self.object_context_key)
        except (RequestException, ValueError):
            messages.error(self.request, _('This service is currently unavailable'))
            return {}

    def get_context_data(self, **kwargs):
        self.object = self.get_object()

        context_data = super().get_context_data(**kwargs)
        context_data[self.object_context_key] = self.get_object_for_template(self.object)

        list_url = self.get_list_url()

        context_data['breadcrumbs'] = self.get_breadcrumbs(list_url)
        return context_data

    def get_object_for_template(self, obj):
        return obj

    def get_list_url(self):
        list_url = self.request.build_absolute_uri(str(self.list_url))
        referrer_url = self.request.META.get('HTTP_REFERER', '-')
        if referrer_url.split('?', 1)[0] == list_url:
            list_url = referrer_url
        return list_url

    def get_breadcrumbs(self, list_url):
        return [
            {'name': _('Home'), 'url': reverse('security:dashboard')},
            {'name': self.list_title, 'url': list_url},
            {'name': self.title}
        ]

class SecurityView(FormView):
    """
    Base view for retrieving security-related searches
    Allows form submission via GET, i.e. form is always bound
    """
    title = NotImplemented
    advanced_search_template_name = NotImplemented
    object_list_context_key = NotImplemented
    view_type = None
    search_results_view = None
    simple_search_view = None
    advanced_search_view = None
    export_download_limit = settings.MAX_CREDITS_TO_DOWNLOAD
    export_email_limit = settings.MAX_CREDITS_TO_EMAIL
    object_name = None
    object_name_plural = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.redirect_on_single = False

    def get_template_names(self):
        """
        Return the advanced search template if the view type is 'adanced search' or the default
        template otherwise.
        """
        if self.view_type == ViewType.advanced_search_form:
            return self.advanced_search_template_name
        return self.template_name

    def get_form_kwargs(self):
        form_kwargs = super().get_form_kwargs()
        form_kwargs['request'] = self.request
        request_data = self.request.GET.copy()
        if 'redirect-on-single' in request_data:
            self.redirect_on_single = True
        form_kwargs['data'] = request_data
        return form_kwargs

    def is_export_view_type(self):
        return self.view_type in (ViewType.export_download, ViewType.export_email)

    def form_valid(self, form):
        """
        If it's an export view, export and redirect to the referral view.
        If it's a simple form view, the form is valid and was submitted,
            redirect to the search results page.
        Else, render the template.
        """
        if self.is_export_view_type():
            attachment_name = 'exported-%s-%s.xlsx' % (
                self.object_list_context_key, date_format(timezone.now(), 'Y-m-d')
            )
            if self.view_type == ViewType.export_email:
                email_export_xlsx(
                    object_type=self.object_list_context_key,
                    user=self.request.user,
                    session=self.request.session,
                    endpoint_path=form.get_object_list_endpoint_path(),
                    filters=form.get_api_request_params(),
                    export_description=self.get_export_description(form),
                )
                messages.info(
                    self.request,
                    _('The spreadsheet will be emailed to you at %(email)s') % {'email': self.request.user.email}
                )
                return self.redirect_to_referral_url()
            return ObjectListXlsxResponse(form.get_complete_object_list(),
                                          object_type=self.object_list_context_key,
                                          attachment_name=attachment_name)

        if (
            SEARCH_FORM_SUBMITTED_INPUT_NAME in self.request.GET
            and self.search_results_view
        ):
            search_results_url = f'{reverse(self.search_results_view)}?{form.query_string}'
            return redirect(search_results_url)

        if self.view_type != ViewType.advanced_search_form:
            object_list = form.get_object_list()
            context = self.get_context_data(form=form)
            if self.redirect_on_single and len(object_list) == 1 and hasattr(self, 'url_for_single_result'):
                return redirect(self.url_for_single_result(object_list[0]))
            context[self.object_list_context_key] = object_list
            # add objects as an alias for generic logic
            context['objects'] = object_list
        else:
            context = self.get_context_data(form=form)
        return render(self.request, self.get_template_names(), context)

    def form_invalid(self, form):
        """
        If the form is invalid and the view type is export or search results,
        it redirects back to the referral view so that the user can see and correct the errors.
        """
        if self.is_export_view_type() or self.view_type == ViewType.search_results:
            return self.redirect_to_referral_url()

        return super().form_invalid(form)

    def _get_breadcrumbs(self, **kwargs):
        prisons_param = initial_params(self.request).get('initial_params', '')
        if self.view_type == ViewType.advanced_search_form:
            return [
                {'name': _('Home'), 'url': reverse('security:dashboard')},
                {'name': self.title, 'url': f'{reverse(self.simple_search_view)}?{prisons_param}'},
                {'name': _('Advanced search')},
            ]

        if self.view_type == ViewType.search_results:
            if kwargs['form'].was_advanced_search_used():
                return [
                    {'name': _('Home'), 'url': reverse('security:dashboard')},
                    {'name': self.title, 'url': f'{reverse(self.simple_search_view)}?{prisons_param}'},
                    {
                        'name': _('Advanced search'),
                        'url': f'{reverse(self.advanced_search_view)}?{kwargs["form"].query_string}',
                    },
                    {'name': _('Advanced search results')},
                ]

            return [
                {'name': _('Home'), 'url': reverse('security:dashboard')},
                {'name': self.title, 'url': f'{reverse(self.simple_search_view)}?{prisons_param}'},
                {'name': _('Search results')},
            ]

        return [
            {'name': _('Home'), 'url': reverse('security:dashboard')},
            {'name': self.title},
        ]

    def get_context_data(self, **kwargs):
        context_data = super().get_context_data(**kwargs)

        return {
            **context_data,

            'breadcrumbs': self._get_breadcrumbs(**kwargs),
            'google_analytics_pageview': genericised_pageview(self.request, self.get_generic_title()),
            'search_form_submitted_input_name': SEARCH_FORM_SUBMITTED_INPUT_NAME,
        }

    def redirect_to_referral_url(self):
        """
        Returns an HttpResponseRedirect to the referer preserving the same kwargs and query string.
        """
        referer = self.request.META.get('HTTP_REFERER')
        if referer:
            referer = unquote(referer)  # HTTP_REFERER may be encoded.

        if not is_safe_url(
            url=referer,
            allowed_hosts={self.request.get_host()},
            require_https=self.request.is_secure(),
        ):
            referer = '/'
        return redirect(referer)

    def get_export_description(self, form):
        return str(form.search_description['description'])

    def get_class_name(self):
        return self.__class__.__name__

    def get_generic_title(self):
        # not customised for specifc search or detail object
        return self.__class__.title

    def get_used_request_params(self):
        return sorted(
            param
            for param, value in self.request.GET.items()
            if value
        )

    get = FormView.post

class SecurityDetailView(SecurityView):
    """
    Base view for presenting a profile with associated credits
    Allows form submission via GET
    """
    list_title = NotImplemented
    list_url = NotImplemented
    id_kwarg_name = NotImplemented
    object_list_context_key = 'credits'
    object_context_key = NotImplemented
    view_type = ViewType.detail

    def get_form_kwargs(self):
        form_kwargs = super().get_form_kwargs()
        form_kwargs['object_id'] = self.kwargs[self.id_kwarg_name]
        return form_kwargs

    def get_context_data(self, **kwargs):
        context_data = super().get_context_data(**kwargs)

        detail_object = context_data['form'].get_object()
        if detail_object is None:
            raise Http404('Detail object not found')
        self.title = self.get_title_for_object(detail_object)
        list_url = self.request.build_absolute_uri(str(self.list_url))
        referrer_url = self.request.META.get('HTTP_REFERER', '-')
        if referrer_url.split('?', 1)[0] == list_url:
            list_url = referrer_url
        context_data[self.object_context_key] = detail_object

        context_data['breadcrumbs'] = [
            {'name': _('Home'), 'url': reverse('security:dashboard')},
            {'name': self.list_title, 'url': list_url},
            {'name': self.title}
        ]
        context_data['form'].check_and_update_saved_searches(str(self.title))
        return context_data

    def get_title_for_object(self, detail_object):
        raise NotImplementedError

    def get_export_description(self, form):
        detail_object = form.get_object()
        title = self.get_title_for_object(detail_object)
        return '%s: %s' % (title, super().get_export_description(form))


import io
import os
import time
from collections import defaultdict, deque
import datetime

import torch
import torch.distributed as dist

import sys
from torch import optim as optim
from typing import List, Union
import json
import csv
import random
import torch.nn.functional as F
import numpy as np

class SmoothedValue(object):
    """Track a series of values and provide access to smoothed values over a
    window or the global series average.
    """

    def __init__(self, window_size=20, fmt=None):
        if fmt is None:
            fmt = "{median:.4f} ({global_avg:.4f})"
        self.deque = deque(maxlen=window_size)
        self.total = 0.0
        self.count = 0
        self.fmt = fmt

    def update(self, value, n=1):
        self.deque.append(value)
        self.count += n
        self.total += value * n

    def synchronize_between_processes(self):
        """
        Warning: does not synchronize the deque!
        """
        if not is_dist_avail_and_initialized():
            return
        t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda')
        dist.barrier()
        dist.all_reduce(t)
        t = t.tolist()
        self.count = int(t[0])
        self.total = t[1]

    @property
    def median(self):
        d = torch.tensor(list(self.deque))
        return d.median().item()

    @property
    def avg(self):
        d = torch.tensor(list(self.deque), dtype=torch.float32)
        return d.mean().item()

    @property
    def global_avg(self):
        return self.total / max(1, self.count)

    @property
    def max(self):
        return 0 if len(self.deque)==0 else max(self.deque)

    @property
    def value(self):
        return 0 if len(self.deque)==0 else self.deque[-1]

    def __str__(self):
        return self.fmt.format(
            median=self.median,
            avg=self.avg,
            global_avg=self.global_avg,
            max=self.max,
            value=self.value)

class MetricLogger(object):
    def __init__(self, delimiter="\t"):
        self.meters = defaultdict(SmoothedValue)
        self.delimiter = delimiter

    def update(self, **kwargs):
        for k, v in kwargs.items():
            if isinstance(v, torch.Tensor):
                v = v.item()
            assert isinstance(v, (float, int))
            self.meters[k].update(v)

    def __getattr__(self, attr):
        if attr in self.meters:
            return self.meters[attr]
        if attr in self.__dict__:
            return self.__dict__[attr]
        raise AttributeError("'{}' object has no attribute '{}'".format(
            type(self).__name__, attr))

    def __str__(self):
        loss_str = []
        for name, meter in self.meters.items():
            loss_str.append(
                "{}: {}".format(name, str(meter))
            )
        return self.delimiter.join(loss_str)

    def synchronize_between_processes(self):
        for meter in self.meters.values():
            meter.synchronize_between_processes()

    def add_meter(self, name, meter):
        self.meters[name] = meter

    def log_every(self, iterable, print_freq, header=None):
        i = 0
        if not header:
            header = ''
        start_time = time.time()
        end = time.time()
        iter_time = SmoothedValue(fmt='{avg:.4f}')
        data_time = SmoothedValue(fmt='{avg:.4f}')
        space_fmt = ':' + str(len(str(len(iterable)))) + 'd'
        log_msg = [
            header,
            '[{0' + space_fmt + '}/{1}]',
            'eta: {eta}',
            '{meters}',
            'time: {time}',
            'data: {data}'
        ]
        if torch.cuda.is_available():
            log_msg.append('max mem: {memory:.0f}')
        log_msg = self.delimiter.join(log_msg)
        MB = 1024.0 * 1024.0
        for obj in iterable:
            data_time.update(time.time() - end)
            yield obj
            iter_time.update(time.time() - end)
            if i % print_freq == 0 or i == len(iterable) - 1:
                eta_seconds = iter_time.global_avg * (len(iterable) - i)
                eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))
                if torch.cuda.is_available():
                    print(log_msg.format(
                        i, len(iterable), eta=eta_string,
                        meters=str(self),
                        time=str(iter_time), data=str(data_time),
                        memory=torch.cuda.max_memory_allocated() / MB))
                    sys.stdout.flush()
                else:
                    print(log_msg.format(
                        i, len(iterable), eta=eta_string,
                        meters=str(self),
                        time=str(iter_time), data=str(data_time)))
            i += 1
            end = time.time()
        total_time = time.time() - start_time
        total_time_str = str(datetime.timedelta(seconds=int(total_time)))
        print('{} Total time: {} ({:.4f} s / it)'.format(
            header, total_time_str, total_time / max(1, len(iterable))))
        sys.stdout.flush()

    def log_every_no_print(self, iterable, print_freq, header=None):
        i = 0
        if not header:
            header = ''
        start_time = time.time()
        end = time.time()
        iter_time = SmoothedValue(fmt='{avg:.4f}')
        data_time = SmoothedValue(fmt='{avg:.4f}')
        space_fmt = ':' + str(len(str(len(iterable)))) + 'd'
        log_msg = [
            header,
            '[{0' + space_fmt + '}/{1}]',
            'eta: {eta}',
            '{meters}',
            'time: {time}',
            'data: {data}'
        ]
        if torch.cuda.is_available():
            log_msg.append('max mem: {memory:.0f}')
        log_msg = self.delimiter.join(log_msg)
        MB = 1024.0 * 1024.0
        for obj in iterable:
            data_time.update(time.time() - end)
            yield obj
            iter_time.update(time.time() - end)
            if i % print_freq == 0 or i == len(iterable) - 1:
                eta_seconds = iter_time.global_avg * (len(iterable) - i)
                eta_string = str(datetime.timedelta(seconds=int(eta_seconds)))
                # if torch.cuda.is_available():
                #     print(log_msg.format(
                #         i, len(iterable), eta=eta_string,
                #         meters=str(self),
                #         time=str(iter_time), data=str(data_time),
                #         memory=torch.cuda.max_memory_allocated() / MB))
                #     sys.stdout.flush()
                # else:
                #     print(log_msg.format(
                #         i, len(iterable), eta=eta_string,
                #         meters=str(self),
                #         time=str(iter_time), data=str(data_time)))
            i += 1
            end = time.time()
        total_time = time.time() - start_time
        total_time_str = str(datetime.timedelta(seconds=int(total_time)))
        # print('{} Total time: {} ({:.4f} s / it)'.format(
        #     header, total_time_str, total_time / max(1, len(iterable))))
        sys.stdout.flush()

def _load_checkpoint_for_ema(model_ema, checkpoint):
    """
    Workaround for ModelEma._load_checkpoint to accept an already-loaded object
    """
    mem_file = io.BytesIO()
    torch.save(checkpoint, mem_file)
    mem_file.seek(0)
    model_ema._load_checkpoint(mem_file)

def setup_for_distributed(is_master):
    """
    This function disables printing when not in master process
    """
    import builtins as __builtin__
    builtin_print = __builtin__.print

    def print(*args, **kwargs):
        force = kwargs.pop('force', False)
        if is_master or force:
            builtin_print(*args, **kwargs)

    __builtin__.print = print

def is_dist_avail_and_initialized():
    if not dist.is_available():
        return False
    if not dist.is_initialized():
        return False
    return True

def get_world_size():
    if not is_dist_avail_and_initialized():
        return 1
    return dist.get_world_size()

def get_rank():
    if not is_dist_avail_and_initialized():
        return 0
    return dist.get_rank()

def is_main_process():
    return get_rank() == 0

def save_on_master(*args, **kwargs):
    if is_main_process():
        torch.save(*args, **kwargs)

def init_distributed_mode(args):
    if 'OMPI_COMM_WORLD_RANK' in os.environ:
        args.rank = int(os.environ.get('OMPI_COMM_WORLD_RANK'))
        args.world_size = int(os.environ.get('OMPI_COMM_WORLD_SIZE'))
        args.gpu = args.rank % torch.cuda.device_count()
    elif 'RANK' in os.environ and 'WORLD_SIZE' in os.environ:
        args.rank = int(os.environ["RANK"])
        args.world_size = int(os.environ['WORLD_SIZE'])
        args.gpu = int(os.environ['LOCAL_RANK'])
    elif 'SLURM_PROCID' in os.environ:
        args.rank = int(os.environ['SLURM_PROCID'])
        args.gpu = args.rank % torch.cuda.device_count()
    else:
        print('Not using distributed mode')
        args.distributed = False
        return

    args.distributed = True

    torch.cuda.set_device(args.gpu)
    args.dist_backend = 'nccl'
    print('| distributed init (rank {}): {}'.format(
        args.rank, args.dist_url), flush=True)
    torch.distributed.init_process_group(backend=args.dist_backend, init_method=args.dist_url,
                                         world_size=args.world_size, rank=args.rank)
    torch.distributed.barrier()
    setup_for_distributed(args.rank == 0)

def build_optimizer(config, model, momentum=0.9):
    """
    Build optimizer, set weight decay of normalization to 0 by default.
    """
    skip = {}
    skip_keywords = {}
    high_lr_keywords = {}
    low_keywords = {}  # low_keywords disabled
    # if hasattr(model, 'low_weight_decay_keywords'):
    #     low_keywords = model.low_weight_decay_keywords()
    if hasattr(model, 'no_weight_decay'):
        skip = model.no_weight_decay()
    if hasattr(model, 'no_weight_decay_keywords'):
        skip_keywords = model.no_weight_decay_keywords()
    if hasattr(model, 'no_weight_decay_keywords'):
        skip_keywords = model.no_weight_decay_keywords()

    if hasattr(model, 'high_lr_keywords'):
        high_lr_keywords = model.high_lr_keywords()

    parameters = set_weight_decay(model, skip_list=skip, skip_keywords=skip_keywords, low_keywords=low_keywords,
                                  high_lr_keywords=high_lr_keywords, high_lr_num=config.lr * 10)

    opt_lower = config.opt.lower()
    optimizer = None
    if opt_lower == 'sgd':
        optimizer = optim.SGD(parameters, momentum=float(momentum), nesterov=True,
                              lr=config.lr, weight_decay=config.weight_decay)
    elif opt_lower == 'adamw':
        optimizer = AdamWCustomized(parameters, eps=config.opt_eps,
                                lr=config.lr, weight_decay=config.weight_decay)
    else:
        raise NotImplementedError

    return optimizer

def set_weight_decay(model, skip_list=(), skip_keywords=(), low_keywords=(), high_lr_keywords=(), high_lr_num=None):
    has_decay = []
    has_decay_name = []
    no_decay = []
    no_decay_name = []
    low_decay = []
    low_decay_name = []
    high_lr = []
    high_lr_name = []

    for name, param in model.named_parameters():
        # if 'adapter' in name:
        #     print(
        #         'hi'
        #     )
        if not param.requires_grad:
            continue  # frozen weights
        if len(param.shape) == 1 or name.endswith(".bias") or (name in skip_list) or \
                check_keywords_in_name(name, skip_keywords):

            no_decay.append(param)
            no_decay_name.append(name)
        # elif check_keywords_in_name(name, low_keywords):
        #     low_decay.append(param)
        #     low_decay_name.append(name)
        elif check_keywords_in_name(name, high_lr_keywords):
            high_lr.append(param)
            high_lr_name.append(name)
        else:
            has_decay.append(param)
            has_decay_name.append(name)

    if high_lr_keywords:
        print('high lr params: ', high_lr_name)
        return [{'params': has_decay},
                {'params': low_decay, 'weight_decay': 0.0001},
                {'params': no_decay, 'weight_decay': 0.},
                {'params': high_lr, 'lr': high_lr_num}]
    else:
        return [{'params': has_decay},
                {'params': low_decay, 'weight_decay': 0.0001},
                {'params': no_decay, 'weight_decay': 0.}]

def check_keywords_in_name(name, keywords=()):
    isin = False
    for keyword in keywords:
        if keyword in name:
            isin = True
    return isin

class AdamWCustomized(optim.AdamW):
    def fake_step(self, closure=None):  # for testing...
        loss = None
        if closure is not None:
            with torch.enable_grad():
                loss = closure()

        for group in self.param_groups:
            params_with_grad = []
            grads = []
            exp_avgs = []
            exp_avg_sqs = []
            state_sums = []
            max_exp_avg_sqs = []
            state_steps = []
            amsgrad = group['amsgrad']

            for p in group['params']:
                if p.grad is None:
                    continue
                params_with_grad.append(p)
                if p.grad.is_sparse:
                    raise RuntimeError('AdamW does not support sparse gradients')
                grads.append(p.grad)

                state = self.state[p]

                # State initialization
                if len(state) == 0:
                    state['step'] = 0
                    # Exponential moving average of gradient values
                    state['exp_avg'] = torch.zeros_like(p, memory_format=torch.preserve_format)
                    # Exponential moving average of squared gradient values
                    state['exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)
                    if amsgrad:
                        # Maintains max of all exp. moving avg. of sq. grad. values
                        state['max_exp_avg_sq'] = torch.zeros_like(p, memory_format=torch.preserve_format)

                exp_avgs.append(state['exp_avg'])
                exp_avg_sqs.append(state['exp_avg_sq'])

                if amsgrad:
                    max_exp_avg_sqs.append(state['max_exp_avg_sq'])

                beta1, beta2 = group['betas']
                # update the steps for each param group update
                state['step'] += 1
                # record the step after step update
                state_steps.append(state['step'])

def read_json(filename: str) -> Union[list, dict]:
    """read json files"""
    with open(filename, "rb") as fin:
        data = json.load(fin, encoding="utf-8")
    return data

DATASETS = {
    'cifar': 1,
    'caltech101': 2,
    'dtd': 3,
    'oxford_flowers102': 4,
    'svhn': 5,
    'sun397': 6,
    'oxford_iiit_pet': 7,
    'natural': -1,
    'patch_camelyon': 8,
    'eurosat': 9,
    'resisc45': 10,
    'diabetic_retinopathy': 11,
    'specialized': -1,
    'clevr_count': 12,
    'clevr_dist': 13,
    'dmlab': 14,
    'kitti': 15,
    'dsprites_loc': 16,
    'dsprites_ori': 17,
    'smallnorb_azi': 18,
    'smallnorb_ele': 19,
    'structured': -1,
    'cub': 1,
    'nabirds': 2,
    'oxfordflower': 3,
    'stanforddog': 4,
    'stanfordcar': 5
}

def save_to_csv(path, dataset, acc):
    if not path.endswith('.csv'):
        path += '.csv'

    try:
        f = open(path, 'r')
    except:
        f = open(path, 'w', newline='')
        writer = csv.DictWriter(f, fieldnames=list(DATASETS.keys()))
        writer.writerow({})
        f.close()
        f = open(path, 'r')

    reader = csv.DictReader(f, fieldnames=list(DATASETS.keys()))

    # Always save to the last line
    my_dict = reader.__next__()
    f.close()

    my_dict[dataset] = acc
    f = open(path, 'w+')
    writer = csv.DictWriter(f, fieldnames=list(DATASETS.keys()))
    writer.writerow(my_dict)
    f.close()

def softmax(x, t=0.0005):
    x = [i / t for i in x]
    return np.exp(x) / np.sum(np.exp(x))


g = int(input())
p = int(input())
parent = [*range(g+1)]
left_max = [*range(g+1)]
docked = [False]*(g+2)

def find(node):
    if parent[node] == node:
        return node
    parent[node] = find(parent[node])
    left_max[parent[node]] = min(left_max[node], left_max[parent[node]])
    return parent[node]

r = 0
for _ in range(p):
    x = int(input())
    if docked[x]:
        x = left_max[find(x)]
    if not x:
        break
    r += 1
    docked[x] = True
    if docked[x-1] and docked[x+1]:
        parent[x-1] = x
        parent[x] = x+1
        find(x-1)
    elif docked[x-1]:
        parent[x-1] = x
        left_max[x] = left_max[x-1]
    elif docked[x+1]:
        parent[x] = x+1
        left_max[x] = x-1
        find(x)
    else:
        left_max[x] = x-1
print(r)


"""
Pure-Python3 implementation of Gradient Descent (https://en.wikipedia.org/wiki/Gradient_descent).
Written for educational/learning purposes and not performance.

Completed as part of the UTS course '35512 - Modelling Change' (https://handbook.uts.edu.au/subjects/35512.html).
"""
import random
from typing import List, Mapping, Optional, Dict, Set, Tuple

# Used to make chars like 'x' resemble typical mathematical symbols.
def _italic_str(text: str) -> str:
    return f"\x1B[3m{text}\x1B[23m"

def _superscript_exp(n: str) -> str:
    return "".join(["⁰¹²³⁴⁵⁶⁷⁸⁹"[ord(c) - ord('0')] for c in str(n)])

class Variable:
    """
    A object representing a mathematical variable, for use in building expressions.

    Usage: `x = Variable("x")`
    """
    def __init__(self, var: str):
        if len(var) != 1 or (not var.isalpha()):
            raise ValueError("Variable must be single alphabetical character. eg. 'x'")
        self.var = var

    def __repr__(self):
        return _italic_str(self.var)

    def __eq__(self, other):
        """Overrides the default implementation"""
        if isinstance(other, Variable):
            return self.var == other.var
        return False

    def __key(self):
        return self.var

    def __hash__(self):
        return hash(self.__key())

# An element of some set called a space. Here, that 'space' will be the domain of a multi-variable function.
Point = Dict[Variable, float]

class Expression:
    def diff(self, ref_var: Optional[Variable] = None) -> Optional["Expression"]:
        raise NotImplementedError

    def evaluate(self, point: Point) -> float:
        raise NotImplementedError

class ConstantExpression(Expression):
    """
    ConstantExpression is a single real-valued number.
    It cannot be parameterised and it's first-derivative is always 0 (None).
    """

    def __init__(self, real: float):
        super().__init__()
        self.real = real

    def diff(self, ref_var: Optional[Variable] = None) -> Optional[Expression]:
        return None

    def evaluate(self, point: Point) -> float:
        return self.real

    def __repr__(self):
        return str(self.real)

class PolynomialExpression(Expression):
    """
    An expression object that support evaluation and differentiation of single-variable polynomials.
    """
    def __init__(
            self,
            variable: Variable,
            coefficient: float,
            exponent: int
    ):
        super().__init__()
        self.var = variable
        self.coefficient = coefficient
        self.exp = exponent

    def diff(self, ref_var: Optional[Variable] = None) -> Optional[Expression]:
        if ref_var and ref_var != self.var:
            return None
        if self.exp == 1:
            return ConstantExpression(real=self.coefficient)
        return PolynomialExpression(
            variable=self.var,
            coefficient=self.coefficient * self.exp,
            exponent=self.exp - 1,
        )

    def evaluate(self, point: Point) -> float:
        return (
                self.coefficient *
                point[self.var] ** self.exp
        )

    def __repr__(self):
        return f"{self.coefficient}{self.var}{_superscript_exp(str(self.exp))}"

class Multiply(Expression):
    def __init__(self, a: PolynomialExpression, b: PolynomialExpression):
        self.a = a
        self.b = b

    def diff(self, ref_var: Optional[Variable] = None) -> Optional["Expression"]:
        if not ref_var:
            raise RuntimeError("Must pass ref_var when differentiating Multiply expression")
        if self.a.var == ref_var:
            diff_a = self.a.diff(ref_var=ref_var)
            if not diff_a:
                return None
            else:
                return Multiply(a=diff_a, b=self.b)
        elif self.b.var == ref_var:
            diff_b = self.b.diff(ref_var=ref_var)
            if not diff_b:
                return None
            else:
                return Multiply(a=self.a, b=diff_b)
        else:
            return None  # diff with respect to some non-involved variable is 0

    def evaluate(self, point: Point) -> float:
        return self.a.evaluate(point) * self.b.evaluate(point)

    def __repr__(self):
        return f"({self.a})({self.b})"

GradientVector = Dict[Variable, "MultiVariableFunction"]

class MultiVariableFunction:
    """
    MultiVariableFunction support the composition of expressions by addition into a
    function of multiple real-valued variables.

    Partial differentiation with respect to a single variable is supported, as is
    evaluation at a Point, and gradient finding.
    """

    def __init__(self, variables: Set[Variable], expressions: List[Expression]):
        self.vars = variables
        self.expressions = expressions

    def gradient(self) -> GradientVector:
        grad_v: GradientVector = {}
        for v in self.vars:
            grad_v[v] = self.diff(ref_var=v)
        return grad_v

    def diff(self, ref_var: Variable) -> "MultiVariableFunction":
        first_partial_derivatives: List[Expression] = []
        for expression in self.expressions:
            first_partial_diff = expression.diff(ref_var=ref_var)
            if first_partial_diff:
                first_partial_derivatives.append(first_partial_diff)
        return MultiVariableFunction(
            variables=self.vars,
            expressions=first_partial_derivatives,
        )

    def evaluate(self, point: Point) -> float:
        return sum(
            expression.evaluate(point)
            for expression
            in self.expressions
        )

    def __repr__(self):
        return " + ".join([str(e) for e in self.expressions])

def gradient_descent(
    gamma: float,
    max_iterations: int,
    f: MultiVariableFunction,
    initial_point: Optional[Point] = None,
) -> Tuple[float, Point]:
    """
    Implements Gradient Descent (https://en.wikipedia.org/wiki/Gradient_descent) in pure-Python3.6+ with
    no external dependencies.

    :param gamma: 'step size', or 'learning rate'
    :param max_iterations: Maximum number of steps in descent process.
    :param f: A differentiable function off multiple real-valued variables.
    :param initial_point: Optionally, a place to start the descent process
    :return: A tuple of first a local minimum and second the point at which minimum is found.
    """
    if gamma <= 0:
        raise ValueError("gamma value must be a positive real number, γ∈ℝ+")

    iterations_per_logline = 100
    a: Point = {}
    f_grad = f.gradient()

    if not initial_point:
        for v in f.vars:
            a[v] = random.randrange(4)
    else:
        a = initial_point
    for i in range(max_iterations):
        # Calculate function's gradient @ point `a`
        grad_a: Mapping[Variable, float] = {
            var: grad_elem.evaluate(a)
            for var, grad_elem
            in f_grad.items()
        }
        # update estimate of minimum point
        a_next = {
            var: current - (gamma * grad_a[var])
            for var, current
            in a.items()
        }
        a_prev = a
        a = a_next

        if a_prev == a:
            print("Iteration as not changed value. Stopping early.")
            break
        if i % iterations_per_logline == 0:
            print(f"Iteration {i}. Current min estimate: {a}")
    return f.evaluate(a), a


#import matplotlib as mpl
import networkx as nx
import warnings
warnings.filterwarnings("ignore")
import datetime
now1 = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print("时间1:", now1)
import pandas as pd
import numpy as np
import scanpy as sc
import matplotlib.pyplot as plt

category = "Adult Mouse Brain (FFPE)"
adata = sc.read(f"./h5ad2/{category}/"
                "final_Adult Mouse Brain (FFPE).h5ad")

att_df = pd.DataFrame(adata.uns['STMSGAL_attention'], index=adata.obs_names, columns=adata.obs_names)
att_df = att_df.values
for it in range(att_df.shape[0]):
    att_df[it, it] = 0

G_atten = nx.from_numpy_matrix(att_df)
M = G_atten.number_of_edges()
edge_colors = range(2, M + 2)

coor_df = pd.DataFrame(adata.obsm['spatial'].copy(), index=adata.obs_names)
coor_df[1] = -1 * coor_df[1]
image_pos = dict(zip(range(coor_df.shape[0]), [np.array(coor_df.iloc[it,]) for it in range(coor_df.shape[0])]))

labels = nx.get_edge_attributes(G_atten,'weight')
plt.rcParams["figure.figsize"] = (3, 3)
fig, ax = plt.subplots(figsize=[10,9])
nx.draw_networkx_nodes(G_atten, image_pos, node_size=5, ax=ax)
cmap = plt.cm.plasma
edges = nx.draw_networkx_edges(G_atten, image_pos, edge_color=labels.values(),width=4, ax=ax,
                               edge_cmap=cmap,edge_vmax=0.25,edge_vmin=0.05)
ax = plt.gca()

sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin = 0.05, vmax=0.25))
sm._A = []
plt.colorbar(sm)
plt.rcParams["figure.figsize"] = (3, 3)
ax.set_axis_off()
plt.axis('off')
plt.savefig(f'./outputs/{category}/attention0.5.jpg',
            bbox_inches='tight', dpi=200)
plt.show()
print(sm)


from urllib.request import urlopen, Request
import pandas as pd
import os
import time
import numpy as np
from datetime import datetime
import datetime as dt
import sys
from io import StringIO
from joblib import Parallel, delayed
import requests
from jailscrape.common import save_to_s3, get_browser, get_logger, record_error
from selenium.webdriver.common.keys import Keys
import watchtower
from bs4 import BeautifulSoup
import re
import math

ROW_INDEX = 420 # Change this for each scraper
def main(roster_row):
    try:
        logger = get_logger(roster_row)
        browser = get_browser()
        urlAddress = roster_row['Working Link']
        page_index = 0
        logger.info('Set working link to _%s_', urlAddress)
        #Boilerplate code setting up logger, getting initial URL

        #Given the urlAddress passed to the function we will navigate to the page
        browser.get(urlAddress)

        ##########
        # Begin core specific scraping code
        assert roster_row['State'].lower() == 'michigan'
        assert roster_row['County'].lower() == 'muskegon'
        """
        Site requres 'Load More' to be cicked multiple times; even with long
        wait times, it does not reliably load in time.

        Resulting file is large (~100 MB), and may not render inmate names
        visually.
        Search for phrase: "name of the inmate" to find roster of names.
        """

        #urlAddress = roster['Working Link'].values[index]
        #This will use the root directory defined at the top of the script to identify where the chromdriver for selium is located
        #Wait
        time.sleep(np.random.uniform(5,10,1))
        #Use elements like below to find xpath keys and click through

        finished = False

        prev_entries_loaded = 0
        #Iterate until the "Load More" button disappears:
        #(Button takes a few seconds to appear, hence longer wait time)
        while finished == False:
            store_source = browser.page_source
            soup = BeautifulSoup(store_source, 'lxml')
            entries_loaded = len(soup.find_all('div', {'class':'md-headline p2c-card-title ng-binding'}))
            if entries_loaded == prev_entries_loaded:
                finished = True
            else:
                #Wait
                logger.info('Waiting to load more...')
                time.sleep(np.random.uniform(15,30,1))
                secondtry = 2
                while secondtry > 0:
                    try:
                        elem = browser.find_element_by_xpath('//*[@id="inmatesCatalog"]/div[2]/div[2]/div[6]/button/span')
                        elem.click()
                        secondtry = 0
                    except:
                        secondtry -= 1
                        time.sleep(np.random.uniform(15,30,1))

            #Wait
            time.sleep(np.random.uniform(30,45))
            try:
                elem = browser.find_element_by_xpath('//*[@id="inmatesCatalog"]/div[2]/div[2]/div[6]/button/span')
                elem.click()
            except:
                finished = True

        page_data = browser.page_source
        save_to_s3(page_data, page_index, roster_row)
        logger.info('Saved page _%s_', page_index)
        # End core specific scraping code
        ##########

        #Close the browser
        logger.info('complete!')
        browser.close()

    except Exception as errorMessage:
        browser.close()
        record_error(str(errorMessage), page_index, roster_row)
        # Record error in S3
        logger.error('Error: %s', errorMessage)
        # Log error
        pass

if __name__ == "__main__":
    #This will load in the current jail roster list
    #Select the index of the roster this script is for:
    #Write the name of the county and state
    roster = pd.read_csv('/opt/jail_roster_final_rmDuplicates.csv',encoding = "utf-8")
    main(roster[roster['index'] == ROW_INDEX].iloc[0])


def extractCrappyMachineTranslation(item):
	"""

	"""
	vol, chp, frag, postfix = extractVolChapterFragmentPostfix(item['title'])
	if not (chp or vol or frag) or 'preview' in item['title'].lower():
		return None
	if 'Blade Online' in item['tags']:
		return buildReleaseMessageWithType(item, 'Blade Online', vol, chp, frag=frag, postfix=postfix)
	if "Another World's Savior" in item['tags']:
		return buildReleaseMessageWithType(item, "Another World's Savior", vol, chp, frag=frag, postfix=postfix)
	return False


import os

import cv2
import numpy as np
import scipy.ndimage
from torch.nn.functional import cosine_similarity

import torch
from matplotlib import pyplot as plt

def denormalize_(image):  # 去归一化
    image = image * (255 - 0) + 0
    return image

def array2image(array):
    image = np.ceil(255 * (array - np.min(array)) / (np.max(array) - np.min(array)))
    return image

def normalize_(array):
    array = array / np.max(array)
    return array

def ssim(prediction, target):
    C1 = (0.01 * 255) ** 2
    C2 = (0.03 * 255) ** 2
    img1 = prediction.astype(np.float64)
    img2 = target.astype(np.float64)
    kernel = cv2.getGaussianKernel(11, 0.5)
    window = np.outer(kernel, kernel.transpose())
    mu1 = cv2.filter2D(img1, -1, window)[5:-5, 5:-5]  # valid
    mu2 = cv2.filter2D(img2, -1, window)[5:-5, 5:-5]
    mu1_sq = mu1 ** 2
    mu2_sq = mu2 ** 2
    mu1_mu2 = mu1 * mu2
    sigma1_sq = cv2.filter2D(img1 ** 2, -1, window)[5:-5, 5:-5] - mu1_sq
    sigma2_sq = cv2.filter2D(img2 ** 2, -1, window)[5:-5, 5:-5] - mu2_sq
    sigma12 = cv2.filter2D(img1 * img2, -1, window)[5:-5, 5:-5] - mu1_mu2
    ssim_map = ((2 * mu1_mu2 + C1) *
                (2 * sigma12 + C2)) / ((mu1_sq + mu2_sq + C1) *
                                       (sigma1_sq + sigma2_sq + C2))
    return ssim_map.mean()

def calculate_ssim(target, ref):
    '''
    calculate SSIM
    the same outputs as MATLAB's
    img1, img2: [0, 255]
    '''
    img1 = np.array(target, dtype=np.float64)
    img2 = np.array(ref, dtype=np.float64)
    if not img1.shape == img2.shape:
        raise ValueError('Input images must have the same dimensions.')
    if img1.ndim == 2:
        return ssim(img1, img2)
    elif img1.ndim == 3:
        if img1.shape[2] == 3:
            ssims = []
            for i in range(3):
                ssims.append(ssim(img1[:, :, i], img2[:, :, i]))
            return np.array(ssims).mean()
        elif img1.shape[2] == 1:
            return ssim(np.squeeze(img1), np.squeeze(img2))
    else:
        raise ValueError('Wrong input image dimensions.')

def calculate_psnr(target, ref):
    img1 = np.array(target, dtype=np.float32)
    img2 = np.array(ref, dtype=np.float32)
    diff = img1 - img2
    psnr = 10.0 * np.log10(255.0 * 255.0 / np.mean(np.square(diff)))
    return psnr

def calculate_MSE(target, ref):
    H, W = target.shape[0], target.shape[1]
    img1 = np.array(target, dtype=np.float32)
    img2 = np.array(ref, dtype=np.float32)
    mse = ((img2 - img1) ** 2).sum() / (H * W)
    return mse

def patch2all(patch_set, numx, numy):
    # 把衍射图按照扫描位置拼接成一个大图
    positions0 = np.zeros((len(patch_set), 2), dtype=np.int32)
    positions0[:, 1] = np.tile(np.arange(numy) * patch_set.shape[1], numx)
    positions0[:, 0] = np.repeat(np.arange(numx) * patch_set.shape[2], numy)
    sample = np.zeros((numx * patch_set.shape[1], numy * patch_set.shape[2]))
    indi_x, indi_y = np.indices((patch_set.shape[1], patch_set.shape[2]))
    for i, pos in enumerate(positions0):
        sample[pos[0] + indi_x, pos[1] + indi_y] = patch_set[i, :, :]
    sample = np.array(sample)
    return sample

# 读取探针数据并将其弄到合适的大小
def probe_read(path, probe_size):
    probe = np.load(path)
    ori_shape = probe.shape
    y = (probe_size[0] - ori_shape[0]) // 2
    x = (probe_size[1] - ori_shape[1]) // 2
    probe = np.pad(probe, ((y, y), (x, x)))
    return probe

def mask(size, edge):
    '''
    generate mask to make sure target right
    parameter:
    size: the target ROI size
    edge: how many 0 pad to ROI round
    '''
    msk = np.ones(size)
    msk = np.pad(msk, ((edge, edge), (edge, edge)), constant_values=0)
    return msk

def cosine_similar(x, y):
    assert x.type() == 'torch.FloatTensor'
    assert y.type() == 'torch.FloatTensor'

    return cosine_similarity(x, y)

def checkpoint(net, opt, epoch, name, systime):
    save_model_path = os.path.join(opt.save_model_path, opt.log_name, systime)
    os.makedirs(save_model_path, exist_ok=True)
    model_name = 'epoch_{}_{:03d}.pth'.format(name, epoch)
    save_model_path = os.path.join(save_model_path, model_name)
    torch.save(net.state_dict(), save_model_path)
    print('Checkpoint saved to {}'.format(save_model_path))

def spinavej(x):
    '''
    Read the shape and dimensions of the input image
    '''
    shape = np.shape(x)
    dim = np.size(shape)

    if dim == 2:
        nr, nc = shape
        nrdc = np.floor(nr / 2) + 1
        ncdc = np.floor(nc / 2) + 1
        r = np.arange(nr) - nrdc + 1
        c = np.arange(nc) - ncdc + 1
        [R, C] = np.meshgrid(r, c)
        index = np.round(np.sqrt(R ** 2 + C ** 2)) + 1

    elif dim == 3:
        nr, nc, nz = shape
        nrdc = np.floor(nr / 2) + 1
        ncdc = np.floor(nc / 2) + 1
        nzdc = np.floor(nz / 2) + 1
        r = np.arange(nr) - nrdc + 1
        c = np.arange(nc) - ncdc + 1
        z = np.arange(nz) - nzdc + 1
        [R, C, Z] = np.meshgrid(r, c, z)
        index = np.round(np.sqrt(R ** 2 + C ** 2 + Z ** 2)) + 1
    else:
        print('Input is neither a 2D or 3D array')

    maxindex = np.max(index)
    output = np.zeros(int(maxindex), dtype=complex)

    if nr >= 512:
        print('Performed by pixel method')
        sumf = np.zeros(int(maxindex), dtype=complex)
        count = np.zeros(int(maxindex), dtype=complex)
        for ri in range(nr):
            for ci in range(nc):
                if index[ci, ri] <= maxindex:  # 边界检查
                    sumf[int(index[ci, ri]) - 1] += x[ri, ci]
                    count[int(index[ci, ri]) - 1] += 1
        output = sumf / count
        return output
    else:
        print('Performed by index method')
        indices = [np.where(index == i + 1) for i in np.arange(int(maxindex))]
        for i in np.arange(int(maxindex)):
            output[i] = np.sum(x[indices[i]]) / len(indices[i][0])
        return output

def compute_frc(original_image, reconstructed_image):
    """
    Compute the Fourier Ring Correlation (FRC) of two images.
    Parameters
    ----------
    original_image : ndarray
        The original image.
    reconstructed_image : ndarray
        The reconstructed image.
    Returns
    -------
    frc : ndarray
        The Fourier Ring Correlation (FRC) curve.
    """
    # 将图像转换为傅里叶空间
    original_image_fft = np.fft.fftshift(np.fft.fft2(original_image))
    reconstructed_image_fft = np.fft.fftshift(np.fft.fft2(reconstructed_image))

    # 计算两个图像在傅里叶空间中的相关性
    corr = spinavej(np.multiply(original_image_fft, np.conj(reconstructed_image_fft)))
    corr1 = spinavej(np.abs(original_image_fft) ** 2)
    corr2 = spinavej(np.abs(reconstructed_image_fft) ** 2)

    # 根据FRC的定义，计算相关性的平方根，并归一化到0到1的范围
    frc = corr / np.sqrt(corr1 * corr2)
    # 把frc归一化
    frc = frc / np.max(frc)
    frc = np.clip(frc, 0, 1)
    # frc = np.where(frc == 0, 1e-6, frc)  # 将FRC曲线中小于0.2071的值设为0

    # 绘制FRC曲线，并使用半比特阈值函数来确定分辨率
    freq = np.linspace(0, 0.05, len(frc))  # 计算空间频率

    half_bit = 0.2071 + 1.9102 / freq - 7.4774 / freq ** 2 + 6.8839 / freq ** 3  # 计算半比特阈值函数

    return frc, freq

if __name__ == '__main__':
    a = np.random.rand(1606, 2355)
    b = np.random.rand(1606, 2355)
    c, d = compute_frc(a, b)

    plt.plot(d, c)
    plt.show()


import random
import sys

def drawBoard(board):

    HLINE = ' +---+---+---+---+---+---+---+---+ '
    VLINE = ' |   |   |   |   |   |   |   |   | '

    print(' 1  2  3  4  5  6  7  8')
    print(HLINE)
    for y in range(8):
        print(VLINE)
        print(y+1, end='')
        for x in range(8):
            print('| %s' % (board[x][y]), end='')
        print('|')
        print(VLINE)
        print(HLINE)

def resetBoard(board):

    for x in range(8):
        for y in range(8):
            board[x][y] = ' '
            board[3][3] = 'X'
            board[3][4] = '0'
            board[4][3] = '0'
            board[4][4] = 'X'

def getNewBoard():

    board = []
    for i in range(8):
        board.append([''] * 8)

    return board

def isValidMove(board, tile, xstart, ystart):

        if board[xstart][ystart] != '' or not isOnBoard(xstart, ystart):
            return False
        board[xstart][ystart] = tile
        if tile == 'X':
            otherTile = '0'
        else:
            otherTile = 'X'
        tileToFlip = []

        for xdirection, ydirection in [[0, 1], [1, 1], [1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1]]:
            x, y = xstart, ystart
            x += xdirection()
            y += ydirection()

            if isOnBoard(x, y) and board[x][y] == otherTile:
                x += xdirection()
                y += ydirection()
                if not isOnBoard(x, y):
                    continue

                while board[x][y] == otherTile:
                    x += xdirection()
                    y += ydirection()
                    if not isOnBoard(x, y):
                        break
                    if not isOnBoard(x, y):
                        continue

                    if board[x][y] == tile:
                        while True:
                            x -= xdirection()
                            y -= ydirection()
                            if x == xstart and y == ystart:
                                break
                            tileToFlip.append([x,y])
        board[xstart][ystart] = ' '
        if len(tileToFlip) == 0:
            return False
        return tileToFlip

def isOnBoard(x, y):

    return x >= 0 and x <= 7 and y >= 0 and y <=  7

def getBoardWithValidMoves(board, tile):

    dupeBoard = getBoardCopy(board)

    for x, y in getValidMoves(dupeBoard, tile):
        dupeBoard[x][y] = '.'
    return dupeBoard

def getValidMoves(board, tile):

    validMoves = []

    for x in range(8):
        for y in range(8):
            if isValidMove(board, tile, x, y) != False:
                validMoves.append([x, y])
    return validMoves

def getScoreOfBoard(board):

    xscore = 0
    oscore = 0

    for x in range(8):
        for y in range(8):
            if board[x][y] == 'X':
                xscore += 1
            if board[x][y] == '0':
                oscore += 1
    return {'X':xscore, '0':oscore}

def enterPlayerTile():

    tile = ''

    while not (tile == 'X' or tile == '0'):
        print("Queres ser X ou 0?")
        tile = input().upper()

    if tile == 'X':
        return ['X', '0']
    else:
        return ['0', 'X']

def whoGoesFirst():

    if random.randint(0, 1) == 0:
        return 'computer'
    else:
        return 'player'

def playAgain():

    print('Queres jogar de novo? (sim ou não)')
    return input().lower().startwith('s')

def makeMove(board, tile, xstart, ystart):

    tilesToFlip = isValidMove(board, tile, xstart, ystart)

    if tilesToFlip == False:
        return False

    board[xstart][ystart] = tile
    for x, y in tilesToFlip:
        board[x][y] = tile
    return True

def getBoardCopy(board):

    dupeBoard = getNewBoard()

    for x in range(8):
        for y in range(8):
            dupeBoard[x][y] = board[x][y]
    return dupeBoard

def isOnCorner(x, y):

    return (x == 0 and y == 0) or (x == 7 and y == 0) or (x == 0 and y == 7) or (x == 7 and y == 7)

def getPlayerMove(board, playerTile):

    DIGITS1TO8 = '1 2 3 4 5 6 7 8'.split()

    while True:
            print('Insira seu movimento, ou escreve quit para terminar o jogo, ou hints para desligar/ligar dicas.')
            move = input().lower()
            if move == 'quit':
                return 'quit'
            if move == 'hints':
                return 'hints'
            if len(move) == 2 and move[2] in DIGITS1TO8:
                x = int(move[0]) - 1
                y = int(move[1]) - 1
                if isValidMove(board, playerTile, x, y) == False:
                    continue
                else:
                    break
            else:
                print('O movimento não é valido. Digite o digito x de (1-8), depois o digito y de (1-8)')
                print('Por exemplo, 81 sera o canto superior.')
    return [x, y]

def getComputerMove(board, computerTile):

    possibleMoves = getValidMoves(board, computerTile)
    random.shuffle(possibleMoves)

    for x, y in possibleMoves:
        if isOnCorner(x, y):
            return [x, y]

    bestScore = - 1
    for x, y in possibleMoves:
        dupeBoard = getBoardCopy(board)
        makeMove(dupeBoard, computerTile, x , y)
        score = getScoreOfBoard(dupeBoard)[computerTile]
        if score > bestScore:
            bestMove = [x, y]
            bestScore = score
    return bestMove

def showPoints(playerTile, computerTile):

    scores = getScoreOfBoard(mainBoard)
    print('Voce tem %s pontos. O computador tem %s pontos'%(scores[playerTile], scores[computerTile]))

print('Bem-vindo ao Reversi!')

while True:
    mainBoard = getNewBoard()
    resetBoard(mainBoard)
    playerTile, computerTile = enterPlayerTile()
    showHints = False
    turn = whoGoesFirst()
    print('O' + turn + 'vai primeiro.')

    while True:
        if turn == 'player':
            if showHints:
                validMovesBoard = getBoardWithValidMoves(mainBoard, playerTile)
                drawBoard(validMovesBoard)
            else:
                drawBoard(mainBoard)
            showPoints(playerTile, computerTile)
            move = getPlayerMove(mainBoard, playerTile)

            if move == 'quit':
                print('Obrigado por jogar!')
                sys.exit()
            elif move == 'hints':
                showHints = not showHints
                continue
            else:
                makeMove(mainBoard, playerTile, move[0], move[1])
            if getValidMoves(mainBoard, computerTile) == []:
                break
            else:
                turn = 'computer'
        else:
            drawBoard(mainBoard)
            showPoints(playerTile, computerTile)
            input('Pressiona enter para ver o movimento do computador.')
            x, y = getComputerMove(mainBoard, computerTile)
            makeMove(mainBoard, computerTile, x, y)

            if getValidMoves(mainBoard, playerTile) == []:
                break
            else:
                turn = 'player'

    drawBoard(mainBoard)
    scores = getScoreOfBoard(mainBoard)
    print('X marcou %s pontos. 0 marcou %s pontos.'%(scores['X'],scores['0']))
    if scores[playerTile] > scores[computerTile]:
        print('Voce ganhou o computador por %s pontos! Parabens!'%(scores[playerTile] - scores[computerTile]))
    elif scores[playerTile] < scores[computerTile]:
        print('Voce perdeu. O computador ganhou por %s pontos.'%(scores[computerTile] - scores[playerTile]))
    else:
        print('O jogo foi empate!')
    if not playAgain():
        break


#nmigen: UnusedElaboratable=no
import types, math

from nmigen import *
from nmigen.asserts import Assert, Assume

from gram.core.bankmachine import _AddressSlicer, BankMachine
from gram.test.utils import *

class AddressSlicerBijectionSpec(Elaboratable):
    def __init__(self, dut1, dut2):
        self.dut1 = dut1
        self.dut2 = dut2

    def elaborate(self, platform):
        m = Module()
        m.submodules.dut1 = dut1 = self.dut1
        m.submodules.dut2 = dut2 = self.dut2

        m.d.comb += Assert((dut1.address != dut2.address) == (Cat(dut1.row, dut1.col) != Cat(dut2.row, dut2.col)))
        return m

class AddressSlicerTestCase(FHDLTestCase):
    addrbits = 12
    colbits = 5
    address_align = 1

    def test_parameters(self):
        dut = _AddressSlicer(self.addrbits, self.colbits, self.address_align)
        self.assertEqual(dut.col.width, self.colbits)
        self.assertEqual(dut.address.width, self.addrbits)

    def test_bijection(self):
        dut1 = _AddressSlicer(self.addrbits, self.colbits, self.address_align)
        dut2 = _AddressSlicer(self.addrbits, self.colbits, self.address_align)
        spec = AddressSlicerBijectionSpec(dut1, dut2)
        self.assertFormal(spec, "bmc", depth=1)

class BankMachineRequestGrantSpec(Elaboratable):
    def __init__(self, dut):
        self.dut = dut

    def elaborate(self, platform):
        m = Module()
        m.d.comb += Assume(~dut.request_req)
        m.d.comb += Assert(~dut.request_gnt)
        return m

class BankMachineTestCase(FHDLTestCase):
    settings = types.SimpleNamespace()
    settings.cmd_buffer_depth = 1
    settings.cmd_buffer_buffered = False
    settings.with_auto_precharge = False
    settings.geom = types.SimpleNamespace()
    settings.geom.addressbits = 20
    settings.geom.colbits = 8
    settings.geom.rowbits = 12
    settings.geom.bankbits = 3
    settings.timing = types.SimpleNamespace()
    settings.timing.tWR = 5
    settings.timing.tCCD = 4
    settings.timing.tRC = 6
    settings.timing.tRAS = 7
    settings.timing.tRP = 10
    settings.timing.tRCD = 8
    settings.phy = types.SimpleNamespace()
    settings.phy.cwl = 6
    settings.phy.nphases = 2

    def test_no_request_grant(self):
        dut = BankMachine(0, 20, 2, 1, self.settings)
        self.assertFormal(dut, "bmc", depth=21)