class ShowNodeLabels:
    def __init__(self, app):
        self._app = app

    def __call__(self, target):
        current = target.current_item
        labels = current["metadata"]["labels"]
        name = current["metadata"]["name"]
        labels_text = "\n".join(
            [f"Labels for node {name}", "======================", ""]
            + [f"{k:50} : {v}" for k, v in labels.items()]
            + [""]
        )
        self._app.show_text_popup(labels_text)


from BookPool import BookPool
from MessagePrinter import MessagePrinter
import time

'''
This file is dependent on BookPool.py, MessagePrinter.py.
Code might not be clear without understanding those files first.
'''

# read from file when the program starts
BookPool.readFromFile()

def printMainMenu():
    '''
    This method prints the main menu and asks the user for a numeric input of where they want to go

    @parameters: none
    @returns: user input wihtout input validation
    '''

    print("--------------------------------------------\n")
    MessagePrinter.printHeading("Main menu:")  # print in color
    print("1: Add a new book")
    print("2: Borrow books")
    print("3: Return books")
    print("4: Display all books")
    print("5: Exit")
    print('--------------------------------------- \n')
    return input("Enter the number for where you want to go: ")

def addBookMenu():
    '''
    This method deals with the addition of a new book to the current list of books.
    It prints all the required user input prompts.

    @parameters: none
    @returns: name, author, quantity, price of book in that order (all numeric fields have been input validated)
    '''

    print("---------------------------------------------")
    MessagePrinter.printHeading("Add a book: ")  # print in color
    name = input("Enter the name of the new book: ")
    author = input("Enter the name of the author: ")

    # keep asking for input until user inputs valid value
    while(True):
        try:
            quantity = int(input("Enter the quantity of the book: "))
            if quantity > 0:
                break  # input is valid so break out of while loop
            quantity = int('a')

        except:
            MessagePrinter.printError(
                "Please enter a valid number for quantity. Valid values are positive integers: 1, 2, 3,...")  # print error message in color

    # keep asking for input until user inputs valid value
    while(True):
        try:
            price = float(input("Enter the price of the book: "))
            if price > 0:
                break  # input is valid so break out of while loop
            price = float('a')
        except:
            MessagePrinter.printError(
                "Please enter a valid number for price. Valid values are positive decimal numbers. 0.1, 2,...")  # print error message in color

    return name, author, quantity, price

def borrowBooksMenu():

    '''
    This method deals with the borrowing of books.
    It prints all the required user input prompts.

    @parameters: none
    @returns: list of names of books to borrow, name of the borrower in that order (all numeric fields have been input validated)
    '''

    print('--------------------------------------------')
    MessagePrinter.printHeading('Borrow Menu:')  # print in color

    borrowedBooks = []  # list to store all books names user wishes to borrow

    # get all books' names which can be borrowed
    borrowableBooks = BookPool.getBorrowableBooks()

    # if no books can be borrowed print message
    if len(borrowableBooks) == 0:
        MessagePrinter.printWarning(
            "No books can be borrowed!!! Please wait for some books to be added or returned.")  # print warning in color
        print("---------------------------------------")
        return '', ''  # return empty because no books can be borrowed

    borrowerName = input("Enter the name of the borrower: ")

    # keep asking for input until user inputs valid value
    while(True):
        try:
            numberOfBooks = int(input(
                "Out of " + str(len(borrowableBooks)) + " books how many books to borrow: "))
            if numberOfBooks < 1 or numberOfBooks > len(borrowableBooks):
                # input not in range so induce an exception manually
                numberOfBooks = int('a')
            break  # if code reaches here, then input is valid so break out of loop

        except:
            MessagePrinter.printError(
                "Please enter a valid number. Valid values are positive integers: 1 to " + str(len(borrowableBooks)))  # print error in color

    MessagePrinter.printHeading(
        "---Number indices for all the borrowable books:---")  # print in color

    i = 1  # indices for books start at one and i stores them
    for b in borrowableBooks:
        print(str(i) + ":", b)  # print index with book's name
        i += 1
    print('----------------------------------------- \n')

    # take input numberOfBooks times as the user wishes to borrow that many books
    for i in range(0, numberOfBooks):

        # keep asking for input until user inputs valid value
        while(True):
            try:
                toBorrowInd = int(
                    input("Enter the index number for book to be borrowed: "))
                if toBorrowInd > 0 and toBorrowInd <= len(borrowableBooks):
                    # user cant borrow same book twice
                    if(borrowableBooks[toBorrowInd - 1] in borrowedBooks):
                        MessagePrinter.printError(
                            "ERROR:You cannot borrow 2 of the same book! Please choose another book!")  # print error in color
                    else:
                        # add the book name to the borrowedBooks list
                        borrowedBooks.append(borrowableBooks[toBorrowInd - 1])
                        break  # valid input so break out of while loop
                else:
                    # input out of valid range so induce exception manually
                    toBorrowInd = int('a')

            except:
                MessagePrinter.printError(
                    "Please enter a valid number for index. Valid values are positive integers: 1 to " + str(len(borrowableBooks)))  # print error in color

    return borrowedBooks, borrowerName  # return list of books and name of borrower

def returnBooksMenu():

    '''
    This method deals with the returning of books.
    It prints all the required user input prompts.

    @parameters: none
    @returns: Index of the borrow that user wishes to return, name of the returner in that order (all numeric fields have been input validated)
    '''

    # get all pending borrows that have not yet been returned
    allBorrows = BookPool.getAllBorrows()

    print('--------------------------------------------')
    MessagePrinter.printHeading('Return Menu:')

    # if no borrows are pending
    if(len(allBorrows) == 0):
        MessagePrinter.printWarning("All borrows have been returned!!!!")
        return -1, ''

    returnerName = input('Enter the name of the returner: ')

    MessagePrinter.printHeading(
        "Number indices for all the returnable borrows:")  # print in color
    print('-----------------------------')

    # print all the borrows with their indices, borrower name and book names
    for b in allBorrows:
        print(str(b['Index']) + ": Borrower Name:",
              b['Name'] + ', Borrowed Books:', b['Books'])

    print('------------------------------ \n')

    toReturnInd = 0  # this variable stores the index of borrow that user wishes to return

    # keep asking for input until user inputs valid value
    while(True):
        try:
            toReturnInd = int(
                input("Enter the index number for borrow to be returned: "))
            if toReturnInd < 1 or toReturnInd > len(allBorrows):
                # input out of valid range so induce exception manually
                toReturnInd = int('a')
            break  # if code reaches here then input was valid so break out of while loop

        except:
            MessagePrinter.printError(
                "Please enter a valid number for index. Valid values are positive integers: 1 to " + str(len(allBorrows)))  # print error in color

    ''' toReturnInd is decremented by 1 as the indices in list start at 0 but
    user was shown borrows with indices starting with 1 '''
    return toReturnInd - 1, returnerName  # return return index and returner name

# main infinite loop which breaks only when user wishes to exit the program
while(True):
    try:
        ip = int(printMainMenu()) # get value from method and try to convert to integer

        if ip < 1 or ip > 5:
            ip = int('a') # input was in invalid range so induce exception manually

        # input is in between 1 and 5 so check for each case
        if ip == 1:
            #add book menu
            name, author, quantity, price = addBookMenu() # call addBookMenu() and store returned values in respective variables

            BookPool.addBook(name, author, quantity, price, True) # add new book to the books dictionary and to books.txt file
            MessagePrinter.printSuccess('Book added successfully!!!!') # print success message in color
            print('--------------------------------- \n')

        elif ip == 2:
            # borrow book menu
            borrowedBookNames, borrowerName = borrowBooksMenu() # call borrowBooksMenu() and store returned values in respective variables

            # if no books can be borrowed, then borrowedBookNames == ''
            if borrowedBookNames != '':
                BookPool.borrowBooks(borrowedBookNames, borrowerName) # add to borrows list and borrows.txt file and create a borrow note

        elif ip == 3:
            # return book menu
            returnInd, returnerName = returnBooksMenu() # call returnBooksMenu() and store returned values in respective variables
            if returnInd != -1:
                BookPool.returnBooks(returnInd, returnerName) # remove from borrows list and borrows.txt file and create a return note

        elif ip == 4:
            BookPool.printAllBooks() # print all the books with their details

        elif ip == 5:
            MessagePrinter.printHeading("GOODBYE!!!!!! See you soon!!!!") # print goodbye message in color
            time.sleep(5) # sleep so that the user can see the message before termination
            break # user wishes to exit so break out of main while loop
    except:
        MessagePrinter.printError("Please enter an integer between 1 and 5.") # print error message in color


import asyncio, time, re, os, requests, json, random, datetime
from lxml import etree
from multiprocessing.dummy import Pool
from pymongo import MongoClient
from PIL import Image
from tool.ImgOut import iop

client = MongoClient('localhost', 27017)
db = client.MMjpg
collection1 = db.Image1
collection2 = db.Image2
collection3 = db.Image3
collection4 = db.Image4
set_path = 'C:/Users/Administrator/Desktop/图/fhx20161004'
out_path = 'C:/Users/Administrator/Desktop'

def random_output(output_path, source_collection):
    st = time.time()
    img_list = source_collection.find()
    img_random = img_list[random.randint(0, img_list.count())]
    img_set_name = img_random['set_name']
    img_set_list = source_collection.find({'set_name':img_set_name})
    if not os.path.exists(output_path + os.path.sep + img_set_name):
        os.mkdir(output_path + os.path.sep + img_set_name)
    for img_dict in img_set_list:
        save_path = output_path + os.path.sep + img_dict['set_name'] + os.path.sep + img_dict['img_num'] + '.' + img_dict['img_type']
        with open(save_path, 'wb') as img:
            img.write(img_dict['img_data'])
        img.close()
    print('输出完毕，耗时%s' % str(time.time()-st))
# iop.random_output(out_path, collection4)

def insert_pic(i, save_collection = collection4):
    img_name_list = os.listdir(set_path + os.path.sep + i)
    for each_img in img_name_list:
        if save_collection.find({'set_name': i, 'img_num': each_img.split('.')[0]}).count() == 0:
            start_time = time.time()
            with open(set_path + os.path.sep + i + os.path.sep + each_img, 'rb') as img:
                img_data = img.read()
            img.close()
            insert_dict = {}
            insert_dict['img_data'] = img_data
            insert_dict['set_name'] = i
            insert_dict['img_type'] = each_img.split('.')[-1]
            insert_dict['img_num'] = each_img.split('.')[0]
            save_collection.insert(insert_dict)
            print('录入一张图片，来自%s的第%s张图片，耗时%s' % (insert_dict['set_name'], insert_dict['img_num'], str(time.time() - start_time)))

def up_photos():
    collection = [collection1, collection2, collection3, collection4]
    save_file = out_path + os.sep + 'photos'
    for i in collection:
        pic_list = i.find()
        collection_count = 0
        while True:
            try:
                save_pic_count = len(os.listdir(save_file))
                save_path = save_file + os.sep + 'img_%s.' % str(save_pic_count + 1) + pic_list[collection_count]['img_type']
                with open(save_path, 'wb') as pic:
                    pic.write(pic_list[collection_count]['img_data'])
                pic.close()
                collection_count += 2
            except:
                break

