from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.background = QtWidgets.QLabel(self.centralwidget)
        self.background.setGeometry(QtCore.QRect(-170, -50, 2291, 1191))
        self.background.setText("")
        self.background.setPixmap(QtGui.QPixmap("../Images/bg.png"))
        self.background.setScaledContents(True)
        self.background.setObjectName("background")
        self.label_1 = QtWidgets.QLabel(self.centralwidget)
        self.label_1.setGeometry(QtCore.QRect(20, 10, 1891, 1021))
        self.label_1.setText("")
        self.label_1.setPixmap(QtGui.QPixmap("../Images/background_2.png"))
        self.label_1.setScaledContents(True)
        self.label_1.setObjectName("label_1")
        self.label_wa = QtWidgets.QLabel(self.centralwidget)
        self.label_wa.setGeometry(QtCore.QRect(180, 320, 441, 161))
        self.label_wa.setText("")
        self.label_wa.setPixmap(QtGui.QPixmap("../Images/new_tab.png"))
        self.label_wa.setScaledContents(True)
        self.label_wa.setObjectName("label_wa")
        self.label_mc = QtWidgets.QLabel(self.centralwidget)
        self.label_mc.setGeometry(QtCore.QRect(180, 710, 441, 161))
        self.label_mc.setText("")
        self.label_mc.setPixmap(QtGui.QPixmap("../Images/new_tab.png"))
        self.label_mc.setScaledContents(True)
        self.label_mc.setObjectName("label_mc")
        self.label_ig = QtWidgets.QLabel(self.centralwidget)
        self.label_ig.setGeometry(QtCore.QRect(180, 510, 441, 161))
        self.label_ig.setText("")
        self.label_ig.setPixmap(QtGui.QPixmap("../Images/new_tab.png"))
        self.label_ig.setScaledContents(True)
        self.label_ig.setObjectName("label_ig")
        self.label_yt = QtWidgets.QLabel(self.centralwidget)
        self.label_yt.setGeometry(QtCore.QRect(180, 110, 441, 161))
        self.label_yt.setText("")
        self.label_yt.setPixmap(QtGui.QPixmap("../Images/new_tab.png"))
        self.label_yt.setScaledContents(True)
        self.label_yt.setObjectName("label_yt")
        self.Gif_1 = QtWidgets.QLabel(self.centralwidget)
        self.Gif_1.setGeometry(QtCore.QRect(760, 270, 720, 405))
        self.Gif_1.setText("")
        self.Gif_1.setPixmap(QtGui.QPixmap("../Gifs/center.gif"))
        self.Gif_1.setScaledContents(True)
        self.Gif_1.setObjectName("Gif_1")
        self.Gif_4 = QtWidgets.QLabel(self.centralwidget)
        self.Gif_4.setGeometry(QtCore.QRect(850, 700, 571, 141))
        self.Gif_4.setText("")
        self.Gif_4.setPixmap(QtGui.QPixmap("../Gifs/listening.gif"))
        self.Gif_4.setScaledContents(True)
        self.Gif_4.setObjectName("Gif_4")
        self.Gif_3 = QtWidgets.QLabel(self.centralwidget)
        self.Gif_3.setGeometry(QtCore.QRect(920, 60, 431, 201))
        self.Gif_3.setText("")
        self.Gif_3.setPixmap(QtGui.QPixmap("../Gifs/INITILIZING.gif"))
        self.Gif_3.setScaledContents(True)
        self.Gif_3.setObjectName("Gif_3")
        self.Gif_2 = QtWidgets.QLabel(self.centralwidget)
        self.Gif_2.setGeometry(QtCore.QRect(1500, 110, 351, 261))
        self.Gif_2.setText("")
        self.Gif_2.setPixmap(QtGui.QPixmap("../Gifs/round_1.gif"))
        self.Gif_2.setScaledContents(True)
        self.Gif_2.setObjectName("Gif_2")
        self.label_9 = QtWidgets.QLabel(self.centralwidget)
        self.label_9.setGeometry(QtCore.QRect(1500, 560, 261, 121))
        self.label_9.setText("")
        self.label_9.setPixmap(QtGui.QPixmap("../Images/Start.png"))
        self.label_9.setScaledContents(True)
        self.label_9.setObjectName("label_9")
        self.label_10 = QtWidgets.QLabel(self.centralwidget)
        self.label_10.setGeometry(QtCore.QRect(1500, 700, 261, 111))
        self.label_10.setText("")
        self.label_10.setPixmap(QtGui.QPixmap("../Images/quiet.png"))
        self.label_10.setScaledContents(True)
        self.label_10.setObjectName("label_10")
        self.date_time_day_temp = QtWidgets.QTextBrowser(self.centralwidget)
        self.date_time_day_temp.setGeometry(QtCore.QRect(230, 890, 521, 81))
        self.date_time_day_temp.setStyleSheet("background-color: Transparent;\n"
"color: rgb(85, 255, 255);\n"
"border-color: rgb(0, 0, 255);")
        self.date_time_day_temp.setObjectName("date_time_day_temp")
        self.pushButton_start = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_start.setGeometry(QtCore.QRect(1510, 580, 241, 81))
        self.pushButton_start.setStyleSheet("background-color: Transparent;")
        self.pushButton_start.setText("")
        self.pushButton_start.setObjectName("pushButton_start")
        self.pushButton_quit = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_quit.setGeometry(QtCore.QRect(1510, 710, 241, 81))
        self.pushButton_quit.setStyleSheet("background-color: Transparent;")
        self.pushButton_quit.setText("")
        self.pushButton_quit.setObjectName("pushButton_quit")
        self.pushButton_youtube = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_youtube.setGeometry(QtCore.QRect(240, 140, 331, 91))
        self.pushButton_youtube.setStyleSheet("background-color: Transparent;\n"
"font: 75 28pt \"Arial\";\n"
"color: rgb(0, 255, 255);")
        self.pushButton_youtube.setObjectName("pushButton_youtube")
        self.pushButton_whatapp = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_whatapp.setGeometry(QtCore.QRect(240, 350, 331, 91))
        self.pushButton_whatapp.setStyleSheet("background-color: Transparent;\n"
"font: 75 28pt \"Arial\";\n"
"color: rgb(0, 255, 255);")
        self.pushButton_whatapp.setObjectName("pushButton_whatapp")
        self.pushButton_instagram = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_instagram.setGeometry(QtCore.QRect(230, 540, 331, 91))
        self.pushButton_instagram.setStyleSheet("background-color: Transparent;\n"
"font: 75 28pt \"Arial\";\n"
"color: rgb(0, 255, 255);")
        self.pushButton_instagram.setObjectName("pushButton_instagram")
        self.pushButton_myclass = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_myclass.setGeometry(QtCore.QRect(240, 740, 331, 91))
        self.pushButton_myclass.setStyleSheet("background-color: Transparent;\n"
"font: 75 28pt \"Arial\";\n"
"color: rgb(0, 255, 255);")
        self.pushButton_myclass.setObjectName("pushButton_myclass")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1922, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.date_time_day_temp.setHtml(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<meta name=\"qrichtext\" content=\"1\" />\n"
"

"))
        self.pushButton_youtube.setText(_translate("MainWindow", "YOU TUBE"))
        self.pushButton_whatapp.setText(_translate("MainWindow", "WHAT\'S APP"))
        self.pushButton_instagram.setText(_translate("MainWindow", "INSTAGRAM"))
        self.pushButton_myclass.setText(_translate("MainWindow", "MY CLASS"))

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    MainWindow.showFullScreen()
    sys.exit(app.exec_())


import random
import math

def getPi():
    """
    运用Monte Carno方法计算圆周率的近似值
    :return: float
    """
    count = 0
    n = 10000000
    for _ in range(n):
        x = random.random()
        y = random.random()
        if math.sqrt(x ** 2 + y ** 2) <= 1:
            count += 1
    return 4 * count / n

print(getPi())


import datetime
import math

def get_julian_datetime(date):
    """
    Convert a datetime object into julian float.
    Args:
        date: datetime-object of date in question

    Returns: float - Julian calculated datetime.
    Raises:
        TypeError : Incorrect parameter type
        ValueError: Date out of range of equation
    """

    # Ensure correct format
    if not isinstance(date, datetime.datetime):
        raise TypeError('Invalid type for parameter "date" - need datetime')
    if date.year < 1801 or date.year > 2099:
        raise ValueError('Datetime must be between year 1801 and 2099')

    # Perform the calculation
    julian_datetime = (367 * date.year
                       - int((7*(date.year+int((date.month+9)/12.0)))/4.0)
                       + int((275*date.month)/9.0)
                       + date.day
                       + 1721013.5
                       + (date.hour
                          + date.minute/60.0
                          + date.second/math.pow(60, 2))/24.0
                       - 0.5*math.copysign(1, (100*date.year
                                               + date.month-190002.5))
                       + 0.5)

    return julian_datetime

# GATHER OBSERVATIONS
lcdb_fdir = "/home/xubu/NEFE/LCDB/LC_DAT_PUB.TXT"
observations = []
dates = []
with open(lcdb_fdir, "r") as lcdb_file:
    lines = lcdb_file.readlines()[5:]
    counter = 0
    numslst = []
    object_counter = 0
    quality_dict = {"": -1.0, "0": 0.0,
                    "1-": 1.0, "1": 2.0, "1+": 3.0,
                    "2-": 4.0, "2": 5.0, "2+": 6.0,
                    "3-": 7.0, "3": 8.0}
    for lnidx, line in enumerate(lines):
        if len(line.strip()) == 0:
            continue
        varstrs = []
        split_pos = [7, 9, 40, 61, 70, 72, 78, 80, 82, 91, 93, 100, 102, 105,
                     107, 114, 116, 130, 146, 148, 153, 158, 161, 167, 171,
                     175, 179, 185, 189]
        for idx, pos in enumerate(split_pos):
            if idx == 0:
                previous_pos = 0
            else:
                previous_pos = split_pos[idx-1]
            varstrs.append(line[previous_pos:pos])
        for idx, item in enumerate(varstrs):
            varstrs[idx] = item.strip()

        MPCnum, entryflag, namestr, col_3, family, csource, clss = varstrs[0:7]
        dsource, dflag, diam, hsource, h, hband, asource = varstrs[7:14]
        albedoflag, alb, pflag, period, pdescrip, ampflag = varstrs[14:20]
        ampmin, ampmax, u, notes, binary, private, pole = varstrs[20:27]
        survey, extnotes = varstrs[27:]
        if hband == "":
            hband = "V"
        try:
            MPCnum = int(MPCnum)
        except ValueError:
            if MPCnum != "":
                print(lnidx)
                print("MPCnum: ", MPCnum)
            MPCnum = None
        qual_obs = quality_dict[u]
        float_list = [diam, h, alb, period, ampmin, ampmax]
        for idx, item in enumerate(float_list):
            try:
                float_list[idx] = float(item)
            except ValueError:
                if item != "":
                    print(lnidx)
                    print(idx, "float item: ", item)
                float_list[idx] = None
        diam, h, alb, period, ampmin, ampmax = float_list
        if MPCnum is not None:
            object_counter = object_counter + 1
            mpc_number = MPCnum
            name = namestr
            desig = col_3
            diameter = diam
            obj_class = clss
            fam = family
            abs_mag = h
            albedo = alb
            qual_gen = qual_obs
            minamp = ampmin
            maxamp = ampmax
            bin_flag = binary
            pole_availability = pole
            srvy_nm = survey
        try:
            date = datetime.datetime.strptime(col_3, '%Y-%m-%d')
        except ValueError:
            date = None
        if (isinstance(date, datetime.datetime) and isinstance(period, float)
            and isinstance(diameter, float)):
            date = str(get_julian_datetime(date))
            observations.append([object_counter, mpc_number, name, desig,
                                 date, period, diameter, abs_mag,
                                 obj_class, fam, albedo, qual_obs, qual_gen,
                                 minamp, maxamp,
                                 bin_flag, pole_availability, srvy_nm])
            dates.append(date)
            counter = counter + 1


import numpy as np
from collections import namedtuple, defaultdict, Counter
from itertools import product, combinations
import json
import argparse
import input_output
import os
import traceback

parser = argparse.ArgumentParser(description='Check that all families were phased.')
parser.add_argument('phase_dir', type=str, help='Directory with phase data.')
parser.add_argument('data_dir', type=str, default=None, help='Directory gen data.')
parser.add_argument('param_file', type=str, default=None, help='Param file.')
parser.add_argument('--family_size', type=int, default=None, help='Family size.')

args = parser.parse_args()

with open('%s/info.json' % args.phase_dir, 'r') as f:
	info = json.load(f)

ped_file = info['ped_file']

if args.data_dir is None:
	data_dir = info['data_dir']
else:
	data_dir = args.data_dir

if args.param_file is None:
	param_file = info['param_file']
else:
	param_file = args.param_file

# start by pulling families, only consider nuclear families
families = input_output.pull_families(ped_file)
families = [x for x in families if x.num_ancestors()==2 and len(x.ordered_couples)==1]

sample_file = '%s/samples.json' % data_dir
with open(sample_file, 'r') as f:
    sample_ids = set(json.load(f))

with open(param_file, 'r') as f:
    params = json.load(f)

for family in families:
    to_be_removed = [x for x in family.individuals if x not in sample_ids]
    family.prune(to_be_removed)

families = [x for x in families if x.num_descendents()>0]
print(len(families), 'have genomic data')

# if we have a family size limit, then enforce
if args.family_size is not None:
	families = [x for x in families if len(x)==args.family_size]
	print(len(families), 'of size', args.family_size)

for family in families:
    to_be_removed = [x for x in family.individuals if (x not in params and '%s.%s' % (family.id, x) not in params)]
    family.prune(to_be_removed)

families = [x for x in families if x.num_descendents()>0]
print(len(families), 'have parameters')

#pulls phase data from a file
def pull_phased_chroms(filename):
	with open(filename, 'r') as f:
		header = next(f).strip().split('\t')[1:-2] # skip header
		individuals = [x[:-4] for x in header if x.endswith('_mat')]

		chrs = set()
		for line in f:
			pieces = line.strip().split('\t', maxsplit=1)
			chrs.add(pieces[0][3:])

	return chrs, individuals

def write_to_file(outf, chrom, positions, states):
	# write final states to file
	change_indices = [-1] + np.where(np.any(states[1:, :]!=states[:-1, :], axis=1))[0].tolist() + [positions.shape[0]-1]
	for j in range(1, len(change_indices)):
		start_index, end_index = change_indices[j-1]+1, change_indices[j]
		outf.write('%s\t%s\t%d\t%d\n' % (
						'chr' + chrom,
						'\t'.join(map(str, states[start_index, :])),
						positions[start_index, 0], positions[end_index, 1]))

# writes a recomb only file
def create_recomb_file(filename):
	states = []
	positions = []
	current_chrom = None
	with open(filename, 'r') as f, open(filename + '.recomb.txt', 'w+') as outf:
		header = next(f).strip().split('\t')[1:-2] # skip header

		for line in f:
			pieces = line.strip().split('\t')
			chrom = pieces[0][3:]
			if current_chrom is None:
				current_chrom = chrom
				positions.append([int(x) for x in pieces[-2:]])
				states.append([int(x) for x in pieces[1:-2]])
			elif chrom == current_chrom:
				positions.append([int(x) for x in pieces[-2:]])
				states.append([int(x) for x in pieces[1:-2]])
			else:
				# write data from previous chrom
				states = np.array(states)
				positions = np.array(positions)

				# ignore deletions and haplotypes, we only care about crossovers
				states[:, :4] = -1

				# if this is a hard to sequences region, we don't know the exact location of crossovers
				states[states[:, -1]==1, :] = -1

				# ignore hard to sequence regions as well
				#states[:, -1] = -1

				write_to_file(outf, current_chrom, positions, states)

				# reset for next chrom
				current_chrom = chrom
				positions = [[int(x) for x in pieces[-2:]]]
				states = [[int(x) for x in pieces[1:-2]]]

		# write data from last chrom
		states = np.array(states)
		positions = np.array(positions)

		# ignore deletions and haplotypes, we only care about crossovers
		states[:, :4] = -1

		# if this is a hard to sequences region, we don't know the exact location of crossovers
		states[states[:, -1]==1, :] = -1

		# ignore hard to sequence regions as well
		#states[:, -1] = -1

		write_to_file(outf, current_chrom, positions, states)

has_no_data = []
has_missing_chroms = []
has_missing_individuals = []
is_missing_phase_data = []
all_good = 0

for family in families:
	phase_filename = '%s/%s.phased.txt' % (args.phase_dir, family.id)
	if os.path.isfile(phase_filename):
		try:
			chroms, individuals = pull_phased_chroms(phase_filename)
			missing_chroms = set([str(x) for x in range(1, 23)]) - chroms
			if len(missing_chroms)>0:
				has_missing_chroms.append(family.id)
			elif len(individuals) != len(family.individuals):
				has_missing_individuals.append((family.id, set(family.individuals)-set(individuals)))
			else:
				all_good += 1
				#create_recomb_file(phase_filename)
		except StopIteration:
			has_no_data.append(family.id)
	else:
		is_missing_phase_data.append(family.id)

print('has no data', has_no_data)
print('has missing chroms', has_missing_chroms)
print('has missing individuals', has_missing_individuals)
print('is missing phase data', is_missing_phase_data)
print('all good', all_good)


from celery.decorators import task
from gypsum.positioning.models import Track
from django.contrib.auth.models import User
from gypsum.positioning import weather
from datetime import timedelta

def enumerate_tracks():
    for u in User.objects.all():
        last_date = None
        n = 0
        for t in Track.objects.filter(owner=u).order_by('date'):
            date = t.date
            if  last_date == None or last_date == date:
                n = n + 1
            else:
                n = 0

            t.number = n
            t.save()

            last_date = date

@task
def get_track_weather():
    for t in Track.objects.filter(has_weather=None)[0:10]:
        try:
            c = t.center_coordinate()
            w = weather.get_weather_from_location(c[0], c[1], t.positions()[0].time + timedelta(seconds=t.time / 2))
            t.temperature = w.temperature
            t.weather_conditions = w.conditions
            t.precipitation = w.precipitation_cm
            t.has_weather = True
        except weather.WeatherNotAvailableError:
            t.has_weather = False

        t.save()


import asyncio
import time
from pprint import pprint

from jopify.celery import app
from job_scraper.models import *
from scraper.stackoverflow import StackOverflowScrapper

def create_new_job(job):
    if "technology" in job:
        for key in job["technology"]:
            job[key] = job["technology"][key]

        job.pop("technology", None)

    return Job(**job)

@app.task
def scrape_job():
    start = time.time()
    scraper = StackOverflowScrapper()

    jobs = asyncio.run(scraper.extract_jobs())
    for job in jobs:
        if not Job.objects.filter(url=job["url"]).exists():
            new_job = create_new_job(job)

            new_job.save()

    print(f"Elapsed time for {len(jobs)} jobs ({scraper.n_failed_request} failed.): {time.time() - start}")

if __name__ == '__main__':
    scrape_job()


# -*- coding: utf-8 -*-
"""
@author:XuMing(xuming624@qq.com)
@description:
"""

from typing import Any, List, Tuple

import torch
import torch.nn as nn

# constants:
CHECKPOINT_FILE = "checkpoint.torch"
CPU_DEVICE = torch.device("cpu")
GPU_DEVICE = torch.device("cuda")

def is_pos_int(number: int) -> bool:
    """
    Returns True if a number is a positive integer.
    """
    return type(number) == int and number >= 0

def is_pos_float(number: float) -> bool:
    """
    Returns True if a number is a positive float.
    """
    return type(number) == float and number >= 0.0

def is_pos_int_list(l: List) -> bool:
    """
    Returns True if a list contains positive integers
    """
    return type(l) == list and all(is_pos_int(n) for n in l)

def is_pos_int_tuple(t: Tuple) -> bool:
    """
    Returns True if a tuple contains positive integers
    """
    return type(t) == tuple and all(is_pos_int(n) for n in t)

def is_long_tensor(tensor: torch.Tensor) -> bool:
    """
    Returns True if a tensor is a long tensor.
    """
    if torch.is_tensor(tensor):
        return tensor.type().endswith("LongTensor")
    else:
        return False

def is_float_tensor(tensor: torch.Tensor) -> bool:
    """
    Returns True if a tensor is a float tensor.
    """
    if torch.is_tensor(tensor):
        return tensor.type().endswith("FloatTensor")
    else:
        return False

def is_double_tensor(tensor: torch.Tensor) -> bool:
    """
    Returns True if a tensor is a double tensor.
    """
    if torch.is_tensor(tensor):
        return tensor.type().endswith("DoubleTensor")
    else:
        return False

def is_leaf(module: nn.Module) -> bool:
    """
    Returns True if module is leaf in the graph.
    """
    assert isinstance(module, nn.Module), "module should be nn.Module"
    return len([c for c in module.children()]) == 0 or hasattr(module, "_mask")

def is_on_gpu(model: torch.nn.Module) -> bool:
    """
    Returns True if all parameters of a model live on the GPU.
    """
    assert isinstance(model, torch.nn.Module)
    on_gpu = True
    has_params = False
    for param in model.parameters():
        has_params = True
        if not param.data.is_cuda:
            on_gpu = False
    return has_params and on_gpu

def is_not_none(sample: Any) -> bool:
    """
    Returns True if sample is not None and constituents are not none.
    """
    if sample is None:
        return False

    if isinstance(sample, (list, tuple)):
        if any(s is None for s in sample):
            return False

    if isinstance(sample, dict):
        if any(s is None for s in sample.values()):
            return False
    return True


#!/usr/bin/env python3

import sys

def clean_sequence(sequence):
    cleaned_sequence = ''.join(['N' if c not in 'ATGCN-' else c for c in sequence])
    return cleaned_sequence

if len(sys.argv) != 3:
    print("Usage: {} input.fasta output.fasta".format(sys.argv[0]))
    sys.exit(1)

input_file = sys.argv[1]
output_file = sys.argv[2]

with open(input_file, 'r', encoding='utf-8') as in_fasta, open(output_file, 'w') as out_fasta:
    for line in in_fasta:
        if line.startswith('>'):
            out_fasta.write(line)
        else:
            cleaned_sequence = clean_sequence(line.strip())
            out_fasta.write(cleaned_sequence + '\n')

print("FASTA file cleaned and saved to", output_file)


#-----------------------------------------------------------------------------
# Description: Example of calling the NoFrixion MoneyMoov API payouts PUT
# method. It provides a convenient way to modify a previously created payout
#
# Usage:
# 1. Create a user access token in the sandbox portal at:
#    https:#portal-sandbox.nofrixion.com.
# 2. Set the token as an environment variable in your console:
#    set NOFRIXION_USER_TOKEN=<JWT token from previous step>
# 3. Run the script using the command: python -u "filename"
# 4. If successful the updated payout object will be displayed.
#-----------------------------------------------------------------------------

# The 'requests' library for Python can be used to make calls to the MoneyMoov API in popular python frameworks such as Django and Flask.
import requests
import os

# Remember, the JWT access token must be securely stored ('os' module above allows storage in environment variable)
jwtToken = os.environ['NOFRIXION_USER_TOKEN']

baseUrl = "https://api-sandbox.nofrixion.com/api/v1/payouts"

# id of payout to update must be specified
payoutId = "432629ae-d126-41d6-002b-08d9f815df7c"

headers = {
    "Accept": "application/json",
    "Authorization": f"Bearer {jwtToken}"
}

payoutData = {
    "AccountID": "A120P0JR",
    "Currency": "EUR",
    "Amount": "0.01",
    "YourReference": "Updated payout",
    "DestinationIBAN": "GB94BARC10201530093459",
    "DestinationAccountName": "Destination Name",
    "TheirReference": "Their reference"
}

response = requests.request("PUT", f"{baseUrl}/{payoutId}", headers=headers, data=payoutData)

if response.ok:
    # API returns updated payout object response body.
    print(response.json())
else:
    # If not OK, response contains MoneyMoov problem ( see https://docs.nofrixion.com/reference/error-messages for details)
    print(response.json())


import json
from box import Box
from .helpers import check_required_keys, fatal
from .images import (
    resample_ct_like_spect,
    resample_roi_like_spect,
    convert_ct_to_densities,
)
from .opendose import get_svalue_and_mass_scaling
from pathlib import Path
import os
import opengate as gate
from opengate import g4_units
from opengate.geometry.materials import HounsfieldUnit_to_material
from opengate.image import get_translation_between_images_center, read_image_info
import pkg_resources
import SimpleITK as itk
import numpy as np

def read_dose_rate_options(json_file):
    print(json_file)
    if json_file is None:
        options = init_dose_rate_options()
    else:
        f = open(json_file).read()
        options = Box(json.loads(f))
    check_dose_rate_options(options)
    return options

def check_dose_rate_options(options):
    ref = init_dose_rate_options()
    check_required_keys(options, ref.keys())

def init_dose_rate_options():
    options = Box()
    options.number_of_threads = 1
    options.density_tolerance_gcm3 = 0.1
    return options

def get_timepoint_output_folder(output_folder, cycle, timepoint, name="doserate"):
    folder = (
            Path(output_folder)
            / Path(cycle.cycle_id)
            / Path(timepoint.acquisition_id)
            / name
    )
    os.makedirs(folder, exist_ok=True)
    return folder

def simu_default_init(sim):
    sim.visu_type = "vrml"
    m = gate.g4_units.m
    world = sim.world
    world.size = [2 * m, 2 * m, 2 * m]

    # cuts
    sim.physics_manager.physics_list_name = "G4EmStandardPhysics_option3"
    sim.physics_manager.enable_decay = True
    sim.physics_manager.set_production_cut("world", "all", 1 * m)

    # add stat actor
    stats = sim.add_actor("SimulationStatisticsActor", "stats")
    stats.track_types_flag = True
    stats.output = "stats.txt"

def sim_add_waterbox(sim, ct_filename):
    wb = sim.add_volume("Box", "waterbox")
    info = read_image_info(ct_filename)
    wb.size = info.size
    wb.material = "G4_WATER"
    wb.color = [0, 0, 1, 1]
    return wb

def simu_add_ct(sim, ct_filename, density_tolerance_gcm3):
    if sim.visu:
        return sim_add_waterbox(sim, ct_filename)
    ct = sim.add_volume("Image", "ct")
    ct.image = ct_filename
    # material used by default
    ct.material = "G4_AIR"
    gcm3 = g4_units.g_cm3
    tol = density_tolerance_gcm3 * gcm3
    # default tables
    table_mat = pkg_resources.resource_filename(
        "rpt_dosi", "data/Schneider2000MaterialsTable.txt"
    )
    table_density = pkg_resources.resource_filename(
        "rpt_dosi", "data/Schneider2000DensitiesTable.txt"
    )
    ct.voxel_materials, materials = HounsfieldUnit_to_material(
        sim, tol, table_mat, table_density
    )
    print(f"Density tolerance = {tol / gcm3} gcm3")
    print(f"Number of materials in the CT : {len(ct.voxel_materials)} materials")
    # ct.dump_label_image = "labels.mhd"

    # production cut
    mm = gate.g4_units.mm
    sim.physics_manager.set_production_cut("ct", "all", 1 * mm)
    return ct

def simu_add_activity_source(
        sim,
        ct,
        activity_filename,
        rad,
):
    rad_list = {
        "Lu177": {"Z": 71, "A": 177, "name": "Lutetium 177"},
        "Y90": {"Z": 39, "A": 90, "name": "Yttrium 90"},
        "In111": {"Z": 49, "A": 111, "name": "Indium 111"},
        "I131": {"Z": 53, "A": 131, "name": "Iodine 131"},
    }

    # Activity source from an image
    source = sim.add_source("VoxelsSource", "vox")
    source.mother = ct.name
    source.particle = "ion"
    source.ion.Z = rad_list[rad]["Z"]
    source.ion.A = rad_list[rad]["A"]
    source.image = activity_filename
    source.direction.type = "iso"
    keV = g4_units.keV
    source.energy.mono = 0 * keV
    if ct.name == "ct":
        source.position.translation = get_translation_between_images_center(
            ct.image, activity_filename
        )
    return source

def add_dose_actor(sim, ct, source):
    # add dose actor (get the same size as the source)
    source_info = read_image_info(source.image)
    dose = sim.add_actor("DoseActor", "dose")
    dose.output = "edep.mhd"
    dose.mother = ct.name
    dose.size = source_info.size
    dose.spacing = source_info.spacing
    # translate the dose the same way as the source
    dose.translation = source.position.translation
    # set the origin of the dose like the source
    if not sim.user_info.visu:
        dose.img_coord_system = True
    dose.hit_type = "random"
    # dose.hit_type = "pre"
    dose.uncertainty = True
    dose.square = True
    dose.gray = True
    return dose

def scale_to_absorbed_dose_rate(
        activity,
        dose_in_gray,
        simu_activity,
        calibration_factor,
        verbose=True,
):
    dose_a = itk.GetArrayFromImage(dose_in_gray)
    activity_a = itk.GetArrayFromImage(activity)

    volume_voxel_mL = np.prod(activity.GetSpacing()) / 1000
    total_activity = np.sum(activity_a) * volume_voxel_mL / calibration_factor

    if verbose:
        print(f"Total activity in the image FOV: {total_activity / 1e6:.2f} MBq")

    print(f"dose mean = {np.mean(dose_a)} gray.s-1")
    dose_a = dose_a / simu_activity * total_activity
    print(f"dose mean after scaling = {np.mean(dose_a)} gray.s-1")

    # create output image
    o = itk.GetImageFromArray(dose_a)
    o.CopyInformation(dose_in_gray)
    return o

def spect_calibration(spect, calibration_factor, concentration_flag, verbose=True):
    # get voxel volume
    volume_voxel_mL = np.prod(spect.GetSpacing()) / 1000
    arr = itk.GetArrayFromImage(spect)
    total_activity = np.sum(arr) * volume_voxel_mL / calibration_factor
    if verbose:
        print(f"Total activity in the image FOV: {total_activity / 1e6:.2f} MBq")
    # calibration
    if concentration_flag:
        arr = arr * volume_voxel_mL / calibration_factor
    else:
        arr = arr / calibration_factor

    # create output image
    o = itk.GetImageFromArray(arr)
    o.CopyInformation(spect)
    return o

def dose_hanscheid2018(spect_Bq, roi, time_sec, svalue, mass_scaling):
    """
    Input image and ROI must be numpy arrays
    - spect must be in Bq (not concentration)
    - svalue in mGy/MBq/s
    - time in seconds
    - output is in Gray
    """
    # compute mean activity in the ROI, in MBq
    v = spect_Bq[roi == 1]
    At = np.sum(v) / 1e6

    # S is in (mGy/MBq/s), so we get dose in mGy
    dose = mass_scaling * At * svalue * (2 * time_sec) / np.log(2) / 1000

    return dose

def dose_hanscheid2017(spect_Bqml, roi, time_sec, pixel_volume_ml):
    """
    Input img and ROI must be numpy arrays

    """

    time_h = time_sec / 3600
    time_eff_h = 67.0

    # compute mean activity in the ROI
    v = spect_Bqml[roi == 1] / pixel_volume_ml
    Ct = np.mean(v) / 1e6

    #
    dose = 0.125 * Ct * np.power(2, time_h / time_eff_h) * time_eff_h

    return dose

def dose_hanscheid2018_from_filenames(
        spect_file, ct_file, roi_file, phantom, roi_name, rad_name, time_h
):
    # read spect
    spect = itk.ReadImage(spect_file)
    spect_a = itk.GetArrayFromImage(spect)

    # prepare ct : resample and densities
    ct = itk.ReadImage(ct_file)
    ct_a = resample_ct_like_spect(spect, ct)
    densities = convert_ct_to_densities(ct_a)
    volume_voxel_mL = np.prod(spect.GetSpacing()) / 1000

    # prepare roi : resample
    roi = itk.ReadImage(roi_file)
    roi_a = resample_roi_like_spect(spect, roi)

    # get svalues and scaling
    svalue, mass_scaling = get_svalue_and_mass_scaling(
        phantom, roi_a, roi_name, rad_name, volume_voxel_mL, densities
    )

    # compute dose
    time_sec = time_h * 3600
    dose = dose_hanscheid2018(spect_a, roi_a, time_sec, svalue, mass_scaling)
    print(f"Dose for {roi_file}: {dose:.2f} Gray")
    return dose

def get_roi_list(filename):
    # open the file
    with open(filename, "r") as f:
        data = json.load(f)
    l = []
    for item in data:
        l.append((item["roi_filename"], item["roi_name"]))
    return l

def fit_exp_linear(x, y):
    denegative = 1
    if y[0] < 0:
        denegative = -1
        y[0] = -y[0]
    y = np.log(y)
    k, A_log = np.polyfit(x, y, 1)
    A = np.exp(A_log) * denegative
    if k > 0:
        k = 0
    return A, k

def fit_triexpo(times, activities):
    t0 = float(times[0])
    t1 = float(times[1])
    t3 = float(times[2])
    d0 = float(activities[0])
    d1 = float(activities[1])
    d3 = float(activities[2])
    if d3 == d1 or d1 == d0 or d0 == d3:
        d0 += 0.0001
        d1 += 0.0002
        d3 += 0.0003
    d1lin = (((d3 - d0) / (t3 - t0)) * t1 + (d0 - ((d3 - d0) / ((t3 - t0)) * t0)))
    A3 = d3 / (np.exp(-0.001 * t3))
    d1_lowslope = A3 * np.exp(-0.001 * t1)
    if d1 < d1_lowslope:
        if d0 < d3:
            if d1 < d1lin:
                d1 = d1lin
        else:
            d1 = d1_lowslope
    if d3 < 0.1 * d1:
        d3 = 0.1 * d1
    if d0 < 0.2 * d1:
        d0 = 0.2 * d1
    if d3 > d1:
        k3 = -0.001
        A3 = d3 / (np.exp(k3 * t3))

        x = np.array([t1, t3])
        y = np.array([d1 - (A3 * np.exp(k3 * t1)), 0.01 * d3])
        A2, k2 = fit_exp_linear(x, y)
        if (A2 + A3) < 0:
            A2 = -A3
            A1 = 0
            k1 = 0
        else:
            A1 = -(A2 + A3)
            k1 = -1.3
    else:
        x = np.array([t1, t3])
        y = np.array([d1, d3])
        A3, k3 = fit_exp_linear(x, y)
        if k3 > -0.001:
            k3 = -0.001
            A3 = d3 / (np.exp(k3 * t3))
        x = np.array([t0, t1])
        y = np.array([d0 - (A3 * np.exp(k3 * t0)), 0.01 * d1])
        A2, k2 = fit_exp_linear(x, y)
        if (A2 + A3) < 0:
            A2 = -A3
            A1 = 0
            k1 = 0
        else:
            A1 = -(A2 + A3)
            k1 = -1.3
    params = np.array([A1, k1, A2, k2, A3, k3])
    return params


# import library for computer vision
import cv2

# READ, WRITE, AND DISPLAY AN IMAGE

# read an image
image = cv2.imread(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\image_flower.jpg')
# storing image reading into a variable to use it multiple times without rereading
# less expensive method

# showing the image
cv2.imshow('The title of the window for the image of flower, 9th June 2021', image)

# put execution on hold, value 0 allows forever until window is closed
cv2.waitKey(0)

# close the window to show next image
cv2.destroyAllWindows()

# write the image
cv2.imwrite(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\cv outputs\image_flower.png',
            image)
# output is "True" for successful write as .png file at the location

# COLOR SPACE CONVERSION

# read an image
image2 = cv2.imread(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\Penguins.jpg')

# show the image
cv2.imshow(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\BGR_Penguins',
           image2)
cv2.waitKey(0)
cv2.destroyAllWindows()

# convert the BGR image into grayscale
image2 = cv2.cvtColor(image2,cv2.COLOR_BGR2GRAY)

# show the converted image
cv2.imshow(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\Gray_Penguins',
           image2)
cv2.waitKey(0)
cv2.destroyAllWindows()

# EDGE DETECTION

# read an image
image3 = cv2.imread(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\Penguins.jpg')

# detect the edges using Canny()
cv2.imwrite(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\cv outputs\edges_Penguins.jpg',
            cv2.Canny(image3,200,300))

# show the image edges
cv2.imshow(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\edges_Penguins',
           image3)
cv2.waitKey(0)
cv2.destroyAllWindows()

# FACE DETECTION

# import haarcascade_frontalface_default.xml classifier
face_detection = cv2.CascadeClassifier(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\haarcascades\haarcascades\haarcascade_frontalface_default.xml')

# read an image
image4 = cv2.imread(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\AB.jpg')

# convert the BGR image to only acceptable grayscale format
gray = cv2.cvtColor(image4, cv2.COLOR_BGR2GRAY)

# perform face detection
faces = face_detection.detectMultiScale(gray, 1.3, 5)

# draw a rectangle to indicate the face
for (x,y,w,h) in faces:
    image4 = cv2.rectangle(image4,(x,y),(x+w, y+h),(0,255,0),3)
    # (0,255,0) indicates the color of the rectangle
    # 3 indicates the thickness of the border lines

# write the image
cv2.imwrite(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\cv outputs\Face_AB.jpg',
            image4)

# EYE DETECTION

# import the haarcascade_eye.xml classifier
eye_cascade = cv2.CascadeClassifier(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\haarcascades\haarcascades\haarcascade_eye.xml')

# read an image
image5 = cv2.imread(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\AB_Eye.jpg')

# convert the BGR image to only acceptable grayscale format
gray2 = cv2.cvtColor(image5, cv2.COLOR_BGR2GRAY)

# perform eye detection
eyes = eye_cascade.detectMultiScale(gray2, 1.03, 5)

# draw a rectangle to indicate the eyes
for (ex,ey,ew,eh) in eyes:
    image5 = cv2.rectangle(image5,(ex,ey),(ex+ew, ey+eh),(0,0,255),2)

# write  the image
cv2.imwrite(r'C:\Users\annel\Downloads\ITS66604 Machine Learning and Parallel Computing\labs\cv outputs\Eye_AB.jpg',
            image5)


import urllib.request
import posixpath
import os

video_url = ["https://dsmilab.nctu.edu.tw/static/assets/video/left_view.mp4",
             "https://dsmilab.nctu.edu.tw/static/assets/video/right_view.mp4"]

def main():
    for file in video_url:
        file_name = file.split('/')[-1]
        print('downloading: ', file_name)
        target_path = posixpath.join('data', 'map', file_name)
        os.makedirs(os.path.dirname(target_path), exist_ok=True)
        urllib.request.urlretrieve(file, target_path)
    print('finish..')

if __name__ == '__main__':
    main()


# validator.py
# Program validates the solution of a nondeterministic turing machine.
# Project: Turing-machine (FLP project 2)
# Author: Michal Šedý <xsedym02@stud.fit.vutbr.cz>
# Last change: 24.04.2022

import sys
from collections import defaultdict
from curses.ascii import islower, isupper

class Ntm:
    def __init__(self) -> None:
        self.state = "S"
        self.head = 0
        self.tape = list()
        self.rules = defaultdict(lambda: defaultdict(set))
        self.step_cnt = 1

    def load_rules(self, file_name: str):
        """Load Turing machine rules and the input tape from a given file.

        Args:
            file_name (str): file with a turing machine

        Raises:
            e: Bad file format
        """
        fd = open(file_name, "r")
        lines = fd.readlines()
        tape = "S" + lines[-1].rstrip()
        self.tape = list(tape)
        for line in lines[:-1]:
            line = line.rstrip()
            if not line:
                continue
            try:
                s1, l1, s2, l2 = line.split(" ")
            except Exception as e:
                print(tape)
                print(line.split(" "))
                raise e
            self.rules[s1][l1].add((s2, l2))

    def next_tape(self, tape):
        """Test that the Turing machine configuration has been reached correctly.

        Args:
            tape (list): Turing machine tape.
        """
        def cmp_tapes(t1, t2):
            if not all(map(lambda x: islower(x) or x == "#", t1)) or not all(map(lambda x: islower(x) or x == "#", t2)):
                return False
            return t1 == t2

        s1 = self.state
        l1 = self.tape[self.head+1]
        s2 = ""
        l2 = ""

        new_head = self.head
        test_tape1 = self.tape[:self.head] + self.tape[self.head+2:]

        if self.head != 0 and isupper(tape[self.head-1]):
            l2 = "L"
            s2 = tape[self.head-1]
            test_tape2 = tape[:self.head-1] + [tape[self.head]] + tape[self.head+2:]
            new_head -= 1
        elif isupper(tape[self.head]):
            l2 = tape[self.head+1]
            s2 = tape[self.head]
            test_tape2 = tape[:self.head] + tape[self.head+2:]
        elif isupper(tape[self.head+1]):
            l2 = "R"
            s2 = tape[self.head+1]
            if self.head+2 == len(self.tape):
                test_tape2 = tape[:-3]
            else:
                test_tape2 = tape[:self.head] + tape[self.head+2:]
            new_head += 1
        else:
            print("DIFF: State is not on the expected possition or on the tape.", file=sys.stderr)
            print(f"Mismatch between {self.step_cnt} and {self.step_cnt+1} step.", file=sys.stderr)
            print(f"{self.step_cnt}:{''.join(self.tape)}\n{self.step_cnt+1}:{''.join(tape)}", file=sys.stderr)
            return False

        if s1 not in self.rules or l1 not in self.rules[s1] or (s2, l2) not in self.rules[s1][l1]:
            print(f"DIFF: No rule '{s1} {l1} {s2} {l2}' for the tape modification.", file=sys.stderr)
            print(f"Mismatch between {self.step_cnt} and {self.step_cnt+1} step.", file=sys.stderr)
            print(f"{self.step_cnt}:{''.join(self.tape)}\n{self.step_cnt+1}:{''.join(tape)}", file=sys.stderr)
            return False

        if not cmp_tapes(test_tape1, test_tape2):
            print("DIFF: More states in the tape or tape modification withou head on.", file=sys.stderr)
            print(f"Mismatch between {self.step_cnt} and {self.step_cnt+1} step.", file=sys.stderr)
            print(f"{self.step_cnt}:{''.join(self.tape)}\n{self.step_cnt+1}:{''.join(tape)}", file=sys.stderr)
            return False

        self.tape = tape
        self.head = new_head
        self.state = self.tape[new_head]
        self.step_cnt += 1

        return True

class Validator:
    def __init__(self, input_file, rules_file) -> None:
        self.ntm = Ntm()
        self.ntm.load_rules(rules_file)
        self.cmpt_steps = list()
        with open(input_file, "r") as fd:
            for line in fd.readlines():
                self.cmpt_steps.append(list(line.rstrip()))

    def validate(self):
        if "S" not in self.cmpt_steps[0]:
            print("DIFF: No initial state S.", file=sys.stderr)
            print(f"Expected:{''.join(self.ntm.tape)}\nPROLOG:{''.join(self.cmpt_steps[0])}", file=sys.stderr)
            return False

        if "F" not in self.cmpt_steps[-1]:
            print("DIFF: No final state F.", file=sys.stderr)
            print(f"PROLOG:{''.join(self.cmpt_steps[-1])}", file=sys.stderr)
            return False

        if self.ntm.tape != self.cmpt_steps[0]:
            print("DIFF: Input tape differ in rules and output.", file=sys.stderr)
            print(f"ORIG:{''.join(self.ntm.tape)}\nPROLOG:{''.join(self.cmpt_steps[0])}", file=sys.stderr)
            return False

        for step in self.cmpt_steps[1:]:
            if not self.ntm.next_tape(step):
                return False

        return True

def main():
    if {"-h", "--help"}.intersection(sys.argv):
        print("USAGE:")
        print("validator.py test_input rules")
        sys.exit(0)

    if len(sys.argv) != 3:
        raise Exception("Bad number of program parameters.")

    validator = Validator(sys.argv[1], sys.argv[2])
    if not validator.validate():
        sys.exit(1)

if __name__ == "__main__":
    main()


"""Urls for the demo of Zinnia"""
from functools import partial

from django.conf import settings
from django.contrib import admin
from django.contrib.sitemaps.views import index
from django.contrib.sitemaps.views import sitemap
from django.urls import include, re_path
from django.views.defaults import bad_request, page_not_found, permission_denied, server_error
from django.views.generic.base import RedirectView
from django.views.static import serve
from django_xmlrpc.views import handle_xmlrpc

from zinnia.sitemaps import AuthorSitemap
from zinnia.sitemaps import CategorySitemap
from zinnia.sitemaps import EntrySitemap
from zinnia.sitemaps import TagSitemap

urlpatterns = [
    re_path(r'^$', RedirectView.as_view(url='/blog/', permanent=True)),
    re_path(r'^blog/', include('zinnia.urls')),
    re_path(r'^comments/', include('django_comments.urls')),
    re_path(r'^xmlrpc/$', handle_xmlrpc),
    re_path(r'^i18n/', include('django.conf.urls.i18n')),
    re_path(r'^admin/doc/', include('django.contrib.admindocs.urls')),
    re_path(r'^admin/', admin.site.urls),
]

sitemaps = {
    'tags': TagSitemap,
    'blog': EntrySitemap,
    'authors': AuthorSitemap,
    'categories': CategorySitemap
}

urlpatterns += [
    re_path(r'^sitemap.xml$',
            index,
            {'sitemaps': sitemaps}),
    re_path(r'^sitemap-(?P<section>.+)\.xml$',
            sitemap,
            {'sitemaps': sitemaps}),
]

urlpatterns += [
    re_path(r'^400/$', partial(bad_request, exception=None)),
    re_path(r'^403/$', partial(permission_denied, exception=None)),
    re_path(r'^404/$', partial(page_not_found, exception=None)),
    re_path(r'^500/$', server_error),
]

if settings.DEBUG:
    urlpatterns += [
        re_path(r'^media/(?P<path>.*)$', serve,
                {'document_root': settings.MEDIA_ROOT})
    ]


import importlib
import pkgutil
from argparse import ArgumentParser
from re import Pattern
from types import ModuleType
from typing import List, Union, cast
from unittest import mock

import pytest

def pytest_addoption(parser):
    """Standard pytest hook invoked to add options to pytest CLI"""
    parser.addoption(
        "--xfail-providers-with-missing-deps",
        action="store_true",
        help="Skip tests on providers with optional dependencies",
    )

def pytest_runtest_setup(item):
    """Standard pytest hook invoked before each test execution"""
    try:
        skip_providers_with_optdeps = getattr(
            item.config.option, "xfail_providers_with_missing_deps"
        )
    except AttributeError:
        pass
    else:
        if skip_providers_with_optdeps:
            from lexicon._private.discovery import find_providers

            providers = find_providers()
            skip = [
                available
                for provider, available in providers.items()
                if provider in item.parent.name.lower()
            ]
            if skip and not skip[0]:
                pytest.xfail(
                    "Test expected to fail because --skip-providers-with-missing-deps "
                    "is set and provider has missing required dependencies."
                )

@pytest.fixture(scope="session")
def mock_provider():
    """
    Create a fake provider module, and mock relevant
    functions to make it appear as a real module.
    """
    from lexicon.interfaces import Provider as BaseProvider

    class Provider(BaseProvider):
        """
        Fake provider to simulate the provider resolution from configuration,
        and to have execution traces when lexicon client is invoked
        """

        @staticmethod
        def get_nameservers() -> Union[List[str], List[Pattern]]:
            return cast(List[str], [])

        @staticmethod
        def configure_parser(parser: ArgumentParser) -> None:
            pass

        def authenticate(self):
            print("Authenticate action")

        def create_record(self, rtype, name, content):
            return {
                "action": "create",
                "domain": self.domain,
                "type": rtype,
                "name": name,
                "content": content,
            }

        def list_records(self, rtype=None, name=None, content=None):
            return {
                "action": "list",
                "domain": self.domain,
                "type": rtype,
                "name": name,
                "content": content,
            }

        def update_record(self, identifier=None, rtype=None, name=None, content=None):
            return {
                "action": "update",
                "domain": self.domain,
                "identifier": identifier,
                "type": rtype,
                "name": name,
                "content": content,
            }

        def delete_record(self, identifier=None, rtype=None, name=None, content=None):
            return {
                "action": "delete",
                "domain": self.domain,
                "identifier": identifier,
                "type": rtype,
                "name": name,
                "content": content,
            }

        def _request(self, action="GET", url="/", data=None, query_params=None):
            # Not use for tests
            pass

    original_iter = pkgutil.iter_modules
    original_import = importlib.import_module

    with mock.patch("lexicon._private.discovery.pkgutil.iter_modules") as mock_iter:
        with mock.patch(
            "lexicon._private.discovery.importlib.import_module"
        ) as mock_import:

            def return_iter(path):
                """
                This will include an adhoc fakeprovider module
                to the normal return of pkgutil.iter_modules.
                """
                modules = list(original_iter(path))
                modules.append((None, "fakeprovider", None))
                return modules

            mock_iter.side_effect = return_iter

            def return_import(module_name):
                """
                This will return a adhoc fakeprovider module if necessary,
                or fallback to the normal return of importlib.import_module.
                """
                if module_name == "lexicon._private.providers.fakeprovider":
                    module = ModuleType("lexicon._private.providers.fakeprovider")
                    setattr(module, "Provider", Provider)
                    return module
                return original_import(module_name)

            mock_import.side_effect = return_import

            yield


"""
Cleans OpenStack from test entities

Usage: openstack_clean [(-u | --unassigned)] [--keyword=<keyword>] [--read-only] [--verbose]

Options:
    -u --unassigned      Deletes not assigned to any tenant entities
    --keyword=<keyword>  Keyword for searching users and tenants [default: $test]
    --read-only          Just display objects which should be removed
    -v --verbose         Output debug messages

"""
import logbook as log

from utils import find_first, setup_backend_logbook
from os_interfaces.openstack_wrapper import openstack
from functools import lru_cache, reduce

@lru_cache()
def get_tenant(tenant_id):
    return openstack.get_tenant(tenant_id)

def ignore_exception(fn):
    def inner(*args, **kwargs):
        try:
            return fn(*args, **kwargs)
        except Exception:
            import traceback
            traceback.print_exc()
    return inner

def get_tenants_and_users(keyword):
    id_pairs = []
    all_tenants = openstack.get_tenants()
    tenants = [tenant.id for tenant in all_tenants if keyword in tenant.description]
    for tenant in tenants:
        users = openstack.client_keystone.tenants.list_users(tenant)
        user = users[0].id if users else None
        id_pairs.append((user, tenant))

    all_users = openstack.client_keystone.users.list()
    users = [(user.id, None) for user in all_users
             if keyword in user.name and find_first(id_pairs, lambda x: x[0] == user.id) is None]
    id_pairs.extend(users)

    return id_pairs

def accum_total(accum, resources):
    for resource in resources:
        if accum.get(resource, None):
            accum[resource] += resources[resource]
        else:
            accum[resource] = resources[resource]

    return accum

def delete_by_keyword(keyword, read_only):
    id_pairs = get_tenants_and_users(keyword)
    total_list = []
    for id_pair in id_pairs:
        try:
            deleted = openstack.final_delete(*id_pair)
            total_list.append(deleted)
        except Exception:
            log.warning("Deleting of tenant {} has failed", id_pair, exc_info=True)

    if total_list:
        total = reduce(accum_total, total_list, {})
        total_str = ",".join("%s %s" % (k, total[k]) for k in sorted(total))
        log.info('Deleted {}', total_str)
    else:
        log.info('Nothing to delete by keyword {}', keyword)

@ignore_exception
def delete_floatingips(read_only):
    all_ips = openstack.client_neutron.list_floatingips()['floatingips']
    ips = [ip['id'] for ip in all_ips if not get_tenant(ip['tenant_id'])]

    if ips:
        log.info("Delete unassigned ips: {}", ",".join(ips))
    if not read_only:
        for ip in ips:
            openstack.delete_floating_ip(ip)

    return len(ips)

@ignore_exception
def delete_instances(read_only):
    all_instances = openstack.get_nova_servers()
    instances = [instance for instance in all_instances if not get_tenant(instance.tenant_id)]

    for instance in instances:
        log.info('Deleting unassigned instance {} in tenant {}', instance.id, instance.tenant_id)
        if not read_only:
            openstack.client_nova.servers.delete(instance)

    return len(instances)

@ignore_exception
def delete_volumes(read_only):
    all_volumes = openstack.get_volumes()
    volumes = [volume for volume in all_volumes if not get_tenant(getattr(volume, 'os-vol-tenant-attr:tenant_id'))]
    for volume in volumes:
        log.info('Deleting unassigned volume {}, name: {}, size: {}, image_name: {}. tenant: {}',
                 volume.id, volume.name, volume.size, getattr(volume, "volume_image_metadata", None)["image_name"],
                 getattr(volume, 'os-vol-tenant-attr:tenant_id'))
        if not read_only:
            try:
                openstack.client_cinder.volumes.delete(volume.id)
            except Exception as e:
                log.warning("Can't remove volume {}: {}", volume._info, e)

    return len(volumes)

@ignore_exception
def delete_snapshots(read_only):
    all_snapshots = openstack.get_snapshots()
    snapshots = [snapshot for snapshot in all_snapshots if not get_tenant(snapshot.project_id)]
    for snapshot in snapshots:
        log.info('Deleting unassigned snapshot {}, tenant: {}', snapshot, snapshot.project_id)
        if not read_only:
            try:
                openstack.client_cinder.volume_snapshots.delete(snapshot.id)
            except Exception as e:
                log.warning("Can't remove snapshot {}: {}", snapshot._info, e)

    return len(snapshots)

@ignore_exception
def delete_images(read_only):
    all_images = openstack.get_nova_images()
    images = 0
    for image in all_images:
        tenant_id = image.metadata.get('owner_id', None)
        if tenant_id is not None and not get_tenant(tenant_id):
            log.info('Deleted unassigned image {}', image.id)
            if not read_only:
                openstack.client_nova.images.delete(image.id)
            images += 1

    return images

@ignore_exception
def delete_router(router, read_only):
    info = router["external_gateway_info"]
    ips = [ip["ip_address"] for ip in info["external_fixed_ips"] if ip]
    log.info('Deleting unassigned router {}, network {}, ip {}, tenant {}',
             router["id"], info["network_id"], ",".join(ips), router["tenant_id"])

    search_opts = {'device_owner': 'network:router_interface',
                   'device_id': router["id"]}
    ports = openstack.client_neutron.list_ports(**search_opts)['ports']
    for port in ports:
        log.info("Removes port {} from router", port["mac_address"])
        if not read_only:
            openstack.client_neutron.remove_interface_router(router["id"], body=dict(port_id=port['id']))
    if not read_only:
        openstack.client_neutron.delete_router(router["id"])

@ignore_exception
def delete_routers(read_only):
    all_routers = openstack.get_routers()
    routers = [router for router in all_routers if not get_tenant(router['tenant_id'])]
    for router in routers:
        delete_router(router, read_only)

    return len(routers)

@ignore_exception
def delete_networks(read_only):
    all_networks = openstack.get_networks()
    networks = [network for network in all_networks if not get_tenant(network['tenant_id'])]
    for network in networks:
        log.info('Deleted unassigned network {}, tenant {}', network["id"], network["tenant_id"])
        if not read_only:
            try:
                openstack.client_neutron.delete_network(network["id"])
            except Exception as e:
                log.warning("Can't delete network {}: {}", network, e)

    return len(networks)

@ignore_exception
def delete_subnets(read_only):
    all_subnets = openstack.get_subnets()
    subnets = [subnet for subnet in all_subnets if not get_tenant(subnet['tenant_id'])]
    for subnet in subnets:
        log.info('Deleted unassigned subnet {}, name: {}, gateway: {}, network: {}, tenant {}',
                 subnet["id"], subnet["name"], subnet["gateway_ip"], subnet["network_id"], subnet["tenant_id"])
        if not read_only:
            try:
                openstack.client_neutron.delete_subnet(subnet["id"])
            except Exception as e:
                log.warning("Can't delete subnet {}: {}", subnet, e)

    return len(subnets)

@ignore_exception
def delete_ports(read_only):
    all_ports = openstack.get_ports()
    ports = [port for port in all_ports if port['tenant_id'] and not get_tenant(port['tenant_id'])]
    for port in ports:
        log.info('Deleted unassigned port {}, ips: {}, network: {}, tenant: {}',
                 port["mac_address"], ",".join(ip["ip_address"] for ip in port["fixed_ips"]), port["network_id"], port["tenant_id"])
        if not read_only:
            try:
                openstack.client_neutron.delete_port(port['id'])
            except Exception as e:
                log.warning("Can't delete port {}: {}", port, e)

    return len(ports)

@ignore_exception
def delete_security_groups(read_only):
    all_groups = openstack.get_neutron_security_groups()
    groups = [group['id'] for group in all_groups if not get_tenant(group['tenant_id'])]
    for group in groups:
        log.info('Deleted unassigned security group {}', group)
        if not read_only:
            openstack.client_neutron.delete_security_group(group)

    return len(groups)

def delete_unassigned_resources(read_only):
    fips = delete_floatingips(read_only)
    instances = delete_instances(read_only)
    volumes = delete_volumes(read_only)
    snapshots = delete_snapshots(read_only)
    images = delete_images(read_only)
    routers = delete_routers(read_only)
    networks = delete_networks(read_only)
    subnets = delete_subnets(read_only)
    ports = delete_ports(read_only)
    sgroups = delete_security_groups(read_only)

    log.info('Deleted unassigned resources: {} floating ips, {} instances, {} volumes, {} snapshots, {} images, '
             '{} routers, {} networks, {} subnets, {} ports, {} security groups', fips, instances, volumes, snapshots,
             images, routers, networks, subnets, ports, sgroups)

def main():
    import docopt
    opt = docopt.docopt(__doc__)
    verbose = opt["--verbose"]

    with setup_backend_logbook('stderr', openstack_log_level="DEBUG" if verbose else "INFO",
                               min_level="DEBUG" if verbose else "INFO"):
        read_only = opt["--read-only"]
        if opt['--unassigned']:
            delete_unassigned_resources(read_only)
        delete_by_keyword(opt['--keyword'], read_only)

if __name__ == "__main__":
    main()


# Definition for an interval.
class Interval:
    def __init__(self, s=0, e=0):
        self.start = s
        self.end = e

    def __str__(self):
        return "[{}, {}]".format(self.start, self.end)

    def __repr__(self):
        return self.__str__()

class SummaryRanges:
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.head2tail = dict()

    def addNum(self, val):
        """
        :type val: int
        :rtype: void
        """
        before = after = None
        for h, t in self.head2tail.items():
            if h <= val <= t:
                return
            elif t + 1 == val:
                before = [h, t]
            elif val + 1 == h:
                after = [h, t]
        if before and after:
            self.head2tail.pop(after[0])
            self.head2tail[before[0]] = after[1]
        elif before:
            self.head2tail[before[0]] = val
        elif after:
            self.head2tail.pop(after[0])
            self.head2tail[val] = after[1]
        else:
            self.head2tail[val] = val

    def getIntervals(self):
        """
        :rtype: List[Interval]
        """
        return [Interval(s, e) for s, e in sorted(self.head2tail.items())]

# Your SummaryRanges object will be instantiated and called as such:
obj = SummaryRanges()
for val in [1, 3, 7, 2, 6]:
    obj.addNum(val)
    print(obj.getIntervals())


question = ["Napisz wiadomość:", "Skarga na panią Jagódkę bo robi fikołki pokoju recepcjonistek"]
person_list={"1":"Kacper Gazda"}
number_question={"Jagódka Wacławczyk" : "789654231"}
problem_dic={int : str}
days_dic={"1": "Wyjazd z Polski o 4:00. ", "2": "Śniadanie w Macedoni.\nPrzyjazd do Grecji w godzinach: 14-16", "3": "Zwiedzenie góry Olimp oraz saloników"}

print("Wycieczka do Grecji\n")

password_check = "123"
login_check = "user"
choice_start = -1
choice = -1
choice_dp = -1
choice_log = "-1"

def sendmessage():
    question.append(input("Napisz swoje pytanie:\n\n"))

def login_start():
    a = login_check
    b = password_check
    if a == input("Wpisz login: ") and b == input("Wpisz hasło: "):
        print("Zostałeś zalogowany!")
    else:
        print("Niepoprawne dane... Wybierz opcję:")
        print("1. Spróbuj ponownie")
        print("2. Wyjdź")
        choice_log = int(input("Wybór: "))
        if choice_log == 1:
            login_start()
        elif choice_log == 2:
            key_exit()
        else:
            bad_choice()

def bad_choice():
    print("--------------------")
    print("Niepoprawna wartość!")
    key_continue

def first_choice():
    print("Wybierz kim jesteś: \n")
    print("1. Uczestnik")
    print("2. Opiekun")
    print("3. Wyjście")

def key_continue():
    input("Kontynuuj... [Enter]")

def key_back():
    input("Powrót... [Enter]")

def key_exit():
    print("\nŻegnaj !")
    quit()

def addDay():
    input("Dodaj dzień:\n")

if choice_start:
    first_choice()
    try:
        choice_start = int(input("Podaj odpowiedni numer: "))
        print()
    except ValueError:
        bad_choice()

    if choice_start == 1:
        while choice:
            print("Wybierz jedną z opcji:\n")
            print("1. Pokaż plan wycieczki")
            print("2. Pokaż uczestników wycieczki")
            print("3. Pokaż numer do opiekunów")
            print("4. Masz problem? Zgłoś go nam")
            print("5. Wyjście")

            try:
                choice = int(input("Podaj odpowiedni numer: "))
                print()
            except ValueError:
                bad_choice()

            if choice == 1:
                print("Wycieczka składa się z: 7dni")
                for days , plan in days_dic.items():
                    print("-----------\n" + days + "\n" + plan)
                key_continue()
            elif choice == 2:
                print("Uczestnicy wycieczki:\n")
                print(person_list)
                key_continue()
            elif choice == 3:
                print("Numer do opiekunów:\n")
                print(number_question)
                key_continue()
            elif choice == 4:
                print("Zgłoś problem:\n")
                sendmessage()
                key_continue()
            elif choice == 5:
                key_exit()
            else:
                print("\nZły wybór\n")
                continue

    elif choice_start == 2:
        login_start()
        print("Nie działa")
        while choice:
            print("Wybierz jedną z opcji:\n")
            print("1. Pokaż plan wycieczki")
            print("2. Modyfikuj plan wycieczki")
            print("3. Pokaż uczestników")
            print("4. Modyfikuj uczestników")
            print("5. Pokaż numer do opiekuna")
            print("6. Modyfikuj numer do opiekuna")
            print("7. Przeczytaj wiadomości")
            print("8. Wyjście")

            try:
                choice = int(input("Podaj odpowiedni numer: "))
                print()
            except ValueError:
                bad_choice()

            if choice == 1:
                print("Wycieczka składa się z: 7dni")
                for days , plan in days_dic.items():
                    print("-----------\n" + days + "\n" + plan)
                key_continue()
            elif choice == 2:
                days_dic[input("Podaj dzień:\n")]= input("Podaj plan dnia:\n")
                key_continue()
            elif choice == 3:
                print("Uczestnicy wycieczki:\n")
                for number , name in person_list.items():
                    print("---" + number + "--- " + name)
                key_continue()
            elif choice == 4:
                print(" ")
                print("1. Dodaj uczestnika")
                print("2. Usuń uczestnika")
                print("3. Zaktualizuj uczestnika")

                try:
                    choice_dp = int(input("\nPodaj odpowiednią liczbę: "))
                    print()
                except ValueError:
                    bad_choice()

                if choice_dp == 1:
                    person_list[input("Podaj numer uczestnika: ")]= input("Podaj imię i nazwisko uczestnika: ")
                elif choice_dp == 2:
                    person_list.pop(input("Wpisz numer uczestnika: "))
                else:
                    print("Niepoprawna treść")
                key_continue()
            elif choice == 5:
                for name , number in number_question.items():
                    print("---" + name + "--- " + number)
                key_continue()
            elif choice == 6:
                print("1. Dodaj numer")
                print("2. Usuń numer")
                print("3. Zaktualizuj numer")

                try:
                    choice_dp = int(input("\nPodaj odpowiednią liczbę: "))
                    print()
                except ValueError:
                    bad_choice()

                if choice_dp == 1:
                    number_question[input("Podaj nazwę opiekuna: ")]= input("Podaj numer opiekuna: ")
                elif choice_dp == 2:
                    number_question.pop(input("Wpisz nazwę opiekuna: "))
                elif choice_dp == 3:
                    number_question[input("Podaj nazwę opiekuna: ")]= input("Podaj nowy numer opiekuna: ")
                else:
                    print("Nieprawidłowa wartość")
            elif choice == 7:
                print("Przeczytaj wiadomości:\n")
                for val in question:
                    print("---" + val + "---")
                print()
                key_continue()
            elif choice == 8:
                key_exit()
            else:
                print("\nNiepoprawna wartość\n")
                continue
    elif choice_start == 3:
        key_exit()


# Question: 1
# A number is called a magic number if the first digit equals the summation
# of the rest of the digits. Your task is to write a Python code that takes
# some numbers as user input (separated by a comma) in a single line and
# groups the magic & normal numbers in a dictionary. Note that the values
# corresponding to the keys in that dictionary must be in tuple format.

# Sample Input 1:
# 9432, 346, 4538, 83221, 7999888
# Sample Output 1:
# {'Magic': (9432, 83221), 'Normal': (346, 4538, 7999888)}
# -----------------------------------------------------------------
# Sample Input 2:
# 1431, 69716, 353, 7969828
# Sample Output 2:
# {'Magic': (), 'Normal': (1431, 69716, 353, 7969828)}

def mySum(nums):
    total = 0
    for num in nums:
        total += num
    return total

def properDivisors(num):
    divisors = []
    for i in range(1, num):
        if num % i == 0:
            divisors.append(i)
    return divisors

perfect_numbers = []
imperfect_numbers = []

while True:
    user_input = input('Enter a number or \'stop\' to exit: ')

    if user_input.casefold() == 'stop':
        break

    if mySum(properDivisors(int(user_input))) == int(user_input):
        perfect_numbers.append(int(user_input))
    else:
        imperfect_numbers.append(int(user_input))

    # Print after appending
    print({'Perfect': perfect_numbers, 'Imperfect': imperfect_numbers})
    print('===***===')


import numpy as np

class Game:

    def __init__(self,num_rows, num_cols, action_space, obs_space):
        self.num_rows = num_rows
        self.num_cols = num_cols
        self.action_space = action_space
        self.obs_space = obs_space

    def board2array(self):
        new_board = np.zeros(self.obs_space)
        for row in range(self.num_rows):
            for col in range(self.num_cols):
                val = self.board[row][col]
                new_board[(3 * self.num_cols) * row + 3 * col + val] = 1
        return new_board.reshape(1, self.obs_space)


#! /usr/bin/env python
# -*- coding:utf-8 -*-
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r"^$",views.index, name="index"),
    url(r"^login/$",views.login, name="login"),
    url(r"^regist/$",views.regist,name="regist"),
    url(r"^list/$",views.list, name="list"),
    url(r"^edit/?P<user>(\w+)/$",views.edit,name="edit"),
    url(r"^edit_before/$",views.edit_before,name="edit_before"),
    url(r"^add/$",views.add,name="add"),
    url(r"^delete/?P<user>(\w+)/$",views.delete,name="delete"),
]


from django.contrib import admin
from django.urls import path, include
from account_main import views

urlpatterns = [
    path('signup/', views.signup, name='signup'),
    path('signin/', views.signin, name='signin'),
    path('set_user_data/', views.set_user_data, name="set_user_data"),
    path('login/', views.login, name="login"),
    path('logout/', views.LogoutView.as_view(), name="logout"),
    path('login1/', views.LoginView.as_view(), name="login1"),
]


import sys
# sys.path.append("..")#这一句
from IPython import display
from matplotlib import pyplot as plt
import random
import torch
from torch import nn
import torchvision
import torchvision.transforms as transforms
import time
import sys
sys.path.append("..")
import d2lzh_pytorch as d2l
import random
import zipfile
import numpy as np
import math
from collections import namedtuple

# dim=0，表示的是行，函数的意思就是不要行这一维度，保留列
#。data = 通过对修改参数的data来来写模型参数同时不会影响梯度

def use_svg_display():
    # 用矢量图显示
    display.set_matplotlib_formats('svg')

def set_figsize(figsize=(5, 4)):
    use_svg_display()
    # 设置图的尺寸
    plt.rcParams['figure.figsize'] = figsize

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # 样本的读取顺序是随机的
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch
        yield  features.index_select(0, j), labels.index_select(0, j)

def linreg(X, w, b):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    return torch.mm(X, w) + b

def squared_loss(y_hat, y):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    # 注意这里返回的是向量, 另外, pytorch里的MSELoss并没有除以 2
    return (y_hat - y.view(y_hat.size())) ** 2 / 2

def sgd(params, lr, batch_size):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    for param in params:
        param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data

def get_fashion_mnist_labels(labels):
    text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
                   'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
    return [text_labels[int(i)] for i in labels]

def show_fashion_mnist(images, labels):
    use_svg_display()
    # 这里的_表示我们忽略（不使用）的变量
    _, figs = plt.subplots(1, len(images), figsize=(12, 12))
    # print(figs)
    for f, img, lbl in zip(figs, images, labels):
        # print("f",f)
        # print("img",img)
        # print("lbl",lbl)
        f.imshow(img.view((28, 28)).numpy())
        f.set_title(lbl)
        f.axes.get_xaxis().set_visible(False)
        f.axes.get_yaxis().set_visible(False)
    plt.show()

# def load_data_fashion_mnist(batch_size):
#     mnist_train = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=True, download=True,
#                                                     transform=transforms.ToTensor())
#     mnist_test = torchvision.datasets.FashionMNIST(root='~/Datasets/FashionMNIST', train=False, download=True,
#                                                    transform=transforms.ToTensor())
#     if sys.platform.startswith('win'):
#         num_workers = 0  # 0表示不用额外的进程来加速读取数据
#     else:
#         num_workers = 4
#     train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
#     test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
#     return train_iter,test_iter

# softmax运算
def softmax(X):
    X_exp = X.exp()
    partition = X_exp.sum(dim=1, keepdim=True)
    return X_exp / partition  # 这里应用了广播机制

# 定义模型
# def net(X):
#     return softmax(torch.mm(X.view((-1, num_inputs)), W) + b)

# 定义损失函数
def cross_entropy(y_hat, y):
    return - torch.log(y_hat.gather(1, y.view(-1, 1)))

# 计算分类准去率
def accuracy(y_hat, y):
    return (y_hat.argmax(dim=1) == y).float().mean().item()

# 评价模型net在数据集data_iter上的准确率
def evaluate_accuracy(data_iter, net):
    acc_sum, n = 0.0, 0
    for X, y in data_iter:
        if isinstance(net, torch.nn.Module):
            net.eval()  # 评估模式, 这会关闭dropout
            acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            net.train()  # 改回训练模式
        else:  # 自定义的模型
            if ('is_training' in net.__code__.co_varnames):  # 如果有is_training这个参数
                # 将is_training设置成False
                acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
            else:
                acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
        n += y.shape[0]
    return acc_sum / n

# 训练模型
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
              params=None, lr=None, optimizer=None):
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n = 0.0, 0.0, 0
        for X, y in train_iter:
            y_hat = net(X)
            l = loss(y_hat, y).sum()

            # 梯度清零
            if optimizer is not None:
                optimizer.zero_grad()
            elif params is not None and params[0].grad is not None:
                for param in params:
                    param.grad.data.zero_()

            l.backward()
            if optimizer is None:
                sgd(params, lr, batch_size)
            else:
                optimizer.step()  # “softmax回归的简洁实现”一节将用到，更新参数

            train_l_sum += l.item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
              % (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))

#作图函数，其中y轴使用了对数尺度
def semilogy(x_vals, y_vals, x_label, y_label, x2_vals=None, y2_vals=None,
             legend=None, figsize=(5, 5)):
    set_figsize(figsize)
    plt.xlabel(x_label)
    plt.ylabel(y_label)
    plt.semilogy(x_vals, y_vals)
    if x2_vals and y2_vals:
        plt.semilogy(x2_vals, y2_vals, linestyle=':')
        plt.legend(legend)

def corr2d(X, K):  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    h, w = K.shape
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            Y[i, j] = (X[i: i + h, j: j + w] * K).sum()
    return Y

def evaluate_accuracy(data_iter, net, device=None):
    if device is None and isinstance(net, torch.nn.Module):
        # 如果没指定device就使用net的device
        device = list(net.parameters())[0].device #先把net.parameters转成list型
    acc_sum, n = 0.0, 0
    with torch.no_grad():
        for X, y in data_iter:
            if isinstance(net, torch.nn.Module):
                net.eval() # 评估模式, 这会关闭dropout
                acc_sum += (net(X.to(device)).argmax(dim=1) == y.to(device)).float().sum().cpu().item()
                net.train() # 改回训练模式
            else: # 自定义的模型, 3.13节之后不会用到, 不考虑GPU
                if('is_training' in net.__code__.co_varnames): # 如果有is_training这个参数
                    # 将is_training设置成False
                    acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item()
                else:
                    acc_sum += (net(X).argmax(dim=1) == y).float().sum().item()
            n += y.shape[0]
    return acc_sum / n

def train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs):
    net = net.to(device)
    print("training on ", device)
    # print(train_iter.device)
    loss = torch.nn.CrossEntropyLoss()
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, batch_count, start = 0.0, 0.0, 0, 0, time.time()
        for X, y in train_iter:
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
            n += y.shape[0]
            batch_count += 1
        test_acc = evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'
              % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))

def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
    #我们在ToTensor实例前使用Resize实例,将图像扩大到224*224
    """Download the fashion mnist dataset and then load into memory."""
    trans = []
    if resize:
        trans.append(torchvision.transforms.Resize(size=resize))
    trans.append(torchvision.transforms.ToTensor())

    transform = torchvision.transforms.Compose(trans)
    mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
    mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
    if sys.platform.startswith('win'):
        num_workers = 0  # 0表示不用额外的进程来加速读取数据
    else:
        num_workers = 4
    train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
    test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)

    return train_iter, test_iter