up_photos()
# exit()

# b = requests.get('http://imgs.aixifan.com/live/1493028073796/1493028073796.jpg').content
#
# fl = open('a.txt', 'wb')
# fl.write(b)
# fl.close()
# fl = open('a.txt', 'rb')
# with open('b.jpg', 'wb') as c:
#     c.write(fl.read())
# c.close()


from django.test import TestCase
from .models import Entry, Project, Client
from datetime import timedelta
from django.utils.timezone import now

class DurationTestCase(TestCase):
    def setUp(self):
        client = Client.objects.create(name="Darren's Sweet New Company")
        self.project = Project.objects.create(name="First Project", client=client)

    def test_duration_is_accurate(self):
        e = Entry.objects.create(
            title='Our first test time entry',
            project=self.project,
            start_time=now() - timedelta(minutes=55),
            end_time=now()
        )
        self.assertEqual(e.duration, 55)

        e2 = Entry.objects.create(
            title='Our first test time entry',
            project=self.project,
            start_time=now() - timedelta(minutes=550),
            end_time=now()
        )
        self.assertEqual(e2.duration, 550)

    def test_setting_duration_works(self):
        # create Entry with start time now - 30
        # end_time = now
        e = Entry.objects.create(
            title='Our first test time entry',
            project=self.project,
            start_time=now() - timedelta(minutes=30),
            end_time=now()
        )
        self.assertEqual(e.duration, 30)

        # set .duration = 5
        e.duration = 5

        # test .duration == starttime + timedelta(5)
        self.assertEqual(e.duration, 5)
        self.assertEqual(e.end_time, e.start_time + timedelta(minutes=5))


import sys
import numpy as np
import pandas as pd
import mykmeanssp

# --------------- Auxiliary functions --------------- #

def euclid(x, y):  # actually euc^2
    sumUp = 0
    dim = len(x)
    coordinateSub = [0 for init in range(dim)]
    for coordinate in range(dim):
        coordinateSub[coordinate] = x[coordinate] - y[coordinate]
        coordinateSub[coordinate] = coordinateSub[coordinate] * coordinateSub[coordinate]
        sumUp += coordinateSub[coordinate]
    res = (sumUp)

    return res

def getRandCentroids(points, k, indexToKey):

    # what we need:

    n = len(points)
    dim = len(points[1])

    BoolPoints = []
    for i in range(0, n):
        BoolPoints.append(False)  # initialize to false values

    Yindexes = []
    for i in range(0, k):
        Yindexes.append(-1)  # initialize to -1 values

    Dpoints = []
    for i in range(0, n):
        Dpoints.append(-1)  # initialize to -1 values

    Ppoints = []
    for i in range(0, n):
        Ppoints.append(0)  # initialize to 0 values
    toprint = ""

    # actual function:

    SumD = 0
    np.random.seed(0)

    for z in range(0, k):
        SelectedIndex = 0
        if (z == 0):
            SelectedIndex = np.random.choice(n, 1)[0]  # select a random number Rand from 0 to n-1
            toprint = toprint + str(indexToKey[SelectedIndex])
            Yindexes[z] = SelectedIndex
            BoolPoints[SelectedIndex] = True

        else:  # z!=0
            SelectedIndex = np.random.choice(n, 1, p=Ppoints)[0]
            toprint = toprint + "," + str(indexToKey[SelectedIndex])
            Yindexes[z] = SelectedIndex
            BoolPoints[SelectedIndex] = True

            if (z == k - 1):  # no need to update Ppoints again
                break
        for i in range(0, n):  # reset Dpoints, Ppoints
            Dpoints[i] = -1
            Ppoints[i] = 0  # seems like shouldnt reset P
        SumD = 0

        # Yindexes[z] = SelectedIndex
        # BoolPoints[SelectedIndex] = True

        for i in range(0, n):
            for j in range(0, z + 1):
                Yj = points[Yindexes[j]]
                CurrDi = euclid(points[i], Yj)
                # CurrDi = CurrDi * CurrDi
                if (Dpoints[i] == -1 or CurrDi < Dpoints[i]):
                    Dpoints[i] = CurrDi
        SumD = sum(Dpoints)

        for index in range(0, n):
            Ppoints[index] = Dpoints[index] / SumD

    CentArray = []
    for i in range(0, k):
        CentArray.append(points[Yindexes[i]])

    # print("toprint:", toprint)
    return CentArray, Yindexes

# print the recieved matrix
def printResult(ArrayToPrint, rowDim, colDim):
    ArrayToPrint = np.round(ArrayToPrint, decimals=4)
    for i in range(rowDim):
        lineString = ""
        for j in range(colDim):
            if j > 0:
                lineString += ","
            if abs(ArrayToPrint[i * colDim + j]) < 0.00005:
                lineString += "%.4f" % abs(ArrayToPrint[i * colDim + j])
            else:
                #lineString += str(ArrayToPrint[i * colDim + j])
                lineString += "%.4f" % (ArrayToPrint[i * colDim + j])
        print(lineString.replace("[", "").replace("]", ""))

# --------------- K-means-PP algorithm --------------- #

def kmeansPP(points, dim, n, k):

    # ----- verify length's correctness ----- #

    # K must be bigger than zero and integer
    if k < 1 or type(k) != int:
        sys.exit("An Error Has Occured")
    if n == 0:  # At least one point is needed
        sys.exit("An Error Has Occured")
    if n <= k:  # K must be smaller than N and not equal to N
        sys.exit("An Error Has Occured")
    if dim == 0:  # Dim of points is zero
        sys.exit("An Error Has Occured")

    # ----- indexing centroids  ----- #

    points = pd.DataFrame(points)

    indexToKey = []  # indexToKey[i] is the i-th smallest key.
    for i in range(len(points.index)):
        indexToKey.append(int(points.index[i]))

    # ----- choosing random initial centroids  ----- #

    regPoints = np.array(points).tolist()
    _, centroidsIndexes = getRandCentroids(regPoints, k, indexToKey)

    # ----- print random initial centroids  ----- #

    print(",".join([str(index) for index in centroidsIndexes]))

    # ----- create arrays for kmeans algo in c  ----- #

    centroids_vals = []
    for cent_index in centroidsIndexes:
        centroids_vals.append(points.loc[cent_index])
    centroids = pd.DataFrame(centroids_vals)

    centroidsResult = np.zeros(dim * k)

    # ----- calls kmeans in c through fit function in API  ----- #
    # maxIter = 300

    mykmeanssp.fit(points, centroidsResult, centroids, dim, k, 300)

    # ----- printing results ----- #
    printResult(centroidsResult, k, dim)

# --------------- main  --------------- #

if len(sys.argv) != 4:
    sys.exit("An Error Has Occured")

# ----- calling SPKmeans in c ----- #
kStr = sys.argv[1]
goalStr = sys.argv[2]
fileNameStr = sys.argv[3]

n = 0
with open(fileNameStr, 'r') as fin:
  for line in fin:
    n += 1

# TODO: get n,k from file!!!
#n = 10
k = int(kStr)
x,y = n+1, n
#results = np.zeros((n+1, n))
results = np.zeros((x, y))
k_ans = np.zeros((1, 1))

mykmeanssp.fitToSPKmeans(kStr, goalStr, fileNameStr, results, k_ans)

# ----- taking k as int ----- #
k = int(kStr)

if goalStr == "spk":
  k_ans = int(k_ans[0][0])
  results.shape = (x*y, )
  results = results[0:n*k_ans]
  results.shape = (n, k_ans)
  kmeansPP(results, k_ans, n, k_ans)

elif goalStr == "jacobi":   # (n+1) * n
  results.shape = (x*y, 1)
  results = results[0:(n+1)*n]
  printResult(results, n+1, n)

elif goalStr == "wam":
  results.shape = (x*y, )
  results = results[0:n*n]
  printResult(results, n, n)

elif goalStr == "ddg":
  results.shape = (x*y, )
  results = results[0:n*n]
  printResult(results, n, n)

elif goalStr == "lnorm":
  results.shape = (x*y, )
  results = results[0:n*n]
  printResult(results, n, n)


from sacred import Experiment
import torch
import numpy as np
import pandas as pd
import os
import math
import warnings
import itertools
import numbers
import torch.utils.data as utils
#import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc, roc_auc_score, average_precision_score

import sys
sys.path.insert(0, '../src')
from data_loader_oo import DataContainer
from GRUD_model import grud_model

ex = Experiment("GRU-D-mean")
use_cuda = torch.cuda.is_available()
device = torch.device("cuda:0" if use_cuda else "cpu")

#TODO: data gets padded before, does this have any effect if i just take the last step output as output?