import torch.nn.functional as F
class GlobalAvgPool2d(nn.Module):
    # 全局平均池化层可通过将池化窗口形状设置成输入的高和宽实现
    def __init__(self):
        super(GlobalAvgPool2d, self).__init__()
    def forward(self, x):
        return F.avg_pool2d(x, kernel_size=x.size()[2:])

    # 残差块
class Residual(nn.Module):  # 本类已保存在d2lzh_pytorch包中方便以后使用
    def __init__(self, in_channels, out_channels, use_1x1conv=False, stride=1):
        super(Residual, self).__init__()
        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1, stride=stride)
        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1)
        if use_1x1conv:
            self.conv3 = nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride)
        else:
            self.conv3 = None
        self.bn1 = nn.BatchNorm2d(out_channels)
        self.bn2 = nn.BatchNorm2d(out_channels)

    def forward(self, X):
        Y = F.relu(self.bn1(self.conv1(X)))
        Y = self.bn2(self.conv2(Y))
        if self.conv3:
            X = self.conv3(X)
        return F.relu(Y + X)

def load_data_jay_lyrics():
    with zipfile.ZipFile('jaychou_lyrics.txt.zip') as zin:
        with zin.open('jaychou_lyrics.txt') as f:
            corpus_chars = f.read().decode('utf-8')
    corpus_chars = corpus_chars.replace('\n', ' ').replace('\r', ' ')
    corpus_chars = corpus_chars[0:10000]
    idx_to_char = list(set(corpus_chars))
    char_to_idx = dict([(char, i) for i, char in enumerate(idx_to_char)])
    vocab_size = len(char_to_idx)
    corpus_indices = [char_to_idx[char] for char in corpus_chars]
    return corpus_indices,char_to_idx,idx_to_char,vocab_size

def train_2d(trainer):  # 本函数将保存在d2lzh_pytorch包中方便以后使用
    x1, x2, s1, s2 = -5, -2, 0, 0  # s1和s2是自变量状态，本章后续几节会使用
    results = [(x1, x2)]
    for i in range(20):
        x1, x2, s1, s2 = trainer(x1, x2, s1, s2)
        results.append((x1, x2))
    print('epoch %d, x1 %f, x2 %f' % (i + 1, x1, x2))
    return results

def show_trace_2d(f, results):  # 本函数将保存在d2lzh_pytorch包中方便以后使用
    d2l.plt.plot(*zip(*results), '-o', color='#ff7f0e')
    x1, x2 = np.meshgrid(np.arange(-5.5, 1.0, 0.1), np.arange(-3.0, 1.0, 0.1))
    d2l.plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
    d2l.plt.xlabel('x1')
    d2l.plt.ylabel('x2')
    d2l.plt.show()

def get_data_ch7():  # 本函数已保存在d2lzh_pytorch包中方便以后使用
    data = np.genfromtxt('../airfoil_self_noise.dat', delimiter='\t')
    data = (data - data.mean(axis=0)) / data.std(axis=0)
    return torch.tensor(data[:1500, :-1], dtype=torch.float32), \
    torch.tensor(data[:1500, -1], dtype=torch.float32) # 前1500个样本(每个样本5个特征)