def fit(model, criterion, l2_penalty, learning_rate,\
        train_dataloader, dev_dataloader, test_dataloader,\
        learning_rate_decay=0, n_epochs=30, checkpoint_path = 'model_checkpoints', sacred_run=None):

    test_freq = int(len(train_dataloader) / 4)
    use_cuda = torch.cuda.is_available()
    device = torch.device("cuda:0" if use_cuda else "cpu")

    # to check the update
    old_state_dict = {}
    for key in model.state_dict():
        old_state_dict[key] = model.state_dict()[key].clone()

    AU_PRCs = list()
    n_batch = 0
    for epoch in range(n_epochs):
        print("starting Epoch: {}".format(epoch))
        if learning_rate_decay != 0:

            # every [decay_step] epoch reduce the learning rate by half
            if  epoch % learning_rate_decay == 0:
                learning_rate = learning_rate/2
                optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate, weight_decay= l2_penalty)
                print('at epoch {} learning_rate is updated to {}'.format(epoch, learning_rate))

        # train the model
        losses, acc = [], []
        label, pred = [], []
        y_pred_col= []

        #get a minibatch
        for train_data, train_label, train_num_obs in train_dataloader:
            model.train()
            #push current sample to GPU or CPU
            train_data, train_label, train_num_obs = train_data.to(device), train_label.to(device), train_num_obs.to(device)

            # Zero the parameter gradients
            optimizer.zero_grad()

            # Forward pass : Compute predicted y by passing train data to the model
            #y_pred = model(train_data)[:,-1,:] #last output

            #with pad after:
            y_pred = model(train_data)
            #print(y_pred.size())
            y_pred = torch.squeeze(y_pred)
            #print(y_pred.size())
            y_pred = torch.gather(y_pred,1,train_num_obs.long())
            #print(y_pred.size())
            #print(train_num_obs.size())

            #print(train_num_obs)

            # Compute loss
            loss = criterion(y_pred, train_label)
            #print(loss)
            acc.append(
                torch.eq(
                    (torch.sigmoid(y_pred).data > 0.5).float(),
                    train_label)
            )
            losses.append(loss.item())
            print(str(n_batch)+" loss: "+ str(loss.item()) )
            #print(model.w_dg_x.weight)
            #print(model.w_dg_x.bias)

            # perform a backward pass, and update the weights.
            loss.backward()
            optimizer.step()

            #sacred_run.log_scalar('loss', loss, n_batch)
            #sacred_run.log_scalar('acc', acc, n_batch)
            n_batch = n_batch + 1

            if n_batch % test_freq == 0:
                print("Validate...")
                losses, acc = [], []
                label, pred = np.array([]), np.array([])
                model.eval()

                for dev_data, dev_label, dev_num_obs in dev_dataloader:

                    dev_data, dev_label, dev_num_obs = dev_data.to(device), dev_label.to(device), dev_num_obs.to(device)

                    print(dev_data.size())
                    print(dev_label.size())
                    print(dev_num_obs.size())

                    optimizer.zero_grad()
                    # Forward pass : Compute predicted y by passing train data to the model

                    #y_pred = model(dev_data)[:,-1,:] #last output
                    y_pred = model(dev_data)
                    y_pred = torch.squeeze(y_pred)
                    #print(y_pred.size())
                    y_pred = torch.gather(y_pred,1,dev_num_obs.long())

                    # Compute loss
                    loss = criterion(y_pred, dev_label)
                    acc.append(
                        torch.eq(
                            (torch.sigmoid(y_pred).data > 0.5).float(),
                            dev_label)
                    )
                    losses.append(loss.item())

                    label = np.append(label, dev_label.detach().cpu().numpy())
                    pred = np.append(pred, y_pred.detach().cpu().numpy())

                dev_acc = torch.mean(torch.cat(acc).float())
                dev_loss = np.mean(losses)

                dev_pred_out = pred
                dev_label_out = label

                va_auc = roc_auc_score(label, pred)
                va_prc = average_precision_score(label, pred)
                print("validation auc:{}".format(va_auc))
                print("validation prc:{}".format(va_prc))

                sacred_run.log_scalar('va_auc', va_auc, n_batch)
                sacred_run.log_scalar('va_prc', va_prc, n_batch)
                sacred_run.log_scalar('loss', loss.item(), n_batch) #TODO untested

                filename = checkpoint_path+"/GRU_D_epoch_"+str(epoch)+"_step_"+str(n_batch)+".pth"
                print('trying to save file {}'.format(filename) )
                torch.save(model.state_dict(),filename)

                AU_PRCs.append(va_prc)
                train_loss = np.mean(losses)
                # print("Epoch: {} Train: {:.4f}/{:.2f}%, Dev: {:.4f}/{:.2f}%, Test: {:.4f}/{:.2f}% AUC: {:.4f}".format(
                #     epoch, train_loss, train_acc*100, dev_loss, dev_acc*100, test_loss, test_acc*100, auc_score))
                print("Epoch: {} Train loss: {:.4f}, Dev loss: {:.4f}, Val PRC: {:.4f}".format(
                    epoch, train_loss, dev_loss, va_prc))

        train_acc = torch.mean(torch.cat(acc).float())
        train_loss = np.mean(losses)

        train_pred_out = pred
        train_label_out = label

        # save new params
        new_state_dict= {}
        for key in model.state_dict():
            new_state_dict[key] = model.state_dict()[key].clone()

        # compare params
        for key in old_state_dict:
            if (old_state_dict[key] == new_state_dict[key]).all():
                print('Not updated in {}'.format(key))

        """
        # dev loss
        print("Validate...")
        losses, acc = [], []
        label, pred = np.array([]), np.array([])
        model.eval()
        for dev_data, dev_label, num_obs in dev_dataloader:
            # Squeeze the data [1, 33, 49], [1,5] to [33, 49], [5]
            #dev_data = torch.squeeze(dev_data)
            #dev_label = torch.squeeze(dev_label)
            dev_data, dev_label, = dev_data.to(device), dev_label.to(device)

            optimizer.zero_grad()
            # Forward pass : Compute predicted y by passing train data to the model
            #print(dev_data.size())
            #print(model.w_dg_x.weight)
            #print(model.w_dg_x.bias)
            y_pred = model(dev_data)[:,-1,:] #last output
            #print(y_pred.size())

            # Save predict and label
            #pred.append(y_pred.item())
            #label.append(dev_label.item())

            # Compute loss
            loss = criterion(y_pred, dev_label)
            acc.append(
                torch.eq(
                    (torch.sigmoid(y_pred).data > 0.5).float(),
                    dev_label)
            )
            losses.append(loss.item())

            # Save predict and label
            #print(dev_label.detach().numpy().shape)
            #print(y_pred.detach().numpy().shape)
            label = np.append(label, dev_label.detach().cpu().numpy())
            pred = np.append(pred, y_pred.detach().cpu().numpy())
            #print(label.shape)

        dev_acc = torch.mean(torch.cat(acc).float())
        #print(dev_acc)
        #print(label.shape)
        dev_loss = np.mean(losses)

        dev_pred_out = pred
        dev_label_out = label

        va_auc = roc_auc_score(label, pred)
        va_prc = average_precision_score(label, pred)
        print("validation auc:{}".format(va_auc))
        print("validation prc:{}".format(va_prc))

        sacred_run.log_scalar('va_auc', va_auc, n_batch)
        sacred_run.log_scalar('va_prc', va_prc, n_batch)

        AU_PRCs[epoch] = va_prc
        # print("Epoch: {} Train: {:.4f}/{:.2f}%, Dev: {:.4f}/{:.2f}%, Test: {:.4f}/{:.2f}% AUC: {:.4f}".format(
        #     epoch, train_loss, train_acc*100, dev_loss, dev_acc*100, test_loss, test_acc*100, auc_score))
        print("Epoch: {} Train loss: {:.4f}, Dev loss: {:.4f}, Val PRC: {:.4f}".format(
            epoch, train_loss, dev_loss, va_prc))
        """

        # save the parameters
        train_log = []
        train_log.append(model.state_dict())
        #torch.save(model.state_dict(), './save/grud_mean_grud_para.pt')

        #print(train_log)

    return AU_PRCs

#____________________________________________________________________________________________________________________________________________________

@ex.config
def gru_d_config():
    #data config
    file_path = "../mgp-rnn-datadump_labs_vitals_covs_na_thres_500_min_length_7_max_length_200_horizon_0_split_0.pkl"
    data_sources = ['labs','vitals','covs']

    #model config
    hidden_size = 100
    n_layers = 1

    #training config
    n_epochs = 40
    batch_size = 100
    learning_rate = 0.001
    learning_rate_decay =7
    dropout = 0.1
    l2_penalty = 0.001

def count_parameters(model: torch.nn.Module):
    return sum(p.numel() for p in model.parameters() if p.requires_grad)

@ex.main
def run_gru_d(_run, file_path, data_sources, batch_size, n_epochs, hidden_size, n_layers, learning_rate, learning_rate_decay, l2_penalty, dropout):

    #setup data
    data = DataContainer(file_path,data_sources) #problem: need same padding for train and va data
    va_data = data.va_data_gru_d()
    tr_data = data.tr_data_gru_d()
    x_mean = tr_data[:,0,:,:].mean(axis=0).mean(axis=1)

    train_dataset = torch.utils.data.TensorDataset(torch.Tensor(tr_data),torch.Tensor(np.expand_dims(data.labels_tr, axis =1)),torch.Tensor(np.expand_dims(data.num_obs_times_tr-1, axis =1)))
    val_dataset = torch.utils.data.TensorDataset(torch.Tensor(va_data), torch.Tensor(np.expand_dims(data.labels_va, axis =1)),torch.Tensor(np.expand_dims(data.num_obs_times_va-1, axis =1)))
    x_mean = torch.Tensor(x_mean)

    #needs int() bc hyperparameter script passes np.int64 and pytorch doesnt like that
    #TODO add num_obs to dataset, then dataloader

    train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=int(batch_size), shuffle=True)
    val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size= int(batch_size), shuffle=True)

    print(va_data.shape)
    print(tr_data.shape)
    print('Data loaded!')
    #print(tr_data[1])

    print("Build model with params:")
    print("hidden_size: {}, dropout: {}, num_layers: {}".format(hidden_size,dropout,n_layers))

    #fit the model
    input_size = 44 # num of variables
    #hidden_size = 100 # same as inputsize
    output_size = 1
    #num_layers = 3 # num of GRU layers (only first layer is GRU-D)
    seq_len = 1264 # max seq len based on data
    model = grud_model(input_size = input_size, hidden_size= int(hidden_size), output_size=output_size, dropout=dropout, dropout_type='mloss', x_mean=x_mean, num_layers=int(n_layers))

    use_cuda = torch.cuda.is_available()
    if use_cuda :
        torch.backends.cudnn.benchmark = True
        model = model.cuda()
        print("Running on GPU")

    criterion = torch.nn.BCELoss()
    #untested
    #logits=preds, targets=minibatch.O_dupe_onehot, pos_weight=self.class_imb)
    #Get class imbalance (for weighted loss):
    case_prev = data.labels_tr.sum()/float(len(data.labels_tr)) #get prevalence of cases in train dataset
    class_imb = torch.tensor(1/case_prev) #class imbalance to use as class weight if losstype='weighted'
    #TODO then we do not need to apply sigmoid to the model output in the model
    criterion = torch.nn.BCEWithLogitsLoss(pos_weight=class_imb)

    if len(_run.observers) > 0:
         checkpoint_path = os.path.join(_run.observers[0].dir, 'model_checkpoints')
    else:
        checkpoint_path = 'model_checkpoints'

    if not os.path.exists(checkpoint_path):
        os.makedirs(checkpoint_path)
    print(model)

    print('Begin training!')
    AU_PRCs = fit(model, criterion, l2_penalty, learning_rate,\
                   train_dataloader, val_dataloader, val_dataloader,\
                   learning_rate_decay, n_epochs, checkpoint_path, _run)

    print(AU_PRCs)
    best_val = np.array(AU_PRCs).max()

    return {'Best Validation AUPRC': best_val}

if __name__ == '__main__':
    ex.run_commandline()


# #풀이 1
# import sys
# n=int(sys.stdin.readlsne())
# k=int(sys.stdin.readlsne())
# card_num=lsst()

# for i in range(n):
#     card_num.append(input())

# # card_num2=[sys.stdin.readlsne().rstrip() for i in range(n)]
# result=set() #중복을없애려고 set사용

# 빽트래킹(https://han-py.tistory.com/240)
# def permu(cnt,per,visit):
#     global card_num

#     if cnt==k:
#         result.add(''.join(per))
#         return
#     else:
#         for index in range(n):
#             if not visit[index]:
#                 visit[index]=1
#                 permu(cnt+1,per+[card_num[index]],visit)
#                 visit[index]=0
# permu(0,[],[0]*n)
# print(len(result))

# 코딩테스트에서는 순열,조합같은 라이브러리 제공 X
# 1234,2431,... 같이 순서를 전부확인하는걸 backtracking 코드
# https://han-py.tistory.com/240

# 풀이 2
# import sys
# from itertools import permutations

# n=int(sys.stdin.readlsne())
# k=int(sys.stdin.readlsne())
# card_ls = []  # 전체 카드리스트 저장소

# result=set() #중복없애려고 집합활용
# for _ in range(n):
#     # input_num=int(sys.stdin.readlsne())
#     # card_ls.append(input_num) 이거 오류
#     card_ls.append(input())

# for i in permutations(card_ls, k):
#     result.add(''.join(i)) # 'a', 'b', 'c' 리스트를 'abc' 문자열로 만듦
# print(len(result))