def train_pytorch_ch7(optimizer_fn, optimizer_hyperparams, features, labels,
                    batch_size=10, num_epochs=2):
    # 初始化模型
    net = nn.Sequential(
        nn.Linear(features.shape[-1], 1)
    )
    loss = nn.MSELoss()
    optimizer = optimizer_fn(net.parameters(), **optimizer_hyperparams)

    def eval_loss():
        return loss(net(features).view(-1), labels).item() / 2

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)

    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            # 除以2是为了和train_ch7保持一致, 因为squared_loss中除了2
            l = loss(net(X).view(-1), y) / 2

            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())
    # 打印结果和作图
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    d2l.set_figsize()
    d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    d2l.plt.xlabel('epoch')
    d2l.plt.ylabel('loss')
    d2l.plt.show()

def show_images(imgs, num_rows, num_cols, scale=2):
    figsize = (num_cols * scale, num_rows * scale)
    _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
    for i in range(num_rows):
        for j in range(num_cols):
            axes[i][j].imshow(imgs[i * num_cols + j])
            axes[i][j].axes.get_xaxis().set_visible(False)
            axes[i][j].axes.get_yaxis().set_visible(False)
    return axes

def train(train_iter, test_iter, net, loss, optimizer, device, num_epochs):
    net = net.to(device)
    print("training on ", device)
    batch_count = 0
    for epoch in range(num_epochs):
        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()
        for X, y in train_iter:
            X = X.to(device)
            y = y.to(device)
            y_hat = net(X)
            l = loss(y_hat, y)
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            train_l_sum += l.cpu().item()
            train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
            n += y.shape[0]
            batch_count += 1
        test_acc = d2l.evaluate_accuracy(test_iter, net)
        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'
              % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))

def resnet_block(in_channels, out_channels, num_residuals, first_block=False):
    if first_block:
        assert in_channels == out_channels # 第一个模块的通道数同输入通道数一致
    blk = []
    for i in range(num_residuals):
        if i == 0 and not first_block:
            blk.append(Residual(in_channels, out_channels, use_1x1conv=True, stride=2))
        else:
            blk.append(Residual(out_channels, out_channels))
    return nn.Sequential(*blk)

def resnet18():
    net = nn.Sequential(
        nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3),
        nn.BatchNorm2d(64),
        nn.ReLU(),
        nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
    net.add_module("resnet_block1", resnet_block(64, 64, 2, first_block=True))
    net.add_module("resnet_block2", resnet_block(64, 128, 2))
    net.add_module("resnet_block3", resnet_block(128, 256, 2))
    net.add_module("resnet_block4", resnet_block(256, 512, 2))
    net.add_module("global_avg_pool", d2l.GlobalAvgPool2d())  # GlobalAvgPool2d的输出: (Batch, 512, 1, 1)
    net.add_module("fc", nn.Sequential(nn.Flatten(), nn.Linear(512, 10)))
    return net

def bbox_to_rect(bbox, color):  # 本函数已保存在d2lzh_pytorch中方便以后使用
    # 将边界框(左上x, 左上y, 右下x, 右下y)格式转换成matplotlib格式：
    # ((左上x, 左上y), 宽, 高)
    return d2l.plt.Rectangle(#矩形函数
        xy=(bbox[0], bbox[1]), width=bbox[2]-bbox[0], height=bbox[3]-bbox[1],
        fill=False, edgecolor=color, linewidth=2)