#풀이3 (https://jinho-study.tistory.com/1047)
def dfs(depth):
    if depth == k:
        s.add(''.join(map(str, li)))
        print('s:',s)
        return ;
    for i in range(n):
        print('check:',check)
        if check[i]:
            continue
        li.append(nums[i])
        print('ls',li)
        check[i] = 1

        dfs(depth+1)
        li.pop()
        check[i] = 0

n, k = int(input()), int(input())
nums = [int(input()) for _ in range(n)]
li, s = [], set()
check = [0]*n
dfs(0)
print(len(s))


import asyncio

host = '127.0.0.1'
port = 8889
global CLIENTS
CLIENTS = {}

async def handle_client_msg(reader, writer):
    addr = writer.get_extra_info('peername')
    print(f"New client: {addr[0]}:{addr[1]}")

    data = await reader.read(1024)
    message = data.decode()

    pseudo = message.split("|")[1]
    print(f"{addr[0]}:{addr[1]} has chosen the pseudo {pseudo}")
    if addr not in CLIENTS:
        CLIENTS[addr] = {}
        CLIENTS[addr]["r"] = reader
        CLIENTS[addr]["w"] = writer
        CLIENTS[addr]["pseudo"] = pseudo
        await announce(f"{pseudo} has joined the chatroom")

    while True:
        data = await reader.read(1024)
        if data == b'':
            break

        msg = data.decode()
        sender_pseudo = CLIENTS[addr]["pseudo"]
        formatted_msg = f"{sender_pseudo}: {msg}"
        print(formatted_msg)

        for client_addr, client_info in CLIENTS.items():
            if client_addr != addr:
                client_info["w"].write(formatted_msg.encode())
                await client_info["w"].drain()

        if addr in CLIENTS and "saluted" not in CLIENTS[addr]:
            await writer.drain()
            CLIENTS[addr]["saluted"] = True

    print(f"Connection with {pseudo} closed")
    writer.close()

async def announce(announcement):
    for client_addr, client_info in CLIENTS.items():
        announcement = f"{announcement}"
        client_info["w"].write(f"Announcement: {announcement}".encode())
        await client_info["w"].drain()

async def main():
    server = await asyncio.start_server(handle_client_msg, host, port)
    addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
    print(f'Serving on {addrs}')

    async with server:
        await server.serve_forever()

if __name__ == "__main__":

    asyncio.run(main())


from __future__ import absolute_import, print_function, unicode_literals

import os
from re import compile as re_compile
from burger import save_text_file_if_newer, encapsulate_path_linux, \
    convert_to_linux_slashes, convert_to_windows_slashes, where_is_watcom, \
    get_windows_host_type

try:
    from wslwinreg import convert_to_windows_path
except ImportError:
    pass

from .enums import FileTypes, ProjectTypes, PlatformTypes, IDETypes, \
    get_output_template
from .build_objects import BuildObject, BuildError
from .watcom_util import fixup_env, get_custom_list, get_output_list

_WATCOMFILE_MATCH = re_compile("(?is).*\\.wmk\\Z")

SUPPORTED_IDES = (IDETypes.watcom,)

########################################

class BuildWatcomFile(BuildObject):
    """
    Class to build watcom make files

    Attribute:
        verbose: Save the verbose flag
    """

    def __init__(self, file_name, priority, configuration, verbose=False):
        """
        Class to handle watcom make files

        Args:
            file_name: Pathname to the *.wmk to build
            priority: Priority to build this object
            configuration: Build configuration
            verbose: True if verbose output
        """

        super(BuildWatcomFile, self).__init__(
            file_name, priority, configuration=configuration)
        self.verbose = verbose

    def build(self):
        """
        Build Watcom MakeFile.
        @details
        On Linux and Windows hosts, this function will invoke the ``wmake``
        tool to build the watcom make file.

        The PATH will be temporarily adjusted to include the watcom tools so
        wmake can find its shared libraries.

        The default target built is ``all``.

        Returns:
            List of BuildError objects
        """

        # Is Watcom installed?
        watcom_path = where_is_watcom(verbose=self.verbose)
        if watcom_path is None:
            return BuildError(
                0, self.file_name,
                msg="{} requires Watcom to be installed to build!".format(
                    self.file_name))

        # Watcom requires the path set up so it can access link files
        exe_name = where_is_watcom("wmake", verbose=self.verbose)

        saved_path = os.environ["PATH"]
        if get_windows_host_type() or exe_name.endswith(".exe"):
            new_path = os.pathsep.join(
                (os.path.join(
                    watcom_path, "binnt"), os.path.join(
                        watcom_path, "binw")))
            file_name = convert_to_windows_path(self.file_name)

        else:
            new_path = os.path.join(watcom_path, "binl")
            file_name = self.file_name

        os.environ["PATH"] = new_path + os.pathsep + saved_path

        # Set the configuration target
        cmd = [exe_name, "-e", "-h", "-f", file_name, self.configuration]

        # Iterate over the commands
        if self.verbose:
            print(" ".join(cmd))

        result = self.run_command(cmd, self.verbose)

        # Restore the path variable
        os.environ["PATH"] = saved_path

        # Return the error code
        return result

    ########################################

    def clean(self):
        """
        Delete temporary files.

        This function is called by ``cleanme`` to remove temporary files.

        On exit, return 0 for no error, or a non zero error code if there was an
        error to report. None if not implemented or not applicable.

        Returns:
            None if not implemented, otherwise an integer error code.
        """
        return self.build()

########################################

def match(filename):
    """
    Check if the filename is a type that this module supports

    Args:
        filename: Filename to match
    Returns:
        False if not a match, True if supported
    """

    return _WATCOMFILE_MATCH.match(filename)

########################################

def create_build_object(file_name, priority=50,
                 configurations=None, verbose=False):
    """
    Create BuildWatcomFile build records for every desired configuration

    Args:
        file_name: Pathname to the *.wmk to build
        priority: Priority to build this object
        configurations: Configuration list to build
        verbose: True if verbose output
    Returns:
        list of BuildWatcomFile classes
    """

    if not configurations:
        return [BuildWatcomFile(file_name, priority, "all", verbose)]

    results = []
    for configuration in configurations:
        results.append(
            BuildWatcomFile(
                file_name,
                priority,
                configuration,
                verbose))
    return results

########################################

def create_clean_object(file_name, priority=50,
                 configurations=None, verbose=False):
    """
    Create BuildWatcomFile build records for every desired configuration

    Args:
        file_name: Pathname to the *.wmk to build
        priority: Priority to build this object
        configurations: Configuration list to build
        verbose: True if verbose output
    Returns:
        list of BuildWatcomFile classes
    """

    if not configurations:
        return [BuildWatcomFile(file_name, priority, "clean", verbose)]

    results = []
    for configuration in configurations:

        # If clean is invoked, pass it through
        if configuration != "clean" and not configuration.startswith("clean_"):

            # Convert Release to clean_Release
            configuration = "clean_" + configuration

        results.append(
            BuildWatcomFile(file_name, priority, configuration, verbose))
    return results

########################################

def test(ide, platform_type):
    """ Filter for supported platforms

    Args:
        ide: IDETypes
        platform_type: PlatformTypes
    Returns:
        True if supported, False if not
    """

    # pylint: disable=unused-argument

    return platform_type in (
        PlatformTypes.win32, PlatformTypes.msdos4gw, PlatformTypes.msdosx32)

########################################

def generate(solution):
    """
    Create a project file for Watcom.

    Given a Solution object, create an appropriate Watcom WMAKE
    file to allow this project to build.

    Args:
        solution: Solution instance.

    Returns:
        Zero if no error, non-zero on error.
    """

    # Failsafe
    if solution.ide not in SUPPORTED_IDES:
        return 10

    # Create the output filename and pass it to the generator
    # so it can reference itself in make targets
    solution.watcom_filename = "{}{}{}.wmk".format(
        solution.name, solution.ide_code, solution.platform_code)

    # Create an instance of the generator
    exporter = WatcomProject(solution)

    # Output the actual project file
    watcom_lines = []
    error = exporter.generate(watcom_lines)
    if error:
        return error

    # Handle any post processing
    watcom_lines = solution.post_process(watcom_lines)

    # Save the file if it changed
    save_text_file_if_newer(
        os.path.join(solution.working_directory, solution.watcom_filename),
        watcom_lines,
        bom=False,
        perforce=solution.perforce,
        verbose=solution.verbose)
    return 0

########################################