def MultiBoxPrior(feature_map, sizes=[0.75, 0.5, 0.25], ratios=[1, 2, 0.5]):
    """
    # 按照「9.4.1. 生成多个锚框」所讲的实现, anchor表示成(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        feature_map: torch tensor, Shape: [N, C, H, W].
        sizes: List of sizes (0~1) of generated MultiBoxPriores.
        ratios: List of aspect ratios (non-negative) of generated MultiBoxPriores.
    Returns:
        anchors of shape (1, num_anchors, 4). 由于batch里每个都一样, 所以第一维为1
    """
    pairs = [] # pair of (size, sqrt(ration))
    for r in ratios:
        pairs.append([sizes[0], math.sqrt(r)])
    for s in sizes[1:]:
        pairs.append([s, math.sqrt(ratios[0])])

    pairs = np.array(pairs)

    ss1 = pairs[:, 0] * pairs[:, 1] # size * sqrt(ration)
    ss2 = pairs[:, 0] / pairs[:, 1] # size / sqrt(ration)

    base_anchors = np.stack([-ss1, -ss2, ss1, ss2], axis=1) / 2

    h, w = feature_map.shape[-2:]
    shifts_x = np.arange(0, w) / w
    shifts_y = np.arange(0, h) / h
    shift_x, shift_y = np.meshgrid(shifts_x, shifts_y)
    shift_x = shift_x.reshape(-1)
    shift_y = shift_y.reshape(-1)
    shifts = np.stack((shift_x, shift_y, shift_x, shift_y), axis=1)

    anchors = shifts.reshape((-1, 1, 4)) + base_anchors.reshape((1, -1, 4))

    return torch.tensor(anchors, dtype=torch.float32).view(1, -1, 4)

def show_bboxes(axes, bboxes, labels=None, colors=None):
    def _make_list(obj, default_values=None):
        if obj is None:
            obj = default_values
        elif not isinstance(obj, (list, tuple)):
            obj = [obj]
        return obj

    labels = _make_list(labels)
    colors = _make_list(colors, ['b', 'g', 'r', 'm', 'c'])
    for i, bbox in enumerate(bboxes):
        color = colors[i % len(colors)]
        rect = d2l.bbox_to_rect(bbox.detach().cpu().numpy(), color)
        axes.add_patch(rect)
        if labels and len(labels) > i:
            text_color = 'k' if color == 'w' else 'w'
            axes.text(rect.xy[0], rect.xy[1], labels[i],
                      va='center', ha='center', fontsize=6, color=text_color,
                      bbox=dict(facecolor=color, lw=0))

def compute_intersection(set_1, set_2):
    """
    计算anchor之间的交集
    Args:
        set_1: a tensor of dimensions (n1, 4), anchor表示成(xmin, ymin, xmax, ymax)
        set_2: a tensor of dimensions (n2, 4), anchor表示成(xmin, ymin, xmax, ymax)
    Returns:
        intersection of each of the boxes in set 1 with respect to each of the boxes in set 2, shape: (n1, n2)
    """
    # PyTorch auto-broadcasts singleton dimensions
    lower_bounds = torch.max(set_1[:, :2].unsqueeze(1), set_2[:, :2].unsqueeze(0))  # (n1, n2, 2)
    upper_bounds = torch.min(set_1[:, 2:].unsqueeze(1), set_2[:, 2:].unsqueeze(0))  # (n1, n2, 2)
    intersection_dims = torch.clamp(upper_bounds - lower_bounds, min=0)  # (n1, n2, 2)
    return intersection_dims[:, :, 0] * intersection_dims[:, :, 1]  # (n1, n2)