class WatcomProject(object):
    """
    Root object for a Watcom IDE project file
    Created with the name of the project, the IDE code
    the platform code (4gw, x32, win)

    Attributes:
        solution: Parent solution
        platforms: List of all platform types
        configuration_list: List of all configurations
        configuration_names: List of configuration names
        custom_list: List of custom built files
        output_list: List of custom output files
    """

    def __init__(self, solution):
        """
        Initialize the exporter.
        """

        self.solution = solution
        self.platforms = []
        self.configuration_list = []
        self.configuration_names = []

        # Init the list of custom rules
        custom_list = []

        # Process all the projects and configurations
        for project in solution.project_list:

            # Process the filename types supported by Open Watcom
            project.get_file_list(
                [FileTypes.h, FileTypes.cpp, FileTypes.c, FileTypes.x86,
                 FileTypes.hlsl, FileTypes.glsl])

            # Keep a copy of the filenames for now
            codefiles = project.codefiles

            # Add to the master list
            self.configuration_list.extend(project.configuration_list)

            # Create sets of configuration names and projects
            for configuration in project.configuration_list:

                configuration.watcommake_name = configuration.name + \
                    configuration.platform.get_short_code()

                # Add only if not already present
                for item in self.configuration_names:
                    if configuration.name == item.name:
                        break
                else:
                    self.configuration_names.append(configuration)

                # Add platform if not already found
                if configuration.platform not in self.platforms:
                    self.platforms.append(configuration.platform)

                # Get the rule list
                rule_list = (configuration.custom_rules,
                    configuration.parent.custom_rules,
                    configuration.parent.parent.custom_rules)
                get_custom_list(custom_list, rule_list, codefiles)

        self.custom_list = custom_list
        self.output_list = get_output_list(custom_list)

    ########################################

    def write_header(self, line_list):
        """
        Write the header for a Watcom wmake file

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        line_list.extend([
            "#",
            "# Build " + self.solution.name + " with WMAKE",
            "# Generated with makeprojects.watcom",
            "#",
            "# Require the environment variable WATCOM set to the OpenWatcom "
            "folder",
            "# Example: WATCOM=C:\\WATCOM",
            "#",
            "",
            "# This speeds up the building process for Watcom because it",
            "# keeps the apps in memory and doesn't have "
            "to reload for every source file",
            "# Note: There is a bug that if the wlib app is loaded, "
            "it will not",
            "# get the proper WOW file if a full build is performed",
            "",
            "# The bug is gone from Watcom 1.2",
            "",
            "!ifdef %WATCOM",
            "!ifdef __LOADDLL__",
            "!loaddll wcc $(%WATCOM)/binnt/wccd",
            "!loaddll wccaxp $(%WATCOM)/binnt/wccdaxp",
            "!loaddll wcc386 $(%WATCOM)/binnt/wccd386",
            "!loaddll wpp $(%WATCOM)/binnt/wppdi86",
            "!loaddll wppaxp $(%WATCOM)/binnt/wppdaxp",
            "!loaddll wpp386 $(%WATCOM)/binnt/wppd386",
            "!loaddll wlink $(%WATCOM)/binnt/wlinkd",
            "!loaddll wlib $(%WATCOM)/binnt/wlibd",
            "!endif",
            "!endif"])
        return 0

    ########################################

    def write_output_list(self, line_list):
        """
        Output the list of object files to create.

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        line_list.extend([
            "",
            "#",
            "# Custom output files",
            "#",
            ""
        ])

        # Get a list of custom files
        output_list = self.output_list
        if not output_list:
            line_list.append("EXTRA_OBJS=")
            return 0

        colon = "EXTRA_OBJS= "
        for item in output_list:
            line_list.append(
                colon +
                convert_to_linux_slashes(
                    item) + " &")
            colon = "\t"

        # Remove the " &" from the last line
        line_list[-1] = line_list[-1][:-2]
        return 0

    ########################################

    def _write_phony_all(self, line_list):
        """
        Generate ``all`` symbolic target

        Args:
            line_list: List of lines of text generated.
        """

        target_list = []
        for item in self.configuration_names:
            target_list.append(item.name)

        line_all = "all: " + " ".join(target_list) + " .SYMBOLIC"
        line_clean = "clean: " + \
            " ".join(["clean_" + x for x in target_list]) + " .SYMBOLIC"

        line_list.extend((
            "",
            "#",
            "# List the names of all of the final binaries to build and clean",
            "#",
            "",
            line_all,
            "\t@%null",
            "",
            line_clean,
            "\t@%null"
        ))

    ########################################

    def _write_phony_configurations(self, line_list):
        """
        Generate symbolic configuration targets

        Args:
            line_list: List of lines of text generated.
        """

        # Only generate if there are configurations
        if self.configuration_names:
            line_list.extend((
                "",
                "#",
                "# Configurations",
                "#"
            ))

            for configuration in self.configuration_names:
                target_list = []
                for platform in self.platforms:
                    target_list.append(
                        configuration.name +
                        platform.get_short_code())

                line_configuration = configuration.name + \
                    ": " + " ".join(target_list) + " .SYMBOLIC"
                line_clean = "clean_" + configuration.name + ": " + \
                    " ".join(["clean_" + x for x in target_list]) + \
                    " .SYMBOLIC"

                line_list.extend(("",
                                  line_configuration,
                                  "\t@%null",
                                  "",
                                  line_clean,
                                  "\t@%null"
                                  ))

    ########################################

    def _write_phony_platforms(self, line_list):
        """
        Generate a list of platforms

        Args:
            line_list: List of lines of text generated.
        """

        # Only generate if there are platforms
        if self.platforms:
            line_list.extend((
                "",
                "#",
                "# Platforms",
                "#"
            ))

            for platform in self.platforms:

                short_code = platform.get_short_code()

                target_list = []
                for configuration in self.configuration_list:
                    target_list.append(
                        configuration.name +
                        short_code)

                line_platform = short_code + \
                    ": " + " ".join(target_list) + " .SYMBOLIC"
                line_clean = "clean_" + short_code + ": " + \
                    " ".join(["clean_" + x for x in target_list]) + \
                    " .SYMBOLIC"

                line_list.extend(("",
                                  line_platform,
                                  "\t@%null",
                                  "",
                                  line_clean,
                                  "\t@%null"))

    ########################################

    def _write_phony_binaries(self, line_list):
        """
        Generate phony targets for binaries.

        Args:
            line_list: List of lines of text generated.
        """

        if self.configuration_list:

            line_list.extend((
                "",
                "#",
                "# List of binaries to build or clean",
                "#"
            ))

            for configuration in self.configuration_list:
                template = get_output_template(
                    configuration.project_type, configuration.platform)

                platform_short_code = configuration.platform.get_short_code()
                target_name = configuration.name + platform_short_code
                bin_folder = self.solution.name + "wat" + \
                    platform_short_code[-3:] + configuration.short_code
                bin_name = template.format(bin_folder)

                # Instructions to build
                line_list.extend((
                    "",
                    target_name + ": .SYMBOLIC",
                    "\t@if not exist bin @mkdir bin",
   	                "\t@if not exist \"temp\\{0}\" @mkdir \"temp\\{0}\"".format(
   	                    bin_folder),
                    "\t@set CONFIG=" + configuration.name,
                    "\t@set TARGET=" + platform_short_code,
                    "\t@%make bin\\" + bin_name
                ))

                # Instructions to clean
                line_list.extend((
                    "",
                    "clean_" + target_name + ": .SYMBOLIC"
                ))

                # Optional custom outputs
                if self.output_list:
                    line_list.append("\t@rm -f $(EXTRA_OBJS)")

                line_list.extend((
                    "\t@if exist temp\\{0} @rmdir /s /q temp\\{0}".format(
                        bin_folder),
                    "\t@if exist bin\\{0} @del /q bin\\{0}".format(
                        bin_name),
                    # Test if the directory is empty, if so, delete the
                    # directory
                    "\t@-if exist bin @rmdir bin 2>NUL",
                    "\t@-if exist temp @rmdir temp 2>NUL"
                ))

    ########################################

    def write_all_targets(self, line_list):
        """
        Output all of the .SYMBOLIC targets.
        Create all of the targets, starting with all, and then all the
        configurations, followed by the clean targets

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        # Save the "All" targets
        self._write_phony_all(line_list)

        # Save the configuration targets.
        self._write_phony_configurations(line_list)

        # Save the platform targets.
        self._write_phony_platforms(line_list)

        # Generate the list of final binaries
        self._write_phony_binaries(line_list)

        return 0

    ########################################

    def write_directory_targets(self, line_list):
        """
        Create directory and make file targets

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        line_list.extend((
            "",
            "#",
            "# Create the folder for the binary output",
            "#",
            "",
            "bin:",
            "\t@if not exist bin @mkdir bin",
            "",
            "temp:",
            "\t@if not exist temp @mkdir temp"
        ))

        line_list.extend((
            "",
            "#",
            "# Disable building this make file",
            "#",
            "",
            self.solution.watcom_filename + ":",
            "\t@%null"))
        return 0

    ########################################

    def write_test_variables(self, line_list):
        """
        Create tests for environment variables

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        # Scan all entries and make sure all duplicates are purged
        variable_list = set()

        for project in self.solution.project_list:

            # Create sets of configuration names and projects
            for configuration in project.configuration_list:
                variable_list.update(
                    configuration.get_unique_chained_list("env_variable_list"))

        # Anything found?
        if variable_list:
            line_list.extend((
                "",
                "#",
                "# Required environment variables",
                "#"
            ))
            for variable in sorted(variable_list):
                line_list.extend((
                    "",
                    "!ifndef %" + variable,
                    ("!error the environment variable {} "
                     "was not declared").format(variable),
                    "!endif"
                ))
        return 0

    ########################################

    def write_configurations(self, line_list):
        """
        Write configuration list.

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        # Default configuration, assume Release or use
        # the one found first.
        config = None
        for item in self.configuration_list:
            if item.name == "Release":
                config = "Release"
                break
            if config is None:
                config = item.name

        # Nothing in self.configuration_list?
        if config is None:
            config = "Release"

        line_list.extend([
            "",
            "#",
            "# Default configuration",
            "#",
            "",
            "!ifndef CONFIG",
            "CONFIG = " + config,
            "!endif"
        ])

        # Default platform is Dos4GW unless it's not in the list
        target = PlatformTypes.msdos4gw.get_short_code()
        if self.platforms and PlatformTypes.msdos4gw not in self.platforms:
            target = self.platforms[0].get_short_code()

        line_list.extend([
            "",
            "#",
            "# Default target",
            "#",
            "",
            "!ifndef TARGET",
            "TARGET = " + target,
            "!endif"
        ])

        line_list.extend([
            "",
            "#",
            "# Directory name fragments",
            "#"
        ])

        # List all platforms
        line_list.append("")
        for platform in self.platforms:
            line_list.append(
                "TARGET_SUFFIX_{0} = {1}".format(
                    platform.get_short_code(),
                    platform.get_short_code()[-3:]))

        line_list.append("")
        for item in self.configuration_names:
            line_list.append("CONFIG_SUFFIX_{0} = {1}".format(item.name,
                                                              item.short_code))

        # Save the base name of the temp directory
        line_list.extend([
            "",
            "#",
            "# Base name of the temp directory",
            "#",
            "",
            "BASE_TEMP_DIR = temp\\" + self.solution.name,
            "BASE_SUFFIX = wat$(TARGET_SUFFIX_$(%TARGET))"
            "$(CONFIG_SUFFIX_$(%CONFIG))",
            "TEMP_DIR = temp\\{0}$(BASE_SUFFIX)".format(self.solution.name)
        ])

        line_list.extend([
            "",
            "#",
            "# Set the set of known files supported",
            "# Note: They are in the reverse order of building. .c is "
            "built first, then .x86",
            "# until the .exe or .lib files are built",
            "#",
            "",
            ".extensions:",
            ".extensions: .exe .exp .lib .obj .h .cpp .x86 .c .i86",
        ])
        return 0

    ########################################

    def write_source_dir(self, line_list):
        """
        Write out the list of directories for the source

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        # Set the folders for the source code to search
        line_list.extend([
            "",
            "#",
            "# SOURCE_DIRS = Work directories for the source code",
            "#",
            ""
        ])

        # Extract the directories from the files
        # Sort them for consistent diffs for source control
        include_folders = []
        source_folders = []
        for configuration in self.configuration_list:
            for item in configuration.get_unique_chained_list(
                    "_source_include_list"):
                if item not in source_folders:
                    source_folders.append(item)

            for item in configuration.get_unique_chained_list(
                    "include_folders_list"):
                if item not in include_folders:
                    include_folders.append(item)

        if source_folders:
            colon = "="
            for item in sorted(source_folders):
                line_list.append(
                    "SOURCE_DIRS " +
                    colon +
                    encapsulate_path_linux(item))
                colon = "+=;"
        else:
            line_list.append("SOURCE_DIRS =")

        # Extra include folders
        line_list.extend([
            "",
            "#",
            "# INCLUDE_DIRS = Header includes",
            "#",
            "",
            "INCLUDE_DIRS = $(SOURCE_DIRS)"
        ])

        for item in include_folders:
            item = fixup_env(item)
            line_list.append(
                "INCLUDE_DIRS +=;" +
                convert_to_linux_slashes(item))

        return 0

    ########################################

    @staticmethod
    def _setwatcomdirs(line_list):
        """
        Output the default rules for building object code
        """

        # Set the search directories for source files
        line_list.extend([
            "",
            "#",
            "# Tell WMAKE where to find the files to work with",
            "#",
            "",
            ".c: $(SOURCE_DIRS)",
            ".cpp: $(SOURCE_DIRS)",
            ".x86: $(SOURCE_DIRS)",
            ".i86: $(SOURCE_DIRS)"
        ])

    ########################################

    def _setcppflags(self, line_list):
        """
        Output the default rules for C and C++

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        # C and C++ flags
        line_list.extend((
            "",
            "#",
            "# Set the compiler flags for each of the build types",
            "#",
            ""))

        for configuration in self.configuration_list:
            entries = ["CFlags" + configuration.watcommake_name + "="]

            if configuration.platform is PlatformTypes.msdos4gw:
                entries.append("-bt=DOS")
                entries.append("-i=\"$(%WATCOM)/h;$(%WATCOM)/h/nt\"")

            elif configuration.platform is PlatformTypes.msdosx32:
                entries.append("-bt=DOS")
                entries.append("-i=\"$(%WATCOM)/h\"")

            else:
                entries.append("-bm")
                entries.append("-bt=NT")
                entries.append("-dTYPE_BOOL=1")
                entries.append("-dTARGET_CPU_X86=1")
                entries.append("-dTARGET_OS_WIN32=1")
                entries.append("-i=\"$(%WATCOM)/h;$(%WATCOM)/h/nt\"")

            # Enable debug information
            if configuration.debug:
                entries.append("-d2")
            else:
                entries.append("-d0")

            # Enable optimization
            if configuration.optimization:
                entries.append("-oaxsh")
            else:
                entries.append("-od")

            # Add defines
            define_list = configuration.get_chained_list("define_list")
            for item in define_list:
                entries.append("-D" + item)

            line_list.append(" ".join(entries))
        return 0

    ########################################

    def _setasmflags(self, line_list):
        """
        Output the default rules for assembler

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        # Global assembler flags
        line_list.extend((
            "",
            "#",
            "# Set the assembler flags for each of the build types",
            "#",
            ""))

        for configuration in self.configuration_list:
            entries = ["AFlags" + configuration.watcommake_name + "="]

            if configuration.platform.is_windows():
                entries.append("-d__WIN32__=1")

            # Add defines
            define_list = configuration.get_chained_list("define_list")
            for item in define_list:
                entries.append("-D" + item)

            line_list.append(" ".join(entries))
        return 0

    ########################################

    def _setlinkerflags(self, line_list):
        """
        Output the default rules for linker

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        line_list.extend((
            "",
            "#",
            "# Set the Linker flags for each of the build types",
            "#",
            ""))

        for configuration in self.configuration_list:
            entries = ["LFlags" + configuration.watcommake_name + "="]

            if configuration.platform is PlatformTypes.msdos4gw:
                entries.append("system dos4g")

            elif configuration.platform is PlatformTypes.msdosx32:
                entries.append("system x32r")

            else:
                entries.append("system nt")

            # Add libraries

            if not configuration.project_type.is_library():
                lib_list = configuration.get_unique_chained_list(
                    "library_folders_list")
                if lib_list:
                    entries.append("libp")
                    entries.append(";".join([fixup_env(x) for x in lib_list]))

                lib_list = configuration.get_unique_chained_list(
                    "libraries_list")

                if lib_list:
                    entries.append("LIBRARY")
                    entries.append(",".join(lib_list))

            line_list.append(" ".join(entries))

        return 0

    ########################################

    def write_rules(self, line_list):
        """
        Output the default rules for building object code

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        self._setwatcomdirs(line_list)
        self._setcppflags(line_list)
        self._setasmflags(line_list)
        self._setlinkerflags(line_list)

        # Global compiler flags
        line_list.extend([
            "",
            "# Now, set the compiler flags",
            "",
            "CL=WCC386 -6r -fp6 -w4 -ei -j -mf -zq -zp=8 "
            "-wcd=7 -i=\"$(INCLUDE_DIRS)\"",
            "CP=WPP386 -6r -fp6 -w4 -ei -j -mf -zq -zp=8 "
            "-wcd=7 -i=\"$(INCLUDE_DIRS)\"",
            "ASM=WASM -5r -fp6 -w4 -zq -d__WATCOM__=1",
            "LINK=*WLINK option caseexact option quiet PATH $(%WATCOM)/binnt;"
            "$(%WATCOM)/binw;.",
            "",
            "# Set the default build rules",
            "# Requires ASM, CP to be set",
            "",
            "# Macro expansion is on page 93 of the C/C++ Tools User's Guide",
            "# $^* = C:\\dir\\target (No extension)",
            "# $[* = C:\\dir\\dep (No extension)",
            "# $^@ = C:\\dir\\target.ext",
            "# $^: = C:\\dir\\",
            "",
            ".i86.obj : .AUTODEPEND",
            "\t@echo $[&.i86 / $(%CONFIG) / $(%TARGET)",
            "\t@$(ASM) -0 -w4 -zq -d__WATCOM__=1 $(AFlags$(%CONFIG)"
            "$(%TARGET)) $[*.i86 -fo=$^@ -fr=$^*.err",
            "",
            ".x86.obj : .AUTODEPEND",
            "\t@echo $[&.x86 / $(%CONFIG) / $(%TARGET)",
            "\t@$(ASM) $(AFlags$(%CONFIG)$(%TARGET)) "
            "$[*.x86 -fo=$^@ -fr=$^*.err",
            "",
            ".c.obj : .AUTODEPEND",
            "\t@echo $[&.c / $(%CONFIG) / $(%TARGET)",
            "\t@$(CL) $(CFlags$(%CONFIG)$(%TARGET)) $[*.c "
            "-fo=$^@ -fr=$^*.err",
            "",
            ".cpp.obj : .AUTODEPEND",
            "\t@echo $[&.cpp / $(%CONFIG) / $(%TARGET)",
            "\t@$(CP) $(CFlags$(%CONFIG)$(%TARGET)) $[*.cpp "
            "-fo=$^@ -fr=$^*.err"
        ])
        return 0

    ########################################

    def write_files(self, line_list):
        """
        Output the list of object files to create.

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        line_list.extend([
            "",
            "#",
            "# Object files to work with for the library",
            "#",
            ""
        ])

        obj_list = []
        if self.solution.project_list:
            codefiles = self.solution.project_list[0].codefiles

            for item in codefiles:
                if item.type in (FileTypes.c, FileTypes.cpp, FileTypes.x86):

                    tempfile = convert_to_linux_slashes(
                        item.relative_pathname)
                    index = tempfile.rfind(".")
                    if index == -1:
                        entry = tempfile
                    else:
                        entry = tempfile[:index]

                    index = entry.rfind("/")
                    if index != -1:
                        entry = entry[index + 1:]

                    obj_list.append(entry)

        if obj_list:
            colon = "OBJS= "
            for item in sorted(obj_list):
                line_list.append(colon + "$(A)/" + item + ".obj &")
                colon = "\t"
            # Remove the " &" from the last line
            line_list[-1] = line_list[-1][:-2]

        else:
            line_list.append("OBJS=")
        return 0