def compute_jaccard(set_1, set_2):
    """
    计算anchor之间的Jaccard系数(IoU)
    Args:
        set_1: a tensor of dimensions (n1, 4), anchor表示成(xmin, ymin, xmax, ymax)
        set_2: a tensor of dimensions (n2, 4), anchor表示成(xmin, ymin, xmax, ymax)
    Returns:
        Jaccard Overlap of each of the boxes in set 1 with respect to each of the boxes in set 2, shape: (n1, n2)
    """
    # Find intersections
    intersection = compute_intersection(set_1, set_2)  # (n1, n2)

    # Find areas of each box in both sets
    areas_set_1 = (set_1[:, 2] - set_1[:, 0]) * (set_1[:, 3] - set_1[:, 1])  # (n1)
    areas_set_2 = (set_2[:, 2] - set_2[:, 0]) * (set_2[:, 3] - set_2[:, 1])  # (n2)

    # Find the union
    # PyTorch auto-broadcasts singleton dimensions
    union = areas_set_1.unsqueeze(1) + areas_set_2.unsqueeze(0) - intersection  # (n1, n2)

    return intersection / union  # (n1, n2)

def assign_anchor(bb, anchor, jaccard_threshold=0.5):
    """
    # 按照「9.4.1. 生成多个锚框」图9.3所讲为每个anchor分配真实的bb, anchor表示成归一化(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        bb: 真实边界框(bounding box), shape:（nb, 4）
        anchor: 待分配的anchor, shape:（na, 4）
        jaccard_threshold: 预先设定的阈值
    Returns:
        assigned_idx: shape: (na, ), 每个anchor分配的真实bb对应的索引, 若未分配任何bb则为-1
    """
    na = anchor.shape[0]
    nb = bb.shape[0]
    jaccard = compute_jaccard(anchor, bb).detach().cpu().numpy()  # shape: (na, nb)
    assigned_idx = np.ones(na) * -1  # 初始全为-1

    # 先为每个bb分配一个anchor(不要求满足jaccard_threshold)
    jaccard_cp = jaccard.copy()
    for j in range(nb):
        i = np.argmax(jaccard_cp[:, j])
        assigned_idx[i] = j
        jaccard_cp[i, :] = float("-inf")  # 赋值为负无穷, 相当于去掉这一行

    # 处理还未被分配的anchor, 要求满足jaccard_threshold
    for i in range(na):
        if assigned_idx[i] == -1:
            j = np.argmax(jaccard[i, :])
            if jaccard[i, j] >= jaccard_threshold:
                assigned_idx[i] = j

    return torch.tensor(assigned_idx, dtype=torch.long)

def xy_to_cxcy(xy):
    """
    将(x_min, y_min, x_max, y_max)形式的anchor转换成(center_x, center_y, w, h)形式的.
    https://github.com/sgrvinod/a-PyTorch-Tutorial-to-Object-Detection/blob/master/utils.py
    Args:
        xy: bounding boxes in boundary coordinates, a tensor of size (n_boxes, 4)
    Returns:
        bounding boxes in center-size coordinates, a tensor of size (n_boxes, 4)
    """
    return torch.cat([(xy[:, 2:] + xy[:, :2]) / 2,  # c_x, c_y
                      xy[:, 2:] - xy[:, :2]], 1)  # w, h

def MultiBoxTarget(anchor, label):
    """
    # 按照「9.4.1. 生成多个锚框」所讲的实现, anchor表示成归一化(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        anchor: torch tensor, 输入的锚框, 一般是通过MultiBoxPrior生成, shape:（1，锚框总数，4）
        label: 真实标签, shape为(bn, 每张图片最多的真实锚框数, 5)
               第二维中，如果给定图片没有这么多锚框, 可以先用-1填充空白, 最后一维中的元素为[类别标签, 四个坐标值]
    Returns:
        列表, [bbox_offset, bbox_mask, cls_labels]
        bbox_offset: 每个锚框的标注偏移量，形状为(bn，锚框总数*4)
        bbox_mask: 形状同bbox_offset, 每个锚框的掩码, 一一对应上面的偏移量, 负类锚框(背景)对应的掩码均为0, 正类锚框的掩码均为1
        cls_labels: 每个锚框的标注类别, 其中0表示为背景, 形状为(bn，锚框总数)
    """
    assert len(anchor.shape) == 3 and len(label.shape) == 3
    bn = label.shape[0]

    def MultiBoxTarget_one(anc, lab, eps=1e-6):
        """
        MultiBoxTarget函数的辅助函数, 处理batch中的一个
        Args:
            anc: shape of (锚框总数, 4)
            lab: shape of (真实锚框数, 5), 5代表[类别标签, 四个坐标值]
            eps: 一个极小值, 防止log0
        Returns:
            offset: (锚框总数*4, )
            bbox_mask: (锚框总数*4, ), 0代表背景, 1代表非背景
            cls_labels: (锚框总数, 4), 0代表背景
        """
        an = anc.shape[0]
        assigned_idx = assign_anchor(lab[:, 1:], anc)  # (锚框总数, )
        bbox_mask = ((assigned_idx >= 0).float().unsqueeze(-1)).repeat(1, 4)  # (锚框总数, 4)

        cls_labels = torch.zeros(an, dtype=torch.long)  # 0表示背景
        assigned_bb = torch.zeros((an, 4), dtype=torch.float32)  # 所有anchor对应的bb坐标
        for i in range(an):
            bb_idx = assigned_idx[i]
            if bb_idx >= 0:  # 即非背景
                cls_labels[i] = lab[bb_idx, 0].long().item() + 1  # 注意要加一
                assigned_bb[i, :] = lab[bb_idx, 1:]

        center_anc = xy_to_cxcy(anc)  # (center_x, center_y, w, h)
        center_assigned_bb = xy_to_cxcy(assigned_bb)

        offset_xy = 10.0 * (center_assigned_bb[:, :2] - center_anc[:, :2]) / center_anc[:, 2:]
        offset_wh = 5.0 * torch.log(eps + center_assigned_bb[:, 2:] / center_anc[:, 2:])
        offset = torch.cat([offset_xy, offset_wh], dim=1) * bbox_mask  # (锚框总数, 4)

        return offset.view(-1), bbox_mask.view(-1), cls_labels

    batch_offset = []
    batch_mask = []
    batch_cls_labels = []
    for b in range(bn):
        offset, bbox_mask, cls_labels = MultiBoxTarget_one(anchor[0, :, :], label[b, :, :])

        batch_offset.append(offset)
        batch_mask.append(bbox_mask)
        batch_cls_labels.append(cls_labels)

    bbox_offset = torch.stack(batch_offset)
    bbox_mask = torch.stack(batch_mask)
    cls_labels = torch.stack(batch_cls_labels)
    return [bbox_offset, bbox_mask, cls_labels]

# 以下函数已保存在d2lzh_pytorch包中方便以后使用
from collections import namedtuple
Pred_BB_Info = namedtuple("Pred_BB_Info", ["index", "class_id", "confidence", "xyxy"])

def non_max_suppression(bb_info_list, nms_threshold = 0.5):
    """
    非极大抑制处理预测的边界框
    Args:
        bb_info_list: Pred_BB_Info的列表, 包含预测类别、置信度等信息
        nms_threshold: 阈值
    Returns:
        output: Pred_BB_Info的列表, 只保留过滤后的边界框信息
    """
    output = []
    # 先根据置信度从高到低排序
    sorted_bb_info_list = sorted(bb_info_list, key = lambda x: x.confidence, reverse=True)

    while len(sorted_bb_info_list) != 0:
        best = sorted_bb_info_list.pop(0)
        output.append(best)

        if len(sorted_bb_info_list) == 0:
            break

        bb_xyxy = []
        for bb in sorted_bb_info_list:
            bb_xyxy.append(bb.xyxy)

        iou = compute_jaccard(torch.tensor([best.xyxy]),
                              torch.tensor(bb_xyxy))[0] # shape: (len(sorted_bb_info_list), )

        n = len(sorted_bb_info_list)
        sorted_bb_info_list = [sorted_bb_info_list[i] for i in range(n) if iou[i] <= nms_threshold]
    return output

def MultiBoxDetection(cls_prob, loc_pred, anchor, nms_threshold = 0.5):
    """
    # 按照「9.4.1. 生成多个锚框」所讲的实现, anchor表示成归一化(xmin, ymin, xmax, ymax).
    https://zh.d2l.ai/chapter_computer-vision/anchor.html
    Args:
        cls_prob: 经过softmax后得到的各个锚框的预测概率, shape:(bn, 预测总类别数+1, 锚框个数)
        loc_pred: 预测的各个锚框的偏移量, shape:(bn, 锚框个数*4)
        anchor: MultiBoxPrior输出的默认锚框, shape: (1, 锚框个数, 4)
        nms_threshold: 非极大抑制中的阈值
    Returns:
        所有锚框的信息, shape: (bn, 锚框个数, 6)
        每个锚框信息由[class_id, confidence, xmin, ymin, xmax, ymax]表示
        class_id=-1 表示背景或在非极大值抑制中被移除了
    """
    assert len(cls_prob.shape) == 3 and len(loc_pred.shape) == 2 and len(anchor.shape) == 3
    bn = cls_prob.shape[0]

    def MultiBoxDetection_one(c_p, l_p, anc, nms_threshold = 0.5):
        """
        MultiBoxDetection的辅助函数, 处理batch中的一个
        Args:
            c_p: (预测总类别数+1, 锚框个数)
            l_p: (锚框个数*4, )
            anc: (锚框个数, 4)
            nms_threshold: 非极大抑制中的阈值
        Return:
            output: (锚框个数, 6)
        """
        pred_bb_num = c_p.shape[1]
        anc = (anc + l_p.view(pred_bb_num, 4)).detach().cpu().numpy() # 加上偏移量

        confidence, class_id = torch.max(c_p, 0)
        confidence = confidence.detach().cpu().numpy()
        class_id = class_id.detach().cpu().numpy()

        pred_bb_info = [Pred_BB_Info(
                            index = i,
                            class_id = class_id[i] - 1, # 正类label从0开始
                            confidence = confidence[i],
                            xyxy=[*anc[i]]) # xyxy是个列表
                        for i in range(pred_bb_num)]

        # 正类的index
        obj_bb_idx = [bb.index for bb in non_max_suppression(pred_bb_info, nms_threshold)]

        output = []
        for bb in pred_bb_info:
            output.append([
                (bb.class_id if bb.index in obj_bb_idx else -1.0),
                bb.confidence,
                *bb.xyxy
            ])

        return torch.tensor(output) # shape: (锚框个数, 6)

    batch_output = []
    for b in range(bn):
        batch_output.append(MultiBoxDetection_one(cls_prob[b], loc_pred[b], anchor[0], nms_threshold))

    return torch.stack(batch_output)