########################################

    def write_custom_files(self, line_list):
        """
        Output the list of object files to create.

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        # Get a list of custom files
        output_list = self.output_list
        if not output_list:
            return 0

        line_list.extend([
            "",
            "#",
            "# Build custom files",
            "#"
        ])

        output_list = list(self.output_list)
        # Output the execution lines
        while output_list:

            output = output_list[0]

            entry = None
            for item in self.custom_list:
                for output_test in item[2]:
                    if output_test == output:
                        entry = item
                        break
                if entry:
                    break

            else:
                output_list.remove(output)
                continue

            line_list.append("")
            line_list.append(
                " ".join(entry[2]) + " : " +
                convert_to_linux_slashes(
                    entry[3].relative_pathname))
            line_list.append("\t@echo " + entry[1])
            line_list.append("\t@cmd /c & " + fixup_env(entry[0]))

            for output_test in entry[2]:
                output_list.remove(output_test)

        return 0

    ########################################

    def write_builds(self, line_list):
        """
        Output the rule to build the exes/libs

        Args:
            line_list: List of lines of text generated.
        Returns:
            Zero
        """

        line_list.extend([
            "",
            "#",
            "# A = The object file temp folder",
            "#"
        ])

        for configuration in self.configuration_list:
            if configuration.project_type is ProjectTypes.library:
                suffix = ".lib"
            else:
                suffix = ".exe"
            line_list.append("")
            line_list.append(
                "A = $(BASE_TEMP_DIR)wat" +
                configuration.platform.get_short_code()[-3:] +
                configuration.short_code)

            line_list.append(
                "bin\\" + self.solution.name + "wat" +
                configuration.platform.get_short_code()[-3:] +
                configuration.short_code + suffix +
                ": $(EXTRA_OBJS) $+$(OBJS)$- " + self.solution.watcom_filename)

            if configuration.project_type is ProjectTypes.library:

                line_list.extend([
                    "\t@SET WOW=$+$(OBJS)$-",
                    "\t@WLIB -q -b -c -n $^@ @WOW"
                ])

                if configuration.deploy_folder:
                    deploy_folder = convert_to_windows_slashes(
                        configuration.deploy_folder,
                        force_ending_slash=True)[:-1]
                    deploy_folder = fixup_env(deploy_folder)
                    line_list.extend([
                        "\t@p4 edit \"{}\\$^.\"".format(deploy_folder),
                        "\t@copy /y \"$^@\" \"{}\\$^.\"".format(deploy_folder),
                        "\t@p4 revert -a \"{}\\$^.\"".format(deploy_folder)
                    ])
            else:
                line_list.extend([
                    "\t@SET WOW={$+$(OBJS)$-}",
                    "\t@$(LINK) $(LFlags" + configuration.name + \
                    configuration.platform.get_short_code() + ") "
                    "NAME $^@ FILE @wow"
                ])

        return 0

    ########################################

    def generate(self, line_list=None):
        """
        Write out the watcom make project.

        Args:
            line_list: string list to save the XML text
        Returns:
            Zero on no error, non-zero on error.
        """

        if line_list is None:
            line_list = []

        self.write_header(line_list)
        self.write_output_list(line_list)
        self.write_all_targets(line_list)
        self.write_directory_targets(line_list)
        self.write_test_variables(line_list)
        self.write_configurations(line_list)
        self.write_source_dir(line_list)
        self.write_rules(line_list)
        self.write_files(line_list)
        self.write_custom_files(line_list)
        self.write_builds(line_list)
        return 0


import torch
from torch import nn
from torchvision.transforms import functional as F
import numpy as np
import os
from collections.abc import Iterable

def detect_face(imgs, minsize, pnet, rnet, onet, threshold, factor, device):
    if not isinstance(imgs, Iterable):
        imgs = [imgs]
    if any(img.size != imgs[0].size for img in imgs):
        raise Exception("MTCNN batch processing only compatible with equal-dimension images.")

    imgs = [torch.as_tensor(np.uint8(img)).float().to(device) for img in imgs]
    imgs = torch.stack(imgs).permute(0, 3, 1, 2)

    batch_size = len(imgs)
    h, w = imgs.shape[2:4]
    m = 12.0 / minsize
    minl = min(h, w)
    minl = minl * m

    # First stage
    # Create scale pyramid
    total_boxes_all = [np.empty((0, 9)) for i in range(batch_size)]
    scale = m
    while minl >= 12:
        hs = int(h * scale + 1)
        ws = int(w * scale + 1)
        im_data = imresample(imgs, (hs, ws))
        im_data = (im_data - 127.5) * 0.0078125
        reg, probs = pnet(im_data)

        for b_i in range(batch_size):
            boxes = generateBoundingBox(reg[b_i], probs[b_i, 1], scale, threshold[0]).numpy()

            # inter-scale nms
            pick = nms(boxes, 0.5, "Union")
            if boxes.size > 0 and pick.size > 0:
                boxes = boxes[pick, :]
                total_boxes_all[b_i] = np.append(total_boxes_all[b_i], boxes, axis=0)

        scale = scale * factor
        minl = minl * factor

    batch_boxes = []
    batch_points = []
    for img, total_boxes in zip(imgs, total_boxes_all):
        points = []
        numbox = total_boxes.shape[0]
        if numbox > 0:
            pick = nms(total_boxes, 0.7, "Union")
            total_boxes = total_boxes[pick, :]
            regw = total_boxes[:, 2] - total_boxes[:, 0]
            regh = total_boxes[:, 3] - total_boxes[:, 1]
            qq1 = total_boxes[:, 0] + total_boxes[:, 5] * regw
            qq2 = total_boxes[:, 1] + total_boxes[:, 6] * regh
            qq3 = total_boxes[:, 2] + total_boxes[:, 7] * regw
            qq4 = total_boxes[:, 3] + total_boxes[:, 8] * regh
            total_boxes = np.transpose(np.vstack([qq1, qq2, qq3, qq4, total_boxes[:, 4]]))
            total_boxes = rerec(total_boxes)
            total_boxes[:, 0:4] = np.fix(total_boxes[:, 0:4]).astype(np.int32)
            y, ey, x, ex = pad(total_boxes, w, h)

        numbox = total_boxes.shape[0]
        if numbox > 0:
            # second stage
            im_data = []
            for k in range(0, numbox):
                if ey[k] > (y[k] - 1) and ex[k] > (x[k] - 1):
                    img_k = img[:, (y[k] - 1): ey[k], (x[k] - 1): ex[k]].unsqueeze(0)
                    im_data.append(imresample(img_k, (24, 24)))
            im_data = torch.cat(im_data, 0)
            im_data = (im_data - 127.5) * 0.0078125
            out = rnet(im_data)

            out0 = np.transpose(out[0].numpy())
            out1 = np.transpose(out[1].numpy())
            score = out1[1, :]
            ipass = np.where(score > threshold[1])
            total_boxes = np.hstack(
                [total_boxes[ipass[0], 0:4].copy(), np.expand_dims(score[ipass].copy(), 1)]
            )
            mv = out0[:, ipass[0]]
            if total_boxes.shape[0] > 0:
                pick = nms(total_boxes, 0.7, "Union")
                total_boxes = total_boxes[pick, :]
                total_boxes = bbreg(total_boxes.copy(), np.transpose(mv[:, pick]))
                total_boxes = rerec(total_boxes.copy())

        numbox = total_boxes.shape[0]
        if numbox > 0:
            # third stage
            total_boxes = np.fix(total_boxes).astype(np.int32)
            y, ey, x, ex = pad(total_boxes.copy(), w, h)
            im_data = []
            for k in range(0, numbox):
                if ey[k] > (y[k] - 1) and ex[k] > (x[k] - 1):
                    img_k = img[:, (y[k] - 1): ey[k], (x[k] - 1): ex[k]].unsqueeze(0)
                    im_data.append(imresample(img_k, (48, 48)))
            im_data = torch.cat(im_data, 0)
            im_data = (im_data - 127.5) * 0.0078125
            out = onet(im_data)

            out0 = np.transpose(out[0].numpy())
            out1 = np.transpose(out[1].numpy())
            out2 = np.transpose(out[2].numpy())
            score = out2[1, :]
            points = out1
            ipass = np.where(score > threshold[2])
            points = points[:, ipass[0]]
            total_boxes = np.hstack(
                [total_boxes[ipass[0], 0:4].copy(), np.expand_dims(score[ipass].copy(), 1)]
            )
            mv = out0[:, ipass[0]]

            w_i = total_boxes[:, 2] - total_boxes[:, 0] + 1
            h_i = total_boxes[:, 3] - total_boxes[:, 1] + 1
            points_x = (
                    np.tile(w_i, (5, 1)) * points[0:5, :] + np.tile(total_boxes[:, 0], (5, 1)) - 1
            )
            points_y = (
                    np.tile(h_i, (5, 1)) * points[5:10, :] + np.tile(total_boxes[:, 1], (5, 1)) - 1
            )
            points = np.stack((points_x, points_y), axis=0)
            if total_boxes.shape[0] > 0:
                total_boxes = bbreg(total_boxes, np.transpose(mv))
                pick = nms(total_boxes, 0.7, "Min")
                total_boxes = total_boxes[pick, :]
                points = np.transpose(points[:, :, pick])

        batch_boxes.append(total_boxes)
        batch_points.append(points)

    return np.array(batch_boxes), np.array(batch_points)

def bbreg(boundingbox, reg):
    if reg.shape[1] == 1:
        reg = np.reshape(reg, (reg.shape[2], reg.shape[3]))

    w = boundingbox[:, 2] - boundingbox[:, 0] + 1
    h = boundingbox[:, 3] - boundingbox[:, 1] + 1
    b1 = boundingbox[:, 0] + reg[:, 0] * w
    b2 = boundingbox[:, 1] + reg[:, 1] * h
    b3 = boundingbox[:, 2] + reg[:, 2] * w
    b4 = boundingbox[:, 3] + reg[:, 3] * h
    boundingbox[:, 0:4] = np.transpose(np.vstack([b1, b2, b3, b4]))
    return boundingbox

def generateBoundingBox(reg, probs, scale, thresh):
    stride = 2
    cellsize = 12

    mask = probs >= thresh
    score = probs[mask]
    reg = reg[:, mask].permute(1, 0)
    bb = mask.nonzero().float().flip(1)
    q1 = ((stride * bb + 1) / scale).floor()
    q2 = ((stride * bb + cellsize - 1 + 1) / scale).floor()
    boundingbox = torch.cat([q1, q2, score.unsqueeze(1), reg], dim=1)
    return boundingbox

def nms(boxes, threshold, method):
    if boxes.size == 0:
        return np.empty((0, 3))
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    s = boxes[:, 4]
    area = (x2 - x1 + 1) * (y2 - y1 + 1)
    I = np.argsort(s)
    pick = np.zeros_like(s, dtype=np.int16)
    counter = 0
    while I.size > 0:
        i = I[-1]
        pick[counter] = i
        counter += 1
        idx = I[0:-1]
        xx1 = np.maximum(x1[i], x1[idx])
        yy1 = np.maximum(y1[i], y1[idx])
        xx2 = np.minimum(x2[i], x2[idx])
        yy2 = np.minimum(y2[i], y2[idx])
        w = np.maximum(0.0, xx2 - xx1 + 1)
        h = np.maximum(0.0, yy2 - yy1 + 1)
        inter = w * h
        if method is "Min":
            o = inter / np.minimum(area[i], area[idx])
        else:
            o = inter / (area[i] + area[idx] - inter)
        I = I[np.where(o <= threshold)]
    pick = pick[0:counter]
    return pick

def pad(total_boxes, w, h):
    x = total_boxes[:, 0].copy().astype(np.int32)
    y = total_boxes[:, 1].copy().astype(np.int32)
    ex = total_boxes[:, 2].copy().astype(np.int32)
    ey = total_boxes[:, 3].copy().astype(np.int32)

    x[np.where(x < 1)] = 1
    y[np.where(y < 1)] = 1
    ex[np.where(ex > w)] = w
    ey[np.where(ey > h)] = h

    return y, ey, x, ex

def rerec(bboxA):
    h = bboxA[:, 3] - bboxA[:, 1]
    w = bboxA[:, 2] - bboxA[:, 0]
    l = np.maximum(w, h)
    bboxA[:, 0] = bboxA[:, 0] + w * 0.5 - l * 0.5
    bboxA[:, 1] = bboxA[:, 1] + h * 0.5 - l * 0.5
    bboxA[:, 2:4] = bboxA[:, 0:2] + np.transpose(np.tile(l, (2, 1)))
    return bboxA

def imresample(img, sz):
    out_shape = (sz[0], sz[1])
    im_data = torch.nn.functional.interpolate(img, size=out_shape, mode="area")
    return im_data

def extract_face(img, box, image_size=160, margin=0, save_path=None):
    """Extract face + margin from PIL Image given bounding box.

    Arguments:
        img {PIL.Image} -- A PIL Image.
        box {numpy.ndarray} -- Four-element bounding box.
        image_size {int} -- Output image size in pixels. The image will be square.
        margin {int} -- Margin to add to bounding box, in terms of pixels in the final image.
            Note that the application of the margin differs slightly from the davidsandberg/facenet
            repo, which applies the margin to the original image before resizing, making the margin
            dependent on the original image size.
        save_path {str} -- Save path for extracted face image. (default: {None})

    Returns:
        torch.tensor -- tensor representing the extracted face.
    """
    margin = [
        margin * (box[2] - box[0]) / (image_size - margin),
        margin * (box[3] - box[1]) / (image_size - margin),
    ]
    box = [
        int(max(box[0] - margin[0] / 2, 0)),
        int(max(box[1] - margin[1] / 2, 0)),
        int(min(box[2] + margin[0] / 2, img.size[0])),
        int(min(box[3] + margin[1] / 2, img.size[1])),
    ]

    face = img.crop(box).resize((image_size, image_size), 2)

    if save_path is not None:
        os.makedirs(os.path.dirname(save_path) + "/", exist_ok=True)
        save_args = {"compress_level": 0} if ".png" in save_path else {}
        face.save(save_path, **save_args)

    face = F.to_tensor(np.float32(face))

    return face

class PNet(nn.Module):
    def __init__(self):
        super().__init__()

        self.conv1 = nn.Conv2d(3, 10, kernel_size=3)
        self.prelu1 = nn.PReLU(10)
        self.pool1 = nn.MaxPool2d(2, 2, ceil_mode=True)
        self.conv2 = nn.Conv2d(10, 16, kernel_size=3)
        self.prelu2 = nn.PReLU(16)
        self.conv3 = nn.Conv2d(16, 32, kernel_size=3)
        self.prelu3 = nn.PReLU(32)
        self.conv4_1 = nn.Conv2d(32, 2, kernel_size=1)
        self.softmax4_1 = nn.Softmax(dim=1)
        self.conv4_2 = nn.Conv2d(32, 4, kernel_size=1)

        self.training = False

    def forward(self, x):
        x = self.conv1(x)
        x = self.prelu1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.prelu2(x)
        x = self.conv3(x)
        x = self.prelu3(x)
        a = self.conv4_1(x)
        a = self.softmax4_1(a)
        b = self.conv4_2(x)
        return b.cpu(), a.cpu()

class RNet(nn.Module):
    def __init__(self):
        super().__init__()

        self.conv1 = nn.Conv2d(3, 28, kernel_size=3)
        self.prelu1 = nn.PReLU(28)
        self.pool1 = nn.MaxPool2d(3, 2, ceil_mode=True)
        self.conv2 = nn.Conv2d(28, 48, kernel_size=3)
        self.prelu2 = nn.PReLU(48)
        self.pool2 = nn.MaxPool2d(3, 2, ceil_mode=True)
        self.conv3 = nn.Conv2d(48, 64, kernel_size=2)
        self.prelu3 = nn.PReLU(64)
        self.dense4 = nn.Linear(576, 128)
        self.prelu4 = nn.PReLU(128)
        self.dense5_1 = nn.Linear(128, 2)
        self.softmax5_1 = nn.Softmax(dim=1)
        self.dense5_2 = nn.Linear(128, 4)

        self.training = False

    def forward(self, x):
        x = self.conv1(x)
        x = self.prelu1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.prelu2(x)
        x = self.pool2(x)
        x = self.conv3(x)
        x = self.prelu3(x)
        x = x.permute(0, 3, 2, 1).contiguous()
        x = self.dense4(x.view(x.shape[0], -1))
        x = self.prelu4(x)
        a = self.dense5_1(x)
        a = self.softmax5_1(a)
        b = self.dense5_2(x)
        return b.cpu(), a.cpu()

class ONet(nn.Module):
    def __init__(self):
        super().__init__()

        self.conv1 = nn.Conv2d(3, 32, kernel_size=3)
        self.prelu1 = nn.PReLU(32)
        self.pool1 = nn.MaxPool2d(3, 2, ceil_mode=True)
        self.conv2 = nn.Conv2d(32, 64, kernel_size=3)
        self.prelu2 = nn.PReLU(64)
        self.pool2 = nn.MaxPool2d(3, 2, ceil_mode=True)
        self.conv3 = nn.Conv2d(64, 64, kernel_size=3)
        self.prelu3 = nn.PReLU(64)
        self.pool3 = nn.MaxPool2d(2, 2, ceil_mode=True)
        self.conv4 = nn.Conv2d(64, 128, kernel_size=2)
        self.prelu4 = nn.PReLU(128)
        self.dense5 = nn.Linear(1152, 256)
        self.prelu5 = nn.PReLU(256)
        self.dense6_1 = nn.Linear(256, 2)
        self.softmax6_1 = nn.Softmax(dim=1)
        self.dense6_2 = nn.Linear(256, 4)
        self.dense6_3 = nn.Linear(256, 10)

        self.training = False

    def forward(self, x):
        x = self.conv1(x)
        x = self.prelu1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.prelu2(x)
        x = self.pool2(x)
        x = self.conv3(x)
        x = self.prelu3(x)
        x = self.pool3(x)
        x = self.conv4(x)
        x = self.prelu4(x)
        x = x.permute(0, 3, 2, 1).contiguous()
        x = self.dense5(x.view(x.shape[0], -1))
        x = self.prelu5(x)
        a = self.dense6_1(x)
        a = self.softmax6_1(a)
        b = self.dense6_2(x)
        c = self.dense6_3(x)
        return b.cpu(), c.cpu(), a.cpu()

class MTCNN(nn.Module):
    """MTCNN face detection module.

    This class loads pretrained P-, R-, and O-nets and, given raw input images as PIL images,
    returns images cropped to include the face only. Cropped faces can optionally be saved to file
    also.

    Keyword Arguments:
        image_size {int} -- Output image size in pixels. The image will be square. (default: {160})
        margin {int} -- Margin to add to bounding box, in terms of pixels in the final image.
            Note that the application of the margin differs slightly from the davidsandberg/facenet
            repo, which applies the margin to the original image before resizing, making the margin
            dependent on the original image size (this is a bug in davidsandberg/facenet).
            (default: {0})
        min_face_size {int} -- Minimum face size to search for. (default: {20})
        thresholds {list} -- MTCNN face detection thresholds (default: {[0.6, 0.7, 0.7]})
        factor {float} -- Factor used to create a scaling pyramid of face sizes. (default: {0.709})
        prewhiten {bool} -- Whether or not to prewhiten images before returning. (default: {True})
        select_largest {bool} -- If True, if multiple faces are detected, the largest is returned.
            If False, the face with the highest detection probability is returned. (default: {True})
        keep_all {bool} -- If True, all detected faces are returned, in the order dictated by the
            select_largest parameter. If a save_path is specified, the first face is saved to that
            path and the remaining faces are saved to <save_path>1, <save_path>2 etc.
        device {torch.device} -- The device on which to run neural net passes. Image tensors and
            models are copied to this device before running forward passes. (default: {None})
    """

    def __init__(
        self, image_size=160, margin=0, min_face_size=20,
        thresholds=[0.6, 0.7, 0.7], factor=0.709, prewhiten=True,
        select_largest=True, keep_all=False, device=None
    ):
        super().__init__()

        self.image_size = image_size
        self.margin = margin
        self.min_face_size = min_face_size
        self.thresholds = thresholds
        self.factor = factor
        self.prewhiten = prewhiten
        self.select_largest = select_largest
        self.keep_all = keep_all

        self.pnet = PNet()
        self.rnet = RNet()
        self.onet = ONet()

        self.device = torch.device('cpu')
        if device is not None:
            self.device = device
            self.to(device)

    def forward(self, img, save_path=None, return_prob=False):
        """Run MTCNN face detection on a PIL image. This method performs both detection and
        extraction of faces, returning tensors representing detected faces rather than the bounding
        boxes. To access bounding boxes, see the MTCNN.detect() method below.

        Arguments:
            img {PIL.Image or list} -- A PIL image or a list of PIL images.

        Keyword Arguments:
            save_path {str} -- An optional save path for the cropped image. Note that when
                self.prewhiten=True, although the returned tensor is prewhitened, the saved face
                image is not, so it is a true representation of the face in the input image.
                If `img` is a list of images, `save_path` should be a list of equal length.
                (default: {None})
            return_prob {bool} -- Whether or not to return the detection probability.
                (default: {False})

        Returns:
            Union[torch.Tensor, tuple(torch.tensor, float)] -- If detected, cropped image of a face
                with dimensions 3 x image_size x image_size. Optionally, the probability that a
                face was detected. If self.keep_all is True, n detected faces are returned in an
                n x 3 x image_size x image_size tensor with an optional list of detection
                probabilities. If `img` is a list of images, the item(s) returned have an extra
                dimension (batch) as the first dimension.

        Example:
        >>> from facenet_pytorch import MTCNN
        >>> mtcnn = MTCNN()
        >>> face_tensor, prob = mtcnn(img, save_path='face.png', return_prob=True)
        """

        # Detect faces
        with torch.no_grad():
            batch_boxes, batch_probs = self.detect(img)

        # Determine if a batch or single image was passed
        batch_mode = True
        if not isinstance(img, Iterable):
            img = [img]
            batch_boxes = [batch_boxes]
            batch_probs = [batch_probs]
            batch_mode = False

        # Parse save path(s)
        if save_path is not None:
            if isinstance(save_path, str):
                save_path = [save_path]
        else:
            save_path = [None for _ in range(len(img))]

        # Process all bounding boxes and probabilities
        faces, probs = [], []
        for im, box_im, prob_im, path_im in zip(img, batch_boxes, batch_probs, save_path):
            if box_im is None:
                faces.append(None)
                probs.append([None] if self.keep_all else None)
                continue

            if not self.keep_all:
                box_im = box_im[[0]]

            faces_im = []
            for i, box in enumerate(box_im):
                face_path = path_im
                if path_im is not None and i > 0:
                    save_name, ext = os.path.splitext(path_im)
                    face_path = save_name + '_' + str(i + 1) + ext

                face = extract_face(im, box, self.image_size,
                                    self.margin, face_path)
                if self.prewhiten:
                    face = prewhiten(face)
                faces_im.append(face)

            if self.keep_all:
                faces_im = torch.stack(faces_im)
            else:
                faces_im = faces_im[0]
                prob_im = prob_im[0]

            faces.append(faces_im)
            probs.append(prob_im)

        if not batch_mode:
            faces = faces[0]
            probs = probs[0]

        if return_prob:
            return faces, probs
        else:
            return faces

    def detect(self, img, landmarks=False):
        """Detect all faces in PIL image and return bounding boxes and optional facial landmarks.

        This method is used by the forward method and is also useful for face detection tasks
        that require lower-level handling of bounding boxes and facial landmarks (e.g., face
        tracking). The functionality of the forward function can be emulated by using this method
        followed by the extract_face() function.

        Arguments:
            img {PIL.Image or list} -- A PIL image or a list of PIL images.

        Keyword Arguments:
            landmarks {bool} -- Whether to return facial landmarks in addition to bounding boxes.
                (default: {False})

        Returns:
            tuple(numpy.ndarray, list) -- For N detected faces, a tuple containing an
                Nx4 array of bounding boxes and a length N list of detection probabilities.
                Returned boxes will be sorted in descending order by detection probability if
                self.select_largest=False, otherwise the largest face will be returned first.
                If `img` is a list of images, the items returned have an extra dimension
                (batch) as the first dimension. Optionally, a third item, the facial landmarks,
                are returned if `landmarks=True`.

        Example:
        >>> from PIL import Image, ImageDraw
        >>> from facenet_pytorch import MTCNN, extract_face
        >>> mtcnn = MTCNN(keep_all=True)
        >>> boxes, probs, points = mtcnn.detect(img, landmarks=True)
        >>> # Draw boxes and save faces
        >>> img_draw = img.copy()
        >>> draw = ImageDraw.Draw(img_draw)
        >>> for i, (box, point) in enumerate(zip(boxes, points)):
        ...     draw.rectangle(box.tolist(), width=5)
        ...     for p in point:
        ...         draw.rectangle((p - 10).tolist() + (p + 10).tolist(), width=10)
        ...     extract_face(img, box, save_path='detected_face_{}.png'.format(i))
        >>> img_draw.save('annotated_faces.png')
        """

        with torch.no_grad():
            batch_boxes, batch_points = detect_face(
                img, self.min_face_size,
                self.pnet, self.rnet, self.onet,
                self.thresholds, self.factor,
                self.device
            )

        boxes, probs, points = [], [], []
        for box, point in zip(batch_boxes, batch_points):
            box = np.array(box)
            point = np.array(point)
            if len(box) == 0:
                boxes.append(None)
                probs.append([None])
                points.append(None)
            elif self.select_largest:
                box_order = np.argsort(
                    (box[:, 2] - box[:, 0]) * (box[:, 3] - box[:, 1]))[::-1]
                box = box[box_order]
                point = point[box_order]
                boxes.append(box[:, :4])
                probs.append(box[:, 4])
                points.append(point)
            else:
                boxes.append(box[:, :4])
                probs.append(box[:, 4])
                points.append(point)
        boxes = np.array(boxes)
        probs = np.array(probs)
        points = np.array(points)

        if not isinstance(img, Iterable):
            boxes = boxes[0]
            probs = probs[0]
            points = points[0]

        if landmarks:
            return boxes, probs, points

        return boxes, probs

def prewhiten(x):
    mean = x.mean()
    std = x.std()
    std_adj = std.clamp(min=1.0 / (float(x.numel())**0.5))
    y = (x - mean) / std_adj
    return y


class Solution:
    def diStringMatch(self, S: str) -> List[int]:
        N = len(S)
        result = [0] * (N+1)
        left, right = 0, N
        for i in range(N):
            if S[i] == 'I':
                result[i] = left
                left += 1
            else:
                result[i] = right
                right -= 1
        result[-1] = left
        return result

0901-1000/0942-DI String Match/0942-DI String Match.py